| 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 ITEM_WIDTH = 0; |
| 8 var ITEM_HEIGHT = 1; |
| 8 var SECRET_TABINDEX = -100; | 9 var SECRET_TABINDEX = -100; |
| 9 | 10 |
| 10 Polymer({ | 11 Polymer({ |
| 11 | 12 |
| 12 is: 'iron-list', | 13 is: 'iron-list', |
| 13 | 14 |
| 14 properties: { | 15 properties: { |
| 15 | 16 |
| 16 /** | 17 /** |
| 17 * An array containing items determining how many instances of the templat
e | 18 * An array containing items determining how many instances of the templat
e |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 | 230 |
| 230 /** | 231 /** |
| 231 * A Polymer collection for the items. | 232 * A Polymer collection for the items. |
| 232 * @type {?Polymer.Collection} | 233 * @type {?Polymer.Collection} |
| 233 */ | 234 */ |
| 234 _collection: null, | 235 _collection: null, |
| 235 | 236 |
| 236 /** | 237 /** |
| 237 * The max number of pages to render. One page is equivalent to the height o
f the list. | 238 * The max number of pages to render. One page is equivalent to the height o
f the list. |
| 238 */ | 239 */ |
| 239 _maxPages: 3, | 240 _maxPages: 2, |
| 240 | 241 |
| 241 /** | 242 /** |
| 242 * The currently focused physical item. | 243 * The currently focused physical item. |
| 243 */ | 244 */ |
| 244 _focusedItem: null, | 245 _focusedItem: null, |
| 245 | 246 |
| 246 /** | 247 /** |
| 247 * The index of the `_focusedItem`. | 248 * The index of the `_focusedItem`. |
| 248 */ | 249 */ |
| 249 _focusedIndex: -1, | 250 _focusedIndex: -1, |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 384 * This default value assumes that we will at least have the equivalent | 385 * This default value assumes that we will at least have the equivalent |
| 385 * to a viewport of physical items above and below the user's viewport. | 386 * to a viewport of physical items above and below the user's viewport. |
| 386 */ | 387 */ |
| 387 get _optPhysicalSize() { | 388 get _optPhysicalSize() { |
| 388 if (this.grid) { | 389 if (this.grid) { |
| 389 return this._estRowsInView * this._rowHeight * this._maxPages; | 390 return this._estRowsInView * this._rowHeight * this._maxPages; |
| 390 } | 391 } |
| 391 return this._viewportHeight * this._maxPages; | 392 return this._viewportHeight * this._maxPages; |
| 392 }, | 393 }, |
| 393 | 394 |
| 394 get _optPhysicalCount() { | |
| 395 return this._estRowsInView * this._itemsPerRow * this._maxPages; | |
| 396 }, | |
| 397 | |
| 398 /** | 395 /** |
| 399 * True if the current list is visible. | 396 * True if the current list is visible. |
| 400 */ | 397 */ |
| 401 get _isVisible() { | 398 get _isVisible() { |
| 402 return Boolean(this.offsetWidth || this.offsetHeight); | 399 return Boolean(this.offsetWidth || this.offsetHeight); |
| 403 }, | 400 }, |
| 404 | 401 |
| 405 /** | 402 /** |
| 406 * Gets the index of the first visible item in the viewport. | 403 * Gets the index of the first visible item in the viewport. |
| 407 * | 404 * |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 | 464 |
| 468 get _physicalRows() { | 465 get _physicalRows() { |
| 469 return Math.ceil(this._physicalCount / this._itemsPerRow); | 466 return Math.ceil(this._physicalCount / this._itemsPerRow); |
| 470 }, | 467 }, |
| 471 | 468 |
| 472 ready: function() { | 469 ready: function() { |
| 473 this.addEventListener('focus', this._didFocus.bind(this), true); | 470 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 474 }, | 471 }, |
| 475 | 472 |
| 476 attached: function() { | 473 attached: function() { |
| 477 this.updateViewportBoundaries(); | |
| 478 if (this._physicalCount === 0) { | 474 if (this._physicalCount === 0) { |
| 479 this._debounceTemplate(this._render); | 475 this._debounceTemplate(this._render); |
| 480 } | 476 } |
| 481 // `iron-resize` is fired when the list is attached if the event is added | 477 // `iron-resize` is fired when the list is attached if the event is added |
| 482 // before attached causing unnecessary work. | 478 // before attached causing unnecessary work. |
| 483 this.listen(this, 'iron-resize', '_resizeHandler'); | 479 this.listen(this, 'iron-resize', '_resizeHandler'); |
| 484 }, | 480 }, |
| 485 | 481 |
| 486 detached: function() { | 482 detached: function() { |
| 487 this.unlisten(this, 'iron-resize', '_resizeHandler'); | 483 this.unlisten(this, 'iron-resize', '_resizeHandler'); |
| 488 }, | 484 }, |
| 489 | 485 |
| 490 /** | 486 /** |
| 491 * Set the overflow property if this element has its own scrolling region | 487 * Set the overflow property if this element has its own scrolling region |
| 492 */ | 488 */ |
| 493 _setOverflow: function(scrollTarget) { | 489 _setOverflow: function(scrollTarget) { |
| 494 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; | 490 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; |
| 495 this.style.overflow = scrollTarget === this ? 'auto' : ''; | 491 this.style.overflow = scrollTarget === this ? 'auto' : ''; |
| 496 }, | 492 }, |
| 497 | 493 |
| 498 /** | 494 /** |
| 499 * Invoke this method if you dynamically update the viewport's | 495 * Invoke this method if you dynamically update the viewport's |
| 500 * size or CSS padding. | 496 * size or CSS padding. |
| 501 * | 497 * |
| 502 * @method updateViewportBoundaries | 498 * @method updateViewportBoundaries |
| 503 */ | 499 */ |
| 504 updateViewportBoundaries: function() { | 500 updateViewportBoundaries: function() { |
| 505 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : | 501 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : |
| 506 parseInt(window.getComputedStyle(this)['padding-top'], 10); | 502 parseInt(window.getComputedStyle(this)['padding-top'], 10); |
| 503 this._viewportWidth = this.$.items.offsetWidth; |
| 504 this._viewportHeight = this._scrollTargetHeight; |
| 505 this.grid && this._updateGridMetrics(); |
| 506 }, |
| 507 | 507 |
| 508 this._viewportHeight = this._scrollTargetHeight; | 508 /** |
| 509 if (this.grid) { | 509 * Recycles the physical items when needed. |
| 510 this._updateGridMetrics(); | 510 */ |
| 511 _scrollHandler: function() { |
| 512 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; |
| 513 var delta = scrollTop - this._scrollPosition; |
| 514 var isScrollingDown = delta >= 0; |
| 515 // Track the current scroll position. |
| 516 this._scrollPosition = scrollTop; |
| 517 // Clear indexes. |
| 518 this._firstVisibleIndexVal = null; |
| 519 this._lastVisibleIndexVal = null; |
| 520 |
| 521 // Random access. |
| 522 if (Math.abs(delta) > this._physicalSize) { |
| 523 var idxAdjustment = Math.round(delta / this._physicalAverage) * this._i
temsPerRow |
| 524 this._physicalTop = this._physicalTop + delta; |
| 525 this._virtualStart = this._virtualStart + idxAdjustment; |
| 526 this._physicalStart = this._physicalStart + idxAdjustment; |
| 527 this._update(); |
| 528 } else { |
| 529 var reusables = this._getReusables(isScrollingDown); |
| 530 if (isScrollingDown) { |
| 531 this._physicalTop = reusables.physicalTop; |
| 532 this._virtualStart = this._virtualStart + reusables.indexes.length; |
| 533 this._physicalStart = this._physicalStart + reusables.indexes.length; |
| 534 } else { |
| 535 this._virtualStart = this._virtualStart - reusables.indexes.length; |
| 536 this._physicalStart = this._physicalStart - reusables.indexes.length; |
| 537 } |
| 538 if (reusables.indexes.length === 0) { |
| 539 this._increasePoolIfNeeded(); |
| 540 } else { |
| 541 this._update(reusables.indexes, isScrollingDown ? null : reusables.ind
exes); |
| 542 } |
| 511 } | 543 } |
| 512 }, | 544 }, |
| 513 | 545 |
| 514 /** | 546 /** |
| 515 * Update the models, the position of the | 547 * Returns an object that contains the indexes of the physical items |
| 516 * items in the viewport and recycle tiles as needed. | 548 * that might be reused and the physicalTop. |
| 549 * |
| 550 * @param {boolean} fromTop If the potential reusable items are above the sc
rolling region. |
| 517 */ | 551 */ |
| 518 _scrollHandler: function() { | 552 _getReusables: function(fromTop) { |
| 519 // clamp the `scrollTop` value | 553 var ith, lastIth, offsetContent, physicalItemHeight; |
| 520 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; | 554 var idxs = []; |
| 521 var delta = scrollTop - this._scrollPosition; | 555 var protectedOffsetContent = this._hiddenContentSize * this._ratio; |
| 522 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto
m; | 556 var virtualStart = this._virtualStart; |
| 523 var ratio = this._ratio; | 557 var virtualEnd = this._virtualEnd; |
| 524 var recycledTiles = 0; | 558 var physicalCount = this._physicalCount; |
| 525 var hiddenContentSize = this._hiddenContentSize; | 559 var physicalTop = this._physicalTop; |
| 526 var currentRatio = ratio; | 560 var scrollTop = this._scrollTop; |
| 527 var movingUp = []; | 561 var scrollBottom = this._scrollBottom; |
| 528 | 562 |
| 529 // track the last `scrollTop` | 563 if (fromTop) { |
| 530 this._scrollPosition = scrollTop; | 564 ith = this._physicalStart; |
| 531 | 565 lastIth = this._physicalEnd; |
| 532 // clear cached visible indexes | 566 offsetContent = scrollTop - physicalTop; |
| 533 this._firstVisibleIndexVal = null; | 567 } else { |
| 534 this._lastVisibleIndexVal = null; | 568 ith = this._physicalEnd; |
| 535 | 569 lastIth = this._physicalStart; |
| 536 scrollBottom = this._scrollBottom; | 570 offsetContent = this._physicalBottom - scrollBottom; |
| 537 physicalBottom = this._physicalBottom; | |
| 538 | |
| 539 // random access | |
| 540 if (Math.abs(delta) > this._physicalSize) { | |
| 541 this._physicalTop += delta; | |
| 542 recycledTiles = Math.round(delta / this._physicalAverage); | |
| 543 } | 571 } |
| 544 // scroll up | 572 while (true) { |
| 545 else if (delta < 0) { | 573 physicalItemHeight = this._getPhysicalSizeIncrement(ith); |
| 546 var topSpace = scrollTop - this._physicalTop; | 574 offsetContent = offsetContent - physicalItemHeight; |
| 547 var virtualStart = this._virtualStart; | 575 if (idxs.length >= physicalCount || offsetContent <= protectedOffsetCont
ent) { |
| 548 | 576 break; |
| 549 recycledTileSet = []; | |
| 550 | |
| 551 kth = this._physicalEnd; | |
| 552 currentRatio = topSpace / hiddenContentSize; | |
| 553 | |
| 554 // move tiles from bottom to top | |
| 555 while ( | |
| 556 // approximate `currentRatio` to `ratio` | |
| 557 currentRatio < ratio && | |
| 558 // recycle less physical items than the total | |
| 559 recycledTiles < this._physicalCount && | |
| 560 // ensure that these recycled tiles are needed | |
| 561 virtualStart - recycledTiles > 0 && | |
| 562 // ensure that the tile is not visible | |
| 563 physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom | |
| 564 ) { | |
| 565 | |
| 566 tileHeight = this._getPhysicalSizeIncrement(kth); | |
| 567 currentRatio += tileHeight / hiddenContentSize; | |
| 568 physicalBottom -= tileHeight; | |
| 569 recycledTileSet.push(kth); | |
| 570 recycledTiles++; | |
| 571 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; | |
| 572 } | 577 } |
| 573 | 578 if (fromTop) { |
| 574 movingUp = recycledTileSet; | 579 // Check that index is within the valid range. |
| 575 recycledTiles = -recycledTiles; | 580 if (virtualEnd + idxs.length + 1 >= this._virtualCount) { |
| 576 } | 581 break; |
| 577 // scroll down | 582 } |
| 578 else if (delta > 0) { | 583 // Check that the index is not visible. |
| 579 var bottomSpace = physicalBottom - scrollBottom; | 584 if (physicalTop + physicalItemHeight >= scrollTop) { |
| 580 var virtualEnd = this._virtualEnd; | 585 break; |
| 581 var lastVirtualItemIndex = this._virtualCount-1; | 586 } |
| 582 | 587 idxs.push(ith); |
| 583 recycledTileSet = []; | 588 physicalTop = physicalTop + physicalItemHeight; |
| 584 | 589 ith = (ith + 1) % physicalCount; |
| 585 kth = this._physicalStart; | 590 } else { |
| 586 currentRatio = bottomSpace / hiddenContentSize; | 591 // Check that index is within the valid range. |
| 587 | 592 if (virtualStart - idxs.length <= 0) { |
| 588 // move tiles from top to bottom | 593 break; |
| 589 while ( | 594 } |
| 590 // approximate `currentRatio` to `ratio` | 595 // Check that the index is not visible. |
| 591 currentRatio < ratio && | 596 if (physicalTop + this._physicalSize - physicalItemHeight <= scrollBot
tom) { |
| 592 // recycle less physical items than the total | 597 break; |
| 593 recycledTiles < this._physicalCount && | 598 } |
| 594 // ensure that these recycled tiles are needed | 599 idxs.push(ith); |
| 595 virtualEnd + recycledTiles < lastVirtualItemIndex && | 600 physicalTop = physicalTop - physicalItemHeight; |
| 596 // ensure that the tile is not visible | 601 ith = (ith === 0) ? physicalCount - 1 : ith - 1; |
| 597 this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop | |
| 598 ) { | |
| 599 | |
| 600 tileHeight = this._getPhysicalSizeIncrement(kth); | |
| 601 currentRatio += tileHeight / hiddenContentSize; | |
| 602 | |
| 603 this._physicalTop += tileHeight; | |
| 604 recycledTileSet.push(kth); | |
| 605 recycledTiles++; | |
| 606 kth = (kth + 1) % this._physicalCount; | |
| 607 } | 602 } |
| 608 } | 603 } |
| 609 | 604 return { indexes: idxs, physicalTop: physicalTop }; |
| 610 if (recycledTiles === 0) { | |
| 611 // Try to increase the pool if the list's client isn't filled up with ph
ysical items | |
| 612 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { | |
| 613 this._increasePoolIfNeeded(); | |
| 614 } | |
| 615 } else { | |
| 616 this._virtualStart = this._virtualStart + recycledTiles; | |
| 617 this._physicalStart = this._physicalStart + recycledTiles; | |
| 618 this._update(recycledTileSet, movingUp); | |
| 619 } | |
| 620 }, | 605 }, |
| 621 | 606 |
| 622 /** | 607 /** |
| 623 * Update the list of items, starting from the `_virtualStart` item. | 608 * Update the list of items, starting from the `_virtualStart` item. |
| 624 * @param {!Array<number>=} itemSet | 609 * @param {!Array<number>=} itemSet |
| 625 * @param {!Array<number>=} movingUp | 610 * @param {!Array<number>=} movingUp |
| 626 */ | 611 */ |
| 627 _update: function(itemSet, movingUp) { | 612 _update: function(itemSet, movingUp) { |
| 613 if (itemSet && itemSet.length === 0) { |
| 614 return; |
| 615 } |
| 628 this._manageFocus(); | 616 this._manageFocus(); |
| 629 this._assignModels(itemSet); | 617 this._assignModels(itemSet); |
| 630 this._updateMetrics(itemSet); | 618 this._updateMetrics(itemSet); |
| 631 // Adjust offset after measuring. | 619 // Adjust offset after measuring. |
| 632 if (movingUp) { | 620 if (movingUp) { |
| 633 while (movingUp.length) { | 621 while (movingUp.length) { |
| 634 var idx = movingUp.pop(); | 622 var idx = movingUp.pop(); |
| 635 this._physicalTop -= this._getPhysicalSizeIncrement(idx); | 623 this._physicalTop -= this._getPhysicalSizeIncrement(idx); |
| 636 } | 624 } |
| 637 } | 625 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 729 if (this._physicalStart > this._physicalEnd && | 717 if (this._physicalStart > this._physicalEnd && |
| 730 this._isIndexRendered(this._focusedIndex) && | 718 this._isIndexRendered(this._focusedIndex) && |
| 731 this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { | 719 this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { |
| 732 this._physicalStart = this._physicalStart + delta; | 720 this._physicalStart = this._physicalStart + delta; |
| 733 } | 721 } |
| 734 this._update(); | 722 this._update(); |
| 735 this._templateCost = (window.performance.now() - ts) / delta; | 723 this._templateCost = (window.performance.now() - ts) / delta; |
| 736 }, | 724 }, |
| 737 | 725 |
| 738 /** | 726 /** |
| 739 * Render a new list of items. | 727 * Renders the a new list. |
| 740 */ | 728 */ |
| 741 _render: function() { | 729 _render: function() { |
| 742 if (this.isAttached && this._isVisible) { | 730 if (this.isAttached && this._isVisible) { |
| 743 if (this._physicalCount === 0) { | 731 if (this._physicalCount === 0) { |
| 732 this.updateViewportBoundaries(); |
| 744 this._increasePool(DEFAULT_PHYSICAL_COUNT); | 733 this._increasePool(DEFAULT_PHYSICAL_COUNT); |
| 745 } else { | 734 } else { |
| 735 // Try to recycle nodes |
| 736 var reusables = this._getReusables(true); |
| 737 this._physicalTop = reusables.physicalTop; |
| 738 this._virtualStart = this._virtualStart + reusables.indexes.length; |
| 739 this._physicalStart = this._physicalStart + reusables.indexes.length; |
| 740 this._update(reusables.indexes); |
| 746 this._update(); | 741 this._update(); |
| 747 } | 742 } |
| 748 } | 743 } |
| 749 }, | 744 }, |
| 750 | 745 |
| 751 /** | 746 /** |
| 752 * Templetizes the user template. | 747 * Templetizes the user template. |
| 753 */ | 748 */ |
| 754 _ensureTemplatized: function() { | 749 _ensureTemplatized: function() { |
| 755 if (!this.ctor) { | 750 if (!this.ctor) { |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 871 this._physicalItems = this._physicalItems || []; | 866 this._physicalItems = this._physicalItems || []; |
| 872 this._physicalSizes = this._physicalSizes || []; | 867 this._physicalSizes = this._physicalSizes || []; |
| 873 this._physicalStart = 0; | 868 this._physicalStart = 0; |
| 874 this._resetScrollPosition(0); | 869 this._resetScrollPosition(0); |
| 875 this._removeFocusedItem(); | 870 this._removeFocusedItem(); |
| 876 this._debounceTemplate(this._render); | 871 this._debounceTemplate(this._render); |
| 877 | 872 |
| 878 } else if (change.path === 'items.splices') { | 873 } else if (change.path === 'items.splices') { |
| 879 this._adjustVirtualIndex(change.value.indexSplices); | 874 this._adjustVirtualIndex(change.value.indexSplices); |
| 880 this._virtualCount = this.items ? this.items.length : 0; | 875 this._virtualCount = this.items ? this.items.length : 0; |
| 876 |
| 881 this._debounceTemplate(this._render); | 877 this._debounceTemplate(this._render); |
| 882 | 878 |
| 883 } else { | 879 } else { |
| 884 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); | 880 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); |
| 885 } | 881 } |
| 886 }, | 882 }, |
| 887 | 883 |
| 888 /** | 884 /** |
| 889 * @param {!Array<!PolymerSplice>} splices | 885 * @param {!Array<!PolymerSplice>} splices |
| 890 */ | 886 */ |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 965 | 961 |
| 966 /** | 962 /** |
| 967 * Assigns the data models to a given set of items. | 963 * Assigns the data models to a given set of items. |
| 968 * @param {!Array<number>=} itemSet | 964 * @param {!Array<number>=} itemSet |
| 969 */ | 965 */ |
| 970 _assignModels: function(itemSet) { | 966 _assignModels: function(itemSet) { |
| 971 this._iterateItems(function(pidx, vidx) { | 967 this._iterateItems(function(pidx, vidx) { |
| 972 var el = this._physicalItems[pidx]; | 968 var el = this._physicalItems[pidx]; |
| 973 var inst = el._templateInstance; | 969 var inst = el._templateInstance; |
| 974 var item = this.items && this.items[vidx]; | 970 var item = this.items && this.items[vidx]; |
| 975 | |
| 976 if (item != null) { | 971 if (item != null) { |
| 977 inst[this.as] = item; | 972 inst[this.as] = item; |
| 978 inst.__key__ = this._collection.getKey(item); | 973 inst.__key__ = this._collection.getKey(item); |
| 979 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); | 974 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); |
| 980 inst[this.indexAs] = vidx; | 975 inst[this.indexAs] = vidx; |
| 981 inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; | 976 inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; |
| 982 this._physicalIndexForKey[inst.__key__] = pidx; | 977 this._physicalIndexForKey[inst.__key__] = pidx; |
| 983 el.removeAttribute('hidden'); | 978 el.removeAttribute('hidden'); |
| 984 } else { | 979 } else { |
| 985 inst.__key__ = null; | 980 inst.__key__ = null; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 997 // Make sure we distributed all the physical items | 992 // Make sure we distributed all the physical items |
| 998 // so we can measure them. | 993 // so we can measure them. |
| 999 Polymer.dom.flush(); | 994 Polymer.dom.flush(); |
| 1000 | 995 |
| 1001 var newPhysicalSize = 0; | 996 var newPhysicalSize = 0; |
| 1002 var oldPhysicalSize = 0; | 997 var oldPhysicalSize = 0; |
| 1003 var prevAvgCount = this._physicalAverageCount; | 998 var prevAvgCount = this._physicalAverageCount; |
| 1004 var prevPhysicalAvg = this._physicalAverage; | 999 var prevPhysicalAvg = this._physicalAverage; |
| 1005 | 1000 |
| 1006 this._iterateItems(function(pidx, vidx) { | 1001 this._iterateItems(function(pidx, vidx) { |
| 1007 | |
| 1008 oldPhysicalSize += this._physicalSizes[pidx] || 0; | 1002 oldPhysicalSize += this._physicalSizes[pidx] || 0; |
| 1009 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; | 1003 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; |
| 1010 newPhysicalSize += this._physicalSizes[pidx]; | 1004 newPhysicalSize += this._physicalSizes[pidx]; |
| 1011 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; | 1005 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; |
| 1012 | |
| 1013 }, itemSet); | 1006 }, itemSet); |
| 1014 | 1007 |
| 1015 this._viewportHeight = this._scrollTargetHeight; | |
| 1016 if (this.grid) { | 1008 if (this.grid) { |
| 1017 this._updateGridMetrics(); | 1009 this._updateGridMetrics(); |
| 1018 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow)
* this._rowHeight; | 1010 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow)
* this._rowHeight; |
| 1019 } else { | 1011 } else { |
| 1020 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS
ize; | 1012 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS
ize; |
| 1021 } | 1013 } |
| 1022 | |
| 1023 // Update the average if it measured something. | 1014 // Update the average if it measured something. |
| 1024 if (this._physicalAverageCount !== prevAvgCount) { | 1015 if (this._physicalAverageCount !== prevAvgCount) { |
| 1025 this._physicalAverage = Math.round( | 1016 this._physicalAverage = Math.round( |
| 1026 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / | 1017 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
| 1027 this._physicalAverageCount); | 1018 this._physicalAverageCount); |
| 1028 } | 1019 } |
| 1029 }, | 1020 }, |
| 1030 | 1021 |
| 1031 _updateGridMetrics: function() { | 1022 _updateGridMetrics: function() { |
| 1032 this._viewportWidth = this.$.items.offsetWidth; | 1023 this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoun
dingClientRect().width : 200; |
| 1033 // Set item width to the value of the _physicalItems offsetWidth | 1024 this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetH
eight : 200; |
| 1034 this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].getBoun
dingClientRect().width : DEFAULT_GRID_SIZE; | |
| 1035 // Set row height to the value of the _physicalItems offsetHeight | |
| 1036 this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetH
eight : DEFAULT_GRID_SIZE; | |
| 1037 // If in grid mode compute how many items with exist in each row | |
| 1038 this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / thi
s._itemWidth) : this._itemsPerRow; | 1025 this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / thi
s._itemWidth) : this._itemsPerRow; |
| 1039 }, | 1026 }, |
| 1040 | 1027 |
| 1041 /** | 1028 /** |
| 1042 * Updates the position of the physical items. | 1029 * Updates the position of the physical items. |
| 1043 */ | 1030 */ |
| 1044 _positionItems: function() { | 1031 _positionItems: function() { |
| 1045 this._adjustScrollPosition(); | 1032 this._adjustScrollPosition(); |
| 1046 | 1033 |
| 1047 var y = this._physicalTop; | 1034 var y = this._physicalTop; |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1212 // iOS fires the resize event when the address bar slides up | 1199 // iOS fires the resize event when the address bar slides up |
| 1213 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100
) { | 1200 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100
) { |
| 1214 return; | 1201 return; |
| 1215 } | 1202 } |
| 1216 // In Desktop Safari 9.0.3, if the scroll bars are always shown, | 1203 // In Desktop Safari 9.0.3, if the scroll bars are always shown, |
| 1217 // changing the scroll position from a resize handler would result in | 1204 // changing the scroll position from a resize handler would result in |
| 1218 // the scroll position being reset. Waiting 1ms fixes the issue. | 1205 // the scroll position being reset. Waiting 1ms fixes the issue. |
| 1219 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { | 1206 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { |
| 1220 this.updateViewportBoundaries(); | 1207 this.updateViewportBoundaries(); |
| 1221 this._render(); | 1208 this._render(); |
| 1222 | 1209 if (this._isVisible) { |
| 1223 if (this._physicalCount > 0 && this._isVisible) { | 1210 this.toggleScrollListener(true); |
| 1224 this._resetAverage(); | 1211 if (this._physicalCount > 0) { |
| 1225 this.scrollToIndex(this.firstVisibleIndex); | 1212 this._resetAverage(); |
| 1213 this.scrollToIndex(this.firstVisibleIndex); |
| 1214 } |
| 1215 } else { |
| 1216 this.toggleScrollListener(false); |
| 1226 } | 1217 } |
| 1227 }.bind(this), 1)); | 1218 }.bind(this), 1)); |
| 1228 }, | 1219 }, |
| 1229 | 1220 |
| 1230 _getModelFromItem: function(item) { | 1221 _getModelFromItem: function(item) { |
| 1231 var key = this._collection.getKey(item); | 1222 var key = this._collection.getKey(item); |
| 1232 var pidx = this._physicalIndexForKey[key]; | 1223 var pidx = this._physicalIndexForKey[key]; |
| 1233 | 1224 |
| 1234 if (pidx != null) { | 1225 if (pidx != null) { |
| 1235 return this._physicalItems[pidx]._templateInstance; | 1226 return this._physicalItems[pidx]._templateInstance; |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1358 if (target.localName === 'input' || | 1349 if (target.localName === 'input' || |
| 1359 target.localName === 'button' || | 1350 target.localName === 'button' || |
| 1360 target.localName === 'select') { | 1351 target.localName === 'select') { |
| 1361 return; | 1352 return; |
| 1362 } | 1353 } |
| 1363 // Set a temporary tabindex | 1354 // Set a temporary tabindex |
| 1364 modelTabIndex = model.tabIndex; | 1355 modelTabIndex = model.tabIndex; |
| 1365 model.tabIndex = SECRET_TABINDEX; | 1356 model.tabIndex = SECRET_TABINDEX; |
| 1366 activeElTabIndex = activeEl ? activeEl.tabIndex : -1; | 1357 activeElTabIndex = activeEl ? activeEl.tabIndex : -1; |
| 1367 model.tabIndex = modelTabIndex; | 1358 model.tabIndex = modelTabIndex; |
| 1368 | |
| 1369 // Only select the item if the tap wasn't on a focusable child | 1359 // Only select the item if the tap wasn't on a focusable child |
| 1370 // or the element bound to `tabIndex` | 1360 // or the element bound to `tabIndex` |
| 1371 if (activeEl && physicalItem !== activeEl && physicalItem.contains(activeE
l) && activeElTabIndex !== SECRET_TABINDEX) { | 1361 if (activeEl && physicalItem !== activeEl && physicalItem.contains(activeE
l) && activeElTabIndex !== SECRET_TABINDEX) { |
| 1372 return; | 1362 return; |
| 1373 } | 1363 } |
| 1374 this.toggleSelectionForItem(model[this.as]); | 1364 this.toggleSelectionForItem(model[this.as]); |
| 1375 }, | 1365 }, |
| 1376 | 1366 |
| 1377 _multiSelectionChanged: function(multiSelection) { | 1367 _multiSelectionChanged: function(multiSelection) { |
| 1378 this.clearSelection(); | 1368 this.clearSelection(); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1439 } | 1429 } |
| 1440 this._restoreFocusedItem(); | 1430 this._restoreFocusedItem(); |
| 1441 // scroll to index to make sure it's rendered | 1431 // scroll to index to make sure it's rendered |
| 1442 if (!this._isIndexRendered(idx)) { | 1432 if (!this._isIndexRendered(idx)) { |
| 1443 this.scrollToIndex(idx); | 1433 this.scrollToIndex(idx); |
| 1444 } | 1434 } |
| 1445 | 1435 |
| 1446 var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)]; | 1436 var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)]; |
| 1447 var model = physicalItem._templateInstance; | 1437 var model = physicalItem._templateInstance; |
| 1448 var focusable; | 1438 var focusable; |
| 1449 | |
| 1450 // set a secret tab index | 1439 // set a secret tab index |
| 1451 model.tabIndex = SECRET_TABINDEX; | 1440 model.tabIndex = SECRET_TABINDEX; |
| 1452 // check if focusable element is the physical item | 1441 // check if focusable element is the physical item |
| 1453 if (physicalItem.tabIndex === SECRET_TABINDEX) { | 1442 if (physicalItem.tabIndex === SECRET_TABINDEX) { |
| 1454 focusable = physicalItem; | 1443 focusable = physicalItem; |
| 1455 } | 1444 } |
| 1456 // search for the element which tabindex is bound to the secret tab index | 1445 // search for the element which tabindex is bound to the secret tab index |
| 1457 if (!focusable) { | 1446 if (!focusable) { |
| 1458 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET_TABINDEX + '"]'); | 1447 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET_TABINDEX + '"]'); |
| 1459 } | 1448 } |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1561 this._focusPhysicalItem(this._focusedIndex + 1); | 1550 this._focusPhysicalItem(this._focusedIndex + 1); |
| 1562 }, | 1551 }, |
| 1563 | 1552 |
| 1564 _didEnter: function(e) { | 1553 _didEnter: function(e) { |
| 1565 this._focusPhysicalItem(this._focusedIndex); | 1554 this._focusPhysicalItem(this._focusedIndex); |
| 1566 this._selectionHandler(e.detail.keyboardEvent); | 1555 this._selectionHandler(e.detail.keyboardEvent); |
| 1567 } | 1556 } |
| 1568 }); | 1557 }); |
| 1569 | 1558 |
| 1570 })(); | 1559 })(); |
| OLD | NEW |