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..909df6e1a6cbf6bb1c281afd639ffeea13d6a500 100644 |
--- a/chrome/browser/resources/md_downloads/crisper.js |
+++ b/chrome/browser/resources/md_downloads/crisper.js |
@@ -1561,7 +1561,7 @@ cr.define('downloads', function() { |
* (i.e. has a non-empty search term). |
*/ |
isSearching: function() { |
- return this.searchText_.length > 0; |
+ return (this.searchText_ || '').length > 0; |
}, |
/** Opens the current local destination for downloads. */ |
@@ -1596,7 +1596,8 @@ cr.define('downloads', function() { |
// Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). |
function trim(s) { return s.trim(); } |
- chrome.send('getDownloads', searchText.split(/"([^"]*)"/).map(trim)); |
+ chrome.send('getDownloads', |
+ searchText ? searchText.split(/"([^"]*)"/).map(trim) : []); |
}, |
/** |
@@ -2045,7 +2046,7 @@ document.registerElement('dom-module', DomModule); |
function forceDocumentUpgrade() { |
if (cePolyfill) { |
var script = document._currentScript || document.currentScript; |
-var doc = script && script.ownerDocument; |
+var doc = script && script.ownerDocument || document; |
if (doc) { |
CustomElements.upgradeAll(doc); |
} |
@@ -2357,7 +2358,7 @@ debouncer.stop(); |
} |
} |
}); |
-Polymer.version = '1.1.5'; |
+Polymer.version = '1.2.1'; |
Polymer.Base._addFeature({ |
_registerFeatures: function () { |
this._prepIs(); |
@@ -2638,61 +2639,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; |
@@ -2818,6 +2764,17 @@ 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 +2788,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 +2820,7 @@ nativeAppendChild.call(container, node); |
if (addedInsertionPoint) { |
this._updateInsertionPoints(root.host); |
} |
+this.notifyObserver(); |
return node; |
}, |
removeChild: function (node) { |
@@ -2877,6 +2835,7 @@ removeFromComposedParent(container, node); |
nativeRemoveChild.call(container, node); |
} |
} |
+this.notifyObserver(); |
return node; |
}, |
replaceChild: function (node, ref_node) { |
@@ -2969,6 +2928,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 +2946,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) { |
@@ -3099,24 +3065,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 +3131,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) { |
@@ -3371,6 +3330,17 @@ return n$ ? Array.prototype.slice.call(n$) : []; |
}; |
DomApi.prototype._distributeParent = function () { |
}; |
+var nativeForwards = [ |
+'appendChild', |
+'insertBefore', |
+'removeChild', |
+'replaceChild' |
+]; |
+nativeForwards.forEach(function (forward) { |
+DomApi.prototype[forward] = function () { |
+return this.node[forward].apply(this.node, arguments); |
+}; |
+}); |
Object.defineProperties(DomApi.prototype, { |
childNodes: { |
get: function () { |
@@ -3424,13 +3394,17 @@ 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,43 +3412,6 @@ 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; |
@@ -3538,10 +3475,399 @@ saveLightChildrenIfNeeded: saveLightChildrenIfNeeded, |
matchesSelector: matchesSelector, |
hasInsertionPoint: hasInsertionPoint, |
ctor: DomApi, |
-factory: factory |
+factory: factory, |
+hasDomApi: hasDomApi |
+}; |
+}(); |
+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 h = Polymer.dom(content).observeNodes(this._scheduleNotify.bind(this)); |
+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 = this._flush.bind(this); |
+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) { |
+this._observer = Polymer.dom(host).observeNodes(this._scheduleNotify.bind(this)); |
+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); |
@@ -3612,6 +3938,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 +3949,9 @@ var children = this._composeNode(this); |
this._updateChildNodes(this, children); |
} |
} |
+if (!this.shadyRoot._hasDistributed) { |
+notifyInitialDistribution(this); |
+} |
this.shadyRoot._hasDistributed = true; |
} |
}, |
@@ -3839,6 +4169,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) { |
@@ -3904,22 +4247,66 @@ return list; |
_parseNodeAnnotations: function (node, list) { |
return node.nodeType === Node.TEXT_NODE ? this._parseTextNodeAnnotation(node, list) : this._parseElementAnnotations(node, list); |
}, |
-_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); |
@@ -3981,62 +4368,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 |
}; |
} |
}, |
@@ -4127,9 +4502,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 +4520,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); |
@@ -4154,15 +4536,17 @@ _discoverTemplateParentProps: function (notes) { |
var pp = {}; |
notes.forEach(function (n) { |
n.bindings.forEach(function (b) { |
-if (b.signature) { |
-var args = b.signature.args; |
+b.parts.forEach(function (p) { |
+if (p.signature) { |
+var args = p.signature.args; |
for (var k = 0; k < args.length; k++) { |
pp[args[k].model] = true; |
} |
} else { |
-pp[b.model] = true; |
+pp[p.model] = true; |
} |
}); |
+}); |
if (n.templateContent) { |
var tpp = n.templateContent._parentProps; |
Polymer.Base.mixin(pp, tpp); |
@@ -4181,15 +4565,43 @@ 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.$ = {}; |
@@ -4981,7 +5393,9 @@ 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) { |
@@ -5063,6 +5477,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).queryAllDistributedElements(slctr); |
+}, |
getContentChildNodes: function (slctr) { |
var content = Polymer.dom(this.root).querySelector(slctr || 'content'); |
return content ? Polymer.dom(content).getDistributedNodes() : []; |
@@ -5100,7 +5540,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); |
@@ -5139,7 +5579,7 @@ 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(); |
@@ -5282,7 +5722,7 @@ _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); |
+this._notifyPath(this._fixPath(path, property, e.detail.path), e.detail.value); |
} else { |
var value = target[property]; |
if (!isStructured) { |
@@ -5308,16 +5748,16 @@ node.addEventListener(info.event, inst._notifyListener.bind(inst, info.changedFn |
}; |
Polymer.Base.extend(Polymer.Bind, { |
_shouldAddListener: function (effect) { |
-return effect.name && effect.mode === '{' && !effect.negate && effect.kind != 'attribute'; |
+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) { |
@@ -5355,7 +5795,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 +5811,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 +5827,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]; |
} |
@@ -5450,7 +5890,7 @@ this._addPropertyEffect(arg.model, 'compute', { |
method: sig.method, |
args: sig.args, |
trigger: arg, |
-property: name |
+name: name |
}); |
}, this); |
}, |
@@ -5488,35 +5928,49 @@ this._addAnnotationEffect(binding, index); |
}, |
_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) { |
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 |
}); |
}, |
@@ -5585,9 +6039,14 @@ _marshalInstanceEffects: function () { |
Polymer.Bind.prepareInstance(this); |
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 { |
@@ -5667,10 +6126,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); |
} |
} |
@@ -5717,11 +6176,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,41 +6212,67 @@ 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); |
+} |
} |
-return prop[last]; |
+array = Array.isArray(prop) ? prop : null; |
+} |
+if (info) { |
+info.path = parts.join('.'); |
+} |
+return prop; |
}, |
_pathEffector: function (path, value) { |
var model = this._modelForPath(path); |
@@ -5804,9 +6294,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,16 +6336,16 @@ _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; |
@@ -5869,47 +6359,62 @@ 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,26 +6427,28 @@ 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; |
}, |
@@ -5949,8 +6456,10 @@ prepareModelNotifyPath: function (model) { |
this.mixin(model, { |
fire: Polymer.Base.fire, |
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 +6467,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 |
}); |
} |
}); |
@@ -6095,7 +6605,7 @@ _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/ |
@@ -6798,7 +7308,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, |
@@ -7182,7 +7692,7 @@ archetype._prepEffects(); |
this._customPrepEffects(archetype); |
archetype._prepBehaviors(); |
archetype._prepBindings(); |
-archetype._notifyPath = this._notifyPathImpl; |
+archetype._notifyPathUp = this._notifyPathUpImpl; |
archetype._scopeElementClass = this._scopeElementClassImpl; |
archetype.listen = this._listenImpl; |
archetype._showHideChildren = this._showHideChildrenImpl; |
@@ -7320,7 +7830,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,9 +7843,12 @@ _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) { |
@@ -7438,9 +7951,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 +7971,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 +8006,7 @@ this.pmap[item] = key; |
this.store[key] = item; |
}, |
getItem: function (key) { |
+key = this._parseKey(key); |
return this.store[key]; |
}, |
getItems: function () { |
@@ -7872,7 +8400,7 @@ 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) { |
@@ -7882,7 +8410,7 @@ inst.__setProperty(prop, value, true); |
}, |
_forwardParentPath: function (path, value) { |
this._instances.forEach(function (inst) { |
-inst.notifyPath(path, value, true); |
+inst._notifyPath(path, value, true); |
}, this); |
}, |
_forwardItemPath: function (path, value) { |
@@ -7894,7 +8422,7 @@ var inst = this._instances[idx]; |
if (inst) { |
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); |
} |
@@ -7946,6 +8474,7 @@ this.unlinkPaths('selected.' + i); |
} |
} else { |
this.unlinkPaths('selected'); |
+this.unlinkPaths('selectedItem'); |
} |
if (this.multi) { |
if (!this.selected || this.selected.length) { |
@@ -8085,7 +8614,7 @@ this._instance[prop] = value; |
}, |
_forwardParentPath: function (path, value) { |
if (this._instance) { |
-this._instance.notifyPath(path, value, true); |
+this._instance._notifyPath(path, value, true); |
} |
} |
}); |
@@ -8566,6 +9095,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() { |
@@ -8720,19 +9256,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; |
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,18 +9274,19 @@ this.fire('dom-change'); |
// clear cached visible index |
this._firstVisibleIndexVal = null; |
+ scrollBottom = this._scrollBottom; |
+ |
// random access |
if (Math.abs(delta) > this._physicalSize) { |
this._physicalTop += delta; |
- direction = SCROLL_DIRECTION_NONE; |
recycledTiles = Math.round(delta / this._physicalAverage); |
} |
// scroll up |
else if (delta < 0) { |
var topSpace = scrollTop - this._physicalTop; |
var virtualStart = this._virtualStart; |
+ var physicalBottom = this._physicalBottom; |
- direction = SCROLL_DIRECTION_UP; |
recycledTileSet = []; |
kth = this._physicalEnd; |
@@ -8768,12 +9299,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 +9314,13 @@ this.fire('dom-change'); |
movingUp = recycledTileSet; |
recycledTiles = -recycledTiles; |
- |
} |
// scroll down |
else if (delta > 0) { |
- var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize); |
+ var bottomSpace = this._physicalBottom - scrollBottom; |
var virtualEnd = this._virtualEnd; |
var lastVirtualItemIndex = this._virtualCount-1; |
- direction = SCROLL_DIRECTION_DOWN; |
recycledTileSet = []; |
kth = this._physicalStart; |
@@ -8802,10 +9333,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 +9348,15 @@ 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 (this._increasePoolIfNeeded()) { |
+ // yield and set models to the new items |
+ this.async(this._update); |
+ } |
+ } else { |
this._virtualStart = this._virtualStart + recycledTiles; |
this._update(recycledTileSet, movingUp); |
} |
@@ -8847,7 +9388,7 @@ this.fire('dom-change'); |
// increase the pool of physical items if needed |
if (this._increasePoolIfNeeded()) { |
- // set models to the new items |
+ // yield set models to the new items |
this.async(this._update); |
} |
}, |
@@ -8873,7 +9414,7 @@ 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` |
@@ -8881,16 +9422,22 @@ this.fire('dom-change'); |
* @return boolean |
*/ |
_increasePoolIfNeeded: function() { |
- if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage === 0) { |
+ if (this._physicalAverage === 0) { |
return false; |
} |
+ if (this._physicalBottom < this._scrollBottom || this._physicalTop > this._scrollPosition) { |
+ return this._increasePool(1); |
+ } |
+ if (this._physicalSize < this._optPhysicalSize) { |
+ return this._increasePool(Math.round((this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAverage)); |
+ } |
+ 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, |
@@ -8905,11 +9452,8 @@ this.fire('dom-change'); |
return false; |
} |
- var newPhysicalItems = this._createPool(delta); |
- var emptyArray = new Array(delta); |
- |
- [].push.apply(this._physicalItems, newPhysicalItems); |
- [].push.apply(this._physicalSizes, emptyArray); |
+ [].push.apply(this._physicalItems, this._createPool(delta)); |
+ [].push.apply(this._physicalSizes, new Array(delta)); |
this._physicalCount = prevPhysicalCount + delta; |
@@ -9304,10 +9848,9 @@ this.fire('dom-change'); |
// increase the pool of physical items if needed |
if (this._increasePoolIfNeeded()) { |
- // set models to the new items |
+ // yield set models to the new items |
this.async(this._update); |
} |
- |
// clear cached visible index |
this._firstVisibleIndexVal = null; |
}, |
@@ -9738,6 +10281,7 @@ this.fire('dom-change'); |
// monostate data |
var metaDatas = {}; |
var metaArrays = {}; |
+ var singleton = null; |
Polymer.IronMeta = Polymer({ |
@@ -9790,9 +10334,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 +10434,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 +10507,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 +10645,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 +10655,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 +10672,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 +10689,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 +10697,10 @@ Polymer({ |
}, |
+ attached: function() { |
+ this.style.display = 'none'; |
+ }, |
+ |
/** |
* Construct an array of all icon names in this iconset. |
* |
@@ -10166,7 +10722,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 +11062,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 +11214,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 +11231,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 +11302,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 +11454,43 @@ Polymer({ |
this._setPressed(false); |
}, |
+ __isFocusedLightDescendant: function(target) { |
+ var root = Polymer.dom(this).getOwnerRoot() || document; |
+ var focusedElement = root.activeElement; |
+ |
+ // TODO(noms): remove the `this !== target` check once polymer#2610 is fixed. |
+ return this !== target && this.isLightDescendant(target) && target == focusedElement; |
+ }, |
+ |
+ /** |
+ * @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.__isFocusedLightDescendant(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.__isFocusedLightDescendant(target)) |
+ return; |
+ |
if (this.pressed) { |
this._asyncClick(); |
} |
@@ -11666,7 +12267,7 @@ Polymer({ |
/** |
* `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus. |
* |
- * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl |
+ * @polymerBehavior Polymer.PaperInkyFocusBehavior |
*/ |
Polymer.PaperInkyFocusBehaviorImpl = { |
@@ -11704,7 +12305,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 +12446,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 +12519,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 +12527,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() { |
@@ -12382,7 +12983,7 @@ Polymer({ |
is: 'paper-item', |
hostAttributes: { |
- role: 'listitem', |
+ role: 'option', |
tabindex: '0' |
}, |
@@ -12542,6 +13143,8 @@ Polymer({ |
/** |
* Returns the currently selected item. |
+ * |
+ * @type {?Object} |
*/ |
selectedItem: { |
type: Object, |
@@ -12583,10 +13186,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 +13219,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 +13232,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 +13279,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 +13296,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 +13360,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 +13370,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 +13460,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); |
@@ -13416,6 +14009,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 |
@@ -15407,7 +16004,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 |
* |
@@ -15883,6 +16493,21 @@ Polymer({ |
} else { |
this._handleValue(this._inputElement); |
} |
+ |
+ this._numberOfPrefixNodes = 0; |
+ this._prefixObserver = Polymer.dom(this.$.prefix).observeNodes( |
+ function(mutations) { |
+ // Keep track whether there's at least one prefix node, since it |
+ // affects laying out the floating label. |
+ this._numberOfPrefixNodes += mutations.addedNodes.length - |
+ mutations.removedNodes.length; |
+ }.bind(this)); |
+ }, |
+ |
+ detached: function() { |
+ if (this._prefixObserver) { |
+ Polymer.dom(this.$.prefix).unobserveNodes(this._prefixObserver); |
+ } |
}, |
_onAddonAttached: function(event) { |
@@ -15979,16 +16604,15 @@ Polymer({ |
} else if (focused) { |
cls += " label-is-highlighted"; |
} |
- // The label might have a horizontal offset if a prefix element exists |
+ // If a prefix element exists, the label has a horizontal offset |
// 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'; |
+ if (this._numberOfPrefixNodes > 0) { |
+ this.$.labelAndInputContainer.style.position = 'static'; |
} |
} 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 { |
@@ -16200,15 +16824,56 @@ cr.define('downloads', function() { |
properties: { |
hasDownloads_: { |
+ observer: 'hasDownloadsChanged_', |
type: Boolean, |
- value: false, |
}, |
items_: { |
type: Array, |
+ value: function() { return []; }, |
}, |
}, |
+ observers: [ |
+ 'itemsChanged_(items_.*)', |
+ ], |
+ |
+ /** @private */ |
+ clearAll_: function() { |
+ this.set('items_', []); |
+ }, |
+ |
+ /** @private */ |
+ hasDownloadsChanged_: function() { |
+ if (loadTimeData.getBoolean('allowDeletingHistory')) |
+ this.$.toolbar.downloadsShowing = this.hasDownloads_; |
+ |
+ if (this.hasDownloads_) { |
+ this.$['downloads-list'].fire('iron-resize'); |
+ } else { |
+ var isSearching = downloads.ActionService.getInstance().isSearching(); |
+ var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; |
+ this.$['no-downloads'].querySelector('span').textContent = |
+ loadTimeData.getString(messageToShow); |
+ } |
+ }, |
+ |
+ /** |
+ * @param {number} index |
+ * @param {!Array<!downloads.Data>} list |
+ * @private |
+ */ |
+ insertItems_: function(index, list) { |
+ this.splice.apply(this, ['items_', index, 0].concat(list)); |
+ this.updateHideDates_(index, index + list.length); |
+ this.$.panel.classList.remove('loading'); |
+ }, |
+ |
+ /** @private */ |
+ itemsChanged_: function() { |
+ this.hasDownloads_ = this.size_() > 0; |
+ }, |
+ |
/** |
* @param {Event} e |
* @private |
@@ -16247,6 +16912,15 @@ cr.define('downloads', function() { |
}, |
/** |
+ * @param {number} index |
+ * @private |
+ */ |
+ removeItem_: function(index) { |
+ this.splice('items_', index, 1); |
+ this.updateHideDates_(index, index); |
+ }, |
+ |
+ /** |
* @return {number} The number of downloads shown on the page. |
* @private |
*/ |
@@ -16255,70 +16929,58 @@ cr.define('downloads', function() { |
}, |
/** |
- * Called when all items need to be updated. |
- * @param {!Array<!downloads.Data>} list A list of new download data. |
+ * @param {number} start |
+ * @param {number} end |
* @private |
*/ |
- updateAll_: function(list) { |
- /** @private {!Object<number>} */ |
- this.idToIndex_ = {}; |
- |
- for (var i = 0; i < list.length; ++i) { |
- var data = list[i]; |
- |
- this.idToIndex_[data.id] = data.index = i; |
- |
- var prev = list[i - 1]; |
- data.hideDate = !!prev && prev.date_string == data.date_string; |
- } |
- |
- // TODO(dbeam): this resets the scroll position, which is a huge bummer. |
- // Removing something from the bottom of the list should not scroll you |
- // back to the top. The grand plan is to restructure how the C++ sends the |
- // JS data so that it only gets updates (rather than the most recent set |
- // of items). TL;DR - we can't ship with this bug. |
- this.items_ = list; |
- |
- var hasDownloads = this.size_() > 0; |
- if (!hasDownloads) { |
- var isSearching = downloads.ActionService.getInstance().isSearching(); |
- var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads'; |
- this.$['no-downloads'].querySelector('span').textContent = |
- loadTimeData.getString(messageToShow); |
+ updateHideDates_: function(start, end) { |
+ for (var i = start; i <= end; ++i) { |
+ var current = this.items_[i]; |
+ if (!current) |
+ continue; |
+ var prev = this.items_[i - 1]; |
+ current.hideDate = !!prev && prev.date_string == current.date_string; |
} |
- this.hasDownloads_ = hasDownloads; |
- |
- if (loadTimeData.getBoolean('allowDeletingHistory')) |
- this.$.toolbar.downloadsShowing = this.hasDownloads_; |
- |
- this.$.panel.classList.remove('loading'); |
}, |
/** |
+ * @param {number} index |
* @param {!downloads.Data} data |
* @private |
*/ |
- updateItem_: function(data) { |
- var index = this.idToIndex_[data.id]; |
+ updateItem_: function(index, data) { |
this.set('items_.' + index, data); |
this.$['downloads-list'].updateSizeForItem(index); |
}, |
}); |
- Manager.size = function() { |
- return document.querySelector('downloads-manager').size_(); |
+ Manager.clearAll = function() { |
+ Manager.get().clearAll_(); |
}; |
- Manager.updateAll = function(list) { |
- document.querySelector('downloads-manager').updateAll_(list); |
+ /** @return {downloads.Manager} */ |
+ Manager.get = function() { |
+ return document.querySelector('downloads-manager'); |
}; |
- Manager.updateItem = function(item) { |
- document.querySelector('downloads-manager').updateItem_(item); |
+ Manager.insertItems = function(index, list) { |
+ Manager.get().insertItems_(index, list); |
}; |
Manager.onLoad = function() { |
- document.querySelector('downloads-manager').onLoad_(); |
+ Manager.get().onLoad_(); |
+ }; |
+ |
+ Manager.removeItem = function(index) { |
+ Manager.get().removeItem_(index); |
+ }; |
+ |
+ Manager.size = function() { |
+ return Manager.get().size_(); |
+ }; |
+ |
+ Manager.updateItem = function(index, data) { |
+ Manager.get().updateItem_(index, data); |
}; |
return {Manager: Manager}; |