| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Assertion support. | 6 * @fileoverview Assertion support. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Verify |condition| is truthy and return |condition| if so. | 10 * Verify |condition| is truthy and return |condition| if so. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 * ... | 86 * ... |
| 87 * ... | 87 * ... |
| 88 * resolver.resolve({hello: 'world'}); | 88 * resolver.resolve({hello: 'world'}); |
| 89 */ | 89 */ |
| 90 | 90 |
| 91 /** | 91 /** |
| 92 * @constructor @struct | 92 * @constructor @struct |
| 93 * @template T | 93 * @template T |
| 94 */ | 94 */ |
| 95 function PromiseResolver() { | 95 function PromiseResolver() { |
| 96 /** @private {function(T): void} */ | 96 /** @private {function(T=): void} */ |
| 97 this.resolve_; | 97 this.resolve_; |
| 98 | 98 |
| 99 /** @private {function(*=): void} */ | 99 /** @private {function(*=): void} */ |
| 100 this.reject_; | 100 this.reject_; |
| 101 | 101 |
| 102 /** @private {!Promise<T>} */ | 102 /** @private {!Promise<T>} */ |
| 103 this.promise_ = new Promise(function(resolve, reject) { | 103 this.promise_ = new Promise(function(resolve, reject) { |
| 104 this.resolve_ = resolve; | 104 this.resolve_ = resolve; |
| 105 this.reject_ = reject; | 105 this.reject_ = reject; |
| 106 }.bind(this)); | 106 }.bind(this)); |
| 107 } | 107 } |
| 108 | 108 |
| 109 PromiseResolver.prototype = { | 109 PromiseResolver.prototype = { |
| 110 /** @return {!Promise<T>} */ | 110 /** @return {!Promise<T>} */ |
| 111 get promise() { return this.promise_; }, | 111 get promise() { return this.promise_; }, |
| 112 set promise(p) { assertNotReached(); }, | 112 set promise(p) { assertNotReached(); }, |
| 113 | 113 |
| 114 /** @return {function(T): void} */ | 114 /** @return {function(T=): void} */ |
| 115 get resolve() { return this.resolve_; }, | 115 get resolve() { return this.resolve_; }, |
| 116 set resolve(r) { assertNotReached(); }, | 116 set resolve(r) { assertNotReached(); }, |
| 117 | 117 |
| 118 /** @return {function(*=): void} */ | 118 /** @return {function(*=): void} */ |
| 119 get reject() { return this.reject_; }, | 119 get reject() { return this.reject_; }, |
| 120 set reject(s) { assertNotReached(); }, | 120 set reject(s) { assertNotReached(); }, |
| 121 }; | 121 }; |
| 122 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 122 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 123 // Use of this source code is governed by a BSD-style license that can be | 123 // Use of this source code is governed by a BSD-style license that can be |
| 124 // found in the LICENSE file. | 124 // found in the LICENSE file. |
| (...skipping 2336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2461 */ | 2461 */ |
| 2462 _isValidScrollTarget: function() { | 2462 _isValidScrollTarget: function() { |
| 2463 return this.scrollTarget instanceof HTMLElement; | 2463 return this.scrollTarget instanceof HTMLElement; |
| 2464 } | 2464 } |
| 2465 }; | 2465 }; |
| 2466 (function() { | 2466 (function() { |
| 2467 | 2467 |
| 2468 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); | 2468 var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); |
| 2469 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; | 2469 var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8; |
| 2470 var DEFAULT_PHYSICAL_COUNT = 3; | 2470 var DEFAULT_PHYSICAL_COUNT = 3; |
| 2471 var MAX_PHYSICAL_COUNT = 500; | |
| 2472 var HIDDEN_Y = '-10000px'; | 2471 var HIDDEN_Y = '-10000px'; |
| 2472 var DEFAULT_GRID_SIZE = 200; |
| 2473 | 2473 |
| 2474 Polymer({ | 2474 Polymer({ |
| 2475 | 2475 |
| 2476 is: 'iron-list', | 2476 is: 'iron-list', |
| 2477 | 2477 |
| 2478 properties: { | 2478 properties: { |
| 2479 | 2479 |
| 2480 /** | 2480 /** |
| 2481 * An array containing items determining how many instances of the templat
e | 2481 * An array containing items determining how many instances of the templat
e |
| 2482 * to stamp and that that each template instance should bind to. | 2482 * to stamp and that that each template instance should bind to. |
| 2483 */ | 2483 */ |
| 2484 items: { | 2484 items: { |
| 2485 type: Array | 2485 type: Array |
| 2486 }, | 2486 }, |
| 2487 | 2487 |
| 2488 /** | 2488 /** |
| 2489 * The max count of physical items the pool can extend to. |
| 2490 */ |
| 2491 maxPhysicalCount: { |
| 2492 type: Number, |
| 2493 value: 500 |
| 2494 }, |
| 2495 |
| 2496 /** |
| 2489 * The name of the variable to add to the binding scope for the array | 2497 * The name of the variable to add to the binding scope for the array |
| 2490 * element associated with a given template instance. | 2498 * element associated with a given template instance. |
| 2491 */ | 2499 */ |
| 2492 as: { | 2500 as: { |
| 2493 type: String, | 2501 type: String, |
| 2494 value: 'item' | 2502 value: 'item' |
| 2495 }, | 2503 }, |
| 2496 | 2504 |
| 2497 /** | 2505 /** |
| 2498 * The name of the variable to add to the binding scope with the index | 2506 * The name of the variable to add to the binding scope with the index |
| 2499 * for the row. | 2507 * for the row. |
| 2500 */ | 2508 */ |
| 2501 indexAs: { | 2509 indexAs: { |
| 2502 type: String, | 2510 type: String, |
| 2503 value: 'index' | 2511 value: 'index' |
| 2504 }, | 2512 }, |
| 2505 | 2513 |
| 2506 /** | 2514 /** |
| 2507 * The name of the variable to add to the binding scope to indicate | 2515 * The name of the variable to add to the binding scope to indicate |
| 2508 * if the row is selected. | 2516 * if the row is selected. |
| 2509 */ | 2517 */ |
| 2510 selectedAs: { | 2518 selectedAs: { |
| 2511 type: String, | 2519 type: String, |
| 2512 value: 'selected' | 2520 value: 'selected' |
| 2513 }, | 2521 }, |
| 2514 | 2522 |
| 2515 /** | 2523 /** |
| 2524 * When true, the list is rendered as a grid. Grid items must have |
| 2525 * fixed width and height set via CSS. e.g. |
| 2526 * |
| 2527 * ```html |
| 2528 * <iron-list grid> |
| 2529 * <template> |
| 2530 * <div style="width: 100px; height: 100px;"> 100x100 </div> |
| 2531 * </template> |
| 2532 * </iron-list> |
| 2533 * ``` |
| 2534 */ |
| 2535 grid: { |
| 2536 type: Boolean, |
| 2537 value: false, |
| 2538 reflectToAttribute: true |
| 2539 }, |
| 2540 |
| 2541 /** |
| 2516 * When true, tapping a row will select the item, placing its data model | 2542 * When true, tapping a row will select the item, placing its data model |
| 2517 * in the set of selected items retrievable via the selection property. | 2543 * in the set of selected items retrievable via the selection property. |
| 2518 * | 2544 * |
| 2519 * Note that tapping focusable elements within the list item will not | 2545 * Note that tapping focusable elements within the list item will not |
| 2520 * result in selection, since they are presumed to have their * own action
. | 2546 * result in selection, since they are presumed to have their * own action
. |
| 2521 */ | 2547 */ |
| 2522 selectionEnabled: { | 2548 selectionEnabled: { |
| 2523 type: Boolean, | 2549 type: Boolean, |
| 2524 value: false | 2550 value: false |
| 2525 }, | 2551 }, |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2625 _estScrollHeight: 0, | 2651 _estScrollHeight: 0, |
| 2626 | 2652 |
| 2627 /** | 2653 /** |
| 2628 * The scroll height of the dom node | 2654 * The scroll height of the dom node |
| 2629 */ | 2655 */ |
| 2630 _scrollHeight: 0, | 2656 _scrollHeight: 0, |
| 2631 | 2657 |
| 2632 /** | 2658 /** |
| 2633 * The height of the list. This is referred as the viewport in the context o
f list. | 2659 * The height of the list. This is referred as the viewport in the context o
f list. |
| 2634 */ | 2660 */ |
| 2635 _viewportSize: 0, | 2661 _viewportHeight: 0, |
| 2662 |
| 2663 /** |
| 2664 * The width of the list. This is referred as the viewport in the context of
list. |
| 2665 */ |
| 2666 _viewportWidth: 0, |
| 2636 | 2667 |
| 2637 /** | 2668 /** |
| 2638 * An array of DOM nodes that are currently in the tree | 2669 * An array of DOM nodes that are currently in the tree |
| 2639 * @type {?Array<!TemplatizerNode>} | 2670 * @type {?Array<!TemplatizerNode>} |
| 2640 */ | 2671 */ |
| 2641 _physicalItems: null, | 2672 _physicalItems: null, |
| 2642 | 2673 |
| 2643 /** | 2674 /** |
| 2644 * An array of heights for each item in `_physicalItems` | 2675 * An array of heights for each item in `_physicalItems` |
| 2645 * @type {?Array<number>} | 2676 * @type {?Array<number>} |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2698 */ | 2729 */ |
| 2699 _offscreenFocusedItem: null, | 2730 _offscreenFocusedItem: null, |
| 2700 | 2731 |
| 2701 /** | 2732 /** |
| 2702 * The item that backfills the `_offscreenFocusedItem` in the physical items | 2733 * The item that backfills the `_offscreenFocusedItem` in the physical items |
| 2703 * list when that item is moved offscreen. | 2734 * list when that item is moved offscreen. |
| 2704 */ | 2735 */ |
| 2705 _focusBackfillItem: null, | 2736 _focusBackfillItem: null, |
| 2706 | 2737 |
| 2707 /** | 2738 /** |
| 2739 * The maximum items per row |
| 2740 */ |
| 2741 _itemsPerRow: 1, |
| 2742 |
| 2743 /** |
| 2744 * The width of each grid item |
| 2745 */ |
| 2746 _itemWidth: 0, |
| 2747 |
| 2748 /** |
| 2749 * The height of the row in grid layout. |
| 2750 */ |
| 2751 _rowHeight: 0, |
| 2752 |
| 2753 /** |
| 2708 * The bottom of the physical content. | 2754 * The bottom of the physical content. |
| 2709 */ | 2755 */ |
| 2710 get _physicalBottom() { | 2756 get _physicalBottom() { |
| 2711 return this._physicalTop + this._physicalSize; | 2757 return this._physicalTop + this._physicalSize; |
| 2712 }, | 2758 }, |
| 2713 | 2759 |
| 2714 /** | 2760 /** |
| 2715 * The bottom of the scroll. | 2761 * The bottom of the scroll. |
| 2716 */ | 2762 */ |
| 2717 get _scrollBottom() { | 2763 get _scrollBottom() { |
| 2718 return this._scrollPosition + this._viewportSize; | 2764 return this._scrollPosition + this._viewportHeight; |
| 2719 }, | 2765 }, |
| 2720 | 2766 |
| 2721 /** | 2767 /** |
| 2722 * The n-th item rendered in the last physical item. | 2768 * The n-th item rendered in the last physical item. |
| 2723 */ | 2769 */ |
| 2724 get _virtualEnd() { | 2770 get _virtualEnd() { |
| 2725 return this._virtualStart + this._physicalCount - 1; | 2771 return this._virtualStart + this._physicalCount - 1; |
| 2726 }, | 2772 }, |
| 2727 | 2773 |
| 2728 /** | 2774 /** |
| 2729 * The height of the physical content that isn't on the screen. | 2775 * The height of the physical content that isn't on the screen. |
| 2730 */ | 2776 */ |
| 2731 get _hiddenContentSize() { | 2777 get _hiddenContentSize() { |
| 2732 return this._physicalSize - this._viewportSize; | 2778 var size = this.grid ? this._physicalRows * this._rowHeight : this._physic
alSize; |
| 2779 return size - this._viewportHeight; |
| 2733 }, | 2780 }, |
| 2734 | 2781 |
| 2735 /** | 2782 /** |
| 2736 * The maximum scroll top value. | 2783 * The maximum scroll top value. |
| 2737 */ | 2784 */ |
| 2738 get _maxScrollTop() { | 2785 get _maxScrollTop() { |
| 2739 return this._estScrollHeight - this._viewportSize + this._scrollerPaddingT
op; | 2786 return this._estScrollHeight - this._viewportHeight + this._scrollerPaddin
gTop; |
| 2740 }, | 2787 }, |
| 2741 | 2788 |
| 2742 /** | 2789 /** |
| 2743 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. | 2790 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. |
| 2744 */ | 2791 */ |
| 2745 _minVirtualStart: 0, | 2792 _minVirtualStart: 0, |
| 2746 | 2793 |
| 2747 /** | 2794 /** |
| 2748 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. | 2795 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. |
| 2749 */ | 2796 */ |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2801 _physicalEnd: 0, | 2848 _physicalEnd: 0, |
| 2802 | 2849 |
| 2803 /** | 2850 /** |
| 2804 * An optimal physical size such that we will have enough physical items | 2851 * An optimal physical size such that we will have enough physical items |
| 2805 * to fill up the viewport and recycle when the user scrolls. | 2852 * to fill up the viewport and recycle when the user scrolls. |
| 2806 * | 2853 * |
| 2807 * This default value assumes that we will at least have the equivalent | 2854 * This default value assumes that we will at least have the equivalent |
| 2808 * to a viewport of physical items above and below the user's viewport. | 2855 * to a viewport of physical items above and below the user's viewport. |
| 2809 */ | 2856 */ |
| 2810 get _optPhysicalSize() { | 2857 get _optPhysicalSize() { |
| 2811 return this._viewportSize * this._maxPages; | 2858 if (this.grid) { |
| 2859 return this._estRowsInView * this._rowHeight * this._maxPages; |
| 2860 } |
| 2861 return this._viewportHeight * this._maxPages; |
| 2862 }, |
| 2863 |
| 2864 get _optPhysicalCount() { |
| 2865 return this._estRowsInView * this._itemsPerRow * this._maxPages; |
| 2812 }, | 2866 }, |
| 2813 | 2867 |
| 2814 /** | 2868 /** |
| 2815 * True if the current list is visible. | 2869 * True if the current list is visible. |
| 2816 */ | 2870 */ |
| 2817 get _isVisible() { | 2871 get _isVisible() { |
| 2818 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); | 2872 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); |
| 2819 }, | 2873 }, |
| 2820 | 2874 |
| 2821 /** | 2875 /** |
| 2822 * Gets the index of the first visible item in the viewport. | 2876 * Gets the index of the first visible item in the viewport. |
| 2823 * | 2877 * |
| 2824 * @type {number} | 2878 * @type {number} |
| 2825 */ | 2879 */ |
| 2826 get firstVisibleIndex() { | 2880 get firstVisibleIndex() { |
| 2827 if (this._firstVisibleIndexVal === null) { | 2881 if (this._firstVisibleIndexVal === null) { |
| 2828 var physicalOffset = this._physicalTop + this._scrollerPaddingTop; | 2882 var physicalOffset = Math.floor(this._physicalTop + this._scrollerPaddin
gTop); |
| 2829 | 2883 |
| 2830 this._firstVisibleIndexVal = this._iterateItems( | 2884 this._firstVisibleIndexVal = this._iterateItems( |
| 2831 function(pidx, vidx) { | 2885 function(pidx, vidx) { |
| 2832 physicalOffset += this._physicalSizes[pidx]; | 2886 physicalOffset += this._getPhysicalSizeIncrement(pidx); |
| 2887 |
| 2833 if (physicalOffset > this._scrollPosition) { | 2888 if (physicalOffset > this._scrollPosition) { |
| 2834 return vidx; | 2889 return this.grid ? vidx - (vidx % this._itemsPerRow) : vidx; |
| 2890 } |
| 2891 |
| 2892 // Handle a partially rendered final row in grid mode |
| 2893 if (this.grid && this._virtualCount - 1 === vidx) { |
| 2894 return vidx - (vidx % this._itemsPerRow); |
| 2835 } | 2895 } |
| 2836 }) || 0; | 2896 }) || 0; |
| 2837 } | 2897 } |
| 2838 return this._firstVisibleIndexVal; | 2898 return this._firstVisibleIndexVal; |
| 2839 }, | 2899 }, |
| 2840 | 2900 |
| 2841 /** | 2901 /** |
| 2842 * Gets the index of the last visible item in the viewport. | 2902 * Gets the index of the last visible item in the viewport. |
| 2843 * | 2903 * |
| 2844 * @type {number} | 2904 * @type {number} |
| 2845 */ | 2905 */ |
| 2846 get lastVisibleIndex() { | 2906 get lastVisibleIndex() { |
| 2847 if (this._lastVisibleIndexVal === null) { | 2907 if (this._lastVisibleIndexVal === null) { |
| 2848 var physicalOffset = this._physicalTop; | 2908 if (this.grid) { |
| 2909 var lastIndex = this.firstVisibleIndex + this._estRowsInView * this._i
temsPerRow - 1; |
| 2910 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? this._vir
tualCount : lastIndex; |
| 2911 } else { |
| 2912 var physicalOffset = this._physicalTop; |
| 2849 | 2913 |
| 2850 this._iterateItems(function(pidx, vidx) { | 2914 this._iterateItems(function(pidx, vidx) { |
| 2851 physicalOffset += this._physicalSizes[pidx]; | 2915 physicalOffset += this._getPhysicalSizeIncrement(pidx); |
| 2852 | 2916 |
| 2853 if (physicalOffset <= this._scrollBottom) { | 2917 if(physicalOffset <= this._scrollBottom) { |
| 2854 this._lastVisibleIndexVal = vidx; | 2918 if (this.grid) { |
| 2855 } | 2919 var lastIndex = vidx - vidx % this._itemsPerRow + this._itemsPer
Row - 1; |
| 2856 }); | 2920 this._lastVisibleIndexVal = lastIndex > this._virtualCount ? thi
s._virtualCount : lastIndex; |
| 2921 } else { |
| 2922 this._lastVisibleIndexVal = vidx; |
| 2923 } |
| 2924 } |
| 2925 }); |
| 2926 } |
| 2857 } | 2927 } |
| 2858 return this._lastVisibleIndexVal; | 2928 return this._lastVisibleIndexVal; |
| 2859 }, | 2929 }, |
| 2860 | 2930 |
| 2861 get _defaultScrollTarget() { | 2931 get _defaultScrollTarget() { |
| 2862 return this; | 2932 return this; |
| 2863 }, | 2933 }, |
| 2934 get _virtualRowCount() { |
| 2935 return Math.ceil(this._virtualCount / this._itemsPerRow); |
| 2936 }, |
| 2937 |
| 2938 get _estRowsInView() { |
| 2939 return Math.ceil(this._viewportHeight / this._rowHeight); |
| 2940 }, |
| 2941 |
| 2942 get _physicalRows() { |
| 2943 return Math.ceil(this._physicalCount / this._itemsPerRow); |
| 2944 }, |
| 2864 | 2945 |
| 2865 ready: function() { | 2946 ready: function() { |
| 2866 this.addEventListener('focus', this._didFocus.bind(this), true); | 2947 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 2867 }, | 2948 }, |
| 2868 | 2949 |
| 2869 attached: function() { | 2950 attached: function() { |
| 2870 this.updateViewportBoundaries(); | 2951 this.updateViewportBoundaries(); |
| 2871 this._render(); | 2952 this._render(); |
| 2872 // `iron-resize` is fired when the list is attached if the event is added | 2953 // `iron-resize` is fired when the list is attached if the event is added |
| 2873 // before attached causing unnecessary work. | 2954 // before attached causing unnecessary work. |
| (...skipping 16 matching lines...) Expand all Loading... |
| 2890 /** | 2971 /** |
| 2891 * Invoke this method if you dynamically update the viewport's | 2972 * Invoke this method if you dynamically update the viewport's |
| 2892 * size or CSS padding. | 2973 * size or CSS padding. |
| 2893 * | 2974 * |
| 2894 * @method updateViewportBoundaries | 2975 * @method updateViewportBoundaries |
| 2895 */ | 2976 */ |
| 2896 updateViewportBoundaries: function() { | 2977 updateViewportBoundaries: function() { |
| 2897 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : | 2978 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : |
| 2898 parseInt(window.getComputedStyle(this)['padding-top'], 10); | 2979 parseInt(window.getComputedStyle(this)['padding-top'], 10); |
| 2899 | 2980 |
| 2900 this._viewportSize = this._scrollTargetHeight; | 2981 this._viewportHeight = this._scrollTargetHeight; |
| 2982 if (this.grid) { |
| 2983 this._updateGridMetrics(); |
| 2984 } |
| 2901 }, | 2985 }, |
| 2902 | 2986 |
| 2903 /** | 2987 /** |
| 2904 * Update the models, the position of the | 2988 * Update the models, the position of the |
| 2905 * items in the viewport and recycle tiles as needed. | 2989 * items in the viewport and recycle tiles as needed. |
| 2906 */ | 2990 */ |
| 2907 _scrollHandler: function() { | 2991 _scrollHandler: function() { |
| 2908 // clamp the `scrollTop` value | 2992 // clamp the `scrollTop` value |
| 2909 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; | 2993 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; |
| 2910 var delta = scrollTop - this._scrollPosition; | 2994 var delta = scrollTop - this._scrollPosition; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2942 | 3026 |
| 2943 // move tiles from bottom to top | 3027 // move tiles from bottom to top |
| 2944 while ( | 3028 while ( |
| 2945 // approximate `currentRatio` to `ratio` | 3029 // approximate `currentRatio` to `ratio` |
| 2946 currentRatio < ratio && | 3030 currentRatio < ratio && |
| 2947 // recycle less physical items than the total | 3031 // recycle less physical items than the total |
| 2948 recycledTiles < this._physicalCount && | 3032 recycledTiles < this._physicalCount && |
| 2949 // ensure that these recycled tiles are needed | 3033 // ensure that these recycled tiles are needed |
| 2950 virtualStart - recycledTiles > 0 && | 3034 virtualStart - recycledTiles > 0 && |
| 2951 // ensure that the tile is not visible | 3035 // ensure that the tile is not visible |
| 2952 physicalBottom - this._physicalSizes[kth] > scrollBottom | 3036 physicalBottom - this._getPhysicalSizeIncrement(kth) > scrollBottom |
| 2953 ) { | 3037 ) { |
| 2954 | 3038 |
| 2955 tileHeight = this._physicalSizes[kth]; | 3039 tileHeight = this._getPhysicalSizeIncrement(kth); |
| 2956 currentRatio += tileHeight / hiddenContentSize; | 3040 currentRatio += tileHeight / hiddenContentSize; |
| 2957 physicalBottom -= tileHeight; | 3041 physicalBottom -= tileHeight; |
| 2958 recycledTileSet.push(kth); | 3042 recycledTileSet.push(kth); |
| 2959 recycledTiles++; | 3043 recycledTiles++; |
| 2960 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; | 3044 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; |
| 2961 } | 3045 } |
| 2962 | 3046 |
| 2963 movingUp = recycledTileSet; | 3047 movingUp = recycledTileSet; |
| 2964 recycledTiles = -recycledTiles; | 3048 recycledTiles = -recycledTiles; |
| 2965 } | 3049 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2976 | 3060 |
| 2977 // move tiles from top to bottom | 3061 // move tiles from top to bottom |
| 2978 while ( | 3062 while ( |
| 2979 // approximate `currentRatio` to `ratio` | 3063 // approximate `currentRatio` to `ratio` |
| 2980 currentRatio < ratio && | 3064 currentRatio < ratio && |
| 2981 // recycle less physical items than the total | 3065 // recycle less physical items than the total |
| 2982 recycledTiles < this._physicalCount && | 3066 recycledTiles < this._physicalCount && |
| 2983 // ensure that these recycled tiles are needed | 3067 // ensure that these recycled tiles are needed |
| 2984 virtualEnd + recycledTiles < lastVirtualItemIndex && | 3068 virtualEnd + recycledTiles < lastVirtualItemIndex && |
| 2985 // ensure that the tile is not visible | 3069 // ensure that the tile is not visible |
| 2986 this._physicalTop + this._physicalSizes[kth] < scrollTop | 3070 this._physicalTop + this._getPhysicalSizeIncrement(kth) < scrollTop |
| 2987 ) { | 3071 ) { |
| 2988 | 3072 |
| 2989 tileHeight = this._physicalSizes[kth]; | 3073 tileHeight = this._getPhysicalSizeIncrement(kth); |
| 2990 currentRatio += tileHeight / hiddenContentSize; | 3074 currentRatio += tileHeight / hiddenContentSize; |
| 2991 | 3075 |
| 2992 this._physicalTop += tileHeight; | 3076 this._physicalTop += tileHeight; |
| 2993 recycledTileSet.push(kth); | 3077 recycledTileSet.push(kth); |
| 2994 recycledTiles++; | 3078 recycledTiles++; |
| 2995 kth = (kth + 1) % this._physicalCount; | 3079 kth = (kth + 1) % this._physicalCount; |
| 2996 } | 3080 } |
| 2997 } | 3081 } |
| 2998 | 3082 |
| 2999 if (recycledTiles === 0) { | 3083 if (recycledTiles === 0) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 3016 _update: function(itemSet, movingUp) { | 3100 _update: function(itemSet, movingUp) { |
| 3017 // manage focus | 3101 // manage focus |
| 3018 this._manageFocus(); | 3102 this._manageFocus(); |
| 3019 // update models | 3103 // update models |
| 3020 this._assignModels(itemSet); | 3104 this._assignModels(itemSet); |
| 3021 // measure heights | 3105 // measure heights |
| 3022 this._updateMetrics(itemSet); | 3106 this._updateMetrics(itemSet); |
| 3023 // adjust offset after measuring | 3107 // adjust offset after measuring |
| 3024 if (movingUp) { | 3108 if (movingUp) { |
| 3025 while (movingUp.length) { | 3109 while (movingUp.length) { |
| 3026 this._physicalTop -= this._physicalSizes[movingUp.pop()]; | 3110 var idx = movingUp.pop(); |
| 3111 this._physicalTop -= this._getPhysicalSizeIncrement(idx); |
| 3027 } | 3112 } |
| 3028 } | 3113 } |
| 3029 // update the position of the items | 3114 // update the position of the items |
| 3030 this._positionItems(); | 3115 this._positionItems(); |
| 3031 // set the scroller size | 3116 // set the scroller size |
| 3032 this._updateScrollerSize(); | 3117 this._updateScrollerSize(); |
| 3033 // increase the pool of physical items | 3118 // increase the pool of physical items |
| 3034 this._increasePoolIfNeeded(); | 3119 this._increasePoolIfNeeded(); |
| 3035 }, | 3120 }, |
| 3036 | 3121 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 3051 } | 3136 } |
| 3052 return physicalItems; | 3137 return physicalItems; |
| 3053 }, | 3138 }, |
| 3054 | 3139 |
| 3055 /** | 3140 /** |
| 3056 * Increases the pool of physical items only if needed. | 3141 * Increases the pool of physical items only if needed. |
| 3057 * | 3142 * |
| 3058 * @return {boolean} True if the pool was increased. | 3143 * @return {boolean} True if the pool was increased. |
| 3059 */ | 3144 */ |
| 3060 _increasePoolIfNeeded: function() { | 3145 _increasePoolIfNeeded: function() { |
| 3061 // Base case 1: the list has no size. | 3146 // Base case 1: the list has no height. |
| 3062 if (this._viewportSize === 0) { | 3147 if (this._viewportHeight === 0) { |
| 3063 return false; | 3148 return false; |
| 3064 } | 3149 } |
| 3065 // Base case 2: If the physical size is optimal and the list's client heig
ht is full | 3150 // Base case 2: If the physical size is optimal and the list's client heig
ht is full |
| 3066 // with physical items, don't increase the pool. | 3151 // with physical items, don't increase the pool. |
| 3067 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi
s._physicalTop <= this._scrollPosition; | 3152 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi
s._physicalTop <= this._scrollPosition; |
| 3068 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { | 3153 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { |
| 3069 return false; | 3154 return false; |
| 3070 } | 3155 } |
| 3071 // this value should range between [0 <= `currentPage` <= `_maxPages`] | 3156 // this value should range between [0 <= `currentPage` <= `_maxPages`] |
| 3072 var currentPage = Math.floor(this._physicalSize / this._viewportSize); | 3157 var currentPage = Math.floor(this._physicalSize / this._viewportHeight); |
| 3073 | 3158 |
| 3074 if (currentPage === 0) { | 3159 if (currentPage === 0) { |
| 3075 // fill the first page | 3160 // fill the first page |
| 3076 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); | 3161 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); |
| 3077 } else if (this._lastPage !== currentPage && isClientHeightFull) { | 3162 } else if (this._lastPage !== currentPage && isClientHeightFull) { |
| 3078 // paint the page and defer the next increase | 3163 // paint the page and defer the next increase |
| 3079 // wait 16ms which is rough enough to get paint cycle. | 3164 // wait 16ms which is rough enough to get paint cycle. |
| 3080 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, 1), 16)); | 3165 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, this._itemsPerRow), 16)); |
| 3081 } else { | 3166 } else { |
| 3082 // fill the rest of the pages | 3167 // fill the rest of the pages |
| 3083 this._debounceTemplate(this._increasePool.bind(this, 1)); | 3168 this._debounceTemplate(this._increasePool.bind(this, this._itemsPerRow))
; |
| 3084 } | 3169 } |
| 3085 | 3170 |
| 3086 this._lastPage = currentPage; | 3171 this._lastPage = currentPage; |
| 3087 | 3172 |
| 3088 return true; | 3173 return true; |
| 3089 }, | 3174 }, |
| 3090 | 3175 |
| 3091 /** | 3176 /** |
| 3092 * Increases the pool size. | 3177 * Increases the pool size. |
| 3093 */ | 3178 */ |
| 3094 _increasePool: function(missingItems) { | 3179 _increasePool: function(missingItems) { |
| 3095 var nextPhysicalCount = Math.min( | 3180 var nextPhysicalCount = Math.min( |
| 3096 this._physicalCount + missingItems, | 3181 this._physicalCount + missingItems, |
| 3097 this._virtualCount - this._virtualStart, | 3182 this._virtualCount - this._virtualStart, |
| 3098 MAX_PHYSICAL_COUNT | 3183 Math.max(this.maxPhysicalCount, DEFAULT_PHYSICAL_COUNT) |
| 3099 ); | 3184 ); |
| 3100 var prevPhysicalCount = this._physicalCount; | 3185 var prevPhysicalCount = this._physicalCount; |
| 3101 var delta = nextPhysicalCount - prevPhysicalCount; | 3186 var delta = nextPhysicalCount - prevPhysicalCount; |
| 3102 | 3187 |
| 3103 if (delta <= 0) { | 3188 if (delta <= 0) { |
| 3104 return; | 3189 return; |
| 3105 } | 3190 } |
| 3106 | 3191 |
| 3107 [].push.apply(this._physicalItems, this._createPool(delta)); | 3192 [].push.apply(this._physicalItems, this._createPool(delta)); |
| 3108 [].push.apply(this._physicalSizes, new Array(delta)); | 3193 [].push.apply(this._physicalSizes, new Array(delta)); |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3313 * | 3398 * |
| 3314 * @param {!function(number, number)} fn | 3399 * @param {!function(number, number)} fn |
| 3315 * @param {!Array<number>=} itemSet | 3400 * @param {!Array<number>=} itemSet |
| 3316 */ | 3401 */ |
| 3317 _iterateItems: function(fn, itemSet) { | 3402 _iterateItems: function(fn, itemSet) { |
| 3318 var pidx, vidx, rtn, i; | 3403 var pidx, vidx, rtn, i; |
| 3319 | 3404 |
| 3320 if (arguments.length === 2 && itemSet) { | 3405 if (arguments.length === 2 && itemSet) { |
| 3321 for (i = 0; i < itemSet.length; i++) { | 3406 for (i = 0; i < itemSet.length; i++) { |
| 3322 pidx = itemSet[i]; | 3407 pidx = itemSet[i]; |
| 3323 if (pidx >= this._physicalStart) { | 3408 vidx = this._computeVidx(pidx); |
| 3324 vidx = this._virtualStart + (pidx - this._physicalStart); | |
| 3325 } else { | |
| 3326 vidx = this._virtualStart + (this._physicalCount - this._physicalSta
rt) + pidx; | |
| 3327 } | |
| 3328 if ((rtn = fn.call(this, pidx, vidx)) != null) { | 3409 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 3329 return rtn; | 3410 return rtn; |
| 3330 } | 3411 } |
| 3331 } | 3412 } |
| 3332 } else { | 3413 } else { |
| 3333 pidx = this._physicalStart; | 3414 pidx = this._physicalStart; |
| 3334 vidx = this._virtualStart; | 3415 vidx = this._virtualStart; |
| 3335 | 3416 |
| 3336 for (; pidx < this._physicalCount; pidx++, vidx++) { | 3417 for (; pidx < this._physicalCount; pidx++, vidx++) { |
| 3337 if ((rtn = fn.call(this, pidx, vidx)) != null) { | 3418 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 3338 return rtn; | 3419 return rtn; |
| 3339 } | 3420 } |
| 3340 } | 3421 } |
| 3341 for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) { | 3422 for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) { |
| 3342 if ((rtn = fn.call(this, pidx, vidx)) != null) { | 3423 if ((rtn = fn.call(this, pidx, vidx)) != null) { |
| 3343 return rtn; | 3424 return rtn; |
| 3344 } | 3425 } |
| 3345 } | 3426 } |
| 3346 } | 3427 } |
| 3347 }, | 3428 }, |
| 3348 | 3429 |
| 3349 /** | 3430 /** |
| 3431 * Returns the virtual index for a given physical index |
| 3432 * |
| 3433 * @param {number} pidx Physical index |
| 3434 * @return {number} |
| 3435 */ |
| 3436 _computeVidx: function(pidx) { |
| 3437 if (pidx >= this._physicalStart) { |
| 3438 return this._virtualStart + (pidx - this._physicalStart); |
| 3439 } |
| 3440 return this._virtualStart + (this._physicalCount - this._physicalStart) +
pidx; |
| 3441 }, |
| 3442 |
| 3443 /** |
| 3350 * Assigns the data models to a given set of items. | 3444 * Assigns the data models to a given set of items. |
| 3351 * @param {!Array<number>=} itemSet | 3445 * @param {!Array<number>=} itemSet |
| 3352 */ | 3446 */ |
| 3353 _assignModels: function(itemSet) { | 3447 _assignModels: function(itemSet) { |
| 3354 this._iterateItems(function(pidx, vidx) { | 3448 this._iterateItems(function(pidx, vidx) { |
| 3355 var el = this._physicalItems[pidx]; | 3449 var el = this._physicalItems[pidx]; |
| 3356 var inst = el._templateInstance; | 3450 var inst = el._templateInstance; |
| 3357 var item = this.items && this.items[vidx]; | 3451 var item = this.items && this.items[vidx]; |
| 3358 | 3452 |
| 3359 if (item != null) { | 3453 if (item != null) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 3388 | 3482 |
| 3389 this._iterateItems(function(pidx, vidx) { | 3483 this._iterateItems(function(pidx, vidx) { |
| 3390 | 3484 |
| 3391 oldPhysicalSize += this._physicalSizes[pidx] || 0; | 3485 oldPhysicalSize += this._physicalSizes[pidx] || 0; |
| 3392 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; | 3486 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight; |
| 3393 newPhysicalSize += this._physicalSizes[pidx]; | 3487 newPhysicalSize += this._physicalSizes[pidx]; |
| 3394 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; | 3488 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0; |
| 3395 | 3489 |
| 3396 }, itemSet); | 3490 }, itemSet); |
| 3397 | 3491 |
| 3398 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSiz
e; | 3492 this._viewportHeight = this._scrollTargetHeight; |
| 3399 this._viewportSize = this._scrollTargetHeight; | 3493 if (this.grid) { |
| 3494 this._updateGridMetrics(); |
| 3495 this._physicalSize = Math.ceil(this._physicalCount / this._itemsPerRow)
* this._rowHeight; |
| 3496 } else { |
| 3497 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalS
ize; |
| 3498 } |
| 3400 | 3499 |
| 3401 // update the average if we measured something | 3500 // update the average if we measured something |
| 3402 if (this._physicalAverageCount !== prevAvgCount) { | 3501 if (this._physicalAverageCount !== prevAvgCount) { |
| 3403 this._physicalAverage = Math.round( | 3502 this._physicalAverage = Math.round( |
| 3404 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / | 3503 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) / |
| 3405 this._physicalAverageCount); | 3504 this._physicalAverageCount); |
| 3406 } | 3505 } |
| 3407 }, | 3506 }, |
| 3408 | 3507 |
| 3508 _updateGridMetrics: function() { |
| 3509 this._viewportWidth = this._scrollTargetWidth; |
| 3510 // Set item width to the value of the _physicalItems offsetWidth |
| 3511 this._itemWidth = this._physicalCount > 0 ? this._physicalItems[0].offsetW
idth : DEFAULT_GRID_SIZE; |
| 3512 // Set row height to the value of the _physicalItems offsetHeight |
| 3513 this._rowHeight = this._physicalCount > 0 ? this._physicalItems[0].offsetH
eight : DEFAULT_GRID_SIZE; |
| 3514 // If in grid mode compute how many items with exist in each row |
| 3515 this._itemsPerRow = this._itemWidth ? Math.floor(this._viewportWidth / thi
s._itemWidth) : this._itemsPerRow; |
| 3516 }, |
| 3517 |
| 3409 /** | 3518 /** |
| 3410 * Updates the position of the physical items. | 3519 * Updates the position of the physical items. |
| 3411 */ | 3520 */ |
| 3412 _positionItems: function() { | 3521 _positionItems: function() { |
| 3413 this._adjustScrollPosition(); | 3522 this._adjustScrollPosition(); |
| 3414 | 3523 |
| 3415 var y = this._physicalTop; | 3524 var y = this._physicalTop; |
| 3416 | 3525 |
| 3417 this._iterateItems(function(pidx) { | 3526 if (this.grid) { |
| 3418 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); | 3527 var totalItemWidth = this._itemsPerRow * this._itemWidth; |
| 3419 y += this._physicalSizes[pidx]; | 3528 var rowOffset = (this._viewportWidth - totalItemWidth) / 2; |
| 3420 }); | 3529 |
| 3530 this._iterateItems(function(pidx, vidx) { |
| 3531 |
| 3532 var modulus = vidx % this._itemsPerRow; |
| 3533 var x = Math.floor((modulus * this._itemWidth) + rowOffset); |
| 3534 |
| 3535 this.translate3d(x + 'px', y + 'px', 0, this._physicalItems[pidx]); |
| 3536 |
| 3537 if (this._shouldRenderNextRow(vidx)) { |
| 3538 y += this._rowHeight; |
| 3539 } |
| 3540 |
| 3541 }); |
| 3542 } else { |
| 3543 this._iterateItems(function(pidx, vidx) { |
| 3544 |
| 3545 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); |
| 3546 y += this._physicalSizes[pidx]; |
| 3547 |
| 3548 }); |
| 3549 } |
| 3550 }, |
| 3551 |
| 3552 _getPhysicalSizeIncrement: function(pidx) { |
| 3553 if (!this.grid) { |
| 3554 return this._physicalSizes[pidx]; |
| 3555 } |
| 3556 if (this._computeVidx(pidx) % this._itemsPerRow !== this._itemsPerRow - 1)
{ |
| 3557 return 0; |
| 3558 } |
| 3559 return this._rowHeight; |
| 3421 }, | 3560 }, |
| 3422 | 3561 |
| 3423 /** | 3562 /** |
| 3563 * Returns, based on the current index, |
| 3564 * whether or not the next index will need |
| 3565 * to be rendered on a new row. |
| 3566 * |
| 3567 * @param {number} vidx Virtual index |
| 3568 * @return {boolean} |
| 3569 */ |
| 3570 _shouldRenderNextRow: function(vidx) { |
| 3571 return vidx % this._itemsPerRow === this._itemsPerRow - 1; |
| 3572 }, |
| 3573 |
| 3574 /** |
| 3424 * Adjusts the scroll position when it was overestimated. | 3575 * Adjusts the scroll position when it was overestimated. |
| 3425 */ | 3576 */ |
| 3426 _adjustScrollPosition: function() { | 3577 _adjustScrollPosition: function() { |
| 3427 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : | 3578 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : |
| 3428 Math.min(this._scrollPosition + this._physicalTop, 0); | 3579 Math.min(this._scrollPosition + this._physicalTop, 0); |
| 3429 | 3580 |
| 3430 if (deltaHeight) { | 3581 if (deltaHeight) { |
| 3431 this._physicalTop = this._physicalTop - deltaHeight; | 3582 this._physicalTop = this._physicalTop - deltaHeight; |
| 3432 // juking scroll position during interial scrolling on iOS is no bueno | 3583 // juking scroll position during interial scrolling on iOS is no bueno |
| 3433 if (!IOS_TOUCH_SCROLLING) { | 3584 if (!IOS_TOUCH_SCROLLING) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 3445 this._scrollPosition = this._scrollTop; | 3596 this._scrollPosition = this._scrollTop; |
| 3446 } | 3597 } |
| 3447 }, | 3598 }, |
| 3448 | 3599 |
| 3449 /** | 3600 /** |
| 3450 * Sets the scroll height, that's the height of the content, | 3601 * Sets the scroll height, that's the height of the content, |
| 3451 * | 3602 * |
| 3452 * @param {boolean=} forceUpdate If true, updates the height no matter what. | 3603 * @param {boolean=} forceUpdate If true, updates the height no matter what. |
| 3453 */ | 3604 */ |
| 3454 _updateScrollerSize: function(forceUpdate) { | 3605 _updateScrollerSize: function(forceUpdate) { |
| 3455 this._estScrollHeight = (this._physicalBottom + | 3606 if (this.grid) { |
| 3456 Math.max(this._virtualCount - this._physicalCount - this._virtualStart
, 0) * this._physicalAverage); | 3607 this._estScrollHeight = this._virtualRowCount * this._rowHeight; |
| 3608 } else { |
| 3609 this._estScrollHeight = (this._physicalBottom + |
| 3610 Math.max(this._virtualCount - this._physicalCount - this._virtualSta
rt, 0) * this._physicalAverage); |
| 3611 } |
| 3457 | 3612 |
| 3458 forceUpdate = forceUpdate || this._scrollHeight === 0; | 3613 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 3459 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; | 3614 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 3615 forceUpdate = forceUpdate || this.grid && this.$.items.style.height < this
._estScrollHeight; |
| 3460 | 3616 |
| 3461 // amortize height adjustment, so it won't trigger repaints very often | 3617 // amortize height adjustment, so it won't trigger repaints very often |
| 3462 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { | 3618 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| 3463 this.$.items.style.height = this._estScrollHeight + 'px'; | 3619 this.$.items.style.height = this._estScrollHeight + 'px'; |
| 3464 this._scrollHeight = this._estScrollHeight; | 3620 this._scrollHeight = this._estScrollHeight; |
| 3465 } | 3621 } |
| 3466 }, | 3622 }, |
| 3467 /** | 3623 /** |
| 3468 * Scroll to a specific item in the virtual list regardless | 3624 * Scroll to a specific item in the virtual list regardless |
| 3469 * of the physical items in the DOM tree. | 3625 * of the physical items in the DOM tree. |
| 3470 * | 3626 * |
| 3471 * @method scrollToIndex | 3627 * @method scrollToIndex |
| 3472 * @param {number} idx The index of the item | 3628 * @param {number} idx The index of the item |
| 3473 */ | 3629 */ |
| 3474 scrollToIndex: function(idx) { | 3630 scrollToIndex: function(idx) { |
| 3475 if (typeof idx !== 'number') { | 3631 if (typeof idx !== 'number') { |
| 3476 return; | 3632 return; |
| 3477 } | 3633 } |
| 3478 | 3634 |
| 3479 Polymer.dom.flush(); | 3635 Polymer.dom.flush(); |
| 3480 | 3636 |
| 3481 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); | 3637 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
| 3482 // update the virtual start only when needed | 3638 // update the virtual start only when needed |
| 3483 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { | 3639 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { |
| 3484 this._virtualStart = idx - 1; | 3640 this._virtualStart = this.grid ? (idx - this._itemsPerRow * 2) : (idx -
1); |
| 3485 } | 3641 } |
| 3486 // manage focus | 3642 // manage focus |
| 3487 this._manageFocus(); | 3643 this._manageFocus(); |
| 3488 // assign new models | 3644 // assign new models |
| 3489 this._assignModels(); | 3645 this._assignModels(); |
| 3490 // measure the new sizes | 3646 // measure the new sizes |
| 3491 this._updateMetrics(); | 3647 this._updateMetrics(); |
| 3648 |
| 3492 // estimate new physical offset | 3649 // estimate new physical offset |
| 3493 this._physicalTop = this._virtualStart * this._physicalAverage; | 3650 var estPhysicalTop = Math.floor(this._virtualStart / this._itemsPerRow) *
this._physicalAverage; |
| 3651 this._physicalTop = estPhysicalTop; |
| 3494 | 3652 |
| 3495 var currentTopItem = this._physicalStart; | 3653 var currentTopItem = this._physicalStart; |
| 3496 var currentVirtualItem = this._virtualStart; | 3654 var currentVirtualItem = this._virtualStart; |
| 3497 var targetOffsetTop = 0; | 3655 var targetOffsetTop = 0; |
| 3498 var hiddenContentSize = this._hiddenContentSize; | 3656 var hiddenContentSize = this._hiddenContentSize; |
| 3499 | 3657 |
| 3500 // scroll to the item as much as we can | 3658 // scroll to the item as much as we can |
| 3501 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { | 3659 while (currentVirtualItem < idx && targetOffsetTop <= hiddenContentSize) { |
| 3502 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; | 3660 targetOffsetTop = targetOffsetTop + this._getPhysicalSizeIncrement(curre
ntTopItem); |
| 3503 currentTopItem = (currentTopItem + 1) % this._physicalCount; | 3661 currentTopItem = (currentTopItem + 1) % this._physicalCount; |
| 3504 currentVirtualItem++; | 3662 currentVirtualItem++; |
| 3505 } | 3663 } |
| 3506 // update the scroller size | 3664 // update the scroller size |
| 3507 this._updateScrollerSize(true); | 3665 this._updateScrollerSize(true); |
| 3508 // update the position of the items | 3666 // update the position of the items |
| 3509 this._positionItems(); | 3667 this._positionItems(); |
| 3510 // set the new scroll position | 3668 // set the new scroll position |
| 3511 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop); | 3669 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop); |
| 3512 // increase the pool of physical items if needed | 3670 // increase the pool of physical items if needed |
| (...skipping 10 matching lines...) Expand all Loading... |
| 3523 this._physicalAverage = 0; | 3681 this._physicalAverage = 0; |
| 3524 this._physicalAverageCount = 0; | 3682 this._physicalAverageCount = 0; |
| 3525 }, | 3683 }, |
| 3526 | 3684 |
| 3527 /** | 3685 /** |
| 3528 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` | 3686 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` |
| 3529 * when the element is resized. | 3687 * when the element is resized. |
| 3530 */ | 3688 */ |
| 3531 _resizeHandler: function() { | 3689 _resizeHandler: function() { |
| 3532 // iOS fires the resize event when the address bar slides up | 3690 // iOS fires the resize event when the address bar slides up |
| 3533 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100)
{ | 3691 if (IOS && Math.abs(this._viewportHeight - this._scrollTargetHeight) < 100
) { |
| 3534 return; | 3692 return; |
| 3535 } | 3693 } |
| 3536 // In Desktop Safari 9.0.3, if the scroll bars are always shown, | 3694 // In Desktop Safari 9.0.3, if the scroll bars are always shown, |
| 3537 // changing the scroll position from a resize handler would result in | 3695 // changing the scroll position from a resize handler would result in |
| 3538 // the scroll position being reset. Waiting 1ms fixes the issue. | 3696 // the scroll position being reset. Waiting 1ms fixes the issue. |
| 3539 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', | 3697 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', function() { |
| 3540 function() { | 3698 this.updateViewportBoundaries(); |
| 3541 this._render(); | 3699 this._render(); |
| 3542 | 3700 |
| 3543 if (this._itemsRendered && this._physicalItems && this._isVisible) { | 3701 if (this._itemsRendered && this._physicalItems && this._isVisible) { |
| 3544 this._resetAverage(); | 3702 this._resetAverage(); |
| 3545 this.updateViewportBoundaries(); | 3703 this.scrollToIndex(this.firstVisibleIndex); |
| 3546 this.scrollToIndex(this.firstVisibleIndex); | 3704 } |
| 3547 } | 3705 }.bind(this), 1)); |
| 3548 }.bind(this), 1)); | |
| 3549 }, | 3706 }, |
| 3550 | 3707 |
| 3551 _getModelFromItem: function(item) { | 3708 _getModelFromItem: function(item) { |
| 3552 var key = this._collection.getKey(item); | 3709 var key = this._collection.getKey(item); |
| 3553 var pidx = this._physicalIndexForKey[key]; | 3710 var pidx = this._physicalIndexForKey[key]; |
| 3554 | 3711 |
| 3555 if (pidx != null) { | 3712 if (pidx != null) { |
| 3556 return this._physicalItems[pidx]._templateInstance; | 3713 return this._physicalItems[pidx]._templateInstance; |
| 3557 } | 3714 } |
| 3558 return null; | 3715 return null; |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3849 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { | 4006 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { |
| 3850 this._update(); | 4007 this._update(); |
| 3851 } | 4008 } |
| 3852 } | 4009 } |
| 3853 }, | 4010 }, |
| 3854 | 4011 |
| 3855 _didMoveUp: function() { | 4012 _didMoveUp: function() { |
| 3856 this._focusPhysicalItem(this._focusedIndex - 1); | 4013 this._focusPhysicalItem(this._focusedIndex - 1); |
| 3857 }, | 4014 }, |
| 3858 | 4015 |
| 3859 _didMoveDown: function() { | 4016 _didMoveDown: function(e) { |
| 4017 // disable scroll when pressing the down key |
| 4018 e.detail.keyboardEvent.preventDefault(); |
| 3860 this._focusPhysicalItem(this._focusedIndex + 1); | 4019 this._focusPhysicalItem(this._focusedIndex + 1); |
| 3861 }, | 4020 }, |
| 3862 | 4021 |
| 3863 _didEnter: function(e) { | 4022 _didEnter: function(e) { |
| 3864 this._focusPhysicalItem(this._focusedIndex); | 4023 this._focusPhysicalItem(this._focusedIndex); |
| 3865 this._selectionHandler(e.detail.keyboardEvent); | 4024 this._selectionHandler(e.detail.keyboardEvent); |
| 3866 } | 4025 } |
| 3867 }); | 4026 }); |
| 3868 | 4027 |
| 3869 })(); | 4028 })(); |
| (...skipping 3294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7164 * Given a KeyboardEvent, this method will focus the appropriate item in the | 7323 * Given a KeyboardEvent, this method will focus the appropriate item in the |
| 7165 * menu (if there is a relevant item, and it is possible to focus it). | 7324 * menu (if there is a relevant item, and it is possible to focus it). |
| 7166 * | 7325 * |
| 7167 * @param {KeyboardEvent} event A KeyboardEvent. | 7326 * @param {KeyboardEvent} event A KeyboardEvent. |
| 7168 */ | 7327 */ |
| 7169 _focusWithKeyboardEvent: function(event) { | 7328 _focusWithKeyboardEvent: function(event) { |
| 7170 for (var i = 0, item; item = this.items[i]; i++) { | 7329 for (var i = 0, item; item = this.items[i]; i++) { |
| 7171 var attr = this.attrForItemTitle || 'textContent'; | 7330 var attr = this.attrForItemTitle || 'textContent'; |
| 7172 var title = item[attr] || item.getAttribute(attr); | 7331 var title = item[attr] || item.getAttribute(attr); |
| 7173 | 7332 |
| 7174 if (title && title.trim().charAt(0).toLowerCase() === String.fromCharCod
e(event.keyCode).toLowerCase()) { | 7333 if (!item.hasAttribute('disabled') && title && |
| 7334 title.trim().charAt(0).toLowerCase() === String.fromCharCode(event.k
eyCode).toLowerCase()) { |
| 7175 this._setFocusedItem(item); | 7335 this._setFocusedItem(item); |
| 7176 break; | 7336 break; |
| 7177 } | 7337 } |
| 7178 } | 7338 } |
| 7179 }, | 7339 }, |
| 7180 | 7340 |
| 7181 /** | 7341 /** |
| 7182 * Focuses the previous item (relative to the currently focused item) in the | 7342 * Focuses the previous item (relative to the currently focused item) in the |
| 7183 * menu. | 7343 * menu, disabled items will be skipped. |
| 7184 */ | 7344 */ |
| 7185 _focusPrevious: function() { | 7345 _focusPrevious: function() { |
| 7186 var length = this.items.length; | 7346 var length = this.items.length; |
| 7187 var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length
; | 7347 var curFocusIndex = Number(this.indexOf(this.focusedItem)); |
| 7188 this._setFocusedItem(this.items[index]); | 7348 for (var i = 1; i < length; i++) { |
| 7349 var item = this.items[(curFocusIndex - i + length) % length]; |
| 7350 if (!item.hasAttribute('disabled')) { |
| 7351 this._setFocusedItem(item); |
| 7352 return; |
| 7353 } |
| 7354 } |
| 7189 }, | 7355 }, |
| 7190 | 7356 |
| 7191 /** | 7357 /** |
| 7192 * Focuses the next item (relative to the currently focused item) in the | 7358 * Focuses the next item (relative to the currently focused item) in the |
| 7193 * menu. | 7359 * menu, disabled items will be skipped. |
| 7194 */ | 7360 */ |
| 7195 _focusNext: function() { | 7361 _focusNext: function() { |
| 7196 var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.leng
th; | 7362 var length = this.items.length; |
| 7197 this._setFocusedItem(this.items[index]); | 7363 var curFocusIndex = Number(this.indexOf(this.focusedItem)); |
| 7364 for (var i = 1; i < length; i++) { |
| 7365 var item = this.items[(curFocusIndex + i) % length]; |
| 7366 if (!item.hasAttribute('disabled')) { |
| 7367 this._setFocusedItem(item); |
| 7368 return; |
| 7369 } |
| 7370 } |
| 7198 }, | 7371 }, |
| 7199 | 7372 |
| 7200 /** | 7373 /** |
| 7201 * Mutates items in the menu based on provided selection details, so that | 7374 * Mutates items in the menu based on provided selection details, so that |
| 7202 * all items correctly reflect selection state. | 7375 * all items correctly reflect selection state. |
| 7203 * | 7376 * |
| 7204 * @param {Element} item An item in the menu. | 7377 * @param {Element} item An item in the menu. |
| 7205 * @param {boolean} isSelected True if the item should be shown in a | 7378 * @param {boolean} isSelected True if the item should be shown in a |
| 7206 * selected state, otherwise false. | 7379 * selected state, otherwise false. |
| 7207 */ | 7380 */ |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7296 this._defaultFocusAsync = this.async(function() { | 7469 this._defaultFocusAsync = this.async(function() { |
| 7297 // focus the selected item when the menu receives focus, or the first it
em | 7470 // focus the selected item when the menu receives focus, or the first it
em |
| 7298 // if no item is selected | 7471 // if no item is selected |
| 7299 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | 7472 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; |
| 7300 | 7473 |
| 7301 this._setFocusedItem(null); | 7474 this._setFocusedItem(null); |
| 7302 | 7475 |
| 7303 if (selectedItem) { | 7476 if (selectedItem) { |
| 7304 this._setFocusedItem(selectedItem); | 7477 this._setFocusedItem(selectedItem); |
| 7305 } else if (this.items[0]) { | 7478 } else if (this.items[0]) { |
| 7306 this._setFocusedItem(this.items[0]); | 7479 // We find the first none-disabled item (if one exists) |
| 7480 this._focusNext(); |
| 7307 } | 7481 } |
| 7308 }); | 7482 }); |
| 7309 }, | 7483 }, |
| 7310 | 7484 |
| 7311 /** | 7485 /** |
| 7312 * Handler that is called when the up key is pressed. | 7486 * Handler that is called when the up key is pressed. |
| 7313 * | 7487 * |
| 7314 * @param {CustomEvent} event A key combination event. | 7488 * @param {CustomEvent} event A key combination event. |
| 7315 */ | 7489 */ |
| 7316 _onUpKey: function(event) { | 7490 _onUpKey: function(event) { |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7664 document.addEventListener('focus', this._onCaptureFocus.bind(this), true); | 7838 document.addEventListener('focus', this._onCaptureFocus.bind(this), true); |
| 7665 document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true
); | 7839 document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true
); |
| 7666 }; | 7840 }; |
| 7667 | 7841 |
| 7668 Polymer.IronOverlayManagerClass.prototype = { | 7842 Polymer.IronOverlayManagerClass.prototype = { |
| 7669 | 7843 |
| 7670 constructor: Polymer.IronOverlayManagerClass, | 7844 constructor: Polymer.IronOverlayManagerClass, |
| 7671 | 7845 |
| 7672 /** | 7846 /** |
| 7673 * The shared backdrop element. | 7847 * The shared backdrop element. |
| 7674 * @type {Element} backdropElement | 7848 * @type {!Element} backdropElement |
| 7675 */ | 7849 */ |
| 7676 get backdropElement() { | 7850 get backdropElement() { |
| 7677 if (!this._backdropElement) { | 7851 if (!this._backdropElement) { |
| 7678 this._backdropElement = document.createElement('iron-overlay-backdrop'); | 7852 this._backdropElement = document.createElement('iron-overlay-backdrop'); |
| 7679 } | 7853 } |
| 7680 return this._backdropElement; | 7854 return this._backdropElement; |
| 7681 }, | 7855 }, |
| 7682 | 7856 |
| 7683 /** | 7857 /** |
| 7684 * The deepest active element. | 7858 * The deepest active element. |
| 7685 * @type {Element} activeElement the active element | 7859 * @type {!Element} activeElement the active element |
| 7686 */ | 7860 */ |
| 7687 get deepActiveElement() { | 7861 get deepActiveElement() { |
| 7688 // document.activeElement can be null | 7862 // document.activeElement can be null |
| 7689 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement | 7863 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement |
| 7690 // In case of null, default it to document.body. | 7864 // In case of null, default it to document.body. |
| 7691 var active = document.activeElement || document.body; | 7865 var active = document.activeElement || document.body; |
| 7692 while (active.root && Polymer.dom(active.root).activeElement) { | 7866 while (active.root && Polymer.dom(active.root).activeElement) { |
| 7693 active = Polymer.dom(active.root).activeElement; | 7867 active = Polymer.dom(active.root).activeElement; |
| 7694 } | 7868 } |
| 7695 return active; | 7869 return active; |
| 7696 }, | 7870 }, |
| 7697 | 7871 |
| 7698 /** | 7872 /** |
| 7699 * Brings the overlay at the specified index to the front. | 7873 * Brings the overlay at the specified index to the front. |
| 7700 * @param {number} i | 7874 * @param {number} i |
| 7701 * @private | 7875 * @private |
| 7702 */ | 7876 */ |
| 7703 _bringOverlayAtIndexToFront: function(i) { | 7877 _bringOverlayAtIndexToFront: function(i) { |
| 7704 var overlay = this._overlays[i]; | 7878 var overlay = this._overlays[i]; |
| 7879 if (!overlay) { |
| 7880 return; |
| 7881 } |
| 7705 var lastI = this._overlays.length - 1; | 7882 var lastI = this._overlays.length - 1; |
| 7883 var currentOverlay = this._overlays[lastI]; |
| 7706 // Ensure always-on-top overlay stays on top. | 7884 // Ensure always-on-top overlay stays on top. |
| 7707 if (!overlay.alwaysOnTop && this._overlays[lastI].alwaysOnTop) { | 7885 if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)
) { |
| 7708 lastI--; | 7886 lastI--; |
| 7709 } | 7887 } |
| 7710 // If already the top element, return. | 7888 // If already the top element, return. |
| 7711 if (!overlay || i >= lastI) { | 7889 if (i >= lastI) { |
| 7712 return; | 7890 return; |
| 7713 } | 7891 } |
| 7714 // Update z-index to be on top. | 7892 // Update z-index to be on top. |
| 7715 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | 7893 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
| 7716 if (this._getZ(overlay) <= minimumZ) { | 7894 if (this._getZ(overlay) <= minimumZ) { |
| 7717 this._applyOverlayZ(overlay, minimumZ); | 7895 this._applyOverlayZ(overlay, minimumZ); |
| 7718 } | 7896 } |
| 7719 | 7897 |
| 7720 // Shift other overlays behind the new on top. | 7898 // Shift other overlays behind the new on top. |
| 7721 while (i < lastI) { | 7899 while (i < lastI) { |
| 7722 this._overlays[i] = this._overlays[i + 1]; | 7900 this._overlays[i] = this._overlays[i + 1]; |
| 7723 i++; | 7901 i++; |
| 7724 } | 7902 } |
| 7725 this._overlays[lastI] = overlay; | 7903 this._overlays[lastI] = overlay; |
| 7726 }, | 7904 }, |
| 7727 | 7905 |
| 7728 /** | 7906 /** |
| 7729 * Adds the overlay and updates its z-index if it's opened, or removes it if
it's closed. | 7907 * Adds the overlay and updates its z-index if it's opened, or removes it if
it's closed. |
| 7730 * Also updates the backdrop z-index. | 7908 * Also updates the backdrop z-index. |
| 7731 * @param {Element} overlay | 7909 * @param {!Element} overlay |
| 7732 */ | 7910 */ |
| 7733 addOrRemoveOverlay: function(overlay) { | 7911 addOrRemoveOverlay: function(overlay) { |
| 7734 if (overlay.opened) { | 7912 if (overlay.opened) { |
| 7735 this.addOverlay(overlay); | 7913 this.addOverlay(overlay); |
| 7736 } else { | 7914 } else { |
| 7737 this.removeOverlay(overlay); | 7915 this.removeOverlay(overlay); |
| 7738 } | 7916 } |
| 7739 this.trackBackdrop(); | 7917 this.trackBackdrop(); |
| 7740 }, | 7918 }, |
| 7741 | 7919 |
| 7742 /** | 7920 /** |
| 7743 * Tracks overlays for z-index and focus management. | 7921 * Tracks overlays for z-index and focus management. |
| 7744 * Ensures the last added overlay with always-on-top remains on top. | 7922 * Ensures the last added overlay with always-on-top remains on top. |
| 7745 * @param {Element} overlay | 7923 * @param {!Element} overlay |
| 7746 */ | 7924 */ |
| 7747 addOverlay: function(overlay) { | 7925 addOverlay: function(overlay) { |
| 7748 var i = this._overlays.indexOf(overlay); | 7926 var i = this._overlays.indexOf(overlay); |
| 7749 if (i >= 0) { | 7927 if (i >= 0) { |
| 7750 this._bringOverlayAtIndexToFront(i); | 7928 this._bringOverlayAtIndexToFront(i); |
| 7751 return; | 7929 return; |
| 7752 } | 7930 } |
| 7753 var insertionIndex = this._overlays.length; | 7931 var insertionIndex = this._overlays.length; |
| 7754 var currentOverlay = this._overlays[insertionIndex - 1]; | 7932 var currentOverlay = this._overlays[insertionIndex - 1]; |
| 7755 var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); | 7933 var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); |
| 7756 var newZ = this._getZ(overlay); | 7934 var newZ = this._getZ(overlay); |
| 7757 | 7935 |
| 7758 // Ensure always-on-top overlay stays on top. | 7936 // Ensure always-on-top overlay stays on top. |
| 7759 if (currentOverlay && currentOverlay.alwaysOnTop && !overlay.alwaysOnTop)
{ | 7937 if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)
) { |
| 7760 // This bumps the z-index of +2. | 7938 // This bumps the z-index of +2. |
| 7761 this._applyOverlayZ(currentOverlay, minimumZ); | 7939 this._applyOverlayZ(currentOverlay, minimumZ); |
| 7762 insertionIndex--; | 7940 insertionIndex--; |
| 7763 // Update minimumZ to match previous overlay's z-index. | 7941 // Update minimumZ to match previous overlay's z-index. |
| 7764 var previousOverlay = this._overlays[insertionIndex - 1]; | 7942 var previousOverlay = this._overlays[insertionIndex - 1]; |
| 7765 minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); | 7943 minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); |
| 7766 } | 7944 } |
| 7767 | 7945 |
| 7768 // Update z-index and insert overlay. | 7946 // Update z-index and insert overlay. |
| 7769 if (newZ <= minimumZ) { | 7947 if (newZ <= minimumZ) { |
| 7770 this._applyOverlayZ(overlay, minimumZ); | 7948 this._applyOverlayZ(overlay, minimumZ); |
| 7771 } | 7949 } |
| 7772 this._overlays.splice(insertionIndex, 0, overlay); | 7950 this._overlays.splice(insertionIndex, 0, overlay); |
| 7773 | 7951 |
| 7774 // Get focused node. | 7952 // Get focused node. |
| 7775 var element = this.deepActiveElement; | 7953 var element = this.deepActiveElement; |
| 7776 overlay.restoreFocusNode = this._overlayParent(element) ? null : element; | 7954 overlay.restoreFocusNode = this._overlayParent(element) ? null : element; |
| 7777 }, | 7955 }, |
| 7778 | 7956 |
| 7779 /** | 7957 /** |
| 7780 * @param {Element} overlay | 7958 * @param {!Element} overlay |
| 7781 */ | 7959 */ |
| 7782 removeOverlay: function(overlay) { | 7960 removeOverlay: function(overlay) { |
| 7783 var i = this._overlays.indexOf(overlay); | 7961 var i = this._overlays.indexOf(overlay); |
| 7784 if (i === -1) { | 7962 if (i === -1) { |
| 7785 return; | 7963 return; |
| 7786 } | 7964 } |
| 7787 this._overlays.splice(i, 1); | 7965 this._overlays.splice(i, 1); |
| 7788 | 7966 |
| 7789 var node = overlay.restoreFocusOnClose ? overlay.restoreFocusNode : null; | 7967 var node = overlay.restoreFocusOnClose ? overlay.restoreFocusNode : null; |
| 7790 overlay.restoreFocusNode = null; | 7968 overlay.restoreFocusNode = null; |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7888 // Check if is a number | 8066 // Check if is a number |
| 7889 // Number.isNaN not supported in IE 10+ | 8067 // Number.isNaN not supported in IE 10+ |
| 7890 if (z1 === z1) { | 8068 if (z1 === z1) { |
| 7891 z = z1; | 8069 z = z1; |
| 7892 } | 8070 } |
| 7893 } | 8071 } |
| 7894 return z; | 8072 return z; |
| 7895 }, | 8073 }, |
| 7896 | 8074 |
| 7897 /** | 8075 /** |
| 7898 * @param {Element} element | 8076 * @param {!Element} element |
| 7899 * @param {number|string} z | 8077 * @param {number|string} z |
| 7900 * @private | 8078 * @private |
| 7901 */ | 8079 */ |
| 7902 _setZ: function(element, z) { | 8080 _setZ: function(element, z) { |
| 7903 element.style.zIndex = z; | 8081 element.style.zIndex = z; |
| 7904 }, | 8082 }, |
| 7905 | 8083 |
| 7906 /** | 8084 /** |
| 7907 * @param {Element} overlay | 8085 * @param {!Element} overlay |
| 7908 * @param {number} aboveZ | 8086 * @param {number} aboveZ |
| 7909 * @private | 8087 * @private |
| 7910 */ | 8088 */ |
| 7911 _applyOverlayZ: function(overlay, aboveZ) { | 8089 _applyOverlayZ: function(overlay, aboveZ) { |
| 7912 this._setZ(overlay, aboveZ + 2); | 8090 this._setZ(overlay, aboveZ + 2); |
| 7913 }, | 8091 }, |
| 7914 | 8092 |
| 7915 /** | 8093 /** |
| 7916 * Returns the overlay containing the provided node. If the node is an overl
ay, | 8094 * Returns the overlay containing the provided node. If the node is an overl
ay, |
| 7917 * it returns the node. | 8095 * it returns the node. |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7977 */ | 8155 */ |
| 7978 _onCaptureKeyDown: function(event) { | 8156 _onCaptureKeyDown: function(event) { |
| 7979 var overlay = /** @type {?} */ (this.currentOverlay()); | 8157 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7980 if (overlay) { | 8158 if (overlay) { |
| 7981 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc'))
{ | 8159 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc'))
{ |
| 7982 overlay._onCaptureEsc(event); | 8160 overlay._onCaptureEsc(event); |
| 7983 } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,
'tab')) { | 8161 } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,
'tab')) { |
| 7984 overlay._onCaptureTab(event); | 8162 overlay._onCaptureTab(event); |
| 7985 } | 8163 } |
| 7986 } | 8164 } |
| 8165 }, |
| 8166 |
| 8167 /** |
| 8168 * Returns if the overlay1 should be behind overlay2. |
| 8169 * @param {!Element} overlay1 |
| 8170 * @param {!Element} overlay2 |
| 8171 * @return {boolean} |
| 8172 * @private |
| 8173 */ |
| 8174 _shouldBeBehindOverlay: function(overlay1, overlay2) { |
| 8175 var o1 = /** @type {?} */ (overlay1); |
| 8176 var o2 = /** @type {?} */ (overlay2); |
| 8177 return !o1.alwaysOnTop && o2.alwaysOnTop; |
| 7987 } | 8178 } |
| 7988 }; | 8179 }; |
| 7989 | 8180 |
| 7990 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); | 8181 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); |
| 7991 (function() { | 8182 (function() { |
| 7992 | 8183 |
| 7993 Polymer({ | 8184 Polymer({ |
| 7994 | 8185 |
| 7995 is: 'iron-overlay-backdrop', | 8186 is: 'iron-overlay-backdrop', |
| 7996 | 8187 |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8376 if (!this._overlaySetup) { | 8567 if (!this._overlaySetup) { |
| 8377 return; | 8568 return; |
| 8378 } | 8569 } |
| 8379 | 8570 |
| 8380 this._manager.addOrRemoveOverlay(this); | 8571 this._manager.addOrRemoveOverlay(this); |
| 8381 | 8572 |
| 8382 this.__isAnimating = true; | 8573 this.__isAnimating = true; |
| 8383 | 8574 |
| 8384 // requestAnimationFrame for non-blocking rendering | 8575 // requestAnimationFrame for non-blocking rendering |
| 8385 if (this.__openChangedAsync) { | 8576 if (this.__openChangedAsync) { |
| 8386 cancelAnimationFrame(this.__openChangedAsync); | 8577 window.cancelAnimationFrame(this.__openChangedAsync); |
| 8387 } | 8578 } |
| 8388 if (this.opened) { | 8579 if (this.opened) { |
| 8389 if (this.withBackdrop) { | 8580 if (this.withBackdrop) { |
| 8390 this.backdropElement.prepare(); | 8581 this.backdropElement.prepare(); |
| 8391 } | 8582 } |
| 8392 this.__openChangedAsync = requestAnimationFrame(function() { | 8583 this.__openChangedAsync = window.requestAnimationFrame(function() { |
| 8393 this.__openChangedAsync = null; | 8584 this.__openChangedAsync = null; |
| 8394 this._prepareRenderOpened(); | 8585 this._prepareRenderOpened(); |
| 8395 this._renderOpened(); | 8586 this._renderOpened(); |
| 8396 }.bind(this)); | 8587 }.bind(this)); |
| 8397 } else { | 8588 } else { |
| 8398 this._renderClosed(); | 8589 this._renderClosed(); |
| 8399 } | 8590 } |
| 8400 }, | 8591 }, |
| 8401 | 8592 |
| 8402 _canceledChanged: function() { | 8593 _canceledChanged: function() { |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8593 this._focusedChild = nodeToSet; | 8784 this._focusedChild = nodeToSet; |
| 8594 } | 8785 } |
| 8595 }, | 8786 }, |
| 8596 | 8787 |
| 8597 /** | 8788 /** |
| 8598 * Refits if the overlay is opened and not animating. | 8789 * Refits if the overlay is opened and not animating. |
| 8599 * @protected | 8790 * @protected |
| 8600 */ | 8791 */ |
| 8601 _onIronResize: function() { | 8792 _onIronResize: function() { |
| 8602 if (this.__onIronResizeAsync) { | 8793 if (this.__onIronResizeAsync) { |
| 8603 cancelAnimationFrame(this.__onIronResizeAsync); | 8794 window.cancelAnimationFrame(this.__onIronResizeAsync); |
| 8604 this.__onIronResizeAsync = null; | 8795 this.__onIronResizeAsync = null; |
| 8605 } | 8796 } |
| 8606 if (this.opened && !this.__isAnimating) { | 8797 if (this.opened && !this.__isAnimating) { |
| 8607 this.__onIronResizeAsync = requestAnimationFrame(function() { | 8798 this.__onIronResizeAsync = window.requestAnimationFrame(function() { |
| 8608 this.__onIronResizeAsync = null; | 8799 this.__onIronResizeAsync = null; |
| 8609 this.refit(); | 8800 this.refit(); |
| 8610 }.bind(this)); | 8801 }.bind(this)); |
| 8611 } | 8802 } |
| 8612 }, | 8803 }, |
| 8613 | 8804 |
| 8614 /** | 8805 /** |
| 8615 * Will call notifyResize if overlay is opened. | 8806 * Will call notifyResize if overlay is opened. |
| 8616 * Can be overridden in order to avoid multiple observers on the same node. | 8807 * Can be overridden in order to avoid multiple observers on the same node. |
| 8617 * @protected | 8808 * @protected |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8668 return { | 8859 return { |
| 8669 duration: 500, | 8860 duration: 500, |
| 8670 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', | 8861 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', |
| 8671 fill: 'both' | 8862 fill: 'both' |
| 8672 } | 8863 } |
| 8673 } | 8864 } |
| 8674 } | 8865 } |
| 8675 | 8866 |
| 8676 }, | 8867 }, |
| 8677 | 8868 |
| 8678 registered: function() { | 8869 /** |
| 8679 new Polymer.IronMeta({type: 'animation', key: this.is, value: this.constru
ctor}); | 8870 * Can be used to determine that elements implement this behavior. |
| 8680 }, | 8871 */ |
| 8872 isNeonAnimation: true, |
| 8681 | 8873 |
| 8682 /** | 8874 /** |
| 8683 * Do any animation configuration here. | 8875 * Do any animation configuration here. |
| 8684 */ | 8876 */ |
| 8685 // configure: function(config) { | 8877 // configure: function(config) { |
| 8686 // }, | 8878 // }, |
| 8687 | 8879 |
| 8688 /** | 8880 /** |
| 8689 * Returns the animation timing by mixing in properties from `config` to the
defaults defined | 8881 * Returns the animation timing by mixing in properties from `config` to the
defaults defined |
| 8690 * by the animation. | 8882 * by the animation. |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8889 }; | 9081 }; |
| 8890 /** | 9082 /** |
| 8891 * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. | 9083 * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. |
| 8892 * | 9084 * |
| 8893 * @polymerBehavior Polymer.NeonAnimationRunnerBehavior | 9085 * @polymerBehavior Polymer.NeonAnimationRunnerBehavior |
| 8894 */ | 9086 */ |
| 8895 Polymer.NeonAnimationRunnerBehaviorImpl = { | 9087 Polymer.NeonAnimationRunnerBehaviorImpl = { |
| 8896 | 9088 |
| 8897 properties: { | 9089 properties: { |
| 8898 | 9090 |
| 8899 _animationMeta: { | |
| 8900 type: Object, | |
| 8901 value: function() { | |
| 8902 return new Polymer.IronMeta({type: 'animation'}); | |
| 8903 } | |
| 8904 }, | |
| 8905 | |
| 8906 /** @type {?Object} */ | 9091 /** @type {?Object} */ |
| 8907 _player: { | 9092 _player: { |
| 8908 type: Object | 9093 type: Object |
| 8909 } | 9094 } |
| 8910 | 9095 |
| 8911 }, | 9096 }, |
| 8912 | 9097 |
| 8913 _configureAnimationEffects: function(allConfigs) { | 9098 _configureAnimationEffects: function(allConfigs) { |
| 8914 var allAnimations = []; | 9099 var allAnimations = []; |
| 8915 if (allConfigs.length > 0) { | 9100 if (allConfigs.length > 0) { |
| 8916 for (var config, index = 0; config = allConfigs[index]; index++) { | 9101 for (var config, index = 0; config = allConfigs[index]; index++) { |
| 8917 var animationConstructor = this._animationMeta.byKey(config.name); | 9102 var animation = document.createElement(config.name); |
| 8918 if (animationConstructor) { | 9103 // is this element actually a neon animation? |
| 8919 var animation = animationConstructor && new animationConstructor(); | 9104 if (animation.isNeonAnimation) { |
| 8920 var effect = animation.configure(config); | 9105 var effect = animation.configure(config); |
| 8921 if (effect) { | 9106 if (effect) { |
| 8922 allAnimations.push({ | 9107 allAnimations.push({ |
| 8923 animation: animation, | 9108 animation: animation, |
| 8924 config: config, | 9109 config: config, |
| 8925 effect: effect | 9110 effect: effect |
| 8926 }); | 9111 }); |
| 8927 } | 9112 } |
| 8928 } else { | 9113 } else { |
| 8929 console.warn(this.is + ':', config.name, 'not found!'); | 9114 console.warn(this.is + ':', config.name, 'not found!'); |
| (...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9338 | 9523 |
| 9339 listeners: { | 9524 listeners: { |
| 9340 'neon-animation-finish': '_onNeonAnimationFinish' | 9525 'neon-animation-finish': '_onNeonAnimationFinish' |
| 9341 }, | 9526 }, |
| 9342 | 9527 |
| 9343 observers: [ | 9528 observers: [ |
| 9344 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' | 9529 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' |
| 9345 ], | 9530 ], |
| 9346 | 9531 |
| 9347 attached: function() { | 9532 attached: function() { |
| 9348 this.positionTarget = this.positionTarget || this._defaultPositionTarg
et; | |
| 9349 // Memoize this to avoid expensive calculations & relayouts. | 9533 // Memoize this to avoid expensive calculations & relayouts. |
| 9350 this._isRTL = window.getComputedStyle(this).direction == 'rtl'; | 9534 this._isRTL = window.getComputedStyle(this).direction == 'rtl'; |
| 9535 this.positionTarget = this.positionTarget || this._defaultPositionTarg
et; |
| 9351 }, | 9536 }, |
| 9352 | 9537 |
| 9353 /** | 9538 /** |
| 9354 * The element that is contained by the dropdown, if any. | 9539 * The element that is contained by the dropdown, if any. |
| 9355 */ | 9540 */ |
| 9356 get containedElement() { | 9541 get containedElement() { |
| 9357 return Polymer.dom(this.$.content).getDistributedNodes()[0]; | 9542 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| 9358 }, | 9543 }, |
| 9359 | 9544 |
| 9360 /** | 9545 /** |
| (...skipping 27 matching lines...) Expand all Loading... |
| 9388 return this.horizontalAlign === 'right' ? 'left' : 'right'; | 9573 return this.horizontalAlign === 'right' ? 'left' : 'right'; |
| 9389 } else { | 9574 } else { |
| 9390 return this.horizontalAlign; | 9575 return this.horizontalAlign; |
| 9391 } | 9576 } |
| 9392 }, | 9577 }, |
| 9393 | 9578 |
| 9394 /** | 9579 /** |
| 9395 * The horizontal offset value used to position the dropdown. | 9580 * The horizontal offset value used to position the dropdown. |
| 9396 * @param {ClientRect} dropdownRect | 9581 * @param {ClientRect} dropdownRect |
| 9397 * @param {ClientRect} positionRect | 9582 * @param {ClientRect} positionRect |
| 9398 * @param {boolean=false} fromRight | 9583 * @param {boolean=} fromRight |
| 9399 * @return {number} pixels | 9584 * @return {number} pixels |
| 9400 * @private | 9585 * @private |
| 9401 */ | 9586 */ |
| 9402 _horizontalAlignTargetValue: function(dropdownRect, positionRect, fromRi
ght) { | 9587 _horizontalAlignTargetValue: function(dropdownRect, positionRect, fromRi
ght) { |
| 9403 var target; | 9588 var target; |
| 9404 if (fromRight) { | 9589 if (fromRight) { |
| 9405 target = document.documentElement.clientWidth - positionRect.right -
(this._fitWidth - dropdownRect.right); | 9590 target = document.documentElement.clientWidth - positionRect.right -
(this._fitWidth - dropdownRect.right); |
| 9406 } else { | 9591 } else { |
| 9407 target = positionRect.left - dropdownRect.left; | 9592 target = positionRect.left - dropdownRect.left; |
| 9408 } | 9593 } |
| 9409 target += this.horizontalOffset; | 9594 target += this.horizontalOffset; |
| 9410 | 9595 |
| 9411 return Math.max(target, 0); | 9596 return Math.max(target, 0); |
| 9412 }, | 9597 }, |
| 9413 | 9598 |
| 9414 /** | 9599 /** |
| 9415 * The vertical offset value used to position the dropdown. | 9600 * The vertical offset value used to position the dropdown. |
| 9416 * @param {ClientRect} dropdownRect | 9601 * @param {ClientRect} dropdownRect |
| 9417 * @param {ClientRect} positionRect | 9602 * @param {ClientRect} positionRect |
| 9418 * @param {boolean=false} fromBottom | 9603 * @param {boolean=} fromBottom |
| 9419 * @return {number} pixels | 9604 * @return {number} pixels |
| 9420 * @private | 9605 * @private |
| 9421 */ | 9606 */ |
| 9422 _verticalAlignTargetValue: function(dropdownRect, positionRect, fromBott
om) { | 9607 _verticalAlignTargetValue: function(dropdownRect, positionRect, fromBott
om) { |
| 9423 var target; | 9608 var target; |
| 9424 if (fromBottom) { | 9609 if (fromBottom) { |
| 9425 target = document.documentElement.clientHeight - positionRect.bottom
- (this._fitHeight - dropdownRect.bottom); | 9610 target = document.documentElement.clientHeight - positionRect.bottom
- (this._fitHeight - dropdownRect.bottom); |
| 9426 } else { | 9611 } else { |
| 9427 target = positionRect.top - dropdownRect.top; | 9612 target = positionRect.top - dropdownRect.top; |
| 9428 } | 9613 } |
| 9429 target += this.verticalOffset; | 9614 target += this.verticalOffset; |
| 9430 | 9615 |
| 9431 return Math.max(target, 0); | 9616 return Math.max(target, 0); |
| 9432 }, | 9617 }, |
| 9433 | 9618 |
| 9434 /** | 9619 /** |
| 9435 * Called when the value of `opened` changes. | 9620 * Called when the value of `opened` changes. |
| 9436 * | 9621 * Overridden from `IronOverlayBehavior` |
| 9437 * @param {boolean} opened True if the dropdown is opened. | |
| 9438 */ | 9622 */ |
| 9439 _openedChanged: function(opened) { | 9623 _openedChanged: function() { |
| 9440 if (opened && this.disabled) { | 9624 if (this.opened && this.disabled) { |
| 9441 this.cancel(); | 9625 this.cancel(); |
| 9442 } else { | 9626 } else { |
| 9443 this.cancelAnimation(); | 9627 this.cancelAnimation(); |
| 9444 this.sizingTarget = this.containedElement || this.sizingTarget; | 9628 this.sizingTarget = this.containedElement || this.sizingTarget; |
| 9445 this._updateAnimationConfig(); | 9629 this._updateAnimationConfig(); |
| 9630 if (this.opened && !this.allowOutsideScroll) { |
| 9631 Polymer.IronDropdownScrollManager.pushScrollLock(this); |
| 9632 } else { |
| 9633 Polymer.IronDropdownScrollManager.removeScrollLock(this); |
| 9634 } |
| 9446 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); | 9635 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); |
| 9447 } | 9636 } |
| 9448 }, | 9637 }, |
| 9449 | 9638 |
| 9450 /** | 9639 /** |
| 9451 * Overridden from `IronOverlayBehavior`. | 9640 * Overridden from `IronOverlayBehavior`. |
| 9452 */ | 9641 */ |
| 9453 _renderOpened: function() { | 9642 _renderOpened: function() { |
| 9454 if (!this.allowOutsideScroll) { | |
| 9455 Polymer.IronDropdownScrollManager.pushScrollLock(this); | |
| 9456 } | |
| 9457 | |
| 9458 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { | 9643 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { |
| 9459 if (this.withBackdrop) { | 9644 if (this.withBackdrop) { |
| 9460 this.backdropElement.open(); | 9645 this.backdropElement.open(); |
| 9461 } | 9646 } |
| 9462 this.$.contentWrapper.classList.add('animating'); | 9647 this.$.contentWrapper.classList.add('animating'); |
| 9463 this.playAnimation('open'); | 9648 this.playAnimation('open'); |
| 9464 } else { | 9649 } else { |
| 9465 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; | 9650 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; |
| 9466 } | 9651 } |
| 9467 }, | 9652 }, |
| 9468 | 9653 |
| 9469 /** | 9654 /** |
| 9470 * Overridden from `IronOverlayBehavior`. | 9655 * Overridden from `IronOverlayBehavior`. |
| 9471 */ | 9656 */ |
| 9472 _renderClosed: function() { | 9657 _renderClosed: function() { |
| 9473 Polymer.IronDropdownScrollManager.removeScrollLock(this); | |
| 9474 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { | 9658 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { |
| 9475 if (this.withBackdrop) { | 9659 if (this.withBackdrop) { |
| 9476 this.backdropElement.close(); | 9660 this.backdropElement.close(); |
| 9477 } | 9661 } |
| 9478 this.$.contentWrapper.classList.add('animating'); | 9662 this.$.contentWrapper.classList.add('animating'); |
| 9479 this.playAnimation('close'); | 9663 this.playAnimation('close'); |
| 9480 } else { | 9664 } else { |
| 9481 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; | 9665 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; |
| 9482 } | 9666 } |
| 9483 }, | 9667 }, |
| (...skipping 1732 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 11216 // found in the LICENSE file. | 11400 // found in the LICENSE file. |
| 11217 | 11401 |
| 11218 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> | 11402 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> |
| 11219 | 11403 |
| 11220 i18nTemplate.process(document, loadTimeData); | 11404 i18nTemplate.process(document, loadTimeData); |
| 11221 // Copyright 2015 The Chromium Authors. All rights reserved. | 11405 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 11222 // Use of this source code is governed by a BSD-style license that can be | 11406 // Use of this source code is governed by a BSD-style license that can be |
| 11223 // found in the LICENSE file. | 11407 // found in the LICENSE file. |
| 11224 | 11408 |
| 11225 window.addEventListener('load', downloads.Manager.onLoad); | 11409 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |