Index: third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js |
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js |
index 57f1260dd4598a7df9a823652a56c487f9d078cd..6159e4946800d830b155a8aee6043672e6d24d7d 100644 |
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js |
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js |
@@ -234,17 +234,6 @@ |
_collection: null, |
/** |
- * True if the current item list was rendered for the first time |
- * after attached. |
- */ |
- _itemsRendered: false, |
- |
- /** |
- * The page that is currently rendered. |
- */ |
- _lastPage: null, |
- |
- /** |
* The max number of pages to render. One page is equivalent to the height of the list. |
*/ |
_maxPages: 3, |
@@ -287,6 +276,11 @@ |
_rowHeight: 0, |
/** |
+ * The cost of stamping a template in ms. |
+ */ |
+ _templateCost: 0, |
+ |
+ /** |
* The bottom of the physical content. |
*/ |
get _physicalBottom() { |
@@ -405,7 +399,7 @@ |
* True if the current list is visible. |
*/ |
get _isVisible() { |
- return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.scrollTarget.offsetHeight); |
+ return Boolean(this.offsetWidth || this.offsetHeight); |
}, |
/** |
@@ -462,6 +456,7 @@ |
get _defaultScrollTarget() { |
return this; |
}, |
+ |
get _virtualRowCount() { |
return Math.ceil(this._virtualCount / this._itemsPerRow); |
}, |
@@ -480,14 +475,15 @@ |
attached: function() { |
this.updateViewportBoundaries(); |
- this._render(); |
+ if (this._physicalCount === 0) { |
+ this._debounceTemplate(this._render); |
+ } |
// `iron-resize` is fired when the list is attached if the event is added |
// before attached causing unnecessary work. |
this.listen(this, 'iron-resize', '_resizeHandler'); |
}, |
detached: function() { |
- this._itemsRendered = false; |
this.unlisten(this, 'iron-resize', '_resizeHandler'); |
}, |
@@ -612,7 +608,7 @@ |
} |
if (recycledTiles === 0) { |
- // Try to increase the pool if the list's client height isn't filled up with physical items |
+ // Try to increase the pool if the list's client isn't filled up with physical items |
if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
this._increasePoolIfNeeded(); |
} |
@@ -629,29 +625,25 @@ |
* @param {!Array<number>=} movingUp |
*/ |
_update: function(itemSet, movingUp) { |
- // manage focus |
this._manageFocus(); |
- // update models |
this._assignModels(itemSet); |
- // measure heights |
this._updateMetrics(itemSet); |
- // adjust offset after measuring |
+ // Adjust offset after measuring. |
if (movingUp) { |
while (movingUp.length) { |
var idx = movingUp.pop(); |
this._physicalTop -= this._getPhysicalSizeIncrement(idx); |
} |
} |
- // update the position of the items |
this._positionItems(); |
- // set the scroller size |
this._updateScrollerSize(); |
- // increase the pool of physical items |
this._increasePoolIfNeeded(); |
}, |
/** |
* Creates a pool of DOM elements and attaches them to the local dom. |
+ * |
+ * @param {number} size Size of the pool |
*/ |
_createPool: function(size) { |
var physicalItems = new Array(size); |
@@ -661,7 +653,7 @@ |
for (var i = 0; i < size; i++) { |
var inst = this.stamp(null); |
// First element child is item; Safari doesn't support children[0] |
- // on a doc fragment |
+ // on a doc fragment. |
physicalItems[i] = inst.root.querySelector('*'); |
Polymer.dom(this).appendChild(inst.root); |
} |
@@ -678,32 +670,39 @@ |
if (this._viewportHeight === 0) { |
return false; |
} |
- // Base case 2: If the physical size is optimal and the list's client height is full |
+ var self = this; |
+ var isClientFull = this._physicalBottom >= this._scrollBottom && |
+ this._physicalTop <= this._scrollPosition; |
+ |
+ // Base case 2: if the physical size is optimal and the list's client height is full |
// with physical items, don't increase the pool. |
- var isClientHeightFull = this._physicalBottom >= this._scrollBottom && this._physicalTop <= this._scrollPosition; |
- if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { |
+ if (this._physicalSize >= this._optPhysicalSize && isClientFull) { |
return false; |
} |
- // this value should range between [0 <= `currentPage` <= `_maxPages`] |
- var currentPage = Math.floor(this._physicalSize / this._viewportHeight); |
- |
- if (currentPage === 0) { |
- // fill the first page |
- this._debounceTemplate(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5))); |
- } else if (this._lastPage !== currentPage && isClientHeightFull) { |
- // paint the page and defer the next increase |
- // wait 16ms which is rough enough to get paint cycle. |
- Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increasePool.bind(this, this._itemsPerRow), 16)); |
- } else { |
- // fill the rest of the pages |
- this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow)); |
+ var maxPoolSize = Math.round(this._physicalCount * 0.5); |
+ // Increase the pool synchronously until the client is filled. |
+ if (!isClientFull) { |
+ this._debounceTemplate(this._increasePool.bind(this, maxPoolSize)); |
+ return true; |
} |
- |
- this._lastPage = currentPage; |
- |
+ this._yield(function() { |
+ self._increasePool(Math.min(maxPoolSize, Math.max(1, Math.round(50 / self._templateCost)))); |
+ }); |
return true; |
}, |
+ _yield: function(cb) { |
+ var g = window; |
+ var handle = g.requestIdleCallback ? g.requestIdleCallback(cb) : g.setTimeout(cb, 16); |
+ // Polymer/issues/3895 |
+ Polymer.dom.addDebouncer(/** @type {!Polymer.Debouncer} */({ |
+ complete: function() { |
+ g.cancelIdleCallback ? g.cancelIdleCallback(handle) : g.clearTimeout(handle); |
+ cb(); |
+ } |
+ })); |
+ }, |
+ |
/** |
* Increases the pool size. |
*/ |
@@ -715,17 +714,16 @@ |
); |
var prevPhysicalCount = this._physicalCount; |
var delta = nextPhysicalCount - prevPhysicalCount; |
+ var ts = window.performance.now(); |
if (delta <= 0) { |
return; |
} |
- |
+ // Concat arrays in place. |
[].push.apply(this._physicalItems, this._createPool(delta)); |
[].push.apply(this._physicalSizes, new Array(delta)); |
- |
this._physicalCount = prevPhysicalCount + delta; |
- |
- // update the physical start if we need to preserve the model of the focused item. |
+ // Update the physical start if it needs to preserve the model of the focused item. |
// In this situation, the focused item is currently rendered and its model would |
// have changed after increasing the pool if the physical start remained unchanged. |
if (this._physicalStart > this._physicalEnd && |
@@ -734,19 +732,19 @@ |
this._physicalStart = this._physicalStart + delta; |
} |
this._update(); |
+ this._templateCost = (window.performance.now() - ts) / delta; |
}, |
/** |
- * Render a new list of items. This method does exactly the same as `update`, |
- * but it also ensures that only one `update` cycle is created. |
+ * Render a new list of items. |
*/ |
_render: function() { |
- var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
- |
- if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) { |
- this._lastPage = 0; |
- this._update(); |
- this._itemsRendered = true; |
+ if (this.isAttached && this._isVisible) { |
+ if (this._physicalCount === 0) { |
+ this._increasePool(DEFAULT_PHYSICAL_COUNT); |
+ } else { |
+ this._update(); |
+ } |
} |
}, |
@@ -762,7 +760,6 @@ |
props[this.indexAs] = true; |
props[this.selectedAs] = true; |
props.tabIndex = true; |
- |
this._instanceProps = props; |
this._userTemplate = Polymer.dom(this).querySelector('template'); |
@@ -863,7 +860,6 @@ |
*/ |
_itemsChanged: function(change) { |
if (change.path === 'items') { |
- // reset items |
this._virtualStart = 0; |
this._physicalTop = 0; |
this._virtualCount = this.items ? this.items.length : 0; |
@@ -871,31 +867,22 @@ |
this._physicalIndexForKey = {}; |
this._firstVisibleIndexVal = null; |
this._lastVisibleIndexVal = null; |
- |
+ this._physicalCount = this._physicalCount || 0; |
+ this._physicalItems = this._physicalItems || []; |
+ this._physicalSizes = this._physicalSizes || []; |
+ this._physicalStart = 0; |
this._resetScrollPosition(0); |
this._removeFocusedItem(); |
- // create the initial physical items |
- if (!this._physicalItems) { |
- this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, this._virtualCount)); |
- this._physicalItems = this._createPool(this._physicalCount); |
- this._physicalSizes = new Array(this._physicalCount); |
- } |
- |
- this._physicalStart = 0; |
+ this._debounceTemplate(this._render); |
} else if (change.path === 'items.splices') { |
- |
this._adjustVirtualIndex(change.value.indexSplices); |
this._virtualCount = this.items ? this.items.length : 0; |
+ this._debounceTemplate(this._render); |
} else { |
- // update a single item |
this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.value); |
- return; |
} |
- |
- this._itemsRendered = false; |
- this._debounceTemplate(this._render); |
}, |
/** |
@@ -1008,7 +995,7 @@ |
*/ |
_updateMetrics: function(itemSet) { |
// Make sure we distributed all the physical items |
- // so we can measure them |
+ // so we can measure them. |
Polymer.dom.flush(); |
var newPhysicalSize = 0; |
@@ -1033,7 +1020,7 @@ |
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize; |
} |
- // update the average if we measured something |
+ // Update the average if it measured something. |
if (this._physicalAverageCount !== prevAvgCount) { |
this._physicalAverage = Math.round( |
((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
@@ -1064,23 +1051,17 @@ |
var rowOffset = (this._viewportWidth - totalItemWidth) / 2; |
this._iterateItems(function(pidx, vidx) { |
- |
var modulus = vidx % this._itemsPerRow; |
var x = Math.floor((modulus * this._itemWidth) + rowOffset); |
- |
this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); |
- |
if (this._shouldRenderNextRow(vidx)) { |
y += this._rowHeight; |
} |
- |
}); |
} else { |
this._iterateItems(function(pidx, vidx) { |
- |
this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); |
y += this._physicalSizes[pidx]; |
- |
}); |
} |
}, |
@@ -1150,7 +1131,7 @@ |
forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; |
forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this._estScrollHeight; |
- // amortize height adjustment, so it won't trigger repaints very often |
+ // Amortize height adjustment, so it won't trigger large repaints too often. |
if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { |
this.$.items.style.height = this._estScrollHeight + 'px'; |
this._scrollHeight = this._estScrollHeight; |
@@ -1179,44 +1160,38 @@ |
if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { |
return; |
} |
+ |
Polymer.dom.flush(); |
// Items should have been rendered prior scrolling to an index. |
- if (!this._itemsRendered) { |
+ if (this._physicalCount === 0) { |
return; |
} |
idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
- // update the virtual start only when needed |
+ // Update the virtual start only when needed. |
if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { |
this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx - 1); |
} |
- // manage focus |
this._manageFocus(); |
- // assign new models |
this._assignModels(); |
- // measure the new sizes |
this._updateMetrics(); |
- // estimate new physical offset |
+ // Estimate new physical offset. |
this._physicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage; |
var currentTopItem = this._physicalStart; |
var currentVirtualItem = this._virtualStart; |
var targetOffsetTop = 0; |
var hiddenContentSize = this._hiddenContentSize; |
- // scroll to the item as much as we can |
+ // scroll to the item as much as we can. |
while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { |
targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(currentTopItem); |
currentTopItem = (currentTopItem + 1) % this._physicalCount; |
currentVirtualItem++; |
} |
- // update the scroller size |
this._updateScrollerSize(true); |
- // update the position of the items |
this._positionItems(); |
- // set the new scroll position |
this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop); |
- // increase the pool of physical items if needed |
this._increasePoolIfNeeded(); |
- // clear cached visible index |
+ // clear cached visible index. |
this._firstVisibleIndexVal = null; |
this._lastVisibleIndexVal = null; |
}, |
@@ -1245,7 +1220,7 @@ |
this.updateViewportBoundaries(); |
this._render(); |
- if (this._itemsRendered && this._physicalItems && this._isVisible) { |
+ if (this._physicalCount > 0 && this._isVisible) { |
this._resetAverage(); |
this.scrollToIndex(this.firstVisibleIndex); |
} |