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

Unified Diff: chrome/browser/resources/md_downloads/crisper.js

Issue 1428833005: MD Downloads: track downloads in C++, dispatch discrete JS updates (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: asdf Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: 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..0c58ef74c1549000c61eb61302eb53ce0c2f335f 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.
@@ -1561,7 +1562,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. */
@@ -1594,9 +1595,16 @@ cr.define('downloads', function() {
this.searchText_ = searchText;
- // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']).
+ /**
+ * @param {string} s
+ * @return {string} |s| without whitespace at the ends (trimmed).
+ */
function trim(s) { return s.trim(); }
- chrome.send('getDownloads', searchText.split(/"([^"]*)"/).map(trim));
+
+ // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']).
+ var terms = searchText ? searchText.split(/"([^"]*)"/).map(trim) : [];
+
+ chrome.send('getDownloads', terms);
},
/**
@@ -2045,7 +2053,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 +2365,7 @@ debouncer.stop();
}
}
});
-Polymer.version = '1.1.5';
+Polymer.version = '1.2.1';
Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
@@ -2638,61 +2646,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 +2771,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 +2795,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 +2827,7 @@ nativeAppendChild.call(container, node);
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
}
+this.notifyObserver();
return node;
},
removeChild: function (node) {
@@ -2877,6 +2842,7 @@ removeFromComposedParent(container, node);
nativeRemoveChild.call(container, node);
}
}
+this.notifyObserver();
return node;
},
replaceChild: function (node, ref_node) {
@@ -2969,6 +2935,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 +2953,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 +3072,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 +3138,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 +3337,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 +3401,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 +3419,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 +3482,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 +3945,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 +3956,9 @@ var children = this._composeNode(this);
this._updateChildNodes(this, children);
}
}
+if (!this.shadyRoot._hasDistributed) {
+notifyInitialDistribution(this);
+}
this.shadyRoot._hasDistributed = true;
}
},
@@ -3839,6 +4176,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 +4254,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 +4375,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 +4509,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 +4527,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 +4543,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 +4572,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 +5400,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 +5484,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 +5547,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 +5586,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 +5729,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 +5755,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 +5802,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 +5818,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 +5834,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 +5897,7 @@ this._addPropertyEffect(arg.model, 'compute', {
method: sig.method,
args: sig.args,
trigger: arg,
-property: name
+name: name
});
}, this);
},
@@ -5488,35 +5935,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 +6046,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 +6133,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 +6183,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 +6219,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);
+}
+}
+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);
@@ -5804,9 +6301,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 +6343,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 +6366,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 +6434,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 +6463,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 +6474,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 +6612,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 +7315,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 +7699,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 +7837,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 +7850,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 +7958,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 +7978,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 +8013,7 @@ this.pmap[item] = key;
this.store[key] = item;
},
getItem: function (key) {
+key = this._parseKey(key);
return this.store[key];
},
getItems: function () {
@@ -7872,7 +8407,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 +8417,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 +8429,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 +8481,7 @@ this.unlinkPaths('selected.' + i);
}
} else {
this.unlinkPaths('selected');
+this.unlinkPaths('selectedItem');
}
if (this.multi) {
if (!this.selected || this.selected.length) {
@@ -8085,7 +8621,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 +9102,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 +9263,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 +9281,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 +9306,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 +9321,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 +9340,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 +9355,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 +9395,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 +9421,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 +9429,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 +9459,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 +9855,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 +10288,7 @@ this.fire('dom-change');
// monostate data
var metaDatas = {};
var metaArrays = {};
+ var singleton = null;
Polymer.IronMeta = Polymer({
@@ -9790,9 +10341,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 +10441,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 +10514,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 +10652,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 +10662,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 +10679,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 +10696,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 +10704,10 @@ Polymer({
},
+ attached: function() {
+ this.style.display = 'none';
+ },
+
/**
* Construct an array of all icon names in this iconset.
*
@@ -10166,7 +10729,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 +11069,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 +11221,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 +11238,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 +11309,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 +11461,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 +12274,7 @@ Polymer({
/**
* `Polymer.PaperInkyFocusBehavior` implements a ripple when the element has keyboard focus.
*
- * @polymerBehavior Polymer.PaperInkyFocusBehaviorImpl
+ * @polymerBehavior Polymer.PaperInkyFocusBehavior
*/
Polymer.PaperInkyFocusBehaviorImpl = {
@@ -11704,7 +12312,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 +12453,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 +12526,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 +12534,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 +12990,7 @@ Polymer({
is: 'paper-item',
hostAttributes: {
- role: 'listitem',
+ role: 'option',
tabindex: '0'
},
@@ -12542,6 +13150,8 @@ Polymer({
/**
* Returns the currently selected item.
+ *
+ * @type {?Object}
*/
selectedItem: {
type: Object,
@@ -12583,10 +13193,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 +13226,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 +13239,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 +13286,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 +13303,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 +13367,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 +13377,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 +13467,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 +14016,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 +16011,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 +16500,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 +16611,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 +16831,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.items_.length > 0;
+ },
+
/**
* @param {Event} e
* @private
@@ -16247,78 +16919,64 @@ cr.define('downloads', function() {
},
/**
- * @return {number} The number of downloads shown on the page.
+ * @param {number} index
* @private
*/
- size_: function() {
- return this.items_.length;
+ removeItem_: function(index) {
+ this.splice('items_', index, 1);
+ this.updateHideDates_(index, index);
},
/**
- * 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;
+ 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;
}
-
- // 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);
- }
- 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.updateHideDates_(index, index);
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 queryRequiredElement('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.updateItem = function(index, data) {
+ Manager.get().updateItem_(index, data);
};
return {Manager: Manager};

Powered by Google App Engine
This is Rietveld 408576698