| OLD | NEW |
| 1 | |
| 2 | |
| 3 (function() { | 1 (function() { |
| 4 | 2 |
| 5 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+)/); |
| 6 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; | 4 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; |
| 7 var DEFAULT_PHYSICAL_COUNT = 20; | 5 var DEFAULT_PHYSICAL_COUNT = 20; |
| 8 var MAX_PHYSICAL_COUNT = 500; | 6 var MAX_PHYSICAL_COUNT = 500; |
| 9 | 7 |
| 10 Polymer({ | 8 Polymer({ |
| 11 | 9 |
| 12 is: 'iron-list', | 10 is: 'iron-list', |
| (...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 /** | 293 /** |
| 296 * True if the current list is visible. | 294 * True if the current list is visible. |
| 297 */ | 295 */ |
| 298 get _isVisible() { | 296 get _isVisible() { |
| 299 return this._scroller && Boolean(this._scroller.offsetWidth || this._scrol
ler.offsetHeight); | 297 return this._scroller && Boolean(this._scroller.offsetWidth || this._scrol
ler.offsetHeight); |
| 300 }, | 298 }, |
| 301 | 299 |
| 302 /** | 300 /** |
| 303 * Gets the first visible item in the viewport. | 301 * Gets the first visible item in the viewport. |
| 304 * | 302 * |
| 305 * @property firstVisibleIndex | 303 * @type {number} |
| 306 */ | 304 */ |
| 307 get firstVisibleIndex() { | 305 get firstVisibleIndex() { |
| 308 var physicalOffset; | 306 var physicalOffset; |
| 309 | 307 |
| 310 if (this._firstVisibleIndexVal === null) { | 308 if (this._firstVisibleIndexVal === null) { |
| 311 physicalOffset = this._physicalTop; | 309 physicalOffset = this._physicalTop; |
| 312 | 310 |
| 313 this._firstVisibleIndexVal = this._iterateItems( | 311 this._firstVisibleIndexVal = this._iterateItems( |
| 314 function(pidx, vidx) { | 312 function(pidx, vidx) { |
| 315 physicalOffset += this._physicalSizes[pidx]; | 313 physicalOffset += this._physicalSizes[pidx]; |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 /** | 488 /** |
| 491 * Update the list of items, starting from the `_virtualStartVal` item. | 489 * Update the list of items, starting from the `_virtualStartVal` item. |
| 492 * @param {!Array<number>=} itemSet | 490 * @param {!Array<number>=} itemSet |
| 493 * @param {!Array<number>=} movingUp | 491 * @param {!Array<number>=} movingUp |
| 494 */ | 492 */ |
| 495 _update: function(itemSet, movingUp) { | 493 _update: function(itemSet, movingUp) { |
| 496 // update models | 494 // update models |
| 497 this._assignModels(itemSet); | 495 this._assignModels(itemSet); |
| 498 | 496 |
| 499 // measure heights | 497 // measure heights |
| 500 // TODO(blasten) pass `recycledTileSet` | 498 this._updateMetrics(itemSet); |
| 501 this._updateMetrics(); | |
| 502 | 499 |
| 503 // adjust offset after measuring | 500 // adjust offset after measuring |
| 504 if (movingUp) { | 501 if (movingUp) { |
| 505 while (movingUp.length) { | 502 while (movingUp.length) { |
| 506 this._physicalTop -= this._physicalSizes[movingUp.pop()]; | 503 this._physicalTop -= this._physicalSizes[movingUp.pop()]; |
| 507 } | 504 } |
| 508 } | 505 } |
| 509 | |
| 510 // update the position of the items | 506 // update the position of the items |
| 511 this._positionItems(); | 507 this._positionItems(); |
| 512 | 508 |
| 513 // set the scroller size | 509 // set the scroller size |
| 514 this._updateScrollerSize(); | 510 this._updateScrollerSize(); |
| 515 | 511 |
| 516 // increase the pool of physical items if needed | 512 // increase the pool of physical items if needed |
| 517 if (this._increasePoolIfNeeded()) { | 513 if (this._increasePoolIfNeeded()) { |
| 518 // set models to the new items | 514 // set models to the new items |
| 519 this.async(this._update); | 515 this.async(this._update); |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 821 } else { | 817 } else { |
| 822 inst.__key__ = null; | 818 inst.__key__ = null; |
| 823 el.setAttribute('hidden', ''); | 819 el.setAttribute('hidden', ''); |
| 824 } | 820 } |
| 825 | 821 |
| 826 }, itemSet); | 822 }, itemSet); |
| 827 }, | 823 }, |
| 828 | 824 |
| 829 /** | 825 /** |
| 830 * Updates the height for a given set of items. | 826 * Updates the height for a given set of items. |
| 827 * |
| 828 * @param {!Array<number>=} itemSet |
| 831 */ | 829 */ |
| 832 _updateMetrics: function() { | 830 _updateMetrics: function(itemSet) { |
| 833 var total = 0; | 831 var newPhysicalSize = 0; |
| 832 var oldPhysicalSize = 0; |
| 834 var prevAvgCount = this._physicalAverageCount; | 833 var prevAvgCount = this._physicalAverageCount; |
| 835 var prevPhysicalAvg = this._physicalAverage; | 834 var prevPhysicalAvg = this._physicalAverage; |
| 836 | |
| 837 // Make sure we distributed all the physical items | 835 // Make sure we distributed all the physical items |
| 838 // so we can measure them | 836 // so we can measure them |
| 839 Polymer.dom.flush(); | 837 Polymer.dom.flush(); |
| 840 | 838 |
| 841 for (var i = 0; i < this._physicalCount; i++) { | 839 this._iterateItems(function(pidx, vidx) { |
| 842 this._physicalSizes[i] = this._physicalItems[i].offsetHeight; | 840 oldPhysicalSize += this._physicalSizes[pidx] || 0; |
| 843 total += this._physicalSizes[i]; | 841 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; |
| 844 this._physicalAverageCount += this._physicalSizes[i] ? 1 : 0; | 842 newPhysicalSize += this._physicalSizes[pidx]; |
| 845 } | 843 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; |
| 844 }, itemSet); |
| 846 | 845 |
| 847 this._physicalSize = total; | 846 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSiz
e; |
| 848 this._viewportSize = this._scroller.offsetHeight; | 847 this._viewportSize = this._scroller.offsetHeight; |
| 849 | 848 |
| 849 // update the average if we measured something |
| 850 if (this._physicalAverageCount !== prevAvgCount) { | 850 if (this._physicalAverageCount !== prevAvgCount) { |
| 851 this._physicalAverage = Math.round( | 851 this._physicalAverage = Math.round( |
| 852 ((prevPhysicalAvg * prevAvgCount) + total) / | 852 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
| 853 this._physicalAverageCount); | 853 this._physicalAverageCount); |
| 854 } | 854 } |
| 855 }, | 855 }, |
| 856 | 856 |
| 857 /** | 857 /** |
| 858 * Updates the position of the physical items. | 858 * Updates the position of the physical items. |
| 859 */ | 859 */ |
| 860 _positionItems: function() { | 860 _positionItems: function() { |
| 861 this._adjustScrollPosition(); | 861 this._adjustScrollPosition(); |
| 862 | 862 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 892 */ | 892 */ |
| 893 _resetScrollPosition: function(pos) { | 893 _resetScrollPosition: function(pos) { |
| 894 if (this._scroller) { | 894 if (this._scroller) { |
| 895 this._scroller.scrollTop = pos; | 895 this._scroller.scrollTop = pos; |
| 896 this._scrollPosition = this._scroller.scrollTop; | 896 this._scrollPosition = this._scroller.scrollTop; |
| 897 } | 897 } |
| 898 }, | 898 }, |
| 899 | 899 |
| 900 /** | 900 /** |
| 901 * Sets the scroll height, that's the height of the content, | 901 * Sets the scroll height, that's the height of the content, |
| 902 * |
| 903 * @param {boolean=} forceUpdate If true, updates the height no matter what. |
| 902 */ | 904 */ |
| 903 _updateScrollerSize: function(forceUpdate) { | 905 _updateScrollerSize: function(forceUpdate) { |
| 904 this._estScrollHeight = (this._physicalBottom + | 906 this._estScrollHeight = (this._physicalBottom + |
| 905 Math.max(this._virtualCount - this._physicalCount - this._virtualStart
Val, 0) * this._physicalAverage); | 907 Math.max(this._virtualCount - this._physicalCount - this._virtualStart
Val, 0) * this._physicalAverage); |
| 906 | 908 |
| 907 forceUpdate = forceUpdate || this._scrollHeight === 0; | 909 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 908 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; | 910 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 909 | 911 |
| 910 // amortize height adjustment, so it won't trigger repaints very often | 912 // amortize height adjustment, so it won't trigger repaints very often |
| 911 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { | 913 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 976 | 978 |
| 977 /** | 979 /** |
| 978 * Reset the physical average and the average count. | 980 * Reset the physical average and the average count. |
| 979 */ | 981 */ |
| 980 _resetAverage: function() { | 982 _resetAverage: function() { |
| 981 this._physicalAverage = 0; | 983 this._physicalAverage = 0; |
| 982 this._physicalAverageCount = 0; | 984 this._physicalAverageCount = 0; |
| 983 }, | 985 }, |
| 984 | 986 |
| 985 /** | 987 /** |
| 986 * A handler for the `resize` event triggered by `IronResizableBehavior` | 988 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` |
| 987 * when the element is resized. | 989 * when the element is resized. |
| 988 */ | 990 */ |
| 989 _resizeHandler: function() { | 991 _resizeHandler: function() { |
| 990 this.debounce('resize', function() { | 992 this.debounce('resize', function() { |
| 991 this._render(); | 993 this._render(); |
| 992 if (this._itemsRendered && this._physicalItems && this._isVisible) { | 994 if (this._itemsRendered && this._physicalItems && this._isVisible) { |
| 993 this._resetAverage(); | 995 this._resetAverage(); |
| 994 this.updateViewportBoundaries(); | 996 this.updateViewportBoundaries(); |
| 995 this.scrollToIndex(this.firstVisibleIndex); | 997 this.scrollToIndex(this.firstVisibleIndex); |
| 996 } | 998 } |
| 997 }); | 999 }); |
| 998 }, | 1000 }, |
| 999 | 1001 |
| 1000 _getModelFromItem: function(item) { | 1002 _getModelFromItem: function(item) { |
| 1001 var key = this._collection.getKey(item); | 1003 var key = this._collection.getKey(item); |
| 1002 var pidx = this._physicalIndexForKey[key]; | 1004 var pidx = this._physicalIndexForKey[key]; |
| 1003 | 1005 |
| 1004 if (pidx !== undefined) { | 1006 if (pidx !== undefined) { |
| 1005 return this._physicalItems[pidx]._templateInstance; | 1007 return this._physicalItems[pidx]._templateInstance; |
| 1006 } | 1008 } |
| 1007 return null; | 1009 return null; |
| 1008 }, | 1010 }, |
| 1009 | 1011 |
| 1010 /** | 1012 /** |
| 1013 * Gets a valid item instance from its index or the object value. |
| 1014 * |
| 1015 * @param {(Object|number)} item The item object or its index |
| 1016 */ |
| 1017 _getNormalizedItem: function(item) { |
| 1018 if (typeof item === 'number') { |
| 1019 item = this.items[item]; |
| 1020 if (!item) { |
| 1021 throw new RangeError('<item> not found'); |
| 1022 } |
| 1023 } else if (this._collection.getKey(item) === undefined) { |
| 1024 throw new TypeError('<item> should be a valid item'); |
| 1025 } |
| 1026 return item; |
| 1027 }, |
| 1028 |
| 1029 /** |
| 1011 * Select the list item at the given index. | 1030 * Select the list item at the given index. |
| 1012 * | 1031 * |
| 1013 * @method selectItem | 1032 * @method selectItem |
| 1014 * @param {(Object|number)} item the item object or its index | 1033 * @param {(Object|number)} item The item object or its index |
| 1015 */ | 1034 */ |
| 1016 selectItem: function(item) { | 1035 selectItem: function(item) { |
| 1017 if (typeof item === 'number') { | 1036 item = this._getNormalizedItem(item); |
| 1018 item = this.items[item]; | |
| 1019 if (!item) { | |
| 1020 throw new RangeError('<item> not found'); | |
| 1021 } | |
| 1022 } else { | |
| 1023 if (this._collection.getKey(item) === undefined) { | |
| 1024 throw new TypeError('<item> should be a valid item'); | |
| 1025 } | |
| 1026 } | |
| 1027 | |
| 1028 var model = this._getModelFromItem(item); | 1037 var model = this._getModelFromItem(item); |
| 1029 | 1038 |
| 1030 if (!this.multiSelection && this.selectedItem) { | 1039 if (!this.multiSelection && this.selectedItem) { |
| 1031 this.deselectItem(this.selectedItem); | 1040 this.deselectItem(this.selectedItem); |
| 1032 } | 1041 } |
| 1033 if (model) { | 1042 if (model) { |
| 1034 model[this.selectedAs] = true; | 1043 model[this.selectedAs] = true; |
| 1035 } | 1044 } |
| 1036 this.$.selector.select(item); | 1045 this.$.selector.select(item); |
| 1037 }, | 1046 }, |
| 1038 | 1047 |
| 1039 /** | 1048 /** |
| 1040 * Deselects the given item list if it is already selected. | 1049 * Deselects the given item list if it is already selected. |
| 1041 * | 1050 * |
| 1051 |
| 1042 * @method deselect | 1052 * @method deselect |
| 1043 * @param {(Object|number)} item the item object or its index | 1053 * @param {(Object|number)} item The item object or its index |
| 1044 */ | 1054 */ |
| 1045 deselectItem: function(item) { | 1055 deselectItem: function(item) { |
| 1046 if (typeof item === 'number') { | 1056 item = this._getNormalizedItem(item); |
| 1047 item = this.items[item]; | |
| 1048 if (!item) { | |
| 1049 throw new RangeError('<item> not found'); | |
| 1050 } | |
| 1051 } else { | |
| 1052 if (this._collection.getKey(item) === undefined) { | |
| 1053 throw new TypeError('<item> should be a valid item'); | |
| 1054 } | |
| 1055 } | |
| 1056 | |
| 1057 var model = this._getModelFromItem(item); | 1057 var model = this._getModelFromItem(item); |
| 1058 | 1058 |
| 1059 if (model) { | 1059 if (model) { |
| 1060 model[this.selectedAs] = false; | 1060 model[this.selectedAs] = false; |
| 1061 } | 1061 } |
| 1062 this.$.selector.deselect(item); | 1062 this.$.selector.deselect(item); |
| 1063 }, | 1063 }, |
| 1064 | 1064 |
| 1065 /** | 1065 /** |
| 1066 * Select or deselect a given item depending on whether the item | 1066 * Select or deselect a given item depending on whether the item |
| 1067 * has already been selected. | 1067 * has already been selected. |
| 1068 * | 1068 * |
| 1069 * @method toggleSelectionForItem | 1069 * @method toggleSelectionForItem |
| 1070 * @param {(Object|number)} item the item object or its index | 1070 * @param {(Object|number)} item The item object or its index |
| 1071 */ | 1071 */ |
| 1072 toggleSelectionForItem: function(item) { | 1072 toggleSelectionForItem: function(item) { |
| 1073 item = typeof item === 'number' ? this.items[item] : item; | 1073 item = this._getNormalizedItem(item); |
| 1074 if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item
)) { | 1074 if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item
)) { |
| 1075 this.deselectItem(item); | 1075 this.deselectItem(item); |
| 1076 } else { | 1076 } else { |
| 1077 this.selectItem(item); | 1077 this.selectItem(item); |
| 1078 } | 1078 } |
| 1079 }, | 1079 }, |
| 1080 | 1080 |
| 1081 /** | 1081 /** |
| 1082 * Clears the current selection state of the list. | 1082 * Clears the current selection state of the list. |
| 1083 * | 1083 * |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1111 } else { | 1111 } else { |
| 1112 this.unlisten(this, 'tap', '_selectionHandler'); | 1112 this.unlisten(this, 'tap', '_selectionHandler'); |
| 1113 this.unlisten(this, 'keypress', '_selectionHandler'); | 1113 this.unlisten(this, 'keypress', '_selectionHandler'); |
| 1114 } | 1114 } |
| 1115 }, | 1115 }, |
| 1116 | 1116 |
| 1117 /** | 1117 /** |
| 1118 * Select an item from an event object. | 1118 * Select an item from an event object. |
| 1119 */ | 1119 */ |
| 1120 _selectionHandler: function(e) { | 1120 _selectionHandler: function(e) { |
| 1121 var ENTER_KEY = 13, model; | 1121 if (e.type !== 'keypress' || e.keyCode === 13) { |
| 1122 if (e.type !== 'keypress' || e.keyCode === ENTER_KEY) { | 1122 var model = this.modelForElement(e.target); |
| 1123 model = this.modelForElement(e.target); | |
| 1124 if (model) { | 1123 if (model) { |
| 1125 this.toggleSelectionForItem(model[this.as]); | 1124 this.toggleSelectionForItem(model[this.as]); |
| 1126 } | 1125 } |
| 1127 } | 1126 } |
| 1128 }, | 1127 }, |
| 1129 | 1128 |
| 1130 _multiSelectionChanged: function(multiSelection) { | 1129 _multiSelectionChanged: function(multiSelection) { |
| 1131 this.clearSelection(); | 1130 this.clearSelection(); |
| 1132 this.$.selector.multi = multiSelection; | 1131 this.$.selector.multi = multiSelection; |
| 1132 }, |
| 1133 |
| 1134 /** |
| 1135 * Updates the size of an item. |
| 1136 * |
| 1137 * @method updateSizeForItem |
| 1138 * @param {(Object|number)} item The item object or its index |
| 1139 */ |
| 1140 updateSizeForItem: function(item) { |
| 1141 item = this._getNormalizedItem(item); |
| 1142 var key = this._collection.getKey(item); |
| 1143 var pidx = this._physicalIndexForKey[key]; |
| 1144 |
| 1145 if (pidx !== undefined) { |
| 1146 this._updateMetrics([pidx]); |
| 1147 this._positionItems(); |
| 1148 } |
| 1133 } | 1149 } |
| 1134 }); | 1150 }); |
| 1135 | 1151 |
| 1136 })(); | 1152 })(); |
| 1137 | |
| OLD | NEW |