Index: chrome/browser/resources/md_downloads/crisper.js |
diff --git a/chrome/browser/resources/md_downloads/crisper.js b/chrome/browser/resources/md_downloads/crisper.js |
index 49ea6974f7a2836f59d20dad47cf5299136340f7..da4452387f9851bc529b874c8665cd0736bb3498 100644 |
--- a/chrome/browser/resources/md_downloads/crisper.js |
+++ b/chrome/browser/resources/md_downloads/crisper.js |
@@ -1465,12 +1465,15 @@ function elide(original, maxLength) { |
* @return {T} A non-null |condition|. |
*/ |
function assert(condition, opt_message) { |
- 'use strict'; |
if (!condition) { |
- var msg = 'Assertion failed'; |
+ var message = 'Assertion failed'; |
if (opt_message) |
- msg = msg + ': ' + opt_message; |
- throw new Error(msg); |
+ message = message + ': ' + opt_message; |
+ var error = new Error(message); |
+ var global = function() { return this; }(); |
+ if (global.traceAssertionsForTesting) |
+ console.warn(error.stack); |
+ throw error; |
} |
return condition; |
} |
@@ -1497,7 +1500,7 @@ function assert(condition, opt_message) { |
* @param {string=} opt_message A message to show when this is hit. |
*/ |
function assertNotReached(opt_message) { |
- throw new Error(opt_message || 'Unreachable code hit'); |
+ assert(false, opt_message || 'Unreachable code hit'); |
} |
/** |
@@ -1508,10 +1511,8 @@ function assertNotReached(opt_message) { |
* @template T |
*/ |
function assertInstanceof(value, type, opt_message) { |
- if (!(value instanceof type)) { |
- throw new Error(opt_message || |
- value + ' is not a[n] ' + (type.name || typeof type)); |
- } |
+ assert(value instanceof type, |
+ opt_message || value + ' is not a[n] ' + (type.name || typeof type)); |
return value; |
}; |
// Copyright 2015 The Chromium Authors. All rights reserved. |
@@ -1804,10 +1805,11 @@ addEventListener('DOMContentLoaded', resolve); |
window.Polymer = { |
Settings: function () { |
var user = window.Polymer || {}; |
-location.search.slice(1).split('&').forEach(function (o) { |
+var parts = location.search.slice(1).split('&'); |
+for (var i = 0, o; i < parts.length && (o = parts[i]); i++) { |
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; |
@@ -1894,15 +1896,53 @@ this._callbacks.push(cb); |
}, |
_makeReady: function () { |
this._ready = true; |
-this._callbacks.forEach(function (cb) { |
-cb(); |
-}); |
+for (var i = 0; i < this._callbacks.length; i++) { |
+this._callbacks[i](); |
+} |
this._callbacks = []; |
}, |
_catchFirstRender: function () { |
requestAnimationFrame(function () { |
Polymer.RenderStatus._makeReady(); |
}); |
+}, |
+_afterNextRenderQueue: [], |
+_waitingNextRender: false, |
+afterNextRender: function (element, fn, args) { |
+this._watchNextRender(); |
+this._afterNextRenderQueue.push([ |
+element, |
+fn, |
+args |
+]); |
+}, |
+_watchNextRender: function () { |
+if (!this._waitingNextRender) { |
+this._waitingNextRender = true; |
+var fn = function () { |
+Polymer.RenderStatus._flushNextRender(); |
+}; |
+if (!this._ready) { |
+this.whenReady(fn); |
+} else { |
+requestAnimationFrame(fn); |
+} |
+} |
+}, |
+_flushNextRender: function () { |
+var self = this; |
+setTimeout(function () { |
+self._flushRenderCallbacks(self._afterNextRenderQueue); |
+self._afterNextRenderQueue = []; |
+self._waitingNextRender = false; |
+}); |
+}, |
+_flushRenderCallbacks: function (callbacks) { |
+for (var i = 0, h; i < callbacks.length; i++) { |
+h = callbacks[i]; |
+h[1].apply(h[0], h[2] || Polymer.nar); |
+} |
+; |
} |
}; |
if (window.HTMLImports) { |
@@ -1932,27 +1972,33 @@ this._doBehavior('created'); |
this._initFeatures(); |
}, |
attachedCallback: function () { |
+var self = this; |
Polymer.RenderStatus.whenReady(function () { |
-this.isAttached = true; |
-this._doBehavior('attached'); |
-}.bind(this)); |
+self.isAttached = true; |
+self._doBehavior('attached'); |
+}); |
}, |
detachedCallback: function () { |
this.isAttached = false; |
this._doBehavior('detached'); |
}, |
-attributeChangedCallback: function (name) { |
+attributeChangedCallback: function (name, oldValue, newValue) { |
this._attributeChangedImpl(name); |
-this._doBehavior('attributeChanged', arguments); |
+this._doBehavior('attributeChanged', [ |
+name, |
+oldValue, |
+newValue |
+]); |
}, |
_attributeChangedImpl: function (name) { |
this._setAttributeToProperty(this, name); |
}, |
extend: function (prototype, api) { |
if (prototype && api) { |
-Object.getOwnPropertyNames(api).forEach(function (n) { |
+var n$ = Object.getOwnPropertyNames(api); |
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { |
this.copyOwnProperty(n, api, prototype); |
-}, this); |
+} |
} |
return prototype || api; |
}, |
@@ -2030,7 +2076,7 @@ import: function (id, selector) { |
if (id) { |
var m = findModule(id); |
if (!m) { |
-forceDocumentUpgrade(); |
+forceDomModulesUpgrade(); |
m = findModule(id); |
} |
if (m && selector) { |
@@ -2042,12 +2088,17 @@ return m; |
}); |
var cePolyfill = window.CustomElements && !CustomElements.useNative; |
document.registerElement('dom-module', DomModule); |
-function forceDocumentUpgrade() { |
+function forceDomModulesUpgrade() { |
if (cePolyfill) { |
var script = document._currentScript || document.currentScript; |
-var doc = script && script.ownerDocument; |
-if (doc) { |
-CustomElements.upgradeAll(doc); |
+var doc = script && script.ownerDocument || document; |
+var modules = doc.querySelectorAll('dom-module'); |
+for (var i = modules.length - 1, m; i >= 0 && (m = modules[i]); i--) { |
+if (m.__upgraded__) { |
+return; |
+} else { |
+CustomElements.upgrade(m); |
+} |
} |
} |
} |
@@ -2082,7 +2133,8 @@ return behaviors; |
}, |
_flattenBehaviorsList: function (behaviors) { |
var flat = []; |
-behaviors.forEach(function (b) { |
+for (var i = 0; i < behaviors.length; i++) { |
+var b = behaviors[i]; |
if (b instanceof Array) { |
flat = flat.concat(this._flattenBehaviorsList(b)); |
} else if (b) { |
@@ -2090,31 +2142,16 @@ flat.push(b); |
} else { |
this._warn(this._logf('_flattenBehaviorsList', 'behavior is null, check for missing or 404 import')); |
} |
-}, this); |
+} |
return flat; |
}, |
_mixinBehavior: function (b) { |
-Object.getOwnPropertyNames(b).forEach(function (n) { |
-switch (n) { |
-case 'hostAttributes': |
-case 'registered': |
-case 'properties': |
-case 'observers': |
-case 'listeners': |
-case 'created': |
-case 'attached': |
-case 'detached': |
-case 'attributeChanged': |
-case 'configure': |
-case 'ready': |
-break; |
-default: |
-if (!this.hasOwnProperty(n)) { |
+var n$ = Object.getOwnPropertyNames(b); |
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { |
+if (!Polymer.Base._behaviorProperties[n] && !this.hasOwnProperty(n)) { |
this.copyOwnProperty(n, b, this); |
} |
-break; |
} |
-}, this); |
}, |
_prepBehaviors: function () { |
this._prepFlattenedBehaviors(this.behaviors); |
@@ -2126,9 +2163,9 @@ this._prepBehavior(behaviors[i]); |
this._prepBehavior(this); |
}, |
_doBehavior: function (name, args) { |
-this.behaviors.forEach(function (b) { |
-this._invokeBehavior(b, name, args); |
-}, this); |
+for (var i = 0; i < this.behaviors.length; i++) { |
+this._invokeBehavior(this.behaviors[i], name, args); |
+} |
this._invokeBehavior(this, name, args); |
}, |
_invokeBehavior: function (b, name, args) { |
@@ -2138,12 +2175,24 @@ fn.apply(this, args || Polymer.nar); |
} |
}, |
_marshalBehaviors: function () { |
-this.behaviors.forEach(function (b) { |
-this._marshalBehavior(b); |
-}, this); |
+for (var i = 0; i < this.behaviors.length; i++) { |
+this._marshalBehavior(this.behaviors[i]); |
+} |
this._marshalBehavior(this); |
} |
}); |
+Polymer.Base._behaviorProperties = { |
+hostAttributes: true, |
+registered: true, |
+properties: true, |
+observers: true, |
+listeners: true, |
+created: true, |
+attached: true, |
+detached: true, |
+attributeChanged: true, |
+ready: true |
+}; |
Polymer.Base._addFeature({ |
_getExtendedPrototype: function (tag) { |
return this._getExtendedNativePrototype(tag); |
@@ -2195,9 +2244,13 @@ 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); |
+for (var i = 0; i < this.behaviors.length; i++) { |
+info = this._getPropertyInfo(property, this.behaviors[i].properties); |
+if (info) { |
+return info; |
+} |
+} |
+; |
} |
return info || Polymer.nob; |
}, |
@@ -2210,6 +2263,40 @@ if (p) { |
p.defined = true; |
} |
return p; |
+}, |
+_prepPropertyInfo: function () { |
+this._propertyInfo = {}; |
+for (var i = 0, p; i < this.behaviors.length; i++) { |
+this._addPropertyInfo(this._propertyInfo, this.behaviors[i].properties); |
+} |
+this._addPropertyInfo(this._propertyInfo, this.properties); |
+this._addPropertyInfo(this._propertyInfo, this._propertyEffects); |
+}, |
+_addPropertyInfo: function (target, source) { |
+if (source) { |
+var t, s; |
+for (var i in source) { |
+t = target[i]; |
+s = source[i]; |
+if (i[0] === '_' && !s.readOnly) { |
+continue; |
+} |
+if (!target[i]) { |
+target[i] = { |
+type: typeof s === 'function' ? s : s.type, |
+readOnly: s.readOnly, |
+attribute: Polymer.CaseMap.camelToDashCase(i) |
+}; |
+} else { |
+if (!t.type) { |
+t.type = s.type; |
+} |
+if (!t.readOnly) { |
+t.readOnly = s.readOnly; |
+} |
+} |
+} |
+} |
} |
}); |
Polymer.CaseMap = { |
@@ -2237,21 +2324,24 @@ return g[0] + '-' + g[1].toLowerCase(); |
} |
}; |
Polymer.Base._addFeature({ |
-_prepAttributes: function () { |
-this._aggregatedAttributes = {}; |
-}, |
_addHostAttributes: function (attributes) { |
+if (!this._aggregatedAttributes) { |
+this._aggregatedAttributes = {}; |
+} |
if (attributes) { |
this.mixin(this._aggregatedAttributes, attributes); |
} |
}, |
_marshalHostAttributes: function () { |
+if (this._aggregatedAttributes) { |
this._applyAttributes(this, this._aggregatedAttributes); |
+} |
}, |
_applyAttributes: function (node, attr$) { |
for (var n in attr$) { |
if (!this.hasAttribute(n) && n !== 'class') { |
-this.serializeValueToAttribute(attr$[n], n, this); |
+var v = attr$[n]; |
+this.serializeValueToAttribute(v, n, this); |
} |
} |
}, |
@@ -2259,29 +2349,40 @@ _marshalAttributes: function () { |
this._takeAttributesToModel(this); |
}, |
_takeAttributesToModel: function (model) { |
-for (var i = 0, l = this.attributes.length; i < l; i++) { |
-this._setAttributeToProperty(model, this.attributes[i].name); |
+if (this.hasAttributes()) { |
+for (var i in this._propertyInfo) { |
+var info = this._propertyInfo[i]; |
+if (this.hasAttribute(info.attribute)) { |
+this._setAttributeToProperty(model, info.attribute, i, info); |
+} |
+} |
} |
}, |
-_setAttributeToProperty: function (model, attrName) { |
+_setAttributeToProperty: function (model, attribute, property, info) { |
if (!this._serializing) { |
-var propName = Polymer.CaseMap.dashToCamelCase(attrName); |
-var info = this.getPropertyInfo(propName); |
-if (info.defined || this._propertyEffects && this._propertyEffects[propName]) { |
-var val = this.getAttribute(attrName); |
-model[propName] = this.deserialize(val, info.type); |
+var property = property || Polymer.CaseMap.dashToCamelCase(attribute); |
+info = info || this._propertyInfo && this._propertyInfo[property]; |
+if (info && !info.readOnly) { |
+var v = this.getAttribute(attribute); |
+model[property] = this.deserialize(v, info.type); |
} |
} |
}, |
_serializing: false, |
-reflectPropertyToAttribute: function (name) { |
+reflectPropertyToAttribute: function (property, attribute, value) { |
this._serializing = true; |
-this.serializeValueToAttribute(this[name], Polymer.CaseMap.camelToDashCase(name)); |
+value = value === undefined ? this[property] : value; |
+this.serializeValueToAttribute(value, attribute || Polymer.CaseMap.camelToDashCase(property)); |
this._serializing = false; |
}, |
serializeValueToAttribute: function (value, attribute, node) { |
var str = this.serialize(value); |
-(node || this)[str === undefined ? 'removeAttribute' : 'setAttribute'](attribute, str); |
+node = node || this; |
+if (str === undefined) { |
+node.removeAttribute(attribute); |
+} else { |
+node.setAttribute(attribute, str); |
+} |
}, |
deserialize: function (value, type) { |
switch (type) { |
@@ -2357,13 +2458,13 @@ debouncer.stop(); |
} |
} |
}); |
-Polymer.version = '1.1.5'; |
+Polymer.version = '1.2.3'; |
Polymer.Base._addFeature({ |
_registerFeatures: function () { |
this._prepIs(); |
-this._prepAttributes(); |
this._prepBehaviors(); |
this._prepConstructor(); |
+this._prepPropertyInfo(); |
}, |
_prepBehavior: function (b) { |
this._addHostAttributes(b.hostAttributes); |
@@ -2378,13 +2479,14 @@ this._marshalBehaviors(); |
}); |
Polymer.Base._addFeature({ |
_prepTemplate: function () { |
-this._template = this._template || Polymer.DomModule.import(this.is, 'template'); |
+if (this._template === undefined) { |
+this._template = Polymer.DomModule.import(this.is, 'template'); |
+} |
if (this._template && this._template.hasAttribute('is')) { |
this._warn(this._logf('_prepTemplate', 'top-level Polymer template ' + 'must not be a type-extension, found', this._template, 'Move inside simple <template>.')); |
} |
-if (this._template && !this._template.content && HTMLTemplateElement.bootstrap) { |
+if (this._template && !this._template.content && window.HTMLTemplateElement && HTMLTemplateElement.decorate) { |
HTMLTemplateElement.decorate(this._template); |
-HTMLTemplateElement.bootstrap(this._template.content); |
} |
}, |
_stampTemplate: function () { |
@@ -2403,20 +2505,19 @@ Polymer.Base._addFeature({ |
_hostStack: [], |
ready: function () { |
}, |
-_pushHost: function (host) { |
+_registerHost: function (host) { |
this.dataHost = host = host || Polymer.Base._hostStack[Polymer.Base._hostStack.length - 1]; |
if (host && host._clients) { |
host._clients.push(this); |
} |
-this._beginHost(); |
}, |
-_beginHost: function () { |
+_beginHosting: function () { |
Polymer.Base._hostStack.push(this); |
if (!this._clients) { |
this._clients = []; |
} |
}, |
-_popHost: function () { |
+_endHosting: function () { |
Polymer.Base._hostStack.pop(); |
}, |
_tryReady: function () { |
@@ -2429,20 +2530,24 @@ return !this.dataHost || this.dataHost._clientsReadied; |
}, |
_ready: function () { |
this._beforeClientsReady(); |
+if (this._template) { |
this._setupRoot(); |
this._readyClients(); |
+} |
+this._clientsReadied = true; |
+this._clients = null; |
this._afterClientsReady(); |
this._readySelf(); |
}, |
_readyClients: function () { |
this._beginDistribute(); |
var c$ = this._clients; |
+if (c$) { |
for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { |
c._ready(); |
} |
+} |
this._finishDistribute(); |
-this._clientsReadied = true; |
-this._clients = null; |
}, |
_readySelf: function () { |
this._doBehavior('ready'); |
@@ -2638,61 +2743,6 @@ 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]; |
-} |
-} |
-}, |
-get path() { |
-if (!this.event._path) { |
-var path = []; |
-var o = this.rootTarget; |
-while (o) { |
-path.push(o); |
-o = Polymer.dom(o).parentNode || o.host; |
-} |
-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.domInnerHTML = function () { |
var escapeAttrRegExp = /[&\u00A0"]/g; |
var escapeDataRegExp = /[&\u00A0<>]/g; |
@@ -2800,24 +2850,31 @@ var nativeRemoveChild = Element.prototype.removeChild; |
var nativeAppendChild = Element.prototype.appendChild; |
var nativeCloneNode = Element.prototype.cloneNode; |
var nativeImportNode = Document.prototype.importNode; |
-var DomApi = function (node) { |
-this.node = node; |
-if (this.patch) { |
-this.patch(); |
-} |
+var needsToWrap = Settings.hasShadow && !Settings.nativeShadow; |
+var wrap = window.wrap ? window.wrap : function (node) { |
+return node; |
}; |
-if (window.wrap && Settings.useShadow && !Settings.useNativeShadow) { |
-DomApi = function (node) { |
-this.node = wrap(node); |
+var DomApi = function (node) { |
+this.node = needsToWrap ? wrap(node) : node; |
if (this.patch) { |
this.patch(); |
} |
}; |
-} |
DomApi.prototype = { |
flush: function () { |
Polymer.dom.flush(); |
}, |
+deepContains: function (node) { |
+if (this.node.contains(node)) { |
+return true; |
+} |
+var n = node; |
+var wrappedDocument = wrap(document); |
+while (n && n !== wrappedDocument && n !== this.node) { |
+n = Polymer.dom(n).parentNode || n.host; |
+} |
+return n === this.node; |
+}, |
_lazyDistribute: function (host) { |
if (host.shadyRoot && host.shadyRoot._distributionClean) { |
host.shadyRoot._distributionClean = false; |
@@ -2831,7 +2888,7 @@ insertBefore: function (node, ref_node) { |
return this._addNode(node, ref_node); |
}, |
_addNode: function (node, ref_node) { |
-this._removeNodeFromHost(node, true); |
+this._removeNodeFromParent(node); |
var addedInsertionPoint; |
var root = this.getOwnerRoot(); |
if (root) { |
@@ -2863,6 +2920,7 @@ nativeAppendChild.call(container, node); |
if (addedInsertionPoint) { |
this._updateInsertionPoints(root.host); |
} |
+this.notifyObserver(); |
return node; |
}, |
removeChild: function (node) { |
@@ -2877,6 +2935,7 @@ removeFromComposedParent(container, node); |
nativeRemoveChild.call(container, node); |
} |
} |
+this.notifyObserver(); |
return node; |
}, |
replaceChild: function (node, ref_node) { |
@@ -2969,6 +3028,13 @@ return Boolean(node._lightChildren !== undefined); |
_parentNeedsDistribution: function (parent) { |
return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot); |
}, |
+_removeNodeFromParent: function (node) { |
+var parent = node._lightParent || node.parentNode; |
+if (parent && hasDomApi(parent)) { |
+factory(parent).notifyObserver(); |
+} |
+this._removeNodeFromHost(node, true); |
+}, |
_removeNodeFromHost: function (node, ensureComposedRemoval) { |
var hostNeedsDist; |
var root; |
@@ -2980,7 +3046,7 @@ if (root) { |
root.host._elementRemove(node); |
hostNeedsDist = this._removeDistributedChildren(root, node); |
} |
-this._removeLogicalInfo(node, node._lightParent); |
+this._removeLogicalInfo(node, parent); |
} |
this._removeOwnerShadyRoot(node); |
if (root && hostNeedsDist) { |
@@ -3028,7 +3094,7 @@ _addLogicalInfo: function (node, container, index) { |
var children = factory(container).childNodes; |
index = index === undefined ? children.length : index; |
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { |
-var c$ = Array.prototype.slice.call(node.childNodes); |
+var c$ = arrayCopyChildNodes(node); |
for (var i = 0, n; i < c$.length && (n = c$[i]); i++) { |
children.splice(index++, 0, n); |
n._lightParent = container; |
@@ -3099,24 +3165,29 @@ getDistributedNodes: function () { |
return this.node._distributedNodes || []; |
}, |
queryDistributedElements: function (selector) { |
-var c$ = this.childNodes; |
+var c$ = this.getEffectiveChildNodes(); |
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); |
+if (c.nodeType === Node.ELEMENT_NODE && matchesSelector.call(c, selector)) { |
+list.push(c); |
} |
} |
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); |
+getEffectiveChildNodes: function () { |
+var list = []; |
+var c$ = this.childNodes; |
+for (var i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) { |
+if (c.localName === CONTENT) { |
+var d$ = factory(c).getDistributedNodes(); |
+for (var j = 0; j < d$.length; j++) { |
+list.push(d$[j]); |
+} |
+} else { |
+list.push(c); |
} |
} |
-return results; |
+return list; |
}, |
_clear: function () { |
while (this.childNodes.length) { |
@@ -3160,36 +3231,24 @@ d.appendChild(nc); |
} |
} |
return n; |
+}, |
+observeNodes: function (callback) { |
+if (callback) { |
+if (!this.observer) { |
+this.observer = this.node.localName === CONTENT ? new DomApi.DistributedNodesObserver(this) : new DomApi.EffectiveNodesObserver(this); |
} |
-}; |
-Object.defineProperty(DomApi.prototype, 'classList', { |
-get: function () { |
-if (!this._classList) { |
-this._classList = new DomApi.ClassList(this); |
+return this.observer.addListener(callback); |
} |
-return this._classList; |
}, |
-configurable: true |
-}); |
-DomApi.ClassList = function (host) { |
-this.domApi = host; |
-this.node = host.node; |
-}; |
-DomApi.ClassList.prototype = { |
-add: function () { |
-this.node.classList.add.apply(this.node.classList, arguments); |
-this.domApi._distributeParent(); |
-}, |
-remove: function () { |
-this.node.classList.remove.apply(this.node.classList, arguments); |
-this.domApi._distributeParent(); |
-}, |
-toggle: function () { |
-this.node.classList.toggle.apply(this.node.classList, arguments); |
-this.domApi._distributeParent(); |
+unobserveNodes: function (handle) { |
+if (this.observer) { |
+this.observer.removeListener(handle); |
+} |
}, |
-contains: function () { |
-return this.node.classList.contains.apply(this.node.classList, arguments); |
+notifyObserver: function () { |
+if (this.observer) { |
+this.observer.notify(); |
+} |
} |
}; |
if (!Settings.useShadow) { |
@@ -3197,7 +3256,7 @@ Object.defineProperties(DomApi.prototype, { |
childNodes: { |
get: function () { |
var c$ = getLightChildren(this.node); |
-return Array.isArray(c$) ? c$ : Array.prototype.slice.call(c$); |
+return Array.isArray(c$) ? c$ : arrayCopyChildNodes(this.node); |
}, |
configurable: true |
}, |
@@ -3320,7 +3379,7 @@ if (nt !== Node.TEXT_NODE || nt !== Node.COMMENT_NODE) { |
this._clear(); |
var d = document.createElement('div'); |
d.innerHTML = text; |
-var c$ = Array.prototype.slice.call(d.childNodes); |
+var c$ = arrayCopyChildNodes(d); |
for (var i = 0; i < c$.length; i++) { |
this.appendChild(c$[i]); |
} |
@@ -3333,20 +3392,25 @@ DomApi.prototype._getComposedInnerHTML = function () { |
return getInnerHTML(this.node, true); |
}; |
} else { |
-var forwardMethods = [ |
+var forwardMethods = function (m$) { |
+for (var i = 0; i < m$.length; i++) { |
+forwardMethod(m$[i]); |
+} |
+}; |
+var forwardMethod = function (method) { |
+DomApi.prototype[method] = function () { |
+return this.node[method].apply(this.node, arguments); |
+}; |
+}; |
+forwardMethods([ |
'cloneNode', |
'appendChild', |
'insertBefore', |
'removeChild', |
'replaceChild' |
-]; |
-forwardMethods.forEach(function (name) { |
-DomApi.prototype[name] = function () { |
-return this.node[name].apply(this.node, arguments); |
-}; |
-}); |
+]); |
DomApi.prototype.querySelectorAll = function (selector) { |
-return Array.prototype.slice.call(this.node.querySelectorAll(selector)); |
+return arrayCopy(this.node.querySelectorAll(selector)); |
}; |
DomApi.prototype.getOwnerRoot = function () { |
var n = this.node; |
@@ -3363,24 +3427,24 @@ return doc.importNode(externalNode, deep); |
}; |
DomApi.prototype.getDestinationInsertionPoints = function () { |
var n$ = this.node.getDestinationInsertionPoints && this.node.getDestinationInsertionPoints(); |
-return n$ ? Array.prototype.slice.call(n$) : []; |
+return n$ ? arrayCopy(n$) : []; |
}; |
DomApi.prototype.getDistributedNodes = function () { |
var n$ = this.node.getDistributedNodes && this.node.getDistributedNodes(); |
-return n$ ? Array.prototype.slice.call(n$) : []; |
+return n$ ? arrayCopy(n$) : []; |
}; |
DomApi.prototype._distributeParent = function () { |
}; |
Object.defineProperties(DomApi.prototype, { |
childNodes: { |
get: function () { |
-return Array.prototype.slice.call(this.node.childNodes); |
+return arrayCopyChildNodes(this.node); |
}, |
configurable: true |
}, |
children: { |
get: function () { |
-return Array.prototype.slice.call(this.node.children); |
+return arrayCopyChildren(this.node); |
}, |
configurable: true |
}, |
@@ -3403,7 +3467,20 @@ return this.node.innerHTML = value; |
configurable: true |
} |
}); |
-var forwardProperties = [ |
+var forwardProperties = function (f$) { |
+for (var i = 0; i < f$.length; i++) { |
+forwardProperty(f$[i]); |
+} |
+}; |
+var forwardProperty = function (name) { |
+Object.defineProperty(DomApi.prototype, name, { |
+get: function () { |
+return this.node[name]; |
+}, |
+configurable: true |
+}); |
+}; |
+forwardProperties([ |
'parentNode', |
'firstChild', |
'lastChild', |
@@ -3413,24 +3490,21 @@ var forwardProperties = [ |
'lastElementChild', |
'nextElementSibling', |
'previousElementSibling' |
-]; |
-forwardProperties.forEach(function (name) { |
-Object.defineProperty(DomApi.prototype, name, { |
-get: function () { |
-return this.node[name]; |
-}, |
-configurable: true |
-}); |
-}); |
+]); |
} |
var CONTENT = 'content'; |
-var factory = function (node, patch) { |
+function factory(node, patch) { |
node = node || document; |
if (!node.__domApi) { |
node.__domApi = new DomApi(node, patch); |
} |
return node.__domApi; |
-}; |
+} |
+; |
+function hasDomApi(node) { |
+return Boolean(node.__domApi); |
+} |
+; |
Polymer.dom = function (obj, patch) { |
if (obj instanceof Event) { |
return Polymer.EventApi.factory(obj); |
@@ -3438,50 +3512,13 @@ return Polymer.EventApi.factory(obj); |
return factory(obj, patch); |
} |
}; |
-Polymer.Base.extend(Polymer.dom, { |
-_flushGuard: 0, |
-_FLUSH_MAX: 100, |
-_needsTakeRecords: !Polymer.Settings.useNativeCustomElements, |
-_debouncers: [], |
-_finishDebouncer: null, |
-flush: function () { |
-for (var i = 0; i < this._debouncers.length; i++) { |
-this._debouncers[i].complete(); |
-} |
-if (this._finishDebouncer) { |
-this._finishDebouncer.complete(); |
-} |
-this._flushPolyfills(); |
-if (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) { |
-this._flushGuard++; |
-this.flush(); |
-} else { |
-if (this._flushGuard >= this._FLUSH_MAX) { |
-console.warn('Polymer.dom.flush aborted. Flush may not be complete.'); |
-} |
-this._flushGuard = 0; |
-} |
-}, |
-_flushPolyfills: function () { |
-if (this._needsTakeRecords) { |
-CustomElements.takeRecords(); |
-} |
-}, |
-addDebouncer: function (debouncer) { |
-this._debouncers.push(debouncer); |
-this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush); |
-}, |
-_finishFlush: function () { |
-Polymer.dom._debouncers = []; |
-} |
-}); |
function getLightChildren(node) { |
var children = node._lightChildren; |
return children ? children : node.childNodes; |
} |
function getComposedChildren(node) { |
if (!node._composedChildren) { |
-node._composedChildren = Array.prototype.slice.call(node.childNodes); |
+node._composedChildren = arrayCopyChildNodes(node); |
} |
return node._composedChildren; |
} |
@@ -3517,13 +3554,35 @@ children.splice(i, 1); |
} |
function saveLightChildrenIfNeeded(node) { |
if (!node._lightChildren) { |
-var c$ = Array.prototype.slice.call(node.childNodes); |
+var c$ = arrayCopyChildNodes(node); |
for (var i = 0, l = c$.length, child; i < l && (child = c$[i]); i++) { |
child._lightParent = child._lightParent || node; |
} |
node._lightChildren = c$; |
} |
} |
+function arrayCopyChildNodes(parent) { |
+var copy = [], i = 0; |
+for (var n = parent.firstChild; n; n = n.nextSibling) { |
+copy[i++] = n; |
+} |
+return copy; |
+} |
+function arrayCopyChildren(parent) { |
+var copy = [], i = 0; |
+for (var n = parent.firstElementChild; n; n = n.nextElementSibling) { |
+copy[i++] = n; |
+} |
+return copy; |
+} |
+function arrayCopy(a$) { |
+var l = a$.length; |
+var copy = new Array(l); |
+for (var i = 0; i < l; i++) { |
+copy[i] = a$[i]; |
+} |
+return copy; |
+} |
function hasInsertionPoint(root) { |
return Boolean(root && root._insertionPoints.length); |
} |
@@ -3538,15 +3597,416 @@ saveLightChildrenIfNeeded: saveLightChildrenIfNeeded, |
matchesSelector: matchesSelector, |
hasInsertionPoint: hasInsertionPoint, |
ctor: DomApi, |
-factory: factory |
+factory: factory, |
+hasDomApi: hasDomApi, |
+arrayCopy: arrayCopy, |
+arrayCopyChildNodes: arrayCopyChildNodes, |
+arrayCopyChildren: arrayCopyChildren, |
+wrap: wrap |
}; |
}(); |
-(function () { |
-Polymer.Base._addFeature({ |
-_prepShady: function () { |
-this._useContent = this._useContent || Boolean(this._template); |
-}, |
-_poolContent: function () { |
+Polymer.Base.extend(Polymer.dom, { |
+_flushGuard: 0, |
+_FLUSH_MAX: 100, |
+_needsTakeRecords: !Polymer.Settings.useNativeCustomElements, |
+_debouncers: [], |
+_staticFlushList: [], |
+_finishDebouncer: null, |
+flush: function () { |
+this._flushGuard = 0; |
+this._prepareFlush(); |
+while (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) { |
+for (var i = 0; i < this._debouncers.length; i++) { |
+this._debouncers[i].complete(); |
+} |
+if (this._finishDebouncer) { |
+this._finishDebouncer.complete(); |
+} |
+this._prepareFlush(); |
+this._flushGuard++; |
+} |
+if (this._flushGuard >= this._FLUSH_MAX) { |
+console.warn('Polymer.dom.flush aborted. Flush may not be complete.'); |
+} |
+}, |
+_prepareFlush: function () { |
+if (this._needsTakeRecords) { |
+CustomElements.takeRecords(); |
+} |
+for (var i = 0; i < this._staticFlushList.length; i++) { |
+this._staticFlushList[i](); |
+} |
+}, |
+addStaticFlush: function (fn) { |
+this._staticFlushList.push(fn); |
+}, |
+removeStaticFlush: function (fn) { |
+var i = this._staticFlushList.indexOf(fn); |
+if (i >= 0) { |
+this._staticFlushList.splice(i, 1); |
+} |
+}, |
+addDebouncer: function (debouncer) { |
+this._debouncers.push(debouncer); |
+this._finishDebouncer = Polymer.Debounce(this._finishDebouncer, this._finishFlush); |
+}, |
+_finishFlush: function () { |
+Polymer.dom._debouncers = []; |
+} |
+}); |
+Polymer.EventApi = function () { |
+'use strict'; |
+var DomApi = Polymer.DomApi.ctor; |
+var Settings = Polymer.Settings; |
+DomApi.Event = function (event) { |
+this.event = event; |
+}; |
+if (Settings.useShadow) { |
+DomApi.Event.prototype = { |
+get rootTarget() { |
+return this.event.path[0]; |
+}, |
+get localTarget() { |
+return this.event.target; |
+}, |
+get path() { |
+return this.event.path; |
+} |
+}; |
+} else { |
+DomApi.Event.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]; |
+} |
+} |
+}, |
+get path() { |
+if (!this.event._path) { |
+var path = []; |
+var o = this.rootTarget; |
+while (o) { |
+path.push(o); |
+o = Polymer.dom(o).parentNode || o.host; |
+} |
+path.push(window); |
+this.event._path = path; |
+} |
+return this.event._path; |
+} |
+}; |
+} |
+var factory = function (event) { |
+if (!event.__eventApi) { |
+event.__eventApi = new DomApi.Event(event); |
+} |
+return event.__eventApi; |
+}; |
+return { factory: factory }; |
+}(); |
+(function () { |
+'use strict'; |
+var DomApi = Polymer.DomApi.ctor; |
+Object.defineProperty(DomApi.prototype, 'classList', { |
+get: function () { |
+if (!this._classList) { |
+this._classList = new DomApi.ClassList(this); |
+} |
+return this._classList; |
+}, |
+configurable: true |
+}); |
+DomApi.ClassList = function (host) { |
+this.domApi = host; |
+this.node = host.node; |
+}; |
+DomApi.ClassList.prototype = { |
+add: function () { |
+this.node.classList.add.apply(this.node.classList, arguments); |
+this.domApi._distributeParent(); |
+}, |
+remove: function () { |
+this.node.classList.remove.apply(this.node.classList, arguments); |
+this.domApi._distributeParent(); |
+}, |
+toggle: function () { |
+this.node.classList.toggle.apply(this.node.classList, arguments); |
+this.domApi._distributeParent(); |
+}, |
+contains: function () { |
+return this.node.classList.contains.apply(this.node.classList, arguments); |
+} |
+}; |
+}()); |
+(function () { |
+'use strict'; |
+var DomApi = Polymer.DomApi.ctor; |
+var Settings = Polymer.Settings; |
+var hasDomApi = Polymer.DomApi.hasDomApi; |
+DomApi.EffectiveNodesObserver = function (domApi) { |
+this.domApi = domApi; |
+this.node = this.domApi.node; |
+this._listeners = []; |
+}; |
+DomApi.EffectiveNodesObserver.prototype = { |
+addListener: function (callback) { |
+if (!this._isSetup) { |
+this._setup(); |
+this._isSetup = true; |
+} |
+var listener = { |
+fn: callback, |
+_nodes: [] |
+}; |
+this._listeners.push(listener); |
+this._scheduleNotify(); |
+return listener; |
+}, |
+removeListener: function (handle) { |
+var i = this._listeners.indexOf(handle); |
+if (i >= 0) { |
+this._listeners.splice(i, 1); |
+handle._nodes = []; |
+} |
+if (!this._hasListeners()) { |
+this._cleanup(); |
+this._isSetup = false; |
+} |
+}, |
+_setup: function () { |
+this._observeContentElements(this.domApi.childNodes); |
+}, |
+_cleanup: function () { |
+this._unobserveContentElements(this.domApi.childNodes); |
+}, |
+_hasListeners: function () { |
+return Boolean(this._listeners.length); |
+}, |
+_scheduleNotify: function () { |
+if (this._debouncer) { |
+this._debouncer.stop(); |
+} |
+this._debouncer = Polymer.Debounce(this._debouncer, this._notify); |
+this._debouncer.context = this; |
+Polymer.dom.addDebouncer(this._debouncer); |
+}, |
+notify: function () { |
+if (this._hasListeners()) { |
+this._scheduleNotify(); |
+} |
+}, |
+_notify: function (mxns) { |
+this._beforeCallListeners(); |
+this._callListeners(); |
+}, |
+_beforeCallListeners: function () { |
+this._updateContentElements(); |
+}, |
+_updateContentElements: function () { |
+this._observeContentElements(this.domApi.childNodes); |
+}, |
+_observeContentElements: function (elements) { |
+for (var i = 0, n; i < elements.length && (n = elements[i]); i++) { |
+if (this._isContent(n)) { |
+n.__observeNodesMap = n.__observeNodesMap || new WeakMap(); |
+if (!n.__observeNodesMap.has(this)) { |
+n.__observeNodesMap.set(this, this._observeContent(n)); |
+} |
+} |
+} |
+}, |
+_observeContent: function (content) { |
+var self = this; |
+var h = Polymer.dom(content).observeNodes(function () { |
+self._scheduleNotify(); |
+}); |
+h._avoidChangeCalculation = true; |
+return h; |
+}, |
+_unobserveContentElements: function (elements) { |
+for (var i = 0, n, h; i < elements.length && (n = elements[i]); i++) { |
+if (this._isContent(n)) { |
+h = n.__observeNodesMap.get(this); |
+if (h) { |
+Polymer.dom(n).unobserveNodes(h); |
+n.__observeNodesMap.delete(this); |
+} |
+} |
+} |
+}, |
+_isContent: function (node) { |
+return node.localName === 'content'; |
+}, |
+_callListeners: function () { |
+var o$ = this._listeners; |
+var nodes = this._getEffectiveNodes(); |
+for (var i = 0, o; i < o$.length && (o = o$[i]); i++) { |
+var info = this._generateListenerInfo(o, nodes); |
+if (info || o._alwaysNotify) { |
+this._callListener(o, info); |
+} |
+} |
+}, |
+_getEffectiveNodes: function () { |
+return this.domApi.getEffectiveChildNodes(); |
+}, |
+_generateListenerInfo: function (listener, newNodes) { |
+if (listener._avoidChangeCalculation) { |
+return true; |
+} |
+var oldNodes = listener._nodes; |
+var info = { |
+target: this.node, |
+addedNodes: [], |
+removedNodes: [] |
+}; |
+var splices = Polymer.ArraySplice.calculateSplices(newNodes, oldNodes); |
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { |
+for (var j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) { |
+info.removedNodes.push(n); |
+} |
+} |
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { |
+for (var j = s.index; j < s.index + s.addedCount; j++) { |
+info.addedNodes.push(newNodes[j]); |
+} |
+} |
+listener._nodes = newNodes; |
+if (info.addedNodes.length || info.removedNodes.length) { |
+return info; |
+} |
+}, |
+_callListener: function (listener, info) { |
+return listener.fn.call(this.node, info); |
+}, |
+enableShadowAttributeTracking: function () { |
+} |
+}; |
+if (Settings.useShadow) { |
+var baseSetup = DomApi.EffectiveNodesObserver.prototype._setup; |
+var baseCleanup = DomApi.EffectiveNodesObserver.prototype._cleanup; |
+var beforeCallListeners = DomApi.EffectiveNodesObserver.prototype._beforeCallListeners; |
+Polymer.Base.extend(DomApi.EffectiveNodesObserver.prototype, { |
+_setup: function () { |
+if (!this._observer) { |
+var self = this; |
+this._mutationHandler = function (mxns) { |
+if (mxns && mxns.length) { |
+self._scheduleNotify(); |
+} |
+}; |
+this._observer = new MutationObserver(this._mutationHandler); |
+this._boundFlush = function () { |
+self._flush(); |
+}; |
+Polymer.dom.addStaticFlush(this._boundFlush); |
+this._observer.observe(this.node, { childList: true }); |
+} |
+baseSetup.call(this); |
+}, |
+_cleanup: function () { |
+this._observer.disconnect(); |
+this._observer = null; |
+this._mutationHandler = null; |
+Polymer.dom.removeStaticFlush(this._boundFlush); |
+baseCleanup.call(this); |
+}, |
+_flush: function () { |
+if (this._observer) { |
+this._mutationHandler(this._observer.takeRecords()); |
+} |
+}, |
+enableShadowAttributeTracking: function () { |
+if (this._observer) { |
+this._makeContentListenersAlwaysNotify(); |
+this._observer.disconnect(); |
+this._observer.observe(this.node, { |
+childList: true, |
+attributes: true, |
+subtree: true |
+}); |
+var root = this.domApi.getOwnerRoot(); |
+var host = root && root.host; |
+if (host && Polymer.dom(host).observer) { |
+Polymer.dom(host).observer.enableShadowAttributeTracking(); |
+} |
+} |
+}, |
+_makeContentListenersAlwaysNotify: function () { |
+for (var i = 0, h; i < this._listeners.length; i++) { |
+h = this._listeners[i]; |
+h._alwaysNotify = h._isContentListener; |
+} |
+} |
+}); |
+} |
+}()); |
+(function () { |
+'use strict'; |
+var DomApi = Polymer.DomApi.ctor; |
+var Settings = Polymer.Settings; |
+DomApi.DistributedNodesObserver = function (domApi) { |
+DomApi.EffectiveNodesObserver.call(this, domApi); |
+}; |
+DomApi.DistributedNodesObserver.prototype = Object.create(DomApi.EffectiveNodesObserver.prototype); |
+Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, { |
+_setup: function () { |
+}, |
+_cleanup: function () { |
+}, |
+_beforeCallListeners: function () { |
+}, |
+_getEffectiveNodes: function () { |
+return this.domApi.getDistributedNodes(); |
+} |
+}); |
+if (Settings.useShadow) { |
+Polymer.Base.extend(DomApi.DistributedNodesObserver.prototype, { |
+_setup: function () { |
+if (!this._observer) { |
+var root = this.domApi.getOwnerRoot(); |
+var host = root && root.host; |
+if (host) { |
+var self = this; |
+this._observer = Polymer.dom(host).observeNodes(function () { |
+self._scheduleNotify(); |
+}); |
+this._observer._isContentListener = true; |
+if (this._hasAttrSelect()) { |
+Polymer.dom(host).observer.enableShadowAttributeTracking(); |
+} |
+} |
+} |
+}, |
+_hasAttrSelect: function () { |
+var select = this.node.getAttribute('select'); |
+return select && select.match(/[[.]+/); |
+}, |
+_cleanup: function () { |
+var root = this.domApi.getOwnerRoot(); |
+var host = root && root.host; |
+if (host) { |
+Polymer.dom(host).unobserveNodes(this._observer); |
+} |
+this._observer = null; |
+} |
+}); |
+} |
+}()); |
+(function () { |
+var hasDomApi = Polymer.DomApi.hasDomApi; |
+Polymer.Base._addFeature({ |
+_prepShady: function () { |
+this._useContent = this._useContent || Boolean(this._template); |
+}, |
+_poolContent: function () { |
if (this._useContent) { |
saveLightChildrenIfNeeded(this); |
} |
@@ -3562,6 +4022,7 @@ upgradeLightChildren(this._lightChildren); |
_createLocalRoot: function () { |
this.shadyRoot = this.root; |
this.shadyRoot._distributionClean = false; |
+this.shadyRoot._hasDistributed = false; |
this.shadyRoot._isShadyRoot = true; |
this.shadyRoot._dirtyRoots = []; |
var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : []; |
@@ -3612,6 +4073,7 @@ if (this._useContent) { |
this.shadyRoot._distributionClean = true; |
if (hasInsertionPoint(this.shadyRoot)) { |
this._composeTree(); |
+notifyContentObservers(this.shadyRoot); |
} else { |
if (!this.shadyRoot._hasDistributed) { |
this.textContent = ''; |
@@ -3622,6 +4084,9 @@ var children = this._composeNode(this); |
this._updateChildNodes(this, children); |
} |
} |
+if (!this.shadyRoot._hasDistributed) { |
+notifyInitialDistribution(this); |
+} |
this.shadyRoot._hasDistributed = true; |
} |
}, |
@@ -3839,6 +4304,19 @@ return host.domHost; |
} |
} |
} |
+function notifyContentObservers(root) { |
+for (var i = 0, c; i < root._insertionPoints.length; i++) { |
+c = root._insertionPoints[i]; |
+if (hasDomApi(c)) { |
+Polymer.dom(c).notifyObserver(); |
+} |
+} |
+} |
+function notifyInitialDistribution(host) { |
+if (hasDomApi(host)) { |
+Polymer.dom(host).notifyObserver(); |
+} |
+} |
var needsUpgrade = window.CustomElements && !CustomElements.useNative; |
function upgradeLightChildren(children) { |
if (needsUpgrade && children) { |
@@ -3871,20 +4349,23 @@ Polymer.DomModule = document.createElement('dom-module'); |
Polymer.Base._addFeature({ |
_registerFeatures: function () { |
this._prepIs(); |
-this._prepAttributes(); |
this._prepBehaviors(); |
this._prepConstructor(); |
this._prepTemplate(); |
this._prepShady(); |
+this._prepPropertyInfo(); |
}, |
_prepBehavior: function (b) { |
this._addHostAttributes(b.hostAttributes); |
}, |
_initFeatures: function () { |
+this._registerHost(); |
+if (this._template) { |
this._poolContent(); |
-this._pushHost(); |
+this._beginHosting(); |
this._stampTemplate(); |
-this._popHost(); |
+this._endHosting(); |
+} |
this._marshalHostAttributes(); |
this._setupDebouncers(); |
this._marshalBehaviors(); |
@@ -3898,35 +4379,79 @@ Polymer.Annotations = { |
parseAnnotations: function (template) { |
var list = []; |
var content = template._content || template.content; |
-this._parseNodeAnnotations(content, list); |
+this._parseNodeAnnotations(content, list, template.hasAttribute('strip-whitespace')); |
return list; |
}, |
-_parseNodeAnnotations: function (node, list) { |
-return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list); |
+_parseNodeAnnotations: function (node, list, stripWhiteSpace) { |
+return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list, stripWhiteSpace); |
}, |
-_testEscape: function (value) { |
-var escape = value.slice(0, 2); |
-if (escape === '{{' || escape === '[[') { |
-return escape; |
+_bindingRegex: /([^{[]*)(\{\{|\[\[)(?!\}\}|\]\])(.+?)(?:\]\]|\}\})/g, |
+_parseBindings: function (text) { |
+var re = this._bindingRegex; |
+var parts = []; |
+var m, lastIndex; |
+while ((m = re.exec(text)) !== null) { |
+if (m[1]) { |
+parts.push({ literal: m[1] }); |
+} |
+var mode = m[2][0]; |
+var value = m[3].trim(); |
+var negate = false; |
+if (value[0] == '!') { |
+negate = true; |
+value = value.substring(1).trim(); |
+} |
+var customEvent, notifyEvent, colon; |
+if (mode == '{' && (colon = value.indexOf('::')) > 0) { |
+notifyEvent = value.substring(colon + 2); |
+value = value.substring(0, colon); |
+customEvent = true; |
+} |
+parts.push({ |
+compoundIndex: parts.length, |
+value: value, |
+mode: mode, |
+negate: negate, |
+event: notifyEvent, |
+customEvent: customEvent |
+}); |
+lastIndex = re.lastIndex; |
+} |
+if (lastIndex && lastIndex < text.length) { |
+var literal = text.substring(lastIndex); |
+if (literal) { |
+parts.push({ literal: literal }); |
+} |
+} |
+if (parts.length) { |
+return parts; |
} |
}, |
+_literalFromParts: function (parts) { |
+var s = ''; |
+for (var i = 0; i < parts.length; i++) { |
+var literal = parts[i].literal; |
+s += literal || ''; |
+} |
+return s; |
+}, |
_parseTextNodeAnnotation: function (node, list) { |
-var v = node.textContent; |
-var escape = this._testEscape(v); |
-if (escape) { |
-node.textContent = ' '; |
+var parts = this._parseBindings(node.textContent); |
+if (parts) { |
+node.textContent = this._literalFromParts(parts) || ' '; |
var annote = { |
bindings: [{ |
kind: 'text', |
-mode: escape[0], |
-value: v.slice(2, -2).trim() |
+name: 'textContent', |
+parts: parts, |
+isCompound: parts.length !== 1 |
}] |
}; |
list.push(annote); |
return annote; |
} |
}, |
-_parseElementAnnotations: function (element, list) { |
+_parseElementAnnotations: function (element, list, stripWhiteSpace) { |
var annote = { |
bindings: [], |
events: [] |
@@ -3934,7 +4459,7 @@ events: [] |
if (element.localName === 'content') { |
list._hasContent = true; |
} |
-this._parseChildNodesAnnotations(element, annote, list); |
+this._parseChildNodesAnnotations(element, annote, list, stripWhiteSpace); |
if (element.attributes) { |
this._parseNodeAttributeAnnotations(element, annote, list); |
if (this.prepElement) { |
@@ -3946,26 +4471,38 @@ list.push(annote); |
} |
return annote; |
}, |
-_parseChildNodesAnnotations: function (root, annote, list, callback) { |
+_parseChildNodesAnnotations: function (root, annote, list, stripWhiteSpace) { |
if (root.firstChild) { |
-for (var i = 0, node = root.firstChild; node; node = node.nextSibling, i++) { |
+var node = root.firstChild; |
+var i = 0; |
+while (node) { |
+var next = node.nextSibling; |
if (node.localName === 'template' && !node.hasAttribute('preserve-content')) { |
this._parseTemplate(node, i, list, annote); |
} |
if (node.nodeType === Node.TEXT_NODE) { |
-var n = node.nextSibling; |
+var n = next; |
while (n && n.nodeType === Node.TEXT_NODE) { |
node.textContent += n.textContent; |
+next = n.nextSibling; |
root.removeChild(n); |
-n = n.nextSibling; |
+n = next; |
+} |
+if (stripWhiteSpace && !node.textContent.trim()) { |
+root.removeChild(node); |
+i--; |
} |
} |
-var childAnnotation = this._parseNodeAnnotations(node, list, callback); |
+if (node.parentNode) { |
+var childAnnotation = this._parseNodeAnnotations(node, list, stripWhiteSpace); |
if (childAnnotation) { |
childAnnotation.parent = annote; |
childAnnotation.index = i; |
} |
} |
+node = next; |
+i++; |
+} |
} |
}, |
_parseTemplate: function (node, index, list, parent) { |
@@ -3981,62 +4518,50 @@ index: index |
}); |
}, |
_parseNodeAttributeAnnotations: function (node, annotation) { |
-for (var i = node.attributes.length - 1, a; a = node.attributes[i]; i--) { |
-var n = a.name, v = a.value; |
-if (n === 'id' && !this._testEscape(v)) { |
-annotation.id = v; |
-} else if (n.slice(0, 3) === 'on-') { |
+var attrs = Array.prototype.slice.call(node.attributes); |
+for (var i = attrs.length - 1, a; a = attrs[i]; i--) { |
+var n = a.name; |
+var v = a.value; |
+var b; |
+if (n.slice(0, 3) === 'on-') { |
node.removeAttribute(n); |
annotation.events.push({ |
name: n.slice(3), |
value: v |
}); |
-} else { |
-var b = this._parseNodeAttributeAnnotation(node, n, v); |
-if (b) { |
+} else if (b = this._parseNodeAttributeAnnotation(node, n, v)) { |
annotation.bindings.push(b); |
-} |
+} else if (n === 'id') { |
+annotation.id = v; |
} |
} |
}, |
-_parseNodeAttributeAnnotation: function (node, n, v) { |
-var escape = this._testEscape(v); |
-if (escape) { |
-var customEvent; |
-var name = n; |
-var mode = escape[0]; |
-v = v.slice(2, -2).trim(); |
-var not = false; |
-if (v[0] == '!') { |
-v = v.substring(1); |
-not = true; |
-} |
+_parseNodeAttributeAnnotation: function (node, name, value) { |
+var parts = this._parseBindings(value); |
+if (parts) { |
+var origName = name; |
var kind = 'property'; |
-if (n[n.length - 1] == '$') { |
-name = n.slice(0, -1); |
+if (name[name.length - 1] == '$') { |
+name = name.slice(0, -1); |
kind = 'attribute'; |
} |
-var notifyEvent, colon; |
-if (mode == '{' && (colon = v.indexOf('::')) > 0) { |
-notifyEvent = v.substring(colon + 2); |
-v = v.substring(0, colon); |
-customEvent = true; |
+var literal = this._literalFromParts(parts); |
+if (literal && kind == 'attribute') { |
+node.setAttribute(name, literal); |
} |
-if (node.localName == 'input' && n == 'value') { |
-node.setAttribute(n, ''); |
+if (node.localName == 'input' && name == 'value') { |
+node.setAttribute(origName, ''); |
} |
-node.removeAttribute(n); |
+node.removeAttribute(origName); |
if (kind === 'property') { |
name = Polymer.CaseMap.dashToCamelCase(name); |
} |
return { |
kind: kind, |
-mode: mode, |
name: name, |
-value: v, |
-negate: not, |
-event: notifyEvent, |
-customEvent: customEvent |
+parts: parts, |
+literal: literal, |
+isCompound: parts.length !== 1 |
}; |
} |
}, |
@@ -4112,7 +4637,10 @@ _prepAnnotations: function () { |
if (!this._template) { |
this._notes = []; |
} else { |
-Polymer.Annotations.prepElement = this._prepElement.bind(this); |
+var self = this; |
+Polymer.Annotations.prepElement = function (element) { |
+self._prepElement(element); |
+}; |
if (this._template._content && this._template._content._notes) { |
this._notes = this._template._content._notes; |
} else { |
@@ -4127,9 +4655,14 @@ for (var i = 0; i < notes.length; i++) { |
var note = notes[i]; |
for (var j = 0; j < note.bindings.length; j++) { |
var b = note.bindings[j]; |
-b.signature = this._parseMethod(b.value); |
-if (!b.signature) { |
-b.model = this._modelForPath(b.value); |
+for (var k = 0; k < b.parts.length; k++) { |
+var p = b.parts[k]; |
+if (!p.literal) { |
+p.signature = this._parseMethod(p.value); |
+if (!p.signature) { |
+p.model = this._modelForPath(p.value); |
+} |
+} |
} |
} |
if (note.templateContent) { |
@@ -4140,10 +4673,12 @@ for (var prop in pp) { |
bindings.push({ |
index: note.index, |
kind: 'property', |
-mode: '{', |
name: '_parent_' + prop, |
+parts: [{ |
+mode: '{', |
model: prop, |
value: prop |
+}] |
}); |
} |
note.bindings = note.bindings.concat(bindings); |
@@ -4152,22 +4687,24 @@ note.bindings = note.bindings.concat(bindings); |
}, |
_discoverTemplateParentProps: function (notes) { |
var pp = {}; |
-notes.forEach(function (n) { |
-n.bindings.forEach(function (b) { |
-if (b.signature) { |
-var args = b.signature.args; |
-for (var k = 0; k < args.length; k++) { |
-pp[args[k].model] = true; |
+for (var i = 0, n; i < notes.length && (n = notes[i]); i++) { |
+for (var j = 0, b$ = n.bindings, b; j < b$.length && (b = b$[j]); j++) { |
+for (var k = 0, p$ = b.parts, p; k < p$.length && (p = p$[k]); k++) { |
+if (p.signature) { |
+var args = p.signature.args; |
+for (var kk = 0; kk < args.length; kk++) { |
+pp[args[kk].model] = true; |
} |
} else { |
-pp[b.model] = true; |
+pp[p.model] = true; |
+} |
+} |
} |
-}); |
if (n.templateContent) { |
var tpp = n.templateContent._parentProps; |
Polymer.Base.mixin(pp, tpp); |
} |
-}); |
+} |
return pp; |
}, |
_prepElement: function (element) { |
@@ -4181,56 +4718,86 @@ this._marshalAnnotatedNodes(); |
this._marshalAnnotatedListeners(); |
} |
}, |
-_configureAnnotationReferences: function () { |
-this._configureTemplateContent(); |
+_configureAnnotationReferences: function (config) { |
+var notes = this._notes; |
+var nodes = this._nodes; |
+for (var i = 0; i < notes.length; i++) { |
+var note = notes[i]; |
+var node = nodes[i]; |
+this._configureTemplateContent(note, node); |
+this._configureCompoundBindings(note, node); |
+} |
}, |
-_configureTemplateContent: function () { |
-this._notes.forEach(function (note, i) { |
+_configureTemplateContent: function (note, node) { |
if (note.templateContent) { |
-this._nodes[i]._content = note.templateContent; |
+node._content = note.templateContent; |
+} |
+}, |
+_configureCompoundBindings: function (note, node) { |
+var bindings = note.bindings; |
+for (var i = 0; i < bindings.length; i++) { |
+var binding = bindings[i]; |
+if (binding.isCompound) { |
+var storage = node.__compoundStorage__ || (node.__compoundStorage__ = {}); |
+var parts = binding.parts; |
+var literals = new Array(parts.length); |
+for (var j = 0; j < parts.length; j++) { |
+literals[j] = parts[j].literal; |
+} |
+var name = binding.name; |
+storage[name] = literals; |
+if (binding.literal && binding.kind == 'property') { |
+if (node._configValue) { |
+node._configValue(name, binding.literal); |
+} else { |
+node[name] = binding.literal; |
+} |
+} |
+} |
} |
-}, this); |
}, |
_marshalIdNodes: function () { |
this.$ = {}; |
-this._notes.forEach(function (a) { |
+for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) { |
if (a.id) { |
this.$[a.id] = this._findAnnotatedNode(this.root, a); |
} |
-}, this); |
+} |
}, |
_marshalAnnotatedNodes: function () { |
-if (this._nodes) { |
-this._nodes = this._nodes.map(function (a) { |
-return this._findAnnotatedNode(this.root, a); |
-}, this); |
+if (this._notes && this._notes.length) { |
+var r = new Array(this._notes.length); |
+for (var i = 0; i < this._notes.length; i++) { |
+r[i] = this._findAnnotatedNode(this.root, this._notes[i]); |
+} |
+this._nodes = r; |
} |
}, |
_marshalAnnotatedListeners: function () { |
-this._notes.forEach(function (a) { |
+for (var i = 0, l = this._notes.length, a; i < l && (a = this._notes[i]); i++) { |
if (a.events && a.events.length) { |
var node = this._findAnnotatedNode(this.root, a); |
-a.events.forEach(function (e) { |
+for (var j = 0, e$ = a.events, e; j < e$.length && (e = e$[j]); j++) { |
this.listen(node, e.name, e.value); |
-}, this); |
} |
-}, this); |
+} |
+} |
} |
}); |
Polymer.Base._addFeature({ |
listeners: {}, |
_listenListeners: function (listeners) { |
-var node, name, key; |
-for (key in listeners) { |
-if (key.indexOf('.') < 0) { |
+var node, name, eventName; |
+for (eventName in listeners) { |
+if (eventName.indexOf('.') < 0) { |
node = this; |
-name = key; |
+name = eventName; |
} else { |
-name = key.split('.'); |
+name = eventName.split('.'); |
node = this.$[name[0]]; |
name = name[1]; |
} |
-this.listen(node, name, listeners[key]); |
+this.listen(node, name, listeners[eventName]); |
} |
}, |
listen: function (node, eventName, methodName) { |
@@ -4301,6 +4868,7 @@ node.removeEventListener(eventName, handler); |
}); |
(function () { |
'use strict'; |
+var wrap = Polymer.DomApi.wrap; |
var HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string'; |
var GESTURE_KEY = '__polymerGestures'; |
var HANDLED_OBJ = '__polymerGesturesHandled'; |
@@ -4451,8 +5019,11 @@ return ev.target; |
handleNative: function (ev) { |
var handled; |
var type = ev.type; |
-var node = ev.currentTarget; |
+var node = wrap(ev.currentTarget); |
var gobj = node[GESTURE_KEY]; |
+if (!gobj) { |
+return; |
+} |
var gs = gobj[type]; |
if (!gs) { |
return; |
@@ -4535,6 +5106,7 @@ Gestures.prevent('track'); |
} |
}, |
add: function (node, evType, handler) { |
+node = wrap(node); |
var recognizer = this.gestures[evType]; |
var deps = recognizer.deps; |
var name = recognizer.name; |
@@ -4563,6 +5135,7 @@ this.setTouchAction(node, recognizer.touchAction); |
} |
}, |
remove: function (node, evType, handler) { |
+node = wrap(node); |
var recognizer = this.gestures[evType]; |
var deps = recognizer.deps; |
var name = recognizer.name; |
@@ -4689,7 +5262,9 @@ Gestures.fire(target, type, { |
x: event.clientX, |
y: event.clientY, |
sourceEvent: event, |
-prevent: Gestures.prevent.bind(Gestures) |
+prevent: function (e) { |
+return Gestures.prevent(e); |
+} |
}); |
} |
}); |
@@ -4981,12 +5556,17 @@ this._callbacks.splice(0, len); |
this._lastVal += len; |
} |
}; |
-new (window.MutationObserver || JsMutationObserver)(Polymer.Async._atEndOfMicrotask.bind(Polymer.Async)).observe(Polymer.Async._twiddle, { characterData: true }); |
+new window.MutationObserver(function () { |
+Polymer.Async._atEndOfMicrotask(); |
+}).observe(Polymer.Async._twiddle, { characterData: true }); |
Polymer.Debounce = function () { |
var Async = Polymer.Async; |
var Debouncer = function (context) { |
this.context = context; |
-this.boundComplete = this.complete.bind(this); |
+var self = this; |
+this.boundComplete = function () { |
+self.complete(); |
+}; |
}; |
Debouncer.prototype = { |
go: function (callback, wait) { |
@@ -5063,6 +5643,32 @@ if (toElement) { |
Polymer.dom(toElement).setAttribute(name, ''); |
} |
}, |
+getEffectiveChildNodes: function () { |
+return Polymer.dom(this).getEffectiveChildNodes(); |
+}, |
+getEffectiveChildren: function () { |
+var list = Polymer.dom(this).getEffectiveChildNodes(); |
+return list.filter(function (n) { |
+return n.nodeType === Node.ELEMENT_NODE; |
+}); |
+}, |
+getEffectiveTextContent: function () { |
+var cn = this.getEffectiveChildNodes(); |
+var tc = []; |
+for (var i = 0, c; c = cn[i]; i++) { |
+if (c.nodeType !== Node.COMMENT_NODE) { |
+tc.push(Polymer.dom(c).textContent); |
+} |
+} |
+return tc.join(''); |
+}, |
+queryEffectiveChildren: function (slctr) { |
+var e$ = Polymer.dom(this).queryDistributedElements(slctr); |
+return e$ && e$[0]; |
+}, |
+queryAllEffectiveChildren: function (slctr) { |
+return Polymer.dom(this).queryDistributedElements(slctr); |
+}, |
getContentChildNodes: function (slctr) { |
var content = Polymer.dom(this.root).querySelector(slctr || 'content'); |
return content ? Polymer.dom(content).getDistributedNodes() : []; |
@@ -5075,19 +5681,37 @@ 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 detail = detail === null || detail === undefined ? {} : detail; |
var bubbles = options.bubbles === undefined ? true : options.bubbles; |
var cancelable = Boolean(options.cancelable); |
-var event = new CustomEvent(type, { |
+var useCache = options._useCache; |
+var event = this._getEvent(type, bubbles, cancelable, useCache); |
+event.detail = detail; |
+if (useCache) { |
+this.__eventCache[type] = null; |
+} |
+node.dispatchEvent(event); |
+if (useCache) { |
+this.__eventCache[type] = event; |
+} |
+return event; |
+}, |
+__eventCache: {}, |
+_getEvent: function (type, bubbles, cancelable, useCache) { |
+var event = useCache && this.__eventCache[type]; |
+if (!event || (event.bubbles != bubbles || event.cancelable != cancelable)) { |
+event = new Event(type, { |
bubbles: Boolean(bubbles), |
-cancelable: cancelable, |
-detail: detail |
+cancelable: cancelable |
}); |
-node.dispatchEvent(event); |
+} |
return event; |
}, |
async: function (callback, waitTime) { |
-return Polymer.Async.run(callback.bind(this), waitTime); |
+var self = this; |
+return Polymer.Async.run(function () { |
+callback.call(self); |
+}, waitTime); |
}, |
cancelAsync: function (handle) { |
Polymer.Async.cancel(handle); |
@@ -5100,7 +5724,7 @@ if (index >= 0) { |
return path.splice(index, 1); |
} |
} else { |
-var arr = this.get(path); |
+var arr = this._get(path); |
index = arr.indexOf(item); |
if (index >= 0) { |
return this.splice(path, index, 1); |
@@ -5120,11 +5744,16 @@ importHref: function (href, onload, onerror) { |
var l = document.createElement('link'); |
l.rel = 'import'; |
l.href = href; |
+var self = this; |
if (onload) { |
-l.onload = onload.bind(this); |
+l.onload = function (e) { |
+return onload.call(self, e); |
+}; |
} |
if (onerror) { |
-l.onerror = onerror.bind(this); |
+l.onerror = function (e) { |
+return onerror.call(self, e); |
+}; |
} |
document.head.appendChild(l); |
return l; |
@@ -5139,24 +5768,25 @@ elt[n] = props[n]; |
return elt; |
}, |
isLightDescendant: function (node) { |
-return this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot(); |
+return this !== node && this.contains(node) && Polymer.dom(this).getOwnerRoot() === Polymer.dom(node).getOwnerRoot(); |
}, |
isLocalDescendant: function (node) { |
return this.root === Polymer.dom(node).getOwnerRoot(); |
} |
}); |
Polymer.Bind = { |
+_dataEventCache: {}, |
prepareModel: function (model) { |
-model._propertyEffects = {}; |
-model._bindListeners = []; |
Polymer.Base.mixin(model, this._modelApi); |
}, |
_modelApi: { |
-_notifyChange: function (property) { |
-var eventName = Polymer.CaseMap.camelToDashCase(property) + '-changed'; |
-Polymer.Base.fire(eventName, { value: this[property] }, { |
+_notifyChange: function (source, event, value) { |
+value = value === undefined ? this[source] : value; |
+event = event || Polymer.CaseMap.camelToDashCase(source) + '-changed'; |
+this.fire(event, { value: value }, { |
bubbles: false, |
-node: this |
+cancelable: false, |
+_useCache: true |
}); |
}, |
_propertySetter: function (property, value, effects, fromAbove) { |
@@ -5185,12 +5815,9 @@ node[property] = value; |
} |
}, |
_effectEffects: function (property, value, effects, old, fromAbove) { |
-effects.forEach(function (fx) { |
-var fn = Polymer.Bind['_' + fx.kind + 'Effect']; |
-if (fn) { |
-fn.call(this, property, value, fx.effect, old, fromAbove); |
+for (var i = 0, l = effects.length, fx; i < l && (fx = effects[i]); i++) { |
+fx.fn.call(this, property, value, fx.effect, old, fromAbove); |
} |
-}, this); |
}, |
_clearPath: function (path) { |
for (var prop in this.__data__) { |
@@ -5201,6 +5828,9 @@ this.__data__[prop] = undefined; |
} |
}, |
ensurePropertyEffects: function (model, property) { |
+if (!model._propertyEffects) { |
+model._propertyEffects = {}; |
+} |
var fx = model._propertyEffects[property]; |
if (!fx) { |
fx = model._propertyEffects[property] = []; |
@@ -5209,10 +5839,13 @@ return fx; |
}, |
addPropertyEffect: function (model, property, kind, effect) { |
var fx = this.ensurePropertyEffects(model, property); |
-fx.push({ |
+var propEffect = { |
kind: kind, |
-effect: effect |
-}); |
+effect: effect, |
+fn: Polymer.Bind['_' + kind + 'Effect'] |
+}; |
+fx.push(propEffect); |
+return propEffect; |
}, |
createBindings: function (model) { |
var fx$ = model._propertyEffects; |
@@ -5262,7 +5895,10 @@ 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); |
+if (!model._bindListeners) { |
+model._bindListeners = []; |
+} |
+var fn = this._notedListenerFactory(property, path, this._isStructured(path)); |
var eventName = event || Polymer.CaseMap.camelToDashCase(property) + '-changed'; |
model._bindListeners.push({ |
index: index, |
@@ -5278,54 +5914,59 @@ 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); |
+_notedListenerFactory: function (property, path, isStructured) { |
+return function (target, value, targetPath) { |
+if (targetPath) { |
+this._notifyPath(this._fixPath(path, property, targetPath), value); |
} else { |
-var value = target[property]; |
+value = target[property]; |
if (!isStructured) { |
-this[path] = target[property]; |
+this[path] = value; |
} else { |
if (this.__data__[path] != value) { |
this.set(path, value); |
} |
} |
} |
-} |
}; |
}, |
prepareInstance: function (inst) { |
inst.__data__ = Object.create(null); |
}, |
setupBindListeners: function (inst) { |
-inst._bindListeners.forEach(function (info) { |
+var b$ = inst._bindListeners; |
+for (var i = 0, l = b$.length, info; i < l && (info = b$[i]); i++) { |
var node = inst._nodes[info.index]; |
-node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn)); |
+this._addNotifyListener(node, inst, info.event, info.changedFn); |
+} |
+; |
+}, |
+_addNotifyListener: function (element, context, event, changedFn) { |
+element.addEventListener(event, function (e) { |
+return context._notifyListener(changedFn, e); |
}); |
} |
}; |
Polymer.Base.extend(Polymer.Bind, { |
_shouldAddListener: function (effect) { |
-return effect.name && effect.mode === '{' && !effect.negate && effect.kind != 'attribute'; |
+return effect.name && effect.kind != 'attribute' && effect.kind != 'text' && !effect.isCompound && effect.parts[0].mode === '{' && !effect.parts[0].negate; |
}, |
_annotationEffect: function (source, value, effect) { |
if (source != effect.value) { |
-value = this.get(effect.value); |
+value = this._get(effect.value); |
this.__data__[effect.value] = value; |
} |
var calc = effect.negate ? !value : value; |
if (!effect.customEvent || this._nodes[effect.index][effect.name] !== calc) { |
-return this._applyEffectValue(calc, effect); |
+return this._applyEffectValue(effect, calc); |
} |
}, |
-_reflectEffect: function (source) { |
-this.reflectPropertyToAttribute(source); |
+_reflectEffect: function (source, value, effect) { |
+this.reflectPropertyToAttribute(source, effect.attribute, value); |
}, |
_notifyEffect: function (source, value, effect, old, fromAbove) { |
if (!fromAbove) { |
-this._notifyChange(source); |
+this._notifyChange(source, effect.event, value); |
} |
}, |
_functionEffect: function (source, value, fn, old, fromAbove) { |
@@ -5355,7 +5996,7 @@ var args = Polymer.Bind._marshalArgs(this.__data__, effect, source, value); |
if (args) { |
var fn = this[effect.method]; |
if (fn) { |
-this.__setProperty(effect.property, fn.apply(this, args)); |
+this.__setProperty(effect.name, fn.apply(this, args)); |
} else { |
this._warn(this._logf('_computeEffect', 'compute method `' + effect.method + '` not defined')); |
} |
@@ -5371,7 +6012,7 @@ var computedvalue = fn.apply(computedHost, args); |
if (effect.negate) { |
computedvalue = !computedvalue; |
} |
-this._applyEffectValue(computedvalue, effect); |
+this._applyEffectValue(effect, computedvalue); |
} |
} else { |
computedHost._warn(computedHost._logf('_annotatedComputationEffect', 'compute method `' + effect.method + '` not defined')); |
@@ -5387,7 +6028,7 @@ var v; |
if (arg.literal) { |
v = arg.value; |
} else if (arg.structured) { |
-v = Polymer.Base.get(name, model); |
+v = Polymer.Base._get(name, model); |
} else { |
v = model[name]; |
} |
@@ -5411,7 +6052,8 @@ return values; |
}); |
Polymer.Base._addFeature({ |
_addPropertyEffect: function (property, kind, effect) { |
-Polymer.Bind.addPropertyEffect(this, property, kind, effect); |
+var prop = Polymer.Bind.addPropertyEffect(this, property, kind, effect); |
+prop.pathFn = this['_' + prop.kind + 'PathEffect']; |
}, |
_prepEffects: function () { |
Polymer.Bind.prepareModel(this); |
@@ -5432,10 +6074,10 @@ prop.readOnly = true; |
this._addComputedEffect(p, prop.computed); |
} |
if (prop.notify) { |
-this._addPropertyEffect(p, 'notify'); |
+this._addPropertyEffect(p, 'notify', { event: Polymer.CaseMap.camelToDashCase(p) + '-changed' }); |
} |
if (prop.reflectToAttribute) { |
-this._addPropertyEffect(p, 'reflect'); |
+this._addPropertyEffect(p, 'reflect', { attribute: Polymer.CaseMap.camelToDashCase(p) }); |
} |
if (prop.readOnly) { |
Polymer.Bind.ensurePropertyEffects(this, p); |
@@ -5445,14 +6087,14 @@ Polymer.Bind.ensurePropertyEffects(this, p); |
}, |
_addComputedEffect: function (name, expression) { |
var sig = this._parseMethod(expression); |
-sig.args.forEach(function (arg) { |
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { |
this._addPropertyEffect(arg.model, 'compute', { |
method: sig.method, |
args: sig.args, |
trigger: arg, |
-property: name |
+name: name |
}); |
-}, this); |
+} |
}, |
_addObserverEffect: function (property, observer) { |
this._addPropertyEffect(property, 'observer', { |
@@ -5462,61 +6104,74 @@ property: property |
}, |
_addComplexObserverEffects: function (observers) { |
if (observers) { |
-observers.forEach(function (observer) { |
-this._addComplexObserverEffect(observer); |
-}, this); |
+for (var i = 0, o; i < observers.length && (o = observers[i]); i++) { |
+this._addComplexObserverEffect(o); |
+} |
} |
}, |
_addComplexObserverEffect: function (observer) { |
var sig = this._parseMethod(observer); |
-sig.args.forEach(function (arg) { |
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { |
this._addPropertyEffect(arg.model, 'complexObserver', { |
method: sig.method, |
args: sig.args, |
trigger: arg |
}); |
-}, this); |
+} |
}, |
_addAnnotationEffects: function (notes) { |
-this._nodes = []; |
-notes.forEach(function (note) { |
-var index = this._nodes.push(note) - 1; |
-note.bindings.forEach(function (binding) { |
-this._addAnnotationEffect(binding, index); |
-}, this); |
-}, this); |
+for (var i = 0, note; i < notes.length && (note = notes[i]); i++) { |
+var b$ = note.bindings; |
+for (var j = 0, binding; j < b$.length && (binding = b$[j]); j++) { |
+this._addAnnotationEffect(binding, i); |
+} |
+} |
}, |
_addAnnotationEffect: function (note, index) { |
if (Polymer.Bind._shouldAddListener(note)) { |
-Polymer.Bind._addAnnotatedListener(this, index, note.name, note.value, note.event); |
+Polymer.Bind._addAnnotatedListener(this, index, note.name, note.parts[0].value, note.parts[0].event); |
+} |
+for (var i = 0; i < note.parts.length; i++) { |
+var part = note.parts[i]; |
+if (part.signature) { |
+this._addAnnotatedComputationEffect(note, part, index); |
+} else if (!part.literal) { |
+this._addPropertyEffect(part.model, 'annotation', { |
+kind: note.kind, |
+index: index, |
+name: note.name, |
+value: part.value, |
+isCompound: note.isCompound, |
+compoundIndex: part.compoundIndex, |
+event: part.event, |
+customEvent: part.customEvent, |
+negate: part.negate |
+}); |
} |
-if (note.signature) { |
-this._addAnnotatedComputationEffect(note, index); |
-} else { |
-note.index = index; |
-this._addPropertyEffect(note.model, 'annotation', note); |
} |
}, |
-_addAnnotatedComputationEffect: function (note, index) { |
-var sig = note.signature; |
+_addAnnotatedComputationEffect: function (note, part, index) { |
+var sig = part.signature; |
if (sig.static) { |
-this.__addAnnotatedComputationEffect('__static__', index, note, sig, null); |
+this.__addAnnotatedComputationEffect('__static__', index, note, part, null); |
} else { |
-sig.args.forEach(function (arg) { |
+for (var i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) { |
if (!arg.literal) { |
-this.__addAnnotatedComputationEffect(arg.model, index, note, sig, arg); |
+this.__addAnnotatedComputationEffect(arg.model, index, note, part, arg); |
+} |
} |
-}, this); |
} |
}, |
-__addAnnotatedComputationEffect: function (property, index, note, sig, trigger) { |
+__addAnnotatedComputationEffect: function (property, index, note, part, trigger) { |
this._addPropertyEffect(property, 'annotatedComputation', { |
index: index, |
+isCompound: note.isCompound, |
+compoundIndex: part.compoundIndex, |
kind: note.kind, |
-property: note.name, |
-negate: note.negate, |
-method: sig.method, |
-args: sig.args, |
+name: note.name, |
+negate: part.negate, |
+method: part.signature.method, |
+args: part.signature.args, |
trigger: trigger |
}); |
}, |
@@ -5583,11 +6238,18 @@ return a; |
}, |
_marshalInstanceEffects: function () { |
Polymer.Bind.prepareInstance(this); |
+if (this._bindListeners) { |
Polymer.Bind.setupBindListeners(this); |
+} |
}, |
-_applyEffectValue: function (value, info) { |
+_applyEffectValue: function (info, value) { |
var node = this._nodes[info.index]; |
-var property = info.property || info.name || 'textContent'; |
+var property = info.name; |
+if (info.isCompound) { |
+var storage = node.__compoundStorage__[property]; |
+storage[info.compoundIndex] = value; |
+value = storage.join(''); |
+} |
if (info.kind == 'attribute') { |
this.serializeValueToAttribute(value, property, node); |
} else { |
@@ -5597,11 +6259,14 @@ value = this._scopeElementClass(node, value); |
if (property === 'textContent' || node.localName == 'input' && property == 'value') { |
value = value == undefined ? '' : value; |
} |
-return node[property] = value; |
+var pinfo; |
+if (!node._propertyInfo || !(pinfo = node._propertyInfo[property]) || !pinfo.readOnly) { |
+this.__setProperty(property, value, true, node); |
+} |
} |
}, |
_executeStaticEffects: function () { |
-if (this._propertyEffects.__static__) { |
+if (this._propertyEffects && this._propertyEffects.__static__) { |
this._effectEffects('__static__', null, this._propertyEffects.__static__); |
} |
} |
@@ -5609,12 +6274,14 @@ this._effectEffects('__static__', null, this._propertyEffects.__static__); |
Polymer.Base._addFeature({ |
_setupConfigure: function (initialConfig) { |
this._config = {}; |
+this._handlers = []; |
+if (initialConfig) { |
for (var i in initialConfig) { |
if (initialConfig[i] !== undefined) { |
this._config[i] = initialConfig[i]; |
} |
} |
-this._handlers = []; |
+} |
}, |
_marshalAttributes: function () { |
this._takeAttributesToModel(this._config); |
@@ -5624,7 +6291,10 @@ var model = this._clientsReadied ? this : this._config; |
this._setAttributeToProperty(model, name); |
}, |
_configValue: function (name, value) { |
+var info = this._propertyInfo[name]; |
+if (!info || !info.readOnly) { |
this._config[name] = value; |
+} |
}, |
_beforeClientsReady: function () { |
this._configure(); |
@@ -5633,13 +6303,15 @@ _configure: function () { |
this._configureAnnotationReferences(); |
this._aboveConfig = this.mixin({}, this._config); |
var config = {}; |
-this.behaviors.forEach(function (b) { |
-this._configureProperties(b.properties, config); |
-}, this); |
+for (var i = 0; i < this.behaviors.length; i++) { |
+this._configureProperties(this.behaviors[i].properties, config); |
+} |
this._configureProperties(this.properties, config); |
-this._mixinConfigure(config, this._aboveConfig); |
+this.mixin(config, this._aboveConfig); |
this._config = config; |
+if (this._clients && this._clients.length) { |
this._distributeConfig(this._config); |
+} |
}, |
_configureProperties: function (properties, config) { |
for (var i in properties) { |
@@ -5653,13 +6325,6 @@ config[i] = value; |
} |
} |
}, |
-_mixinConfigure: function (a, b) { |
-for (var prop in b) { |
-if (!this.getPropertyInfo(prop).readOnly) { |
-a[prop] = b[prop]; |
-} |
-} |
-}, |
_distributeConfig: function (config) { |
var fx$ = this._propertyEffects; |
if (fx$) { |
@@ -5667,10 +6332,10 @@ 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') { |
+if (x.kind === 'annotation' && !x.isCompound) { |
var node = this._nodes[x.effect.index]; |
if (node._configValue) { |
-var value = p === x.effect.value ? config[p] : this.get(x.effect.value, config); |
+var value = p === x.effect.value ? config[p] : this._get(x.effect.value, config); |
node._configValue(x.effect.name, value); |
} |
} |
@@ -5692,14 +6357,22 @@ this.__setProperty(n, config[n], n in aboveConfig); |
} |
}, |
_notifyListener: function (fn, e) { |
+if (!Polymer.Bind._isEventBogus(e, e.target)) { |
+var value, path; |
+if (e.detail) { |
+value = e.detail.value; |
+path = e.detail.path; |
+} |
if (!this._clientsReadied) { |
this._queueHandler([ |
fn, |
-e, |
-e.target |
+e.target, |
+value, |
+path |
]); |
} else { |
-return fn.call(this, e, e.target); |
+return fn.call(this, e.target, value, path); |
+} |
} |
}, |
_queueHandler: function (args) { |
@@ -5708,7 +6381,7 @@ 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]); |
+h[0].call(this, h[1], h[2], h[3]); |
} |
this._handlers = []; |
} |
@@ -5717,11 +6390,16 @@ this._handlers = []; |
'use strict'; |
Polymer.Base._addFeature({ |
notifyPath: function (path, value, fromAbove) { |
+var info = {}; |
+this._get(path, this, info); |
+this._notifyPath(info.path, value, fromAbove); |
+}, |
+_notifyPath: function (path, value, fromAbove) { |
var old = this._propertySetter(path, value); |
if (old !== value && (old === old || value === value)) { |
this._pathEffector(path, value); |
if (!fromAbove) { |
-this._notifyPath(path, value); |
+this._notifyPathUp(path, value); |
} |
return true; |
} |
@@ -5748,52 +6426,78 @@ var last = parts[parts.length - 1]; |
if (parts.length > 1) { |
for (var i = 0; i < parts.length - 1; i++) { |
var part = parts[i]; |
+if (array && part[0] == '#') { |
+prop = Polymer.Collection.get(array).getItem(part); |
+} else { |
prop = prop[part]; |
-if (array && parseInt(part) == part) { |
+if (array && parseInt(part, 10) == part) { |
parts[i] = Polymer.Collection.get(array).getKey(prop); |
} |
+} |
if (!prop) { |
return; |
} |
array = Array.isArray(prop) ? prop : null; |
} |
-if (array && parseInt(last) == last) { |
+if (array) { |
var coll = Polymer.Collection.get(array); |
+if (last[0] == '#') { |
+var key = last; |
+var old = coll.getItem(key); |
+last = array.indexOf(old); |
+coll.setItem(key, value); |
+} else if (parseInt(last, 10) == last) { |
var old = prop[last]; |
var key = coll.getKey(old); |
parts[i] = key; |
coll.setItem(key, value); |
} |
+} |
prop[last] = value; |
if (!root) { |
-this.notifyPath(parts.join('.'), value); |
+this._notifyPath(parts.join('.'), value); |
} |
} else { |
prop[path] = value; |
} |
}, |
get: function (path, root) { |
+return this._get(path, root); |
+}, |
+_get: function (path, root, info) { |
var prop = root || this; |
var parts = this._getPathParts(path); |
-var last = parts.pop(); |
-while (parts.length) { |
-prop = prop[parts.shift()]; |
+var array; |
+for (var i = 0; i < parts.length; i++) { |
if (!prop) { |
return; |
} |
+var part = parts[i]; |
+if (array && part[0] == '#') { |
+prop = Polymer.Collection.get(array).getItem(part); |
+} else { |
+prop = prop[part]; |
+if (info && array && parseInt(part, 10) == part) { |
+parts[i] = Polymer.Collection.get(array).getKey(prop); |
+} |
+} |
+array = Array.isArray(prop) ? prop : null; |
+} |
+if (info) { |
+info.path = parts.join('.'); |
} |
-return prop[last]; |
+return prop; |
}, |
_pathEffector: function (path, value) { |
var model = this._modelForPath(path); |
-var fx$ = this._propertyEffects[model]; |
+var fx$ = this._propertyEffects && this._propertyEffects[model]; |
if (fx$) { |
-fx$.forEach(function (fx) { |
-var fxFn = this['_' + fx.kind + 'PathEffect']; |
+for (var i = 0, fx; i < fx$.length && (fx = fx$[i]); i++) { |
+var fxFn = fx.pathFn; |
if (fxFn) { |
fxFn.call(this, path, value, fx.effect); |
} |
-}, this); |
+} |
} |
if (this._boundPaths) { |
this._notifyBoundPaths(path, value); |
@@ -5804,9 +6508,9 @@ if (effect.value === path || effect.value.indexOf(path + '.') === 0) { |
Polymer.Bind._annotationEffect.call(this, path, value, effect); |
} else if (path.indexOf(effect.value + '.') === 0 && !effect.negate) { |
var node = this._nodes[effect.index]; |
-if (node && node.notifyPath) { |
+if (node && node._notifyPath) { |
var p = this._fixPath(effect.name, effect.value, path); |
-node.notifyPath(p, value, true); |
+node._notifyPath(p, value, true); |
} |
} |
}, |
@@ -5846,70 +6550,88 @@ _notifyBoundPaths: function (path, value) { |
for (var a in this._boundPaths) { |
var b = this._boundPaths[a]; |
if (path.indexOf(a + '.') == 0) { |
-this.notifyPath(this._fixPath(b, a, path), value); |
+this._notifyPath(this._fixPath(b, a, path), value); |
} else if (path.indexOf(b + '.') == 0) { |
-this.notifyPath(this._fixPath(a, b, path), value); |
+this._notifyPath(this._fixPath(a, b, path), value); |
} |
} |
}, |
_fixPath: function (property, root, path) { |
return property + path.slice(root.length); |
}, |
-_notifyPath: function (path, value) { |
+_notifyPathUp: 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 }); |
+}, { |
+bubbles: false, |
+_useCache: true |
+}); |
}, |
_modelForPath: function (path) { |
var dot = path.indexOf('.'); |
return dot < 0 ? path : path.slice(0, dot); |
}, |
_EVENT_CHANGED: '-changed', |
-_notifySplice: function (array, path, index, added, removed) { |
-var splices = [{ |
-index: index, |
-addedCount: added, |
-removed: removed, |
-object: array, |
-type: 'splice' |
-}]; |
+notifySplices: function (path, splices) { |
+var info = {}; |
+var array = this._get(path, this, info); |
+this._notifySplices(array, info.path, splices); |
+}, |
+_notifySplices: function (array, path, splices) { |
var change = { |
keySplices: Polymer.Collection.applySplices(array, splices), |
indexSplices: splices |
}; |
-this.set(path + '.splices', change); |
-if (added != removed.length) { |
-this.notifyPath(path + '.length', array.length); |
+if (!array.hasOwnProperty('splices')) { |
+Object.defineProperty(array, 'splices', { |
+configurable: true, |
+writable: true |
+}); |
} |
+array.splices = change; |
+this._notifyPath(path + '.splices', change); |
+this._notifyPath(path + '.length', array.length); |
change.keySplices = null; |
change.indexSplices = null; |
}, |
+_notifySplice: function (array, path, index, added, removed) { |
+this._notifySplices(array, path, [{ |
+index: index, |
+addedCount: added, |
+removed: removed, |
+object: array, |
+type: 'splice' |
+}]); |
+}, |
push: function (path) { |
-var array = this.get(path); |
+var info = {}; |
+var array = this._get(path, this, info); |
var args = Array.prototype.slice.call(arguments, 1); |
var len = array.length; |
var ret = array.push.apply(array, args); |
if (args.length) { |
-this._notifySplice(array, path, len, args.length, []); |
+this._notifySplice(array, info.path, len, args.length, []); |
} |
return ret; |
}, |
pop: function (path) { |
-var array = this.get(path); |
+var info = {}; |
+var array = this._get(path, this, info); |
var hadLength = Boolean(array.length); |
var args = Array.prototype.slice.call(arguments, 1); |
var ret = array.pop.apply(array, args); |
if (hadLength) { |
-this._notifySplice(array, path, array.length, 0, [ret]); |
+this._notifySplice(array, info.path, array.length, 0, [ret]); |
} |
return ret; |
}, |
splice: function (path, start, deleteCount) { |
-var array = this.get(path); |
+var info = {}; |
+var array = this._get(path, this, info); |
if (start < 0) { |
start = array.length - Math.floor(-start); |
} else { |
@@ -5922,35 +6644,41 @@ var args = Array.prototype.slice.call(arguments, 1); |
var ret = array.splice.apply(array, args); |
var addedCount = Math.max(args.length - 2, 0); |
if (addedCount || ret.length) { |
-this._notifySplice(array, path, start, addedCount, ret); |
+this._notifySplice(array, info.path, start, addedCount, ret); |
} |
return ret; |
}, |
shift: function (path) { |
-var array = this.get(path); |
+var info = {}; |
+var array = this._get(path, this, info); |
var hadLength = Boolean(array.length); |
var args = Array.prototype.slice.call(arguments, 1); |
var ret = array.shift.apply(array, args); |
if (hadLength) { |
-this._notifySplice(array, path, 0, 0, [ret]); |
+this._notifySplice(array, info.path, 0, 0, [ret]); |
} |
return ret; |
}, |
unshift: function (path) { |
-var array = this.get(path); |
+var info = {}; |
+var array = this._get(path, this, info); |
var args = Array.prototype.slice.call(arguments, 1); |
var ret = array.unshift.apply(array, args); |
if (args.length) { |
-this._notifySplice(array, path, 0, args.length, []); |
+this._notifySplice(array, info.path, 0, args.length, []); |
} |
return ret; |
}, |
prepareModelNotifyPath: function (model) { |
this.mixin(model, { |
fire: Polymer.Base.fire, |
+_getEvent: Polymer.Base._getEvent, |
+__eventCache: Polymer.Base.__eventCache, |
notifyPath: Polymer.Base.notifyPath, |
+_get: Polymer.Base._get, |
_EVENT_CHANGED: Polymer.Base._EVENT_CHANGED, |
_notifyPath: Polymer.Base._notifyPath, |
+_notifyPathUp: Polymer.Base._notifyPathUp, |
_pathEffector: Polymer.Base._pathEffector, |
_annotationPathEffect: Polymer.Base._annotationPathEffect, |
_complexObserverPathEffect: Polymer.Base._complexObserverPathEffect, |
@@ -5958,7 +6686,8 @@ _annotatedComputationPathEffect: Polymer.Base._annotatedComputationPathEffect, |
_computePathEffect: Polymer.Base._computePathEffect, |
_modelForPath: Polymer.Base._modelForPath, |
_pathMatchesEffect: Polymer.Base._pathMatchesEffect, |
-_notifyBoundPaths: Polymer.Base._notifyBoundPaths |
+_notifyBoundPaths: Polymer.Base._notifyBoundPaths, |
+_getPathParts: Polymer.Base._getPathParts |
}); |
} |
}); |
@@ -6018,6 +6747,8 @@ node.parsedCssText = node.cssText = t.trim(); |
if (node.parent) { |
var ss = node.previous ? node.previous.end : node.parent.start; |
t = text.substring(ss, node.start - 1); |
+t = this._expandUnicodeEscapes(t); |
+t = t.replace(this._rx.multipleSpaces, ' '); |
t = t.substring(t.lastIndexOf(';') + 1); |
var s = node.parsedSelector = node.selector = t.trim(); |
node.atRule = s.indexOf(this.AT_START) === 0; |
@@ -6043,6 +6774,15 @@ this._parseCss(r, text); |
} |
return node; |
}, |
+_expandUnicodeEscapes: function (s) { |
+return s.replace(/\\([0-9a-f]{1,6})\s/gi, function () { |
+var code = arguments[1], repeat = 6 - code.length; |
+while (repeat--) { |
+code = '0' + code; |
+} |
+return '\\' + code; |
+}); |
+}, |
stringify: function (node, preserveProperties, text) { |
text = text || ''; |
var cssText = ''; |
@@ -6072,7 +6812,7 @@ text += this.CLOSE_BRACE + '\n\n'; |
return text; |
}, |
_hasMixinRules: function (rules) { |
-return rules[0].selector.indexOf(this.VAR_START) >= 0; |
+return rules[0].selector.indexOf(this.VAR_START) === 0; |
}, |
removeCustomProps: function (cssText) { |
return cssText; |
@@ -6095,10 +6835,11 @@ _rx: { |
comments: /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//gim, |
port: /@import[^;]*;/gim, |
customProp: /(?:^|[\s;])--[^;{]*?:[^{};]*?(?:[;\n]|$)/gim, |
-mixinProp: /(?:^|[\s;])--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim, |
+mixinProp: /(?:^|[\s;])?--[^;{]*?:[^{;]*?{[^}]*?}(?:[;\n]|$)?/gim, |
mixinApply: /@apply[\s]*\([^)]*?\)[\s]*(?:[;\n]|$)?/gim, |
-varApply: /[^;:]*?:[^;]*var[^;]*(?:[;\n]|$)?/gim, |
-keyframesRule: /^@[^\s]*keyframes/ |
+varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim, |
+keyframesRule: /^@[^\s]*keyframes/, |
+multipleSpaces: /\s+/g |
}, |
VAR_START: '--', |
MEDIA_START: '@media', |
@@ -6178,21 +6919,21 @@ return cssText; |
cssFromModule: function (moduleId, warnIfNotFound) { |
var m = Polymer.DomModule.import(moduleId); |
if (m && !m._cssText) { |
-m._cssText = this._cssFromElement(m); |
+m._cssText = this.cssFromElement(m); |
} |
if (!m && warnIfNotFound) { |
console.warn('Could not find style data in module named', moduleId); |
} |
return m && m._cssText || ''; |
}, |
-_cssFromElement: function (element) { |
+cssFromElement: function (element) { |
var cssText = ''; |
var content = element.content || element; |
-var e$ = Array.prototype.slice.call(content.querySelectorAll(this.MODULE_STYLES_SELECTOR)); |
+var e$ = Polymer.DomApi.arrayCopy(content.querySelectorAll(this.MODULE_STYLES_SELECTOR)); |
for (var i = 0, e; i < e$.length; i++) { |
e = e$[i]; |
if (e.localName === 'template') { |
-cssText += this._cssFromElement(e); |
+cssText += this.cssFromElement(e); |
} else { |
if (e.localName === 'style') { |
var include = e.getAttribute(this.INCLUDE_ATTR); |
@@ -6441,7 +7182,7 @@ _extendRule: function (target, source) { |
if (target.parent !== source.parent) { |
this._cloneAndAddRuleToParent(source, target.parent); |
} |
-target.extends = target.extends || (target.extends = []); |
+target.extends = target.extends || []; |
target.extends.push(source); |
source.selector = source.selector.replace(this.rx.STRIP, ''); |
source.selector = (source.selector && source.selector + ',\n') + target.selector; |
@@ -6482,14 +7223,18 @@ _prepStyles: function () { |
if (this._encapsulateStyle === undefined) { |
this._encapsulateStyle = !nativeShadow && Boolean(this._template); |
} |
+if (this._template) { |
this._styles = this._collectStyles(); |
var cssText = styleTransformer.elementStyles(this); |
-if (cssText && this._template) { |
+if (cssText) { |
var style = styleUtil.applyCss(cssText, this.is, nativeShadow ? this._template.content : null); |
if (!nativeShadow) { |
this._scopeStyle = style; |
} |
} |
+} else { |
+this._styles = []; |
+} |
}, |
_collectStyles: function () { |
var styles = []; |
@@ -6500,6 +7245,10 @@ cssText += styleUtil.cssFromModule(m); |
} |
} |
cssText += styleUtil.cssFromModule(this.is); |
+var p = this._template && this._template.parentNode; |
+if (this._template && (!p || p.id.toLowerCase() !== this.is)) { |
+cssText += styleUtil.cssFromElement(this._template); |
+} |
if (cssText) { |
var style = document.createElement('style'); |
style.textContent = cssText; |
@@ -6533,21 +7282,21 @@ 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) { |
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { |
n.className = self._scopeElementClass(n, n.className); |
-}); |
+} |
} |
}; |
scopify(container); |
if (shouldObserve) { |
var mo = new MutationObserver(function (mxns) { |
-mxns.forEach(function (m) { |
+for (var i = 0, m; i < mxns.length && (m = mxns[i]); i++) { |
if (m.addedNodes) { |
-for (var i = 0; i < m.addedNodes.length; i++) { |
-scopify(m.addedNodes[i]); |
+for (var j = 0; j < m.addedNodes.length; j++) { |
+scopify(m.addedNodes[j]); |
+} |
} |
} |
-}); |
}); |
mo.observe(container, { |
childList: true, |
@@ -6672,7 +7421,9 @@ p = pp.join(':'); |
parts[i] = p && p.lastIndexOf(';') === p.length - 1 ? p.slice(0, -1) : p || ''; |
} |
} |
-return parts.join(';'); |
+return parts.filter(function (v) { |
+return v; |
+}).join(';'); |
}, |
applyProperties: function (rule, props) { |
var output = ''; |
@@ -6798,7 +7549,7 @@ props[i] = v; |
} |
}, |
rx: { |
-VAR_ASSIGN: /(?:^|[;\n]\s*)(--[\w-]*?):\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\n])|$)/gi, |
+VAR_ASSIGN: /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi, |
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i, |
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi, |
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi, |
@@ -6917,9 +7668,12 @@ var styleDefaults = Polymer.StyleDefaults; |
var nativeShadow = Polymer.Settings.useNativeShadow; |
Polymer.Base._addFeature({ |
_prepStyleProperties: function () { |
-this._ownStylePropertyNames = this._styles ? propertyUtils.decorateStyles(this._styles) : []; |
+this._ownStylePropertyNames = this._styles ? propertyUtils.decorateStyles(this._styles) : null; |
+}, |
+customStyle: null, |
+getComputedStyleValue: function (property) { |
+return this._styleProperties && this._styleProperties[property] || getComputedStyle(this).getPropertyValue(property); |
}, |
-customStyle: {}, |
_setupStyleProperties: function () { |
this.customStyle = {}; |
}, |
@@ -7016,7 +7770,7 @@ if (host) { |
value = host._scopeElementClass(node, value); |
} |
} |
-node = Polymer.dom(node); |
+node = this.shadyRoot && this.shadyRoot._hasDistributed ? Polymer.dom(node) : node; |
serializeValueToAttribute.call(this, value, attribute, node); |
}, |
_scopeElementClass: function (element, selector) { |
@@ -7065,7 +7819,6 @@ var XSCOPE_NAME = propertyUtils.XSCOPE_NAME; |
Polymer.Base._addFeature({ |
_registerFeatures: function () { |
this._prepIs(); |
-this._prepAttributes(); |
this._prepConstructor(); |
this._prepTemplate(); |
this._prepStyles(); |
@@ -7073,6 +7826,7 @@ this._prepStyleProperties(); |
this._prepAnnotations(); |
this._prepEffects(); |
this._prepBehaviors(); |
+this._prepPropertyInfo(); |
this._prepBindings(); |
this._prepShady(); |
}, |
@@ -7082,23 +7836,28 @@ this._addComplexObserverEffects(b.observers); |
this._addHostAttributes(b.hostAttributes); |
}, |
_initFeatures: function () { |
-this._poolContent(); |
this._setupConfigure(); |
this._setupStyleProperties(); |
-this._pushHost(); |
+this._setupDebouncers(); |
+this._registerHost(); |
+if (this._template) { |
+this._poolContent(); |
+this._beginHosting(); |
this._stampTemplate(); |
-this._popHost(); |
+this._endHosting(); |
this._marshalAnnotationReferences(); |
-this._setupDebouncers(); |
+} |
this._marshalInstanceEffects(); |
-this._marshalHostAttributes(); |
this._marshalBehaviors(); |
+this._marshalHostAttributes(); |
this._marshalAttributes(); |
this._tryReady(); |
}, |
_marshalBehavior: function (b) { |
+if (b.listeners) { |
this._listenListeners(b.listeners); |
} |
+} |
}); |
(function () { |
var nativeShadow = Polymer.Settings.useNativeShadow; |
@@ -7110,6 +7869,7 @@ var styleTransformer = Polymer.StyleTransformer; |
Polymer({ |
is: 'custom-style', |
extends: 'style', |
+_template: null, |
properties: { include: String }, |
ready: function () { |
this._tryApply(); |
@@ -7124,18 +7884,19 @@ this._appliesToDocument = true; |
var e = this.__appliedElement || this; |
styleDefaults.addStyle(e); |
if (e.textContent || this.include) { |
-this._apply(); |
+this._apply(true); |
} else { |
+var self = this; |
var observer = new MutationObserver(function () { |
observer.disconnect(); |
-this._apply(); |
-}.bind(this)); |
+self._apply(true); |
+}); |
observer.observe(e, { childList: true }); |
} |
} |
} |
}, |
-_apply: function () { |
+_apply: function (deferProperties) { |
var e = this.__appliedElement || this; |
if (this.include) { |
e.textContent = styleUtil.cssFromModules(this.include, true) + e.textContent; |
@@ -7144,7 +7905,19 @@ if (e.textContent) { |
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function (rule) { |
styleTransformer.documentRule(rule); |
}); |
-this._applyCustomProperties(e); |
+var self = this; |
+function fn() { |
+self._applyCustomProperties(e); |
+} |
+if (this._pendingApplyProperties) { |
+cancelAnimationFrame(this._pendingApplyProperties); |
+this._pendingApplyProperties = null; |
+} |
+if (deferProperties) { |
+this._pendingApplyProperties = requestAnimationFrame(fn); |
+} else { |
+fn(); |
+} |
} |
}, |
_applyCustomProperties: function (element) { |
@@ -7181,8 +7954,9 @@ this._prepParentProperties(archetype, template); |
archetype._prepEffects(); |
this._customPrepEffects(archetype); |
archetype._prepBehaviors(); |
+archetype._prepPropertyInfo(); |
archetype._prepBindings(); |
-archetype._notifyPath = this._notifyPathImpl; |
+archetype._notifyPathUp = this._notifyPathUpImpl; |
archetype._scopeElementClass = this._scopeElementClassImpl; |
archetype.listen = this._listenImpl; |
archetype._showHideChildren = this._showHideChildrenImpl; |
@@ -7243,7 +8017,9 @@ var c = template._content; |
if (!c._notes) { |
var rootDataHost = archetype._rootDataHost; |
if (rootDataHost) { |
-Polymer.Annotations.prepElement = rootDataHost._prepElement.bind(rootDataHost); |
+Polymer.Annotations.prepElement = function () { |
+rootDataHost._prepElement(); |
+}; |
} |
c._notes = Polymer.Annotations.parseAnnotations(template); |
Polymer.Annotations.prepElement = null; |
@@ -7271,19 +8047,29 @@ var parentProp = this._parentPropPrefix + prop; |
var effects = [ |
{ |
kind: 'function', |
-effect: this._createForwardPropEffector(prop) |
+effect: this._createForwardPropEffector(prop), |
+fn: Polymer.Bind._functionEffect |
}, |
-{ kind: 'notify' } |
+{ |
+kind: 'notify', |
+fn: Polymer.Bind._notifyEffect, |
+effect: { event: Polymer.CaseMap.camelToDashCase(parentProp) + '-changed' } |
+} |
]; |
Polymer.Bind._createAccessors(proto, parentProp, effects); |
} |
} |
+var self = this; |
if (template != this) { |
Polymer.Bind.prepareInstance(template); |
-template._forwardParentProp = this._forwardParentProp.bind(this); |
+template._forwardParentProp = function (source, value) { |
+self._forwardParentProp(source, value); |
+}; |
} |
this._extendTemplate(template, proto); |
-template._pathEffector = this._pathEffectorImpl.bind(this); |
+template._pathEffector = function (path, value, fromAbove) { |
+return self._pathEffectorImpl(path, value, fromAbove); |
+}; |
} |
}, |
_createForwardPropEffector: function (prop) { |
@@ -7305,14 +8091,15 @@ this.dataHost._forwardInstanceProp(this, prop, value); |
}; |
}, |
_extendTemplate: function (template, proto) { |
-Object.getOwnPropertyNames(proto).forEach(function (n) { |
+var n$ = Object.getOwnPropertyNames(proto); |
+for (var i = 0, n; i < n$.length && (n = n$[i]); i++) { |
var val = template[n]; |
var pd = Object.getOwnPropertyDescriptor(proto, n); |
Object.defineProperty(template, n, pd); |
if (val !== undefined) { |
template._propertySetter(n, val); |
} |
-}); |
+} |
}, |
_showHideChildren: function (hidden) { |
}, |
@@ -7320,7 +8107,7 @@ _forwardInstancePath: function (inst, path, value) { |
}, |
_forwardInstanceProp: function (inst, prop, value) { |
}, |
-_notifyPathImpl: function (path, value) { |
+_notifyPathUpImpl: function (path, value) { |
var dataHost = this.dataHost; |
var dot = path.indexOf('.'); |
var root = dot < 0 ? path : path.slice(0, dot); |
@@ -7333,19 +8120,23 @@ _pathEffectorImpl: function (path, value, fromAbove) { |
if (this._forwardParentPath) { |
if (path.indexOf(this._parentPropPrefix) === 0) { |
var subPath = path.substring(this._parentPropPrefix.length); |
+var model = this._modelForPath(subPath); |
+if (model in this._parentProps) { |
this._forwardParentPath(subPath, value); |
} |
} |
+} |
Polymer.Base._pathEffector.call(this._templatized, path, value, fromAbove); |
}, |
_constructorImpl: function (model, host) { |
this._rootDataHost = host._getRootDataHost(); |
this._setupConfigure(model); |
-this._pushHost(host); |
+this._registerHost(host); |
+this._beginHosting(); |
this.root = this.instanceTemplate(this._template); |
this.root.__noContent = !this._notes._hasContent; |
this.root.__styleScoped = true; |
-this._popHost(); |
+this._endHosting(); |
this._marshalAnnotatedNodes(); |
this._marshalInstanceEffects(); |
this._marshalAnnotatedListeners(); |
@@ -7404,6 +8195,7 @@ el = el.parentNode; |
Polymer({ |
is: 'dom-template', |
extends: 'template', |
+_template: null, |
behaviors: [Polymer.Templatizer], |
ready: function () { |
this.templatize(this); |
@@ -7438,9 +8230,10 @@ this.omap.set(item, key); |
} else { |
this.pmap[item] = key; |
} |
-return key; |
+return '#' + key; |
}, |
removeKey: function (key) { |
+key = this._parseKey(key); |
this._removeFromMap(this.store[key]); |
delete this.store[key]; |
}, |
@@ -7457,16 +8250,29 @@ this.removeKey(key); |
return key; |
}, |
getKey: function (item) { |
+var key; |
if (item && typeof item == 'object') { |
-return this.omap.get(item); |
+key = this.omap.get(item); |
} else { |
-return this.pmap[item]; |
+key = this.pmap[item]; |
+} |
+if (key != undefined) { |
+return '#' + key; |
} |
}, |
getKeys: function () { |
-return Object.keys(this.store); |
+return Object.keys(this.store).map(function (key) { |
+return '#' + key; |
+}); |
+}, |
+_parseKey: function (key) { |
+if (key[0] == '#') { |
+return key.slice(1); |
+} |
+throw new Error('unexpected key ' + key); |
}, |
setItem: function (key, item) { |
+key = this._parseKey(key); |
var old = this.store[key]; |
if (old) { |
this._removeFromMap(old); |
@@ -7479,6 +8285,7 @@ this.pmap[item] = key; |
this.store[key] = item; |
}, |
getItem: function (key) { |
+key = this._parseKey(key); |
return this.store[key]; |
}, |
getItems: function () { |
@@ -7489,21 +8296,21 @@ items.push(store[key]); |
return items; |
}, |
_applySplices: function (splices) { |
-var keyMap = {}, key, i; |
-splices.forEach(function (s) { |
+var keyMap = {}, key; |
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { |
s.addedKeys = []; |
-for (i = 0; i < s.removed.length; i++) { |
-key = this.getKey(s.removed[i]); |
+for (var j = 0; j < s.removed.length; j++) { |
+key = this.getKey(s.removed[j]); |
keyMap[key] = keyMap[key] ? null : -1; |
} |
-for (i = 0; i < s.addedCount; i++) { |
-var item = this.userArray[s.index + i]; |
+for (var j = 0; j < s.addedCount; j++) { |
+var item = this.userArray[s.index + j]; |
key = this.getKey(item); |
key = key === undefined ? this.add(item) : key; |
keyMap[key] = keyMap[key] ? null : 1; |
s.addedKeys.push(key); |
} |
-}, this); |
+} |
var removed = []; |
var added = []; |
for (var key in keyMap) { |
@@ -7531,6 +8338,7 @@ return coll ? coll._applySplices(splices) : null; |
Polymer({ |
is: 'dom-repeat', |
extends: 'template', |
+_template: null, |
properties: { |
items: { type: Array }, |
as: { |
@@ -7553,22 +8361,37 @@ observe: { |
type: String, |
observer: '_observeChanged' |
}, |
-delay: Number |
+delay: Number, |
+initialCount: { |
+type: Number, |
+observer: '_initializeChunking' |
+}, |
+targetFramerate: { |
+type: Number, |
+value: 20 |
+}, |
+_targetFrameTime: { computed: '_computeFrameTime(targetFramerate)' } |
}, |
behaviors: [Polymer.Templatizer], |
observers: ['_itemsChanged(items.*)'], |
created: function () { |
this._instances = []; |
+this._pool = []; |
+this._limit = Infinity; |
+var self = this; |
+this._boundRenderChunk = function () { |
+self._renderChunk(); |
+}; |
}, |
detached: function () { |
for (var i = 0; i < this._instances.length; i++) { |
-this._detachRow(i); |
+this._detachInstance(i); |
} |
}, |
attached: function () { |
-var parentNode = Polymer.dom(this).parentNode; |
+var parent = Polymer.dom(Polymer.dom(this).parentNode); |
for (var i = 0; i < this._instances.length; i++) { |
-Polymer.dom(parentNode).insertBefore(this._instances[i].root, this); |
+this._attachInstance(i, parent); |
} |
}, |
ready: function () { |
@@ -7579,9 +8402,8 @@ if (!this.ctor) { |
this.templatize(this); |
} |
}, |
-_sortChanged: function () { |
+_sortChanged: function (sort) { |
var dataHost = this._getRootDataHost(); |
-var sort = this.sort; |
this._sortFn = sort && (typeof sort == 'function' ? sort : function () { |
return dataHost[sort].apply(dataHost, arguments); |
}); |
@@ -7590,9 +8412,8 @@ if (this.items) { |
this._debounceTemplate(this._render); |
} |
}, |
-_filterChanged: function () { |
+_filterChanged: function (filter) { |
var dataHost = this._getRootDataHost(); |
-var filter = this.filter; |
this._filterFn = filter && (typeof filter == 'function' ? filter : function () { |
return dataHost[filter].apply(dataHost, arguments); |
}); |
@@ -7601,6 +8422,32 @@ if (this.items) { |
this._debounceTemplate(this._render); |
} |
}, |
+_computeFrameTime: function (rate) { |
+return Math.ceil(1000 / rate); |
+}, |
+_initializeChunking: function () { |
+if (this.initialCount) { |
+this._limit = this.initialCount; |
+this._chunkCount = this.initialCount; |
+this._lastChunkTime = performance.now(); |
+} |
+}, |
+_tryRenderChunk: function () { |
+if (this.items && this._limit < this.items.length) { |
+this.debounce('renderChunk', this._requestRenderChunk); |
+} |
+}, |
+_requestRenderChunk: function () { |
+requestAnimationFrame(this._boundRenderChunk); |
+}, |
+_renderChunk: function () { |
+var currChunkTime = performance.now(); |
+var ratio = this._targetFrameTime / (currChunkTime - this._lastChunkTime); |
+this._chunkCount = Math.round(this._chunkCount * ratio) || 1; |
+this._limit += this._chunkCount; |
+this._lastChunkTime = currChunkTime; |
+this._debounceTemplate(this._render); |
+}, |
_observeChanged: function () { |
this._observePaths = this.observe && this.observe.replace('.*', '.').split(' '); |
}, |
@@ -7616,6 +8463,7 @@ this._error(this._logf('dom-repeat', 'expected array for `items`,' + ' found', t |
this._keySplices = []; |
this._indexSplices = []; |
this._needFullRefresh = true; |
+this._initializeChunking(); |
this._debounceTemplate(this._render); |
} else if (change.path == 'items.splices') { |
this._keySplices = this._keySplices.concat(change.value.keySplices); |
@@ -7654,7 +8502,7 @@ var c = this.collection; |
if (this._needFullRefresh) { |
this._applyFullRefresh(); |
this._needFullRefresh = false; |
-} else { |
+} else if (this._keySplices.length) { |
if (this._sortFn) { |
this._applySplicesUserSort(this._keySplices); |
} else { |
@@ -7664,16 +8512,26 @@ this._applyFullRefresh(); |
this._applySplicesArrayOrder(this._indexSplices); |
} |
} |
+} else { |
} |
this._keySplices = []; |
this._indexSplices = []; |
var keyToIdx = this._keyToInstIdx = {}; |
-for (var i = 0; i < this._instances.length; i++) { |
+for (var i = this._instances.length - 1; i >= 0; i--) { |
var inst = this._instances[i]; |
+if (inst.isPlaceholder && i < this._limit) { |
+inst = this._insertInstance(i, inst.__key__); |
+} else if (!inst.isPlaceholder && i >= this._limit) { |
+inst = this._downgradeInstance(i, inst.__key__); |
+} |
keyToIdx[inst.__key__] = i; |
+if (!inst.isPlaceholder) { |
inst.__setProperty(this.indexAs, i, true); |
} |
+} |
+this._pool.length = 0; |
this.fire('dom-change'); |
+this._tryRenderChunk(); |
}, |
_applyFullRefresh: function () { |
var c = this.collection; |
@@ -7689,33 +8547,34 @@ keys.push(c.getKey(items[i])); |
} |
} |
} |
+var self = this; |
if (this._filterFn) { |
keys = keys.filter(function (a) { |
-return this._filterFn(c.getItem(a)); |
-}, this); |
+return self._filterFn(c.getItem(a)); |
+}); |
} |
if (this._sortFn) { |
keys.sort(function (a, b) { |
-return this._sortFn(c.getItem(a), c.getItem(b)); |
-}.bind(this)); |
+return self._sortFn(c.getItem(a), c.getItem(b)); |
+}); |
} |
for (var i = 0; i < keys.length; i++) { |
var key = keys[i]; |
var inst = this._instances[i]; |
if (inst) { |
-inst.__setProperty('__key__', key, true); |
+inst.__key__ = key; |
+if (!inst.isPlaceholder && i < this._limit) { |
inst.__setProperty(this.as, c.getItem(key), true); |
+} |
+} else if (i < this._limit) { |
+this._insertInstance(i, key); |
} else { |
-this._instances.push(this._insertRow(i, key)); |
+this._insertPlaceholder(i, key); |
} |
} |
-for (; i < this._instances.length; i++) { |
-this._detachRow(i); |
+for (var j = this._instances.length - 1; j >= i; j--) { |
+this._detachAndRemoveInstance(j); |
} |
-this._instances.splice(keys.length, this._instances.length - keys.length); |
-}, |
-_keySort: function (a, b) { |
-return this.collection.getKey(a) - this.collection.getKey(b); |
}, |
_numericSort: function (a, b) { |
return a - b; |
@@ -7724,18 +8583,16 @@ _applySplicesUserSort: function (splices) { |
var c = this.collection; |
var instances = this._instances; |
var keyMap = {}; |
-var pool = []; |
-var sortFn = this._sortFn || this._keySort.bind(this); |
-splices.forEach(function (s) { |
-for (var i = 0; i < s.removed.length; i++) { |
-var key = s.removed[i]; |
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { |
+for (var j = 0; j < s.removed.length; j++) { |
+var key = s.removed[j]; |
keyMap[key] = keyMap[key] ? null : -1; |
} |
-for (var i = 0; i < s.added.length; i++) { |
-var key = s.added[i]; |
+for (var j = 0; j < s.added.length; j++) { |
+var key = s.added[j]; |
keyMap[key] = keyMap[key] ? null : 1; |
} |
-}, this); |
+} |
var removedIdxs = []; |
var addedKeys = []; |
for (var key in keyMap) { |
@@ -7751,36 +8608,35 @@ removedIdxs.sort(this._numericSort); |
for (var i = removedIdxs.length - 1; i >= 0; i--) { |
var idx = removedIdxs[i]; |
if (idx !== undefined) { |
-pool.push(this._detachRow(idx)); |
-instances.splice(idx, 1); |
+this._detachAndRemoveInstance(idx); |
} |
} |
} |
+var self = this; |
if (addedKeys.length) { |
if (this._filterFn) { |
addedKeys = addedKeys.filter(function (a) { |
-return this._filterFn(c.getItem(a)); |
-}, this); |
+return self._filterFn(c.getItem(a)); |
+}); |
} |
addedKeys.sort(function (a, b) { |
-return this._sortFn(c.getItem(a), c.getItem(b)); |
-}.bind(this)); |
+return self._sortFn(c.getItem(a), c.getItem(b)); |
+}); |
var start = 0; |
for (var i = 0; i < addedKeys.length; i++) { |
-start = this._insertRowUserSort(start, addedKeys[i], pool); |
+start = this._insertRowUserSort(start, addedKeys[i]); |
} |
} |
}, |
-_insertRowUserSort: function (start, key, pool) { |
+_insertRowUserSort: function (start, key) { |
var c = this.collection; |
var item = c.getItem(key); |
var end = this._instances.length - 1; |
var idx = -1; |
-var sortFn = this._sortFn || this._keySort.bind(this); |
while (start <= end) { |
var mid = start + end >> 1; |
var midKey = this._instances[mid].__key__; |
-var cmp = sortFn(c.getItem(midKey), item); |
+var cmp = this._sortFn(c.getItem(midKey), item); |
if (cmp < 0) { |
start = mid + 1; |
} else if (cmp > 0) { |
@@ -7793,65 +8649,80 @@ break; |
if (idx < 0) { |
idx = end + 1; |
} |
-this._instances.splice(idx, 0, this._insertRow(idx, key, pool)); |
+this._insertPlaceholder(idx, key); |
return idx; |
}, |
_applySplicesArrayOrder: function (splices) { |
-var pool = []; |
var c = this.collection; |
-splices.forEach(function (s) { |
-for (var i = 0; i < s.removed.length; i++) { |
-var inst = this._detachRow(s.index + i); |
-if (!inst.isPlaceholder) { |
-pool.push(inst); |
-} |
-} |
-this._instances.splice(s.index, s.removed.length); |
-for (var i = 0; i < s.addedKeys.length; i++) { |
-var inst = { |
-isPlaceholder: true, |
-key: s.addedKeys[i] |
-}; |
-this._instances.splice(s.index + i, 0, inst); |
+for (var i = 0, s; i < splices.length && (s = splices[i]); i++) { |
+for (var j = 0; j < s.removed.length; j++) { |
+this._detachAndRemoveInstance(s.index); |
} |
-}, this); |
-for (var i = this._instances.length - 1; i >= 0; i--) { |
-var inst = this._instances[i]; |
-if (inst.isPlaceholder) { |
-this._instances[i] = this._insertRow(i, inst.key, pool, true); |
+for (var j = 0; j < s.addedKeys.length; j++) { |
+this._insertPlaceholder(s.index + j, s.addedKeys[j]); |
} |
} |
}, |
-_detachRow: function (idx) { |
+_detachInstance: function (idx) { |
var inst = this._instances[idx]; |
if (!inst.isPlaceholder) { |
-var parentNode = Polymer.dom(this).parentNode; |
for (var i = 0; i < inst._children.length; i++) { |
var el = inst._children[i]; |
Polymer.dom(inst.root).appendChild(el); |
} |
-} |
return inst; |
+} |
+}, |
+_attachInstance: function (idx, parent) { |
+var inst = this._instances[idx]; |
+if (!inst.isPlaceholder) { |
+parent.insertBefore(inst.root, this); |
+} |
+}, |
+_detachAndRemoveInstance: function (idx) { |
+var inst = this._detachInstance(idx); |
+if (inst) { |
+this._pool.push(inst); |
+} |
+this._instances.splice(idx, 1); |
+}, |
+_insertPlaceholder: function (idx, key) { |
+this._instances.splice(idx, 0, { |
+isPlaceholder: true, |
+__key__: key |
+}); |
+}, |
+_stampInstance: function (idx, key) { |
+var model = { __key__: key }; |
+model[this.as] = this.collection.getItem(key); |
+model[this.indexAs] = idx; |
+return this.stamp(model); |
}, |
-_insertRow: function (idx, key, pool, replace) { |
-var inst; |
-if (inst = pool && pool.pop()) { |
+_insertInstance: function (idx, key) { |
+var inst = this._pool.pop(); |
+if (inst) { |
inst.__setProperty(this.as, this.collection.getItem(key), true); |
inst.__setProperty('__key__', key, true); |
} else { |
-inst = this._generateRow(idx, key); |
+inst = this._stampInstance(idx, key); |
} |
-var beforeRow = this._instances[replace ? idx + 1 : idx]; |
-var beforeNode = beforeRow ? beforeRow._children[0] : this; |
+var beforeRow = this._instances[idx + 1]; |
+var beforeNode = beforeRow && !beforeRow.isPlaceholder ? beforeRow._children[0] : this; |
var parentNode = Polymer.dom(this).parentNode; |
Polymer.dom(parentNode).insertBefore(inst.root, beforeNode); |
+this._instances[idx] = inst; |
return inst; |
}, |
-_generateRow: function (idx, key) { |
-var model = { __key__: key }; |
-model[this.as] = this.collection.getItem(key); |
-model[this.indexAs] = idx; |
-var inst = this.stamp(model); |
+_downgradeInstance: function (idx, key) { |
+var inst = this._detachInstance(idx); |
+if (inst) { |
+this._pool.push(inst); |
+} |
+inst = { |
+isPlaceholder: true, |
+__key__: key |
+}; |
+this._instances[idx] = inst; |
return inst; |
}, |
_showHideChildren: function (hidden) { |
@@ -7872,18 +8743,24 @@ this.set('items.' + idx, value); |
}, |
_forwardInstancePath: function (inst, path, value) { |
if (path.indexOf(this.as + '.') === 0) { |
-this.notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value); |
+this._notifyPath('items.' + inst.__key__ + '.' + path.slice(this.as.length + 1), value); |
} |
}, |
_forwardParentProp: function (prop, value) { |
-this._instances.forEach(function (inst) { |
+var i$ = this._instances; |
+for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) { |
+if (!inst.isPlaceholder) { |
inst.__setProperty(prop, value, true); |
-}, this); |
+} |
+} |
}, |
_forwardParentPath: function (path, value) { |
-this._instances.forEach(function (inst) { |
-inst.notifyPath(path, value, true); |
-}, this); |
+var i$ = this._instances; |
+for (var i = 0, inst; i < i$.length && (inst = i$[i]); i++) { |
+if (!inst.isPlaceholder) { |
+inst._notifyPath(path, value, true); |
+} |
+} |
}, |
_forwardItemPath: function (path, value) { |
if (this._keyToInstIdx) { |
@@ -7891,10 +8768,10 @@ var dot = path.indexOf('.'); |
var key = path.substring(0, dot < 0 ? path.length : dot); |
var idx = this._keyToInstIdx[key]; |
var inst = this._instances[idx]; |
-if (inst) { |
+if (inst && !inst.isPlaceholder) { |
if (dot >= 0) { |
path = this.as + '.' + path.substring(dot + 1); |
-inst.notifyPath(path, value, true); |
+inst._notifyPath(path, value, true); |
} else { |
inst.__setProperty(this.as, value, true); |
} |
@@ -7916,6 +8793,7 @@ return instance && instance[this.indexAs]; |
}); |
Polymer({ |
is: 'array-selector', |
+_template: null, |
properties: { |
items: { |
type: Array, |
@@ -7946,6 +8824,7 @@ this.unlinkPaths('selected.' + i); |
} |
} else { |
this.unlinkPaths('selected'); |
+this.unlinkPaths('selectedItem'); |
} |
if (this.multi) { |
if (!this.selected || this.selected.length) { |
@@ -8007,6 +8886,7 @@ this.linkPaths('selectedItem', 'items.' + key); |
Polymer({ |
is: 'dom-if', |
extends: 'template', |
+_template: null, |
properties: { |
'if': { |
type: Boolean, |
@@ -8054,20 +8934,23 @@ this._lastIf = this.if; |
}, |
_ensureInstance: function () { |
if (!this._instance) { |
+var parentNode = Polymer.dom(this).parentNode; |
+if (parentNode) { |
+var parent = Polymer.dom(parentNode); |
this._instance = this.stamp(); |
var root = this._instance.root; |
-var parent = Polymer.dom(Polymer.dom(this).parentNode); |
parent.insertBefore(root, this); |
} |
+} |
}, |
_teardownInstance: function () { |
if (this._instance) { |
-var c = this._instance._children; |
-if (c) { |
-var parent = Polymer.dom(Polymer.dom(c[0]).parentNode); |
-c.forEach(function (n) { |
+var c$ = this._instance._children; |
+if (c$) { |
+var parent = Polymer.dom(Polymer.dom(c$[0]).parentNode); |
+for (var i = 0, n; i < c$.length && (n = c$[i]); i++) { |
parent.removeChild(n); |
-}); |
+} |
} |
this._instance = null; |
} |
@@ -8085,15 +8968,19 @@ this._instance[prop] = value; |
}, |
_forwardParentPath: function (path, value) { |
if (this._instance) { |
-this._instance.notifyPath(path, value, true); |
+this._instance._notifyPath(path, value, true); |
} |
} |
}); |
Polymer({ |
is: 'dom-bind', |
extends: 'template', |
+_template: null, |
created: function () { |
-Polymer.RenderStatus.whenReady(this._markImportsReady.bind(this)); |
+var self = this; |
+Polymer.RenderStatus.whenReady(function () { |
+self._markImportsReady(); |
+}); |
}, |
_ensureReady: function () { |
if (!this._readied) { |
@@ -8132,7 +9019,10 @@ var config = {}; |
for (var prop in this._propertyEffects) { |
config[prop] = this[prop]; |
} |
-this._setupConfigure = this._setupConfigure.bind(this, config); |
+var setupConfigure = this._setupConfigure; |
+this._setupConfigure = function () { |
+setupConfigure.call(this, config); |
+}; |
}, |
attached: function () { |
if (this._importsReady) { |
@@ -8151,8 +9041,9 @@ this._prepEffects(); |
this._prepBehaviors(); |
this._prepConfigure(); |
this._prepBindings(); |
+this._prepPropertyInfo(); |
Polymer.Base._initFeatures.call(this); |
-this._children = Array.prototype.slice.call(this.root.childNodes); |
+this._children = Polymer.DomApi.arrayCopyChildNodes(this.root); |
} |
this._insertChildren(); |
this.fire('dom-change'); |
@@ -8340,7 +9231,7 @@ this.fire('dom-change'); |
var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); |
var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; |
- var DEFAULT_PHYSICAL_COUNT = 20; |
+ var DEFAULT_PHYSICAL_COUNT = 3; |
var MAX_PHYSICAL_COUNT = 500; |
Polymer({ |
@@ -8523,7 +9414,7 @@ this.fire('dom-change'); |
_scrollHeight: 0, |
/** |
- * The size of the viewport |
+ * The height of the list. This is referred as the viewport in the context of list. |
*/ |
_viewportSize: 0, |
@@ -8559,6 +9450,16 @@ this.fire('dom-change'); |
_itemsRendered: false, |
/** |
+ * The page that is currently rendered. |
+ */ |
+ _lastPage: null, |
+ |
+ /** |
+ * The max number of pages to render. One page is equivalent to the height of the list. |
+ */ |
+ _maxPages: 3, |
+ |
+ /** |
* The bottom of the physical content. |
*/ |
get _physicalBottom() { |
@@ -8566,6 +9467,13 @@ this.fire('dom-change'); |
}, |
/** |
+ * The bottom of the scroll. |
+ */ |
+ get _scrollBottom() { |
+ return this._scrollPosition + this._viewportSize; |
+ }, |
+ |
+ /** |
* The n-th item rendered in the last physical item. |
*/ |
get _virtualEnd() { |
@@ -8623,7 +9531,7 @@ this.fire('dom-change'); |
* to a viewport of physical items above and below the user's viewport. |
*/ |
get _optPhysicalSize() { |
- return this._viewportSize * 3; |
+ return this._viewportSize * this._maxPages; |
}, |
/** |
@@ -8720,19 +9628,13 @@ this.fire('dom-change'); |
* items in the viewport and recycle tiles as needed. |
*/ |
_refresh: function() { |
- var SCROLL_DIRECTION_UP = -1; |
- var SCROLL_DIRECTION_DOWN = 1; |
- var SCROLL_DIRECTION_NONE = 0; |
- |
// clamp the `scrollTop` value |
// IE 10|11 scrollTop may go above `_maxScrollTop` |
// iOS `scrollTop` may go below 0 and above `_maxScrollTop` |
var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop)); |
- |
- var tileHeight, kth, recycledTileSet; |
+ var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom; |
var ratio = this._ratio; |
var delta = scrollTop - this._scrollPosition; |
- var direction = SCROLL_DIRECTION_NONE; |
var recycledTiles = 0; |
var hiddenContentSize = this._hiddenContentSize; |
var currentRatio = ratio; |
@@ -8744,10 +9646,12 @@ this.fire('dom-change'); |
// clear cached visible index |
this._firstVisibleIndexVal = null; |
+ scrollBottom = this._scrollBottom; |
+ physicalBottom = this._physicalBottom; |
+ |
// random access |
if (Math.abs(delta) > this._physicalSize) { |
this._physicalTop += delta; |
- direction = SCROLL_DIRECTION_NONE; |
recycledTiles = Math.round(delta / this._physicalAverage); |
} |
// scroll up |
@@ -8755,7 +9659,6 @@ this.fire('dom-change'); |
var topSpace = scrollTop - this._physicalTop; |
var virtualStart = this._virtualStart; |
- direction = SCROLL_DIRECTION_UP; |
recycledTileSet = []; |
kth = this._physicalEnd; |
@@ -8768,12 +9671,14 @@ this.fire('dom-change'); |
// recycle less physical items than the total |
recycledTiles < this._physicalCount && |
// ensure that these recycled tiles are needed |
- virtualStart - recycledTiles > 0 |
+ virtualStart - recycledTiles > 0 && |
+ // ensure that the tile is not visible |
+ physicalBottom - this._physicalSizes[kth] > scrollBottom |
) { |
- tileHeight = this._physicalSizes[kth] || this._physicalAverage; |
+ tileHeight = this._physicalSizes[kth]; |
currentRatio += tileHeight / hiddenContentSize; |
- |
+ physicalBottom -= tileHeight; |
recycledTileSet.push(kth); |
recycledTiles++; |
kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; |
@@ -8781,15 +9686,13 @@ this.fire('dom-change'); |
movingUp = recycledTileSet; |
recycledTiles = -recycledTiles; |
- |
} |
// scroll down |
else if (delta > 0) { |
- var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize); |
+ var bottomSpace = physicalBottom - scrollBottom; |
var virtualEnd = this._virtualEnd; |
var lastVirtualItemIndex = this._virtualCount-1; |
- direction = SCROLL_DIRECTION_DOWN; |
recycledTileSet = []; |
kth = this._physicalStart; |
@@ -8802,10 +9705,12 @@ this.fire('dom-change'); |
// recycle less physical items than the total |
recycledTiles < this._physicalCount && |
// ensure that these recycled tiles are needed |
- virtualEnd + recycledTiles < lastVirtualItemIndex |
+ virtualEnd + recycledTiles < lastVirtualItemIndex && |
+ // ensure that the tile is not visible |
+ this._physicalTop + this._physicalSizes[kth] < scrollTop |
) { |
- tileHeight = this._physicalSizes[kth] || this._physicalAverage; |
+ tileHeight = this._physicalSizes[kth]; |
currentRatio += tileHeight / hiddenContentSize; |
this._physicalTop += tileHeight; |
@@ -8815,7 +9720,14 @@ this.fire('dom-change'); |
} |
} |
- if (recycledTiles !== 0) { |
+ if (recycledTiles === 0) { |
+ // If the list ever reach this case, the physical average is not significant enough |
+ // to create all the items needed to cover the entire viewport. |
+ // e.g. A few items have a height that differs from the average by serveral order of magnitude. |
+ if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
+ this.async(this._increasePool.bind(this, 1)); |
+ } |
+ } else { |
this._virtualStart = this._virtualStart + recycledTiles; |
this._update(recycledTileSet, movingUp); |
} |
@@ -8845,11 +9757,8 @@ this.fire('dom-change'); |
// set the scroller size |
this._updateScrollerSize(); |
- // increase the pool of physical items if needed |
- if (this._increasePoolIfNeeded()) { |
- // set models to the new items |
- this.async(this._update); |
- } |
+ // increase the pool of physical items |
+ this._increasePoolIfNeeded(); |
}, |
/** |
@@ -8862,7 +9771,6 @@ this.fire('dom-change'); |
for (var i = 0; i < size; i++) { |
var inst = this.stamp(null); |
- |
// First element child is item; Safari doesn't support children[0] |
// on a doc fragment |
physicalItems[i] = inst.root.querySelector('*'); |
@@ -8873,47 +9781,52 @@ this.fire('dom-change'); |
}, |
/** |
- * Increases the pool size. That is, the physical items in the DOM. |
+ * Increases the pool of physical items only if needed. |
* This function will allocate additional physical items |
- * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than |
- * `_optPhysicalSize` |
- * |
- * @return boolean |
+ * if the physical size is shorter than `_optPhysicalSize` |
*/ |
_increasePoolIfNeeded: function() { |
- if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage === 0) { |
- return false; |
+ if (this._viewportSize !== 0 && this._physicalSize < this._optPhysicalSize) { |
+ // 0 <= `currentPage` <= `_maxPages` |
+ var currentPage = Math.floor(this._physicalSize / this._viewportSize); |
+ |
+ if (currentPage === 0) { |
+ // fill the first page |
+ this.async(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5))); |
+ } else if (this._lastPage !== currentPage) { |
+ // once a page is filled up, paint it and defer the next increase |
+ requestAnimationFrame(this._increasePool.bind(this, 1)); |
+ } else { |
+ // fill the rest of the pages |
+ this.async(this._increasePool.bind(this, 1)); |
+ } |
+ this._lastPage = currentPage; |
+ return true; |
} |
+ return false; |
+ }, |
- // the estimated number of physical items that we will need to reach |
- // the cap established by `_optPhysicalSize`. |
- var missingItems = Math.round( |
- (this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAverage |
- ); |
- |
+ /** |
+ * Increases the pool size. |
+ */ |
+ _increasePool: function(missingItems) { |
// limit the size |
var nextPhysicalCount = Math.min( |
this._physicalCount + missingItems, |
this._virtualCount, |
MAX_PHYSICAL_COUNT |
); |
- |
var prevPhysicalCount = this._physicalCount; |
var delta = nextPhysicalCount - prevPhysicalCount; |
- if (delta <= 0) { |
- return false; |
- } |
- |
- var newPhysicalItems = this._createPool(delta); |
- var emptyArray = new Array(delta); |
+ if (delta > 0) { |
+ [].push.apply(this._physicalItems, this._createPool(delta)); |
+ [].push.apply(this._physicalSizes, new Array(delta)); |
- [].push.apply(this._physicalItems, newPhysicalItems); |
- [].push.apply(this._physicalSizes, emptyArray); |
- |
- this._physicalCount = prevPhysicalCount + delta; |
- |
- return true; |
+ this._physicalCount = prevPhysicalCount + delta; |
+ // tail call |
+ return this._update(); |
+ } |
}, |
/** |
@@ -8923,7 +9836,8 @@ this.fire('dom-change'); |
_render: function() { |
var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
- if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) { |
+ if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) { |
+ this._lastPage = 0; |
this._update(); |
this._itemsRendered = true; |
} |
@@ -9287,7 +10201,7 @@ this.fire('dom-change'); |
var hiddenContentSize = this._hiddenContentSize; |
// scroll to the item as much as we can |
- while (currentVirtualItem !== idx && targetOffsetTop < hiddenContentSize) { |
+ while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { |
targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; |
currentTopItem = (currentTopItem + 1) % this._physicalCount; |
currentVirtualItem++; |
@@ -9303,10 +10217,7 @@ this.fire('dom-change'); |
this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1); |
// increase the pool of physical items if needed |
- if (this._increasePoolIfNeeded()) { |
- // set models to the new items |
- this.async(this._update); |
- } |
+ this._increasePoolIfNeeded(); |
// clear cached visible index |
this._firstVisibleIndexVal = null; |
@@ -9381,363 +10292,117 @@ this.fire('dom-change'); |
this.$.selector.select(item); |
}, |
- /** |
- * Deselects the given item list if it is already selected. |
- * |
- |
- * @method deselect |
- * @param {(Object|number)} item The item object or its index |
- */ |
- deselectItem: function(item) { |
- item = this._getNormalizedItem(item); |
- var model = this._getModelFromItem(item); |
- |
- if (model) { |
- model[this.selectedAs] = false; |
- } |
- this.$.selector.deselect(item); |
- }, |
- |
- /** |
- * Select or deselect a given item depending on whether the item |
- * has already been selected. |
- * |
- * @method toggleSelectionForItem |
- * @param {(Object|number)} item The item object or its index |
- */ |
- toggleSelectionForItem: function(item) { |
- item = this._getNormalizedItem(item); |
- if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item)) { |
- this.deselectItem(item); |
- } else { |
- this.selectItem(item); |
- } |
- }, |
- |
- /** |
- * Clears the current selection state of the list. |
- * |
- * @method clearSelection |
- */ |
- clearSelection: function() { |
- function unselect(item) { |
- var model = this._getModelFromItem(item); |
- if (model) { |
- model[this.selectedAs] = false; |
- } |
- } |
- |
- if (Array.isArray(this.selectedItems)) { |
- this.selectedItems.forEach(unselect, this); |
- } else if (this.selectedItem) { |
- unselect.call(this, this.selectedItem); |
- } |
- |
- /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection(); |
- }, |
- |
- /** |
- * Add an event listener to `tap` if `selectionEnabled` is true, |
- * it will remove the listener otherwise. |
- */ |
- _selectionEnabledChanged: function(selectionEnabled) { |
- if (selectionEnabled) { |
- this.listen(this, 'tap', '_selectionHandler'); |
- this.listen(this, 'keypress', '_selectionHandler'); |
- } else { |
- this.unlisten(this, 'tap', '_selectionHandler'); |
- this.unlisten(this, 'keypress', '_selectionHandler'); |
- } |
- }, |
- |
- /** |
- * Select an item from an event object. |
- */ |
- _selectionHandler: function(e) { |
- if (e.type !== 'keypress' || e.keyCode === 13) { |
- var model = this.modelForElement(e.target); |
- if (model) { |
- this.toggleSelectionForItem(model[this.as]); |
- } |
- } |
- }, |
- |
- _multiSelectionChanged: function(multiSelection) { |
- this.clearSelection(); |
- this.$.selector.multi = multiSelection; |
- }, |
- |
- /** |
- * Updates the size of an item. |
- * |
- * @method updateSizeForItem |
- * @param {(Object|number)} item The item object or its index |
- */ |
- updateSizeForItem: function(item) { |
- item = this._getNormalizedItem(item); |
- var key = this._collection.getKey(item); |
- var pidx = this._physicalIndexForKey[key]; |
- |
- if (pidx !== undefined) { |
- this._updateMetrics([pidx]); |
- this._positionItems(); |
- } |
- } |
- }); |
- |
-})(); |
-(function() { |
- |
- 'use strict'; |
- |
- var SHADOW_WHEN_SCROLLING = 1; |
- var SHADOW_ALWAYS = 2; |
- |
- |
- var MODE_CONFIGS = { |
- |
- outerScroll: { |
- 'scroll': true |
- }, |
- |
- shadowMode: { |
- 'standard': SHADOW_ALWAYS, |
- 'waterfall': SHADOW_WHEN_SCROLLING, |
- 'waterfall-tall': SHADOW_WHEN_SCROLLING |
- }, |
- |
- tallMode: { |
- 'waterfall-tall': true |
- } |
- }; |
- |
- Polymer({ |
- |
- is: 'paper-header-panel', |
- |
- /** |
- * Fired when the content has been scrolled. `event.detail.target` returns |
- * the scrollable element which you can use to access scroll info such as |
- * `scrollTop`. |
- * |
- * <paper-header-panel on-content-scroll="scrollHandler"> |
- * ... |
- * </paper-header-panel> |
- * |
- * |
- * scrollHandler: function(event) { |
- * var scroller = event.detail.target; |
- * console.log(scroller.scrollTop); |
- * } |
- * |
- * @event content-scroll |
- */ |
- |
- properties: { |
- |
- /** |
- * Controls header and scrolling behavior. Options are |
- * `standard`, `seamed`, `waterfall`, `waterfall-tall`, `scroll` and |
- * `cover`. Default is `standard`. |
- * |
- * `standard`: The header is a step above the panel. The header will consume the |
- * panel at the point of entry, preventing it from passing through to the |
- * opposite side. |
- * |
- * `seamed`: The header is presented as seamed with the panel. |
- * |
- * `waterfall`: Similar to standard mode, but header is initially presented as |
- * seamed with panel, but then separates to form the step. |
- * |
- * `waterfall-tall`: The header is initially taller (`tall` class is added to |
- * the header). As the user scrolls, the header separates (forming an edge) |
- * while condensing (`tall` class is removed from the header). |
- * |
- * `scroll`: The header keeps its seam with the panel, and is pushed off screen. |
- * |
- * `cover`: The panel covers the whole `paper-header-panel` including the |
- * header. This allows user to style the panel in such a way that the panel is |
- * partially covering the header. |
- * |
- * <paper-header-panel mode="cover"> |
- * <paper-toolbar class="tall"> |
- * <core-icon-button icon="menu"></core-icon-button> |
- * </paper-toolbar> |
- * <div class="content"></div> |
- * </paper-header-panel> |
- */ |
- mode: { |
- type: String, |
- value: 'standard', |
- observer: '_modeChanged', |
- reflectToAttribute: true |
- }, |
- |
- /** |
- * If true, the drop-shadow is always shown no matter what mode is set to. |
- */ |
- shadow: { |
- type: Boolean, |
- value: false |
- }, |
- |
- /** |
- * The class used in waterfall-tall mode. Change this if the header |
- * accepts a different class for toggling height, e.g. "medium-tall" |
- */ |
- tallClass: { |
- type: String, |
- value: 'tall' |
- }, |
- |
- /** |
- * If true, the scroller is at the top |
- */ |
- atTop: { |
- type: Boolean, |
- value: true, |
- readOnly: true |
- } |
- }, |
- |
- observers: [ |
- '_computeDropShadowHidden(atTop, mode, shadow)' |
- ], |
- |
- ready: function() { |
- this.scrollHandler = this._scroll.bind(this); |
- this._addListener(); |
- |
- // Run `scroll` logic once to initialze class names, etc. |
- this._keepScrollingState(); |
- }, |
- |
- detached: function() { |
- this._removeListener(); |
- }, |
- |
- /** |
- * Returns the header element |
- * |
- * @property header |
- * @type Object |
- */ |
- get header() { |
- return Polymer.dom(this.$.headerContent).getDistributedNodes()[0]; |
- }, |
- |
- /** |
- * Returns the scrollable element. |
- * |
- * @property scroller |
- * @type Object |
- */ |
- get scroller() { |
- return this._getScrollerForMode(this.mode); |
- }, |
- |
- /** |
- * Returns true if the scroller has a visible shadow. |
- * |
- * @property visibleShadow |
- * @type Boolean |
- */ |
- get visibleShadow() { |
- return this.$.dropShadow.classList.contains('has-shadow'); |
- }, |
- |
- _computeDropShadowHidden: function(atTop, mode, shadow) { |
- |
- var shadowMode = MODE_CONFIGS.shadowMode[mode]; |
- |
- if (this.shadow) { |
- this.toggleClass('has-shadow', true, this.$.dropShadow); |
- |
- } else if (shadowMode === SHADOW_ALWAYS) { |
- this.toggleClass('has-shadow', true, this.$.dropShadow); |
- |
- } else if (shadowMode === SHADOW_WHEN_SCROLLING && !atTop) { |
- this.toggleClass('has-shadow', true, this.$.dropShadow); |
- |
- } else { |
- this.toggleClass('has-shadow', false, this.$.dropShadow); |
- |
- } |
- }, |
- |
- _computeMainContainerClass: function(mode) { |
- // TODO: It will be useful to have a utility for classes |
- // e.g. Polymer.Utils.classes({ foo: true }); |
- |
- var classes = {}; |
- |
- classes['flex'] = mode !== 'cover'; |
- |
- return Object.keys(classes).filter( |
- function(className) { |
- return classes[className]; |
- }).join(' '); |
- }, |
+ /** |
+ * Deselects the given item list if it is already selected. |
+ * |
- _addListener: function() { |
- this.scroller.addEventListener('scroll', this.scrollHandler, false); |
- }, |
+ * @method deselect |
+ * @param {(Object|number)} item The item object or its index |
+ */ |
+ deselectItem: function(item) { |
+ item = this._getNormalizedItem(item); |
+ var model = this._getModelFromItem(item); |
- _removeListener: function() { |
- this.scroller.removeEventListener('scroll', this.scrollHandler); |
- }, |
+ if (model) { |
+ model[this.selectedAs] = false; |
+ } |
+ this.$.selector.deselect(item); |
+ }, |
- _modeChanged: function(newMode, oldMode) { |
- var configs = MODE_CONFIGS; |
- var header = this.header; |
- var animateDuration = 200; |
+ /** |
+ * Select or deselect a given item depending on whether the item |
+ * has already been selected. |
+ * |
+ * @method toggleSelectionForItem |
+ * @param {(Object|number)} item The item object or its index |
+ */ |
+ toggleSelectionForItem: function(item) { |
+ item = this._getNormalizedItem(item); |
+ if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item)) { |
+ this.deselectItem(item); |
+ } else { |
+ this.selectItem(item); |
+ } |
+ }, |
- if (header) { |
- // in tallMode it may add tallClass to the header; so do the cleanup |
- // when mode is changed from tallMode to not tallMode |
- if (configs.tallMode[oldMode] && !configs.tallMode[newMode]) { |
- header.classList.remove(this.tallClass); |
- this.async(function() { |
- header.classList.remove('animate'); |
- }, animateDuration); |
- } else { |
- header.classList.toggle('animate', configs.tallMode[newMode]); |
- } |
+ /** |
+ * Clears the current selection state of the list. |
+ * |
+ * @method clearSelection |
+ */ |
+ clearSelection: function() { |
+ function unselect(item) { |
+ var model = this._getModelFromItem(item); |
+ if (model) { |
+ model[this.selectedAs] = false; |
} |
- this._keepScrollingState(); |
- }, |
+ } |
+ |
+ if (Array.isArray(this.selectedItems)) { |
+ this.selectedItems.forEach(unselect, this); |
+ } else if (this.selectedItem) { |
+ unselect.call(this, this.selectedItem); |
+ } |
- _keepScrollingState: function() { |
- var main = this.scroller; |
- var header = this.header; |
+ /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection(); |
+ }, |
- this._setAtTop(main.scrollTop === 0); |
+ /** |
+ * Add an event listener to `tap` if `selectionEnabled` is true, |
+ * it will remove the listener otherwise. |
+ */ |
+ _selectionEnabledChanged: function(selectionEnabled) { |
+ if (selectionEnabled) { |
+ this.listen(this, 'tap', '_selectionHandler'); |
+ this.listen(this, 'keypress', '_selectionHandler'); |
+ } else { |
+ this.unlisten(this, 'tap', '_selectionHandler'); |
+ this.unlisten(this, 'keypress', '_selectionHandler'); |
+ } |
+ }, |
- if (header && this.tallClass && MODE_CONFIGS.tallMode[this.mode]) { |
- this.toggleClass(this.tallClass, this.atTop || |
- header.classList.contains(this.tallClass) && |
- main.scrollHeight < this.offsetHeight, header); |
+ /** |
+ * Select an item from an event object. |
+ */ |
+ _selectionHandler: function(e) { |
+ if (e.type !== 'keypress' || e.keyCode === 13) { |
+ var model = this.modelForElement(e.target); |
+ if (model) { |
+ this.toggleSelectionForItem(model[this.as]); |
} |
- }, |
+ } |
+ }, |
- _scroll: function() { |
- this._keepScrollingState(); |
- this.fire('content-scroll', {target: this.scroller}, {bubbles: false}); |
- }, |
+ _multiSelectionChanged: function(multiSelection) { |
+ this.clearSelection(); |
+ this.$.selector.multi = multiSelection; |
+ }, |
- _getScrollerForMode: function(mode) { |
- return MODE_CONFIGS.outerScroll[mode] ? |
- this : this.$.mainContainer; |
- } |
+ /** |
+ * Updates the size of an item. |
+ * |
+ * @method updateSizeForItem |
+ * @param {(Object|number)} item The item object or its index |
+ */ |
+ updateSizeForItem: function(item) { |
+ item = this._getNormalizedItem(item); |
+ var key = this._collection.getKey(item); |
+ var pidx = this._physicalIndexForKey[key]; |
- }); |
+ if (pidx !== undefined) { |
+ this._updateMetrics([pidx]); |
+ this._positionItems(); |
+ } |
+ } |
+ }); |
- })(); |
+})(); |
(function() { |
// monostate data |
var metaDatas = {}; |
var metaArrays = {}; |
+ var singleton = null; |
Polymer.IronMeta = Polymer({ |
@@ -9790,9 +10455,15 @@ this.fire('dom-change'); |
}, |
+ hostAttributes: { |
+ hidden: true |
+ }, |
+ |
/** |
* Only runs if someone invokes the factory/constructor directly |
* e.g. `new Polymer.IronMeta()` |
+ * |
+ * @param {{type: (string|undefined), key: (string|undefined), value}=} config |
*/ |
factoryImpl: function(config) { |
if (config) { |
@@ -9884,6 +10555,13 @@ this.fire('dom-change'); |
}); |
+ Polymer.IronMeta.getIronMeta = function getIronMeta() { |
+ if (singleton === null) { |
+ singleton = new Polymer.IronMeta(); |
+ } |
+ return singleton; |
+ }; |
+ |
/** |
`iron-meta-query` can be used to access infomation stored in `iron-meta`. |
@@ -9950,6 +10628,8 @@ this.fire('dom-change'); |
/** |
* Actually a factory method, not a true constructor. Only runs if |
* someone invokes it directly (via `new Polymer.IronMeta()`); |
+ * |
+ * @param {{type: (string|undefined), key: (string|undefined)}=} config |
*/ |
factoryImpl: function(config) { |
if (config) { |
@@ -10086,9 +10766,9 @@ Polymer({ |
* `iron-iconset-svg` element. Multiple icons should be given distinct id's. |
* |
* Using svg elements to create icons has a few advantages over traditional |
- * bitmap graphics like jpg or png. Icons that use svg are vector based so they |
- * are resolution independent and should look good on any device. They are |
- * stylable via css. Icons can be themed, colorized, and even animated. |
+ * bitmap graphics like jpg or png. Icons that use svg are vector based so |
+ * they are resolution independent and should look good on any device. They |
+ * are stylable via css. Icons can be themed, colorized, and even animated. |
* |
* Example: |
* |
@@ -10096,8 +10776,8 @@ Polymer({ |
* <svg> |
* <defs> |
* <g id="shape"> |
- * <rect x="50" y="50" width="50" height="50" /> |
- * <circle cx="50" cy="50" r="50" /> |
+ * <rect x="12" y="0" width="12" height="24" /> |
+ * <circle cx="12" cy="12" r="12" /> |
* </g> |
* </defs> |
* </svg> |
@@ -10113,18 +10793,15 @@ Polymer({ |
* |
* @element iron-iconset-svg |
* @demo demo/index.html |
+ * @implements {Polymer.Iconset} |
*/ |
Polymer({ |
- |
is: 'iron-iconset-svg', |
properties: { |
/** |
* The name of the iconset. |
- * |
- * @attribute name |
- * @type string |
*/ |
name: { |
type: String, |
@@ -10133,10 +10810,6 @@ Polymer({ |
/** |
* The size of an individual icon. Note that icons must be square. |
- * |
- * @attribute iconSize |
- * @type number |
- * @default 24 |
*/ |
size: { |
type: Number, |
@@ -10145,6 +10818,10 @@ Polymer({ |
}, |
+ attached: function() { |
+ this.style.display = 'none'; |
+ }, |
+ |
/** |
* Construct an array of all icon names in this iconset. |
* |
@@ -10166,7 +10843,7 @@ Polymer({ |
* @method applyIcon |
* @param {Element} element Element to which the icon is applied. |
* @param {string} iconName Name of the icon to apply. |
- * @return {Element} The svg element which renders the icon. |
+ * @return {?Element} The svg element which renders the icon. |
*/ |
applyIcon: function(element, iconName) { |
// insert svg element into shadow root, if it exists |
@@ -10506,6 +11183,15 @@ Polymer({ |
} |
}, |
+ /** |
+ * If true, this property will cause the implementing element to |
+ * automatically stop propagation on any handled KeyboardEvents. |
+ */ |
+ stopKeyboardEventPropagation: { |
+ type: Boolean, |
+ value: false |
+ }, |
+ |
_boundKeyHandlers: { |
type: Array, |
value: function() { |
@@ -10649,6 +11335,10 @@ Polymer({ |
}, |
_onKeyBindingEvent: function(keyBindings, event) { |
+ if (this.stopKeyboardEventPropagation) { |
+ event.stopPropagation(); |
+ } |
+ |
keyBindings.forEach(function(keyBinding) { |
var keyCombo = keyBinding[0]; |
var handlerName = keyBinding[1]; |
@@ -10662,10 +11352,14 @@ Polymer({ |
_triggerKeyHandler: function(keyCombo, handlerName, keyboardEvent) { |
var detail = Object.create(keyCombo); |
detail.keyboardEvent = keyboardEvent; |
- |
- this[handlerName].call(this, new CustomEvent(keyCombo.event, { |
- detail: detail |
- })); |
+ var event = new CustomEvent(keyCombo.event, { |
+ detail: detail, |
+ cancelable: true |
+ }); |
+ this[handlerName].call(this, event); |
+ if (event.defaultPrevented) { |
+ keyboardEvent.preventDefault(); |
+ } |
} |
}; |
})(); |
@@ -10729,9 +11423,8 @@ Polymer({ |
// handled). In either case, we can disregard `event.path`. |
if (event.target === this) { |
- var focused = event.type === 'focus'; |
- this._setFocused(focused); |
- } else if (!this.shadowRoot) { |
+ this._setFocused(event.type === 'focus'); |
+ } else if (!this.shadowRoot && !this.isLightDescendant(event.target)) { |
this.fire(event.type, {sourceEvent: event}, { |
node: this, |
bubbles: event.bubbles, |
@@ -10882,14 +11575,35 @@ Polymer({ |
this._setPressed(false); |
}, |
+ /** |
+ * @param {!KeyboardEvent} event . |
+ */ |
_spaceKeyDownHandler: function(event) { |
var keyboardEvent = event.detail.keyboardEvent; |
+ var target = Polymer.dom(keyboardEvent).localTarget; |
+ |
+ // Ignore the event if this is coming from a focused light child, since that |
+ // element will deal with it. |
+ if (this.isLightDescendant(target)) |
+ return; |
+ |
keyboardEvent.preventDefault(); |
keyboardEvent.stopImmediatePropagation(); |
this._setPressed(true); |
}, |
- _spaceKeyUpHandler: function() { |
+ /** |
+ * @param {!KeyboardEvent} event . |
+ */ |
+ _spaceKeyUpHandler: function(event) { |
+ var keyboardEvent = event.detail.keyboardEvent; |
+ var target = Polymer.dom(keyboardEvent).localTarget; |
+ |
+ // Ignore the event if this is coming from a focused light child, since that |
+ // element will deal with it. |
+ if (this.isLightDescendant(target)) |
+ return; |
+ |
if (this.pressed) { |
this._asyncClick(); |
} |
@@ -11350,10 +12064,6 @@ Polymer({ |
} |
}, |
- observers: [ |
- '_noinkChanged(noink, isAttached)' |
- ], |
- |
get target () { |
var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
var target; |
@@ -11374,6 +12084,10 @@ Polymer({ |
}, |
attached: function() { |
+ // Set up a11yKeysBehavior to listen to key events on the target, |
+ // so that space and enter activate the ripple even if the target doesn't |
+ // handle key events. The key handlers deal with `noink` themselves. |
+ this.keyEventTarget = this.target; |
this.listen(this.target, 'up', 'uiUpAction'); |
this.listen(this.target, 'down', 'uiDownAction'); |
}, |
@@ -11543,12 +12257,6 @@ Polymer({ |
} else { |
this.upAction(); |
} |
- }, |
- |
- _noinkChanged: function(noink, attached) { |
- if (attached) { |
- this.keyEventTarget = noink ? this : this.target; |
- } |
} |
}); |
})(); |
@@ -11605,10 +12313,10 @@ Polymer({ |
/** |
* Ensures this element contains a ripple effect. For startup efficiency |
* the ripple effect is dynamically on demand when needed. |
- * @param {!Event=} opt_triggeringEvent (optional) event that triggered the |
+ * @param {!Event=} optTriggeringEvent (optional) event that triggered the |
* ripple. |
*/ |
- ensureRipple: function(opt_triggeringEvent) { |
+ ensureRipple: function(optTriggeringEvent) { |
if (!this.hasRipple()) { |
this._ripple = this._createRipple(); |
this._ripple.noink = this.noink; |
@@ -11616,12 +12324,14 @@ Polymer({ |
if (rippleContainer) { |
Polymer.dom(rippleContainer).appendChild(this._ripple); |
} |
- var domContainer = rippleContainer === this.shadyRoot ? this : |
- rippleContainer; |
- if (opt_triggeringEvent) { |
- var target = opt_triggeringEvent.target; |
- if (domContainer.contains(/** @type {Node} */(target))) { |
- this._ripple.uiDownAction(opt_triggeringEvent); |
+ if (optTriggeringEvent) { |
+ // Check if the event happened inside of the ripple container |
+ // Fall back to host instead of the root because distributed text |
+ // nodes are not valid event targets |
+ var domContainer = Polymer.dom(this._rippleContainer || this); |
+ var target = Polymer.dom(optTriggeringEvent).rootTarget; |
+ if (domContainer.deepContains( /** @type {Node} */(target))) { |
+ this._ripple.uiDownAction(optTriggeringEvent); |
} |
} |
} |
@@ -11666,7 +12376,7 @@ Polymer({ |
/** |
* `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus. |
* |
- * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl |
+ * @polymerBehavior Polymer.PaperInkyFocusBehavior |
*/ |
Polymer.PaperInkyFocusBehaviorImpl = { |
@@ -11704,7 +12414,6 @@ Polymer({ |
is: 'paper-material', |
properties: { |
- |
/** |
* The z-depth of this element, from 0-5. Setting to 0 will remove the |
* shadow, and each increasing number greater than 0 will be "deeper" |
@@ -11846,11 +12555,11 @@ Polymer({ |
} |
} |
}); |
-/** |
+/** |
* `iron-range-behavior` provides the behavior for something with a minimum to maximum range. |
* |
* @demo demo/index.html |
- * @polymerBehavior |
+ * @polymerBehavior |
*/ |
Polymer.IronRangeBehavior = { |
@@ -11919,7 +12628,7 @@ Polymer({ |
_calcStep: function(value) { |
/** |
* if we calculate the step using |
- * `Math.round(value / step) * step` we may hit a precision point issue |
+ * `Math.round(value / step) * step` we may hit a precision point issue |
* eg. 0.1 * 0.2 = 0.020000000000000004 |
* http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html |
* |
@@ -11927,7 +12636,8 @@ Polymer({ |
*/ |
// polymer/issues/2493 |
value = parseFloat(value); |
- return this.step ? (Math.round((value + this.min) / this.step) / (1 / this.step)) - this.min : value; |
+ return this.step ? (Math.round((value + this.min) / this.step) - |
+ (this.min / this.step)) / (1 / this.step) : value; |
}, |
_validateValue: function() { |
@@ -12378,17 +13088,25 @@ cr.define('downloads', function() { |
Item: Item, |
}; |
}); |
+/** @polymerBehavior Polymer.PaperItemBehavior */ |
+ Polymer.PaperItemBehaviorImpl = { |
+ hostAttributes: { |
+ role: 'option', |
+ tabindex: '0' |
+ } |
+ }; |
+ |
+ /** @polymerBehavior */ |
+ Polymer.PaperItemBehavior = [ |
+ Polymer.IronControlState, |
+ Polymer.IronButtonState, |
+ Polymer.PaperItemBehaviorImpl |
+ ]; |
Polymer({ |
is: 'paper-item', |
- hostAttributes: { |
- role: 'listitem', |
- tabindex: '0' |
- }, |
- |
behaviors: [ |
- Polymer.IronControlState, |
- Polymer.IronButtonState |
+ Polymer.PaperItemBehavior |
] |
}); |
/** |
@@ -12542,6 +13260,8 @@ Polymer({ |
/** |
* Returns the currently selected item. |
+ * |
+ * @type {?Object} |
*/ |
selectedItem: { |
type: Object, |
@@ -12583,10 +13303,20 @@ Polymer({ |
}, |
/** |
+ * The list of items from which a selection can be made. |
+ */ |
+ items: { |
+ type: Array, |
+ readOnly: true, |
+ value: function() { |
+ return []; |
+ } |
+ }, |
+ |
+ /** |
* The set of excluded elements where the key is the `localName` |
* of the element that will be ignored from the item list. |
* |
- * @type {object} |
* @default {template: 1} |
*/ |
_excludedLocalNames: { |
@@ -12606,15 +13336,12 @@ Polymer({ |
created: function() { |
this._bindFilterItem = this._filterItem.bind(this); |
this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); |
- // TODO(cdata): When polymer/polymer#2535 lands, we do not need to do this |
- // book keeping anymore: |
- this.__listeningForActivate = false; |
}, |
attached: function() { |
this._observer = this._observeItems(this); |
- this._contentObserver = this._observeContent(this); |
- if (!this.selectedItem && this.selected) { |
+ this._updateItems(); |
+ if (!this._shouldUpdateSelection) { |
this._updateSelected(this.attrForSelected,this.selected) |
} |
this._addListener(this.activateEvent); |
@@ -12622,26 +13349,12 @@ Polymer({ |
detached: function() { |
if (this._observer) { |
- this._observer.disconnect(); |
- } |
- if (this._contentObserver) { |
- this._contentObserver.disconnect(); |
+ Polymer.dom(this).unobserveNodes(this._observer); |
} |
this._removeListener(this.activateEvent); |
}, |
/** |
- * Returns an array of selectable items. |
- * |
- * @property items |
- * @type Array |
- */ |
- get items() { |
- var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); |
- return Array.prototype.filter.call(nodes, this._bindFilterItem); |
- }, |
- |
- /** |
* Returns the index of the given item. |
* |
* @method indexOf |
@@ -12683,18 +13396,16 @@ Polymer({ |
this.selected = this._indexToValue(index); |
}, |
- _addListener: function(eventName) { |
- if (!this.isAttached || this.__listeningForActivate) { |
- return; |
- } |
+ get _shouldUpdateSelection() { |
+ return this.selected != null; |
+ }, |
- this.__listeningForActivate = true; |
+ _addListener: function(eventName) { |
this.listen(this, eventName, '_activateHandler'); |
}, |
_removeListener: function(eventName) { |
this.unlisten(this, eventName, '_activateHandler'); |
- this.__listeningForActivate = false; |
}, |
_activateEventChanged: function(eventName, old) { |
@@ -12702,6 +13413,12 @@ Polymer({ |
this._addListener(eventName); |
}, |
+ _updateItems: function() { |
+ var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); |
+ nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); |
+ this._setItems(nodes); |
+ }, |
+ |
_updateSelected: function() { |
this._selectSelected(this.selected); |
}, |
@@ -12760,18 +13477,9 @@ Polymer({ |
this._setSelectedItem(this._selection.get()); |
}, |
- // observe content changes under the given node. |
- _observeContent: function(node) { |
- var content = node.querySelector('content'); |
- if (content && content.parentElement === node) { |
- return this._observeItems(node.domHost); |
- } |
- }, |
- |
// observe items change under the given node. |
_observeItems: function(node) { |
- // TODO(cdata): Update this when we get distributed children changed. |
- var observer = new MutationObserver(function(mutations) { |
+ return Polymer.dom(node).observeNodes(function(mutations) { |
// Let other interested parties know about the change so that |
// we don't have to recreate mutation observers everywher. |
this.fire('iron-items-changed', mutations, { |
@@ -12779,15 +13487,12 @@ Polymer({ |
cancelable: false |
}); |
- if (this.selected != null) { |
+ this._updateItems(); |
+ |
+ if (this._shouldUpdateSelection) { |
this._updateSelected(); |
} |
- }.bind(this)); |
- observer.observe(node, { |
- childList: true, |
- subtree: true |
}); |
- return observer; |
}, |
_activateHandler: function(e) { |
@@ -12872,6 +13577,11 @@ Polymer({ |
this._selection.multi = multi; |
}, |
+ get _shouldUpdateSelection() { |
+ return this.selected != null || |
+ (this.selectedValues != null && this.selectedValues.length); |
+ }, |
+ |
_updateSelected: function() { |
if (this.multi) { |
this._selectMulti(this.selectedValues); |
@@ -13209,18 +13919,14 @@ Polymer({ |
Polymer.IronMenuBehaviorImpl |
]; |
(function() { |
+ Polymer({ |
+ is: 'paper-menu', |
- Polymer({ |
- |
- is: 'paper-menu', |
- |
- behaviors: [ |
- Polymer.IronMenuBehavior |
- ] |
- |
- }); |
- |
-})(); |
+ behaviors: [ |
+ Polymer.IronMenuBehavior |
+ ] |
+ }); |
+ })(); |
/** |
Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and |
optionally centers it in the window or another element. |
@@ -13416,6 +14122,10 @@ CSS properties | Action |
if (!this._fitInfo.positionedBy.horizontally) { |
this.style.left = '0px'; |
} |
+ if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.horizontally) { |
+ // need position:fixed to properly size the element |
+ this.style.position = 'fixed'; |
+ } |
// need border-box for margin/padding |
this.sizingTarget.style.boxSizing = 'border-box'; |
// constrain the width and height if not already set |
@@ -13461,60 +14171,74 @@ CSS properties | Action |
} |
}; |
-Polymer.IronOverlayManager = (function() { |
+Polymer.IronOverlayManager = { |
+ |
+ _overlays: [], |
+ |
+ // iframes have a default z-index of 100, so this default should be at least |
+ // that. |
+ _minimumZ: 101, |
+ |
+ _backdrops: [], |
- var overlays = []; |
- var DEFAULT_Z = 10; |
- var backdrops = []; |
+ _applyOverlayZ: function(overlay, aboveZ) { |
+ this._setZ(overlay, aboveZ + 2); |
+ }, |
+ |
+ _setZ: function(element, z) { |
+ element.style.zIndex = z; |
+ }, |
// track overlays for z-index and focus managemant |
- function addOverlay(overlay) { |
- var z0 = currentOverlayZ(); |
- overlays.push(overlay); |
- var z1 = currentOverlayZ(); |
- if (z1 <= z0) { |
- applyOverlayZ(overlay, z0); |
+ addOverlay: function(overlay) { |
+ var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
+ this._overlays.push(overlay); |
+ var newZ = this.currentOverlayZ(); |
+ if (newZ <= minimumZ) { |
+ this._applyOverlayZ(overlay, minimumZ); |
} |
- } |
+ }, |
- function removeOverlay(overlay) { |
- var i = overlays.indexOf(overlay); |
+ removeOverlay: function(overlay) { |
+ var i = this._overlays.indexOf(overlay); |
if (i >= 0) { |
- overlays.splice(i, 1); |
- setZ(overlay, ''); |
+ this._overlays.splice(i, 1); |
+ this._setZ(overlay, ''); |
} |
- } |
- |
- function applyOverlayZ(overlay, aboveZ) { |
- setZ(overlay, aboveZ + 2); |
- } |
- |
- function setZ(element, z) { |
- element.style.zIndex = z; |
- } |
+ }, |
- function currentOverlay() { |
- var i = overlays.length - 1; |
- while (overlays[i] && !overlays[i].opened) { |
+ currentOverlay: function() { |
+ var i = this._overlays.length - 1; |
+ while (this._overlays[i] && !this._overlays[i].opened) { |
--i; |
} |
- return overlays[i]; |
- } |
+ return this._overlays[i]; |
+ }, |
- function currentOverlayZ() { |
- var z; |
- var current = currentOverlay(); |
+ currentOverlayZ: function() { |
+ var z = this._minimumZ; |
+ var current = this.currentOverlay(); |
if (current) { |
var z1 = window.getComputedStyle(current).zIndex; |
if (!isNaN(z1)) { |
z = Number(z1); |
} |
} |
- return z || DEFAULT_Z; |
- } |
+ return z; |
+ }, |
+ |
+ /** |
+ * Ensures that the minimum z-index of new overlays is at least `minimumZ`. |
+ * This does not effect the z-index of any existing overlays. |
+ * |
+ * @param {number} minimumZ |
+ */ |
+ ensureMinimumZ: function(minimumZ) { |
+ this._minimumZ = Math.max(this._minimumZ, minimumZ); |
+ }, |
- function focusOverlay() { |
- var current = currentOverlay(); |
+ focusOverlay: function() { |
+ var current = this.currentOverlay(); |
// We have to be careful to focus the next overlay _after_ any current |
// transitions are complete (due to the state being toggled prior to the |
// transition). Otherwise, we risk infinite recursion when a transitioning |
@@ -13526,36 +14250,26 @@ Polymer.IronOverlayManager = (function() { |
if (current && !current.transitioning) { |
current._applyFocus(); |
} |
- } |
+ }, |
- function trackBackdrop(element) { |
+ trackBackdrop: function(element) { |
// backdrops contains the overlays with a backdrop that are currently |
// visible |
if (element.opened) { |
- backdrops.push(element); |
+ this._backdrops.push(element); |
} else { |
- var index = backdrops.indexOf(element); |
+ var index = this._backdrops.indexOf(element); |
if (index >= 0) { |
- backdrops.splice(index, 1); |
+ this._backdrops.splice(index, 1); |
} |
} |
- } |
+ }, |
- function getBackdrops() { |
- return backdrops; |
+ getBackdrops: function() { |
+ return this._backdrops; |
} |
- return { |
- addOverlay: addOverlay, |
- removeOverlay: removeOverlay, |
- currentOverlay: currentOverlay, |
- currentOverlayZ: currentOverlayZ, |
- focusOverlay: focusOverlay, |
- trackBackdrop: trackBackdrop, |
- getBackdrops: getBackdrops |
- }; |
- |
- })(); |
+ }; |
(function() { |
Polymer({ |
@@ -14042,6 +14756,12 @@ context. You should place this element as a child of `<body>` whenever possible. |
*/ |
/** |
+ * Fired when the `iron-overlay` is canceled, but before it is closed. |
+ * Cancel the event to prevent the `iron-overlay` from closing. |
+ * @event iron-overlay-canceled |
+ */ |
+ |
+/** |
* Fired after the `iron-overlay` closes. |
* @event iron-overlay-closed |
* @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute |
@@ -15407,7 +16127,20 @@ Polymer({ |
} |
}); |
/** |
- * Use `Polymer.IronValidatableBehavior` to implement an element that validates user input. |
+ * `Use Polymer.IronValidatableBehavior` to implement an element that validates user input. |
+ * Use the related `Polymer.IronValidatorBehavior` to add custom validation logic to an iron-input. |
+ * |
+ * By default, an `<iron-form>` element validates its fields when the user presses the submit button. |
+ * To validate a form imperatively, call the form's `validate()` method, which in turn will |
+ * call `validate()` on all its children. By using `Polymer.IronValidatableBehavior`, your |
+ * custom element will get a public `validate()`, which |
+ * will return the validity of the element, and a corresponding `invalid` attribute, |
+ * which can be used for styling. |
+ * |
+ * To implement the custom validation logic of your element, you must override |
+ * the protected `_getValidity()` method of this behaviour, rather than `validate()`. |
+ * See [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html) |
+ * for an example. |
* |
* ### Accessibility |
* |
@@ -15577,7 +16310,8 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
* Regular expression to match valid input characters. |
*/ |
allowedPattern: { |
- type: String |
+ type: String, |
+ observer: "_allowedPatternChanged" |
}, |
_previousValidInput: { |
@@ -15628,6 +16362,11 @@ is separate from validation, and `allowed-pattern` does not affect how the input |
this.fire('bind-value-changed', {value: this.bindValue}); |
}, |
+ _allowedPatternChanged: function() { |
+ // Force to prevent invalid input when an `allowed-pattern` is set |
+ this.preventInvalidInput = this.allowedPattern ? true : false; |
+ }, |
+ |
_onInput: function() { |
// Need to validate each of the characters pasted if they haven't |
// been validated inside `_onKeypress` already. |
@@ -15974,21 +16713,19 @@ Polymer({ |
if (alwaysFloatLabel || _inputHasContent) { |
cls += ' label-is-floating'; |
+ // If the label is floating, ignore any offsets that may have been |
+ // applied from a prefix element. |
+ this.$.labelAndInputContainer.style.position = 'static'; |
+ |
if (invalid) { |
cls += ' is-invalid'; |
} else if (focused) { |
cls += " label-is-highlighted"; |
} |
- // The label might have a horizontal offset if a prefix element exists |
- // which needs to be undone when displayed as a floating label. |
- if (Polymer.dom(this.$.prefix).getDistributedNodes().length > 0 && |
- label && label.offsetParent) { |
- label.style.left = -label.offsetParent.offsetLeft + 'px'; |
- } |
} else { |
// When the label is not floating, it should overlap the input element. |
if (label) { |
- label.style.left = 0; |
+ this.$.labelAndInputContainer.style.position = 'relative'; |
} |
} |
} else { |
@@ -16209,6 +16946,10 @@ cr.define('downloads', function() { |
}, |
}, |
+ hostAttributes: { |
+ loading: true, |
+ }, |
+ |
/** |
* @param {Event} e |
* @private |
@@ -16291,7 +17032,7 @@ cr.define('downloads', function() { |
if (loadTimeData.getBoolean('allowDeletingHistory')) |
this.$.toolbar.downloadsShowing = this.hasDownloads_; |
- this.$.panel.classList.remove('loading'); |
+ this.removeAttribute('loading'); |
}, |
/** |