blob: 85a6554d084eb8e9ebf2d0f3302c1278158a976e [file] [log] [blame]
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
Polymer = {};
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
// TODO(sorvell): this ensures Polymer is an object and not a function
// Platform is currently defining it as a function to allow for async loading
// of polymer; once we refine the loading process this likely goes away.
if (typeof window.Polymer === 'function') {
Polymer = {};
}
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// copy own properties from 'api' to 'prototype, with name hinting for 'super'
function extend(prototype, api) {
if (prototype && api) {
// use only own properties of 'api'
Object.getOwnPropertyNames(api).forEach(function(n) {
// acquire property descriptor
var pd = Object.getOwnPropertyDescriptor(api, n);
if (pd) {
// clone property via descriptor
Object.defineProperty(prototype, n, pd);
// cache name-of-method for 'super' engine
if (typeof pd.value == 'function') {
// hint the 'super' engine
pd.value.nom = n;
}
}
});
}
return prototype;
}
// exports
scope.extend = extend;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// usage
// invoke cb.call(this) in 100ms, unless the job is re-registered,
// which resets the timer
//
// this.myJob = this.job(this.myJob, cb, 100)
//
// returns a job handle which can be used to re-register a job
var Job = function(inContext) {
this.context = inContext;
this.boundComplete = this.complete.bind(this)
};
Job.prototype = {
go: function(callback, wait) {
this.callback = callback;
var h;
if (!wait) {
h = requestAnimationFrame(this.boundComplete);
this.handle = function() {
cancelAnimationFrame(h);
}
} else {
h = setTimeout(this.boundComplete, wait);
this.handle = function() {
clearTimeout(h);
}
}
},
stop: function() {
if (this.handle) {
this.handle();
this.handle = null;
}
},
complete: function() {
if (this.handle) {
this.stop();
this.callback.call(this.context);
}
}
};
function job(job, callback, wait) {
if (job) {
job.stop();
} else {
job = new Job(this);
}
job.go(callback, wait);
return job;
}
// exports
scope.job = job;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var registry = {};
HTMLElement.register = function(tag, prototype) {
registry[tag] = prototype;
}
// get prototype mapped to node <tag>
HTMLElement.getPrototypeForTag = function(tag) {
var prototype = !tag ? HTMLElement.prototype : registry[tag];
// TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
return prototype || Object.getPrototypeOf(document.createElement(tag));
};
// we have to flag propagation stoppage for the event dispatcher
var originalStopPropagation = Event.prototype.stopPropagation;
Event.prototype.stopPropagation = function() {
this.cancelBubble = true;
originalStopPropagation.apply(this, arguments);
};
// TODO(sorvell): remove when we're sure imports does not need
// to load stylesheets
/*
HTMLImports.importer.preloadSelectors +=
', polymer-element link[rel=stylesheet]';
*/
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// super
// `arrayOfArgs` is an optional array of args like one might pass
// to `Function.apply`
// TODO(sjmiles):
// $super must be installed on an instance or prototype chain
// as `super`, and invoked via `this`, e.g.
// `this.super();`
// will not work if function objects are not unique, for example,
// when using mixins.
// The memoization strategy assumes each function exists on only one
// prototype chain i.e. we use the function object for memoizing)
// perhaps we can bookkeep on the prototype itself instead
function $super(arrayOfArgs) {
// since we are thunking a method call, performance is important here:
// memoize all lookups, once memoized the fast path calls no other
// functions
//
// find the caller (cannot be `strict` because of 'caller')
var caller = $super.caller;
// memoized 'name of method'
var nom = caller.nom;
// memoized next implementation prototype
var _super = caller._super;
if (!_super) {
if (!nom) {
nom = caller.nom = nameInThis.call(this, caller);
}
if (!nom) {
console.warn('called super() on a method not installed declaratively (has no .nom property)');
}
// super prototype is either cached or we have to find it
// by searching __proto__ (at the 'top')
_super = memoizeSuper(caller, nom, getPrototypeOf(this));
}
if (!_super) {
// if _super is falsey, there is no super implementation
//console.warn('called $super(' + nom + ') where there is no super implementation');
} else {
// our super function
var fn = _super[nom];
// memoize information so 'fn' can call 'super'
if (!fn._super) {
memoizeSuper(fn, nom, _super);
}
// invoke the inherited method
// if 'fn' is not function valued, this will throw
return fn.apply(this, arrayOfArgs || []);
}
}
function nextSuper(proto, name, caller) {
// look for an inherited prototype that implements name
while (proto) {
if ((proto[name] !== caller) && proto[name]) {
return proto;
}
proto = getPrototypeOf(proto);
}
}
function memoizeSuper(method, name, proto) {
// find and cache next prototype containing `name`
// we need the prototype so we can do another lookup
// from here
method._super = nextSuper(proto, name, method);
if (method._super) {
// _super is a prototype, the actual method is _super[name]
// tag super method with it's name for further lookups
method._super[name].nom = name;
}
return method._super;
}
function nameInThis(value) {
var p = this.__proto__;
while (p && p !== HTMLElement.prototype) {
// TODO(sjmiles): getOwnPropertyNames is absurdly expensive
var n$ = Object.getOwnPropertyNames(p);
for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
var d = Object.getOwnPropertyDescriptor(p, n);
if (typeof d.value === 'function' && d.value === value) {
return n;
}
}
p = p.__proto__;
}
}
// NOTE: In some platforms (IE10) the prototype chain is faked via
// __proto__. Therefore, always get prototype via __proto__ instead of
// the more standard Object.getPrototypeOf.
function getPrototypeOf(prototype) {
return prototype.__proto__;
}
// utility function to precompute name tags for functions
// in a (unchained) prototype
function hintSuper(prototype) {
// tag functions with their prototype name to optimize
// super call invocations
for (var n in prototype) {
var pd = Object.getOwnPropertyDescriptor(prototype, n);
if (pd && typeof pd.value === 'function') {
pd.value.nom = n;
}
}
}
// exports
scope.super = $super;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var typeHandlers = {
string: function(value) {
return value;
},
date: function(value) {
return new Date(Date.parse(value) || Date.now());
},
boolean: function(value) {
if (value === '') {
return true;
}
return value === 'false' ? false : !!value;
},
number: function(value) {
var n = parseFloat(value);
// hex values like "0xFFFF" parseFloat as 0
if (n === 0) {
n = parseInt(value);
}
return isNaN(n) ? value : n;
// this code disabled because encoded values (like "0xFFFF")
// do not round trip to their original format
//return (String(floatVal) === value) ? floatVal : value;
},
object: function(value, currentValue) {
if (currentValue === null) {
return value;
}
try {
// If the string is an object, we can parse is with the JSON library.
// include convenience replace for single-quotes. If the author omits
// quotes altogether, parse will fail.
return JSON.parse(value.replace(/'/g, '"'));
} catch(e) {
// The object isn't valid JSON, return the raw value
return value;
}
},
// avoid deserialization of functions
'function': function(value, currentValue) {
return currentValue;
}
};
function deserializeValue(value, currentValue) {
// attempt to infer type from default value
var inferredType = typeof currentValue;
// invent 'date' type value for Date
if (currentValue instanceof Date) {
inferredType = 'date';
}
// delegate deserialization via type string
return typeHandlers[inferredType](value, currentValue);
}
// exports
scope.deserializeValue = deserializeValue;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var extend = scope.extend;
// module
var api = {};
api.declaration = {};
api.instance = {};
api.publish = function(apis, prototype) {
for (var n in apis) {
extend(prototype, apis[n]);
}
}
// exports
scope.api = api;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var utils = {
/**
* Invokes a function asynchronously. The context of the callback
* function is bound to 'this' automatically.
* @method async
* @param {Function|String} method
* @param {any|Array} args
* @param {number} timeout
*/
async: function(method, args, timeout) {
// when polyfilling Object.observe, ensure changes
// propagate before executing the async method
Platform.flush();
// second argument to `apply` must be an array
args = (args && args.length) ? args : [args];
// function to invoke
var fn = function() {
(this[method] || method).apply(this, args);
}.bind(this);
// execute `fn` sooner or later
var handle = timeout ? setTimeout(fn, timeout) :
requestAnimationFrame(fn);
// NOTE: switch on inverting handle to determine which time is used.
return timeout ? handle : ~handle;
},
cancelAsync: function(handle) {
if (handle < 0) {
cancelAnimationFrame(~handle);
} else {
clearTimeout(handle);
}
},
/**
* Fire an event.
* @method fire
* @returns {Object} event
* @param {string} type An event name.
* @param {any} detail
* @param {Node} onNode Target node.
*/
fire: function(type, detail, onNode, bubbles, cancelable) {
var node = onNode || this;
var detail = detail || {};
var event = new CustomEvent(type, {
bubbles: (bubbles !== undefined ? bubbles : true),
cancelable: (cancelable !== undefined ? cancelable : true),
detail: detail
});
node.dispatchEvent(event);
return event;
},
/**
* Fire an event asynchronously.
* @method asyncFire
* @param {string} type An event name.
* @param detail
* @param {Node} toNode Target node.
*/
asyncFire: function(/*inType, inDetail*/) {
this.async("fire", arguments);
},
/**
* Remove class from old, add class to anew, if they exist
* @param classFollows
* @param anew A node.
* @param old A node
* @param className
*/
classFollows: function(anew, old, className) {
if (old) {
old.classList.remove(className);
}
if (anew) {
anew.classList.add(className);
}
}
};
// no-operation function for handy stubs
var nop = function() {};
// null-object for handy stubs
var nob = {};
// deprecated
utils.asyncMethod = utils.async;
// exports
scope.api.instance.utils = utils;
scope.nop = nop;
scope.nob = nob;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
var EVENT_PREFIX = 'on-';
// instance events api
var events = {
// read-only
EVENT_PREFIX: EVENT_PREFIX,
// event listeners on host
addHostListeners: function() {
var events = this.eventDelegates;
log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
// NOTE: host events look like bindings but really are not;
// (1) we don't want the attribute to be set and (2) we want to support
// multiple event listeners ('host' and 'instance') and Node.bind
// by default supports 1 thing being bound.
// We do, however, leverage the event hookup code in PolymerExpressions
// so that we have a common code path for handling declarative events.
var self = this, bindable, eventName;
for (var n in events) {
eventName = EVENT_PREFIX + n;
bindable = PolymerExpressions.prepareEventBinding(
Path.get(events[n]),
eventName,
{
resolveEventHandler: function(model, path, node) {
var fn = path.getValueFrom(self);
if (fn) {
return fn.bind(self);
}
}
}
);
bindable(this, this, false);
}
},
// call 'method' or function method on 'obj' with 'args', if the method exists
dispatchMethod: function(obj, method, args) {
if (obj) {
log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
var fn = typeof method === 'function' ? method : obj[method];
if (fn) {
fn[args ? 'apply' : 'call'](obj, args);
}
log.events && console.groupEnd();
Platform.flush();
}
}
};
// exports
scope.api.instance.events = events;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// instance api for attributes
var attributes = {
copyInstanceAttributes: function () {
var a$ = this._instanceAttributes;
for (var k in a$) {
if (!this.hasAttribute(k)) {
this.setAttribute(k, a$[k]);
}
}
},
// for each attribute on this, deserialize value to property as needed
takeAttributes: function() {
// if we have no publish lookup table, we have no attributes to take
// TODO(sjmiles): ad hoc
if (this._publishLC) {
for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
this.attributeToProperty(a.name, a.value);
}
}
},
// if attribute 'name' is mapped to a property, deserialize
// 'value' into that property
attributeToProperty: function(name, value) {
// try to match this attribute to a property (attributes are
// all lower-case, so this is case-insensitive search)
var name = this.propertyForAttribute(name);
if (name) {
// filter out 'mustached' values, these are to be
// replaced with bound-data and are not yet values
// themselves
if (value && value.search(scope.bindPattern) >= 0) {
return;
}
// get original value
var currentValue = this[name];
// deserialize Boolean or Number values from attribute
var value = this.deserializeValue(value, currentValue);
// only act if the value has changed
if (value !== currentValue) {
// install new value (has side-effects)
this[name] = value;
}
}
},
// return the published property matching name, or undefined
propertyForAttribute: function(name) {
var match = this._publishLC && this._publishLC[name];
//console.log('propertyForAttribute:', name, 'matches', match);
return match;
},
// convert representation of 'stringValue' based on type of 'currentValue'
deserializeValue: function(stringValue, currentValue) {
return scope.deserializeValue(stringValue, currentValue);
},
serializeValue: function(value, inferredType) {
if (inferredType === 'boolean') {
return value ? '' : undefined;
} else if (inferredType !== 'object' && inferredType !== 'function'
&& value !== undefined) {
return value;
}
},
reflectPropertyToAttribute: function(name) {
var inferredType = typeof this[name];
// try to intelligently serialize property value
var serializedValue = this.serializeValue(this[name], inferredType);
// boolean properties must reflect as boolean attributes
if (serializedValue !== undefined) {
this.setAttribute(name, serializedValue);
// TODO(sorvell): we should remove attr for all properties
// that have undefined serialization; however, we will need to
// refine the attr reflection system to achieve this; pica, for example,
// relies on having inferredType object properties not removed as
// attrs.
} else if (inferredType === 'boolean') {
this.removeAttribute(name);
}
}
};
// exports
scope.api.instance.attributes = attributes;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
// magic words
var OBSERVE_SUFFIX = 'Changed';
// element api
var empty = [];
var properties = {
observeProperties: function() {
var n$ = this._observeNames, pn$ = this._publishNames;
if ((n$ && n$.length) || (pn$ && pn$.length)) {
var self = this;
var o = this._propertyObserver = new CompoundObserver();
// keep track of property observer so we can shut it down
this.registerObservers([o]);
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
o.addPath(this, n);
// observer array properties
var pd = Object.getOwnPropertyDescriptor(this.__proto__, n);
if (pd && pd.value) {
this.observeArrayValue(n, pd.value, null);
}
}
for (var i=0, l=pn$.length, n; (i<l) && (n=pn$[i]); i++) {
if (!this.observe || (this.observe[n] === undefined)) {
o.addPath(this, n);
}
}
o.open(this.notifyPropertyChanges, this);
}
},
notifyPropertyChanges: function(newValues, oldValues, paths) {
var name, method, called = {};
for (var i in oldValues) {
// note: paths is of form [object, path, object, path]
name = paths[2 * i + 1];
if (this.publish[name] !== undefined) {
this.reflectPropertyToAttribute(name);
}
method = this.observe[name];
if (method) {
this.observeArrayValue(name, newValues[i], oldValues[i]);
if (!called[method]) {
called[method] = true;
// observes the value if it is an array
this.invokeMethod(method, [oldValues[i], newValues[i], arguments]);
}
}
}
},
observeArrayValue: function(name, value, old) {
// we only care if there are registered side-effects
var callbackName = this.observe[name];
if (callbackName) {
// if we are observing the previous value, stop
if (Array.isArray(old)) {
log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
this.closeNamedObserver(name + '__array');
}
// if the new value is an array, being observing it
if (Array.isArray(value)) {
log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
var observer = new ArrayObserver(value);
observer.open(function(value, old) {
this.invokeMethod(callbackName, [old]);
}, this);
this.registerNamedObserver(name + '__array', observer);
}
}
},
bindProperty: function(property, observable) {
// apply Polymer two-way reference binding
return bindProperties(this, property, observable);
},
invokeMethod: function(method, args) {
var fn = this[method] || method;
if (typeof fn === 'function') {
fn.apply(this, args);
}
},
registerObservers: function(observers) {
this._observers.push(observers);
},
// observer array items are arrays of observers.
closeObservers: function() {
for (var i=0, l=this._observers.length; i<l; i++) {
this.closeObserverArray(this._observers[i]);
}
this._observers = [];
},
closeObserverArray: function(observerArray) {
for (var i=0, l=observerArray.length, o; i<l; i++) {
o = observerArray[i];
if (o && o.close) {
o.close();
}
}
},
// bookkeeping observers for memory management
registerNamedObserver: function(name, observer) {
var o$ = this._namedObservers || (this._namedObservers = {});
o$[name] = observer;
},
closeNamedObserver: function(name) {
var o$ = this._namedObservers;
if (o$ && o$[name]) {
o$[name].close();
o$[name] = null;
return true;
}
},
closeNamedObservers: function() {
if (this._namedObservers) {
var keys=Object.keys(this._namedObservers);
for (var i=0, l=keys.length, k, o; (i < l) && (k=keys[i]); i++) {
o = this._namedObservers[k];
o.close();
}
this._namedObservers = {};
}
}
};
// property binding
// bind a property in A to a path in B by converting A[property] to a
// getter/setter pair that accesses B[...path...]
function bindProperties(inA, inProperty, observable) {
log.bind && console.log(LOG_BIND_PROPS, inB.localName || 'object', inPath, inA.localName, inProperty);
// capture A's value if B's value is null or undefined,
// otherwise use B's value
// TODO(sorvell): need to review, can do with ObserverTransform
var v = observable.discardChanges();
if (v === null || v === undefined) {
observable.setValue(inA[inProperty]);
}
return Observer.defineComputedProperty(inA, inProperty, observable);
}
// logging
var LOG_OBSERVE = '[%s] watching [%s]';
var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
var LOG_BIND_PROPS = "[%s]: bindProperties: [%s] to [%s].[%s]";
// exports
scope.api.instance.properties = properties;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || 0;
var events = scope.api.instance.events;
var syntax = new PolymerExpressions();
syntax.resolveEventHandler = function(model, path, node) {
var ctlr = findEventController(node);
if (ctlr) {
var fn = path.getValueFrom(ctlr);
if (fn) {
return fn.bind(ctlr);
}
}
}
// An event controller is the host element for the shadowRoot in which
// the node exists, or the first ancestor with a 'lightDomController'
// property.
function findEventController(node) {
while (node.parentNode) {
if (node.lightDomController) {
return node;
}
node = node.parentNode;
}
return node.host;
};
// element api supporting mdv
var mdv = {
syntax: syntax,
instanceTemplate: function(template) {
var dom = template.createInstance(this, this.syntax);
this.registerObservers(dom.bindings_);
return dom;
},
bind: function(name, observable, oneTime) {
var property = this.propertyForAttribute(name);
if (!property) {
// TODO(sjmiles): this mixin method must use the special form
// of `super` installed by `mixinMethod` in declaration/prototype.js
return this.mixinSuper(arguments);
} else {
// use n-way Polymer binding
var observer = this.bindProperty(property, observable);
this.reflectPropertyToAttribute(property);
// NOTE: reflecting binding information is typically required only for
// tooling. It has a performance cost so it's opt-in in Node.bind.
if (Platform.enableBindingsReflection) {
observer.path = observable.path_;
this.bindings_ = this.bindings_ || {};
this.bindings_[name] = observer;
}
return observer;
}
},
// TODO(sorvell): unbind/unbindAll has been removed, as public api, from
// TemplateBinding. We still need to close/dispose of observers but perhaps
// we should choose a more explicit name.
asyncUnbindAll: function() {
if (!this._unbound) {
log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
}
},
unbindAll: function() {
if (!this._unbound) {
this.closeObservers();
this.closeNamedObservers();
this._unbound = true;
}
},
cancelUnbindAll: function() {
if (this._unbound) {
log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
return;
}
log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
if (this._unbindAllJob) {
this._unbindAllJob = this._unbindAllJob.stop();
}
}
};
function unbindNodeTree(node) {
forNodeTree(node, _nodeUnbindAll);
}
function _nodeUnbindAll(node) {
node.unbindAll();
}
function forNodeTree(node, callback) {
if (node) {
callback(node);
for (var child = node.firstChild; child; child = child.nextSibling) {
forNodeTree(child, callback);
}
}
}
var mustachePattern = /\{\{([^{}]*)}}/;
// exports
scope.bindPattern = mustachePattern;
scope.api.instance.mdv = mdv;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var base = {
PolymerBase: true,
job: function(job, callback, wait) {
if (typeof job === 'string') {
var n = '___' + job;
this[n] = Polymer.job.call(this, this[n], callback, wait);
} else {
return Polymer.job.call(this, job, callback, wait);
}
},
super: Polymer.super,
// user entry point for element has had its createdCallback called
created: function() {
},
// user entry point for element has shadowRoot and is ready for
// api interaction
ready: function() {
},
createdCallback: function() {
if (this.templateInstance && this.templateInstance.model) {
console.warn('Attributes on ' + this.localName + ' were data bound ' +
'prior to Polymer upgrading the element. This may result in ' +
'incorrect binding types.');
}
this.created();
this.prepareElement();
},
// system entry point, do not override
prepareElement: function() {
this._elementPrepared = true;
// install shadowRoots storage
this.shadowRoots = {};
// storage for closeable observers.
this._observers = [];
// install property observers
this.observeProperties();
// install boilerplate attributes
this.copyInstanceAttributes();
// process input attributes
this.takeAttributes();
// add event listeners
this.addHostListeners();
// process declarative resources
this.parseDeclarations(this.__proto__);
// TODO(sorvell): CE polyfill uses unresolved attribute to simulate
// :unresolved; remove this attribute to be compatible with native
// CE.
this.removeAttribute('unresolved');
// user entry point
this.ready();
},
attachedCallback: function() {
this.cancelUnbindAll();
// invoke user action
if (this.attached) {
this.attached();
}
// TODO(sorvell): bc
if (this.enteredView) {
this.enteredView();
}
// NOTE: domReady can be used to access elements in dom (descendants,
// ancestors, siblings) such that the developer is enured to upgrade
// ordering. If the element definitions have loaded, domReady
// can be used to access upgraded elements.
if (!this.hasBeenAttached) {
this.hasBeenAttached = true;
if (this.domReady) {
this.async('domReady');
}
}
},
detachedCallback: function() {
if (!this.preventDispose) {
this.asyncUnbindAll();
}
// invoke user action
if (this.detached) {
this.detached();
}
// TODO(sorvell): bc
if (this.leftView) {
this.leftView();
}
},
// TODO(sorvell): bc
enteredViewCallback: function() {
this.attachedCallback();
},
// TODO(sorvell): bc
leftViewCallback: function() {
this.detachedCallback();
},
// TODO(sorvell): bc
enteredDocumentCallback: function() {
this.attachedCallback();
},
// TODO(sorvell): bc
leftDocumentCallback: function() {
this.detachedCallback();
},
// recursive ancestral <element> initialization, oldest first
parseDeclarations: function(p) {
if (p && p.element) {
this.parseDeclarations(p.__proto__);
p.parseDeclaration.call(this, p.element);
}
},
// parse input <element> as needed, override for custom behavior
parseDeclaration: function(elementElement) {
var template = this.fetchTemplate(elementElement);
if (template) {
var root = this.shadowFromTemplate(template);
this.shadowRoots[elementElement.name] = root;
}
},
// return a shadow-root template (if desired), override for custom behavior
fetchTemplate: function(elementElement) {
return elementElement.querySelector('template');
},
// utility function that creates a shadow root from a <template>
shadowFromTemplate: function(template) {
if (template) {
// make a shadow root
var root = this.createShadowRoot();
// stamp template
// which includes parsing and applying MDV bindings before being
// inserted (to avoid {{}} in attribute values)
// e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
var dom = this.instanceTemplate(template);
// append to shadow dom
root.appendChild(dom);
// perform post-construction initialization tasks on shadow root
this.shadowRootReady(root, template);
// return the created shadow root
return root;
}
},
// utility function that stamps a <template> into light-dom
lightFromTemplate: function(template, refNode) {
if (template) {
// TODO(sorvell): mark this element as a lightDOMController so that
// event listeners on bound nodes inside it will be called on it.
// Note, the expectation here is that events on all descendants
// should be handled by this element.
this.lightDomController = true;
// stamp template
// which includes parsing and applying MDV bindings before being
// inserted (to avoid {{}} in attribute values)
// e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
var dom = this.instanceTemplate(template);
// append to shadow dom
if (refNode) {
this.insertBefore(dom, refNode);
} else {
this.appendChild(dom);
}
// perform post-construction initialization tasks on ahem, light root
this.shadowRootReady(this);
// return the created shadow root
return dom;
}
},
shadowRootReady: function(root) {
// locate nodes with id and store references to them in this.$ hash
this.marshalNodeReferences(root);
// set up pointer gestures
PointerGestures.register(root);
},
// locate nodes with id and store references to them in this.$ hash
marshalNodeReferences: function(root) {
// establish $ instance variable
var $ = this.$ = this.$ || {};
// populate $ from nodes with ID from the LOCAL tree
if (root) {
var n$ = root.querySelectorAll("[id]");
for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
$[n.id] = n;
};
}
},
attributeChangedCallback: function(name, oldValue) {
// TODO(sjmiles): adhoc filter
if (name !== 'class' && name !== 'style') {
this.attributeToProperty(name, this.getAttribute(name));
}
if (this.attributeChanged) {
this.attributeChanged.apply(this, arguments);
}
},
onMutation: function(node, listener) {
var observer = new MutationObserver(function(mutations) {
listener.call(this, observer, mutations);
observer.disconnect();
}.bind(this));
observer.observe(node, {childList: true, subtree: true});
}
};
// true if object has own PolymerBase api
function isBase(object) {
return object.hasOwnProperty('PolymerBase')
}
// name a base constructor for dev tools
function PolymerBase() {};
PolymerBase.prototype = base;
base.constructor = PolymerBase;
// exports
scope.Base = PolymerBase;
scope.isBase = isBase;
scope.api.instance.base = base;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
// magic words
var STYLE_SCOPE_ATTRIBUTE = 'element';
var STYLE_CONTROLLER_SCOPE = 'controller';
var styles = {
STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
/**
* Installs external stylesheets and <style> elements with the attribute
* polymer-scope='controller' into the scope of element. This is intended
* to be a called during custom element construction.
*/
installControllerStyles: function() {
// apply controller styles, but only if they are not yet applied
var scope = this.findStyleScope();
if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
// allow inherited controller styles
var proto = getPrototypeOf(this), cssText = '';
while (proto && proto.element) {
cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
proto = getPrototypeOf(proto);
}
if (cssText) {
this.installScopeCssText(cssText, scope);
}
}
},
installScopeStyle: function(style, name, scope) {
var scope = scope || this.findStyleScope(), name = name || '';
if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
var cssText = '';
if (style instanceof Array) {
for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
cssText += s.textContent + '\n\n';
}
} else {
cssText = style.textContent;
}
this.installScopeCssText(cssText, scope, name);
}
},
installScopeCssText: function(cssText, scope, name) {
scope = scope || this.findStyleScope();
name = name || '';
if (!scope) {
return;
}
if (window.ShadowDOMPolyfill) {
cssText = shimCssText(cssText, scope.host);
}
var style = this.element.cssTextToScopeStyle(cssText,
STYLE_CONTROLLER_SCOPE);
Polymer.applyStyleToScope(style, scope);
// cache that this style has been applied
scope._scopeStyles[this.localName + name] = true;
},
findStyleScope: function(node) {
// find the shadow root that contains this element
var n = node || this;
while (n.parentNode) {
n = n.parentNode;
}
return n;
},
scopeHasNamedStyle: function(scope, name) {
scope._scopeStyles = scope._scopeStyles || {};
return scope._scopeStyles[name];
}
};
// NOTE: use raw prototype traversal so that we ensure correct traversal
// on platforms where the protoype chain is simulated via __proto__ (IE10)
function getPrototypeOf(prototype) {
return prototype.__proto__;
}
function shimCssText(cssText, host) {
var name = '', is = false;
if (host) {
name = host.localName;
is = host.hasAttribute('is');
}
var selector = Platform.ShadowCSS.makeScopeSelector(name, is);
return Platform.ShadowCSS.shimCssText(cssText, selector);
}
// exports
scope.api.instance.styles = styles;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var extend = scope.extend;
var api = scope.api;
// imperative implementation: Polymer()
// specify an 'own' prototype for tag `name`
function element(name, prototype) {
if (arguments.length === 1 && typeof arguments[0] !== 'string') {
prototype = name;
var script = document._currentScript;
name = script && script.parentNode && script.parentNode.getAttribute ?
script.parentNode.getAttribute('name') : '';
if (!name) {
throw 'Element name could not be inferred.';
}
}
if (getRegisteredPrototype[name]) {
throw 'Already registered (Polymer) prototype for element ' + name;
}
// cache the prototype
registerPrototype(name, prototype);
// notify the registrar waiting for 'name', if any
notifyPrototype(name);
}
// async prototype source
function waitingForPrototype(name, client) {
waitPrototype[name] = client;
}
var waitPrototype = {};
function notifyPrototype(name) {
if (waitPrototype[name]) {
waitPrototype[name].registerWhenReady();
delete waitPrototype[name];
}
}
// utility and bookkeeping
// maps tag names to prototypes, as registered with
// Polymer. Prototypes associated with a tag name
// using document.registerElement are available from
// HTMLElement.getPrototypeForTag().
// If an element was fully registered by Polymer, then
// Polymer.getRegisteredPrototype(name) ===
// HTMLElement.getPrototypeForTag(name)
var prototypesByName = {};
function registerPrototype(name, prototype) {
return prototypesByName[name] = prototype || {};
}
function getRegisteredPrototype(name) {
return prototypesByName[name];
}
// exports
scope.getRegisteredPrototype = getRegisteredPrototype;
scope.waitingForPrototype = waitingForPrototype;
// namespace shenanigans so we can expose our scope on the registration
// function
// make window.Polymer reference `element()`
window.Polymer = element;
// TODO(sjmiles): find a way to do this that is less terrible
// copy window.Polymer properties onto `element()`
extend(Polymer, scope);
// Under the HTMLImports polyfill, scripts in the main document
// do not block on imports; we want to allow calls to Polymer in the main
// document. Platform collects those calls until we can process them, which
// we do here.
var declarations = Platform.deliverDeclarations();
if (declarations) {
for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
element.apply(null, d);
}
}
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var path = {
resolveElementPaths: function(node) {
Platform.urlResolver.resolveDom(node);
},
addResolvePathApi: function() {
// let assetpath attribute modify the resolve path
var assetPath = this.getAttribute('assetpath') || '';
var root = new URL(assetPath, this.ownerDocument.baseURI);
this.prototype.resolvePath = function(urlPath, base) {
var u = new URL(urlPath, base || root);
return u.href;
};
}
};
// exports
scope.api.declaration.path = path;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
var api = scope.api.instance.styles;
var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
// magic words
var STYLE_SELECTOR = 'style';
var STYLE_LOADABLE_MATCH = '@import';
var SHEET_SELECTOR = 'link[rel=stylesheet]';
var STYLE_GLOBAL_SCOPE = 'global';
var SCOPE_ATTR = 'polymer-scope';
var styles = {
// returns true if resources are loading
loadStyles: function(callback) {
var content = this.templateContent();
if (content) {
this.convertSheetsToStyles(content);
}
var styles = this.findLoadableStyles(content);
if (styles.length) {
Platform.styleResolver.loadStyles(styles, callback);
} else if (callback) {
callback();
}
},
convertSheetsToStyles: function(root) {
var s$ = root.querySelectorAll(SHEET_SELECTOR);
for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
this.ownerDocument);
this.copySheetAttributes(c, s);
s.parentNode.replaceChild(c, s);
}
},
copySheetAttributes: function(style, link) {
for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
if (a.name !== 'rel' && a.name !== 'href') {
style.setAttribute(a.name, a.value);
}
}
},
findLoadableStyles: function(root) {
var loadables = [];
if (root) {
var s$ = root.querySelectorAll(STYLE_SELECTOR);
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
loadables.push(s);
}
}
}
return loadables;
},
/**
* Install external stylesheets loaded in <polymer-element> elements into the
* element's template.
* @param elementElement The <element> element to style.
*/
installSheets: function() {
this.cacheSheets();
this.cacheStyles();
this.installLocalSheets();
this.installGlobalStyles();
},
/**
* Remove all sheets from element and store for later use.
*/
cacheSheets: function() {
this.sheets = this.findNodes(SHEET_SELECTOR);
this.sheets.forEach(function(s) {
if (s.parentNode) {
s.parentNode.removeChild(s);
}
});
},
cacheStyles: function() {
this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
this.styles.forEach(function(s) {
if (s.parentNode) {
s.parentNode.removeChild(s);
}
});
},
/**
* Takes external stylesheets loaded in an <element> element and moves
* their content into a <style> element inside the <element>'s template.
* The sheet is then removed from the <element>. This is done only so
* that if the element is loaded in the main document, the sheet does
* not become active.
* Note, ignores sheets with the attribute 'polymer-scope'.
* @param elementElement The <element> element to style.
*/
installLocalSheets: function () {
var sheets = this.sheets.filter(function(s) {
return !s.hasAttribute(SCOPE_ATTR);
});
var content = this.templateContent();
if (content) {
var cssText = '';
sheets.forEach(function(sheet) {
cssText += cssTextFromSheet(sheet) + '\n';
});
if (cssText) {
var style = createStyleElement(cssText, this.ownerDocument);
content.insertBefore(style, content.firstChild);
}
}
},
findNodes: function(selector, matcher) {
var nodes = this.querySelectorAll(selector).array();
var content = this.templateContent();
if (content) {
var templateNodes = content.querySelectorAll(selector).array();
nodes = nodes.concat(templateNodes);
}
return matcher ? nodes.filter(matcher) : nodes;
},
templateContent: function() {
var template = this.querySelector('template');
return template && templateContent(template);
},
/**
* Promotes external stylesheets and <style> elements with the attribute
* polymer-scope='global' into global scope.
* This is particularly useful for defining @keyframe rules which
* currently do not function in scoped or shadow style elements.
* (See wkb.ug/72462)
* @param elementElement The <element> element to style.
*/
// TODO(sorvell): remove when wkb.ug/72462 is addressed.
installGlobalStyles: function() {
var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
applyStyleToScope(style, document.head);
},
cssTextForScope: function(scopeDescriptor) {
var cssText = '';
// handle stylesheets
var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
var matcher = function(s) {
return matchesSelector(s, selector);
};
var sheets = this.sheets.filter(matcher);
sheets.forEach(function(sheet) {
cssText += cssTextFromSheet(sheet) + '\n\n';
});
// handle cached style elements
var styles = this.styles.filter(matcher);
styles.forEach(function(style) {
cssText += style.textContent + '\n\n';
});
return cssText;
},
styleForScope: function(scopeDescriptor) {
var cssText = this.cssTextForScope(scopeDescriptor);
return this.cssTextToScopeStyle(cssText, scopeDescriptor);
},
cssTextToScopeStyle: function(cssText, scopeDescriptor) {
if (cssText) {
var style = createStyleElement(cssText);
style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
'-' + scopeDescriptor);
return style;
}
}
};
function importRuleForSheet(sheet, baseUrl) {
var href = new URL(sheet.getAttribute('href'), baseUrl).href;
return '@import \'' + href + '\';';
}
function applyStyleToScope(style, scope) {
if (style) {
if (scope === document) {
scope = document.head;
}
if (window.ShadowDOMPolyfill) {
scope = document.head;
}
// TODO(sorvell): necessary for IE
// see https://connect.microsoft.com/IE/feedback/details/790212/
// cloning-a-style-element-and-adding-to-document-produces
// -unexpected-result#details
// var clone = style.cloneNode(true);
var clone = createStyleElement(style.textContent);
var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
if (attr) {
clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
}
// TODO(sorvell): probably too brittle; try to figure out
// where to put the element.
var refNode = scope.firstElementChild;
if (scope === document.head) {
var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
var s$ = document.head.querySelectorAll(selector);
if (s$.length) {
refNode = s$[s$.length-1].nextElementSibling;
}
}
scope.insertBefore(clone, refNode);
}
}
function createStyleElement(cssText, scope) {
scope = scope || document;
scope = scope.createElement ? scope : scope.ownerDocument;
var style = scope.createElement('style');
style.textContent = cssText;
return style;
}
function cssTextFromSheet(sheet) {
return (sheet && sheet.__resource) || '';
}
function matchesSelector(node, inSelector) {
if (matches) {
return matches.call(node, inSelector);
}
}
var p = HTMLElement.prototype;
var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector
|| p.mozMatchesSelector;
// exports
scope.api.declaration.styles = styles;
scope.applyStyleToScope = applyStyleToScope;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var log = window.logFlags || {};
var api = scope.api.instance.events;
var EVENT_PREFIX = api.EVENT_PREFIX;
// polymer-element declarative api: events feature
var events = {
parseHostEvents: function() {
// our delegates map
var delegates = this.prototype.eventDelegates;
// extract data from attributes into delegates
this.addAttributeDelegates(delegates);
},
addAttributeDelegates: function(delegates) {
// for each attribute
for (var i=0, a; a=this.attributes[i]; i++) {
// does it have magic marker identifying it as an event delegate?
if (this.hasEventPrefix(a.name)) {
// if so, add the info to delegates
delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
.replace('}}', '').trim();
}
}
},
// starts with 'on-'
hasEventPrefix: function (n) {
return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
},
removeEventPrefix: function(n) {
return n.slice(prefixLength);
}
};
var prefixLength = EVENT_PREFIX.length;
// exports
scope.api.declaration.events = events;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// element api
var properties = {
inferObservers: function(prototype) {
// called before prototype.observe is chained to inherited object
var observe = prototype.observe, property;
for (var n in prototype) {
if (n.slice(-7) === 'Changed') {
if (!observe) {
observe = (prototype.observe = {});
}
property = n.slice(0, -7)
observe[property] = observe[property] || n;
}
}
},
explodeObservers: function(prototype) {
// called before prototype.observe is chained to inherited object
var o = prototype.observe;
if (o) {
var exploded = {};
for (var n in o) {
var names = n.split(' ');
for (var i=0, ni; ni=names[i]; i++) {
exploded[ni] = o[n];
}
}
prototype.observe = exploded;
}
},
optimizePropertyMaps: function(prototype) {
if (prototype.observe) {
// construct name list
var a = prototype._observeNames = [];
for (var n in prototype.observe) {
var names = n.split(' ');
for (var i=0, ni; ni=names[i]; i++) {
a.push(ni);
}
}
}
if (prototype.publish) {
// construct name list
var a = prototype._publishNames = [];
for (var n in prototype.publish) {
a.push(n);
}
}
},
publishProperties: function(prototype, base) {
// if we have any properties to publish
var publish = prototype.publish;
if (publish) {
// transcribe `publish` entries onto own prototype
this.requireProperties(publish, prototype, base);
// construct map of lower-cased property names
prototype._publishLC = this.lowerCaseMap(publish);
}
},
requireProperties: function(properties, prototype, base) {
// ensure a prototype value for each property
for (var n in properties) {
if (prototype[n] === undefined && base[n] === undefined) {
prototype[n] = properties[n];
}
}
},
lowerCaseMap: function(properties) {
var map = {};
for (var n in properties) {
map[n.toLowerCase()] = n;
}
return map;
}
};
// exports
scope.api.declaration.properties = properties;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// magic words
var ATTRIBUTES_ATTRIBUTE = 'attributes';
var ATTRIBUTES_REGEX = /\s|,/;
// attributes api
var attributes = {
inheritAttributesObjects: function(prototype) {
// chain our lower-cased publish map to the inherited version
this.inheritObject(prototype, 'publishLC');
// chain our instance attributes map to the inherited version
this.inheritObject(prototype, '_instanceAttributes');
},
publishAttributes: function(prototype, base) {
// merge names from 'attributes' attribute
var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
if (attributes) {
// get properties to publish
var publish = prototype.publish || (prototype.publish = {});
// names='a b c' or names='a,b,c'
var names = attributes.split(ATTRIBUTES_REGEX);
// record each name for publishing
for (var i=0, l=names.length, n; i<l; i++) {
// remove excess ws
n = names[i].trim();
// do not override explicit entries
if (n && publish[n] === undefined && base[n] === undefined) {
publish[n] = null;
}
}
}
},
// record clonable attributes from <element>
accumulateInstanceAttributes: function() {
// inherit instance attributes
var clonable = this.prototype._instanceAttributes;
// merge attributes from element
var a$ = this.attributes;
for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {
if (this.isInstanceAttribute(a.name)) {
clonable[a.name] = a.value;
}
}
},
isInstanceAttribute: function(name) {
return !this.blackList[name] && name.slice(0,3) !== 'on-';
},
// do not clone these attributes onto instances
blackList: {
name: 1,
'extends': 1,
constructor: 1,
noscript: 1,
assetpath: 1,
'cache-csstext': 1
}
};
// add ATTRIBUTES_ATTRIBUTE to the blacklist
attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
// exports
scope.api.declaration.attributes = attributes;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var api = scope.api;
var isBase = scope.isBase;
var extend = scope.extend;
// prototype api
var prototype = {
register: function(name, extendeeName) {
// build prototype combining extendee, Polymer base, and named api
this.buildPrototype(name, extendeeName);
// register our custom element with the platform
this.registerPrototype(name, extendeeName);
// reference constructor in a global named by 'constructor' attribute
this.publishConstructor();
},
buildPrototype: function(name, extendeeName) {
// get our custom prototype (before chaining)
var extension = scope.getRegisteredPrototype(name);
// get basal prototype
var base = this.generateBasePrototype(extendeeName);
// implement declarative features
this.desugarBeforeChaining(extension, base);
// join prototypes
this.prototype = this.chainPrototypes(extension, base);
// more declarative features
this.desugarAfterChaining(name, extendeeName);
},
desugarBeforeChaining: function(prototype, base) {
// back reference declaration element
// TODO(sjmiles): replace `element` with `elementElement` or `declaration`
prototype.element = this;
// transcribe `attributes` declarations onto own prototype's `publish`
this.publishAttributes(prototype, base);
// `publish` properties to the prototype and to attribute watch
this.publishProperties(prototype, base);
// infer observers for `observe` list based on method names
this.inferObservers(prototype);
// desugar compound observer syntax, e.g. 'a b c'
this.explodeObservers(prototype);
},
chainPrototypes: function(prototype, base) {
// chain various meta-data objects to inherited versions
this.inheritMetaData(prototype, base);
// chain custom api to inherited
var chained = this.chainObject(prototype, base);
// x-platform fixup
ensurePrototypeTraversal(chained);
return chained;
},
inheritMetaData: function(prototype, base) {
// chain observe object to inherited
this.inheritObject('observe', prototype, base);
// chain publish object to inherited
this.inheritObject('publish', prototype, base);
// chain our lower-cased publish map to the inherited version
this.inheritObject('_publishLC', prototype, base);
// chain our instance attributes map to the inherited version
this.inheritObject('_instanceAttributes', prototype, base);
// chain our event delegates map to the inherited version
this.inheritObject('eventDelegates', prototype, base);
},
// implement various declarative features
desugarAfterChaining: function(name, extendee) {
// build side-chained lists to optimize iterations
this.optimizePropertyMaps(this.prototype);
// install external stylesheets as if they are inline
this.installSheets();
// adjust any paths in dom from imports
this.resolveElementPaths(this);
// compile list of attributes to copy to instances
this.accumulateInstanceAttributes();
// parse on-* delegates declared on `this` element
this.parseHostEvents();
//
// install a helper method this.resolvePath to aid in
// setting resource urls. e.g.
// this.$.image.src = this.resolvePath('images/foo.png')
this.addResolvePathApi();
// under ShadowDOMPolyfill, transforms to approximate missing CSS features
if (window.ShadowDOMPolyfill) {
Platform.ShadowCSS.shimStyling(this.templateContent(), name, extendee);
}
// allow custom element access to the declarative context
if (this.prototype.registerCallback) {
this.prototype.registerCallback(this);
}
},
// if a named constructor is requested in element, map a reference
// to the constructor to the given symbol
publishConstructor: function() {
var symbol = this.getAttribute('constructor');
if (symbol) {
window[symbol] = this.ctor;
}
},
// build prototype combining extendee, Polymer base, and named api
generateBasePrototype: function(extnds) {
var prototype = this.findBasePrototype(extnds);
if (!prototype) {
// create a prototype based on tag-name extension
var prototype = HTMLElement.getPrototypeForTag(extnds);
// insert base api in inheritance chain (if needed)
prototype = this.ensureBaseApi(prototype);
// memoize this base
memoizedBases[extnds] = prototype;
}
return prototype;
},
findBasePrototype: function(name) {
return memoizedBases[name];
},
// install Polymer instance api into prototype chain, as needed
ensureBaseApi: function(prototype) {
if (prototype.PolymerBase) {
return prototype;
}
var extended = Object.create(prototype);
// we need a unique copy of base api for each base prototype
// therefore we 'extend' here instead of simply chaining
api.publish(api.instance, extended);
// TODO(sjmiles): sharing methods across prototype chains is
// not supported by 'super' implementation which optimizes
// by memoizing prototype relationships.
// Probably we should have a version of 'extend' that is
// share-aware: it could study the text of each function,
// look for usage of 'super', and wrap those functions in
// closures.
// As of now, there is only one problematic method, so
// we just patch it manually.
// To avoid re-entrancy problems, the special super method
// installed is called `mixinSuper` and the mixin method
// must use this method instead of the default `super`.
this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
// return buffed-up prototype
return extended;
},
mixinMethod: function(extended, prototype, api, name) {
var $super = function(args) {
return prototype[name].apply(this, args);
};
extended[name] = function() {
this.mixinSuper = $super;
return api[name].apply(this, arguments);
}
},
// ensure prototype[name] inherits from a prototype.prototype[name]
inheritObject: function(name, prototype, base) {
// require an object
var source = prototype[name] || {};
// chain inherited properties onto a new object
prototype[name] = this.chainObject(source, base[name]);
},
// register 'prototype' to custom element 'name', store constructor
registerPrototype: function(name, extendee) {
var info = {
prototype: this.prototype
}
// native element must be specified in extends
var typeExtension = this.findTypeExtension(extendee);
if (typeExtension) {
info.extends = typeExtension;
}
// register the prototype with HTMLElement for name lookup
HTMLElement.register(name, this.prototype);
// register the custom type
this.ctor = document.registerElement(name, info);
},
findTypeExtension: function(name) {
if (name && name.indexOf('-') < 0) {
return name;
} else {
var p = this.findBasePrototype(name);
if (p.element) {
return this.findTypeExtension(p.element.extends);
}
}
}
};
// memoize base prototypes
var memoizedBases = {};
// implementation of 'chainObject' depends on support for __proto__
if (Object.__proto__) {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
object.__proto__ = inherited;
}
return object;
}
} else {
prototype.chainObject = function(object, inherited) {
if (object && inherited && object !== inherited) {
var chained = Object.create(inherited);
object = extend(chained, object);
}
return object;
}
}
// On platforms that do not support __proto__ (versions of IE), the prototype
// chain of a custom element is simulated via installation of __proto__.
// Although custom elements manages this, we install it here so it's
// available during desugaring.
function ensurePrototypeTraversal(prototype) {
if (!Object.__proto__) {
var ancestor = Object.getPrototypeOf(prototype);
prototype.__proto__ = ancestor;
if (isBase(ancestor)) {
ancestor.__proto__ = Object.getPrototypeOf(ancestor);
}
}
}
// exports
api.declaration.prototype = prototype;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var queue = {
// tell the queue to wait for an element to be ready
wait: function(element, check, go) {
if (this.indexOf(element) === -1) {
this.add(element);
element.__check = check;
element.__go = go;
}
return (this.indexOf(element) !== 0);
},
add: function(element) {
//console.log('queueing', element.name);
queueForElement(element).push(element);
},
indexOf: function(element) {
var i = queueForElement(element).indexOf(element);
if (i >= 0 && document.contains(element)) {
i += (HTMLImports.useNative || HTMLImports.ready) ?
importQueue.length : 1e9;
}
return i;
},
// tell the queue an element is ready to be registered
go: function(element) {
var readied = this.remove(element);
if (readied) {
readied.__go.call(readied);
readied.__check = readied.__go = null;
this.check();
}
},
remove: function(element) {
var i = this.indexOf(element);
if (i !== 0) {
//console.warn('queue order wrong', i);
return;
}
return queueForElement(element).shift();
},
check: function() {
// next
var element = this.nextElement();
if (element) {
element.__check.call(element);
}
if (this.canReady()) {
this.ready();
return true;
}
},
nextElement: function() {
return nextQueued();
},
canReady: function() {
return !this.waitToReady && this.isEmpty();
},
isEmpty: function() {
return !importQueue.length && !mainQueue.length;
},
ready: function() {
// TODO(sorvell): As an optimization, turn off CE polyfill upgrading
// while registering. This way we avoid having to upgrade each document
// piecemeal per registration and can instead register all elements
// and upgrade once in a batch. Without this optimization, upgrade time
// degrades significantly when SD polyfill is used. This is mainly because
// querying the document tree for elements is slow under the SD polyfill.
if (CustomElements.ready === false) {
CustomElements.upgradeDocumentTree(document);
CustomElements.ready = true;
}
if (readyCallbacks) {
var fn;
while (readyCallbacks.length) {
fn = readyCallbacks.shift();
fn();
}
}
},
addReadyCallback: function(callback) {
if (callback) {
readyCallbacks.push(callback);
}
},
waitToReady: true
};
var importQueue = [];
var mainQueue = [];
var readyCallbacks = [];
function queueForElement(element) {
return document.contains(element) ? mainQueue : importQueue;
}
function nextQueued() {
return importQueue.length ? importQueue[0] : mainQueue[0];
}
var polymerReadied = false;
document.addEventListener('WebComponentsReady', function() {
CustomElements.ready = false;
});
function whenPolymerReady(callback) {
queue.waitToReady = true;
CustomElements.ready = false;
HTMLImports.whenImportsReady(function() {
queue.addReadyCallback(callback);
queue.waitToReady = false;
queue.check();
});
}
// exports
scope.queue = queue;
scope.whenPolymerReady = whenPolymerReady;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
var whenPolymerReady = scope.whenPolymerReady;
function importElements(elementOrFragment, callback) {
if (elementOrFragment) {
document.head.appendChild(elementOrFragment);
whenPolymerReady(callback);
} else if (callback) {
callback();
}
}
function importUrls(urls, callback) {
if (urls && urls.length) {
var frag = document.createDocumentFragment();
for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
link = document.createElement('link');
link.rel = 'import';
link.href = url;
frag.appendChild(link);
}
importElements(frag, callback);
} else if (callback) {
callback();
}
}
// exports
scope.import = importUrls;
scope.importElements = importElements;
})(Polymer);
/*
* Copyright 2013 The Polymer Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
(function(scope) {
// imports
var extend = scope.extend;
var api = scope.api;
var queue = scope.queue;
var whenPolymerReady = scope.whenPolymerReady;
var getRegisteredPrototype = scope.getRegisteredPrototype;
var waitingForPrototype = scope.waitingForPrototype;
// declarative implementation: <polymer-element>
var prototype = extend(Object.create(HTMLElement.prototype), {
createdCallback: function() {
if (this.getAttribute('name')) {
this.init();
}
},
init: function() {
// fetch declared values
this.name = this.getAttribute('name');
this.extends = this.getAttribute('extends');
// initiate any async resource fetches
this.loadResources();
// register when all constraints are met
this.registerWhenReady();
},
registerWhenReady: function() {
if (this.registered
|| this.waitingForPrototype(this.name)
|| this.waitingForQueue()
|| this.waitingForResources()) {
return;
}
// TODO(sorvell): ends up calling '_register' by virtue
// of `waitingForQueue` (see below)
queue.go(this);
},
// TODO(sorvell): refactor, this method is private-ish, but it's being
// called by the queue object.
_register: function() {
//console.log('registering', this.name);
//console.group('registering', this.name);
// warn if extending from a custom element not registered via Polymer
if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
console.warn('%s is attempting to extend %s, an unregistered element ' +
'or one that was not registered with Polymer.', this.name,
this.extends);
}
this.register(this.name, this.extends);
this.registered = true;
//console.groupEnd();
},
waitingForPrototype: function(name) {
if (!getRegisteredPrototype(name)) {
// then wait for a prototype
waitingForPrototype(name, this);
// emulate script if user is not supplying one
this.handleNoScript(name);
// prototype not ready yet
return true;
}
},
handleNoScript: function(name) {
// if explicitly marked as 'noscript'
if (this.hasAttribute('noscript') && !this.noscript) {
this.noscript = true;
// TODO(sorvell): CustomElements polyfill awareness:
// noscript elements should upgrade in logical order
// script injection ensures this under native custom elements;
// under imports + ce polyfills, scripts run before upgrades.
// dependencies should be ready at upgrade time so register
// prototype at this time.
if (window.CustomElements && !CustomElements.useNative) {
Polymer(name);
} else {
var script = document.createElement('script');
script.textContent = 'Polymer(\'' + name + '\');';
this.appendChild(script);
}
}
},
waitingForResources: function() {
return this._needsResources;
},
// NOTE: Elements must be queued in proper order for inheritance/composition
// dependency resolution. Previously this was enforced for inheritance,
// and by rule for composition. It's now entirely by rule.
waitingForQueue: function() {
return queue.wait(this, this.registerWhenReady, this._register);
},
loadResources: function() {
this._needsResources = true;
this.loadStyles(function() {
this._needsResources = false;
this.registerWhenReady();
}.bind(this));
}
});
// semi-pluggable APIs
// TODO(sjmiles): should be fully pluggable (aka decoupled, currently
// the various plugins are allowed to depend on each other directly)
api.publish(api.declaration, prototype);
// utility and bookkeeping
function isRegistered(name) {
return Boolean(HTMLElement.getPrototypeForTag(name));
}
function isCustomTag(name) {
return (name && name.indexOf('-') >= 0);
}
// exports
scope.getRegisteredPrototype = getRegisteredPrototype;
// boot tasks
whenPolymerReady(function() {
document.body.removeAttribute('unresolved');
document.dispatchEvent(
new CustomEvent('polymer-ready', {bubbles: true})
);
});
// register polymer-element with document
document.registerElement('polymer-element', {prototype: prototype});
})(Polymer);
//# sourceMappingURL=polymer.concat.js.map