Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Unified Diff: third_party/polymer/v0_8/components-chromium/polymer/polymer.js

Issue 1162563004: Upgrade to 1.0 and switch clients to dom-repeat where needed. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix a layout import and remove the gzipped webanimation in reproduce.sh Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/polymer/v0_8/components-chromium/polymer/polymer.js
diff --git a/third_party/polymer/v0_8/components-chromium/polymer/polymer.js b/third_party/polymer/v0_8/components-chromium/polymer/polymer.js
deleted file mode 100644
index 7a400f307a6e1785fdca7600a0fab86a4c13b95a..0000000000000000000000000000000000000000
--- a/third_party/polymer/v0_8/components-chromium/polymer/polymer.js
+++ /dev/null
@@ -1,6840 +0,0 @@
-
-
- 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 user = window.Polymer || {};
-
- // via url
- location.search.slice(1).split('&').forEach(function(o) {
- o = o.split('=');
- o[0] && (user[o[0]] = o[1] || true);
- });
-
- var wantShadow = (user.dom === 'shadow');
- var hasShadow = Boolean(Element.prototype.createShadowRoot);
- var nativeShadow = hasShadow && !window.ShadowDOMPolyfill;
- var useShadow = wantShadow && hasShadow;
-
- var hasNativeImports = Boolean('import' in document.createElement('link'));
- var useNativeImports = hasNativeImports;
-
- var useNativeCustomElements = (!window.CustomElements ||
- window.CustomElements.useNative);
-
- return {
- wantShadow: wantShadow,
- hasShadow: hasShadow,
- nativeShadow: nativeShadow,
- useShadow: useShadow,
- useNativeShadow: useShadow && nativeShadow,
- useNativeImports: useNativeImports,
- useNativeCustomElements: useNativeCustomElements
- };
- })()
- };
-
-
-;
-
- // 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.*
- (function() {
- var userPolymer = window.Polymer;
-
- window.Polymer = function(prototype) {
- var ctor = desugar(prototype);
- // native Custom Elements treats 'undefined' extends property
- // as valued, the property must not exist to be ignored
- var options = {
- prototype: ctor.prototype
- };
- if (prototype.extends) {
- options.extends = prototype.extends;
- }
- Polymer.telemetry._registrate(prototype);
- document.registerElement(prototype.is, options);
- return ctor;
- };
-
- var desugar = function(prototype) {
- prototype = Polymer.Base.chainObject(prototype, Polymer.Base);
- prototype.registerCallback();
- return prototype.constructor;
- };
-
- window.Polymer = Polymer;
-
- 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;
- }
- });
-
-
-;
-
- Polymer.Base = {
-
- // 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() {
- this._registerFeatures(); // abstract
- this._doBehavior('registered'); // abstract
- },
-
- createdCallback: function() {
- Polymer.telemetry.instanceCount++;
- this.root = this;
- this._doBehavior('created'); // abstract
- this._initFeatures(); // abstract
- },
-
- // reserved for canonical behavior
- attachedCallback: function() {
- this.isAttached = true;
- this._doBehavior('attached'); // abstract
- },
-
- // reserved for canonical behavior
- detachedCallback: function() {
- this.isAttached = false;
- this._doBehavior('detached'); // abstract
- },
-
- // reserved for canonical behavior
- attributeChangedCallback: function(name) {
- this.setAttributeToProperty(this, name);
- this._doBehavior('attributeChanged', arguments); // abstract
- },
-
- // copy own properties from `api` to `prototype`
- extend: function(prototype, api) {
- if (prototype && api) {
- Object.getOwnPropertyNames(api).forEach(function(n) {
- this.copyOwnProperty(n, api, prototype);
- }, this);
- }
- return prototype || api;
- },
-
- copyOwnProperty: function(name, source, target) {
- var pd = Object.getOwnPropertyDescriptor(source, name);
- if (pd) {
- Object.defineProperty(target, name, pd);
- }
- }
-
- };
-
- if (Object.__proto__) {
- Polymer.Base.chainObject = function(object, inherited) {
- if (object && inherited && object !== inherited) {
- object.__proto__ = inherited;
- }
- return object;
- };
- } else {
- Polymer.Base.chainObject = function(object, inherited) {
- if (object && inherited && object !== inherited) {
- var chained = Object.create(inherited);
- object = Polymer.Base.extend(chained, object);
- }
- return object;
- };
- }
-
- Polymer.Base = Polymer.Base.chainObject(Polymer.Base, HTMLElement.prototype);
-
- // TODO(sjmiles): ad hoc telemetry
- Polymer.telemetry.instanceCount = 0;
-
-
-;
-
-(function() {
-
- var modules = {};
-
- var DomModule = function() {
- return document.createElement('dom-module');
- };
-
- DomModule.prototype = Object.create(HTMLElement.prototype);
-
- DomModule.prototype.constructor = DomModule;
-
- DomModule.prototype.createdCallback = function() {
- var id = this.id || this.getAttribute('name') || this.getAttribute('is');
- if (id) {
- this.id = id;
- modules[id] = this;
- }
- };
-
- DomModule.prototype.import = function(id, slctr) {
- var m = modules[id];
- if (!m) {
- // If polyfilling, a script can run before a dom-module element
- // is upgraded. We force the containing document to upgrade
- // and try again to workaround this polyfill limitation.
- forceDocumentUpgrade();
- m = modules[id];
- }
- if (m && slctr) {
- m = m.querySelector(slctr);
- }
- 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;
- if (cePolyfill) {
- var ready = CustomElements.ready;
- CustomElements.ready = true;
- }
- document.registerElement('dom-module', DomModule);
- if (cePolyfill) {
- CustomElements.ready = ready;
- }
-
- function forceDocumentUpgrade() {
- if (cePolyfill) {
- var script = document._currentScript || document.currentScript;
- if (script) {
- CustomElements.upgradeAll(script.ownerDocument);
- }
- }
- }
-
-})();
-
-
-;
-
- 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;
- }
- }
- }
-
- });
-
-
-;
-
- /**
- * 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({
-
- behaviors: [],
-
- _prepBehaviors: function() {
- this._flattenBehaviors();
- this._prepBehavior(this);
- this.behaviors.forEach(function(b) {
- this._mixinBehavior(b);
- this._prepBehavior(b);
- }, this);
- },
-
- _flattenBehaviors: function() {
- var flat = [];
- this.behaviors.forEach(function(b) {
- if (!b) {
- console.warn('Polymer: undefined behavior in [' + this.is + ']');
- } else if (b instanceof Array) {
- flat = flat.concat(b);
- } else {
- flat.push(b);
- }
- }, this);
- this.behaviors = flat;
- },
-
- _mixinBehavior: function(b) {
- Object.getOwnPropertyNames(b).forEach(function(n) {
- switch (n) {
- case 'registered':
- case 'properties':
- case 'observers':
- case 'listeners':
- case 'keyPresses':
- case 'hostAttributes':
- case 'created':
- case 'attached':
- case 'detached':
- case 'attributeChanged':
- case 'configure':
- case 'ready':
- break;
- default:
- this.copyOwnProperty(n, b, this);
- break;
- }
- }, this);
- },
-
- _doBehavior: function(name, args) {
- this.behaviors.forEach(function(b) {
- this._invokeBehavior(b, name, args);
- }, this);
- this._invokeBehavior(this, name, args);
- },
-
- _invokeBehavior: function(b, name, args) {
- var fn = b[name];
- if (fn) {
- fn.apply(this, args || Polymer.nar);
- }
- },
-
- _marshalBehaviors: function() {
- this.behaviors.forEach(function(b) {
- this._marshalBehavior(b);
- }, this);
- this._marshalBehavior(this);
- }
-
- });
-
-
-;
-
- /**
- * Support `extends` property (for type-extension only).
- *
- * If the mixin is String-valued, the corresponding Polymer module
- * is mixed in.
- *
- * Polymer({
- * is: 'pro-input',
- * extends: 'input',
- * ...
- * });
- *
- * Type-extension objects are created using `is` notation in HTML, or via
- * the secondary argument to `document.createElement` (the type-extension
- * rules are part of the Custom Elements specification, not something
- * created by Polymer).
- *
- * Example:
- *
- * <!-- right: creates a pro-input element -->
- * <input is="pro-input">
- *
- * <!-- wrong: creates an unknown element -->
- * <pro-input>
- *
- * <script>
- * // right: creates a pro-input element
- * var elt = document.createElement('input', 'pro-input');
- *
- * // wrong: creates an unknown element
- * var elt = document.createElement('pro-input');
- * <\script>
- *
- * @class base feature: extends
- */
-
- Polymer.Base._addFeature({
-
- _prepExtends: function() {
- if (this.extends) {
- this.__proto__ = this.getExtendedPrototype(this.extends);
- }
- },
-
- getExtendedPrototype: function(tag) {
- return this.getExtendedNativePrototype(tag);
- },
-
- nativePrototypes: {}, // static
-
- getExtendedNativePrototype: function(tag) {
- var p = this.nativePrototypes[tag];
- if (!p) {
- var np = this.getNativePrototype(tag);
- p = this.extend(Object.create(np), Polymer.Base);
- this.nativePrototypes[tag] = p;
- }
- return p;
- },
-
- getNativePrototype: function(tag) {
- // TODO(sjmiles): sad necessity
- return Object.getPrototypeOf(document.createElement(tag));
- }
-
- });
-
-
-;
-
- /**
- * Generates a boilerplate constructor.
- *
- * XFoo = Polymer({
- * is: 'x-foo'
- * });
- * ASSERT(new XFoo() instanceof XFoo);
- *
- * You can supply a custom constructor on the prototype. But remember that
- * this constructor will only run if invoked **manually**. Elements created
- * via `document.createElement` or from HTML _will not invoke this method_.
- *
- * Instead, we reuse the concept of `constructor` for a factory method which
- * can take arguments.
- *
- * MyFoo = Polymer({
- * is: 'my-foo',
- * constructor: function(foo) {
- * this.foo = foo;
- * }
- * ...
- * });
- *
- * @class base feature: constructor
- */
-
- Polymer.Base._addFeature({
-
- // registration-time
-
- _prepConstructor: function() {
- // support both possible `createElement` signatures
- this._factoryArgs = this.extends ? [this.extends, this.is] : [this.is];
- // thunk the constructor to delegate allocation to `createElement`
- var ctor = function() {
- return this._factory(arguments);
- };
- if (this.hasOwnProperty('extends')) {
- ctor.extends = this.extends;
- }
- // ensure constructor is set. The `constructor` property is
- // not writable on Safari; note: Chrome requires the property
- // to be configurable.
- Object.defineProperty(this, 'constructor', {value: ctor,
- writable: true, configurable: true});
- ctor.prototype = this;
- },
-
- _factory: function(args) {
- var elt = document.createElement.apply(document, this._factoryArgs);
- if (this.factoryImpl) {
- this.factoryImpl.apply(elt, args);
- }
- return elt;
- }
-
- });
-
-
-;
-
- /**
- * Define property metadata.
- *
- * properties: {
- * <property>: <Type || Object>,
- * ...
- * }
- *
- * Example:
- *
- * properties: {
- * // `foo` property can be assigned via attribute, will be deserialized to
- * // the specified data-type. All `properties` properties have this behavior.
- * foo: String,
- *
- * // `bar` property has additional behavior specifiers.
- * // type: as above, type for (de-)serialization
- * // notify: true to send a signal when a value is set to this property
- * // reflectToAttribute: true to serialize the property to an attribute
- * // readOnly: if true, the property has no setter
- * bar: {
- * type: Boolean,
- * notify: true
- * }
- * }
- *
- * By itself the properties feature doesn't do anything but provide property
- * information. Other features use this information to control behavior.
- *
- * The `type` information is used by the `attributes` feature to convert
- * String values in attributes to typed properties. The `bind` feature uses
- * property information to control property access.
- *
- * Marking a property as `notify` causes a change in the property to
- * fire a non-bubbling event called `<property>-changed`. Elements that
- * have enabled two-way binding to the property use this event to
- * observe changes.
- *
- * `readOnly` properties have a getter, but no setter. To set a read-only
- * property, use the private setter method `_set_<property>(value)`.
- *
- * @class base feature: properties
- */
-
- // null object
- Polymer.nob = Object.create(null);
-
- Polymer.Base._addFeature({
-
- properties: {
- },
-
- getPropertyInfo: function(property) {
- var info = this._getPropertyInfo(property, this.properties);
- if (!info) {
- this.behaviors.some(function(b) {
- return info = this._getPropertyInfo(property, b.properties);
- }, this);
- }
- return info || Polymer.nob;
- },
-
- _getPropertyInfo: function(property, properties) {
- var p = properties && properties[property];
- if (typeof(p) === 'function') {
- p = properties[property] = {
- type: p
- };
- }
- return p;
- },
-
- getPropertyType: function(property) {
- return this.getPropertyInfo(property).type;
- },
-
- isReadOnlyProperty: function(property) {
- return this.getPropertyInfo(property).readOnly;
- },
-
- isNotifyProperty: function(property) {
- return this.getPropertyInfo(property).notify;
- },
-
- isReflectedProperty: function(property) {
- return this.getPropertyInfo(property).reflectToAttribute;
- }
-
- });
-
-
-;
-
- Polymer.CaseMap = {
-
- _caseMap: {},
-
- dashToCamelCase: function(dash) {
- var mapped = Polymer.CaseMap._caseMap[dash];
- if (mapped) {
- return mapped;
- }
- // TODO(sjmiles): is rejection test actually helping perf?
- if (dash.indexOf('-') < 0) {
- return Polymer.CaseMap._caseMap[dash] = dash;
- }
- return Polymer.CaseMap._caseMap[dash] = dash.replace(/-([a-z])/g,
- function(m) {
- return m[1].toUpperCase();
- }
- );
- },
-
- camelToDashCase: function(camel) {
- var mapped = Polymer.CaseMap._caseMap[camel];
- if (mapped) {
- return mapped;
- }
- return Polymer.CaseMap._caseMap[camel] = camel.replace(/([a-z][A-Z])/g,
- function (g) {
- return g[0] + '-' + g[1].toLowerCase()
- }
- );
- }
-
- };
-
-
-;
-
- /**
- * Support for `hostAttributes` property.
- *
- * hostAttributes: 'block vertical layout'
- *
- * `hostAttributes` is a space-delimited string of boolean attribute names to
- * set true on each instance.
- *
- * Support for mapping attributes to properties.
- *
- * Properties that are configured in `properties` with a type are mapped
- * to attributes.
- *
- * A value set in an attribute is deserialized into the specified
- * data-type and stored into the matching property.
- *
- * Example:
- *
- * properties: {
- * // values set to index attribute are converted to Number and propagated
- * // to index property
- * index: Number,
- * // values set to label attribute are propagated to index property
- * label: String
- * }
- *
- * Types supported for deserialization:
- *
- * - Number
- * - Boolean
- * - String
- * - Object (JSON)
- * - Array (JSON)
- * - Date
- *
- * This feature implements `attributeChanged` to support automatic
- * propagation of attribute values at run-time. If you override
- * `attributeChanged` be sure to call this base class method
- * if you also want the standard behavior.
- *
- * @class base feature: attributes
- */
-
- Polymer.Base._addFeature({
-
- _marshalAttributes: function() {
- this._takeAttributes();
- },
-
- _installHostAttributes: function(attributes) {
- if (attributes) {
- this.applyAttributes(this, attributes);
- }
- },
-
- applyAttributes: function(node, attr$) {
- for (var n in attr$) {
- this.serializeValueToAttribute(attr$[n], n, this);
- }
- },
-
- _takeAttributes: function() {
- this._takeAttributesToModel(this);
- },
-
- _takeAttributesToModel: function(model) {
- for (var i=0, l=this.attributes.length; i<l; i++) {
- var a = this.attributes[i];
- var property = Polymer.CaseMap.dashToCamelCase(a.name);
- var info = this.getPropertyInfo(property);
- if (info || this._propertyEffects[property]) {
- model[property] =
- this.deserialize(a.value, info.type);
- }
- }
- },
-
- setAttributeToProperty: function(model, attrName) {
- // Don't deserialize back to property if currently reflecting
- if (!this._serializing) {
- var propName = Polymer.CaseMap.dashToCamelCase(attrName);
- if (propName in this.properties) {
- var type = this.getPropertyType(propName);
- var val = this.getAttribute(attrName);
- model[propName] = this.deserialize(val, type);
- }
- }
- },
-
- _serializing: false,
- reflectPropertyToAttribute: function(name) {
- this._serializing = true;
- this.serializeValueToAttribute(this[name],
- Polymer.CaseMap.camelToDashCase(name));
- this._serializing = false;
- },
-
- serializeValueToAttribute: function(value, attribute, node) {
- var str = this.serialize(value);
- (node || this)
- [str === undefined ? 'removeAttribute' : 'setAttribute']
- (attribute, str);
- },
-
- deserialize: function(value, type) {
- switch (type) {
- case Number:
- value = Number(value);
- break;
-
- case Boolean:
- value = (value !== null);
- break;
-
- case Object:
- try {
- value = JSON.parse(value);
- } catch(x) {
- // allow non-JSON literals like Strings and Numbers
- }
- break;
-
- case Array:
- try {
- value = JSON.parse(value);
- } catch(x) {
- value = null;
- console.warn('Polymer::Attributes: couldn`t decode Array as JSON');
- }
- break;
-
- case Date:
- value = new Date(value);
- break;
-
- case String:
- default:
- break;
- }
- return value;
- },
-
- serialize: function(value) {
- switch (typeof value) {
- case 'boolean':
- return value ? '' : undefined;
-
- case 'object':
- if (value instanceof Date) {
- return value;
- } else if (value) {
- try {
- return JSON.stringify(value);
- } catch(x) {
- return '';
- }
- }
-
- default:
- return value != null ? value : undefined;
- }
- }
-
- });
-
-
-;
-
- Polymer.Base._addFeature({
-
- _setupDebouncers: function() {
- this._debouncers = {};
- },
-
- /**
- * Debounce signals.
- *
- * Call `debounce` to collapse multiple requests for a named task into
- * one invocation which is made after the wait time has elapsed with
- * no new request.
- *
- * debouncedClickAction: function(e) {
- * // will not call `processClick` more than once per 100ms
- * this.debounce('click', function() {
- * this.processClick;
- * }, 100);
- * }
- *
- * @method debounce
- * @param String {String} jobName A string to indentify the debounce job.
- * @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
- * @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
- * @type Handle
- */
- debounce: function(jobName, callback, wait) {
- this._debouncers[jobName] = Polymer.Debounce.call(this,
- this._debouncers[jobName], callback, wait);
- },
-
- isDebouncerActive: function(jobName) {
- var debouncer = this._debouncers[jobName];
- return debouncer && debouncer.finish;
- },
-
- flushDebouncer: function(jobName) {
- var debouncer = this._debouncers[jobName];
- if (debouncer) {
- debouncer.complete();
- }
- }
-
- });
-
-
-;
-
- Polymer.Base._addFeature({
-
- _registerFeatures: function() {
- // identity
- this._prepIs();
- // shared behaviors
- this._prepBehaviors();
- // inheritance
- this._prepExtends();
- // factory
- this._prepConstructor();
- },
-
- _prepBehavior: function() {},
-
- _initFeatures: function() {
- // setup debouncers
- this._setupDebouncers();
- // acquire behaviors
- this._marshalBehaviors();
- },
-
- _marshalBehavior: function(b) {
- // publish attributes to instance
- this._installHostAttributes(b.hostAttributes);
- }
-
- });
-
-
-;
-
- /**
- * Automatic template management.
- *
- * The `template` feature locates and instances a `<template>` element
- * corresponding to the current Polymer prototype.
- *
- * The `<template>` element may be immediately preceeding the script that
- * invokes `Polymer()`.
- *
- * @class standard feature: template
- */
-
- Polymer.Base._addFeature({
-
- _prepTemplate: function() {
- // locate template using dom-module
- this._template =
- this._template || Polymer.DomModule.import(this.is, 'template');
- // fallback to look at the node previous to the currentScript.
- if (!this._template) {
- var script = document._currentScript || document.currentScript;
- var prev = script && script.previousElementSibling;
- if (prev && prev.localName === 'template') {
- this._template = prev;
- }
- }
- },
-
- _stampTemplate: function() {
- if (this._template) {
- // note: root is now a fragment which can be manipulated
- // while not attached to the element.
- this.root = this.instanceTemplate(this._template);
- }
- },
-
- instanceTemplate: function(template) {
- var dom =
- document.importNode(template._content || template.content, true);
- return dom;
- }
-
- });
-
-
-;
-
- /**
- * Provides `ready` lifecycle callback which is called parent to child.
- *
- * This can be useful in a number of cases. Here are some examples:
- *
- * Setting a default property value that should have a side effect: To ensure
- * the side effect, an element must set a default value no sooner than
- * `created`; however, since `created` flows child to host, this is before the
- * host has had a chance to set a property value on the child. The `ready`
- * method solves this problem since it's called host to child.
- *
- * Dom distribution: To support reprojection efficiently, it's important to
- * distribute from host to child in one shot. The `attachedCallback` mostly
- * goes in the desired order except for elements that are in dom to start; in
- * this case, all children are attached before the host element. Ready also
- * addresses this case since it's guaranteed to be called host to child.
- *
- * @class standard feature: ready
- */
-
-(function() {
-
- var baseAttachedCallback = Polymer.Base.attachedCallback;
-
- Polymer.Base._addFeature({
-
- hostStack: [],
-
- // for overriding
- ready: function() {
- },
-
- // NOTE: The concept of 'host' is overloaded. There are two different
- // notions:
- // 1. an element hosts the elements in its local dom root.
- // 2. an element hosts the elements on which it configures data.
- // Practially, these notions are almost always coincident.
- // Some special elements like templates may separate them.
- // In order not to over-emphaisize this technical difference, we expose
- // one concept to the user and it maps to the dom-related meaning of host.
- //
- // 1. set this element's `host` and push this element onto the `host`'s
- // list of `client` elements
- // 2. establish this element as the current hosting element (allows
- // any elements we stamp to easily set host to us).
- _pushHost: function(host) {
- // NOTE: The `dataHost` of an element never changes.
- this.dataHost = host = host ||
- Polymer.Base.hostStack[Polymer.Base.hostStack.length-1];
- // this.dataHost reflects the parent element who manages
- // any bindings for the element. Only elements originally
- // stamped from Polymer templates have a dataHost, and this
- // never changes
- if (host && host._clients) {
- host._clients.push(this);
- }
- this._beginHost();
- },
-
- _beginHost: function() {
- Polymer.Base.hostStack.push(this);
- if (!this._clients) {
- this._clients = [];
- }
- },
-
- _popHost: function() {
- // this element is no longer the current hosting element
- Polymer.Base.hostStack.pop();
- },
-
- _tryReady: function() {
- if (this._canReady()) {
- this._ready();
- }
- },
-
- _canReady: function() {
- return !this.dataHost || this.dataHost._clientsReadied;
- },
-
- _ready: function() {
- // extension point
- this._beforeClientsReady();
- this._readyClients();
- // extension point
- this._afterClientsReady();
- this._readySelf();
- },
-
- _readyClients: function() {
- // prepare root
- this._setupRoot();
- // logically distribute self
- this._beginDistribute();
- // now fully prepare localChildren
- var c$ = this._clients;
- for (var i=0, l= c$.length, c; (i<l) && (c=c$[i]); i++) {
- c._ready();
- }
- // perform actual dom composition
- this._finishDistribute();
- // ensure elements are attached if they are in the dom at ready time
- // helps normalize attached ordering between native and polyfill ce.
- // TODO(sorvell): worth perf cost? ~6%
- // if (!Polymer.Settings.useNativeCustomElements) {
- // CustomElements.takeRecords();
- // }
- this._clientsReadied = true;
- this._clients = null;
- },
-
- // mark readied and call `ready`
- // note: called localChildren -> host
- _readySelf: function() {
- this._doBehavior('ready');
- this._readied = true;
- if (this._attachedPending) {
- this._attachedPending = false;
- this.attachedCallback();
- }
- },
-
- // for system overriding
- _beforeClientsReady: function() {},
- _afterClientsReady: function() {},
-
- // normalize lifecycle: ensure attached occurs only after ready.
- attachedCallback: function() {
- if (this._readied) {
- baseAttachedCallback.call(this);
- } else {
- this._attachedPending = true;
- }
- }
-
- });
-
-})();
-
-
-;
-
-Polymer.ArraySplice = (function() {
-
- function newSplice(index, removed, addedCount) {
- return {
- index: index,
- removed: removed,
- addedCount: addedCount
- };
- }
-
- var EDIT_LEAVE = 0;
- var EDIT_UPDATE = 1;
- var EDIT_ADD = 2;
- var EDIT_DELETE = 3;
-
- function ArraySplice() {}
-
- ArraySplice.prototype = {
-
- // Note: This function is *based* on the computation of the Levenshtein
- // "edit" distance. The one change is that "updates" are treated as two
- // edits - not one. With Array splices, an update is really a delete
- // followed by an add. By retaining this, we optimize for "keeping" the
- // maximum array items in the original array. For example:
- //
- // 'xxxx123' -> '123yyyy'
- //
- // With 1-edit updates, the shortest path would be just to update all seven
- // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
- // leaves the substring '123' intact.
- calcEditDistances: function(current, currentStart, currentEnd,
- old, oldStart, oldEnd) {
- // "Deletion" columns
- var rowCount = oldEnd - oldStart + 1;
- var columnCount = currentEnd - currentStart + 1;
- var distances = new Array(rowCount);
-
- // "Addition" rows. Initialize null column.
- for (var i = 0; i < rowCount; i++) {
- distances[i] = new Array(columnCount);
- distances[i][0] = i;
- }
-
- // Initialize null row
- for (var j = 0; j < columnCount; j++)
- distances[0][j] = j;
-
- for (var i = 1; i < rowCount; i++) {
- for (var j = 1; j < columnCount; j++) {
- if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
- distances[i][j] = distances[i - 1][j - 1];
- else {
- var north = distances[i - 1][j] + 1;
- var west = distances[i][j - 1] + 1;
- distances[i][j] = north < west ? north : west;
- }
- }
- }
-
- return distances;
- },
-
- // This starts at the final weight, and walks "backward" by finding
- // the minimum previous weight recursively until the origin of the weight
- // matrix.
- spliceOperationsFromEditDistances: function(distances) {
- var i = distances.length - 1;
- var j = distances[0].length - 1;
- var current = distances[i][j];
- var edits = [];
- while (i > 0 || j > 0) {
- if (i == 0) {
- edits.push(EDIT_ADD);
- j--;
- continue;
- }
- if (j == 0) {
- edits.push(EDIT_DELETE);
- i--;
- continue;
- }
- var northWest = distances[i - 1][j - 1];
- var west = distances[i - 1][j];
- var north = distances[i][j - 1];
-
- var min;
- if (west < north)
- min = west < northWest ? west : northWest;
- else
- min = north < northWest ? north : northWest;
-
- if (min == northWest) {
- if (northWest == current) {
- edits.push(EDIT_LEAVE);
- } else {
- edits.push(EDIT_UPDATE);
- current = northWest;
- }
- i--;
- j--;
- } else if (min == west) {
- edits.push(EDIT_DELETE);
- i--;
- current = west;
- } else {
- edits.push(EDIT_ADD);
- j--;
- current = north;
- }
- }
-
- edits.reverse();
- return edits;
- },
-
- /**
- * Splice Projection functions:
- *
- * A splice map is a representation of how a previous array of items
- * was transformed into a new array of items. Conceptually it is a list of
- * tuples of
- *
- * <index, removed, addedCount>
- *
- * which are kept in ascending index order of. The tuple represents that at
- * the |index|, |removed| sequence of items were removed, and counting forward
- * from |index|, |addedCount| items were added.
- */
-
- /**
- * Lacking individual splice mutation information, the minimal set of
- * splices can be synthesized given the previous state and final state of an
- * array. The basic approach is to calculate the edit distance matrix and
- * choose the shortest path through it.
- *
- * Complexity: O(l * p)
- * l: The length of the current array
- * p: The length of the old array
- */
- calcSplices: function(current, currentStart, currentEnd,
- old, oldStart, oldEnd) {
- var prefixCount = 0;
- var suffixCount = 0;
-
- var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
- if (currentStart == 0 && oldStart == 0)
- prefixCount = this.sharedPrefix(current, old, minLength);
-
- if (currentEnd == current.length && oldEnd == old.length)
- suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
-
- currentStart += prefixCount;
- oldStart += prefixCount;
- currentEnd -= suffixCount;
- oldEnd -= suffixCount;
-
- if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
- return [];
-
- if (currentStart == currentEnd) {
- var splice = newSplice(currentStart, [], 0);
- while (oldStart < oldEnd)
- splice.removed.push(old[oldStart++]);
-
- return [ splice ];
- } else if (oldStart == oldEnd)
- return [ newSplice(currentStart, [], currentEnd - currentStart) ];
-
- var ops = this.spliceOperationsFromEditDistances(
- this.calcEditDistances(current, currentStart, currentEnd,
- old, oldStart, oldEnd));
-
- var splice = undefined;
- var splices = [];
- var index = currentStart;
- var oldIndex = oldStart;
- for (var i = 0; i < ops.length; i++) {
- switch(ops[i]) {
- case EDIT_LEAVE:
- if (splice) {
- splices.push(splice);
- splice = undefined;
- }
-
- index++;
- oldIndex++;
- break;
- case EDIT_UPDATE:
- if (!splice)
- splice = newSplice(index, [], 0);
-
- splice.addedCount++;
- index++;
-
- splice.removed.push(old[oldIndex]);
- oldIndex++;
- break;
- case EDIT_ADD:
- if (!splice)
- splice = newSplice(index, [], 0);
-
- splice.addedCount++;
- index++;
- break;
- case EDIT_DELETE:
- if (!splice)
- splice = newSplice(index, [], 0);
-
- splice.removed.push(old[oldIndex]);
- oldIndex++;
- break;
- }
- }
-
- if (splice) {
- splices.push(splice);
- }
- return splices;
- },
-
- sharedPrefix: function(current, old, searchLength) {
- for (var i = 0; i < searchLength; i++)
- if (!this.equals(current[i], old[i]))
- return i;
- return searchLength;
- },
-
- sharedSuffix: function(current, old, searchLength) {
- var index1 = current.length;
- var index2 = old.length;
- var count = 0;
- while (count < searchLength && this.equals(current[--index1], old[--index2]))
- count++;
-
- return count;
- },
-
- calculateSplices: function(current, previous) {
- return this.calcSplices(current, 0, current.length, previous, 0,
- previous.length);
- },
-
- equals: function(currentValue, previousValue) {
- return currentValue === previousValue;
- }
- };
-
- return new ArraySplice();
-
-})();
-
-;
-
- Polymer.EventApi = (function() {
-
- var Settings = Polymer.Settings;
-
- var EventApi = function(event) {
- this.event = event;
- };
-
- if (Settings.useShadow) {
-
- EventApi.prototype = {
-
- get rootTarget() {
- return this.event.path[0];
- },
-
- get localTarget() {
- return this.event.target;
- },
-
- get path() {
- return this.event.path;
- }
-
- };
-
- } else {
-
- EventApi.prototype = {
-
- get rootTarget() {
- return this.event.target;
- },
-
- get localTarget() {
- var current = this.event.currentTarget;
- var currentRoot = current && Polymer.dom(current).getOwnerRoot();
- var p$ = this.path;
- for (var i=0; i < p$.length; i++) {
- if (Polymer.dom(p$[i]).getOwnerRoot() === currentRoot) {
- return p$[i];
- }
- }
- },
-
- // TODO(sorvell): simulate event.path. This probably incorrect for
- // non-bubbling events.
- get path() {
- if (!this.event._path) {
- var path = [];
- var o = this.rootTarget;
- while (o) {
- path.push(o);
- o = Polymer.dom(o).parentNode || o.host;
- }
- // event path includes window in most recent native implementations
- path.push(window);
- this.event._path = path;
- }
- return this.event._path;
- }
-
- };
-
- }
-
- var factory = function(event) {
- if (!event.__eventApi) {
- event.__eventApi = new EventApi(event);
- }
- return event.__eventApi;
- };
-
- return {
- factory: factory
- };
-
- })();
-
-
-;
-
- Polymer.DomApi = (function() {
-
- var Debounce = Polymer.Debounce;
- var Settings = Polymer.Settings;
-
- var nativeInsertBefore = Element.prototype.insertBefore;
- var nativeRemoveChild = Element.prototype.removeChild;
- var nativeAppendChild = Element.prototype.appendChild;
-
- var dirtyRoots = [];
-
- var DomApi = function(node, patch) {
- this.node = node;
- if (patch) {
- this.patch();
- }
- };
-
- DomApi.prototype = {
-
- // experimental: support patching selected native api.
- patch: function() {
- var self = this;
- this.node.appendChild = function(node) {
- return self.appendChild(node);
- };
- this.node.insertBefore = function(node, ref_node) {
- return self.insertBefore(node, ref_node);
- };
- this.node.removeChild = function(node) {
- return self.removeChild(node);
- };
- },
-
- get childNodes() {
- var c$ = getLightChildren(this.node);
- return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$);
- },
-
- get children() {
- return Array.prototype.filter.call(this.childNodes, function(n) {
- return (n.nodeType === Node.ELEMENT_NODE);
- });
- },
-
- get parentNode() {
- return this.node.lightParent || this.node.parentNode;
- },
-
- flush: function() {
- for (var i=0, host; i<dirtyRoots.length; i++) {
- host = dirtyRoots[i];
- host.flushDebouncer('_distribute');
- }
- dirtyRoots = [];
- },
-
- _lazyDistribute: function(host) {
- if (host.shadyRoot) {
- host.shadyRoot._distributionClean = false;
- }
- // TODO(sorvell): optimize debounce so it does less work by default
- // and then remove these checks...
- // need to dirty distribution once.
- if (!host.isDebouncerActive('_distribute')) {
- host.debounce('_distribute', host._distributeContent);
- dirtyRoots.push(host);
- }
- },
-
- // cases in which we may not be able to just do standard appendChild
- // 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
- // has an insertion point)
- // 2. container is a shadyRoot (don't distribute, instead set
- // container to container.host.
- // 3. node is <content> (host of container needs distribution)
- appendChild: function(node) {
- var distributed;
- this._removeNodeFromHost(node);
- if (this._nodeIsInLogicalTree(this.node)) {
- var host = this._hostForNode(this.node);
- this._addLogicalInfo(node, this.node, host && host.shadyRoot);
- this._addNodeToHost(node);
- if (host) {
- distributed = this._maybeDistribute(node, this.node, host);
- }
- }
- if (!distributed) {
- // if adding to a shadyRoot, add to host instead
- var container = this.node._isShadyRoot ? this.node.host : this.node;
- nativeAppendChild.call(container, node);
- }
- return node;
- },
-
- insertBefore: function(node, ref_node) {
- if (!ref_node) {
- return this.appendChild(node);
- }
- var distributed;
- this._removeNodeFromHost(node);
- if (this._nodeIsInLogicalTree(this.node)) {
- saveLightChildrenIfNeeded(this.node);
- var children = this.childNodes;
- var index = children.indexOf(ref_node);
- if (index < 0) {
- throw Error('The ref_node to be inserted before is not a child ' +
- 'of this node');
- }
- var host = this._hostForNode(this.node);
- this._addLogicalInfo(node, this.node, host && host.shadyRoot, index);
- this._addNodeToHost(node);
- if (host) {
- distributed = this._maybeDistribute(node, this.node, host);
- }
- }
- if (!distributed) {
- // if ref_node is <content> replace with first distributed node
- ref_node = ref_node.localName === CONTENT ?
- this._firstComposedNode(ref_node) : ref_node;
- // if adding to a shadyRoot, add to host instead
- var container = this.node._isShadyRoot ? this.node.host : this.node;
- nativeInsertBefore.call(container, node, ref_node);
- }
- return node;
- },
-
- /**
- Removes the given `node` from the element's `lightChildren`.
- This method also performs dom composition.
- */
- removeChild: function(node) {
- var distributed;
- if (this._nodeIsInLogicalTree(this.node)) {
- var host = this._hostForNode(this.node);
- distributed = this._maybeDistribute(node, this.node, host);
- this._removeNodeFromHost(node);
- }
- if (!distributed) {
- // if removing from a shadyRoot, remove form host instead
- var container = this.node._isShadyRoot ? this.node.host : this.node;
- nativeRemoveChild.call(container, node);
- }
- return node;
- },
-
- replaceChild: function(node, ref_node) {
- this.insertBefore(node, ref_node);
- this.removeChild(ref_node);
- return node;
- },
-
- getOwnerRoot: function() {
- return this._ownerShadyRootForNode(this.node);
- },
-
- _ownerShadyRootForNode: function(node) {
- if (node._ownerShadyRoot === undefined) {
- var root;
- if (node._isShadyRoot) {
- root = node;
- } else {
- var parent = Polymer.dom(node).parentNode;
- if (parent) {
- root = parent._isShadyRoot ? parent :
- this._ownerShadyRootForNode(parent);
- } else {
- root = null;
- }
- }
- node._ownerShadyRoot = root;
- }
- return node._ownerShadyRoot;
-
- },
-
- _maybeDistribute: function(node, parent, host) {
- var nodeNeedsDistribute = this._nodeNeedsDistribution(node);
- var distribute = this._parentNeedsDistribution(parent) ||
- nodeNeedsDistribute;
- if (nodeNeedsDistribute) {
- this._updateInsertionPoints(host);
- }
- if (distribute) {
- this._lazyDistribute(host);
- }
- return distribute;
- },
-
- _updateInsertionPoints: function(host) {
- host.shadyRoot._insertionPoints =
- factory(host.shadyRoot).querySelectorAll(CONTENT);
- },
-
- _nodeIsInLogicalTree: function(node) {
- return Boolean(node._isShadyRoot ||
- this._ownerShadyRootForNode(node) ||
- node.shadyRoot);
- },
-
- // note: a node is its own host
- _hostForNode: function(node) {
- var root = node.shadyRoot || (node._isShadyRoot ?
- node : this._ownerShadyRootForNode(node));
- return root && root.host;
- },
-
- _parentNeedsDistribution: function(parent) {
- return parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
- },
-
- // TODO(sorvell): technically we should check non-fragment nodes for
- // <content> children but since this case is assumed to be exceedingly
- // rare, we avoid the cost and will address with some specific api
- // when the need arises.
- _nodeNeedsDistribution: function(node) {
- return (node.localName === CONTENT) ||
- ((node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
- node.querySelector(CONTENT));
- },
-
- _removeNodeFromHost: function(node) {
- if (node.lightParent) {
- var root = this._ownerShadyRootForNode(node);
- if (root) {
- root.host._elementRemove(node);
- }
- this._removeLogicalInfo(node, node.lightParent);
- }
- this._removeOwnerShadyRoot(node);
- },
-
- _addNodeToHost: function(node) {
- var checkNode = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ?
- node.firstChild : node;
- var root = this._ownerShadyRootForNode(checkNode);
- if (root) {
- root.host._elementAdd(node);
- }
- },
-
- _addLogicalInfo: function(node, container, root, index) {
- saveLightChildrenIfNeeded(container);
- var children = factory(container).childNodes;
- index = index === undefined ? children.length : index;
- // handle document fragments
- if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
- var n = node.firstChild;
- while (n) {
- children.splice(index++, 0, n);
- n.lightParent = container;
- n = n.nextSibling;
- }
- } else {
- children.splice(index, 0, node);
- node.lightParent = container;
- }
- },
-
- // NOTE: in general, we expect contents of the lists here to be small-ish
- // and therefore indexOf to be nbd. Other optimizations can be made
- // for larger lists (linked list)
- _removeLogicalInfo: function(node, container) {
- var children = factory(container).childNodes;
- var index = children.indexOf(node);
- if ((index < 0) || (container !== node.lightParent)) {
- throw Error('The node to be removed is not a child of this node');
- }
- children.splice(index, 1);
- node.lightParent = null;
- },
-
- _removeOwnerShadyRoot: function(node) {
- // TODO(sorvell): need to clear any children of element?
- node._ownerShadyRoot = undefined;
- },
-
- // TODO(sorvell): This will fail if distribution that affects this
- // question is pending; this is expected to be exceedingly rare, but if
- // the issue comes up, we can force a flush in this case.
- _firstComposedNode: function(content) {
- var n$ = factory(content).getDistributedNodes();
- for (var i=0, l=n$.length, n, p$; (i<l) && (n=n$[i]); i++) {
- p$ = factory(n).getDestinationInsertionPoints();
- // means that we're composed to this spot.
- if (p$[p$.length-1] === content) {
- return n;
- }
- }
- },
-
- // TODO(sorvell): consider doing native QSA and filtering results.
- querySelector: function(selector) {
- return this.querySelectorAll(selector)[0];
- },
-
- querySelectorAll: function(selector) {
- return this._query(function(n) {
- return matchesSelector.call(n, selector);
- }, this.node);
- },
-
- _query: function(matcher, node) {
- var list = [];
- this._queryElements(factory(node).childNodes, matcher, list);
- return list;
- },
-
- _queryElements: function(elements, matcher, list) {
- for (var i=0, l=elements.length, c; (i<l) && (c=elements[i]); i++) {
- if (c.nodeType === Node.ELEMENT_NODE) {
- this._queryElement(c, matcher, list);
- }
- }
- },
-
- _queryElement: function(node, matcher, list) {
- if (matcher(node)) {
- list.push(node);
- }
- this._queryElements(factory(node).childNodes, matcher, list);
- },
-
- getDestinationInsertionPoints: function() {
- return this.node._destinationInsertionPoints || [];
- },
-
- getDistributedNodes: function() {
- return this.node._distributedNodes || [];
- },
-
- /*
- Returns a list of nodes distributed within this element. These can be
- dom children or elements distributed to children that are insertion
- points.
- */
- queryDistributedElements: function(selector) {
- var c$ = this.childNodes;
- var list = [];
- this._distributedFilter(selector, c$, list);
- for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
- if (c.localName === CONTENT) {
- this._distributedFilter(selector, factory(c).getDistributedNodes(),
- list);
- }
- }
- return list;
- },
-
- _distributedFilter: function(selector, list, results) {
- results = results || [];
- for (var i=0, l=list.length, d; (i<l) && (d=list[i]); i++) {
- if ((d.nodeType === Node.ELEMENT_NODE) &&
- (d.localName !== CONTENT) &&
- matchesSelector.call(d, selector)) {
- results.push(d);
- }
- }
- return results;
- }
-
- };
-
- if (Settings.useShadow) {
-
- DomApi.prototype.querySelectorAll = function(selector) {
- return Array.prototype.slice.call(this.node.querySelectorAll(selector));
- };
-
- DomApi.prototype.patch = function() {};
-
- DomApi.prototype.getOwnerRoot = function() {
- var n = this.node;
- while (n) {
- if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host) {
- return n;
- }
- n = n.parentNode;
- }
- };
-
- DomApi.prototype.getDestinationInsertionPoints = function() {
- var n$ = this.node.getDestinationInsertionPoints();
- return n$ ? Array.prototype.slice.call(n$) : [];
- };
-
- DomApi.prototype.getDistributedNodes = function() {
- var n$ = this.node.getDistributedNodes();
- return n$ ? Array.prototype.slice.call(n$) : [];
- };
-
-
- }
-
- var CONTENT = 'content';
-
- var factory = function(node, patch) {
- node = node || document;
- if (!node.__domApi) {
- node.__domApi = new DomApi(node, patch);
- }
- return node.__domApi;
- };
-
- Polymer.dom = function(obj, patch) {
- if (obj instanceof Event) {
- return Polymer.EventApi.factory(obj);
- } else {
- return factory(obj, patch);
- }
- };
-
- // make flush available directly.
- Polymer.dom.flush = DomApi.prototype.flush;
-
- function getLightChildren(node) {
- var children = node.lightChildren;
- return children ? children : node.childNodes;
- }
-
- function saveLightChildrenIfNeeded(node) {
- // Capture the list of light children. It's important to do this before we
- // start transforming the DOM into "rendered" state.
- //
- // Children may be added to this list dynamically. It will be treated as the
- // source of truth for the light children of the element. This element's
- // actual children will be treated as the rendered state once lightChildren
- // is populated.
- if (!node.lightChildren) {
- var children = [];
- for (var child = node.firstChild; child; child = child.nextSibling) {
- children.push(child);
- child.lightParent = child.lightParent || node;
- }
- node.lightChildren = children;
- }
- }
-
- function hasInsertionPoint(root) {
- return Boolean(root._insertionPoints.length);
- }
-
- var p = Element.prototype;
- var matchesSelector = p.matches || p.matchesSelector ||
- p.mozMatchesSelector || p.msMatchesSelector ||
- p.oMatchesSelector || p.webkitMatchesSelector;
-
- return {
- getLightChildren: getLightChildren,
- saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
- matchesSelector: matchesSelector,
- hasInsertionPoint: hasInsertionPoint,
- factory: factory
- };
-
- })();
-
-
-;
-
- (function() {
- /**
-
- Implements a pared down version of ShadowDOM's scoping, which is easy to
- polyfill across browsers.
-
- */
- Polymer.Base._addFeature({
-
- _prepShady: function() {
- // Use this system iff localDom is needed.
- this._useContent = this._useContent || Boolean(this._template);
- if (this._useContent) {
- this._template._hasInsertionPoint =
- this._template.content.querySelector('content');
- }
- },
-
- // called as part of content initialization, prior to template stamping
- _poolContent: function() {
- if (this._useContent) {
- // capture lightChildren to help reify dom scoping
- saveLightChildrenIfNeeded(this);
- }
- },
-
- // called as part of content initialization, after template stamping
- _setupRoot: function() {
- if (this._useContent) {
- this._createLocalRoot();
- }
- },
-
- _createLocalRoot: function() {
- this.shadyRoot = this.root;
- this.shadyRoot._distributionClean = false;
- this.shadyRoot._isShadyRoot = true;
- this.shadyRoot._dirtyRoots = [];
- // capture insertion point list
- // TODO(sorvell): it's faster to do this via native qSA than annotator.
- this.shadyRoot._insertionPoints = this._template._hasInsertionPoint ?
- this.shadyRoot.querySelectorAll('content') : [];
- // save logical tree info for shadyRoot.
- saveLightChildrenIfNeeded(this.shadyRoot);
- this.shadyRoot.host = this;
- },
-
- /**
- * Return the element whose local dom within which this element
- * is contained. This is a shorthand for
- * `Polymer.dom(this).getOwnerRoot().host`.
- */
- get domHost() {
- var root = Polymer.dom(this).getOwnerRoot();
- return root && root.host;
- },
-
- /**
- * Force this element to distribute its children to its local dom.
- * A user should call `distributeContent` if distribution has been
- * invalidated due to changes to selectors on child elements that
- * effect distribution. For example, if an element contains an
- * insertion point with <content select=".foo"> and a `foo` class is
- * added to a child, then `distributeContent` must be called to update
- * local dom distribution.
- */
- distributeContent: function() {
- if (this._useContent) {
- this.shadyRoot._distributionClean = false;
- this._distributeContent();
- }
- },
-
- _distributeContent: function() {
- if (this._useContent && !this.shadyRoot._distributionClean) {
- // logically distribute self
- this._beginDistribute();
- this._distributeDirtyRoots();
- this._finishDistribute();
- }
- },
-
- _beginDistribute: function() {
- if (this._useContent && hasInsertionPoint(this.shadyRoot)) {
- // reset distributions
- this._resetDistribution();
- // compute which nodes should be distributed where
- // TODO(jmesserly): this is simplified because we assume a single
- // ShadowRoot per host and no `<shadow>`.
- this._distributePool(this.shadyRoot, this._collectPool());
- }
- },
-
- _distributeDirtyRoots: function() {
- var c$ = this.shadyRoot._dirtyRoots;
- for (var i=0, l= c$.length, c; (i<l) && (c=c$[i]); i++) {
- c._distributeContent();
- }
- this.shadyRoot._dirtyRoots = [];
- },
-
- _finishDistribute: function() {
- // compose self
- if (this._useContent) {
- if (hasInsertionPoint(this.shadyRoot)) {
- this._composeTree();
- } else {
- if (!this.shadyRoot._hasDistributed) {
- this.textContent = '';
- this.appendChild(this.shadyRoot);
- } else {
- // simplified non-tree walk composition
- var children = this._composeNode(this);
- this._updateChildNodes(this, children);
- }
- }
- this.shadyRoot._hasDistributed = true;
- this.shadyRoot._distributionClean = true;
- }
- },
-
- // This is a polyfill for Element.prototype.matches, which is sometimes
- // still prefixed. Alternatively we could just polyfill it somewhere.
- // Note that the arguments are reversed from what you might expect.
- elementMatches: function(selector, node) {
- if (node === undefined) {
- node = this;
- }
- return matchesSelector.call(node, selector);
- },
-
- // Many of the following methods are all conceptually static, but they are
- // included here as "protected" methods to allow overriding.
-
- _resetDistribution: function() {
- // light children
- var children = getLightChildren(this);
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- if (child._destinationInsertionPoints) {
- child._destinationInsertionPoints = undefined;
- }
- }
- // insertion points
- var root = this.shadyRoot;
- var p$ = root._insertionPoints;
- for (var j = 0; j < p$.length; j++) {
- p$[j]._distributedNodes = [];
- }
- },
-
- // Gather the pool of nodes that should be distributed. We will combine
- // these with the "content root" to arrive at the composed tree.
- _collectPool: function() {
- var pool = [];
- var children = getLightChildren(this);
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- if (isInsertionPoint(child)) {
- pool.push.apply(pool, child._distributedNodes);
- } else {
- pool.push(child);
- }
- }
- return pool;
- },
-
- // perform "logical" distribution; note, no actual dom is moved here,
- // instead elements are distributed into a `content._distributedNodes`
- // array where applicable.
- _distributePool: function(node, pool) {
- var p$ = node._insertionPoints;
- for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
- this._distributeInsertionPoint(p, pool);
- }
- },
-
- _distributeInsertionPoint: function(content, pool) {
- // distribute nodes from the pool that this selector matches
- var anyDistributed = false;
- for (var i=0, l=pool.length, node; i < l; i++) {
- node=pool[i];
- // skip nodes that were already used
- if (!node) {
- continue;
- }
- // distribute this node if it matches
- if (this._matchesContentSelect(node, content)) {
- distributeNodeInto(node, content);
- // remove this node from the pool
- pool[i] = undefined;
- // since at least one node matched, we won't need fallback content
- anyDistributed = true;
- var parent = content.lightParent;
- // dirty a shadyRoot if a change may trigger reprojection!
- if (parent && parent.shadyRoot &&
- hasInsertionPoint(parent.shadyRoot)) {
- parent.shadyRoot._distributionClean = false;
- this.shadyRoot._dirtyRoots.push(parent);
- }
- }
- }
- // Fallback content if nothing was distributed here
- if (!anyDistributed) {
- var children = getLightChildren(content);
- for (var j = 0; j < children.length; j++) {
- distributeNodeInto(children[j], content);
- }
- }
- },
-
- // Reify dom such that it is at its correct rendering position
- // based on logical distribution.
- _composeTree: function() {
- this._updateChildNodes(this, this._composeNode(this));
- var p$ = this.shadyRoot._insertionPoints;
- for (var i=0, l=p$.length, p, parent; (i<l) && (p=p$[i]); i++) {
- parent = p.lightParent || p.parentNode;
- if (!parent._useContent && (parent !== this) &&
- (parent !== this.shadyRoot)) {
- this._updateChildNodes(parent, this._composeNode(parent));
- }
- }
- },
-
- // Returns the list of nodes which should be rendered inside `node`.
- _composeNode: function(node) {
- var children = [];
- var c$ = getLightChildren(node.shadyRoot || node);
- for (var i = 0; i < c$.length; i++) {
- var child = c$[i];
- if (isInsertionPoint(child)) {
- var distributedNodes = child._distributedNodes;
- for (var j = 0; j < distributedNodes.length; j++) {
- var distributedNode = distributedNodes[j];
- if (isFinalDestination(child, distributedNode)) {
- children.push(distributedNode);
- }
- }
- } else {
- children.push(child);
- }
- }
- return children;
- },
-
- // Ensures that the rendered node list inside `node` is `children`.
- _updateChildNodes: function(node, children) {
- var splices =
- Polymer.ArraySplice.calculateSplices(children, node.childNodes);
- for (var i=0; i<splices.length; i++) {
- var s = splices[i];
- // remove
- for (var j=0, c; j < s.removed.length; j++) {
- c = s.removed[j];
- if (c.previousSibling == children[s.index-1]) {
- remove(c);
- }
- }
- // insert
- for (var idx=s.index, ch, o; idx < s.index + s.addedCount; idx++) {
- ch = children[idx];
- o = node.childNodes[idx];
- while (o && o === ch) {
- o = o.nextSibling;
- }
- insertBefore(node, ch, o);
- }
- }
- },
-
- _matchesContentSelect: function(node, contentElement) {
- var select = contentElement.getAttribute('select');
- // no selector matches all nodes (including text)
- if (!select) {
- return true;
- }
- select = select.trim();
- // same thing if it had only whitespace
- if (!select) {
- return true;
- }
- // selectors can only match Elements
- if (!(node instanceof Element)) {
- return false;
- }
- // only valid selectors can match:
- // TypeSelector
- // *
- // ClassSelector
- // IDSelector
- // AttributeSelector
- // negation
- var validSelectors = /^(:not\()?[*.#[a-zA-Z_|]/;
- if (!validSelectors.test(select)) {
- return false;
- }
- return this.elementMatches(select, node);
- },
-
- // system override point
- _elementAdd: function() {},
-
- // system override point
- _elementRemove: function() {}
-
- });
-
- var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
- var getLightChildren = Polymer.DomApi.getLightChildren;
- var matchesSelector = Polymer.DomApi.matchesSelector;
- var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;
-
- function distributeNodeInto(child, insertionPoint) {
- insertionPoint._distributedNodes.push(child);
- var points = child._destinationInsertionPoints;
- if (!points) {
- child._destinationInsertionPoints = [insertionPoint];
- // TODO(sorvell): _destinationInsertionPoints may not be cleared when
- // nodes are dynamically added/removed, therefore test before adding
- // insertion points.
- } else if (points.indexOf(insertionPoint) < 0) {
- points.push(insertionPoint);
- }
- }
-
- function isFinalDestination(insertionPoint, node) {
- var points = node._destinationInsertionPoints;
- return points && points[points.length - 1] === insertionPoint;
- }
-
- function isInsertionPoint(node) {
- // TODO(jmesserly): we could add back 'shadow' support here.
- return node.localName == 'content';
- }
-
- var nativeInsertBefore = Element.prototype.insertBefore;
- var nativeRemoveChild = Element.prototype.removeChild;
-
- function insertBefore(parentNode, newChild, refChild) {
- // remove child from its old parent first
- remove(newChild);
- // make sure we never lose logical DOM information:
- // if the parentNode doesn't have lightChildren, save that information now.
- saveLightChildrenIfNeeded(parentNode);
- // insert it into the real DOM
- nativeInsertBefore.call(parentNode, newChild, refChild || null);
- }
-
- function remove(node) {
- var parentNode = node.parentNode;
- if (parentNode) {
- // make sure we never lose logical DOM information:
- // if the parentNode doesn't have lightChildren, save that information now.
- saveLightChildrenIfNeeded(parentNode);
- // remove it from the real DOM
- nativeRemoveChild.call(parentNode, node);
- }
- }
-
- })();
-
-
-;
-
- /**
- Implements `shadyRoot` compatible dom scoping using native ShadowDOM.
- */
-
- // Transform styles if not using ShadowDOM or if flag is set.
-
- if (Polymer.Settings.useShadow) {
-
- Polymer.Base._addFeature({
-
- // no-op's when ShadowDOM is in use
- _poolContent: function() {},
- _beginDistribute: function() {},
- distributeContent: function() {},
- _distributeContent: function() {},
- _finishDistribute: function() {},
-
- // create a shadowRoot
- _createLocalRoot: function() {
- this.createShadowRoot();
- this.shadowRoot.appendChild(this.root);
- this.root = this.shadowRoot;
- }
-
- });
-
- }
-
-
-;
-
- Polymer.DomModule = document.createElement('dom-module');
-
- Polymer.Base._addFeature({
-
- _registerFeatures: function() {
- // identity
- this._prepIs();
- // shared behaviors
- this._prepBehaviors();
- // inheritance
- this._prepExtends();
- // factory
- this._prepConstructor();
- // template
- this._prepTemplate();
- // dom encapsulation
- this._prepShady();
- },
-
- _prepBehavior: function() {},
-
- _initFeatures: function() {
- // manage local dom
- this._poolContent();
- // host stack
- this._pushHost();
- // instantiate template
- this._stampTemplate();
- // host stack
- this._popHost();
- // setup debouncers
- this._setupDebouncers();
- // instance shared behaviors
- this._marshalBehaviors();
- // top-down initial distribution, configuration, & ready callback
- this._tryReady();
- },
-
- _marshalBehavior: function(b) {
- // publish attributes to instance
- this._installHostAttributes(b.hostAttributes);
- }
-
- });
-
-
-;
-(function(scope) {
-
- function withDependencies(task, depends) {
- depends = depends || [];
- if (!depends.map) {
- depends = [depends];
- }
- return task.apply(this, depends.map(marshal));
- }
-
- function module(name, dependsOrFactory, moduleFactory) {
- var module = null;
- switch (arguments.length) {
- case 0:
- return;
- case 2:
- // dependsOrFactory is `factory` in this case
- module = dependsOrFactory.apply(this);
- break;
- default:
- // dependsOrFactory is `depends` in this case
- module = withDependencies(moduleFactory, dependsOrFactory);
- break;
- }
- modules[name] = module;
- };
-
- function marshal(name) {
- return modules[name];
- }
-
- var modules = {};
-
- var using = function(depends, task) {
- withDependencies(task, depends);
- };
-
- // exports
-
- scope.marshal = marshal;
- // `module` confuses commonjs detectors
- scope.modulate = module;
- scope.using = using;
-
-})(this);
-
-;
-/**
- * Scans a template to produce an annotation list that that associates
- * metadata culled from markup with tree locations
- * metadata and information to associate the metadata with nodes in an instance.
- *
- * Supported expressions include:
- *
- * Double-mustache annotations in text content. The annotation must be the only
- * content in the tag, compound expressions are not supported.
- *
- * <[tag]>{{annotation}}<[tag]>
- *
- * Double-escaped annotations in an attribute, either {{}} or [[]].
- *
- * <[tag] someAttribute="{{annotation}}" another="[[annotation]]"><[tag]>
- *
- * `on-` style event declarations.
- *
- * <[tag] on-<event-name>="annotation"><[tag]>
- *
- * Note that the `annotations` feature does not implement any behaviors
- * associated with these expressions, it only captures the data.
- *
- * Generated data-structure:
- *
- * [
- * {
- * id: '<id>',
- * events: [
- * {
- * name: '<name>'
- * value: '<annotation>'
- * }, ...
- * ],
- * bindings: [
- * {
- * kind: ['text'|'attribute'],
- * mode: ['{'|'['],
- * name: '<name>'
- * value: '<annotation>'
- * }, ...
- * ],
- * // TODO(sjmiles): this is annotation-parent, not node-parent
- * parent: <reference to parent annotation object>,
- * index: <integer index in parent's childNodes collection>
- * },
- * ...
- * ]
- *
- * @class Template feature
- */
-
- // null-array (shared empty array to avoid null-checks)
- Polymer.nar = [];
-
- Polymer.Annotations = {
-
- // preprocess-time
-
- // construct and return a list of annotation records
- // by scanning `template`'s content
- //
- parseAnnotations: function(template) {
- var list = [];
- var content = template._content || template.content;
- this._parseNodeAnnotations(content, list);
- return list;
- },
-
- // add annotations gleaned from subtree at `node` to `list`
- _parseNodeAnnotations: function(node, list) {
- return node.nodeType === Node.TEXT_NODE ?
- this._parseTextNodeAnnotation(node, list) :
- // TODO(sjmiles): are there other nodes we may encounter
- // that are not TEXT_NODE but also not ELEMENT?
- this._parseElementAnnotations(node, list);
- },
-
- // add annotations gleaned from TextNode `node` to `list`
- _parseTextNodeAnnotation: function(node, list) {
- var v = node.textContent, escape = v.slice(0, 2);
- if (escape === '{{' || escape === '[[') {
- // NOTE: use a space here so the textNode remains; some browsers
- // (IE) evacipate an empty textNode.
- node.textContent = ' ';
- var annote = {
- bindings: [{
- kind: 'text',
- mode: escape[0],
- value: v.slice(2, -2)
- }]
- };
- list.push(annote);
- return annote;
- }
- },
-
- // add annotations gleaned from Element `node` to `list`
- _parseElementAnnotations: function(element, list) {
- var annote = {
- bindings: [],
- events: []
- };
- this._parseChildNodesAnnotations(element, annote, list);
- // TODO(sjmiles): is this for non-ELEMENT nodes? If so, we should
- // change the contract of this method, or filter these out above.
- if (element.attributes) {
- this._parseNodeAttributeAnnotations(element, annote, list);
- // TODO(sorvell): ad hoc callback for doing work on elements while
- // leveraging annotator's tree walk.
- // Consider adding an node callback registry and moving specific
- // processing out of this module.
- if (this.prepElement) {
- this.prepElement(element);
- }
- }
- if (annote.bindings.length || annote.events.length || annote.id) {
- list.push(annote);
- }
- return annote;
- },
-
- // add annotations gleaned from children of `root` to `list`, `root`'s
- // `annote` is supplied as it is the annote.parent of added annotations
- _parseChildNodesAnnotations: function(root, annote, list, callback) {
- if (root.firstChild) {
- for (var i=0, node=root.firstChild; node; node=node.nextSibling, i++){
- if (node.localName === 'template' &&
- !node.hasAttribute('preserve-content')) {
- this._parseTemplate(node, i, list, annote);
- }
- //
- var childAnnotation = this._parseNodeAnnotations(node, list, callback);
- if (childAnnotation) {
- childAnnotation.parent = annote;
- childAnnotation.index = i;
- }
- }
- }
- },
-
- // 1. Parse annotations from the template and memoize them on
- // content._notes (recurses into nested templates)
- // 2. Parse template bindings for parent.* properties and memoize them on
- // content._parentProps
- // 3. Create bindings in current scope's annotation list to template for
- // parent props found in template
- // 4. Remove template.content and store it in annotation list, where it
- // will be the responsibility of the host to set it back to the template
- // (this is both an optimization to avoid re-stamping nested template
- // children and avoids a bug in Chrome where nested template children
- // upgrade)
- _parseTemplate: function(node, index, list, parent) {
- // TODO(sjmiles): simply altering the .content reference didn't
- // work (there was some confusion, might need verification)
- var content = document.createDocumentFragment();
- content._notes = this.parseAnnotations(node);
- content.appendChild(node.content);
- // Special-case treatment of 'parent.*' props for nested templates
- // Automatically bind `prop` on host to `_parent_prop` on template
- // for any `parent.prop`'s encountered in template binding; it is
- // responsibility of the template implementation to forward
- // these properties as appropriate
- var bindings = [];
- this._discoverTemplateParentProps(content);
- for (var prop in content._parentProps) {
- bindings.push({
- index: index,
- kind: 'property',
- mode: '{',
- name: '_parent_' + prop,
- value: prop
- });
- }
- // TODO(sjmiles): using `nar` to avoid unnecessary allocation;
- // in general the handling of these arrays needs some cleanup
- // in this module
- list.push({
- bindings: bindings,
- events: Polymer.nar,
- templateContent: content,
- parent: parent,
- index: index
- });
- },
-
- // Finds all parent.* properties in template content and stores
- // the path members in content._parentPropChain, which is an array
- // of maps listing the properties of parent templates required at
- // each level. Each outer template merges inner _parentPropChains to
- // propagate inner parent property needs to outer templates.
- // The top-level parent props from the chain (corresponding to this
- // template) are stored in content._parentProps.
- _discoverTemplateParentProps: function(content) {
- var chain = content._parentPropChain = [];
- content._notes.forEach(function(n) {
- // Find all bindings to parent.* and spread them into _parentPropChain
- n.bindings.forEach(function(b) {
- var m;
- if (m = b.value.match(/parent\.((parent\.)*[^.]*)/)) {
- var parts = m[1].split('.');
- for (var i=0; i<parts.length; i++) {
- var pp = chain[i] || (chain[i] = {});
- pp[parts[i]] = true;
- }
- }
- });
- // Merge child _parentPropChain[n+1] into this _parentPropChain[n]
- if (n.templateContent) {
- var tpp = n.templateContent._parentPropChain;
- for (var i=1; i<tpp.length; i++) {
- if (tpp[i]) {
- var pp = chain[i-1] || (chain[i-1] = {});
- Polymer.Base.simpleMixin(pp, tpp[i]);
- }
- }
- }
- });
- // Store this template's parentProps map
- content._parentProps = chain[0];
- },
-
- // add annotation data from attributes to the `annotation` for node `node`
- // TODO(sjmiles): the distinction between an `annotation` and
- // `annotation data` is not as clear as it could be
- // Walk attributes backwards, since removeAttribute can be vetoed by
- // IE in certain cases (e.g. <input value="foo">), resulting in the
- // attribute staying in the attributes list
- _parseNodeAttributeAnnotations: function(node, annotation) {
- for (var i=node.attributes.length-1, a; (a=node.attributes[i]); i--) {
- var n = a.name, v = a.value;
- // id
- if (n === 'id') {
- annotation.id = v;
- }
- // events (on-*)
- else if (n.slice(0, 3) === 'on-') {
- node.removeAttribute(n);
- annotation.events.push({
- name: n.slice(3),
- value: v
- });
- }
- // bindings (other attributes)
- else {
- var b = this._parseNodeAttributeAnnotation(node, n, v);
- if (b) {
- annotation.bindings.push(b);
- }
- }
- }
- },
-
- // construct annotation data from a generic attribute, or undefined
- _parseNodeAttributeAnnotation: function(node, n, v) {
- var mode = '', escape = v.slice(0, 2), name = n;
- if (escape === '{{' || escape === '[[') {
- // Mode (one-way or two)
- mode = escape[0];
- v = v.slice(2, -2);
- // Negate
- var not = false;
- if (v[0] == '!') {
- v = v.substring(1);
- not = true;
- }
- // Attribute or property
- var kind = 'property';
- if (n[n.length-1] == '$') {
- name = n.slice(0, -1);
- kind = 'attribute';
- }
- // Custom notification event
- var notifyEvent, colon;
- if (mode == '{' && (colon = v.indexOf('::')) > 0) {
- notifyEvent = v.substring(colon + 2);
- v = v.substring(0, colon);
- }
- // Remove annotation
- node.removeAttribute(n);
- // Case hackery: attributes are lower-case, but bind targets
- // (properties) are case sensitive. Gambit is to map dash-case to
- // camel-case: `foo-bar` becomes `fooBar`.
- // Attribute bindings are excepted.
- if (kind === 'property') {
- name = Polymer.CaseMap.dashToCamelCase(name);
- }
- return {
- kind: kind,
- mode: mode,
- name: name,
- value: v,
- negate: not,
- event: notifyEvent
- };
- }
- },
-
- // instance-time
-
- _localSubTree: function(node, host) {
- return (node === host) ? node.childNodes :
- (node.lightChildren || node.childNodes);
- },
-
- findAnnotatedNode: function(root, annote) {
- // recursively ascend tree until we hit root
- var parent = annote.parent &&
- Polymer.Annotations.findAnnotatedNode(root, annote.parent);
- // unwind the stack, returning the indexed node at each level
- return !parent ? root :
- Polymer.Annotations._localSubTree(parent, root)[annote.index];
- }
-
- };
-
-
-
-;
-
- (function() {
-
- // path fixup for urls in cssText that's expected to
- // come from a given ownerDocument
- function resolveCss(cssText, ownerDocument) {
- return cssText.replace(CSS_URL_RX, function(m, pre, url, post) {
- return pre + '\'' +
- resolve(url.replace(/["']/g, ''), ownerDocument) +
- '\'' + post;
- });
- }
-
- // url fixup for urls in an element's attributes made relative to
- // ownerDoc's base url
- function resolveAttrs(element, ownerDocument) {
- for (var name in URL_ATTRS) {
- var a$ = URL_ATTRS[name];
- for (var i=0, l=a$.length, a, at, v; (i<l) && (a=a$[i]); i++) {
- if (name === '*' || element.localName === name) {
- at = element.attributes[a];
- v = at && at.value;
- if (v && (v.search(BINDING_RX) < 0)) {
- at.value = (a === 'style') ?
- resolveCss(v, ownerDocument) :
- resolve(v, ownerDocument);
- }
- }
- }
- }
- }
-
- function resolve(url, ownerDocument) {
- var resolver = getUrlResolver(ownerDocument);
- resolver.href = url;
- return resolver.href || url;
- }
-
- var tempDoc;
- var tempDocBase;
- function resolveUrl(url, baseUri) {
- if (!tempDoc) {
- tempDoc = document.implementation.createHTMLDocument('temp');
- tempDocBase = tempDoc.createElement('base');
- tempDoc.head.appendChild(tempDocBase);
- }
- tempDocBase.href = baseUri;
- return resolve(url, tempDoc);
- }
-
- function getUrlResolver(ownerDocument) {
- return ownerDocument.__urlResolver ||
- (ownerDocument.__urlResolver = ownerDocument.createElement('a'));
- }
-
- var CSS_URL_RX = /(url\()([^)]*)(\))/g;
- var URL_ATTRS = {
- '*': ['href', 'src', 'style', 'url'],
- form: ['action']
- };
- var BINDING_RX = /\{\{|\[\[/;
-
- // exports
- Polymer.ResolveUrl = {
- resolveCss: resolveCss,
- resolveAttrs: resolveAttrs,
- resolveUrl: resolveUrl
- };
-
- })();
-
-
-;
-
-/**
- * Scans a template to produce an annotation object that stores expression
- * metadata along with information to associate the metadata with nodes in an
- * instance.
- *
- * Elements with `id` in the template are noted and marshaled into an
- * the `$` hash in an instance.
- *
- * Example
- *
- * &lt;template>
- * &lt;div id="foo">&lt;/div>
- * &lt;/template>
- * &lt;script>
- * Polymer({
- * task: function() {
- * this.$.foo.style.color = 'red';
- * }
- * });
- * &lt;/script>
- *
- * Other expressions that are noted include:
- *
- * Double-mustache annotations in text content. The annotation must be the only
- * content in the tag, compound expressions are not (currently) supported.
- *
- * <[tag]>{{path.to.host.property}}<[tag]>
- *
- * Double-mustache annotations in an attribute.
- *
- * <[tag] someAttribute="{{path.to.host.property}}"><[tag]>
- *
- * Only immediate host properties can automatically trigger side-effects.
- * Setting `host.path` in the example above triggers the binding, setting
- * `host.path.to.host.property` does not.
- *
- * `on-` style event declarations.
- *
- * <[tag] on-<event-name>="{{hostMethodName}}"><[tag]>
- *
- * Note: **the `annotations` feature does not actually implement the behaviors
- * associated with these expressions, it only captures the data**.
- *
- * Other optional features contain actual data implementations.
- *
- * @class standard feature: annotations
- */
-
-/*
-
-Scans a template to produce an annotation map that stores expression metadata
-and information that associates the metadata to nodes in a template instance.
-
-Supported annotations are:
-
- * id attributes
- * binding annotations in text nodes
- * double-mustache expressions: {{expression}}
- * double-bracket expressions: [[expression]]
- * binding annotations in attributes
- * attribute-bind expressions: name="{{expression}} || [[expression]]"
- * property-bind expressions: name*="{{expression}} || [[expression]]"
- * property-bind expressions: name:="expression"
- * event annotations
- * event delegation directives: on-<eventName>="expression"
-
-Generated data-structure:
-
- [
- {
- id: '<id>',
- events: [
- {
- mode: ['auto'|''],
- name: '<name>'
- value: '<expression>'
- }, ...
- ],
- bindings: [
- {
- kind: ['text'|'attribute'|'property'],
- mode: ['auto'|''],
- name: '<name>'
- value: '<expression>'
- }, ...
- ],
- // TODO(sjmiles): confusingly, this is annotation-parent, not node-parent
- parent: <reference to parent annotation>,
- index: <integer index in parent's childNodes collection>
- },
- ...
- ]
-
-TODO(sjmiles): this module should produce either syntactic metadata
-(e.g. double-mustache, double-bracket, star-attr), or semantic metadata
-(e.g. manual-bind, auto-bind, property-bind). Right now it's half and half.
-
-*/
-
- Polymer.Base._addFeature({
-
- // registration-time
-
- _prepAnnotations: function() {
- if (!this._template) {
- this._notes = [];
- } else {
- // TODO(sorvell): ad hoc method of plugging behavior into Annotations
- Polymer.Annotations.prepElement = this._prepElement.bind(this);
- this._notes = Polymer.Annotations.parseAnnotations(this._template);
- Polymer.Annotations.prepElement = null;
- }
- },
-
- _prepElement: function(element) {
- Polymer.ResolveUrl.resolveAttrs(element, this._template.ownerDocument);
- },
-
- // instance-time
-
- findAnnotatedNode: Polymer.Annotations.findAnnotatedNode,
-
- // marshal all teh things
- _marshalAnnotationReferences: function() {
- if (this._template) {
- this._marshalIdNodes();
- this._marshalAnnotatedNodes();
- this._marshalAnnotatedListeners();
- }
- },
-
- // push configuration references at configure time
- _configureAnnotationReferences: function() {
- this._configureTemplateContent();
- },
-
- // nested template contents have been stored prototypically to avoid
- // unnecessary duplication, here we put references to the
- // indirected contents onto the nested template instances
- _configureTemplateContent: function() {
- this._notes.forEach(function(note) {
- if (note.templateContent) {
- var template = this.findAnnotatedNode(this.root, note);
- template._content = note.templateContent;
- }
- }, this);
- },
-
- // construct `$` map (from id annotations)
- _marshalIdNodes: function() {
- this.$ = {};
- this._notes.forEach(function(a) {
- if (a.id) {
- this.$[a.id] = this.findAnnotatedNode(this.root, a);
- }
- }, this);
- },
-
- // concretize `_nodes` map (from anonymous annotations)
- _marshalAnnotatedNodes: function() {
- if (this._nodes) {
- this._nodes = this._nodes.map(function(a) {
- return this.findAnnotatedNode(this.root, a);
- }, this);
- }
- },
-
- // install event listeners (from event annotations)
- _marshalAnnotatedListeners: function() {
- this._notes.forEach(function(a) {
- if (a.events && a.events.length) {
- var node = this.findAnnotatedNode(this.root, a);
- a.events.forEach(function(e) {
- this.listen(node, e.name, e.value);
- }, this);
- }
- }, this);
- }
-
- });
-
-
-;
-
-(function(scope) {
-
- 'use strict';
-
- var async = scope.Base.async;
-
- var Gestures = {
- gestures: {},
-
- // automate the event listeners for the native events
- // TODO(dfreedm): add a way to remove handlers.
- add: function(evType, node, handler) {
- // listen for events in order to "recognize" this event
- var g = this.gestures[evType];
- var gn = '_' + evType;
- var info = {started: false, abortTrack: false, oneshot: false};
- if (g && !node[gn]) {
- if (g.touchaction) {
- this._setupTouchAction(node, g.touchaction, info);
- }
- for (var i = 0, n, sn, fn; i < g.deps.length; i++) {
- n = g.deps[i];
- fn = g[n].bind(g, info);
- sn = '_' + evType + '-' + n;
- // store the handler on the node for future removal
- node[sn] = fn;
- node.addEventListener(n, fn);
- }
- node[gn] = 0;
- }
- // listen for the gesture event
- node[gn]++;
- node.addEventListener(evType, handler);
- },
-
- remove: function(evType, node, handler) {
- var g = this.gestures[evType];
- var gn = '_' + evType;
- if (g && node[gn]) {
- for (var i = 0, n, sn, fn; i < g.deps.length; i++) {
- n = g.deps[i];
- sn = '_' + evType + '-' + n;
- fn = node[sn];
- if (fn){
- node.removeEventListener(n, fn);
- // remove stored handler to allow GC
- node[sn] = undefined;
- }
- }
- node[gn] = node[gn] ? (node[gn] - 1) : 0;
- node.removeEventListener(evType, handler);
- }
- },
-
- register: function(recog) {
- this.gestures[recog.name] = recog;
- },
-
- // touch will make synthetic mouse events
- // preventDefault on touchend will cancel them,
- // but this breaks <input> focus and link clicks
- // Disabling "mouse" handlers for 500ms is enough
-
- _cancelFunction: null,
-
- cancelNextClick: function(timeout) {
- if (!this._cancelFunction) {
- timeout = timeout || 500;
- var self = this;
- var reset = function() {
- var cfn = self._cancelFunction;
- if (cfn) {
- clearTimeout(cfn.id);
- document.removeEventListener('click', cfn, true);
- self._cancelFunction = null;
- }
- };
- var canceller = function(e) {
- e.tapPrevented = true;
- reset();
- };
- canceller.id = setTimeout(reset, timeout);
- this._cancelFunction = canceller;
- document.addEventListener('click', canceller, true);
- }
- },
-
- // try to use the native touch-action, if it exists
- _hasNativeTA: typeof document.head.style.touchAction === 'string',
-
- // set scrolling direction on node to check later on first move
- // must call this before adding event listeners!
- setTouchAction: function(node, value) {
- if (this._hasNativeTA) {
- node.style.touchAction = value;
- }
- node.touchAction = value;
- },
-
- _setupTouchAction: function(node, value, info) {
- // reuse custom value on node if set
- var ta = node.touchAction;
- value = ta || value;
- // set an anchor point to see how far first move is
- node.addEventListener('touchstart', function(e) {
- var t = e.changedTouches[0];
- info.initialTouch = {x: t.clientX, y: t.clientY};
- info.abortTrack = false;
- info.oneshot = false;
- });
- node.addEventListener('touchmove', function(e) {
- // only run this once
- if (info.oneshot) {
- return;
- }
- info.oneshot = true;
- // "none" means always track
- if (value === 'none') {
- return;
- }
- // "auto" is default, always scroll
- // bail-out if touch-action did its job
- // the touchevent is non-cancelable if the page/area is scrolling
- if (value === 'auto' || !value || (ta && !e.cancelable)) {
- info.abortTrack = true;
- return;
- }
- // check first move direction
- // unfortunately, we can only make the decision in the first move,
- // so we have to use whatever values are available.
- // Typically, this can be a really small amount, :(
- var t = e.changedTouches[0];
- var x = t.clientX, y = t.clientY;
- var dx = Math.abs(info.initialTouch.x - x);
- var dy = Math.abs(info.initialTouch.y - y);
- // scroll in x axis, abort track if we move more in x direction
- if (value === 'pan-x') {
- info.abortTrack = dx >= dy;
- // scroll in y axis, abort track if we move more in y direction
- } else if (value === 'pan-y') {
- info.abortTrack = dy >= dx;
- }
- });
- },
-
- fire: function(target, type, detail, bubbles, cancelable) {
- return target.dispatchEvent(
- new CustomEvent(type, {
- detail: detail,
- bubbles: bubbles,
- cancelable: cancelable
- })
- );
- }
-
- };
-
- Gestures.register({
- name: 'track',
- touchaction: 'none',
- deps: ['mousedown', 'touchmove', 'touchend'],
-
- mousedown: function(info, e) {
- var t = e.currentTarget;
- var self = this;
- var movefn = function movefn(e, up) {
- if (!info.tracking && !up) {
- // set up tap prevention
- Gestures.cancelNextClick();
- }
- // first move is 'start', subsequent moves are 'move', mouseup is 'end'
- var state = up ? 'end' : (!info.started ? 'start' : 'move');
- info.started = true;
- self.fire(t, e, state);
- e.preventDefault();
- };
- var upfn = function upfn(e) {
- // call mousemove function with 'end' state
- movefn(e, true);
- info.started = false;
- // remove the temporary listeners
- document.removeEventListener('mousemove', movefn);
- document.removeEventListener('mouseup', upfn);
- };
- // add temporary document listeners as mouse retargets
- document.addEventListener('mousemove', movefn);
- document.addEventListener('mouseup', upfn);
- },
-
- touchmove: function(info, e) {
- var t = e.currentTarget;
- var ct = e.changedTouches[0];
- // if track was aborted, stop tracking
- if (info.abortTrack) {
- return;
- }
- e.preventDefault();
- // the first track event is sent after some hysteresis with touchmove.
- // Use `started` state variable to differentiate the "first" move from
- // the rest to make track.state == 'start'
- // first move is 'start', subsequent moves are 'move'
- var state = !info.started ? 'start' : 'move';
- info.started = true;
- this.fire(t, ct, state);
- },
-
- touchend: function(info, e) {
- var t = e.currentTarget;
- var ct = e.changedTouches[0];
- // only trackend if track was started and not aborted
- if (info.started && !info.abortTrack) {
- // reset started state on up
- info.started = false;
- var ne = this.fire(t, ct, 'end');
- // iff tracking, always prevent tap
- e.tapPrevented = true;
- }
- },
-
- fire: function(target, touch, state) {
- return Gestures.fire(target, 'track', {
- state: state,
- x: touch.clientX,
- y: touch.clientY
- });
- }
-
- });
-
- // dispatch a *bubbling* "tap" only at the node that is the target of the
- // generating event.
- // dispatch *synchronously* so that we can implement prevention of native
- // actions like links being followed.
- //
- // TODO(dfreedm): a tap should not occur when there's too much movement.
- // Right now, a tap can occur when a touchend happens very far from the
- // generating touch.
- // This *should* obviate the need for tapPrevented via track.
- Gestures.register({
- name: 'tap',
- deps: ['click', 'touchend'],
-
- click: function(info, e) {
- this.forward(e);
- },
-
- touchend: function(info, e) {
- Gestures.cancelNextClick();
- this.forward(e);
- },
-
- forward: function(e) {
- // prevent taps from being generated from events that have been
- // canceled (e.g. via cancelNextClick) or already handled via
- // a listener lower in the tree.
- if (!e.tapPrevented) {
- e.tapPrevented = true;
- this.fire(e.target);
- }
- },
-
- // fire a bubbling event from the generating target.
- fire: function(target) {
- Gestures.fire(target, 'tap', {}, true);
- }
-
- });
-
- scope.Gestures = Gestures;
-
-})(Polymer);
-
-
-;
-
- /**
- * Supports `listeners` and `keyPresses` objects.
- *
- * Example:
- *
- * using('Base', function(Base) {
- *
- * Polymer({
- *
- * listeners: {
- * // `click` events on the host are delegated to `clickHandler`
- * 'click': 'clickHandler'
- * },
- *
- * keyPresses: {
- * // 'ESC' key presses are delegated to `escHandler`
- * Base.ESC_KEY: 'escHandler'
- * },
- *
- * ...
- *
- * });
- *
- * });
- *
- * @class standard feature: events
- *
- */
-
- Polymer.Base._addFeature({
-
- listeners: {},
-
- _listenListeners: function(listeners) {
- var node, name, key;
- for (key in listeners) {
- if (key.indexOf('.') < 0) {
- node = this;
- name = key;
- } else {
- name = key.split('.');
- node = this.$[name[0]];
- name = name[1];
- }
- this.listen(node, name, listeners[key]);
- }
- },
-
- listen: function(node, eventName, methodName) {
- var host = this;
- var handler = function(e) {
- if (host[methodName]) {
- host[methodName](e, e.detail);
- } else {
- console.warn('[%s].[%s]: event handler [%s] is null in scope (%o)',
- node.localName, eventName, methodName, host);
- }
- };
- switch (eventName) {
- case 'tap':
- case 'track':
- Polymer.Gestures.add(eventName, node, handler);
- break;
-
- default:
- node.addEventListener(eventName, handler);
- break;
- }
- },
-
- keyCodes: {
- ESC_KEY: 27,
- ENTER_KEY: 13,
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- SPACE: 32
- }
-
- });
-
-
-;
-
-Polymer.Async = (function() {
-
- var currVal = 0;
- var lastVal = 0;
- var callbacks = [];
- var twiddle = document.createTextNode('');
-
- function runAsync(callback, waitTime) {
- if (waitTime > 0) {
- return ~setTimeout(callback, waitTime);
- } else {
- twiddle.textContent = currVal++;
- callbacks.push(callback);
- return currVal - 1;
- }
- }
-
- function cancelAsync(handle) {
- if (handle < 0) {
- clearTimeout(~handle);
- } else {
- var idx = handle - lastVal;
- if (idx >= 0) {
- if (!callbacks[idx]) {
- throw 'invalid async handle: ' + handle;
- }
- callbacks[idx] = null;
- }
- }
- }
-
- function atEndOfMicrotask() {
- var len = callbacks.length;
- for (var i=0; i<len; i++) {
- var cb = callbacks[i];
- if (cb) {
- cb();
- }
- }
- callbacks.splice(0, len);
- lastVal += len;
- }
-
- new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
- .observe(twiddle, {characterData: true})
- ;
-
- // exports
-
- return {
- run: runAsync,
- cancel: cancelAsync
- };
-
-})();
-
-
-;
-
-Polymer.Debounce = (function() {
-
- // usage
-
- // invoke cb.call(this) in 100ms, unless the job is re-registered,
- // which resets the timer
- //
- // this.job = this.debounce(this.job, cb, 100)
- //
- // returns a handle which can be used to re-register a job
-
- var Async = Polymer.Async;
-
- var Debouncer = function(context) {
- this.context = context;
- this.boundComplete = this.complete.bind(this);
- };
-
- Debouncer.prototype = {
- go: function(callback, wait) {
- var h;
- this.finish = function() {
- Async.cancel(h);
- };
- h = Async.run(this.boundComplete, wait);
- this.callback = callback;
- },
- stop: function() {
- if (this.finish) {
- this.finish();
- this.finish = null;
- }
- },
- complete: function() {
- if (this.finish) {
- this.stop();
- this.callback.call(this.context);
- }
- }
- };
-
- function debounce(debouncer, callback, wait) {
- if (debouncer) {
- debouncer.stop();
- } else {
- debouncer = new Debouncer(this);
- }
- debouncer.go(callback, wait);
- return debouncer;
- }
-
- // exports
-
- return debounce;
-
-})();
-
-
-;
-
- Polymer.Base._addFeature({
-
- $$: function(slctr) {
- return Polymer.dom(this.root).querySelector(slctr);
- },
-
- toggleClass: function(name, bool, node) {
- node = node || this;
- if (arguments.length == 1) {
- bool = !node.classList.contains(name);
- }
- if (bool) {
- node.classList.add(name);
- } else {
- node.classList.remove(name);
- }
- },
-
- toggleAttribute: function(name, bool, node) {
- (node || this)[bool ? 'setAttribute' : 'removeAttribute'](name, '');
- },
-
- classFollows: function(className, neo, old) {
- if (old) {
- old.classList.remove(className);
- }
- if (neo) {
- neo.classList.add(className);
- }
- },
-
- attributeFollows: function(name, neo, old) {
- if (old) {
- old.removeAttribute(name);
- }
- if (neo) {
- neo.setAttribute(name, '');
- }
- },
-
- getContentChildNodes: function(slctr) {
- return Polymer.dom(Polymer.dom(this.root).querySelector(
- slctr || 'content')).getDistributedNodes();
- },
-
- getContentChildren: function(slctr) {
- return this.getContentChildNodes(slctr).filter(function(n) {
- return (n.nodeType === Node.ELEMENT_NODE);
- });
- },
-
- fire: function(type, detail, options) {
- options = options || Polymer.nob;
- var node = options.node || this;
- var detail = (detail === null || detail === undefined) ? Polymer.nob : detail;
- var bubbles = options.bubbles === undefined ? true : options.bubbles;
- var event = new CustomEvent(type, {
- bubbles: Boolean(bubbles),
- cancelable: Boolean(options.cancelable),
- detail: detail
- });
- node.dispatchEvent(event);
- return event;
- },
-
- async: function(method, waitTime) {
- return Polymer.Async.run(method.bind(this), waitTime);
- },
-
- cancelAsync: function(handle) {
- Polymer.Async.cancel(handle);
- },
-
- arrayDelete: function(array, item) {
- var index = array.indexOf(item);
- if (index >= 0) {
- return array.splice(index, 1);
- }
- },
-
- transform: function(node, transform) {
- node.style.webkitTransform = transform;
- node.style.transform = transform;
- },
-
- translate3d: function(node, x, y, z) {
- this.transform(node, 'translate3d(' + x + ',' + y + ',' + z + ')');
- },
-
- importHref: function(href, onload, onerror) {
- var l = document.createElement('link');
- l.rel = 'import';
- l.href = href;
- if (onload) {
- l.onload = onload.bind(this);
- }
- if (onerror) {
- l.onerror = onerror.bind(this);
- }
- document.head.appendChild(l);
- return l;
- },
-
- create: function(tag, props) {
- var elt = document.createElement(tag);
- if (props) {
- for (var n in props) {
- elt[n] = props[n];
- }
- }
- return elt;
- },
-
- simpleMixin: function(a, b) {
- for (var i in b) {
- a[i] = b[i];
- }
- }
-
- });
-
-
-;
-
- Polymer.Bind = {
-
- // for prototypes (usually)
-
- prepareModel: function(model) {
- model._propertyEffects = {};
- model._bindListeners = [];
- // TODO(sjmiles): no mixin function?
- var api = this._modelApi;
- for (var n in api) {
- model[n] = api[n];
- }
- },
-
- _modelApi: {
-
- _notifyChange: function(property) {
- var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed';
- // TODO(sjmiles): oops, `fire` doesn't exist at this layer
- this.fire(eventName, {
- value: this[property]
- }, {bubbles: false});
- },
-
- // TODO(sjmiles): removing _notifyListener from here breaks accessors.html
- // as a standalone lib. This is temporary, as standard/configure.html
- // installs it's own version on Polymer.Base, and we need that to work
- // right now.
- // NOTE: exists as a hook for processing listeners
- /*
- _notifyListener: function(fn, e) {
- // NOTE: pass e.target because e.target can get lost if this function
- // is queued asynchrously
- return fn.call(this, e, e.target);
- },
- */
-
- _propertySet: function(property, value, effects) {
- var old = this._data[property];
- if (old !== value) {
- this._data[property] = value;
- if (typeof value == 'object') {
- this._clearPath(property);
- }
- if (effects) {
- this._effectEffects(property, value, effects, old);
- }
- }
- return old;
- },
-
- _effectEffects: function(property, value, effects, old) {
- effects.forEach(function(fx) {
- //console.log(fx);
- var fn = Polymer.Bind[fx.kind + 'Effect'];
- if (fn) {
- fn.call(this, property, value, fx.effect, old);
- }
- }, this);
- },
-
- _clearPath: function(path) {
- for (var prop in this._data) {
- if (prop.indexOf(path + '.') === 0) {
- this._data[prop] = undefined;
- }
- }
- }
-
- },
-
- // a prepared model can acquire effects
-
- ensurePropertyEffects: function(model, property) {
- var fx = model._propertyEffects[property];
- if (!fx) {
- fx = model._propertyEffects[property] = [];
- }
- return fx;
- },
-
- addPropertyEffect: function(model, property, kind, effect) {
- var fx = this.ensurePropertyEffects(model, property);
- fx.push({
- kind: kind,
- effect: effect
- });
- },
-
- createBindings: function(model) {
- //console.group(model.is);
- // map of properties to effects
- var fx$ = model._propertyEffects;
- if (fx$) {
- // for each property with effects
- for (var n in fx$) {
- // array of effects
- var fx = fx$[n];
- // effects have priority
- fx.sort(this._sortPropertyEffects);
- // create accessors
- this._createAccessors(model, n, fx);
- }
- }
- //console.groupEnd();
- },
-
- _sortPropertyEffects: (function() {
- // TODO(sjmiles): EFFECT_ORDER buried this way is not ideal,
- // but presumably the sort method is going to be a hot path and not
- // have a `this`. There is also a problematic dependency on effect.kind
- // values here, which are otherwise pluggable.
- var EFFECT_ORDER = {
- 'compute': 0,
- 'annotation': 1,
- 'computedAnnotation': 2,
- 'reflect': 3,
- 'notify': 4,
- 'observer': 5,
- 'complexObserver': 6,
- 'function': 7
- };
- return function(a, b) {
- return EFFECT_ORDER[a.kind] - EFFECT_ORDER[b.kind];
- };
- })(),
-
- // create accessors that implement effects
-
- _createAccessors: function(model, property, effects) {
- var defun = {
- get: function() {
- // TODO(sjmiles): elide delegation for performance, good ROI?
- return this._data[property];
- }
- };
- var setter = function(value) {
- this._propertySet(property, value, effects);
- };
- // ReadOnly properties have a private setter only
- // TODO(kschaaf): Per current Bind factoring, we shouldn't
- // be interrogating the prototype here
- if (model.isReadOnlyProperty && model.isReadOnlyProperty(property)) {
- //model['_' + property + 'Setter'] = setter;
- //model['_set_' + property] = setter;
- model['_set' + this.upper(property)] = setter;
- } else {
- defun.set = setter;
- }
- Object.defineProperty(model, property, defun);
- },
-
- upper: function(name) {
- return name[0].toUpperCase() + name.substring(1);
- },
-
- _addAnnotatedListener: function(model, index, property, path, event) {
- var fn = this._notedListenerFactory(property, path,
- this._isStructured(path), this._isEventBogus);
- var eventName = event ||
- (Polymer.CaseMap.camelToDashCase(property) + '-changed');
- model._bindListeners.push({
- index: index,
- property: property,
- path: path,
- changedFn: fn,
- event: eventName
- });
- },
-
- _isStructured: function(path) {
- return path.indexOf('.') > 0;
- },
-
- _isEventBogus: function(e, target) {
- return e.path && e.path[0] !== target;
- },
-
- _notedListenerFactory: function(property, path, isStructured, bogusTest) {
- return function(e, target) {
- if (!bogusTest(e, target)) {
- if (e.detail && e.detail.path) {
- this.notifyPath(this._fixPath(path, property, e.detail.path),
- e.detail.value);
- } else {
- var value = target[property];
- if (!isStructured) {
- this[path] = target[property];
- } else {
- // TODO(kschaaf): dirty check avoids null references when the object has gone away
- if (this._data[path] != value) {
- this.setPathValue(path, value);
- }
- }
- }
- }
- };
- },
-
- // for instances
-
- prepareInstance: function(inst) {
- inst._data = Object.create(null);
- },
-
- setupBindListeners: function(inst) {
- inst._bindListeners.forEach(function(info) {
- // Property listeners:
- // <node>.on.<property>-changed: <path]> = e.detail.value
- //console.log('[_setupBindListener]: [%s][%s] listening for [%s][%s-changed]', this.localName, info.path, info.id || info.index, info.property);
- var node = inst._nodes[info.index];
- node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn));
- });
- }
-
- };
-
-
-;
-
- Polymer.Base.extend(Polymer.Bind, {
-
- _shouldAddListener: function(effect) {
- return effect.name &&
- effect.mode === '{' &&
- !effect.negate &&
- effect.kind != 'attribute'
- ;
- },
-
- annotationEffect: function(source, value, effect) {
- if (source != effect.value) {
- value = this.getPathValue(effect.value);
- this._data[effect.value] = value;
- }
- var calc = effect.negate ? !value : value;
- return this._applyEffectValue(calc, effect);
- },
-
- reflectEffect: function(source) {
- this.reflectPropertyToAttribute(source);
- },
-
- notifyEffect: function(source) {
- this._notifyChange(source);
- },
-
- // Raw effect for extension; effect.function is an actual function
- functionEffect: function(source, value, effect, old) {
- effect.function.call(this, source, value, effect, old);
- },
-
- observerEffect: function(source, value, effect, old) {
- this[effect.method](value, old);
- },
-
- complexObserverEffect: function(source, value, effect) {
- var args = Polymer.Bind._marshalArgs(this._data, effect, source, value);
- if (args) {
- this[effect.method].apply(this, args);
- }
- },
-
- computeEffect: function(source, value, effect) {
- var args = Polymer.Bind._marshalArgs(this._data, effect, source, value);
- if (args) {
- this[effect.property] = this[effect.method].apply(this, args);
- }
- },
-
- annotatedComputationEffect: function(source, value, effect) {
- var args = Polymer.Bind._marshalArgs(this._data, effect, source, value);
- if (args) {
- var computedHost = this._rootDataHost || this;
- var computedvalue =
- computedHost[effect.method].apply(computedHost, args);
- this._applyEffectValue(computedvalue, effect);
- }
- },
-
- // path & value are used to fill in wildcard descriptor when effect is
- // being called as a result of a path notification
- _marshalArgs: function(model, effect, path, value) {
- var values = [];
- var args = effect.args;
- for (var i=0, l=args.length; i<l; i++) {
- var arg = args[i];
- var name = arg.name;
- var v = arg.structured ?
- Polymer.Base.getPathValue(name, model) : model[name];
- if (v === undefined) {
- return;
- }
- if (arg.wildcard) {
- // Only send the actual path changed info if the change that
- // caused the observer to run matched the wildcard
- var baseChanged = (name.indexOf(path + '.') === 0);
- var matches = (effect.arg.name.indexOf(name) === 0 && !baseChanged);
- values[i] = {
- path: matches ? path : name,
- value: matches ? value : v,
- base: v
- };
- } else {
- values[i] = v;
- }
- }
- return values;
- }
-
- });
-
-
-;
-
- /**
- * Support for property side effects.
- *
- * Key for effect objects:
- *
- * property | ann | anCmp | cmp | obs | cplxOb | description
- * ---------|-----|-------|-----|-----|--------|----------------------------------------
- * method | | X | X | X | X | function name to call on instance
- * args | | X | X | | X | list of all arg descriptors for fn
- * arg | | X | X | | X | arg descriptor for effect
- * property | | | X | X | | property for effect to set or get
- * name | X | | | | | annotation value (text inside {{...}})
- * kind | X | X | | | | binding type (property or attribute)
- * index | X | X | | | | node index to set
- *
- */
-
- Polymer.Base._addFeature({
-
- _addPropertyEffect: function(property, kind, effect) {
- // TODO(sjmiles): everything to the right of the first '.' is lost, implies
- // there is some duplicate information flow (not the only sign)
- var model = property.split('.').shift();
- Polymer.Bind.addPropertyEffect(this, model, kind, effect);
- },
-
- // prototyping
-
- _prepEffects: function() {
- Polymer.Bind.prepareModel(this);
- this._addAnnotationEffects(this._notes);
- },
-
- _prepBindings: function() {
- Polymer.Bind.createBindings(this);
- },
-
- _addPropertyEffects: function(effects) {
- if (effects) {
- for (var n in effects) {
- var effect = effects[n];
- if (effect.observer) {
- this._addObserverEffect(n, effect.observer);
- }
- if (effect.computed) {
- this._addComputedEffect(n, effect.computed);
- }
- if (effect.notify) {
- this._addPropertyEffect(n, 'notify');
- }
- if (effect.reflectToAttribute) {
- this._addPropertyEffect(n, 'reflect');
- }
- if (this.isReadOnlyProperty(n)) {
- // Ensure accessor is created
- Polymer.Bind.ensurePropertyEffects(this, n);
- }
- }
- }
- },
-
- _parseMethod: function(expression) {
- var m = expression.match(/(\w*)\((.*)\)/);
- if (m) {
- return {
- method: m[1],
- args: m[2].split(/[^\w.*]+/).map(this._parseArg)
- };
- }
- },
-
- _parseArg: function(arg) {
- var a = { name: arg };
- a.structured = arg.indexOf('.') > 0;
- if (a.structured) {
- a.wildcard = (arg.slice(-2) == '.*');
- if (a.wildcard) {
- a.name = arg.slice(0, -2);
- }
- }
- return a;
- },
-
- _addComputedEffect: function(name, expression) {
- var sig = this._parseMethod(expression);
- sig.args.forEach(function(arg) {
- this._addPropertyEffect(arg.name, 'compute', {
- method: sig.method,
- args: sig.args,
- arg: arg,
- property: name
- });
- }, this);
- },
-
- _addObserverEffect: function(property, observer) {
- this._addPropertyEffect(property, 'observer', {
- method: observer,
- property: property
- });
- },
-
- _addComplexObserverEffects: function(observers) {
- if (observers) {
- observers.forEach(function(observer) {
- this._addComplexObserverEffect(observer);
- }, this);
- }
- },
-
- _addComplexObserverEffect: function(observer) {
- var sig = this._parseMethod(observer);
- sig.args.forEach(function(arg) {
- this._addPropertyEffect(arg.name, 'complexObserver', {
- method: sig.method,
- args: sig.args,
- arg: arg
- });
- }, this);
- },
-
- _addAnnotationEffects: function(notes) {
- // create a virtual annotation list, must be concretized at instance time
- this._nodes = [];
- // process annotations that have been parsed from template
- notes.forEach(function(note) {
- // where to find the node in the concretized list
- var index = this._nodes.push(note) - 1;
- note.bindings.forEach(function(binding) {
- this._addAnnotationEffect(binding, index);
- }, this);
- }, this);
- },
-
- _addAnnotationEffect: function(note, index) {
- // TODO(sjmiles): annotations have 'effects' proper and 'listener'
- if (Polymer.Bind._shouldAddListener(note)) {
- // <node>.on.<dash-case-property>-changed: <path> = e.detail.value
- Polymer.Bind._addAnnotatedListener(this, index,
- note.name, note.value, note.event);
- }
- var sig = this._parseMethod(note.value);
- if (sig) {
- this._addAnnotatedComputationEffect(sig, note, index);
- } else {
- // capture the node index
- note.index = index;
- // discover top-level property (model) from path
- var model = note.value.split('.').shift();
- // add 'annotation' binding effect for property 'model'
- this._addPropertyEffect(model, 'annotation', note);
- }
- },
-
- _addAnnotatedComputationEffect: function(sig, note, index) {
- sig.args.forEach(function(arg) {
- this._addPropertyEffect(arg.name, 'annotatedComputation', {
- kind: note.kind,
- method: sig.method,
- args: sig.args,
- arg: arg,
- property: note.name,
- index: index
- });
- }, this);
- },
-
- // instancing
-
- _marshalInstanceEffects: function() {
- Polymer.Bind.prepareInstance(this);
- Polymer.Bind.setupBindListeners(this);
- },
-
- _applyEffectValue: function(value, info) {
- var node = this._nodes[info.index];
- // TODO(sorvell): ideally, the info object is normalized for easy
- // lookup here.
- var property = info.property || info.name || 'textContent';
- // special processing for 'class' and 'className'; 'class' handled
- // when attr is serialized.
- if (info.kind == 'attribute') {
- this.serializeValueToAttribute(value, property, node);
- } else {
- // TODO(sorvell): consider pre-processing this step so we don't need
- // this lookup.
- if (property === 'className') {
- value = this._scopeElementClass(node, value);
- }
- return node[property] = value;
- }
- }
-
- });
-
-
-;
-
- /*
- Process inputs efficiently via a configure lifecycle callback.
- Configure is called top-down, host before local dom. Users should
- implement configure to supply a set of default values for the element by
- returning an object containing the properties and values to set.
-
- Configured values are not immediately set, instead they are set when
- an element becomes ready, after its local dom is ready. This ensures
- that any user change handlers are not called before ready time.
-
- */
-
- /*
- Implementation notes:
-
- Configured values are collected into _config. At ready time, properties
- are set to the values in _config. This ensures properties are set child
- before host and change handlers are called only at ready time. The host
- will reset a value already propagated to a child, but this is not
- inefficient because of dirty checking at the set point.
-
- Bind notification events are sent when properties are set at ready time
- and thus received by the host before it is ready. Since notifications result
- in property updates and this triggers side effects, handling notifications
- is deferred until ready time.
-
- In general, events can be heard before an element is ready. This may occur
- when a user sends an event in a change handler or listens to a data event
- directly (on-foo-changed).
- */
-
- Polymer.Base._addFeature({
-
- // storage for configuration
- _setupConfigure: function(initialConfig) {
- this._config = initialConfig || {};
- this._handlers = [];
- },
-
- // static attributes are deserialized into _config
- _takeAttributes: function() {
- this._takeAttributesToModel(this._config);
- },
-
- // at configure time values are stored in _config
- _configValue: function(name, value) {
- this._config[name] = value;
- },
-
- // Override polymer-mini thunk
- _beforeClientsReady: function() {
- this._configure();
- },
-
- // configure: returns user supplied default property values
- // combines with _config to create final property values
- _configure: function() {
- this._configureAnnotationReferences();
- // get individual default values from property configs
- var config = {};
- this._configureProperties(this.properties, config);
- // behave!
- this.behaviors.forEach(function(b) {
- this._configureProperties(b.properties, config);
- }, this);
- // get add'l default values from central configure
- // combine defaults returned from configure with inputs in _config
- this._mixinConfigure(config, this._config);
- // this is the new _config, which are the final values to be applied
- this._config = config;
- // pass configuration data to bindings
- this._distributeConfig(this._config);
- },
-
- _configureProperties: function(properties, config) {
- for (i in properties) {
- var c = properties[i];
- if (c.value !== undefined) {
- var value = c.value;
- if (typeof value == 'function') {
- // pass existing config values (this._config) to value function
- value = value.call(this, this._config);
- }
- config[i] = value;
- }
- }
- },
-
- _mixinConfigure: function(a, b) {
- for (var prop in b) {
- if (!this.isReadOnlyProperty(prop)) {
- a[prop] = b[prop];
- }
- }
- },
-
- // distribute config values to bound nodes.
- _distributeConfig: function(config) {
- var fx$ = this._propertyEffects;
- if (fx$) {
- for (var p in config) {
- var fx = fx$[p];
- if (fx) {
- for (var i=0, l=fx.length, x; (i<l) && (x=fx[i]); i++) {
- if (x.kind === 'annotation') {
- var node = this._nodes[x.effect.index];
- // seeding configuration only
- if (node._configValue) {
- var value = (p === x.effect.value) ? config[p] :
- this.getPathValue(x.effect.value, config);
- node._configValue(x.effect.name, value);
- }
- }
- }
- }
- }
- }
- },
-
- // Override polymer-mini thunk
- _afterClientsReady: function() {
- this._applyConfig(this._config);
- this._flushHandlers();
- },
-
- // NOTE: values are already propagated to children via
- // _distributeConfig so propagation triggered by effects here is
- // redundant, but safe due to dirty checking
- _applyConfig: function(config) {
- for (var n in config) {
- // Don't stomp on values that may have been set by other side effects
- if (this[n] === undefined) {
- // Call _propertySet for any properties with accessors, which will
- // initialize read-only properties also
- // TODO(kschaaf): consider passing fromAbove here to prevent
- // unnecessary notify for: 1) possible perf, 2) debuggability
- var effects = this._propertyEffects[n];
- if (effects) {
- this._propertySet(n, config[n], effects);
- } else {
- this[n] = config[n];
- }
- }
- }
- },
-
- // NOTE: Notifications can be processed before ready since
- // they are sent at *child* ready time. Since notifications cause side
- // effects and side effects must not be processed before ready time,
- // handling is queue/defered until then.
- _notifyListener: function(fn, e) {
- if (!this._clientsReadied) {
- this._queueHandler([fn, e, e.target]);
- } else {
- return fn.call(this, e, e.target);
- }
- },
-
- _queueHandler: function(args) {
- this._handlers.push(args);
- },
-
- _flushHandlers: function() {
- var h$ = this._handlers;
- for (var i=0, l=h$.length, h; (i<l) && (h=h$[i]); i++) {
- h[0].call(this, h[1], h[2]);
- }
- }
-
- });
-
-
-;
-
- /**
- * Changes to an object sub-field (aka "path") via a binding
- * (e.g. `<x-foo value="{{item.subfield}}"`) will notify other elements bound to
- * the same object automatically.
- *
- * When modifying a sub-field of an object imperatively
- * (e.g. `this.item.subfield = 42`), in order to have the new value propagated
- * to other elements, a special `setPathValue(path, value)` API is provided.
- * `setPathValue` sets the object field at the path specified, and then notifies the
- * binding system so that other elements bound to the same path will update.
- *
- * Example:
- *
- * Polymer({
- *
- * is: 'x-date',
- *
- * properties: {
- * date: {
- * type: Object,
- * notify: true
- * }
- * },
- *
- * attached: function() {
- * this.date = {};
- * setInterval(function() {
- * var d = new Date();
- * // Required to notify elements bound to date of changes to sub-fields
- * // this.date.seconds = d.getSeconds(); <-- Will not notify
- * this.setPathValue('date.seconds', d.getSeconds());
- * this.setPathValue('date.minutes', d.getMinutes());
- * this.setPathValue('date.hours', d.getHours() % 12);
- * }.bind(this), 1000);
- * }
- *
- * });
- *
- * Allows bindings to `date` sub-fields to update on changes:
- *
- * <x-date date="{{date}}"></x-date>
- *
- * Hour: <span>{{date.hours}}</span>
- * Min: <span>{{date.minutes}}</span>
- * Sec: <span>{{date.seconds}}</span>
- *
- * @class data feature: path notification
- */
-
- Polymer.Base._addFeature({
- /**
- Notify that a path has changed. For example:
-
- this.item.user.name = 'Bob';
- this.notifyPath('item.user.name', this.item.user.name);
-
- Returns true if notification actually took place, based on
- a dirty check of whether the new value was already known
- */
- notifyPath: function(path, value, fromAbove) {
- var old = this._propertySet(path, value);
- // manual dirty checking for now...
- if (old !== value) {
- // console.group((this.localName || this.dataHost.id + '-' + this.dataHost.dataHost.index) + '#' + (this.id || this.index) + ' ' + path, value);
- // Take path effects at this level for exact path matches,
- // and notify down for any bindings to a subset of this path
- this._pathEffector(path, value);
- // Send event to notify the path change upwards
- // Optimization: don't notify up if we know the notification
- // is coming from above already (avoid wasted event dispatch)
- if (!fromAbove) {
- // TODO(sorvell): should only notify if notify: true?
- this._notifyPath(path, value);
- }
- // console.groupEnd((this.localName || this.dataHost.id + '-' + this.dataHost.dataHost.index) + '#' + (this.id || this.index) + ' ' + path, value);
- }
- },
-
- /**
- Convienence method for setting a value to a path and calling
- notify path
- */
- setPathValue: function(path, value) {
- var parts = path.split('.');
- if (parts.length > 1) {
- var last = parts.pop();
- var prop = this;
- while (parts.length) {
- prop = prop[parts.shift()];
- if (!prop) {
- return;
- }
- }
- // TODO(kschaaf): want dirty-check here?
- // if (prop[last] !== value) {
- prop[last] = value;
- this.notifyPath(path, value);
- // }
- } else {
- this[path] = value;
- }
- },
-
- getPathValue: function(path, root) {
- var parts = path.split('.');
- var last = parts.pop();
- var prop = root || this;
- while (parts.length) {
- prop = prop[parts.shift()];
- if (!prop) {
- return;
- }
- }
- return prop[last];
- },
-
- // TODO(kschaaf): This machine can be optimized to memoize compiled path
- // effectors as new paths are notified for performance, since it involves
- // a fair amount of runtime lookup
- _pathEffector: function(path, value) {
- // get root property
- var model = this._modelForPath(path);
- // search property effects of the root property for 'annotation' effects
- var fx$ = this._propertyEffects[model];
- if (fx$) {
- fx$.forEach(function(fx) {
- var fxFn = this[fx.kind + 'PathEffect'];
- if (fxFn) {
- fxFn.call(this, path, value, fx.effect);
- }
- }, this);
- }
- // notify runtime-bound paths
- if (this._boundPaths) {
- this._notifyBoundPaths(path, value);
- }
- },
-
- annotationPathEffect: function(path, value, effect) {
- if (effect.value === path || effect.value.indexOf(path + '.') === 0) {
- // TODO(sorvell): ideally the effect function is on this prototype
- // so we don't have to call it like this.
- Polymer.Bind.annotationEffect.call(this, path, value, effect);
- } else if ((path.indexOf(effect.value + '.') === 0) && !effect.negate) {
- // locate the bound node
- var node = this._nodes[effect.index];
- if (node && node.notifyPath) {
- var p = this._fixPath(effect.name , effect.value, path);
- node.notifyPath(p, value, true);
- }
- }
- },
-
- complexObserverPathEffect: function(path, value, effect) {
- if (this._pathMatchesEffect(path, effect)) {
- Polymer.Bind.complexObserverEffect.call(this, path, value, effect);
- }
- },
-
- computePathEffect: function(path, value, effect) {
- if (this._pathMatchesEffect(path, effect)) {
- Polymer.Bind.computeEffect.call(this, path, value, effect);
- }
- },
-
- annotatedComputationPathEffect: function(path, value, effect) {
- if (this._pathMatchesEffect(path, effect)) {
- Polymer.Bind.annotatedComputationEffect.call(this, path, value, effect);
- }
- },
-
- _pathMatchesEffect: function(path, effect) {
- var effectArg = effect.arg.name;
- return (effectArg == path) ||
- (effectArg.indexOf(path + '.') === 0) ||
- (effect.arg.wildcard && path.indexOf(effectArg) === 0);
- },
-
- bindPaths: function(to, from) {
- this._boundPaths = this._boundPaths || {};
- if (from) {
- this._boundPaths[to] = from;
- // this.setPathValue(to, this.getPathValue(from));
- } else {
- this.unbindPath(to);
- // this.setPathValue(to, from);
- }
- },
-
- unbindPaths: function(path) {
- if (this._boundPaths) {
- delete this._boundPaths[path];
- }
- },
-
- _notifyBoundPaths: function(path, value) {
- var from, to;
- for (var a in this._boundPaths) {
- var b = this._boundPaths[a];
- if (path.indexOf(a + '.') == 0) {
- from = a;
- to = b;
- break;
- }
- if (path.indexOf(b + '.') == 0) {
- from = b;
- to = a;
- break;
- }
- }
- if (from && to) {
- var p = this._fixPath(to, from, path);
- this.notifyPath(p, value);
- }
- },
-
- _fixPath: function(property, root, path) {
- return property + path.slice(root.length);
- },
-
- _notifyPath: function(path, value) {
- var rootName = this._modelForPath(path);
- var dashCaseName = Polymer.CaseMap.camelToDashCase(rootName);
- var eventName = dashCaseName + this._EVENT_CHANGED;
- this.fire(eventName, {
- path: path,
- value: value
- }, {bubbles: false});
- },
-
- _modelForPath: function(path) {
- return path.split('.').shift();
- },
-
- _EVENT_CHANGED: '-changed',
-
- });
-
-
-;
-
- Polymer.Base._addFeature({
-
- resolveUrl: function(url) {
- // TODO(sorvell): do we want to put the module reference on the prototype?
- var module = Polymer.DomModule.import(this.is);
- var root = '';
- if (module) {
- var assetPath = module.getAttribute('assetpath') || '';
- root = Polymer.ResolveUrl.resolveUrl(assetPath, module.ownerDocument.baseURI);
- }
- return Polymer.ResolveUrl.resolveUrl(url, root);
- }
-
- });
-
-
-;
-
-/*
- Extremely simple css parser. Intended to be not more than what we need
- and definitely not necessarly correct =).
-*/
-(function() {
-
- // given a string of css, return a simple rule tree
- function parse(text) {
- text = clean(text);
- return parseCss(lex(text), text);
- }
-
- // remove stuff we don't care about that may hinder parsing
- function clean(cssText) {
- return cssText.replace(rx.comments, '').replace(rx.port, '');
- }
-
- // super simple {...} lexer that returns a node tree
- function lex(text) {
- var root = {start: 0, end: text.length};
- var n = root;
- for (var i=0, s=0, l=text.length; i < l; i++) {
- switch (text[i]) {
- case OPEN_BRACE:
- //console.group(i);
- if (!n.rules) {
- n.rules = [];
- }
- var p = n;
- var previous = p.rules[p.rules.length-1];
- n = {start: i+1, parent: p, previous: previous};
- p.rules.push(n);
- break;
- case CLOSE_BRACE:
- //console.groupEnd(n.start);
- n.end = i+1;
- n = n.parent || root;
- break;
- }
- }
- return root;
- }
-
- // add selectors/cssText to node tree
- function parseCss(node, text) {
- var t = text.substring(node.start, node.end-1);
- node.cssText = t.trim();
- if (node.parent) {
- var ss = node.previous ? node.previous.end : node.parent.start;
- t = text.substring(ss, node.start-1);
- // TODO(sorvell): ad hoc; make selector include only after last ;
- // helps with mixin syntax
- t = t.substring(t.lastIndexOf(';')+1);
- node.selector = t.trim();
- }
- var r$ = node.rules;
- if (r$) {
- for (var i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
- parseCss(r, text);
- }
- }
- return node;
- }
-
- // stringify parsed css.
- function stringify(node, text) {
- text = text || '';
- // calc rule cssText
- var cssText = '';
- if (node.cssText || node.rules) {
- var r$ = node.rules;
- if (r$ && !hasMixinRules(r$)) {
- for (var i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
- cssText = stringify(r, cssText);
- }
- } else {
- cssText = removeCustomProps(node.cssText).trim();
- if (cssText) {
- cssText = ' ' + cssText + '\n';
- }
- }
- }
- // emit rule iff there is cssText
- if (cssText) {
- if (node.selector) {
- text += node.selector + ' ' + OPEN_BRACE + '\n';
- }
- text += cssText;
- if (node.selector) {
- text += CLOSE_BRACE + '\n\n';
- }
- }
- return text;
- }
-
- var OPEN_BRACE = '{';
- var CLOSE_BRACE = '}';
-
- function hasMixinRules(rules) {
- return (rules[0].selector.indexOf(VAR_START) >= 0);
- }
-
- function removeCustomProps(cssText) {
- return cssText
- .replace(rx.customProp, '')
- .replace(rx.mixinProp, '')
- .replace(rx.mixinApply, '');
- }
-
- var VAR_START = '--';
-
- // helper regexp's
- var rx = {
- comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
- port: /@import[^;]*;/gim,
- customProp: /--[^;{]*?:[^{};]*?;/gim,
- mixinProp: /--[^;{]*?:[^{;]*?{[^}]*?}/gim,
- mixinApply: /@mixin[\s]*\([^)]*?\)[\s]*;/gim
- };
-
- // exports
- Polymer.CssParse = {
- parse: parse,
- stringify: stringify
- };
-
-})();
-
-
-;
-
- (function() {
-
- function toCssText(rules, callback) {
- if (typeof rules === 'string') {
- rules = Polymer.CssParse.parse(rules);
- }
- if (callback) {
- forEachStyleRule(rules, callback);
- }
- return Polymer.CssParse.stringify(rules);
- }
-
- function forEachStyleRule(node, cb) {
- var s = node.selector;
- var skipRules = false;
- if (s) {
- if ((s.indexOf(AT_RULE) !== 0) && (s.indexOf(MIXIN_SELECTOR) !== 0)) {
- cb(node);
- }
- skipRules = (s.indexOf(KEYFRAME_RULE) >= 0) ||
- (s.indexOf(MIXIN_SELECTOR) >= 0);
- }
- var r$ = node.rules;
- if (r$ && !skipRules) {
- for (var i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
- forEachStyleRule(r, cb);
- }
- }
- }
-
- // add a string of cssText to the document.
- function applyCss(cssText, moniker, target, lowPriority) {
- var style = document.createElement('style');
- if (moniker) {
- style.setAttribute('scope', moniker);
- }
- style.textContent = cssText;
- target = target || document.head;
- if (lowPriority) {
- var n$ = target.querySelectorAll('style[scope]');
- var ref = n$.length ? n$[n$.length-1].nextSibling : target.firstChild;
- target.insertBefore(style, ref);
- } else {
- target.appendChild(style);
- }
- return style;
- }
-
- var AT_RULE = '@';
- var KEYFRAME_RULE = 'keyframe';
- var MIXIN_SELECTOR = '--';
-
- // exports
- Polymer.StyleUtil = {
- parser: Polymer.CssParse,
- applyCss: applyCss,
- forEachStyleRule: forEachStyleRule,
- toCssText: toCssText
- };
-
- })();
-
-
-;
-
- (function() {
-
- /* Transforms ShadowDOM styling into ShadyDOM styling
-
- * scoping:
-
- * elements in scope get scoping selector class="x-foo-scope"
- * selectors re-written as follows:
-
- div button -> div.x-foo-scope button.x-foo-scope
-
- * :host -> scopeName
-
- * :host(...) -> scopeName...
-
- * ::content -> ' ' NOTE: requires use of scoping selector and selectors
- cannot otherwise be scoped:
- e.g. :host ::content > .bar -> x-foo > .bar
-
- * ::shadow, /deep/: processed simimlar to ::content
-
- * :host-context(...): NOT SUPPORTED
-
- */
-
- // Given a node and scope name, add a scoping class to each node
- // in the tree. This facilitates transforming css into scoped rules.
- function transformDom(node, scope, useAttr, shouldRemoveScope) {
- _transformDom(node, scope || '', useAttr, shouldRemoveScope);
- }
-
- function _transformDom(node, selector, useAttr, shouldRemoveScope) {
- if (node.setAttribute) {
- transformElement(node, selector, useAttr, shouldRemoveScope);
- }
- var c$ = Polymer.dom(node).childNodes;
- for (var i=0; i<c$.length; i++) {
- _transformDom(c$[i], selector, useAttr, shouldRemoveScope);
- }
- }
-
- function transformElement(element, scope, useAttr, shouldRemoveScope) {
- if (useAttr) {
- if (shouldRemoveScope) {
- element.removeAttribute(SCOPE_NAME);
- } else {
- element.setAttribute(SCOPE_NAME, scope);
- }
- } else {
- // note: if using classes, we add both the general 'style-scope' class
- // as well as the specific scope. This enables easy filtering of all
- // `style-scope` elements
- if (scope) {
- if (shouldRemoveScope) {
- element.classList.remove(SCOPE_NAME, scope);
- } else {
- element.classList.add(SCOPE_NAME, scope);
- }
- }
- }
- }
-
- function transformHost(host, scope) {
- }
-
- // Given a string of cssText and a scoping string (scope), returns
- // a string of scoped css where each selector is transformed to include
- // a class created from the scope. ShadowDOM selectors are also transformed
- // (e.g. :host) to use the scoping selector.
- function transformCss(rules, scope, ext, callback, useAttr) {
- var hostScope = calcHostScope(scope, ext);
- scope = calcElementScope(scope, useAttr);
- return Polymer.StyleUtil.toCssText(rules, function(rule) {
- transformRule(rule, scope, hostScope);
- if (callback) {
- callback(rule, scope, hostScope);
- }
- });
- }
-
- function calcElementScope(scope, useAttr) {
- if (scope) {
- return useAttr ?
- CSS_ATTR_PREFIX + scope + CSS_ATTR_SUFFIX :
- CSS_CLASS_PREFIX + scope;
- } else {
- return '';
- }
- }
-
- function calcHostScope(scope, ext) {
- return ext ? '[is=' + scope + ']' : scope;
- }
-
- function transformRule(rule, scope, hostScope) {
- _transformRule(rule, transformComplexSelector,
- scope, hostScope);
- }
-
- // transforms a css rule to a scoped rule.
- function _transformRule(rule, transformer, scope, hostScope) {
- var p$ = rule.selector.split(COMPLEX_SELECTOR_SEP);
- for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
- p$[i] = transformer(p, scope, hostScope);
- }
- rule.selector = p$.join(COMPLEX_SELECTOR_SEP);
- }
-
- function transformComplexSelector(selector, scope, hostScope) {
- var stop = false;
- selector = selector.replace(SIMPLE_SELECTOR_SEP, function(m, c, s) {
- if (!stop) {
- var o = transformCompoundSelector(s, c, scope, hostScope);
- if (o.stop) {
- stop = true;
- }
- c = o.combinator;
- s = o.value;
- }
- return c + s;
- });
- return selector;
- }
-
- function transformCompoundSelector(selector, combinator, scope, hostScope) {
- // replace :host with host scoping class
- var jumpIndex = selector.search(SCOPE_JUMP);
- if (selector.indexOf(HOST) >=0) {
- // :host(...)
- selector = selector.replace(HOST_PAREN, function(m, host, paren) {
- return hostScope + paren;
- });
- // now normal :host
- selector = selector.replace(HOST, hostScope);
- // replace other selectors with scoping class
- } else if (jumpIndex !== 0) {
- selector = scope ? transformSimpleSelector(selector, scope) : selector;
- }
- // remove left-side combinator when dealing with ::content.
- if (selector.indexOf(CONTENT) >= 0) {
- combinator = '';
- }
- // process scope jumping selectors up to the scope jump and then stop
- // e.g. .zonk ::content > .foo ==> .zonk.scope > .foo
- var stop;
- if (jumpIndex >= 0) {
- selector = selector.replace(SCOPE_JUMP, ' ');
- stop = true;
- }
- return {value: selector, combinator: combinator, stop: stop};
- }
-
- function transformSimpleSelector(selector, scope) {
- var p$ = selector.split(PSEUDO_PREFIX);
- p$[0] += scope;
- return p$.join(PSEUDO_PREFIX);
- }
-
- function transformRootRule(rule) {
- _transformRule(rule, transformRootSelector);
- }
-
- function transformRootSelector(selector) {
- return selector.match(SCOPE_JUMP) ?
- transformComplexSelector(selector) :
- selector.trim() + SCOPE_ROOT_SELECTOR;
- }
-
- var SCOPE_NAME = 'style-scope';
- var SCOPE_ROOT_SELECTOR = ':not([' + SCOPE_NAME + '])' +
- ':not(.' + SCOPE_NAME + ')';
- var COMPLEX_SELECTOR_SEP = ',';
- var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)([^\s>+~]+)/g;
- var HOST = ':host';
- // NOTE: this supports 1 nested () pair for things like
- // :host(:not([selected]), more general support requires
- // parsing which seems like overkill
- var HOST_PAREN = /(\:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g;
- var CONTENT = '::content';
- var SCOPE_JUMP = /\:\:content|\:\:shadow|\/deep\//;
- var CSS_CLASS_PREFIX = '.';
- var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~=';
- var CSS_ATTR_SUFFIX = ']';
- var PSEUDO_PREFIX = ':';
-
- // exports
- Polymer.StyleTransformer = {
- element: transformElement,
- dom: transformDom,
- host: transformHost,
- css: transformCss,
- rule: transformRule,
- rootRule: transformRootRule,
- SCOPE_NAME: SCOPE_NAME
- };
-
- })();
-
-
-;
-
- (function() {
-
- var prepTemplate = Polymer.Base._prepTemplate;
- var prepElement = Polymer.Base._prepElement;
- var baseStampTemplate = Polymer.Base._stampTemplate;
- var nativeShadow = Polymer.Settings.useNativeShadow;
-
- Polymer.Base._addFeature({
-
- // declaration-y
- _prepTemplate: function() {
- prepTemplate.call(this);
- var port = Polymer.DomModule.import(this.is);
- if (this._encapsulateStyle === undefined) {
- this._encapsulateStyle =
- Boolean(port && !nativeShadow);
- }
- // scope css
- // NOTE: dom scoped via annotations
- if (nativeShadow || this._encapsulateStyle) {
- this._scopeCss();
- }
- },
-
- _prepElement: function(element) {
- if (this._encapsulateStyle) {
- Polymer.StyleTransformer.element(element, this.is,
- this._scopeCssViaAttr);
- }
- prepElement.call(this, element);
- },
-
- _scopeCss: function() {
- this._styles = this._prepareStyles();
- this._scopeStyles(this._styles);
- },
-
- // search for extra style modules via `styleModules`
- _prepareStyles: function() {
- var cssText = '', m$ = this.styleModules;
- if (m$) {
- for (var i=0, l=m$.length, m; (i<l) && (m=m$[i]); i++) {
- cssText += this._cssFromModule(m);
- }
- }
- cssText += this._cssFromModule(this.is);
- var styles = [];
- if (cssText) {
- var s = document.createElement('style');
- s.textContent = cssText;
- styles.push(s);
- }
- return styles;
- },
-
- // returns cssText of styles in a given module; also un-applies any
- // styles that apply to the document.
- _cssFromModule: function(moduleId) {
- var m = Polymer.DomModule.import(moduleId);
- if (m && !m._cssText) {
- var cssText = '';
- var e$ = Array.prototype.slice.call(m.querySelectorAll('style'));
- this._unapplyStyles(e$);
- e$ = e$.concat(Array.prototype.map.call(
- m.querySelectorAll(REMOTE_SHEET_SELECTOR), function(l) {
- return l.import.body;
- }));
- m._cssText = this._cssFromStyles(e$);
- }
- return m && m._cssText || '';
- },
-
- _cssFromStyles: function(styles) {
- var cssText = '';
- for (var i=0, l=styles.length, s; (i<l) && (s = styles[i]); i++) {
- if (s && s.textContent) {
- cssText +=
- Polymer.ResolveUrl.resolveCss(s.textContent, s.ownerDocument);
- }
- }
- return cssText;
- },
-
- _unapplyStyles: function(styles) {
- for (var i=0, l=styles.length, s; (i<l) && (s = styles[i]); i++) {
- s = s.__appliedElement || s;
- s.parentNode.removeChild(s);
- }
- },
-
- _scopeStyles: function(styles) {
- for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
- // transform style if necessary and place in correct place
- if (nativeShadow) {
- if (this._template) {
- this._template.content.appendChild(s);
- }
- } else {
- var rules = this._rulesForStyle(s);
- Polymer.StyleUtil.applyCss(
- Polymer.StyleTransformer.css(rules, this.is, this.extends,
- null, this._scopeCssViaAttr),
- this.is, null, true);
- }
- }
- },
-
- _rulesForStyle: function(style) {
- if (!style.__cssRules) {
- style.__cssRules = Polymer.StyleUtil.parser.parse(style.textContent);
- }
- return style.__cssRules;
- },
-
- // instance-y
- _stampTemplate: function() {
- if (this._encapsulateStyle) {
- Polymer.StyleTransformer.host(this, this.is);
- }
- baseStampTemplate.call(this);
- },
-
- // add scoping class whenever an element is added to localDOM
- _elementAdd: function(node) {
- if (this._encapsulateStyle && !node.__styleScoped) {
- Polymer.StyleTransformer.dom(node, this.is, this._scopeCssViaAttr);
- }
- },
-
- // remove scoping class whenever an element is removed from localDOM
- _elementRemove: function(node) {
- if (this._encapsulateStyle) {
- Polymer.StyleTransformer.dom(node, this.is, this._scopeCssViaAttr, true);
- }
- },
-
- /**
- * Apply style scoping to the specified `container` and all its
- * descendants. If `shoudlObserve` is true, changes to the container are
- * monitored via mutation observer and scoping is applied.
- */
- scopeSubtree: function(container, shouldObserve) {
- if (nativeShadow) {
- return;
- }
- var self = this;
- var scopify = function(node) {
- if (node.nodeType === Node.ELEMENT_NODE) {
- node.className = self._scopeElementClass(node, node.className);
- var n$ = node.querySelectorAll('*');
- Array.prototype.forEach.call(n$, function(n) {
- n.className = self._scopeElementClass(n, n.className);
- });
- }
- };
- scopify(container);
- if (shouldObserve) {
- var mo = new MutationObserver(function(mxns) {
- mxns.forEach(function(m) {
- if (m.addedNodes) {
- for (var i=0; i < m.addedNodes.length; i++) {
- scopify(m.addedNodes[i]);
- }
- }
- });
- });
- mo.observe(container, {childList: true, subtree: true});
- return mo;
- }
- }
-
- });
-
- var REMOTE_SHEET_SELECTOR = 'link[rel=import][type~=css]';
-
- })();
-
-
-;
-
- (function() {
-
- var defaultSheet = document.createElement('style');
-
- function applyCss(cssText) {
- defaultSheet.textContent += cssText;
- defaultSheet.__cssRules =
- Polymer.StyleUtil.parser.parse(defaultSheet.textContent);
- }
-
- applyCss('');
-
- // exports
- Polymer.StyleDefaults = {
- applyCss: applyCss,
- defaultSheet: defaultSheet
- };
-
- })();
-
-;
- (function() {
-
- var baseAttachedCallback = Polymer.Base.attachedCallback;
- var baseSerializeValueToAttribute = Polymer.Base.serializeValueToAttribute;
-
- var nativeShadow = Polymer.Settings.useNativeShadow;
-
- // TODO(sorvell): consider if calculating properties and applying
- // styles with properties should be separate modules.
- Polymer.Base._addFeature({
-
- attachedCallback: function() {
- baseAttachedCallback.call(this);
- if (!this._xScopeSelector) {
- this._updateOwnStyles();
- }
- },
-
- _updateOwnStyles: function() {
- if (this.enableCustomStyleProperties) {
- this._styleProperties = this._computeStyleProperties();
- this._applyStyleProperties(this._styleProperties);
- }
- },
-
- _computeStyleProperties: function() {
- var props = {};
- this.simpleMixin(props, this._computeStylePropertiesFromHost());
- this.simpleMixin(props, this._computeOwnStyleProperties());
- this._reifyCustomProperties(props);
- return props;
- },
-
- _computeStylePropertiesFromHost: function() {
- // TODO(sorvell): experimental feature, global defaults!
- var props = {}, styles = [Polymer.StyleDefaults.defaultSheet];
- var host = this.domHost;
- if (host) {
- // enable finding styles in hosts without `enableStyleCustomProperties`
- if (!host._styleProperties) {
- host._styleProperties = host._computeStyleProperties();
- }
- props = Object.create(host._styleProperties);
- styles = host._styles;
- }
- this.simpleMixin(props,
- this._customPropertiesFromStyles(styles, host));
- return props;
-
- },
-
- _computeOwnStyleProperties: function() {
- var props = {};
- this.simpleMixin(props, this._customPropertiesFromStyles(this._styles));
- if (this.styleProperties) {
- for (var i in this.styleProperties) {
- props[i] = this.styleProperties[i];
- }
- }
- return props;
- },
-
- _customPropertiesFromStyles: function(styles, hostNode) {
- var props = {};
- var p = this._customPropertiesFromRule.bind(this, props, hostNode);
- if (styles) {
- for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
- Polymer.StyleUtil.forEachStyleRule(this._rulesForStyle(s), p);
- }
- }
- return props;
- },
-
- // test if a rule matches the given node and if so,
- // collect any custom properties
- // TODO(sorvell): support custom variable assignment within mixins
- _customPropertiesFromRule: function(props, hostNode, rule) {
- hostNode = hostNode || this;
- // TODO(sorvell): file crbug, ':host' does not match element.
- if (this.elementMatches(rule.selector) ||
- ((hostNode === this) && (rule.selector === ':host'))) {
- // --g: var(--b); or --g: 5;
- this._collectPropertiesFromRule(rule, CUSTOM_VAR_ASSIGN, props);
- // --g: { ... }
- this._collectPropertiesFromRule(rule, CUSTOM_MIXIN_ASSIGN, props);
- }
- },
-
- // given a rule and rx that matches key and value, set key in properties
- // to value
- _collectPropertiesFromRule: function(rule, rx, properties) {
- var m;
- while (m = rx.exec(rule.cssText)) {
- properties[m[1]] = m[2].trim();
- }
- },
-
- _reifyCustomProperties: function(props) {
- for (var i in props) {
- props[i] = this._valueForCustomProperty(props[i], props);
- }
- },
-
- _valueForCustomProperty: function(property, props) {
- var cv;
- while ((typeof property === 'string') &&
- (cv = property.match(CUSTOM_VAR_VALUE))) {
- property = props[cv[1]];
- }
- return property;
- },
-
- // apply styles
- _applyStyleProperties: function(bag) {
- var s$ = this._styles;
- if (s$) {
- var style = styleFromCache(this.is, bag, s$);
- var old = this._xScopeSelector;
- this._ensureScopeSelector(style ? style._scope : null);
- if (!style) {
- var cssText = this._generateCustomStyleCss(bag, s$);
- style = cssText ? this._applyCustomCss(cssText) : {};
- cacheStyle(this.is, style, this._xScopeSelector,
- this._styleProperties, s$);
- } else if (nativeShadow) {
- this._applyCustomCss(style.textContent);
- }
- if (style.textContent || old /*&& !nativeShadow*/) {
- this._applyXScopeSelector(this._xScopeSelector, old);
- }
- }
- },
-
- _applyXScopeSelector: function(selector, old) {
- var c = this._scopeCssViaAttr ? this.getAttribute(SCOPE_NAME) :
- this.className;
- v = old ? c.replace(old, selector) :
- (c ? c + ' ' : '') + XSCOPE_NAME + ' ' + selector;
- if (c !== v) {
- if (this._scopeCssViaAttr) {
- this.setAttribute(SCOPE_NAME, v);
- } else {
- this.className = v;
- }
- }
- },
-
- _generateCustomStyleCss: function(properties, styles) {
- var b = this._applyPropertiesToRule.bind(this, properties);
- var cssText = '';
- // TODO(sorvell): don't redo parsing work each time as below;
- // instead create a sheet with just custom properties
- for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
- cssText += this._transformCss(s.textContent, b) + '\n\n';
- }
- return cssText.trim();
- },
-
- _transformCss: function(cssText, callback) {
- return nativeShadow ?
- Polymer.StyleUtil.toCssText(cssText, callback) :
- Polymer.StyleTransformer.css(cssText, this.is, this.extends, callback,
- this._scopeCssViaAttr);
- },
-
- _xScopeCount: 0,
-
- _ensureScopeSelector: function(selector) {
- selector = selector || (this.is + '-' +
- (Object.getPrototypeOf(this)._xScopeCount++));
- this._xScopeSelector = selector;
- },
-
- _applyCustomCss: function(cssText) {
- if (this._customStyle) {
- this._customStyle.textContent = cssText;
- } else if (cssText) {
- this._customStyle = Polymer.StyleUtil.applyCss(cssText,
- this._xScopeSelector,
- nativeShadow ? this.root : null);
- }
- return this._customStyle;
- },
-
- _applyPropertiesToRule: function(properties, rule) {
- if (!nativeShadow) {
- this._scopifyRule(rule);
- }
- if (rule.cssText.match(CUSTOM_RULE_RX)) {
- rule.cssText = this._applyPropertiesToText(rule.cssText, properties);
- } else {
- rule.cssText = '';
- }
- //console.log(rule.cssText);
- },
-
- _applyPropertiesToText: function(cssText, props) {
- var output = '';
- var m, v;
- // e.g. color: var(--color);
- while (m = CUSTOM_VAR_USE.exec(cssText)) {
- v = props[m[2]];
- if (v) {
- output += '\t' + m[1].trim() + ': ' + this._propertyToCss(v);
- }
- }
- // e.g. @mixin(--stuff);
- while (m = CUSTOM_MIXIN_USE.exec(cssText)) {
- v = m[1];
- if (v) {
- var parts = v.split(' ');
- for (var i=0, p; i < parts.length; i++) {
- p = props[parts[i].trim()];
- if (p) {
- output += '\t' + this._propertyToCss(p);
- }
- }
- }
- }
- return output;
- },
-
- _propertyToCss: function(property) {
- var p = property.trim();
- p = p[p.length-1] === ';' ? p : p + ';';
- return p + '\n';
- },
-
- // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
- // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
- // host selector: x-foo.wide -> x-foo.x-foo-42.wide
- _scopifyRule: function(rule) {
- var selector = rule.selector;
- var host = this.is;
- var rx = new RegExp(HOST_SELECTOR_PREFIX + host + HOST_SELECTOR_SUFFIX);
- var parts = selector.split(',');
- var scope = this._scopeCssViaAttr ?
- SCOPE_PREFIX + this._xScopeSelector + SCOPE_SUFFIX :
- '.' + this._xScopeSelector;
- for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
- parts[i] = p.match(rx) ?
- p.replace(host, host + scope) :
- scope + ' ' + p;
- }
- rule.selector = parts.join(',');
- },
-
- _scopeElementClass: function(element, selector) {
- if (!nativeShadow && !this._scopeCssViaAttr) {
- selector += (selector ? ' ' : '') + SCOPE_NAME + ' ' + this.is +
- (element._xScopeSelector ? ' ' + XSCOPE_NAME + ' ' +
- element._xScopeSelector : '');
- }
- return selector;
- },
-
- // override to ensure whenever classes are set, we need to shim them.
- serializeValueToAttribute: function(value, attribute, node) {
- if (attribute === 'class') {
- // host needed to scope styling.
- var host = node === this ?
- Polymer.dom(this).getOwnerRoot() || this.dataHost :
- this;
- if (host) {
- value = host._scopeElementClass(node, value);
- }
- }
- baseSerializeValueToAttribute.call(this, value, attribute, node);
- },
-
- updateStyles: function() {
- this._updateOwnStyles();
- this._updateRootStyles(this.root);
- },
-
- updateHostStyles: function() {
- var host = Polymer.dom(this).getOwnerRoot() || this.dataHost;
- if (host) {
- host.updateStyles();
- } else {
- this._updateRootStyles(document);
- }
- },
-
- _updateRootStyles: function(root) {
- // TODO(sorvell): temporary way to find local dom that needs
- // x-scope styling.
- var scopeSelector = this._scopeCssViaAttr ?
- '[' + SCOPE_NAME + '~=' + XSCOPE_NAME + ']' : '.' + XSCOPE_NAME;
- var c$ = Polymer.dom(root).querySelectorAll(scopeSelector);
- for (var i=0, l= c$.length, c; (i<l) && (c=c$[i]); i++) {
- if (c.updateStyles) {
- c.updateStyles();
- }
- }
- }
-
- });
-
- var styleCache = {};
- function cacheStyle(is, style, scope, bag, styles) {
- style._scope = scope;
- style._properties = bag;
- style._styles = styles;
- var s$ = styleCache[is] = styleCache[is] || [];
- s$.push(style);
- }
-
- function styleFromCache(is, bag, checkStyles) {
- var styles = styleCache[is];
- if (styles) {
- for (var i=0, s; i < styles.length; i++) {
- s = styles[i];
- if (objectsEqual(bag, s._properties) &&
- objectsEqual(checkStyles, s._styles)) {
- return s;
- }
- }
- }
- }
-
- function objectsEqual(a, b) {
- for (var i in a) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
- for (var i in b) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
- return true;
- }
-
- var SCOPE_NAME= Polymer.StyleTransformer.SCOPE_NAME;
- var XSCOPE_NAME = 'x-scope';
- var SCOPE_PREFIX = '[' + SCOPE_NAME + '~=';
- var SCOPE_SUFFIX = ']';
- var HOST_SELECTOR_PREFIX = '(?:^|[^.])';
- var HOST_SELECTOR_SUFFIX = '($|[.:[\\s>+~])';
- var CUSTOM_RULE_RX = /mixin|var/;
- var CUSTOM_VAR_ASSIGN = /(--[^\:;]*?):\s*?([^;{]*?);/g;
- var CUSTOM_MIXIN_ASSIGN = /(--[^\:;]*?):[^{;]*?{([^}]*?)}/g;
- var CUSTOM_VAR_VALUE = /^var\(([^)]*?)\)/;
- var CUSTOM_VAR_USE = /(?:^|[;}\s])([^;{}]*?):[\s]*?var\(([^)]*)?\)/gim;
- var CUSTOM_MIXIN_USE = /mixin\(([^)]*)\)/gim;
-
- })();
-
-;
-
- Polymer.Base._addFeature({
-
- _registerFeatures: function() {
- // identity
- this._prepIs();
- // inheritance
- this._prepExtends();
- // factory
- this._prepConstructor();
- // template
- this._prepTemplate();
- // template markup
- this._prepAnnotations();
- // accessors
- this._prepEffects();
- // shared behaviors
- this._prepBehaviors();
- // accessors part 2
- this._prepBindings();
- // dom encapsulation
- this._prepShady();
- },
-
- _prepBehavior: function(b) {
- this._addPropertyEffects(b.properties || b.accessors);
- this._addComplexObserverEffects(b.observers);
- },
-
- _initFeatures: function() {
- // manage local dom
- this._poolContent();
- // manage configuration
- this._setupConfigure();
- // host stack
- this._pushHost();
- // instantiate template
- this._stampTemplate();
- // host stack
- this._popHost();
- // concretize template references
- this._marshalAnnotationReferences();
- // setup debouncers
- this._setupDebouncers();
- // concretize effects on instance
- this._marshalInstanceEffects();
- // acquire instance behaviors
- this._marshalBehaviors();
- // acquire initial instance attribute values
- this._marshalAttributes();
- // top-down initial distribution, configuration, & ready callback
- this._tryReady();
- },
-
- _marshalBehavior: function(b) {
- // publish attributes to instance
- this._installHostAttributes(b.hostAttributes);
- // establish listeners on instance
- this._listenListeners(b.listeners);
- }
-
- });
-
-
-;
-(function() {
-
- Polymer({
-
- is: 'x-style',
- extends: 'style',
-
- created: function() {
- var rules = Polymer.StyleUtil.parser.parse(this.textContent);
- this.applyProperties(rules);
- // TODO(sorvell): since custom rules must match directly, they tend to be
- // made with selectors like `*`.
- // We *remove them here* so they don't apply too widely and nerf recalc.
- // This means that normal properties mixe in rules with custom
- // properties will *not* apply.
- var cssText = Polymer.StyleUtil.parser.stringify(rules);
- this.textContent = this.scopeCssText(cssText);
- },
-
- scopeCssText: function(cssText) {
- return Polymer.Settings.useNativeShadow ?
- cssText :
- Polymer.StyleUtil.toCssText(cssText, function(rule) {
- Polymer.StyleTransformer.rootRule(rule);
- });
- },
-
- applyProperties: function(rules) {
- var cssText = '';
- Polymer.StyleUtil.forEachStyleRule(rules, function(rule) {
- if (rule.cssText.match(CUSTOM_RULE)) {
- // TODO(sorvell): use parser.stringify, it needs an option not to
- // strip custom properties.
- cssText += rule.selector + ' {\n' + rule.cssText + '\n}\n';
- }
- });
- if (cssText) {
- Polymer.StyleDefaults.applyCss(cssText);
- }
- }
-
- });
-
- var CUSTOM_RULE = /--[^;{'"]*\:/;
-
-})();
-
-;
-
- Polymer({
-
- is: 'x-autobind',
-
- extends: 'template',
-
- _registerFeatures: function() {
- this._prepExtends();
- this._prepConstructor();
- },
-
- _finishDistribute: function() {
- var parentDom = Polymer.dom(Polymer.dom(this).parentNode);
- parentDom.insertBefore(this.root, this);
- },
-
- _initFeatures: function() {
- this._template = this;
- this._prepAnnotations();
- this._prepEffects();
- this._prepBehaviors();
- this._prepBindings();
- Polymer.Base._initFeatures.call(this);
- }
-
- });
-
-
-;
-
- Polymer.Templatizer = {
-
- templatize: function(template) {
- this._templatized = template;
- // TODO(sjmiles): supply _alternate_ content reference missing from root
- // templates (not nested). `_content` exists to provide content sharing
- // for nested templates.
- if (!template._content) {
- template._content = template.content;
- }
- // fast path if template's anonymous class has been memoized
- if (template._content._ctor) {
- this.ctor = template._content._ctor;
- //console.log('Templatizer.templatize: using memoized archetype');
- // forward parent properties to archetype
- this._prepParentProperties(this.ctor.prototype);
- return;
- }
- // `archetype` is the prototype of the anonymous
- // class created by the templatizer
- var archetype = Object.create(Polymer.Base);
- // normally Annotations.parseAnnotations(template) but
- // archetypes do special caching
- this.customPrepAnnotations(archetype, template);
-
- // setup accessors
- archetype._prepEffects();
- archetype._prepBehaviors();
- archetype._prepBindings();
-
- // forward parent properties to archetype
- this._prepParentProperties(archetype);
-
- // boilerplate code
- archetype._notifyPath = this._notifyPathImpl;
- archetype._scopeElementClass = this._scopeElementClassImpl;
- // boilerplate code
- var _constructor = this._constructorImpl;
- var ctor = function TemplateInstance(model, host) {
- _constructor.call(this, model, host);
- };
- // standard references
- ctor.prototype = archetype;
- archetype.constructor = ctor;
- // TODO(sjmiles): constructor cache?
- template._content._ctor = ctor;
- // TODO(sjmiles): choose less general name
- this.ctor = ctor;
- },
-
- _getRootDataHost: function() {
- return (this.dataHost && this.dataHost._rootDataHost) || this.dataHost;
- },
-
- _getAllStampedChildren: function(children) {
- children = children || [];
- if (this._getStampedChildren) {
- var c$ = this._getStampedChildren();
- for (var i=0, c; c = c$[i]; i++) {
- children.push(c);
- if (c._getAllStampedChildren) {
- c._getAllStampedChildren(children);
- }
- }
- }
- return children;
- },
-
- customPrepAnnotations: function(archetype, template) {
- if (template) {
- archetype._template = template;
- var c = template._content;
- if (c) {
- var rootDataHost = archetype._rootDataHost;
- if (rootDataHost) {
- Polymer.Annotations.prepElement =
- rootDataHost._prepElement.bind(rootDataHost);
- }
- archetype._notes = c._notes ||
- Polymer.Annotations.parseAnnotations(template);
- c._notes = archetype._notes;
- Polymer.Annotations.prepElement = null;
- archetype._parentProps = c._parentProps;
- }
- else {
- console.warn('no _content');
- }
- }
- else {
- console.warn('no _template');
- }
- },
-
- // Sets up accessors on the template to call abstract _forwardParentProp
- // API that should be implemented by Templatizer users to get parent
- // properties to their template instances. These accessors are memoized
- // on the archetype and copied to instances.
- _prepParentProperties: function(archetype) {
- var parentProps = this._parentProps = archetype._parentProps;
- if (this._forwardParentProp && parentProps) {
- // Prototype setup (memoized on archetype)
- var proto = archetype._parentPropProto;
- if (!proto) {
- proto = archetype._parentPropProto = Object.create(null);
- if (this._templatized != this) {
- // Assumption: if `this` isn't the template being templatized,
- // assume that the template is not a Poylmer.Base, so prep it
- // for binding
- Polymer.Bind.prepareModel(proto);
- }
- // Create accessors for each parent prop that forward the property
- // to template instances through abstract _forwardParentProp API
- // that should be implemented by Templatizer users
- for (var prop in parentProps) {
- var parentProp = '_parent_' + prop;
- var effects = [{
- kind: 'function',
- effect: { function: this._createForwardPropEffector(prop) }
- }];
- Polymer.Bind._createAccessors(proto, parentProp, effects);
- }
- }
- // Instance setup
- if (this._templatized != this) {
- Polymer.Bind.prepareInstance(this._templatized);
- this._templatized._forwardParentProp =
- this._forwardParentProp.bind(this);
- }
- this._extendTemplate(this._templatized, proto);
- }
- },
-
- _createForwardPropEffector: function(prop) {
- return function(source, value) {
- this._forwardParentProp(prop, value);
- };
- },
-
- // Similar to Polymer.Base.extend, but retains any previously set instance
- // values (_propertySet back on instance once accessor is installed)
- _extendTemplate: function(template, proto) {
- Object.getOwnPropertyNames(proto).forEach(function(n) {
- var val = template[n];
- var pd = Object.getOwnPropertyDescriptor(proto, n);
- Object.defineProperty(template, n, pd);
- if (val !== undefined) {
- template._propertySet(n, val);
- }
- });
- },
-
- _notifyPathImpl: function(path, value) {
- var p = path.match(/([^.]*)\.(([^.]*).*)/);
- // 'root.sub.path'
- var root = p[1]; // 'root'
- var sub = p[3]; // 'sub'
- var subPath = p[2]; // 'sub.path'
- // Notify host of parent.* path/property changes
- var dataHost = this.dataHost;
- if (root == 'parent') {
- if (sub == subPath) {
- dataHost.dataHost[sub] = value;
- } else {
- dataHost.notifyPath('_parent_' + subPath, value);
- }
- }
- // Extension point for Templatizer sub-classes
- if (dataHost._forwardInstancePath) {
- dataHost._forwardInstancePath.call(dataHost, this, root, subPath, value);
- }
- },
-
- // Overrides Base notify-path module
- _pathEffector: function(path, value, fromAbove) {
- if (this._forwardParentPath) {
- if (path.indexOf('_parent_') === 0) {
- this._forwardParentPath(path.substring(8), value);
- }
- }
- Polymer.Base._pathEffector.apply(this, arguments);
- },
-
- _constructorImpl: function(model, host) {
- var rootDataHost = host._getRootDataHost();
- if (rootDataHost) {
- this.listen = rootDataHost.listen.bind(rootDataHost);
- this._rootDataHost = rootDataHost;
- }
- this._setupConfigure(model);
- this._pushHost(host);
- this.root = this.instanceTemplate(this._template);
- this.root.__styleScoped = true;
- this._popHost();
- this._marshalAnnotatedNodes();
- this._marshalInstanceEffects();
- this._marshalAnnotatedListeners();
- this._tryReady();
- },
-
- _scopeElementClassImpl: function(node, value) {
- var host = this._rootDataHost;
- if (host) {
- return host._scopeElementClass(node, value);
- }
- },
-
- stamp: function(model) {
- model = model || {};
- if (this._parentProps) {
- // TODO(kschaaf): Maybe this is okay
- // model.parent = this.dataHost;
- model.parent = model.parent || {};
- for (var prop in this._parentProps) {
- model.parent[prop] = this['_parent_' + prop];
- }
- }
- return new this.ctor(model, this);
- }
-
- // TODO(sorvell): note, using the template as host is ~5-10% faster if
- // elements have no default values.
- // _constructorImpl: function(model, host) {
- // this._setupConfigure(model);
- // host._beginHost();
- // this.root = this.instanceTemplate(this._template);
- // host._popHost();
- // this._marshalTemplateContent();
- // this._marshalAnnotatedNodes();
- // this._marshalInstanceEffects();
- // this._marshalAnnotatedListeners();
- // this._ready();
- // },
-
- // stamp: function(model) {
- // return new this.ctor(model, this.dataHost);
- // }
-
-
- };
-
-
-;
-
- /**
- * Creates a pseudo-custom-element that maps property values to bindings
- * in DOM.
- *
- * `stamp` method creates an instance of the pseudo-element. The instance
- * references a document-fragment containing the stamped and bound dom
- * via it's `root` property.
- *
- */
- Polymer({
-
- is: 'x-template',
- extends: 'template',
-
- behaviors: [
- Polymer.Templatizer
- ],
-
- ready: function() {
- this.templatize(this);
- }
-
- });
-
-
-;
-
-(function() {
-
- var callbacks = new WeakMap();
-
- function observe(array, cb) {
- if (Array.observe) {
- var ncb = function(changes) {
- changes = changes.filter(function(o) { return o.type == 'splice'; });
- if (changes.length) {
- cb(changes);
- }
- };
- callbacks.set(cb, ncb);
- Array.observe(array, ncb);
- } else {
- if (!array.__polymerObservable) {
- makeObservable(array);
- }
- callbacks.get(array).push(cb);
- }
- }
-
- function unobserve(array, cb) {
- if (Array.observe) {
- var ncb = callbacks.get(cb);
- callbacks.delete(cb);
- Array.unobserve(array, ncb);
- } else {
- var cbs = callbacks.get(array);
- var idx = cbs.indexOf(cb);
- if (idx >= 0) {
- cbs.splice(idx, 1);
- }
- }
- }
-
- function makeObservable(array) {
- var splices = [];
- var debounce;
- var orig = {
- push: array.push,
- pop: array.pop,
- splice: array.splice,
- shift: array.shift,
- unshift: array.unshift,
- sort: array.sort
- };
- var addSplice = function(index, added, removed) {
- splices.push({
- index: index,
- addedCount: added,
- removed: removed,
- object: array,
- type: 'splice'
- });
- };
- callbacks.set(array, []);
- array.push = function() {
- debounce = Polymer.Debounce(debounce, fin);
- addSplice(array.length, 1, []);
- return orig.push.apply(this, arguments);
- };
- array.pop = function() {
- debounce = Polymer.Debounce(debounce, fin);
- addSplice(array.length - 1, 0, array.slice(-1));
- return orig.pop.apply(this, arguments);
- };
- array.splice = function(start, deleteCount) {
- debounce = Polymer.Debounce(debounce, fin);
- addSplice(start, arguments.length - 2, array.slice(start, start + deleteCount));
- return orig.splice.apply(this, arguments);
- };
- array.shift = function() {
- debounce = Polymer.Debounce(debounce, fin);
- addSplice(0, 0, [array[0]]);
- return orig.shift.apply(this, arguments);
- };
- array.unshift = function() {
- debounce = Polymer.Debounce(debounce, fin);
- addSplice(0, 1, []);
- return orig.unshift.apply(this, arguments);
- };
- array.sort = function() {
- debounce = Polymer.Debounce(debounce, fin);
- console.warn('[ArrayObserve]: sort not observable');
- return orig.sort.apply(this, arguments);
- };
- var fin = function() {
- var cbs = callbacks.get(array);
- for (var i=0; i<cbs.length; i++) {
- cbs[i](splices);
- }
- splices = [];
- };
- array.__polymerObservable = true;
- }
-
- Polymer.ArrayObserve = {
- observe: observe,
- unobserve: unobserve
- };
-
-})();
-
-
-;
-
- Polymer._collections = new WeakMap();
-
- Polymer.Collection = function(userArray, noObserve) {
- Polymer._collections.set(userArray, this);
- this.userArray = userArray;
- this.store = userArray.slice();
- this.callbacks = [];
- this.debounce = null;
- this.map = null;
- this.added = [];
- this.removed = [];
- if (!noObserve) {
- Polymer.ArrayObserve.observe(userArray, this.applySplices.bind(this));
- this.initMap();
- }
- };
-
- Polymer.Collection.prototype = {
- constructor: Polymer.Collection,
-
- initMap: function() {
- var map = this.map = new WeakMap();
- var s = this.store;
- var u = this.userArray;
- for (var i=0; i<s.length; i++) {
- var v = s[i];
- if (v) {
- switch (typeof v) {
- case 'string':
- v = s[i] = u[i]= new String(v);
- break;
- case 'number':
- v = s[i] = u[i]= new Number(v);
- break;
- case 'boolean':
- v = s[i] = u[i]= new Boolean(v);
- break;
- }
- map.set(v, i);
- }
- }
- },
-
- add: function(item, squelch) {
- var key = this.store.push(item) - 1;
- if (item != null && this.map) {
- this.map.set(item, key);
- }
- if (!squelch) {
- this.added.push(key);
- this.debounce = Polymer.Debounce(this.debounce, this.notify.bind(this));
- }
- return key;
- },
-
- removeKey: function(key) {
- if (this.map) {
- this.map.delete(this.store[key]);
- }
- delete this.store[key];
- this.removed.push(key);
- this.debounce = Polymer.Debounce(this.debounce, this.notify.bind(this));
- },
-
- remove: function(item, squelch) {
- var key = this.getKey(item);
- if (item != null && this.map) {
- this.map.delete(item);
- }
- delete this.store[key];
- if (!squelch) {
- this.removed.push(key);
- this.debounce = Polymer.Debounce(this.debounce, this.notify.bind(this));
- }
- return key;
- },
-
- notify: function(splices) {
- if (!splices) {
- splices = [{
- added: this.added,
- removed: this.removed
- }];
- this.added = [];
- this.removed = [];
- }
- this.callbacks.forEach(function(cb) {
- cb(splices);
- }, this);
- },
-
- observe: function(callback) {
- this.callbacks.push(callback);
- },
-
- unobserve: function(callback) {
- this.callbacks.splice(this.callbacks.indexOf(callback), 1);
- },
-
- getKey: function(item) {
- if (item != null && this.map) {
- return this.map.get(item);
- } else {
- return this.store.indexOf(item);
- }
- },
-
- getKeys: function() {
- return Object.keys(this.store);
- },
-
- setItem: function(key, value) {
- this.store[key] = value;
- },
-
- getItem: function(key) {
- return this.store[key];
- },
-
- getItems: function() {
- var items = [], store = this.store;
- for (var key in store) {
- items.push(store[key]);
- }
- return items;
- },
-
- applySplices: function(splices) {
- var map = this.map;
- var keySplices = [];
- for (var i=0; i<splices.length; i++) {
- var j, o, key, s = splices[i];
- // Removed keys
- var removed = [];
- for (j=0; j<s.removed.length; j++) {
- o = s.removed[j];
- key = this.remove(o, true);
- removed.push(key);
- }
- // Added keys
- var added = [];
- for (j=0; j<s.addedCount; j++) {
- o = this.userArray[s.index + j];
- key = this.add(o, true);
- added.push(key);
- }
- // Record splice
- keySplices.push({
- index: s.index,
- removed: removed,
- added: added
- });
- }
- this.notify(keySplices);
- }
-
- };
-
- Polymer.Collection.get = function(userArray, noObserve) {
- return Polymer._collections.get(userArray)
- || new Polymer.Collection(userArray, noObserve);
- };
-
-
-;
-
- Polymer({
-
- is: 'x-repeat',
- extends: 'template',
-
- properties: {
-
- /**
- * An array containing items determining how many instances of the template
- * to stamp and that that each template instance should bind to.
- */
- items: {
- type: Array
- },
-
- /**
- * A function that should determine the sort order of the items. This
- * property should either be provided as a string, indicating a method
- * name on the element's host, or else be an actual function. The
- * function should match the sort function passed to `Array.sort`.
- * Using a sort function has no effect on the underlying `items` array.
- */
- sort: {
- type: Function,
- observer: '_sortChanged'
- },
-
- /**
- * A function that can be used to filter items out of the view. This
- * property should either be provided as a string, indicating a method
- * name on the element's host, or else be an actual function. The
- * function should match the sort function passed to `Array.filter`.
- * Using a filter function has no effect on the underlying `items` array.
- */
- filter: {
- type: Function,
- observer: '_filterChanged'
- },
-
- /**
- * When using a `filter` or `sort` function, the `observe` property
- * should be set to a space-separated list of the names of item
- * sub-fields that should trigger a re-sort or re-filter when changed.
- * These should generally be fields of `item` that the sort or filter
- * function depends on.
- */
- observe: {
- type: String,
- observer: '_observeChanged'
- },
-
- /**
- * When using a `filter` or `sort` function, the `delay` property
- * determines a debounce time after a change to observed item
- * properties that must pass before the filter or sort is re-run.
- * This is useful in rate-limiting shuffing of the view when
- * item changes may be frequent.
- */
- delay: Number
- },
-
- behaviors: [
- Polymer.Templatizer
- ],
-
- observers: [
- '_itemsChanged(items.*)'
- ],
-
- created: function() {
- this.boundCollectionObserver = this.render.bind(this);
- },
-
- ready: function() {
- // Templatizing (generating the instance constructor) needs to wait
- // until attached, since it may not have its template content handed
- // back to it until then, following its host template stamping
- if (!this.ctor) {
- this.templatize(this);
- }
- },
-
- _sortChanged: function() {
- var dataHost = this._getRootDataHost();
- this._sortFn = this.sort && (typeof this.sort == 'function' ?
- this.sort : dataHost[this.sort].bind(this.host));
- if (this.items) {
- this.debounce('render', this.render);
- }
- },
-
- _filterChanged: function() {
- var dataHost = this._getRootDataHost();
- this._filterFn = this.filter && (typeof this.filter == 'function' ?
- this.filter : dataHost[this.filter].bind(this.host));
- if (this.items) {
- this.debounce('render', this.render);
- }
- },
-
- _observeChanged: function() {
- this._observePaths = this.observe &&
- this.observe.replace('.*', '.').split(' ');
- },
-
- _itemsChanged: function(change) {
- if (change.path == 'items') {
- this._unobserveCollection();
- if (change.value) {
- this._observeCollection(change.value);
- this.debounce('render', this.render);
- }
- } else {
- this._forwardItemPath(change.path, change.value);
- this._checkObservedPaths(change.path);
- }
- },
-
- _checkObservedPaths: function(path) {
- if (this._observePaths && path.indexOf('items.') === 0) {
- path = path.substring(path.indexOf('.', 6) + 1);
- var paths = this._observePaths;
- for (var i=0; i<paths.length; i++) {
- if (path.indexOf(paths[i]) === 0) {
- this.debounce('render', this.render, this.delay);
- return;
- }
- }
- }
- },
-
- _observeCollection: function(items) {
- this.collection = Array.isArray(items) ? Polymer.Collection.get(items) : items;
- this.collection.observe(this.boundCollectionObserver);
- },
-
- _unobserveCollection: function() {
- if (this.collection) {
- this.collection.unobserve(this.boundCollectionObserver);
- }
- },
-
- render: function(splices) {
- this.flushDebouncer('render');
- var c = this.collection;
- if (splices) {
- if (this._sortFn || splices[0].index == null) {
- this._applySplicesViewSort(splices);
- } else {
- this._applySplicesArraySort(splices);
- }
- } else {
- this._sortAndFilter();
- }
- var rowForKey = this._rowForKey = {};
- var keys = this._orderedKeys;
- // Assign items and keys
- this.rows = this.rows || [];
- for (var i=0; i<keys.length; i++) {
- var key = keys[i];
- var item = c.getItem(key);
- var row = this.rows[i];
- rowForKey[key] = i;
- if (!row) {
- this.rows.push(row = this._insertRow(i, null, item));
- }
- row.item = item;
- row.key = key;
- row.index = i;
- }
- // Remove extra
- for (; i<this.rows.length; i++) {
- this._detachRow(i);
- }
- this.rows.splice(keys.length, this.rows.length-keys.length);
- },
-
- _sortAndFilter: function() {
- var c = this.collection;
- this._orderedKeys = c.getKeys();
- // Filter
- if (this._filterFn) {
- this._orderedKeys = this._orderedKeys.filter(function(a) {
- return this._filterFn(c.getItem(a));
- }, this);
- }
- // Sort
- if (this._sortFn) {
- this._orderedKeys.sort(function(a, b) {
- return this._sortFn(c.getItem(a), c.getItem(b));
- }.bind(this));
- }
- },
-
- _keySort: function(a, b) {
- return this.collection.getKey(a) - this.collection.getKey(b);
- },
-
- _applySplicesViewSort: function(splices) {
- var c = this.collection;
- var keys = this._orderedKeys;
- var rows = this.rows;
- var removedRows = [];
- var addedKeys = [];
- var pool = [];
- var sortFn = this._sortFn || this._keySort.bind(this);
- splices.forEach(function(s) {
- // Collect all removed row idx's
- for (var i=0; i<s.removed.length; i++) {
- var idx = this._rowForKey[s.removed[i]];
- if (idx != null) {
- removedRows.push(idx);
- }
- }
- // Collect all added keys
- for (i=0; i<s.added.length; i++) {
- addedKeys.push(s.added[i]);
- }
- }, this);
- if (removedRows.length) {
- // Sort removed rows idx's
- removedRows.sort();
- // Remove keys and pool rows (backwards, so we don't invalidate rowForKey)
- for (i=removedRows.length-1; i>=0 ; i--) {
- var idx = removedRows[i];
- pool.push(this._detachRow(idx));
- rows.splice(idx, 1);
- keys.splice(idx, 1);
- }
- }
- if (addedKeys.length) {
- // Filter added keys
- if (this._filterFn) {
- addedKeys = addedKeys.filter(function(a) {
- return this._filterFn(c.getItem(a));
- }, this);
- }
- // Sort added keys
- addedKeys.sort(function(a, b) {
- return this.sortFn(c.getItem(a), c.getItem(b));
- }, this);
- // Insert new rows using sort (from pool or newly created)
- var start = 0;
- for (i=0; i<addedKeys.length; i++) {
- start = this._insertRowIntoViewSort(start, addedKeys[i], pool);
- }
- }
- },
-
- _insertRowIntoViewSort: function(start, key, pool) {
- var c = this.collection;
- var item = c.getItem(key);
- var end = this.rows.length - 1;
- var idx = -1;
- var sortFn = this._sortFn || this._keySort.bind(this);
- // Binary search for insertion point
- while (start <= end) {
- var mid = (start + end) >> 1;
- var midKey = this._orderedKeys[mid];
- var cmp = sortFn(c.getItem(midKey), item);
- if (cmp < 0) {
- start = mid + 1;
- } else if (cmp > 0) {
- end = mid - 1;
- } else {
- idx = mid;
- break;
- }
- }
- if (idx < 0) {
- idx = end + 1;
- }
- // Insert key & row at insertion point
- this._orderedKeys.splice(idx, 0, key);
- this.rows.splice(idx, 0, this._insertRow(idx, pool));
- return idx;
- },
-
- _applySplicesArraySort: function(splices) {
- var keys = this._orderedKeys;
- var pool = [];
- splices.forEach(function(s) {
- // Remove & pool rows first, to ensure we can fully reuse removed rows
- for (var i=0; i<s.removed.length; i++) {
- pool.push(this._detachRow(s.index + i));
- }
- this.rows.splice(s.index, s.removed.length);
- }, this);
- var c = this.collection;
- var filterDelta = 0;
- splices.forEach(function(s) {
- // Filter added keys
- var addedKeys = s.added;
- if (this._filterFn) {
- addedKeys = addedKeys.filter(function(a) {
- return this._filterFn(c.getItem(a));
- }, this);
- filterDelta += (s.added.length - addedKeys.length);
- }
- var idx = s.index - filterDelta;
- // Apply splices to keys
- var args = [idx, s.removed.length].concat(addedKeys);
- keys.splice.apply(keys, args);
- // Insert new rows (from pool or newly created)
- var addedRows = [];
- for (i=0; i<s.added.length; i++) {
- addedRows.push(this._insertRow(idx + i, pool));
- }
- args = [s.index, 0].concat(addedRows);
- this.rows.splice.apply(this.rows, args);
- }, this);
- },
-
- _detachRow: function(idx) {
- var row = this.rows[idx];
- var parentNode = Polymer.dom(this).parentNode;
- for (var i=0; i<row._children.length; i++) {
- var el = row._children[i];
- Polymer.dom(row.root).appendChild(el);
- }
- return row;
- },
-
- _insertRow: function(idx, pool, item) {
- var row = (pool && pool.pop()) || this._generateRow(idx, item);
- var beforeRow = this.rows[idx];
- var beforeNode = beforeRow ? beforeRow._children[0] : this;
- var parentNode = Polymer.dom(this).parentNode;
- Polymer.dom(parentNode).insertBefore(row.root, beforeNode);
- return row;
- },
-
- _generateRow: function(idx, item) {
- var row = this.stamp({
- index: idx,
- key: this.collection.getKey(item),
- item: item
- });
- // each row is a document fragment which is lost when we appendChild,
- // so we have to track each child individually
- var children = [];
- for (var n = row.root.firstChild; n; n=n.nextSibling) {
- children.push(n);
- n._templateInstance = row;
- }
- // Since archetype overrides Base/HTMLElement, Safari complains
- // when accessing `children`
- row._children = children;
- return row;
- },
-
- // Implements extension point from Templatizer mixin
- _getStampedChildren: function() {
- var children = [];
- if (this.rows) {
- for (var i=0; i<this.rows.length; i++) {
- var c = this.rows[i]._children;
- for (var j=0; j<c.length; j++)
- children.push(c[j]);
- }
- }
- return children;
- },
-
- // Implements extension point from Templatizer
- // Called as a side effect of a template instance path change, responsible
- // for notifying items.<key-for-row>.<path> change up to host
- _forwardInstancePath: function(row, root, subPath, value) {
- if (root == 'item') {
- this.notifyPath('items.' + row.key + '.' + subPath, value);
- }
- },
-
- // Implements extension point from Templatizer mixin
- // Called as side-effect of a host property change, responsible for
- // notifying parent.<prop> path change on each row
- _forwardParentProp: function(prop, value) {
- if (this.rows) {
- this.rows.forEach(function(row) {
- row.parent[prop] = value;
- row.notifyPath('parent.' + prop, value, true);
- }, this);
- }
- },
-
- // Implements extension point from Templatizer
- // Called as side-effect of a host path change, responsible for
- // notifying parent.<path> path change on each row
- _forwardParentPath: function(path, value) {
- if (this.rows) {
- this.rows.forEach(function(row) {
- row.notifyPath('parent.' + path, value, true);
- }, this);
- }
- },
-
- // Called as a side effect of a host items.<key>.<path> path change,
- // responsible for notifying item.<path> changes to row for key
- _forwardItemPath: function(path, value) {
- if (this._rowForKey) {
- // 'items.'.length == 6
- var dot = path.indexOf('.', 6);
- var key = path.substring(6, dot < 0 ? path.length : dot);
- var idx = this._rowForKey[key];
- var row = this.rows[idx];
- if (row) {
- if (dot >= 0) {
- path = 'item.' + path.substring(dot+1);
- row.notifyPath(path, value, true);
- } else {
- row.item = value;
- }
- }
- }
- },
-
- _instanceForElement: function(el) {
- while (el && !el._templateInstance) {
- el = el.parentNode;
- }
- return el && el._templateInstance;
- },
-
- /**
- * Returns the item associated with a given element stamped by
- * this `x-repeat`.
- */
- itemForElement: function(el) {
- var instance = this._instanceForElement(el);
- return instance && instance.item;
- },
-
- /**
- * Returns the `Polymer.Collection` key associated with a given
- * element stamped by this `x-repeat`.
- */
- keyForElement: function(el) {
- var instance = this._instanceForElement(el);
- return instance && instance.key;
- },
-
- /**
- * Returns the index in `items` associated with a given element
- * stamped by this `x-repeat`.
- */
- indexForElement: function(el) {
- var instance = this._instanceForElement(el);
- return this.rows.indexOf(instance);
- }
-
- });
-
-
-
-;
-
- Polymer({
- is: 'x-array-selector',
-
- properties: {
-
- /**
- * An array containing items from which selection will be made.
- */
- items: {
- type: Array,
- observer: '_itemsChanged'
- },
-
- /**
- * When `multi` is true, this is an array that contains any selected.
- * When `multi` is false, this is the currently selected item, or `null`
- * if no item is selected.
- */
- selected: {
- type: Object,
- notify: true
- },
-
- /**
- * When `true`, calling `select` on an item that is already selected
- * will deselect the item.
- */
- toggle: Boolean,
-
- /**
- * When `true`, multiple items may be selected at once (in this case,
- * `selected` is an array of currently selected items). When `false`,
- * only one item may be selected at a time.
- */
- multi: Boolean
- },
-
- _itemsChanged: function() {
- // Unbind previous selection
- if (Array.isArray(this.selected)) {
- for (var i=0; i<this.selected.length; i++) {
- this.unbindPaths('selected.' + i);
- }
- } else {
- this.unbindPaths('selected');
- }
- // Initialize selection
- if (this.multi) {
- this.selected = [];
- } else {
- this.selected = null;
- }
- },
-
- /**
- * Deselects the given item if it is already selected.
- */
- deselect: function(item) {
- if (this.multi) {
- var scol = Polymer.Collection.get(this.selected);
- // var skey = scol.getKey(item);
- // if (skey >= 0) {
- var sidx = this.selected.indexOf(item);
- if (sidx >= 0) {
- var skey = scol.getKey(item);
- this.selected.splice(sidx, 1);
- // scol.remove(item);
- this.unbindPaths('selected.' + skey);
- return true;
- }
- } else {
- this.selected = null;
- this.unbindPaths('selected');
- }
- },
-
- /**
- * Selects the given item. When `toggle` is true, this will automatically
- * deselect the item if already selected.
- */
- select: function(item) {
- var icol = Polymer.Collection.get(this.items);
- var key = icol.getKey(item);
- if (this.multi) {
- // var sidx = this.selected.indexOf(item);
- // if (sidx < 0) {
- var scol = Polymer.Collection.get(this.selected);
- var skey = scol.getKey(item);
- if (skey >= 0) {
- this.deselect(item);
- } else if (this.toggle) {
- this.selected.push(item);
- // this.bindPaths('selected.' + sidx, 'items.' + skey);
- // skey = Polymer.Collection.get(this.selected).add(item);
- this.async(function() {
- skey = scol.getKey(item);
- this.bindPaths('selected.' + skey, 'items.' + key);
- });
- }
- } else {
- if (this.toggle && item == this.selected) {
- this.deselect();
- } else {
- this.bindPaths('selected', 'items.' + key);
- this.selected = item;
- }
- }
- }
-
- });
-
-
-;
-
- /**
- * Stamps the template iff the `if` property is truthy.
- *
- * When `if` becomes falsey, the stamped content is hidden but not
- * removed from dom. When `if` subsequently becomes truthy again, the content
- * is simply re-shown. This approach is used due to its favorable performance
- * characteristics: the expense of creating template content is paid only
- * once and lazily.
- *
- * Set the `restamp` property to true to force the stamped content to be
- * created / destroyed when the `if` condition changes.
- */
- Polymer({
-
- is: 'x-if',
- extends: 'template',
-
- properties: {
-
- 'if': {
- type: Boolean,
- value: false
- },
-
- restamp: {
- type: Boolean,
- value: false
- }
-
- },
-
- behaviors: [
- Polymer.Templatizer
- ],
-
- observers: [
- 'render(if, restamp)'
- ],
-
- render: function() {
- this.debounce('render', function() {
- if (this.if) {
- if (!this.ctor) {
- this._wrapTextNodes(this._content);
- this.templatize(this);
- }
- this._ensureInstance();
- } else if (this.restamp) {
- this._teardownInstance();
- }
- if (!this.restamp && this._instance) {
- this._showHideInstance(this.if);
- }
- });
- },
-
- _ensureInstance: function() {
- if (!this._instance) {
- // TODO(sorvell): pickup stamping logic from x-repeat
- this._instance = this.stamp();
- var root = this._instance.root;
- this._instance._children = Array.prototype.slice.call(root.childNodes);
- // TODO(sorvell): this incantation needs to be simpler.
- var parent = Polymer.dom(Polymer.dom(this).parentNode);
- parent.insertBefore(root, this);
- }
- },
-
- _teardownInstance: function() {
- if (this._instance) {
- var parent = Polymer.dom(Polymer.dom(this).parentNode);
- this._instance._children.forEach(function(n) {
- parent.removeChild(n);
- });
- this._instance = null;
- }
- },
-
- _wrapTextNodes: function(root) {
- // wrap text nodes in span so they can be hidden.
- for (var n = root.firstChild; n; n=n.nextSibling) {
- if (n.nodeType === Node.TEXT_NODE) {
- var s = document.createElement('span');
- root.insertBefore(s, n);
- s.appendChild(n);
- n = s;
- }
- }
- },
-
- // Implements extension point from Templatizer mixin
- _getStampedChildren: function() {
- return this._instance._children;
- },
-
- _showHideInstance: function(showing) {
- this._getAllStampedChildren().forEach(function(n) {
- if (n.setAttribute) {
- this.serializeValueToAttribute(!showing, 'hidden', n);
- }
- }, this);
- },
-
- // Implements extension point from Templatizer mixin
- // Called as side-effect of a host property change, responsible for
- // notifying parent.<prop> path change on instance
- _forwardParentProp: function(prop, value) {
- if (this._instance) {
- this._instance.parent[prop] = value;
- this._instance.notifyPath('parent.' + prop, value, true);
- }
- },
-
- // Implements extension point from Templatizer
- // Called as side-effect of a host path change, responsible for
- // notifying parent.<path> path change on each row
- _forwardParentPath: function(path, value) {
- if (this._instance) {
- this._instance.notifyPath('parent.' + path, value, true);
- }
- }
-
- });
-

Powered by Google App Engine
This is Rietveld 408576698