(function() { // Ensure that the `unresolved` attribute added by the WebComponents polyfills // is removed. This is done as a convenience so users don't have to remember // to do so themselves. This attribute provides FOUC prevention when // native Custom Elements is not available. function resolve() { document.body.removeAttribute('unresolved'); } if (window.WebComponents) { addEventListener('WebComponentsReady', resolve); } else { if (document.readyState === 'interactive' || document.readyState === 'complete') { resolve(); } else { addEventListener('DOMContentLoaded', resolve); } } })(); window.Polymer = { Settings: (function() { // NOTE: Users must currently opt into using ShadowDOM. They do so by doing: // Polymer = {dom: 'shadow'}; // TODO(sorvell): Decide if this should be auto-use when available. // TODO(sorvell): if SD is auto-use, then the flag above should be something // like: Polymer = {dom: 'shady'} // via Polymer object var settings = window.Polymer || {}; // via url var parts = location.search.slice(1).split('&'); for (var i=0, o; (i < parts.length) && (o=parts[i]); i++) { o = o.split('='); o[0] && (settings[o[0]] = o[1] || true); } settings.wantShadow = (settings.dom === 'shadow'); settings.hasShadow = Boolean(Element.prototype.createShadowRoot); settings.nativeShadow = settings.hasShadow && !window.ShadowDOMPolyfill; settings.useShadow = settings.wantShadow && settings.hasShadow; settings.hasNativeImports = Boolean('import' in document.createElement('link')); settings.useNativeImports = settings.hasNativeImports; settings.useNativeCustomElements = (!window.CustomElements || window.CustomElements.useNative); settings.useNativeShadow = settings.useShadow && settings.nativeShadow; settings.usePolyfillProto = !settings.useNativeCustomElements && !Object.__proto__; return settings; })() }; (function() { // until ES6 modules become standard, we follow Occam and simply stake out // a global namespace // Polymer is a Function, but of course this is also an Object, so we // hang various other objects off of Polymer.* var userPolymer = window.Polymer; window.Polymer = function(prototype) { // if input is a `class` (aka a function with a prototype), use the prototype // remember that the `constructor` will never be called if (typeof prototype === 'function') { prototype = prototype.prototype; } // if there is no prototype, use a default empty object if (!prototype) { prototype = {}; } // desugar the prototype and return a factory object var factory = desugar(prototype); // Polymer.Base is now chained to factory.prototype, and for IE10 compat // this may have resulted in a new prototype being created prototype = factory.prototype; var options = { prototype: prototype }; // NOTE: we're specifically supporting older Chrome versions here // (specifically Chrome 39) that throw when options.extends is undefined. if (prototype.extends) { options.extends = prototype.extends; } Polymer.telemetry._registrate(prototype); document.registerElement(prototype.is, options); return factory; }; var desugar = function(prototype) { // Note: need to chain user prototype with the correct type-extended // version of Polymer.Base; this is especially important when you can't // prototype swizzle (e.g. IE10), since CustomElements uses getPrototypeOf var base = Polymer.Base; if (prototype.extends) { base = Polymer.Base._getExtendedPrototype(prototype.extends); } prototype = Polymer.Base.chainObject(prototype, base); prototype.registerCallback(); return prototype.constructor; }; if (userPolymer) { for (var i in userPolymer) { Polymer[i] = userPolymer[i]; } } Polymer.Class = desugar; })(); /* // Raw usage [ctor =] Polymer.Class(prototype); document.registerElement(name, ctor); // Simplified usage [ctor = ] Polymer(prototype); */ // telemetry: statistics, logging, and debug Polymer.telemetry = { registrations: [], _regLog: function(prototype) { console.log('[' + prototype.is + ']: registered') }, _registrate: function(prototype) { this.registrations.push(prototype); Polymer.log && this._regLog(prototype); }, dumpRegistrations: function() { this.registrations.forEach(this._regLog); } }; // a tiny bit of sugar for `document.currentScript.ownerDocument` Object.defineProperty(window, 'currentImport', { enumerable: true, configurable: true, get: function() { return (document._currentScript || document.currentScript).ownerDocument; } }); /* * Helper for determining when first render occurs. * Call `Polymer.RenderStatus.whenReady(callback)` to be notified when * first render occurs or immediately if it has already occured. * Note that since HTML Imports are designed to load before rendering, * this call can also be used to guarantee that imports have loaded. * This behavior is normalized to function correctly with the HTMLImports * polyfill which does not otherwise maintain this rendering guarantee. * Querying style and layout data before first render is currently * problematic on some browsers (Blink/Webkit) so this helper can be used * to prevent doing so until a safe time. */ Polymer.RenderStatus = { _ready: false, _callbacks: [], whenReady: function(cb) { if (this._ready) { cb(); } else { this._callbacks.push(cb); } }, _makeReady: function() { this._ready = true; for (var i=0; i < this._callbacks.length; i++) { this._callbacks[i](); } this._callbacks = []; }, _catchFirstRender: function() { requestAnimationFrame(function() { Polymer.RenderStatus._makeReady(); }); }, _afterNextRenderQueue: [], _waitingNextRender: false, afterNextRender: function(element, fn, args) { this._watchNextRender(); this._afterNextRenderQueue.push([element, fn, args]); }, _watchNextRender: function() { if (!this._waitingNextRender) { this._waitingNextRender = true; var fn = function() { Polymer.RenderStatus._flushNextRender(); } if (!this._ready) { this.whenReady(fn); } else { requestAnimationFrame(fn); } } }, _flushNextRender: function() { var self = this; // we want to defer after render until just after the paint. setTimeout(function() { self._flushRenderCallbacks(self._afterNextRenderQueue); self._afterNextRenderQueue = []; self._waitingNextRender = false; }); }, _flushRenderCallbacks: function(callbacks) { for (var i=0, h; i < callbacks.length; i++) { h = callbacks[i]; h[1].apply(h[0], h[2] || Polymer.nar); } } }; if (window.HTMLImports) { HTMLImports.whenReady(function() { Polymer.RenderStatus._catchFirstRender(); }); } else { Polymer.RenderStatus._catchFirstRender(); } // NOTE: for bc. Polymer.ImportStatus = Polymer.RenderStatus; Polymer.ImportStatus.whenLoaded = Polymer.ImportStatus.whenReady; (function() { 'use strict'; var settings = Polymer.Settings; Polymer.Base = { // Used for `isInstance` type checking; cannot use `instanceof` because // there is no common Polymer.Base in the prototype chain between type // extensions and normal custom elements __isPolymerInstance__: true, // pluggable features // `this` context is a prototype, not an instance _addFeature: function(feature) { this.extend(this, feature); }, // `this` context is a prototype, not an instance registerCallback: function() { // TODO(sjmiles): perhaps this method should be called from polymer-bootstrap? this._desugarBehaviors(); // abstract this._doBehavior('beforeRegister'); // abstract this._registerFeatures(); // abstract if (!settings.lazyRegister) { this.ensureRegisterFinished(); } }, createdCallback: function() { if (!this.__hasRegisterFinished) { this._ensureRegisterFinished(this.__proto__); } Polymer.telemetry.instanceCount++; this.root = this; this._doBehavior('created'); // abstract this._initFeatures(); // abstract }, /** * As an optimization, when `Polymer.Settings.lazyRegister` is set to true * registration tasks are deferred until the first instance of the element * is created. If an element should not defer registration tasks until * this time, `ensureRegisterFinished` may be called * on the element's prototype. */ ensureRegisterFinished: function() { this._ensureRegisterFinished(this); }, _ensureRegisterFinished: function(proto) { if (proto.__hasRegisterFinished !== proto.is) { proto.__hasRegisterFinished = proto.is; if (proto._finishRegisterFeatures) { proto._finishRegisterFeatures(); } // registration extension point proto._doBehavior('registered'); // where prototypes are simulated (IE10), element instance // must be specfically fixed up. if (settings.usePolyfillProto && proto !== this) { proto.extend(this, proto); } } }, // reserved for canonical behavior attachedCallback: function() { // NOTE: workaround for: // https://code.google.com/p/chromium/issues/detail?id=516550 // To allow querying style/layout data in attached, we defer it // until we are sure rendering is ready. var self = this; Polymer.RenderStatus.whenReady(function() { self.isAttached = true; self._doBehavior('attached'); // abstract }); }, // reserved for canonical behavior detachedCallback: function() { // NOTE: duplicate attachedCallback behavior var self = this; Polymer.RenderStatus.whenReady(function() { self.isAttached = false; self._doBehavior('detached'); // abstract }); }, // reserved for canonical behavior attributeChangedCallback: function(name, oldValue, newValue) { // TODO(sorvell): consider filtering out changes to host attributes // note: this was barely measurable with 3 host attributes. this._attributeChangedImpl(name); // abstract this._doBehavior('attributeChanged', [name, oldValue, newValue]); // abstract }, _attributeChangedImpl: function(name) { this._setAttributeToProperty(this, name); }, /** * Copies own properties (including accessor descriptors) from a source * object to a target object. * * @method extend * @param {Object} prototype Target object to copy properties to. * @param {Object} api Source object to copy properties from. * @return {Object} prototype object that was passed as first argument. */ extend: function(prototype, api) { if (prototype && api) { var n$ = Object.getOwnPropertyNames(api); for (var i=0, n; (i * * * * Then in code in some other location that cannot access the dom-module above * * var img = document.createElement('dom-module').import('foo', 'img'); * */ var DomModule = function() { return document.createElement('dom-module'); }; DomModule.prototype = Object.create(HTMLElement.prototype); Polymer.Base.extend(DomModule.prototype, { constructor: DomModule, createdCallback: function() { this.register(); }, /** * Registers the dom-module at a given id. This method should only be called * when a dom-module is imperatively created. For * example, `document.createElement('dom-module').register('foo')`. * @method register * @param {String} id The id at which to register the dom-module. */ register: function(id) { id = id || this.id || this.getAttribute('name') || this.getAttribute('is'); if (id) { this.id = id; // store id separate from lowercased id so that // in all cases mixedCase id will stored distinctly // and lowercase version is a fallback modules[id] = this; lcModules[id.toLowerCase()] = this; } }, /** * Retrieves the dom specified by `selector` in the module specified by * `id`. For example, this.import('foo', 'img'); * @method register * @param {String} id * @param {String} selector * @return {Object} Returns the dom which matches `selector` in the module * at the specified `id`. */ import: function(id, selector) { if (id) { var m = findModule(id); if (!m) { // If polyfilling, a script can run before a dom-module element // is upgraded. We force the containing document to upgrade // dom-modules and try again to workaround this polyfill limitation. forceDomModulesUpgrade(); m = findModule(id); } if (m && selector) { m = m.querySelector(selector); } return m; } } }); // NOTE: HTMLImports polyfill does not // block scripts on upgrading elements. However, we want to ensure that // any dom-module in the tree is available prior to a subsequent script // processing. // Therefore, we force any dom-modules in the tree to upgrade when dom-module // is registered by temporarily setting CE polyfill to crawl the entire // imports tree. (Note: this should only upgrade any imports that have been // loaded by this point. In addition the HTMLImports polyfill should be // changed to upgrade elements prior to running any scripts.) var cePolyfill = window.CustomElements && !CustomElements.useNative; document.registerElement('dom-module', DomModule); function forceDomModulesUpgrade() { if (cePolyfill) { var script = document._currentScript || document.currentScript; var doc = script && script.ownerDocument || document; // find all dom-modules var modules = doc.querySelectorAll('dom-module'); // minimize work by going backwards and stopping if we find an // upgraded module. for (var i= modules.length-1, m; (i >=0) && (m=modules[i]); i--) { if (m.__upgraded__) { return; } else { CustomElements.upgrade(m); } } } } })(); Polymer.Base._addFeature({ _prepIs: function() { if (!this.is) { var module = (document._currentScript || document.currentScript).parentNode; if (module.localName === 'dom-module') { var id = module.id || module.getAttribute('name') || module.getAttribute('is'); this.is = id; } } if (this.is) { this.is = this.is.toLowerCase(); } } }); /** * Automatically extend using objects referenced in `behaviors` array. * * someBehaviorObject = { * accessors: { * value: {type: Number, observer: '_numberChanged'} * }, * observers: [ * // ... * ], * ready: function() { * // called before prototoype's ready * }, * _numberChanged: function() {} * }; * * Polymer({ * * behaviors: [ * someBehaviorObject * ] * * ... * * }); * * @class base feature: behaviors */ Polymer.Base._addFeature({ /** * Array of objects to extend this prototype with. * * Each entry in the array may specify either a behavior object or array * of behaviors. * * Each behavior object may define lifecycle callbacks, `properties`, * `hostAttributes`, `observers` and `listeners`. * * Lifecycle callbacks will be called for each behavior in the order given * in the `behaviors` array, followed by the callback on the prototype. * Additionally, any non-lifecycle functions on the behavior object are * mixed into the base prototype, such that same-named functions on the * prototype take precedence, followed by later behaviors over earlier * behaviors. */ behaviors: [], _desugarBehaviors: function() { if (this.behaviors.length) { this.behaviors = this._desugarSomeBehaviors(this.behaviors); } }, _desugarSomeBehaviors: function(behaviors) { var behaviorSet = []; // iteration 1 behaviors = this._flattenBehaviorsList(behaviors); // iteration 2 // traverse the behaviors in _reverse_ order (youngest first) because // `_mixinBehavior` has _first property wins_ behavior, this is done // to optimize # of calls to `_copyOwnProperty` for (var i=behaviors.length-1; i>=0; i--) { var b = behaviors[i]; if (behaviorSet.indexOf(b) === -1) { this._mixinBehavior(b); behaviorSet.unshift(b); } } return behaviorSet; }, _flattenBehaviorsList: function(behaviors) { var flat = []; for (var i=0; i < behaviors.length; i++) { var b = behaviors[i]; if (b instanceof Array) { flat = flat.concat(this._flattenBehaviorsList(b)); } // filter out null entries so other iterators don't need to check else if (b) { flat.push(b); } else { this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import')); } } return flat; }, _mixinBehavior: function(b) { var n$ = Object.getOwnPropertyNames(b); for (var i=0, n; (i * * * * * *