Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js

Issue 2324293002: Roll performance-oriented iron-list changes (Closed)
Patch Set: test fix courtesy of michaelpg@ Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 var SECRET_TABINDEX = -100; 8 var SECRET_TABINDEX = -100;
9 9
10 Polymer({ 10 Polymer({
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 */ 227 */
228 _lastVisibleIndexVal: null, 228 _lastVisibleIndexVal: null,
229 229
230 /** 230 /**
231 * A Polymer collection for the items. 231 * A Polymer collection for the items.
232 * @type {?Polymer.Collection} 232 * @type {?Polymer.Collection}
233 */ 233 */
234 _collection: null, 234 _collection: null,
235 235
236 /** 236 /**
237 * True if the current item list was rendered for the first time
238 * after attached.
239 */
240 _itemsRendered: false,
241
242 /**
243 * The page that is currently rendered.
244 */
245 _lastPage: null,
246
247 /**
248 * The max number of pages to render. One page is equivalent to the height o f the list. 237 * The max number of pages to render. One page is equivalent to the height o f the list.
249 */ 238 */
250 _maxPages: 3, 239 _maxPages: 3,
251 240
252 /** 241 /**
253 * The currently focused physical item. 242 * The currently focused physical item.
254 */ 243 */
255 _focusedItem: null, 244 _focusedItem: null,
256 245
257 /** 246 /**
(...skipping 22 matching lines...) Expand all
280 * The width of each grid item 269 * The width of each grid item
281 */ 270 */
282 _itemWidth: 0, 271 _itemWidth: 0,
283 272
284 /** 273 /**
285 * The height of the row in grid layout. 274 * The height of the row in grid layout.
286 */ 275 */
287 _rowHeight: 0, 276 _rowHeight: 0,
288 277
289 /** 278 /**
279 * The cost of stamping a template in ms.
280 */
281 _templateCost: 0,
282
283 /**
290 * The bottom of the physical content. 284 * The bottom of the physical content.
291 */ 285 */
292 get _physicalBottom() { 286 get _physicalBottom() {
293 return this._physicalTop + this._physicalSize; 287 return this._physicalTop + this._physicalSize;
294 }, 288 },
295 289
296 /** 290 /**
297 * The bottom of the scroll. 291 * The bottom of the scroll.
298 */ 292 */
299 get _scrollBottom() { 293 get _scrollBottom() {
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 }, 392 },
399 393
400 get _optPhysicalCount() { 394 get _optPhysicalCount() {
401 return this._estRowsInView * this._itemsPerRow * this._maxPages; 395 return this._estRowsInView * this._itemsPerRow * this._maxPages;
402 }, 396 },
403 397
404 /** 398 /**
405 * True if the current list is visible. 399 * True if the current list is visible.
406 */ 400 */
407 get _isVisible() { 401 get _isVisible() {
408 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this. scrollTarget.offsetHeight); 402 return Boolean(this.offsetWidth || this.offsetHeight);
409 }, 403 },
410 404
411 /** 405 /**
412 * Gets the index of the first visible item in the viewport. 406 * Gets the index of the first visible item in the viewport.
413 * 407 *
414 * @type {number} 408 * @type {number}
415 */ 409 */
416 get firstVisibleIndex() { 410 get firstVisibleIndex() {
417 if (this._firstVisibleIndexVal === null) { 411 if (this._firstVisibleIndexVal === null) {
418 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin gTop); 412 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin gTop);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 physicalOffset += this._getPhysicalSizeIncrement(pidx); 449 physicalOffset += this._getPhysicalSizeIncrement(pidx);
456 }); 450 });
457 } 451 }
458 } 452 }
459 return this._lastVisibleIndexVal; 453 return this._lastVisibleIndexVal;
460 }, 454 },
461 455
462 get _defaultScrollTarget() { 456 get _defaultScrollTarget() {
463 return this; 457 return this;
464 }, 458 },
459
465 get _virtualRowCount() { 460 get _virtualRowCount() {
466 return Math.ceil(this._virtualCount / this._itemsPerRow); 461 return Math.ceil(this._virtualCount / this._itemsPerRow);
467 }, 462 },
468 463
469 get _estRowsInView() { 464 get _estRowsInView() {
470 return Math.ceil(this._viewportHeight / this._rowHeight); 465 return Math.ceil(this._viewportHeight / this._rowHeight);
471 }, 466 },
472 467
473 get _physicalRows() { 468 get _physicalRows() {
474 return Math.ceil(this._physicalCount / this._itemsPerRow); 469 return Math.ceil(this._physicalCount / this._itemsPerRow);
475 }, 470 },
476 471
477 ready: function() { 472 ready: function() {
478 this.addEventListener('focus', this._didFocus.bind(this), true); 473 this.addEventListener('focus', this._didFocus.bind(this), true);
479 }, 474 },
480 475
481 attached: function() { 476 attached: function() {
482 this.updateViewportBoundaries(); 477 this.updateViewportBoundaries();
483 this._render(); 478 if (this._physicalCount === 0) {
479 this._debounceTemplate(this._render);
480 }
484 // `iron-resize` is fired when the list is attached if the event is added 481 // `iron-resize` is fired when the list is attached if the event is added
485 // before attached causing unnecessary work. 482 // before attached causing unnecessary work.
486 this.listen(this, 'iron-resize', '_resizeHandler'); 483 this.listen(this, 'iron-resize', '_resizeHandler');
487 }, 484 },
488 485
489 detached: function() { 486 detached: function() {
490 this._itemsRendered = false;
491 this.unlisten(this, 'iron-resize', '_resizeHandler'); 487 this.unlisten(this, 'iron-resize', '_resizeHandler');
492 }, 488 },
493 489
494 /** 490 /**
495 * Set the overflow property if this element has its own scrolling region 491 * Set the overflow property if this element has its own scrolling region
496 */ 492 */
497 _setOverflow: function(scrollTarget) { 493 _setOverflow: function(scrollTarget) {
498 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; 494 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
499 this.style.overflow = scrollTarget === this ? 'auto' : ''; 495 this.style.overflow = scrollTarget === this ? 'auto' : '';
500 }, 496 },
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 currentRatio += tileHeight / hiddenContentSize; 601 currentRatio += tileHeight / hiddenContentSize;
606 602
607 this._physicalTop += tileHeight; 603 this._physicalTop += tileHeight;
608 recycledTileSet.push(kth); 604 recycledTileSet.push(kth);
609 recycledTiles++; 605 recycledTiles++;
610 kth = (kth + 1) % this._physicalCount; 606 kth = (kth + 1) % this._physicalCount;
611 } 607 }
612 } 608 }
613 609
614 if (recycledTiles === 0) { 610 if (recycledTiles === 0) {
615 // Try to increase the pool if the list's client height isn't filled up with physical items 611 // Try to increase the pool if the list's client isn't filled up with ph ysical items
616 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { 612 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) {
617 this._increasePoolIfNeeded(); 613 this._increasePoolIfNeeded();
618 } 614 }
619 } else { 615 } else {
620 this._virtualStart = this._virtualStart + recycledTiles; 616 this._virtualStart = this._virtualStart + recycledTiles;
621 this._physicalStart = this._physicalStart + recycledTiles; 617 this._physicalStart = this._physicalStart + recycledTiles;
622 this._update(recycledTileSet, movingUp); 618 this._update(recycledTileSet, movingUp);
623 } 619 }
624 }, 620 },
625 621
626 /** 622 /**
627 * Update the list of items, starting from the `_virtualStart` item. 623 * Update the list of items, starting from the `_virtualStart` item.
628 * @param {!Array<number>=} itemSet 624 * @param {!Array<number>=} itemSet
629 * @param {!Array<number>=} movingUp 625 * @param {!Array<number>=} movingUp
630 */ 626 */
631 _update: function(itemSet, movingUp) { 627 _update: function(itemSet, movingUp) {
632 // manage focus
633 this._manageFocus(); 628 this._manageFocus();
634 // update models
635 this._assignModels(itemSet); 629 this._assignModels(itemSet);
636 // measure heights
637 this._updateMetrics(itemSet); 630 this._updateMetrics(itemSet);
638 // adjust offset after measuring 631 // Adjust offset after measuring.
639 if (movingUp) { 632 if (movingUp) {
640 while (movingUp.length) { 633 while (movingUp.length) {
641 var idx = movingUp.pop(); 634 var idx = movingUp.pop();
642 this._physicalTop -= this._getPhysicalSizeIncrement(idx); 635 this._physicalTop -= this._getPhysicalSizeIncrement(idx);
643 } 636 }
644 } 637 }
645 // update the position of the items
646 this._positionItems(); 638 this._positionItems();
647 // set the scroller size
648 this._updateScrollerSize(); 639 this._updateScrollerSize();
649 // increase the pool of physical items
650 this._increasePoolIfNeeded(); 640 this._increasePoolIfNeeded();
651 }, 641 },
652 642
653 /** 643 /**
654 * Creates a pool of DOM elements and attaches them to the local dom. 644 * Creates a pool of DOM elements and attaches them to the local dom.
645 *
646 * @param {number} size Size of the pool
655 */ 647 */
656 _createPool: function(size) { 648 _createPool: function(size) {
657 var physicalItems = new Array(size); 649 var physicalItems = new Array(size);
658 650
659 this._ensureTemplatized(); 651 this._ensureTemplatized();
660 652
661 for (var i = 0; i < size; i++) { 653 for (var i = 0; i < size; i++) {
662 var inst = this.stamp(null); 654 var inst = this.stamp(null);
663 // First element child is item; Safari doesn't support children[0] 655 // First element child is item; Safari doesn't support children[0]
664 // on a doc fragment 656 // on a doc fragment.
665 physicalItems[i] = inst.root.querySelector('*'); 657 physicalItems[i] = inst.root.querySelector('*');
666 Polymer.dom(this).appendChild(inst.root); 658 Polymer.dom(this).appendChild(inst.root);
667 } 659 }
668 return physicalItems; 660 return physicalItems;
669 }, 661 },
670 662
671 /** 663 /**
672 * Increases the pool of physical items only if needed. 664 * Increases the pool of physical items only if needed.
673 * 665 *
674 * @return {boolean} True if the pool was increased. 666 * @return {boolean} True if the pool was increased.
675 */ 667 */
676 _increasePoolIfNeeded: function() { 668 _increasePoolIfNeeded: function() {
677 // Base case 1: the list has no height. 669 // Base case 1: the list has no height.
678 if (this._viewportHeight === 0) { 670 if (this._viewportHeight === 0) {
679 return false; 671 return false;
680 } 672 }
681 // Base case 2: If the physical size is optimal and the list's client heig ht is full 673 var self = this;
674 var isClientFull = this._physicalBottom >= this._scrollBottom &&
675 this._physicalTop <= this._scrollPosition;
676
677 // Base case 2: if the physical size is optimal and the list's client heig ht is full
682 // with physical items, don't increase the pool. 678 // with physical items, don't increase the pool.
683 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi s._physicalTop <= this._scrollPosition; 679 if (this._physicalSize >= this._optPhysicalSize && isClientFull) {
684 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) {
685 return false; 680 return false;
686 } 681 }
687 // this value should range between [0 <= `currentPage` <= `_maxPages`] 682 var maxPoolSize = Math.round(this._physicalCount * 0.5);
688 var currentPage = Math.floor(this._physicalSize / this._viewportHeight); 683 // Increase the pool synchronously until the client is filled.
684 if (!isClientFull) {
685 this._debounceTemplate(this._increasePool.bind(this, maxPoolSize));
686 return true;
687 }
688 this._yield(function() {
689 self._increasePool(Math.min(maxPoolSize, Math.max(1, Math.round(50 / sel f._templateCost))));
690 });
691 return true;
692 },
689 693
690 if (currentPage === 0) { 694 _yield: function(cb) {
691 // fill the first page 695 var g = window;
692 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph ysicalCount * 0.5))); 696 var handle = g.requestIdleCallback ? g.requestIdleCallback(cb) : g.setTime out(cb, 16);
693 } else if (this._lastPage !== currentPage && isClientHeightFull) { 697 // Polymer/issues/3895
694 // paint the page and defer the next increase 698 Polymer.dom.addDebouncer(/** @type {!Polymer.Debouncer} */({
695 // wait 16ms which is rough enough to get paint cycle. 699 complete: function() {
696 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa sePool.bind(this, this._itemsPerRow), 16)); 700 g.cancelIdleCallback ? g.cancelIdleCallback(handle) : g.clearTimeout(h andle);
697 } else { 701 cb();
698 // fill the rest of the pages 702 }
699 this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow)) ; 703 }));
700 }
701
702 this._lastPage = currentPage;
703
704 return true;
705 }, 704 },
706 705
707 /** 706 /**
708 * Increases the pool size. 707 * Increases the pool size.
709 */ 708 */
710 _increasePool: function(missingItems) { 709 _increasePool: function(missingItems) {
711 var nextPhysicalCount = Math.min( 710 var nextPhysicalCount = Math.min(
712 this._physicalCount + missingItems, 711 this._physicalCount + missingItems,
713 this._virtualCount - this._virtualStart, 712 this._virtualCount - this._virtualStart,
714 Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT) 713 Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT)
715 ); 714 );
716 var prevPhysicalCount = this._physicalCount; 715 var prevPhysicalCount = this._physicalCount;
717 var delta = nextPhysicalCount - prevPhysicalCount; 716 var delta = nextPhysicalCount - prevPhysicalCount;
717 var ts = window.performance.now();
718 718
719 if (delta <= 0) { 719 if (delta <= 0) {
720 return; 720 return;
721 } 721 }
722 722 // Concat arrays in place.
723 [].push.apply(this._physicalItems, this._createPool(delta)); 723 [].push.apply(this._physicalItems, this._createPool(delta));
724 [].push.apply(this._physicalSizes, new Array(delta)); 724 [].push.apply(this._physicalSizes, new Array(delta));
725
726 this._physicalCount = prevPhysicalCount + delta; 725 this._physicalCount = prevPhysicalCount + delta;
727 726 // Update the physical start if it needs to preserve the model of the focu sed item.
728 // update the physical start if we need to preserve the model of the focus ed item.
729 // In this situation, the focused item is currently rendered and its model would 727 // In this situation, the focused item is currently rendered and its model would
730 // have changed after increasing the pool if the physical start remained u nchanged. 728 // have changed after increasing the pool if the physical start remained u nchanged.
731 if (this._physicalStart > this._physicalEnd && 729 if (this._physicalStart > this._physicalEnd &&
732 this._isIndexRendered(this._focusedIndex) && 730 this._isIndexRendered(this._focusedIndex) &&
733 this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { 731 this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) {
734 this._physicalStart = this._physicalStart + delta; 732 this._physicalStart = this._physicalStart + delta;
735 } 733 }
736 this._update(); 734 this._update();
735 this._templateCost = (window.performance.now() - ts) / delta;
737 }, 736 },
738 737
739 /** 738 /**
740 * Render a new list of items. This method does exactly the same as `update` , 739 * Render a new list of items.
741 * but it also ensures that only one `update` cycle is created.
742 */ 740 */
743 _render: function() { 741 _render: function() {
744 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; 742 if (this.isAttached && this._isVisible) {
745 743 if (this._physicalCount === 0) {
746 if (this.isAttached && !this._itemsRendered && this._isVisible && requires Update) { 744 this._increasePool(DEFAULT_PHYSICAL_COUNT);
747 this._lastPage = 0; 745 } else {
748 this._update(); 746 this._update();
749 this._itemsRendered = true; 747 }
750 } 748 }
751 }, 749 },
752 750
753 /** 751 /**
754 * Templetizes the user template. 752 * Templetizes the user template.
755 */ 753 */
756 _ensureTemplatized: function() { 754 _ensureTemplatized: function() {
757 if (!this.ctor) { 755 if (!this.ctor) {
758 // Template instance props that should be excluded from forwarding 756 // Template instance props that should be excluded from forwarding
759 var props = {}; 757 var props = {};
760 props.__key__ = true; 758 props.__key__ = true;
761 props[this.as] = true; 759 props[this.as] = true;
762 props[this.indexAs] = true; 760 props[this.indexAs] = true;
763 props[this.selectedAs] = true; 761 props[this.selectedAs] = true;
764 props.tabIndex = true; 762 props.tabIndex = true;
765
766 this._instanceProps = props; 763 this._instanceProps = props;
767 this._userTemplate = Polymer.dom(this).querySelector('template'); 764 this._userTemplate = Polymer.dom(this).querySelector('template');
768 765
769 if (this._userTemplate) { 766 if (this._userTemplate) {
770 this.templatize(this._userTemplate); 767 this.templatize(this._userTemplate);
771 } else { 768 } else {
772 console.warn('iron-list requires a template to be provided in light-do m'); 769 console.warn('iron-list requires a template to be provided in light-do m');
773 } 770 }
774 } 771 }
775 }, 772 },
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
856 el._templateInstance[this.as] = value; 853 el._templateInstance[this.as] = value;
857 } 854 }
858 }, 855 },
859 856
860 /** 857 /**
861 * Called when the items have changed. That is, ressignments 858 * Called when the items have changed. That is, ressignments
862 * to `items`, splices or updates to a single item. 859 * to `items`, splices or updates to a single item.
863 */ 860 */
864 _itemsChanged: function(change) { 861 _itemsChanged: function(change) {
865 if (change.path === 'items') { 862 if (change.path === 'items') {
866 // reset items
867 this._virtualStart = 0; 863 this._virtualStart = 0;
868 this._physicalTop = 0; 864 this._physicalTop = 0;
869 this._virtualCount = this.items ? this.items.length : 0; 865 this._virtualCount = this.items ? this.items.length : 0;
870 this._collection = this.items ? Polymer.Collection.get(this.items) : nul l; 866 this._collection = this.items ? Polymer.Collection.get(this.items) : nul l;
871 this._physicalIndexForKey = {}; 867 this._physicalIndexForKey = {};
872 this._firstVisibleIndexVal = null; 868 this._firstVisibleIndexVal = null;
873 this._lastVisibleIndexVal = null; 869 this._lastVisibleIndexVal = null;
874 870 this._physicalCount = this._physicalCount || 0;
871 this._physicalItems = this._physicalItems || [];
872 this._physicalSizes = this._physicalSizes || [];
873 this._physicalStart = 0;
875 this._resetScrollPosition(0); 874 this._resetScrollPosition(0);
876 this._removeFocusedItem(); 875 this._removeFocusedItem();
877 // create the initial physical items 876 this._debounceTemplate(this._render);
878 if (!this._physicalItems) {
879 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi s._virtualCount));
880 this._physicalItems = this._createPool(this._physicalCount);
881 this._physicalSizes = new Array(this._physicalCount);
882 }
883
884 this._physicalStart = 0;
885 877
886 } else if (change.path === 'items.splices') { 878 } else if (change.path === 'items.splices') {
887
888 this._adjustVirtualIndex(change.value.indexSplices); 879 this._adjustVirtualIndex(change.value.indexSplices);
889 this._virtualCount = this.items ? this.items.length : 0; 880 this._virtualCount = this.items ? this.items.length : 0;
881 this._debounceTemplate(this._render);
890 882
891 } else { 883 } else {
892 // update a single item
893 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change. value); 884 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change. value);
894 return;
895 } 885 }
896
897 this._itemsRendered = false;
898 this._debounceTemplate(this._render);
899 }, 886 },
900 887
901 /** 888 /**
902 * @param {!Array<!PolymerSplice>} splices 889 * @param {!Array<!PolymerSplice>} splices
903 */ 890 */
904 _adjustVirtualIndex: function(splices) { 891 _adjustVirtualIndex: function(splices) {
905 splices.forEach(function(splice) { 892 splices.forEach(function(splice) {
906 // deselect removed items 893 // deselect removed items
907 splice.removed.forEach(this._removeItem, this); 894 splice.removed.forEach(this._removeItem, this);
908 // We only need to care about changes happening above the current positi on 895 // We only need to care about changes happening above the current positi on
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1001 }, itemSet); 988 }, itemSet);
1002 }, 989 },
1003 990
1004 /** 991 /**
1005 * Updates the height for a given set of items. 992 * Updates the height for a given set of items.
1006 * 993 *
1007 * @param {!Array<number>=} itemSet 994 * @param {!Array<number>=} itemSet
1008 */ 995 */
1009 _updateMetrics: function(itemSet) { 996 _updateMetrics: function(itemSet) {
1010 // Make sure we distributed all the physical items 997 // Make sure we distributed all the physical items
1011 // so we can measure them 998 // so we can measure them.
1012 Polymer.dom.flush(); 999 Polymer.dom.flush();
1013 1000
1014 var newPhysicalSize = 0; 1001 var newPhysicalSize = 0;
1015 var oldPhysicalSize = 0; 1002 var oldPhysicalSize = 0;
1016 var prevAvgCount = this._physicalAverageCount; 1003 var prevAvgCount = this._physicalAverageCount;
1017 var prevPhysicalAvg = this._physicalAverage; 1004 var prevPhysicalAvg = this._physicalAverage;
1018 1005
1019 this._iterateItems(function(pidx, vidx) { 1006 this._iterateItems(function(pidx, vidx) {
1020 1007
1021 oldPhysicalSize += this._physicalSizes[pidx] || 0; 1008 oldPhysicalSize += this._physicalSizes[pidx] || 0;
1022 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; 1009 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
1023 newPhysicalSize += this._physicalSizes[pidx]; 1010 newPhysicalSize += this._physicalSizes[pidx];
1024 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; 1011 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
1025 1012
1026 }, itemSet); 1013 }, itemSet);
1027 1014
1028 this._viewportHeight = this._scrollTargetHeight; 1015 this._viewportHeight = this._scrollTargetHeight;
1029 if (this.grid) { 1016 if (this.grid) {
1030 this._updateGridMetrics(); 1017 this._updateGridMetrics();
1031 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight; 1018 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight;
1032 } else { 1019 } else {
1033 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS ize; 1020 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS ize;
1034 } 1021 }
1035 1022
1036 // update the average if we measured something 1023 // Update the average if it measured something.
1037 if (this._physicalAverageCount !== prevAvgCount) { 1024 if (this._physicalAverageCount !== prevAvgCount) {
1038 this._physicalAverage = Math.round( 1025 this._physicalAverage = Math.round(
1039 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / 1026 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) /
1040 this._physicalAverageCount); 1027 this._physicalAverageCount);
1041 } 1028 }
1042 }, 1029 },
1043 1030
1044 _updateGridMetrics: function() { 1031 _updateGridMetrics: function() {
1045 this._viewportWidth = this.$.items.offsetWidth; 1032 this._viewportWidth = this.$.items.offsetWidth;
1046 // Set item width to the value of the _physicalItems offsetWidth 1033 // Set item width to the value of the _physicalItems offsetWidth
(...skipping 10 matching lines...) Expand all
1057 _positionItems: function() { 1044 _positionItems: function() {
1058 this._adjustScrollPosition(); 1045 this._adjustScrollPosition();
1059 1046
1060 var y = this._physicalTop; 1047 var y = this._physicalTop;
1061 1048
1062 if (this.grid) { 1049 if (this.grid) {
1063 var totalItemWidth = this._itemsPerRow * this._itemWidth; 1050 var totalItemWidth = this._itemsPerRow * this._itemWidth;
1064 var rowOffset = (this._viewportWidth - totalItemWidth) / 2; 1051 var rowOffset = (this._viewportWidth - totalItemWidth) / 2;
1065 1052
1066 this._iterateItems(function(pidx, vidx) { 1053 this._iterateItems(function(pidx, vidx) {
1067
1068 var modulus = vidx % this._itemsPerRow; 1054 var modulus = vidx % this._itemsPerRow;
1069 var x = Math.floor((modulus * this._itemWidth) + rowOffset); 1055 var x = Math.floor((modulus * this._itemWidth) + rowOffset);
1070
1071 this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); 1056 this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]);
1072
1073 if (this._shouldRenderNextRow(vidx)) { 1057 if (this._shouldRenderNextRow(vidx)) {
1074 y += this._rowHeight; 1058 y += this._rowHeight;
1075 } 1059 }
1076
1077 }); 1060 });
1078 } else { 1061 } else {
1079 this._iterateItems(function(pidx, vidx) { 1062 this._iterateItems(function(pidx, vidx) {
1080
1081 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); 1063 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
1082 y += this._physicalSizes[pidx]; 1064 y += this._physicalSizes[pidx];
1083
1084 }); 1065 });
1085 } 1066 }
1086 }, 1067 },
1087 1068
1088 _getPhysicalSizeIncrement: function(pidx) { 1069 _getPhysicalSizeIncrement: function(pidx) {
1089 if (!this.grid) { 1070 if (!this.grid) {
1090 return this._physicalSizes[pidx]; 1071 return this._physicalSizes[pidx];
1091 } 1072 }
1092 if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) { 1073 if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) {
1093 return 0; 1074 return 0;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1143 this._estScrollHeight = this._virtualRowCount * this._rowHeight; 1124 this._estScrollHeight = this._virtualRowCount * this._rowHeight;
1144 } else { 1125 } else {
1145 this._estScrollHeight = (this._physicalBottom + 1126 this._estScrollHeight = (this._physicalBottom +
1146 Math.max(this._virtualCount - this._physicalCount - this._virtualSta rt, 0) * this._physicalAverage); 1127 Math.max(this._virtualCount - this._physicalCount - this._virtualSta rt, 0) * this._physicalAverage);
1147 } 1128 }
1148 1129
1149 forceUpdate = forceUpdate || this._scrollHeight === 0; 1130 forceUpdate = forceUpdate || this._scrollHeight === 0;
1150 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; 1131 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
1151 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this ._estScrollHeight; 1132 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this ._estScrollHeight;
1152 1133
1153 // amortize height adjustment, so it won't trigger repaints very often 1134 // Amortize height adjustment, so it won't trigger large repaints too ofte n.
1154 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { 1135 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) {
1155 this.$.items.style.height = this._estScrollHeight + 'px'; 1136 this.$.items.style.height = this._estScrollHeight + 'px';
1156 this._scrollHeight = this._estScrollHeight; 1137 this._scrollHeight = this._estScrollHeight;
1157 } 1138 }
1158 }, 1139 },
1159 1140
1160 /** 1141 /**
1161 * Scroll to a specific item in the virtual list regardless 1142 * Scroll to a specific item in the virtual list regardless
1162 * of the physical items in the DOM tree. 1143 * of the physical items in the DOM tree.
1163 * 1144 *
1164 * @method scrollToItem 1145 * @method scrollToItem
1165 * @param {(Object)} item The item to be scrolled to 1146 * @param {(Object)} item The item to be scrolled to
1166 */ 1147 */
1167 scrollToItem: function(item){ 1148 scrollToItem: function(item){
1168 return this.scrollToIndex(this.items.indexOf(item)); 1149 return this.scrollToIndex(this.items.indexOf(item));
1169 }, 1150 },
1170 1151
1171 /** 1152 /**
1172 * Scroll to a specific index in the virtual list regardless 1153 * Scroll to a specific index in the virtual list regardless
1173 * of the physical items in the DOM tree. 1154 * of the physical items in the DOM tree.
1174 * 1155 *
1175 * @method scrollToIndex 1156 * @method scrollToIndex
1176 * @param {number} idx The index of the item 1157 * @param {number} idx The index of the item
1177 */ 1158 */
1178 scrollToIndex: function(idx) { 1159 scrollToIndex: function(idx) {
1179 if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) { 1160 if (typeof idx !== 'number' || idx < 0 || idx > this.items.length - 1) {
1180 return; 1161 return;
1181 } 1162 }
1163
1182 Polymer.dom.flush(); 1164 Polymer.dom.flush();
1183 // Items should have been rendered prior scrolling to an index. 1165 // Items should have been rendered prior scrolling to an index.
1184 if (!this._itemsRendered) { 1166 if (this._physicalCount === 0) {
1185 return; 1167 return;
1186 } 1168 }
1187 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); 1169 idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
1188 // update the virtual start only when needed 1170 // Update the virtual start only when needed.
1189 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { 1171 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) {
1190 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx - 1); 1172 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx - 1);
1191 } 1173 }
1192 // manage focus
1193 this._manageFocus(); 1174 this._manageFocus();
1194 // assign new models
1195 this._assignModels(); 1175 this._assignModels();
1196 // measure the new sizes
1197 this._updateMetrics(); 1176 this._updateMetrics();
1198 // estimate new physical offset 1177 // Estimate new physical offset.
1199 this._physicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage; 1178 this._physicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage;
1200 1179
1201 var currentTopItem = this._physicalStart; 1180 var currentTopItem = this._physicalStart;
1202 var currentVirtualItem = this._virtualStart; 1181 var currentVirtualItem = this._virtualStart;
1203 var targetOffsetTop = 0; 1182 var targetOffsetTop = 0;
1204 var hiddenContentSize = this._hiddenContentSize; 1183 var hiddenContentSize = this._hiddenContentSize;
1205 // scroll to the item as much as we can 1184 // scroll to the item as much as we can.
1206 while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { 1185 while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) {
1207 targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(curre ntTopItem); 1186 targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(curre ntTopItem);
1208 currentTopItem = (currentTopItem + 1) % this._physicalCount; 1187 currentTopItem = (currentTopItem + 1) % this._physicalCount;
1209 currentVirtualItem++; 1188 currentVirtualItem++;
1210 } 1189 }
1211 // update the scroller size
1212 this._updateScrollerSize(true); 1190 this._updateScrollerSize(true);
1213 // update the position of the items
1214 this._positionItems(); 1191 this._positionItems();
1215 // set the new scroll position
1216 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t argetOffsetTop); 1192 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t argetOffsetTop);
1217 // increase the pool of physical items if needed
1218 this._increasePoolIfNeeded(); 1193 this._increasePoolIfNeeded();
1219 // clear cached visible index 1194 // clear cached visible index.
1220 this._firstVisibleIndexVal = null; 1195 this._firstVisibleIndexVal = null;
1221 this._lastVisibleIndexVal = null; 1196 this._lastVisibleIndexVal = null;
1222 }, 1197 },
1223 1198
1224 /** 1199 /**
1225 * Reset the physical average and the average count. 1200 * Reset the physical average and the average count.
1226 */ 1201 */
1227 _resetAverage: function() { 1202 _resetAverage: function() {
1228 this._physicalAverage = 0; 1203 this._physicalAverage = 0;
1229 this._physicalAverageCount = 0; 1204 this._physicalAverageCount = 0;
1230 }, 1205 },
1231 1206
1232 /** 1207 /**
1233 * A handler for the `iron-resize` event triggered by `IronResizableBehavior ` 1208 * A handler for the `iron-resize` event triggered by `IronResizableBehavior `
1234 * when the element is resized. 1209 * when the element is resized.
1235 */ 1210 */
1236 _resizeHandler: function() { 1211 _resizeHandler: function() {
1237 // iOS fires the resize event when the address bar slides up 1212 // iOS fires the resize event when the address bar slides up
1238 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100 ) { 1213 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100 ) {
1239 return; 1214 return;
1240 } 1215 }
1241 // In Desktop Safari 9.0.3, if the scroll bars are always shown, 1216 // In Desktop Safari 9.0.3, if the scroll bars are always shown,
1242 // changing the scroll position from a resize handler would result in 1217 // changing the scroll position from a resize handler would result in
1243 // the scroll position being reset. Waiting 1ms fixes the issue. 1218 // the scroll position being reset. Waiting 1ms fixes the issue.
1244 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { 1219 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() {
1245 this.updateViewportBoundaries(); 1220 this.updateViewportBoundaries();
1246 this._render(); 1221 this._render();
1247 1222
1248 if (this._itemsRendered && this._physicalItems && this._isVisible) { 1223 if (this._physicalCount > 0 && this._isVisible) {
1249 this._resetAverage(); 1224 this._resetAverage();
1250 this.scrollToIndex(this.firstVisibleIndex); 1225 this.scrollToIndex(this.firstVisibleIndex);
1251 } 1226 }
1252 }.bind(this), 1)); 1227 }.bind(this), 1));
1253 }, 1228 },
1254 1229
1255 _getModelFromItem: function(item) { 1230 _getModelFromItem: function(item) {
1256 var key = this._collection.getKey(item); 1231 var key = this._collection.getKey(item);
1257 var pidx = this._physicalIndexForKey[key]; 1232 var pidx = this._physicalIndexForKey[key];
1258 1233
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after
1586 this._focusPhysicalItem(this._focusedIndex + 1); 1561 this._focusPhysicalItem(this._focusedIndex + 1);
1587 }, 1562 },
1588 1563
1589 _didEnter: function(e) { 1564 _didEnter: function(e) {
1590 this._focusPhysicalItem(this._focusedIndex); 1565 this._focusPhysicalItem(this._focusedIndex);
1591 this._selectionHandler(e.detail.keyboardEvent); 1566 this._selectionHandler(e.detail.keyboardEvent);
1592 } 1567 }
1593 }); 1568 });
1594 1569
1595 })(); 1570 })();
OLDNEW
« no previous file with comments | « third_party/polymer/v1_0/components-chromium/iron-list/bower.json ('k') | third_party/polymer/v1_0/components_summary.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698