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

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

Issue 1901343004: [Polymer] update third_party polymer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: new pull Created 4 years, 8 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 MAX_PHYSICAL_COUNT = 500;
7 var HIDDEN_Y = '-10000px'; 6 var HIDDEN_Y = '-10000px';
7 var DEFAULT_GRID_SIZE = 200;
8 8
9 Polymer({ 9 Polymer({
10 10
11 is: 'iron-list', 11 is: 'iron-list',
12 12
13 properties: { 13 properties: {
14 14
15 /** 15 /**
16 * An array containing items determining how many instances of the templat e 16 * An array containing items determining how many instances of the templat e
17 * to stamp and that that each template instance should bind to. 17 * to stamp and that that each template instance should bind to.
18 */ 18 */
19 items: { 19 items: {
20 type: Array 20 type: Array
21 }, 21 },
22 22
23 /** 23 /**
24 * The max count of physical items the pool can extend to.
25 */
26 maxPhysicalCount: {
27 type: Number,
28 value: 500
29 },
30
31 /**
24 * The name of the variable to add to the binding scope for the array 32 * The name of the variable to add to the binding scope for the array
25 * element associated with a given template instance. 33 * element associated with a given template instance.
26 */ 34 */
27 as: { 35 as: {
28 type: String, 36 type: String,
29 value: 'item' 37 value: 'item'
30 }, 38 },
31 39
32 /** 40 /**
33 * The name of the variable to add to the binding scope with the index 41 * The name of the variable to add to the binding scope with the index
34 * for the row. 42 * for the row.
35 */ 43 */
36 indexAs: { 44 indexAs: {
37 type: String, 45 type: String,
38 value: 'index' 46 value: 'index'
39 }, 47 },
40 48
41 /** 49 /**
42 * The name of the variable to add to the binding scope to indicate 50 * The name of the variable to add to the binding scope to indicate
43 * if the row is selected. 51 * if the row is selected.
44 */ 52 */
45 selectedAs: { 53 selectedAs: {
46 type: String, 54 type: String,
47 value: 'selected' 55 value: 'selected'
48 }, 56 },
49 57
50 /** 58 /**
59 * When true, the list is rendered as a grid. Grid items must have
60 * fixed width and height set via CSS. e.g.
61 *
62 * ```html
63 * <iron-list grid>
64 * <template>
65 * <div style="width: 100px; height: 100px;"> 100x100 </div>
66 * </template>
67 * </iron-list>
68 * ```
69 */
70 grid: {
71 type: Boolean,
72 value: false,
73 reflectToAttribute: true
74 },
75
76 /**
51 * When true, tapping a row will select the item, placing its data model 77 * When true, tapping a row will select the item, placing its data model
52 * in the set of selected items retrievable via the selection property. 78 * in the set of selected items retrievable via the selection property.
53 * 79 *
54 * Note that tapping focusable elements within the list item will not 80 * Note that tapping focusable elements within the list item will not
55 * result in selection, since they are presumed to have their * own action . 81 * result in selection, since they are presumed to have their * own action .
56 */ 82 */
57 selectionEnabled: { 83 selectionEnabled: {
58 type: Boolean, 84 type: Boolean,
59 value: false 85 value: false
60 }, 86 },
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 _estScrollHeight: 0, 186 _estScrollHeight: 0,
161 187
162 /** 188 /**
163 * The scroll height of the dom node 189 * The scroll height of the dom node
164 */ 190 */
165 _scrollHeight: 0, 191 _scrollHeight: 0,
166 192
167 /** 193 /**
168 * The height of the list. This is referred as the viewport in the context o f list. 194 * The height of the list. This is referred as the viewport in the context o f list.
169 */ 195 */
170 _viewportSize: 0, 196 _viewportHeight: 0,
197
198 /**
199 * The width of the list. This is referred as the viewport in the context of list.
200 */
201 _viewportWidth: 0,
171 202
172 /** 203 /**
173 * An array of DOM nodes that are currently in the tree 204 * An array of DOM nodes that are currently in the tree
174 * @type {?Array<!TemplatizerNode>} 205 * @type {?Array<!TemplatizerNode>}
175 */ 206 */
176 _physicalItems: null, 207 _physicalItems: null,
177 208
178 /** 209 /**
179 * An array of heights for each item in `_physicalItems` 210 * An array of heights for each item in `_physicalItems`
180 * @type {?Array<number>} 211 * @type {?Array<number>}
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 */ 264 */
234 _offscreenFocusedItem: null, 265 _offscreenFocusedItem: null,
235 266
236 /** 267 /**
237 * The item that backfills the `_offscreenFocusedItem` in the physical items 268 * The item that backfills the `_offscreenFocusedItem` in the physical items
238 * list when that item is moved offscreen. 269 * list when that item is moved offscreen.
239 */ 270 */
240 _focusBackfillItem: null, 271 _focusBackfillItem: null,
241 272
242 /** 273 /**
274 * The maximum items per row
275 */
276 _itemsPerRow: 1,
277
278 /**
279 * The width of each grid item
280 */
281 _itemWidth: 0,
282
283 /**
284 * The height of the row in grid layout.
285 */
286 _rowHeight: 0,
287
288 /**
243 * The bottom of the physical content. 289 * The bottom of the physical content.
244 */ 290 */
245 get _physicalBottom() { 291 get _physicalBottom() {
246 return this._physicalTop + this._physicalSize; 292 return this._physicalTop + this._physicalSize;
247 }, 293 },
248 294
249 /** 295 /**
250 * The bottom of the scroll. 296 * The bottom of the scroll.
251 */ 297 */
252 get _scrollBottom() { 298 get _scrollBottom() {
253 return this._scrollPosition + this._viewportSize; 299 return this._scrollPosition + this._viewportHeight;
254 }, 300 },
255 301
256 /** 302 /**
257 * The n-th item rendered in the last physical item. 303 * The n-th item rendered in the last physical item.
258 */ 304 */
259 get _virtualEnd() { 305 get _virtualEnd() {
260 return this._virtualStart + this._physicalCount - 1; 306 return this._virtualStart + this._physicalCount - 1;
261 }, 307 },
262 308
263 /** 309 /**
264 * The height of the physical content that isn't on the screen. 310 * The height of the physical content that isn't on the screen.
265 */ 311 */
266 get _hiddenContentSize() { 312 get _hiddenContentSize() {
267 return this._physicalSize - this._viewportSize; 313 var size = this.grid ? this._physicalRows * this._rowHeight : this._physic alSize;
314 return size - this._viewportHeight;
268 }, 315 },
269 316
270 /** 317 /**
271 * The maximum scroll top value. 318 * The maximum scroll top value.
272 */ 319 */
273 get _maxScrollTop() { 320 get _maxScrollTop() {
274 return this._estScrollHeight - this._viewportSize + this._scrollerPaddingT op; 321 return this._estScrollHeight - this._viewportHeight + this._scrollerPaddin gTop;
275 }, 322 },
276 323
277 /** 324 /**
278 * The lowest n-th value for an item such that it can be rendered in `_physi calStart`. 325 * The lowest n-th value for an item such that it can be rendered in `_physi calStart`.
279 */ 326 */
280 _minVirtualStart: 0, 327 _minVirtualStart: 0,
281 328
282 /** 329 /**
283 * The largest n-th value for an item such that it can be rendered in `_phys icalStart`. 330 * The largest n-th value for an item such that it can be rendered in `_phys icalStart`.
284 */ 331 */
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 _physicalEnd: 0, 383 _physicalEnd: 0,
337 384
338 /** 385 /**
339 * An optimal physical size such that we will have enough physical items 386 * An optimal physical size such that we will have enough physical items
340 * to fill up the viewport and recycle when the user scrolls. 387 * to fill up the viewport and recycle when the user scrolls.
341 * 388 *
342 * This default value assumes that we will at least have the equivalent 389 * This default value assumes that we will at least have the equivalent
343 * to a viewport of physical items above and below the user's viewport. 390 * to a viewport of physical items above and below the user's viewport.
344 */ 391 */
345 get _optPhysicalSize() { 392 get _optPhysicalSize() {
346 return this._viewportSize * this._maxPages; 393 if (this.grid) {
394 return this._estRowsInView * this._rowHeight * this._maxPages;
395 }
396 return this._viewportHeight * this._maxPages;
397 },
398
399 get _optPhysicalCount() {
400 return this._estRowsInView * this._itemsPerRow * this._maxPages;
347 }, 401 },
348 402
349 /** 403 /**
350 * True if the current list is visible. 404 * True if the current list is visible.
351 */ 405 */
352 get _isVisible() { 406 get _isVisible() {
353 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this. scrollTarget.offsetHeight); 407 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this. scrollTarget.offsetHeight);
354 }, 408 },
355 409
356 /** 410 /**
357 * Gets the index of the first visible item in the viewport. 411 * Gets the index of the first visible item in the viewport.
358 * 412 *
359 * @type {number} 413 * @type {number}
360 */ 414 */
361 get firstVisibleIndex() { 415 get firstVisibleIndex() {
362 if (this._firstVisibleIndexVal === null) { 416 if (this._firstVisibleIndexVal === null) {
363 var physicalOffset = this._physicalTop + this._scrollerPaddingTop; 417 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin gTop);
364 418
365 this._firstVisibleIndexVal = this._iterateItems( 419 this._firstVisibleIndexVal = this._iterateItems(
366 function(pidx, vidx) { 420 function(pidx, vidx) {
367 physicalOffset += this._physicalSizes[pidx]; 421 physicalOffset += this._getPhysicalSizeIncrement(pidx);
422
368 if (physicalOffset > this._scrollPosition) { 423 if (physicalOffset > this._scrollPosition) {
369 return vidx; 424 return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx;
425 }
426
427 // Handle a partially rendered final row in grid mode
428 if (this.grid && this._virtualCount - 1 === vidx) {
429 return vidx - (vidx % this._itemsPerRow);
370 } 430 }
371 }) || 0; 431 }) || 0;
372 } 432 }
373 return this._firstVisibleIndexVal; 433 return this._firstVisibleIndexVal;
374 }, 434 },
375 435
376 /** 436 /**
377 * Gets the index of the last visible item in the viewport. 437 * Gets the index of the last visible item in the viewport.
378 * 438 *
379 * @type {number} 439 * @type {number}
380 */ 440 */
381 get lastVisibleIndex() { 441 get lastVisibleIndex() {
382 if (this._lastVisibleIndexVal === null) { 442 if (this._lastVisibleIndexVal === null) {
383 var physicalOffset = this._physicalTop; 443 if (this.grid) {
444 var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._i temsPerRow - 1;
445 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? this._vir tualCount : lastIndex;
446 } else {
447 var physicalOffset = this._physicalTop;
384 448
385 this._iterateItems(function(pidx, vidx) { 449 this._iterateItems(function(pidx, vidx) {
386 physicalOffset += this._physicalSizes[pidx]; 450 physicalOffset += this._getPhysicalSizeIncrement(pidx);
387 451
388 if (physicalOffset <= this._scrollBottom) { 452 if(physicalOffset <= this._scrollBottom) {
389 this._lastVisibleIndexVal = vidx; 453 if (this.grid) {
390 } 454 var lastIndex = vidx - vidx % this._itemsPerRow + this._itemsPer Row - 1;
391 }); 455 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? thi s._virtualCount : lastIndex;
456 } else {
457 this._lastVisibleIndexVal = vidx;
458 }
459 }
460 });
461 }
392 } 462 }
393 return this._lastVisibleIndexVal; 463 return this._lastVisibleIndexVal;
394 }, 464 },
395 465
396 get _defaultScrollTarget() { 466 get _defaultScrollTarget() {
397 return this; 467 return this;
398 }, 468 },
469 get _virtualRowCount() {
470 return Math.ceil(this._virtualCount / this._itemsPerRow);
471 },
472
473 get _estRowsInView() {
474 return Math.ceil(this._viewportHeight / this._rowHeight);
475 },
476
477 get _physicalRows() {
478 return Math.ceil(this._physicalCount / this._itemsPerRow);
479 },
399 480
400 ready: function() { 481 ready: function() {
401 this.addEventListener('focus', this._didFocus.bind(this), true); 482 this.addEventListener('focus', this._didFocus.bind(this), true);
402 }, 483 },
403 484
404 attached: function() { 485 attached: function() {
405 this.updateViewportBoundaries(); 486 this.updateViewportBoundaries();
406 this._render(); 487 this._render();
407 // `iron-resize` is fired when the list is attached if the event is added 488 // `iron-resize` is fired when the list is attached if the event is added
408 // before attached causing unnecessary work. 489 // before attached causing unnecessary work.
(...skipping 16 matching lines...) Expand all
425 /** 506 /**
426 * Invoke this method if you dynamically update the viewport's 507 * Invoke this method if you dynamically update the viewport's
427 * size or CSS padding. 508 * size or CSS padding.
428 * 509 *
429 * @method updateViewportBoundaries 510 * @method updateViewportBoundaries
430 */ 511 */
431 updateViewportBoundaries: function() { 512 updateViewportBoundaries: function() {
432 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : 513 this._scrollerPaddingTop = this.scrollTarget === this ? 0 :
433 parseInt(window.getComputedStyle(this)['padding-top'], 10); 514 parseInt(window.getComputedStyle(this)['padding-top'], 10);
434 515
435 this._viewportSize = this._scrollTargetHeight; 516 this._viewportHeight = this._scrollTargetHeight;
517 if (this.grid) {
518 this._updateGridMetrics();
519 }
436 }, 520 },
437 521
438 /** 522 /**
439 * Update the models, the position of the 523 * Update the models, the position of the
440 * items in the viewport and recycle tiles as needed. 524 * items in the viewport and recycle tiles as needed.
441 */ 525 */
442 _scrollHandler: function() { 526 _scrollHandler: function() {
443 // clamp the `scrollTop` value 527 // clamp the `scrollTop` value
444 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)) ; 528 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop)) ;
445 var delta = scrollTop - this._scrollPosition; 529 var delta = scrollTop - this._scrollPosition;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 561
478 // move tiles from bottom to top 562 // move tiles from bottom to top
479 while ( 563 while (
480 // approximate `currentRatio` to `ratio` 564 // approximate `currentRatio` to `ratio`
481 currentRatio < ratio && 565 currentRatio < ratio &&
482 // recycle less physical items than the total 566 // recycle less physical items than the total
483 recycledTiles < this._physicalCount && 567 recycledTiles < this._physicalCount &&
484 // ensure that these recycled tiles are needed 568 // ensure that these recycled tiles are needed
485 virtualStart - recycledTiles > 0 && 569 virtualStart - recycledTiles > 0 &&
486 // ensure that the tile is not visible 570 // ensure that the tile is not visible
487 physicalBottom - this._physicalSizes[kth] > scrollBottom 571 physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom
488 ) { 572 ) {
489 573
490 tileHeight = this._physicalSizes[kth]; 574 tileHeight = this._getPhysicalSizeIncrement(kth);
491 currentRatio += tileHeight / hiddenContentSize; 575 currentRatio += tileHeight / hiddenContentSize;
492 physicalBottom -= tileHeight; 576 physicalBottom -= tileHeight;
493 recycledTileSet.push(kth); 577 recycledTileSet.push(kth);
494 recycledTiles++; 578 recycledTiles++;
495 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; 579 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1;
496 } 580 }
497 581
498 movingUp = recycledTileSet; 582 movingUp = recycledTileSet;
499 recycledTiles = -recycledTiles; 583 recycledTiles = -recycledTiles;
500 } 584 }
(...skipping 10 matching lines...) Expand all
511 595
512 // move tiles from top to bottom 596 // move tiles from top to bottom
513 while ( 597 while (
514 // approximate `currentRatio` to `ratio` 598 // approximate `currentRatio` to `ratio`
515 currentRatio < ratio && 599 currentRatio < ratio &&
516 // recycle less physical items than the total 600 // recycle less physical items than the total
517 recycledTiles < this._physicalCount && 601 recycledTiles < this._physicalCount &&
518 // ensure that these recycled tiles are needed 602 // ensure that these recycled tiles are needed
519 virtualEnd + recycledTiles < lastVirtualItemIndex && 603 virtualEnd + recycledTiles < lastVirtualItemIndex &&
520 // ensure that the tile is not visible 604 // ensure that the tile is not visible
521 this._physicalTop + this._physicalSizes[kth] < scrollTop 605 this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop
522 ) { 606 ) {
523 607
524 tileHeight = this._physicalSizes[kth]; 608 tileHeight = this._getPhysicalSizeIncrement(kth);
525 currentRatio += tileHeight / hiddenContentSize; 609 currentRatio += tileHeight / hiddenContentSize;
526 610
527 this._physicalTop += tileHeight; 611 this._physicalTop += tileHeight;
528 recycledTileSet.push(kth); 612 recycledTileSet.push(kth);
529 recycledTiles++; 613 recycledTiles++;
530 kth = (kth + 1) % this._physicalCount; 614 kth = (kth + 1) % this._physicalCount;
531 } 615 }
532 } 616 }
533 617
534 if (recycledTiles === 0) { 618 if (recycledTiles === 0) {
(...skipping 16 matching lines...) Expand all
551 _update: function(itemSet, movingUp) { 635 _update: function(itemSet, movingUp) {
552 // manage focus 636 // manage focus
553 this._manageFocus(); 637 this._manageFocus();
554 // update models 638 // update models
555 this._assignModels(itemSet); 639 this._assignModels(itemSet);
556 // measure heights 640 // measure heights
557 this._updateMetrics(itemSet); 641 this._updateMetrics(itemSet);
558 // adjust offset after measuring 642 // adjust offset after measuring
559 if (movingUp) { 643 if (movingUp) {
560 while (movingUp.length) { 644 while (movingUp.length) {
561 this._physicalTop -= this._physicalSizes[movingUp.pop()]; 645 var idx = movingUp.pop();
646 this._physicalTop -= this._getPhysicalSizeIncrement(idx);
562 } 647 }
563 } 648 }
564 // update the position of the items 649 // update the position of the items
565 this._positionItems(); 650 this._positionItems();
566 // set the scroller size 651 // set the scroller size
567 this._updateScrollerSize(); 652 this._updateScrollerSize();
568 // increase the pool of physical items 653 // increase the pool of physical items
569 this._increasePoolIfNeeded(); 654 this._increasePoolIfNeeded();
570 }, 655 },
571 656
(...skipping 14 matching lines...) Expand all
586 } 671 }
587 return physicalItems; 672 return physicalItems;
588 }, 673 },
589 674
590 /** 675 /**
591 * Increases the pool of physical items only if needed. 676 * Increases the pool of physical items only if needed.
592 * 677 *
593 * @return {boolean} True if the pool was increased. 678 * @return {boolean} True if the pool was increased.
594 */ 679 */
595 _increasePoolIfNeeded: function() { 680 _increasePoolIfNeeded: function() {
596 // Base case 1: the list has no size. 681 // Base case 1: the list has no height.
597 if (this._viewportSize === 0) { 682 if (this._viewportHeight === 0) {
598 return false; 683 return false;
599 } 684 }
600 // Base case 2: If the physical size is optimal and the list's client heig ht is full 685 // Base case 2: If the physical size is optimal and the list's client heig ht is full
601 // with physical items, don't increase the pool. 686 // with physical items, don't increase the pool.
602 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi s._physicalTop <= this._scrollPosition; 687 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi s._physicalTop <= this._scrollPosition;
603 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { 688 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) {
604 return false; 689 return false;
605 } 690 }
606 // this value should range between [0 <= `currentPage` <= `_maxPages`] 691 // this value should range between [0 <= `currentPage` <= `_maxPages`]
607 var currentPage = Math.floor(this._physicalSize / this._viewportSize); 692 var currentPage = Math.floor(this._physicalSize / this._viewportHeight);
608 693
609 if (currentPage === 0) { 694 if (currentPage === 0) {
610 // fill the first page 695 // fill the first page
611 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph ysicalCount * 0.5))); 696 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph ysicalCount * 0.5)));
612 } else if (this._lastPage !== currentPage && isClientHeightFull) { 697 } else if (this._lastPage !== currentPage && isClientHeightFull) {
613 // paint the page and defer the next increase 698 // paint the page and defer the next increase
614 // wait 16ms which is rough enough to get paint cycle. 699 // wait 16ms which is rough enough to get paint cycle.
615 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa sePool.bind(this, 1), 16)); 700 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa sePool.bind(this, this._itemsPerRow), 16));
616 } else { 701 } else {
617 // fill the rest of the pages 702 // fill the rest of the pages
618 this._debounceTemplate(this._increasePool.bind(this, 1)); 703 this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow)) ;
619 } 704 }
620 705
621 this._lastPage = currentPage; 706 this._lastPage = currentPage;
622 707
623 return true; 708 return true;
624 }, 709 },
625 710
626 /** 711 /**
627 * Increases the pool size. 712 * Increases the pool size.
628 */ 713 */
629 _increasePool: function(missingItems) { 714 _increasePool: function(missingItems) {
630 var nextPhysicalCount = Math.min( 715 var nextPhysicalCount = Math.min(
631 this._physicalCount + missingItems, 716 this._physicalCount + missingItems,
632 this._virtualCount - this._virtualStart, 717 this._virtualCount - this._virtualStart,
633 MAX_PHYSICAL_COUNT 718 Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT)
634 ); 719 );
635 var prevPhysicalCount = this._physicalCount; 720 var prevPhysicalCount = this._physicalCount;
636 var delta = nextPhysicalCount - prevPhysicalCount; 721 var delta = nextPhysicalCount - prevPhysicalCount;
637 722
638 if (delta <= 0) { 723 if (delta <= 0) {
639 return; 724 return;
640 } 725 }
641 726
642 [].push.apply(this._physicalItems, this._createPool(delta)); 727 [].push.apply(this._physicalItems, this._createPool(delta));
643 [].push.apply(this._physicalSizes, new Array(delta)); 728 [].push.apply(this._physicalSizes, new Array(delta));
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
848 * 933 *
849 * @param {!function(number, number)} fn 934 * @param {!function(number, number)} fn
850 * @param {!Array<number>=} itemSet 935 * @param {!Array<number>=} itemSet
851 */ 936 */
852 _iterateItems: function(fn, itemSet) { 937 _iterateItems: function(fn, itemSet) {
853 var pidx, vidx, rtn, i; 938 var pidx, vidx, rtn, i;
854 939
855 if (arguments.length === 2 && itemSet) { 940 if (arguments.length === 2 && itemSet) {
856 for (i = 0; i < itemSet.length; i++) { 941 for (i = 0; i < itemSet.length; i++) {
857 pidx = itemSet[i]; 942 pidx = itemSet[i];
858 if (pidx >= this._physicalStart) { 943 vidx = this._computeVidx(pidx);
859 vidx = this._virtualStart + (pidx - this._physicalStart);
860 } else {
861 vidx = this._virtualStart + (this._physicalCount - this._physicalSta rt) + pidx;
862 }
863 if ((rtn = fn.call(this, pidx, vidx)) != null) { 944 if ((rtn = fn.call(this, pidx, vidx)) != null) {
864 return rtn; 945 return rtn;
865 } 946 }
866 } 947 }
867 } else { 948 } else {
868 pidx = this._physicalStart; 949 pidx = this._physicalStart;
869 vidx = this._virtualStart; 950 vidx = this._virtualStart;
870 951
871 for (; pidx < this._physicalCount; pidx++, vidx++) { 952 for (; pidx < this._physicalCount; pidx++, vidx++) {
872 if ((rtn = fn.call(this, pidx, vidx)) != null) { 953 if ((rtn = fn.call(this, pidx, vidx)) != null) {
873 return rtn; 954 return rtn;
874 } 955 }
875 } 956 }
876 for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) { 957 for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) {
877 if ((rtn = fn.call(this, pidx, vidx)) != null) { 958 if ((rtn = fn.call(this, pidx, vidx)) != null) {
878 return rtn; 959 return rtn;
879 } 960 }
880 } 961 }
881 } 962 }
882 }, 963 },
883 964
884 /** 965 /**
966 * Returns the virtual index for a given physical index
967 *
968 * @param {number} pidx Physical index
969 * @return {number}
970 */
971 _computeVidx: function(pidx) {
972 if (pidx >= this._physicalStart) {
973 return this._virtualStart + (pidx - this._physicalStart);
974 }
975 return this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
976 },
977
978 /**
885 * Assigns the data models to a given set of items. 979 * Assigns the data models to a given set of items.
886 * @param {!Array<number>=} itemSet 980 * @param {!Array<number>=} itemSet
887 */ 981 */
888 _assignModels: function(itemSet) { 982 _assignModels: function(itemSet) {
889 this._iterateItems(function(pidx, vidx) { 983 this._iterateItems(function(pidx, vidx) {
890 var el = this._physicalItems[pidx]; 984 var el = this._physicalItems[pidx];
891 var inst = el._templateInstance; 985 var inst = el._templateInstance;
892 var item = this.items && this.items[vidx]; 986 var item = this.items && this.items[vidx];
893 987
894 if (item != null) { 988 if (item != null) {
(...skipping 28 matching lines...) Expand all
923 1017
924 this._iterateItems(function(pidx, vidx) { 1018 this._iterateItems(function(pidx, vidx) {
925 1019
926 oldPhysicalSize += this._physicalSizes[pidx] || 0; 1020 oldPhysicalSize += this._physicalSizes[pidx] || 0;
927 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; 1021 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
928 newPhysicalSize += this._physicalSizes[pidx]; 1022 newPhysicalSize += this._physicalSizes[pidx];
929 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; 1023 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
930 1024
931 }, itemSet); 1025 }, itemSet);
932 1026
933 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSiz e; 1027 this._viewportHeight = this._scrollTargetHeight;
934 this._viewportSize = this._scrollTargetHeight; 1028 if (this.grid) {
1029 this._updateGridMetrics();
1030 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow) * this._rowHeight;
1031 } else {
1032 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS ize;
1033 }
935 1034
936 // update the average if we measured something 1035 // update the average if we measured something
937 if (this._physicalAverageCount !== prevAvgCount) { 1036 if (this._physicalAverageCount !== prevAvgCount) {
938 this._physicalAverage = Math.round( 1037 this._physicalAverage = Math.round(
939 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / 1038 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) /
940 this._physicalAverageCount); 1039 this._physicalAverageCount);
941 } 1040 }
942 }, 1041 },
943 1042
1043 _updateGridMetrics: function() {
1044 this._viewportWidth = this._scrollTargetWidth;
1045 // Set item width to the value of the _physicalItems offsetWidth
1046 this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].offsetW idth : DEFAULT_GRID_SIZE;
1047 // Set row height to the value of the _physicalItems offsetHeight
1048 this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetH eight : DEFAULT_GRID_SIZE;
1049 // If in grid mode compute how many items with exist in each row
1050 this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / thi s._itemWidth) : this._itemsPerRow;
1051 },
1052
944 /** 1053 /**
945 * Updates the position of the physical items. 1054 * Updates the position of the physical items.
946 */ 1055 */
947 _positionItems: function() { 1056 _positionItems: function() {
948 this._adjustScrollPosition(); 1057 this._adjustScrollPosition();
949 1058
950 var y = this._physicalTop; 1059 var y = this._physicalTop;
951 1060
952 this._iterateItems(function(pidx) { 1061 if (this.grid) {
953 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); 1062 var totalItemWidth = this._itemsPerRow * this._itemWidth;
954 y += this._physicalSizes[pidx]; 1063 var rowOffset = (this._viewportWidth - totalItemWidth) / 2;
955 }); 1064
1065 this._iterateItems(function(pidx, vidx) {
1066
1067 var modulus = vidx % this._itemsPerRow;
1068 var x = Math.floor((modulus * this._itemWidth) + rowOffset);
1069
1070 this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]);
1071
1072 if (this._shouldRenderNextRow(vidx)) {
1073 y += this._rowHeight;
1074 }
1075
1076 });
1077 } else {
1078 this._iterateItems(function(pidx, vidx) {
1079
1080 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
1081 y += this._physicalSizes[pidx];
1082
1083 });
1084 }
1085 },
1086
1087 _getPhysicalSizeIncrement: function(pidx) {
1088 if (!this.grid) {
1089 return this._physicalSizes[pidx];
1090 }
1091 if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1) {
1092 return 0;
1093 }
1094 return this._rowHeight;
956 }, 1095 },
957 1096
958 /** 1097 /**
1098 * Returns, based on the current index,
1099 * whether or not the next index will need
1100 * to be rendered on a new row.
1101 *
1102 * @param {number} vidx Virtual index
1103 * @return {boolean}
1104 */
1105 _shouldRenderNextRow: function(vidx) {
1106 return vidx % this._itemsPerRow === this._itemsPerRow - 1;
1107 },
1108
1109 /**
959 * Adjusts the scroll position when it was overestimated. 1110 * Adjusts the scroll position when it was overestimated.
960 */ 1111 */
961 _adjustScrollPosition: function() { 1112 _adjustScrollPosition: function() {
962 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : 1113 var deltaHeight = this._virtualStart === 0 ? this._physicalTop :
963 Math.min(this._scrollPosition + this._physicalTop, 0); 1114 Math.min(this._scrollPosition + this._physicalTop, 0);
964 1115
965 if (deltaHeight) { 1116 if (deltaHeight) {
966 this._physicalTop = this._physicalTop - deltaHeight; 1117 this._physicalTop = this._physicalTop - deltaHeight;
967 // juking scroll position during interial scrolling on iOS is no bueno 1118 // juking scroll position during interial scrolling on iOS is no bueno
968 if (!IOS_TOUCH_SCROLLING) { 1119 if (!IOS_TOUCH_SCROLLING) {
(...skipping 11 matching lines...) Expand all
980 this._scrollPosition = this._scrollTop; 1131 this._scrollPosition = this._scrollTop;
981 } 1132 }
982 }, 1133 },
983 1134
984 /** 1135 /**
985 * Sets the scroll height, that's the height of the content, 1136 * Sets the scroll height, that's the height of the content,
986 * 1137 *
987 * @param {boolean=} forceUpdate If true, updates the height no matter what. 1138 * @param {boolean=} forceUpdate If true, updates the height no matter what.
988 */ 1139 */
989 _updateScrollerSize: function(forceUpdate) { 1140 _updateScrollerSize: function(forceUpdate) {
990 this._estScrollHeight = (this._physicalBottom + 1141 if (this.grid) {
991 Math.max(this._virtualCount - this._physicalCount - this._virtualStart , 0) * this._physicalAverage); 1142 this._estScrollHeight = this._virtualRowCount * this._rowHeight;
1143 } else {
1144 this._estScrollHeight = (this._physicalBottom +
1145 Math.max(this._virtualCount - this._physicalCount - this._virtualSta rt, 0) * this._physicalAverage);
1146 }
992 1147
993 forceUpdate = forceUpdate || this._scrollHeight === 0; 1148 forceUpdate = forceUpdate || this._scrollHeight === 0;
994 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; 1149 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
1150 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this ._estScrollHeight;
995 1151
996 // amortize height adjustment, so it won't trigger repaints very often 1152 // amortize height adjustment, so it won't trigger repaints very often
997 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { 1153 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) {
998 this.$.items.style.height = this._estScrollHeight + 'px'; 1154 this.$.items.style.height = this._estScrollHeight + 'px';
999 this._scrollHeight = this._estScrollHeight; 1155 this._scrollHeight = this._estScrollHeight;
1000 } 1156 }
1001 }, 1157 },
1002 /** 1158 /**
1003 * Scroll to a specific item in the virtual list regardless 1159 * Scroll to a specific item in the virtual list regardless
1004 * of the physical items in the DOM tree. 1160 * of the physical items in the DOM tree.
1005 * 1161 *
1006 * @method scrollToIndex 1162 * @method scrollToIndex
1007 * @param {number} idx The index of the item 1163 * @param {number} idx The index of the item
1008 */ 1164 */
1009 scrollToIndex: function(idx) { 1165 scrollToIndex: function(idx) {
1010 if (typeof idx !== 'number') { 1166 if (typeof idx !== 'number') {
1011 return; 1167 return;
1012 } 1168 }
1013 1169
1014 Polymer.dom.flush(); 1170 Polymer.dom.flush();
1015 1171
1016 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); 1172 idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
1017 // update the virtual start only when needed 1173 // update the virtual start only when needed
1018 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { 1174 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) {
1019 this._virtualStart = idx - 1; 1175 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx - 1);
1020 } 1176 }
1021 // manage focus 1177 // manage focus
1022 this._manageFocus(); 1178 this._manageFocus();
1023 // assign new models 1179 // assign new models
1024 this._assignModels(); 1180 this._assignModels();
1025 // measure the new sizes 1181 // measure the new sizes
1026 this._updateMetrics(); 1182 this._updateMetrics();
1183
1027 // estimate new physical offset 1184 // estimate new physical offset
1028 this._physicalTop = this._virtualStart * this._physicalAverage; 1185 var estPhysicalTop = Math.floor(this._virtualStart / this._itemsPerRow) * this._physicalAverage;
1186 this._physicalTop = estPhysicalTop;
1029 1187
1030 var currentTopItem = this._physicalStart; 1188 var currentTopItem = this._physicalStart;
1031 var currentVirtualItem = this._virtualStart; 1189 var currentVirtualItem = this._virtualStart;
1032 var targetOffsetTop = 0; 1190 var targetOffsetTop = 0;
1033 var hiddenContentSize = this._hiddenContentSize; 1191 var hiddenContentSize = this._hiddenContentSize;
1034 1192
1035 // scroll to the item as much as we can 1193 // scroll to the item as much as we can
1036 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { 1194 while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) {
1037 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; 1195 targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(curre ntTopItem);
1038 currentTopItem = (currentTopItem + 1) % this._physicalCount; 1196 currentTopItem = (currentTopItem + 1) % this._physicalCount;
1039 currentVirtualItem++; 1197 currentVirtualItem++;
1040 } 1198 }
1041 // update the scroller size 1199 // update the scroller size
1042 this._updateScrollerSize(true); 1200 this._updateScrollerSize(true);
1043 // update the position of the items 1201 // update the position of the items
1044 this._positionItems(); 1202 this._positionItems();
1045 // set the new scroll position 1203 // set the new scroll position
1046 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t argetOffsetTop); 1204 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t argetOffsetTop);
1047 // increase the pool of physical items if needed 1205 // increase the pool of physical items if needed
(...skipping 10 matching lines...) Expand all
1058 this._physicalAverage = 0; 1216 this._physicalAverage = 0;
1059 this._physicalAverageCount = 0; 1217 this._physicalAverageCount = 0;
1060 }, 1218 },
1061 1219
1062 /** 1220 /**
1063 * A handler for the `iron-resize` event triggered by `IronResizableBehavior ` 1221 * A handler for the `iron-resize` event triggered by `IronResizableBehavior `
1064 * when the element is resized. 1222 * when the element is resized.
1065 */ 1223 */
1066 _resizeHandler: function() { 1224 _resizeHandler: function() {
1067 // iOS fires the resize event when the address bar slides up 1225 // iOS fires the resize event when the address bar slides up
1068 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100) { 1226 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100 ) {
1069 return; 1227 return;
1070 } 1228 }
1071 // In Desktop Safari 9.0.3, if the scroll bars are always shown, 1229 // In Desktop Safari 9.0.3, if the scroll bars are always shown,
1072 // changing the scroll position from a resize handler would result in 1230 // changing the scroll position from a resize handler would result in
1073 // the scroll position being reset. Waiting 1ms fixes the issue. 1231 // the scroll position being reset. Waiting 1ms fixes the issue.
1074 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', 1232 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() {
1075 function() { 1233 this.updateViewportBoundaries();
1076 this._render(); 1234 this._render();
1077 1235
1078 if (this._itemsRendered && this._physicalItems && this._isVisible) { 1236 if (this._itemsRendered && this._physicalItems && this._isVisible) {
1079 this._resetAverage(); 1237 this._resetAverage();
1080 this.updateViewportBoundaries(); 1238 this.scrollToIndex(this.firstVisibleIndex);
1081 this.scrollToIndex(this.firstVisibleIndex); 1239 }
1082 } 1240 }.bind(this), 1));
1083 }.bind(this), 1));
1084 }, 1241 },
1085 1242
1086 _getModelFromItem: function(item) { 1243 _getModelFromItem: function(item) {
1087 var key = this._collection.getKey(item); 1244 var key = this._collection.getKey(item);
1088 var pidx = this._physicalIndexForKey[key]; 1245 var pidx = this._physicalIndexForKey[key];
1089 1246
1090 if (pidx != null) { 1247 if (pidx != null) {
1091 return this._physicalItems[pidx]._templateInstance; 1248 return this._physicalItems[pidx]._templateInstance;
1092 } 1249 }
1093 return null; 1250 return null;
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after
1384 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { 1541 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) {
1385 this._update(); 1542 this._update();
1386 } 1543 }
1387 } 1544 }
1388 }, 1545 },
1389 1546
1390 _didMoveUp: function() { 1547 _didMoveUp: function() {
1391 this._focusPhysicalItem(this._focusedIndex - 1); 1548 this._focusPhysicalItem(this._focusedIndex - 1);
1392 }, 1549 },
1393 1550
1394 _didMoveDown: function() { 1551 _didMoveDown: function(e) {
1552 // disable scroll when pressing the down key
1553 e.detail.keyboardEvent.preventDefault();
1395 this._focusPhysicalItem(this._focusedIndex + 1); 1554 this._focusPhysicalItem(this._focusedIndex + 1);
1396 }, 1555 },
1397 1556
1398 _didEnter: function(e) { 1557 _didEnter: function(e) {
1399 this._focusPhysicalItem(this._focusedIndex); 1558 this._focusPhysicalItem(this._focusedIndex);
1400 this._selectionHandler(e.detail.keyboardEvent); 1559 this._selectionHandler(e.detail.keyboardEvent);
1401 } 1560 }
1402 }); 1561 });
1403 1562
1404 })(); 1563 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698