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 |