| OLD | NEW |
| 1 (function() { | 1 (function() { |
| 2 | 2 |
| 3 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); | 3 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); |
| 4 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; | 4 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; |
| 5 var DEFAULT_PHYSICAL_COUNT = 3; | 5 var DEFAULT_PHYSICAL_COUNT = 3; |
| 6 var HIDDEN_Y = '-10000px'; | 6 var HIDDEN_Y = '-10000px'; |
| 7 var DEFAULT_GRID_SIZE = 200; | 7 var DEFAULT_GRID_SIZE = 200; |
| 8 | 8 |
| 9 Polymer({ | 9 Polymer({ |
| 10 | 10 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 * This value is the same as `scrollTop`. | 148 * This value is the same as `scrollTop`. |
| 149 */ | 149 */ |
| 150 _scrollPosition: 0, | 150 _scrollPosition: 0, |
| 151 | 151 |
| 152 /** | 152 /** |
| 153 * The sum of the heights of all the tiles in the DOM. | 153 * The sum of the heights of all the tiles in the DOM. |
| 154 */ | 154 */ |
| 155 _physicalSize: 0, | 155 _physicalSize: 0, |
| 156 | 156 |
| 157 /** | 157 /** |
| 158 * The average `F` of the tiles observed till now. | 158 * The average `offsetHeight` of the tiles observed till now. |
| 159 */ | 159 */ |
| 160 _physicalAverage: 0, | 160 _physicalAverage: 0, |
| 161 | 161 |
| 162 /** | 162 /** |
| 163 * The number of tiles which `offsetHeight` > 0 observed until now. | 163 * The number of tiles which `offsetHeight` > 0 observed until now. |
| 164 */ | 164 */ |
| 165 _physicalAverageCount: 0, | 165 _physicalAverageCount: 0, |
| 166 | 166 |
| 167 /** | 167 /** |
| 168 * The Y position of the item rendered in the `_physicalStart` | 168 * The Y position of the item rendered in the `_physicalStart` |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 if (this._firstVisibleIndexVal === null) { | 416 if (this._firstVisibleIndexVal === null) { |
| 417 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin
gTop); | 417 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin
gTop); |
| 418 | 418 |
| 419 this._firstVisibleIndexVal = this._iterateItems( | 419 this._firstVisibleIndexVal = this._iterateItems( |
| 420 function(pidx, vidx) { | 420 function(pidx, vidx) { |
| 421 physicalOffset += this._getPhysicalSizeIncrement(pidx); | 421 physicalOffset += this._getPhysicalSizeIncrement(pidx); |
| 422 | 422 |
| 423 if (physicalOffset > this._scrollPosition) { | 423 if (physicalOffset > this._scrollPosition) { |
| 424 return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; | 424 return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; |
| 425 } | 425 } |
| 426 | |
| 427 // Handle a partially rendered final row in grid mode | 426 // Handle a partially rendered final row in grid mode |
| 428 if (this.grid && this._virtualCount - 1 === vidx) { | 427 if (this.grid && this._virtualCount - 1 === vidx) { |
| 429 return vidx - (vidx % this._itemsPerRow); | 428 return vidx - (vidx % this._itemsPerRow); |
| 430 } | 429 } |
| 431 }) || 0; | 430 }) || 0; |
| 432 } | 431 } |
| 433 return this._firstVisibleIndexVal; | 432 return this._firstVisibleIndexVal; |
| 434 }, | 433 }, |
| 435 | 434 |
| 436 /** | 435 /** |
| 437 * Gets the index of the last visible item in the viewport. | 436 * Gets the index of the last visible item in the viewport. |
| 438 * | 437 * |
| 439 * @type {number} | 438 * @type {number} |
| 440 */ | 439 */ |
| 441 get lastVisibleIndex() { | 440 get lastVisibleIndex() { |
| 442 if (this._lastVisibleIndexVal === null) { | 441 if (this._lastVisibleIndexVal === null) { |
| 443 if (this.grid) { | 442 if (this.grid) { |
| 444 var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._i
temsPerRow - 1; | 443 var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._i
temsPerRow - 1; |
| 445 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? this._vir
tualCount : lastIndex; | 444 this._lastVisibleIndexVal = Math.min(this._virtualCount, lastIndex); |
| 446 } else { | 445 } else { |
| 447 var physicalOffset = this._physicalTop; | 446 var physicalOffset = this._physicalTop; |
| 448 | |
| 449 this._iterateItems(function(pidx, vidx) { | 447 this._iterateItems(function(pidx, vidx) { |
| 448 if (physicalOffset < this._scrollBottom) { |
| 449 this._lastVisibleIndexVal = vidx; |
| 450 } else { |
| 451 // Break _iterateItems |
| 452 return true; |
| 453 } |
| 450 physicalOffset += this._getPhysicalSizeIncrement(pidx); | 454 physicalOffset += this._getPhysicalSizeIncrement(pidx); |
| 451 | |
| 452 if(physicalOffset <= this._scrollBottom) { | |
| 453 if (this.grid) { | |
| 454 var lastIndex = vidx - vidx % this._itemsPerRow + this._itemsPer
Row - 1; | |
| 455 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? thi
s._virtualCount : lastIndex; | |
| 456 } else { | |
| 457 this._lastVisibleIndexVal = vidx; | |
| 458 } | |
| 459 } | |
| 460 }); | 455 }); |
| 461 } | 456 } |
| 462 } | 457 } |
| 463 return this._lastVisibleIndexVal; | 458 return this._lastVisibleIndexVal; |
| 464 }, | 459 }, |
| 465 | 460 |
| 466 get _defaultScrollTarget() { | 461 get _defaultScrollTarget() { |
| 467 return this; | 462 return this; |
| 468 }, | 463 }, |
| 469 get _virtualRowCount() { | 464 get _virtualRowCount() { |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 824 }, | 819 }, |
| 825 | 820 |
| 826 /** | 821 /** |
| 827 * Called as a side effect of a host items.<key>.<path> path change, | 822 * Called as a side effect of a host items.<key>.<path> path change, |
| 828 * responsible for notifying item.<path> changes. | 823 * responsible for notifying item.<path> changes. |
| 829 */ | 824 */ |
| 830 _forwardItemPath: function(path, value) { | 825 _forwardItemPath: function(path, value) { |
| 831 if (!this._physicalIndexForKey) { | 826 if (!this._physicalIndexForKey) { |
| 832 return; | 827 return; |
| 833 } | 828 } |
| 834 var inst; | |
| 835 var dot = path.indexOf('.'); | 829 var dot = path.indexOf('.'); |
| 836 var key = path.substring(0, dot < 0 ? path.length : dot); | 830 var key = path.substring(0, dot < 0 ? path.length : dot); |
| 837 var idx = this._physicalIndexForKey[key]; | 831 var idx = this._physicalIndexForKey[key]; |
| 838 var el = this._physicalItems[idx]; | 832 var offscreenItem = this._offscreenFocusedItem; |
| 833 var el = offscreenItem && offscreenItem._templateInstance.__key__ === key
? |
| 834 offscreenItem : this._physicalItems[idx]; |
| 839 | 835 |
| 840 | 836 if (!el || el._templateInstance.__key__ !== key) { |
| 841 if (idx === this._focusedIndex && this._offscreenFocusedItem) { | |
| 842 el = this._offscreenFocusedItem; | |
| 843 } | |
| 844 if (!el) { | |
| 845 return; | 837 return; |
| 846 } | 838 } |
| 847 | 839 |
| 848 inst = el._templateInstance; | |
| 849 | |
| 850 if (inst.__key__ !== key) { | |
| 851 return; | |
| 852 } | |
| 853 if (dot >= 0) { | 840 if (dot >= 0) { |
| 854 path = this.as + '.' + path.substring(dot+1); | 841 path = this.as + '.' + path.substring(dot+1); |
| 855 inst.notifyPath(path, value, true); | 842 el._templateInstance.notifyPath(path, value, true); |
| 856 } else { | 843 } else { |
| 857 inst[this.as] = value; | 844 el._templateInstance[this.as] = value; |
| 858 } | 845 } |
| 846 |
| 859 }, | 847 }, |
| 860 | 848 |
| 861 /** | 849 /** |
| 862 * Called when the items have changed. That is, ressignments | 850 * Called when the items have changed. That is, ressignments |
| 863 * to `items`, splices or updates to a single item. | 851 * to `items`, splices or updates to a single item. |
| 864 */ | 852 */ |
| 865 _itemsChanged: function(change) { | 853 _itemsChanged: function(change) { |
| 866 if (change.path === 'items') { | 854 if (change.path === 'items') { |
| 867 // reset items | 855 // reset items |
| 868 this._virtualStart = 0; | 856 this._virtualStart = 0; |
| 869 this._physicalTop = 0; | 857 this._physicalTop = 0; |
| 870 this._virtualCount = this.items ? this.items.length : 0; | 858 this._virtualCount = this.items ? this.items.length : 0; |
| 871 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; | 859 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; |
| 872 this._physicalIndexForKey = {}; | 860 this._physicalIndexForKey = {}; |
| 861 this._firstVisibleIndexVal = null; |
| 862 this._lastVisibleIndexVal = null; |
| 873 | 863 |
| 874 this._resetScrollPosition(0); | 864 this._resetScrollPosition(0); |
| 875 this._removeFocusedItem(); | 865 this._removeFocusedItem(); |
| 876 | |
| 877 // create the initial physical items | 866 // create the initial physical items |
| 878 if (!this._physicalItems) { | 867 if (!this._physicalItems) { |
| 879 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); | 868 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); |
| 880 this._physicalItems = this._createPool(this._physicalCount); | 869 this._physicalItems = this._createPool(this._physicalCount); |
| 881 this._physicalSizes = new Array(this._physicalCount); | 870 this._physicalSizes = new Array(this._physicalCount); |
| 882 } | 871 } |
| 883 | 872 |
| 884 this._physicalStart = 0; | 873 this._physicalStart = 0; |
| 885 | 874 |
| 886 } else if (change.path === 'items.splices') { | 875 } else if (change.path === 'items.splices') { |
| 876 |
| 887 this._adjustVirtualIndex(change.value.indexSplices); | 877 this._adjustVirtualIndex(change.value.indexSplices); |
| 888 this._virtualCount = this.items ? this.items.length : 0; | 878 this._virtualCount = this.items ? this.items.length : 0; |
| 889 | 879 |
| 890 } else { | 880 } else { |
| 891 // update a single item | 881 // update a single item |
| 892 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); | 882 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); |
| 893 return; | 883 return; |
| 894 } | 884 } |
| 895 | 885 |
| 896 this._itemsRendered = false; | 886 this._itemsRendered = false; |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1109 /** | 1099 /** |
| 1110 * Adjusts the scroll position when it was overestimated. | 1100 * Adjusts the scroll position when it was overestimated. |
| 1111 */ | 1101 */ |
| 1112 _adjustScrollPosition: function() { | 1102 _adjustScrollPosition: function() { |
| 1113 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : | 1103 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : |
| 1114 Math.min(this._scrollPosition + this._physicalTop, 0); | 1104 Math.min(this._scrollPosition + this._physicalTop, 0); |
| 1115 | 1105 |
| 1116 if (deltaHeight) { | 1106 if (deltaHeight) { |
| 1117 this._physicalTop = this._physicalTop - deltaHeight; | 1107 this._physicalTop = this._physicalTop - deltaHeight; |
| 1118 // juking scroll position during interial scrolling on iOS is no bueno | 1108 // juking scroll position during interial scrolling on iOS is no bueno |
| 1119 if (!IOS_TOUCH_SCROLLING) { | 1109 if (!IOS_TOUCH_SCROLLING && this._physicalTop !== 0) { |
| 1120 this._resetScrollPosition(this._scrollTop - deltaHeight); | 1110 this._resetScrollPosition(this._scrollTop - deltaHeight); |
| 1121 } | 1111 } |
| 1122 } | 1112 } |
| 1123 }, | 1113 }, |
| 1124 | 1114 |
| 1125 /** | 1115 /** |
| 1126 * Sets the position of the scroll. | 1116 * Sets the position of the scroll. |
| 1127 */ | 1117 */ |
| 1128 _resetScrollPosition: function(pos) { | 1118 _resetScrollPosition: function(pos) { |
| 1129 if (this.scrollTarget) { | 1119 if (this.scrollTarget) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1148 forceUpdate = forceUpdate || this._scrollHeight === 0; | 1138 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 1149 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; | 1139 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 1150 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this
._estScrollHeight; | 1140 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this
._estScrollHeight; |
| 1151 | 1141 |
| 1152 // amortize height adjustment, so it won't trigger repaints very often | 1142 // amortize height adjustment, so it won't trigger repaints very often |
| 1153 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { | 1143 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| 1154 this.$.items.style.height = this._estScrollHeight + 'px'; | 1144 this.$.items.style.height = this._estScrollHeight + 'px'; |
| 1155 this._scrollHeight = this._estScrollHeight; | 1145 this._scrollHeight = this._estScrollHeight; |
| 1156 } | 1146 } |
| 1157 }, | 1147 }, |
| 1148 |
| 1158 /** | 1149 /** |
| 1159 * Scroll to a specific item in the virtual list regardless | 1150 * Scroll to a specific item in the virtual list regardless |
| 1160 * of the physical items in the DOM tree. | 1151 * of the physical items in the DOM tree. |
| 1161 * | 1152 * |
| 1153 * @method scrollToItem |
| 1154 * @param {(Object)} item The item to be scrolled to |
| 1155 */ |
| 1156 scrollToItem: function(item){ |
| 1157 return this.scrollToIndex(this.items.indexOf(item)); |
| 1158 }, |
| 1159 |
| 1160 /** |
| 1161 * Scroll to a specific index in the virtual list regardless |
| 1162 * of the physical items in the DOM tree. |
| 1163 * |
| 1162 * @method scrollToIndex | 1164 * @method scrollToIndex |
| 1163 * @param {number} idx The index of the item | 1165 * @param {number} idx The index of the item |
| 1164 */ | 1166 */ |
| 1165 scrollToIndex: function(idx) { | 1167 scrollToIndex: function(idx) { |
| 1166 if (typeof idx !== 'number') { | 1168 if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { |
| 1167 return; | 1169 return; |
| 1168 } | 1170 } |
| 1169 | 1171 |
| 1170 Polymer.dom.flush(); | 1172 Polymer.dom.flush(); |
| 1171 | 1173 |
| 1172 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); | 1174 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
| 1173 // update the virtual start only when needed | 1175 // update the virtual start only when needed |
| 1174 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { | 1176 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { |
| 1175 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx -
1); | 1177 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx -
1); |
| 1176 } | 1178 } |
| (...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1554 this._focusPhysicalItem(this._focusedIndex + 1); | 1556 this._focusPhysicalItem(this._focusedIndex + 1); |
| 1555 }, | 1557 }, |
| 1556 | 1558 |
| 1557 _didEnter: function(e) { | 1559 _didEnter: function(e) { |
| 1558 this._focusPhysicalItem(this._focusedIndex); | 1560 this._focusPhysicalItem(this._focusedIndex); |
| 1559 this._selectionHandler(e.detail.keyboardEvent); | 1561 this._selectionHandler(e.detail.keyboardEvent); |
| 1560 } | 1562 } |
| 1561 }); | 1563 }); |
| 1562 | 1564 |
| 1563 })(); | 1565 })(); |
| OLD | NEW |