| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 PromiseResolver is a helper class that allows creating a | 6 * @fileoverview PromiseResolver is a helper class that allows creating a |
| 7 * Promise that will be fulfilled (resolved or rejected) some time later. | 7 * Promise that will be fulfilled (resolved or rejected) some time later. |
| 8 * | 8 * |
| 9 * Example: | 9 * Example: |
| 10 * var resolver = new PromiseResolver(); | 10 * var resolver = new PromiseResolver(); |
| (...skipping 2158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2169 * @demo demo/scrolling-region.html Scrolling Region | 2169 * @demo demo/scrolling-region.html Scrolling Region |
| 2170 * @demo demo/document.html Document Element | 2170 * @demo demo/document.html Document Element |
| 2171 * @polymerBehavior | 2171 * @polymerBehavior |
| 2172 */ | 2172 */ |
| 2173 Polymer.IronScrollTargetBehavior = { | 2173 Polymer.IronScrollTargetBehavior = { |
| 2174 | 2174 |
| 2175 properties: { | 2175 properties: { |
| 2176 | 2176 |
| 2177 /** | 2177 /** |
| 2178 * Specifies the element that will handle the scroll event | 2178 * Specifies the element that will handle the scroll event |
| 2179 * on the behalf of the current element. This is typically a reference to
an `Element`, | 2179 * on the behalf of the current element. This is typically a reference to
an element, |
| 2180 * but there are a few more posibilities: | 2180 * but there are a few more posibilities: |
| 2181 * | 2181 * |
| 2182 * ### Elements id | 2182 * ### Elements id |
| 2183 * | 2183 * |
| 2184 *```html | 2184 *```html |
| 2185 * <div id="scrollable-element" style="overflow-y: auto;"> | 2185 * <div id="scrollable-element" style="overflow: auto;"> |
| 2186 * <x-element scroll-target="scrollable-element"> | 2186 * <x-element scroll-target="scrollable-element"> |
| 2187 * Content | 2187 * \x3c!-- Content--\x3e |
| 2188 * </x-element> | 2188 * </x-element> |
| 2189 * </div> | 2189 * </div> |
| 2190 *``` | 2190 *``` |
| 2191 * In this case, `scrollTarget` will point to the outer div element. Alter
natively, | 2191 * In this case, the `scrollTarget` will point to the outer div element. |
| 2192 * you can set the property programatically: | 2192 * |
| 2193 * ### Document scrolling |
| 2194 * |
| 2195 * For document scrolling, you can use the reserved word `document`: |
| 2196 * |
| 2197 *```html |
| 2198 * <x-element scroll-target="document"> |
| 2199 * \x3c!-- Content --\x3e |
| 2200 * </x-element> |
| 2201 *``` |
| 2202 * |
| 2203 * ### Elements reference |
| 2193 * | 2204 * |
| 2194 *```js | 2205 *```js |
| 2195 * appHeader.scrollTarget = document.querySelector('#scrollable-element'); | 2206 * appHeader.scrollTarget = document.querySelector('#scrollable-element'); |
| 2196 *``` | 2207 *``` |
| 2197 * | 2208 * |
| 2198 * @type {HTMLElement} | 2209 * @type {HTMLElement} |
| 2199 */ | 2210 */ |
| 2200 scrollTarget: { | 2211 scrollTarget: { |
| 2201 type: HTMLElement, | 2212 type: HTMLElement, |
| 2202 value: function() { | 2213 value: function() { |
| 2203 return this._defaultScrollTarget; | 2214 return this._defaultScrollTarget; |
| 2204 } | 2215 } |
| 2205 } | 2216 } |
| 2206 }, | 2217 }, |
| 2207 | 2218 |
| 2208 observers: [ | 2219 observers: [ |
| 2209 '_scrollTargetChanged(scrollTarget, isAttached)' | 2220 '_scrollTargetChanged(scrollTarget, isAttached)' |
| 2210 ], | 2221 ], |
| 2211 | 2222 |
| 2212 _scrollTargetChanged: function(scrollTarget, isAttached) { | 2223 _scrollTargetChanged: function(scrollTarget, isAttached) { |
| 2213 // Remove lister to the current scroll target | 2224 var eventTarget; |
| 2225 |
| 2214 if (this._oldScrollTarget) { | 2226 if (this._oldScrollTarget) { |
| 2215 if (this._oldScrollTarget === this._doc) { | 2227 eventTarget = this._oldScrollTarget === this._doc ? window : this._oldSc
rollTarget; |
| 2216 window.removeEventListener('scroll', this._boundScrollHandler); | 2228 eventTarget.removeEventListener('scroll', this._boundScrollHandler); |
| 2217 } else if (this._oldScrollTarget.removeEventListener) { | |
| 2218 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); | |
| 2219 } | |
| 2220 this._oldScrollTarget = null; | 2229 this._oldScrollTarget = null; |
| 2221 } | 2230 } |
| 2222 if (isAttached) { | |
| 2223 // Support element id references | |
| 2224 if (typeof scrollTarget === 'string') { | |
| 2225 | 2231 |
| 2226 var host = this.domHost; | 2232 if (!isAttached) { |
| 2227 this.scrollTarget = host && host.$ ? host.$[scrollTarget] : | 2233 return; |
| 2228 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); | 2234 } |
| 2235 // Support element id references |
| 2236 if (scrollTarget === 'document') { |
| 2229 | 2237 |
| 2230 } else if (this._scrollHandler) { | 2238 this.scrollTarget = this._doc; |
| 2231 | 2239 |
| 2232 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); | 2240 } else if (typeof scrollTarget === 'string') { |
| 2233 // Add a new listener | 2241 |
| 2234 if (scrollTarget === this._doc) { | 2242 this.scrollTarget = this.domHost ? this.domHost.$[scrollTarget] : |
| 2235 window.addEventListener('scroll', this._boundScrollHandler); | 2243 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); |
| 2236 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { | 2244 |
| 2237 this._scrollHandler(); | 2245 } else if (this._isValidScrollTarget()) { |
| 2238 } | 2246 |
| 2239 } else if (scrollTarget && scrollTarget.addEventListener) { | 2247 eventTarget = scrollTarget === this._doc ? window : scrollTarget; |
| 2240 scrollTarget.addEventListener('scroll', this._boundScrollHandler); | 2248 this._boundScrollHandler = this._boundScrollHandler || this._scrollHandl
er.bind(this); |
| 2241 } | 2249 this._oldScrollTarget = scrollTarget; |
| 2242 this._oldScrollTarget = scrollTarget; | 2250 |
| 2243 } | 2251 eventTarget.addEventListener('scroll', this._boundScrollHandler); |
| 2244 } | 2252 } |
| 2245 }, | 2253 }, |
| 2246 | 2254 |
| 2247 /** | 2255 /** |
| 2248 * Runs on every scroll event. Consumer of this behavior may want to overrid
e this method. | 2256 * Runs on every scroll event. Consumer of this behavior may override this m
ethod. |
| 2249 * | 2257 * |
| 2250 * @protected | 2258 * @protected |
| 2251 */ | 2259 */ |
| 2252 _scrollHandler: function scrollHandler() {}, | 2260 _scrollHandler: function scrollHandler() {}, |
| 2253 | 2261 |
| 2254 /** | 2262 /** |
| 2255 * The default scroll target. Consumers of this behavior may want to customi
ze | 2263 * The default scroll target. Consumers of this behavior may want to customi
ze |
| 2256 * the default scroll target. | 2264 * the default scroll target. |
| 2257 * | 2265 * |
| 2258 * @type {Element} | 2266 * @type {Element} |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2465 '_setOverflow(scrollTarget)' | 2473 '_setOverflow(scrollTarget)' |
| 2466 ], | 2474 ], |
| 2467 | 2475 |
| 2468 behaviors: [ | 2476 behaviors: [ |
| 2469 Polymer.Templatizer, | 2477 Polymer.Templatizer, |
| 2470 Polymer.IronResizableBehavior, | 2478 Polymer.IronResizableBehavior, |
| 2471 Polymer.IronA11yKeysBehavior, | 2479 Polymer.IronA11yKeysBehavior, |
| 2472 Polymer.IronScrollTargetBehavior | 2480 Polymer.IronScrollTargetBehavior |
| 2473 ], | 2481 ], |
| 2474 | 2482 |
| 2475 listeners: { | |
| 2476 'iron-resize': '_resizeHandler' | |
| 2477 }, | |
| 2478 | |
| 2479 keyBindings: { | 2483 keyBindings: { |
| 2480 'up': '_didMoveUp', | 2484 'up': '_didMoveUp', |
| 2481 'down': '_didMoveDown', | 2485 'down': '_didMoveDown', |
| 2482 'enter': '_didEnter' | 2486 'enter': '_didEnter' |
| 2483 }, | 2487 }, |
| 2484 | 2488 |
| 2485 /** | 2489 /** |
| 2486 * The ratio of hidden tiles that should remain in the scroll direction. | 2490 * The ratio of hidden tiles that should remain in the scroll direction. |
| 2487 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. | 2491 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. |
| 2488 */ | 2492 */ |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2772 return this; | 2776 return this; |
| 2773 }, | 2777 }, |
| 2774 | 2778 |
| 2775 ready: function() { | 2779 ready: function() { |
| 2776 this.addEventListener('focus', this._didFocus.bind(this), true); | 2780 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 2777 }, | 2781 }, |
| 2778 | 2782 |
| 2779 attached: function() { | 2783 attached: function() { |
| 2780 this.updateViewportBoundaries(); | 2784 this.updateViewportBoundaries(); |
| 2781 this._render(); | 2785 this._render(); |
| 2786 // `iron-resize` is fired when the list is attached if the event is added |
| 2787 // before attached causing unnecessary work. |
| 2788 this.listen(this, 'iron-resize', '_resizeHandler'); |
| 2782 }, | 2789 }, |
| 2783 | 2790 |
| 2784 detached: function() { | 2791 detached: function() { |
| 2785 this._itemsRendered = false; | 2792 this._itemsRendered = false; |
| 2793 this.unlisten(this, 'iron-resize', '_resizeHandler'); |
| 2786 }, | 2794 }, |
| 2787 | 2795 |
| 2788 /** | 2796 /** |
| 2789 * Set the overflow property if this element has its own scrolling region | 2797 * Set the overflow property if this element has its own scrolling region |
| 2790 */ | 2798 */ |
| 2791 _setOverflow: function(scrollTarget) { | 2799 _setOverflow: function(scrollTarget) { |
| 2792 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; | 2800 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; |
| 2793 this.style.overflow = scrollTarget === this ? 'auto' : ''; | 2801 this.style.overflow = scrollTarget === this ? 'auto' : ''; |
| 2794 }, | 2802 }, |
| 2795 | 2803 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2896 currentRatio += tileHeight / hiddenContentSize; | 2904 currentRatio += tileHeight / hiddenContentSize; |
| 2897 | 2905 |
| 2898 this._physicalTop += tileHeight; | 2906 this._physicalTop += tileHeight; |
| 2899 recycledTileSet.push(kth); | 2907 recycledTileSet.push(kth); |
| 2900 recycledTiles++; | 2908 recycledTiles++; |
| 2901 kth = (kth + 1) % this._physicalCount; | 2909 kth = (kth + 1) % this._physicalCount; |
| 2902 } | 2910 } |
| 2903 } | 2911 } |
| 2904 | 2912 |
| 2905 if (recycledTiles === 0) { | 2913 if (recycledTiles === 0) { |
| 2906 // If the list ever reach this case, the physical average is not signifi
cant enough | 2914 // Try to increase the pool if the list's client height isn't filled up
with physical items |
| 2907 // to create all the items needed to cover the entire viewport. | |
| 2908 // e.g. A few items have a height that differs from the average by serve
ral order of magnitude. | |
| 2909 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { | 2915 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
| 2910 this.async(this._increasePool.bind(this, 1)); | 2916 this._increasePoolIfNeeded(); |
| 2911 } | 2917 } |
| 2912 } else { | 2918 } else { |
| 2913 this._virtualStart = this._virtualStart + recycledTiles; | 2919 this._virtualStart = this._virtualStart + recycledTiles; |
| 2914 this._physicalStart = this._physicalStart + recycledTiles; | 2920 this._physicalStart = this._physicalStart + recycledTiles; |
| 2915 this._update(recycledTileSet, movingUp); | 2921 this._update(recycledTileSet, movingUp); |
| 2916 } | 2922 } |
| 2917 }, | 2923 }, |
| 2918 | 2924 |
| 2919 /** | 2925 /** |
| 2920 * Update the list of items, starting from the `_virtualStart` item. | 2926 * Update the list of items, starting from the `_virtualStart` item. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2955 // First element child is item; Safari doesn't support children[0] | 2961 // First element child is item; Safari doesn't support children[0] |
| 2956 // on a doc fragment | 2962 // on a doc fragment |
| 2957 physicalItems[i] = inst.root.querySelector('*'); | 2963 physicalItems[i] = inst.root.querySelector('*'); |
| 2958 Polymer.dom(this).appendChild(inst.root); | 2964 Polymer.dom(this).appendChild(inst.root); |
| 2959 } | 2965 } |
| 2960 return physicalItems; | 2966 return physicalItems; |
| 2961 }, | 2967 }, |
| 2962 | 2968 |
| 2963 /** | 2969 /** |
| 2964 * Increases the pool of physical items only if needed. | 2970 * Increases the pool of physical items only if needed. |
| 2965 * This function will allocate additional physical items | 2971 * |
| 2966 * if the physical size is shorter than `_optPhysicalSize` | 2972 * @return {boolean} True if the pool was increased. |
| 2967 */ | 2973 */ |
| 2968 _increasePoolIfNeeded: function() { | 2974 _increasePoolIfNeeded: function() { |
| 2969 if (this._viewportSize === 0 || this._physicalSize >= this._optPhysicalSiz
e) { | 2975 // Base case 1: the list has no size. |
| 2976 if (this._viewportSize === 0) { |
| 2970 return false; | 2977 return false; |
| 2971 } | 2978 } |
| 2972 // 0 <= `currentPage` <= `_maxPages` | 2979 // Base case 2: If the physical size is optimal and the list's client heig
ht is full |
| 2980 // with physical items, don't increase the pool. |
| 2981 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi
s._physicalTop <= this._scrollPosition; |
| 2982 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { |
| 2983 return false; |
| 2984 } |
| 2985 // this value should range between [0 <= `currentPage` <= `_maxPages`] |
| 2973 var currentPage = Math.floor(this._physicalSize / this._viewportSize); | 2986 var currentPage = Math.floor(this._physicalSize / this._viewportSize); |
| 2987 |
| 2974 if (currentPage === 0) { | 2988 if (currentPage === 0) { |
| 2975 // fill the first page | 2989 // fill the first page |
| 2976 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); | 2990 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); |
| 2977 } else if (this._lastPage !== currentPage) { | 2991 } else if (this._lastPage !== currentPage && isClientHeightFull) { |
| 2978 // paint the page and defer the next increase | 2992 // paint the page and defer the next increase |
| 2979 // wait 16ms which is rough enough to get paint cycle. | 2993 // wait 16ms which is rough enough to get paint cycle. |
| 2980 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, 1), 16)); | 2994 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, 1), 16)); |
| 2981 } else { | 2995 } else { |
| 2982 // fill the rest of the pages | 2996 // fill the rest of the pages |
| 2983 this._debounceTemplate(this._increasePool.bind(this, 1)); | 2997 this._debounceTemplate(this._increasePool.bind(this, 1)); |
| 2984 } | 2998 } |
| 2999 |
| 2985 this._lastPage = currentPage; | 3000 this._lastPage = currentPage; |
| 3001 |
| 2986 return true; | 3002 return true; |
| 2987 }, | 3003 }, |
| 2988 | 3004 |
| 2989 /** | 3005 /** |
| 2990 * Increases the pool size. | 3006 * Increases the pool size. |
| 2991 */ | 3007 */ |
| 2992 _increasePool: function(missingItems) { | 3008 _increasePool: function(missingItems) { |
| 2993 var nextPhysicalCount = Math.min( | 3009 var nextPhysicalCount = Math.min( |
| 2994 this._physicalCount + missingItems, | 3010 this._physicalCount + missingItems, |
| 2995 this._virtualCount - this._virtualStart, | 3011 this._virtualCount - this._virtualStart, |
| (...skipping 25 matching lines...) Expand all Loading... |
| 3021 /** | 3037 /** |
| 3022 * Render a new list of items. This method does exactly the same as `update`
, | 3038 * Render a new list of items. This method does exactly the same as `update`
, |
| 3023 * but it also ensures that only one `update` cycle is created. | 3039 * but it also ensures that only one `update` cycle is created. |
| 3024 */ | 3040 */ |
| 3025 _render: function() { | 3041 _render: function() { |
| 3026 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; | 3042 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
| 3027 | 3043 |
| 3028 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { | 3044 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { |
| 3029 this._lastPage = 0; | 3045 this._lastPage = 0; |
| 3030 this._update(); | 3046 this._update(); |
| 3031 this._scrollHandler(); | |
| 3032 this._itemsRendered = true; | 3047 this._itemsRendered = true; |
| 3033 } | 3048 } |
| 3034 }, | 3049 }, |
| 3035 | 3050 |
| 3036 /** | 3051 /** |
| 3037 * Templetizes the user template. | 3052 * Templetizes the user template. |
| 3038 */ | 3053 */ |
| 3039 _ensureTemplatized: function() { | 3054 _ensureTemplatized: function() { |
| 3040 if (!this.ctor) { | 3055 if (!this.ctor) { |
| 3041 // Template instance props that should be excluded from forwarding | 3056 // Template instance props that should be excluded from forwarding |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3116 var idx = this._physicalIndexForKey[key]; | 3131 var idx = this._physicalIndexForKey[key]; |
| 3117 var el = this._physicalItems[idx]; | 3132 var el = this._physicalItems[idx]; |
| 3118 | 3133 |
| 3119 | 3134 |
| 3120 if (idx === this._focusedIndex && this._offscreenFocusedItem) { | 3135 if (idx === this._focusedIndex && this._offscreenFocusedItem) { |
| 3121 el = this._offscreenFocusedItem; | 3136 el = this._offscreenFocusedItem; |
| 3122 } | 3137 } |
| 3123 if (!el) { | 3138 if (!el) { |
| 3124 return; | 3139 return; |
| 3125 } | 3140 } |
| 3126 | 3141 |
| 3127 inst = el._templateInstance; | 3142 inst = el._templateInstance; |
| 3128 | 3143 |
| 3129 if (inst.__key__ !== key) { | 3144 if (inst.__key__ !== key) { |
| 3130 return; | 3145 return; |
| 3131 } | 3146 } |
| 3132 if (dot >= 0) { | 3147 if (dot >= 0) { |
| 3133 path = this.as + '.' + path.substring(dot+1); | 3148 path = this.as + '.' + path.substring(dot+1); |
| 3134 inst.notifyPath(path, value, true); | 3149 inst.notifyPath(path, value, true); |
| 3135 } else { | 3150 } else { |
| 3136 inst[this.as] = value; | 3151 inst[this.as] = value; |
| (...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3400 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { | 3415 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { |
| 3401 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; | 3416 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; |
| 3402 currentTopItem = (currentTopItem + 1) % this._physicalCount; | 3417 currentTopItem = (currentTopItem + 1) % this._physicalCount; |
| 3403 currentVirtualItem++; | 3418 currentVirtualItem++; |
| 3404 } | 3419 } |
| 3405 // update the scroller size | 3420 // update the scroller size |
| 3406 this._updateScrollerSize(true); | 3421 this._updateScrollerSize(true); |
| 3407 // update the position of the items | 3422 // update the position of the items |
| 3408 this._positionItems(); | 3423 this._positionItems(); |
| 3409 // set the new scroll position | 3424 // set the new scroll position |
| 3410 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop + 1); | 3425 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop); |
| 3411 // increase the pool of physical items if needed | 3426 // increase the pool of physical items if needed |
| 3412 this._increasePoolIfNeeded(); | 3427 this._increasePoolIfNeeded(); |
| 3413 // clear cached visible index | 3428 // clear cached visible index |
| 3414 this._firstVisibleIndexVal = null; | 3429 this._firstVisibleIndexVal = null; |
| 3415 this._lastVisibleIndexVal = null; | 3430 this._lastVisibleIndexVal = null; |
| 3416 }, | 3431 }, |
| 3417 | 3432 |
| 3418 /** | 3433 /** |
| 3419 * Reset the physical average and the average count. | 3434 * Reset the physical average and the average count. |
| 3420 */ | 3435 */ |
| 3421 _resetAverage: function() { | 3436 _resetAverage: function() { |
| 3422 this._physicalAverage = 0; | 3437 this._physicalAverage = 0; |
| 3423 this._physicalAverageCount = 0; | 3438 this._physicalAverageCount = 0; |
| 3424 }, | 3439 }, |
| 3425 | 3440 |
| 3426 /** | 3441 /** |
| 3427 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` | 3442 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` |
| 3428 * when the element is resized. | 3443 * when the element is resized. |
| 3429 */ | 3444 */ |
| 3430 _resizeHandler: function() { | 3445 _resizeHandler: function() { |
| 3431 // iOS fires the resize event when the address bar slides up | 3446 // iOS fires the resize event when the address bar slides up |
| 3432 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100)
{ | 3447 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100)
{ |
| 3433 return; | 3448 return; |
| 3434 } | 3449 } |
| 3435 this._debounceTemplate(function() { | 3450 // In Desktop Safari 9.0.3, if the scroll bars are always shown, |
| 3436 this._render(); | 3451 // changing the scroll position from a resize handler would result in |
| 3437 if (this._itemsRendered && this._physicalItems && this._isVisible) { | 3452 // the scroll position being reset. Waiting 1ms fixes the issue. |
| 3438 this._resetAverage(); | 3453 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', |
| 3439 this.updateViewportBoundaries(); | 3454 function() { |
| 3440 this.scrollToIndex(this.firstVisibleIndex); | 3455 this._render(); |
| 3441 } | 3456 |
| 3442 }); | 3457 if (this._itemsRendered && this._physicalItems && this._isVisible) { |
| 3458 this._resetAverage(); |
| 3459 this.updateViewportBoundaries(); |
| 3460 this.scrollToIndex(this.firstVisibleIndex); |
| 3461 } |
| 3462 }.bind(this), 1)); |
| 3443 }, | 3463 }, |
| 3444 | 3464 |
| 3445 _getModelFromItem: function(item) { | 3465 _getModelFromItem: function(item) { |
| 3446 var key = this._collection.getKey(item); | 3466 var key = this._collection.getKey(item); |
| 3447 var pidx = this._physicalIndexForKey[key]; | 3467 var pidx = this._physicalIndexForKey[key]; |
| 3448 | 3468 |
| 3449 if (pidx != null) { | 3469 if (pidx != null) { |
| 3450 return this._physicalItems[pidx]._templateInstance; | 3470 return this._physicalItems[pidx]._templateInstance; |
| 3451 } | 3471 } |
| 3452 return null; | 3472 return null; |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3754 this._focusPhysicalItem(this._focusedIndex + 1); | 3774 this._focusPhysicalItem(this._focusedIndex + 1); |
| 3755 }, | 3775 }, |
| 3756 | 3776 |
| 3757 _didEnter: function(e) { | 3777 _didEnter: function(e) { |
| 3758 this._focusPhysicalItem(this._focusedIndex); | 3778 this._focusPhysicalItem(this._focusedIndex); |
| 3759 this._selectionHandler(e.detail.keyboardEvent); | 3779 this._selectionHandler(e.detail.keyboardEvent); |
| 3760 } | 3780 } |
| 3761 }); | 3781 }); |
| 3762 | 3782 |
| 3763 })(); | 3783 })(); |
| 3764 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 3765 // Use of this source code is governed by a BSD-style license that can be | |
| 3766 // found in the LICENSE file. | |
| 3767 | |
| 3768 /** | |
| 3769 * @fileoverview Assertion support. | |
| 3770 */ | |
| 3771 | |
| 3772 /** | |
| 3773 * Verify |condition| is truthy and return |condition| if so. | |
| 3774 * @template T | |
| 3775 * @param {T} condition A condition to check for truthiness. Note that this | |
| 3776 * may be used to test whether a value is defined or not, and we don't want | |
| 3777 * to force a cast to Boolean. | |
| 3778 * @param {string=} opt_message A message to show on failure. | |
| 3779 * @return {T} A non-null |condition|. | |
| 3780 */ | |
| 3781 function assert(condition, opt_message) { | |
| 3782 if (!condition) { | |
| 3783 var message = 'Assertion failed'; | |
| 3784 if (opt_message) | |
| 3785 message = message + ': ' + opt_message; | |
| 3786 var error = new Error(message); | |
| 3787 var global = function() { return this; }(); | |
| 3788 if (global.traceAssertionsForTesting) | |
| 3789 console.warn(error.stack); | |
| 3790 throw error; | |
| 3791 } | |
| 3792 return condition; | |
| 3793 } | |
| 3794 | |
| 3795 /** | |
| 3796 * Call this from places in the code that should never be reached. | |
| 3797 * | |
| 3798 * For example, handling all the values of enum with a switch() like this: | |
| 3799 * | |
| 3800 * function getValueFromEnum(enum) { | |
| 3801 * switch (enum) { | |
| 3802 * case ENUM_FIRST_OF_TWO: | |
| 3803 * return first | |
| 3804 * case ENUM_LAST_OF_TWO: | |
| 3805 * return last; | |
| 3806 * } | |
| 3807 * assertNotReached(); | |
| 3808 * return document; | |
| 3809 * } | |
| 3810 * | |
| 3811 * This code should only be hit in the case of serious programmer error or | |
| 3812 * unexpected input. | |
| 3813 * | |
| 3814 * @param {string=} opt_message A message to show when this is hit. | |
| 3815 */ | |
| 3816 function assertNotReached(opt_message) { | |
| 3817 assert(false, opt_message || 'Unreachable code hit'); | |
| 3818 } | |
| 3819 | |
| 3820 /** | |
| 3821 * @param {*} value The value to check. | |
| 3822 * @param {function(new: T, ...)} type A user-defined constructor. | |
| 3823 * @param {string=} opt_message A message to show when this is hit. | |
| 3824 * @return {T} | |
| 3825 * @template T | |
| 3826 */ | |
| 3827 function assertInstanceof(value, type, opt_message) { | |
| 3828 // We don't use assert immediately here so that we avoid constructing an error | |
| 3829 // message if we don't have to. | |
| 3830 if (!(value instanceof type)) { | |
| 3831 assertNotReached(opt_message || 'Value ' + value + | |
| 3832 ' is not a[n] ' + (type.name || typeof type)); | |
| 3833 } | |
| 3834 return value; | |
| 3835 }; | |
| 3836 // Copyright 2015 The Chromium Authors. All rights reserved. | 3784 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 3837 // Use of this source code is governed by a BSD-style license that can be | 3785 // Use of this source code is governed by a BSD-style license that can be |
| 3838 // found in the LICENSE file. | 3786 // found in the LICENSE file. |
| 3839 | 3787 |
| 3840 cr.define('downloads', function() { | 3788 cr.define('downloads', function() { |
| 3841 /** | 3789 /** |
| 3842 * @param {string} chromeSendName | 3790 * @param {string} chromeSendName |
| 3843 * @return {function(string):void} A chrome.send() callback with curried name. | 3791 * @return {function(string):void} A chrome.send() callback with curried name. |
| 3844 */ | 3792 */ |
| 3845 function chromeSendWithId(chromeSendName) { | 3793 function chromeSendWithId(chromeSendName) { |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3959 show: chromeSendWithId('show'), | 3907 show: chromeSendWithId('show'), |
| 3960 | 3908 |
| 3961 /** Undo download removal. */ | 3909 /** Undo download removal. */ |
| 3962 undo: chrome.send.bind(chrome, 'undo'), | 3910 undo: chrome.send.bind(chrome, 'undo'), |
| 3963 }; | 3911 }; |
| 3964 | 3912 |
| 3965 cr.addSingletonGetter(ActionService); | 3913 cr.addSingletonGetter(ActionService); |
| 3966 | 3914 |
| 3967 return {ActionService: ActionService}; | 3915 return {ActionService: ActionService}; |
| 3968 }); | 3916 }); |
| 3917 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3918 // Use of this source code is governed by a BSD-style license that can be |
| 3919 // found in the LICENSE file. |
| 3920 |
| 3921 /** |
| 3922 * @fileoverview Assertion support. |
| 3923 */ |
| 3924 |
| 3925 /** |
| 3926 * Verify |condition| is truthy and return |condition| if so. |
| 3927 * @template T |
| 3928 * @param {T} condition A condition to check for truthiness. Note that this |
| 3929 * may be used to test whether a value is defined or not, and we don't want |
| 3930 * to force a cast to Boolean. |
| 3931 * @param {string=} opt_message A message to show on failure. |
| 3932 * @return {T} A non-null |condition|. |
| 3933 */ |
| 3934 function assert(condition, opt_message) { |
| 3935 if (!condition) { |
| 3936 var message = 'Assertion failed'; |
| 3937 if (opt_message) |
| 3938 message = message + ': ' + opt_message; |
| 3939 var error = new Error(message); |
| 3940 var global = function() { return this; }(); |
| 3941 if (global.traceAssertionsForTesting) |
| 3942 console.warn(error.stack); |
| 3943 throw error; |
| 3944 } |
| 3945 return condition; |
| 3946 } |
| 3947 |
| 3948 /** |
| 3949 * Call this from places in the code that should never be reached. |
| 3950 * |
| 3951 * For example, handling all the values of enum with a switch() like this: |
| 3952 * |
| 3953 * function getValueFromEnum(enum) { |
| 3954 * switch (enum) { |
| 3955 * case ENUM_FIRST_OF_TWO: |
| 3956 * return first |
| 3957 * case ENUM_LAST_OF_TWO: |
| 3958 * return last; |
| 3959 * } |
| 3960 * assertNotReached(); |
| 3961 * return document; |
| 3962 * } |
| 3963 * |
| 3964 * This code should only be hit in the case of serious programmer error or |
| 3965 * unexpected input. |
| 3966 * |
| 3967 * @param {string=} opt_message A message to show when this is hit. |
| 3968 */ |
| 3969 function assertNotReached(opt_message) { |
| 3970 assert(false, opt_message || 'Unreachable code hit'); |
| 3971 } |
| 3972 |
| 3973 /** |
| 3974 * @param {*} value The value to check. |
| 3975 * @param {function(new: T, ...)} type A user-defined constructor. |
| 3976 * @param {string=} opt_message A message to show when this is hit. |
| 3977 * @return {T} |
| 3978 * @template T |
| 3979 */ |
| 3980 function assertInstanceof(value, type, opt_message) { |
| 3981 // We don't use assert immediately here so that we avoid constructing an error |
| 3982 // message if we don't have to. |
| 3983 if (!(value instanceof type)) { |
| 3984 assertNotReached(opt_message || 'Value ' + value + |
| 3985 ' is not a[n] ' + (type.name || typeof type)); |
| 3986 } |
| 3987 return value; |
| 3988 }; |
| 3969 // Copyright 2015 The Chromium Authors. All rights reserved. | 3989 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 3970 // Use of this source code is governed by a BSD-style license that can be | 3990 // Use of this source code is governed by a BSD-style license that can be |
| 3971 // found in the LICENSE file. | 3991 // found in the LICENSE file. |
| 3972 | 3992 |
| 3973 cr.define('downloads', function() { | 3993 cr.define('downloads', function() { |
| 3974 /** | 3994 /** |
| 3975 * Explains why a download is in DANGEROUS state. | 3995 * Explains why a download is in DANGEROUS state. |
| 3976 * @enum {string} | 3996 * @enum {string} |
| 3977 */ | 3997 */ |
| 3978 var DangerType = { | 3998 var DangerType = { |
| (...skipping 2571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6550 * Fired when the list of selectable items changes (e.g., items are | 6570 * Fired when the list of selectable items changes (e.g., items are |
| 6551 * added or removed). The detail of the event is a list of mutation | 6571 * added or removed). The detail of the event is a list of mutation |
| 6552 * records that describe what changed. | 6572 * records that describe what changed. |
| 6553 * | 6573 * |
| 6554 * @event iron-items-changed | 6574 * @event iron-items-changed |
| 6555 */ | 6575 */ |
| 6556 | 6576 |
| 6557 properties: { | 6577 properties: { |
| 6558 | 6578 |
| 6559 /** | 6579 /** |
| 6560 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | 6580 * If you want to use an attribute value or property of an element for |
| 6561 * set this to the name of the attribute. | 6581 * `selected` instead of the index, set this to the name of the attribute |
| 6582 * or property. Hyphenated values are converted to camel case when used to |
| 6583 * look up the property of a selectable element. Camel cased values are |
| 6584 * *not* converted to hyphenated values for attribute lookup. It's |
| 6585 * recommended that you provide the hyphenated form of the name so that |
| 6586 * selection works in both cases. (Use `attr-or-property-name` instead of |
| 6587 * `attrOrPropertyName`.) |
| 6562 */ | 6588 */ |
| 6563 attrForSelected: { | 6589 attrForSelected: { |
| 6564 type: String, | 6590 type: String, |
| 6565 value: null | 6591 value: null |
| 6566 }, | 6592 }, |
| 6567 | 6593 |
| 6568 /** | 6594 /** |
| 6569 * Gets or sets the selected element. The default is to use the index of t
he item. | 6595 * Gets or sets the selected element. The default is to use the index of t
he item. |
| 6570 * @type {string|number} | 6596 * @type {string|number} |
| 6571 */ | 6597 */ |
| (...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6792 var item = this.items[index]; | 6818 var item = this.items[index]; |
| 6793 if (item) { | 6819 if (item) { |
| 6794 return this._valueForItem(item); | 6820 return this._valueForItem(item); |
| 6795 } | 6821 } |
| 6796 } else { | 6822 } else { |
| 6797 return index; | 6823 return index; |
| 6798 } | 6824 } |
| 6799 }, | 6825 }, |
| 6800 | 6826 |
| 6801 _valueForItem: function(item) { | 6827 _valueForItem: function(item) { |
| 6802 var propValue = item[this.attrForSelected]; | 6828 var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)
]; |
| 6803 return propValue != undefined ? propValue : item.getAttribute(this.attrFor
Selected); | 6829 return propValue != undefined ? propValue : item.getAttribute(this.attrFor
Selected); |
| 6804 }, | 6830 }, |
| 6805 | 6831 |
| 6806 _applySelection: function(item, isSelected) { | 6832 _applySelection: function(item, isSelected) { |
| 6807 if (this.selectedClass) { | 6833 if (this.selectedClass) { |
| 6808 this.toggleClass(this.selectedClass, isSelected, item); | 6834 this.toggleClass(this.selectedClass, isSelected, item); |
| 6809 } | 6835 } |
| 6810 if (this.selectedAttribute) { | 6836 if (this.selectedAttribute) { |
| 6811 this.toggleAttribute(this.selectedAttribute, isSelected, item); | 6837 this.toggleAttribute(this.selectedAttribute, isSelected, item); |
| 6812 } | 6838 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6885 */ | 6911 */ |
| 6886 selectedItems: { | 6912 selectedItems: { |
| 6887 type: Array, | 6913 type: Array, |
| 6888 readOnly: true, | 6914 readOnly: true, |
| 6889 notify: true | 6915 notify: true |
| 6890 }, | 6916 }, |
| 6891 | 6917 |
| 6892 }, | 6918 }, |
| 6893 | 6919 |
| 6894 observers: [ | 6920 observers: [ |
| 6895 '_updateSelected(selectedValues)' | 6921 '_updateSelected(selectedValues.splices)' |
| 6896 ], | 6922 ], |
| 6897 | 6923 |
| 6898 /** | 6924 /** |
| 6899 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 6925 * Selects the given value. If the `multi` property is true, then the select
ed state of the |
| 6900 * `value` will be toggled; otherwise the `value` will be selected. | 6926 * `value` will be toggled; otherwise the `value` will be selected. |
| 6901 * | 6927 * |
| 6902 * @method select | 6928 * @method select |
| 6903 * @param {string|number} value the value to select. | 6929 * @param {string|number} value the value to select. |
| 6904 */ | 6930 */ |
| 6905 select: function(value) { | 6931 select: function(value) { |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7046 this._resetTabindices(); | 7072 this._resetTabindices(); |
| 7047 }, | 7073 }, |
| 7048 | 7074 |
| 7049 /** | 7075 /** |
| 7050 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 7076 * Selects the given value. If the `multi` property is true, then the select
ed state of the |
| 7051 * `value` will be toggled; otherwise the `value` will be selected. | 7077 * `value` will be toggled; otherwise the `value` will be selected. |
| 7052 * | 7078 * |
| 7053 * @param {string|number} value the value to select. | 7079 * @param {string|number} value the value to select. |
| 7054 */ | 7080 */ |
| 7055 select: function(value) { | 7081 select: function(value) { |
| 7082 // Cancel automatically focusing a default item if the menu received focus |
| 7083 // through a user action selecting a particular item. |
| 7056 if (this._defaultFocusAsync) { | 7084 if (this._defaultFocusAsync) { |
| 7057 this.cancelAsync(this._defaultFocusAsync); | 7085 this.cancelAsync(this._defaultFocusAsync); |
| 7058 this._defaultFocusAsync = null; | 7086 this._defaultFocusAsync = null; |
| 7059 } | 7087 } |
| 7060 var item = this._valueToItem(value); | 7088 var item = this._valueToItem(value); |
| 7061 if (item && item.hasAttribute('disabled')) return; | 7089 if (item && item.hasAttribute('disabled')) return; |
| 7062 this._setFocusedItem(item); | 7090 this._setFocusedItem(item); |
| 7063 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); | 7091 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); |
| 7064 }, | 7092 }, |
| 7065 | 7093 |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7216 } | 7244 } |
| 7217 | 7245 |
| 7218 // Do not focus the selected tab if the deepest target is part of the | 7246 // Do not focus the selected tab if the deepest target is part of the |
| 7219 // menu element's local DOM and is focusable. | 7247 // menu element's local DOM and is focusable. |
| 7220 var rootTarget = /** @type {?HTMLElement} */( | 7248 var rootTarget = /** @type {?HTMLElement} */( |
| 7221 Polymer.dom(event).rootTarget); | 7249 Polymer.dom(event).rootTarget); |
| 7222 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !
this.isLightDescendant(rootTarget)) { | 7250 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !
this.isLightDescendant(rootTarget)) { |
| 7223 return; | 7251 return; |
| 7224 } | 7252 } |
| 7225 | 7253 |
| 7226 this.blur(); | |
| 7227 | |
| 7228 // clear the cached focus item | 7254 // clear the cached focus item |
| 7229 this._defaultFocusAsync = this.async(function() { | 7255 this._defaultFocusAsync = this.async(function() { |
| 7230 // focus the selected item when the menu receives focus, or the first it
em | 7256 // focus the selected item when the menu receives focus, or the first it
em |
| 7231 // if no item is selected | 7257 // if no item is selected |
| 7232 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | 7258 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; |
| 7233 | 7259 |
| 7234 this._setFocusedItem(null); | 7260 this._setFocusedItem(null); |
| 7235 | 7261 |
| 7236 if (selectedItem) { | 7262 if (selectedItem) { |
| 7237 this._setFocusedItem(selectedItem); | 7263 this._setFocusedItem(selectedItem); |
| 7238 } else { | 7264 } else if (this.items[0]) { |
| 7239 this._setFocusedItem(this.items[0]); | 7265 this._setFocusedItem(this.items[0]); |
| 7240 } | 7266 } |
| 7241 // async 1ms to wait for `select` to get called from `_itemActivate` | 7267 }); |
| 7242 }, 1); | |
| 7243 }, | 7268 }, |
| 7244 | 7269 |
| 7245 /** | 7270 /** |
| 7246 * Handler that is called when the up key is pressed. | 7271 * Handler that is called when the up key is pressed. |
| 7247 * | 7272 * |
| 7248 * @param {CustomEvent} event A key combination event. | 7273 * @param {CustomEvent} event A key combination event. |
| 7249 */ | 7274 */ |
| 7250 _onUpKey: function(event) { | 7275 _onUpKey: function(event) { |
| 7251 // up and down arrows moves the focus | 7276 // up and down arrows moves the focus |
| 7252 this._focusPrevious(); | 7277 this._focusPrevious(); |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7562 if (!positionedBy.horizontally) { | 7587 if (!positionedBy.horizontally) { |
| 7563 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; | 7588 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; |
| 7564 this.style.left = left + 'px'; | 7589 this.style.left = left + 'px'; |
| 7565 } | 7590 } |
| 7566 } | 7591 } |
| 7567 | 7592 |
| 7568 }; | 7593 }; |
| 7569 /** | 7594 /** |
| 7570 * @struct | 7595 * @struct |
| 7571 * @constructor | 7596 * @constructor |
| 7597 * @private |
| 7572 */ | 7598 */ |
| 7573 Polymer.IronOverlayManagerClass = function() { | 7599 Polymer.IronOverlayManagerClass = function() { |
| 7600 /** |
| 7601 * Used to keep track of the opened overlays. |
| 7602 * @private {Array<Element>} |
| 7603 */ |
| 7574 this._overlays = []; | 7604 this._overlays = []; |
| 7575 // Used to keep track of the last focused node before an overlay gets opened
. | 7605 |
| 7576 this._lastFocusedNodes = []; | 7606 /** |
| 7577 | 7607 * iframes have a default z-index of 100, |
| 7578 /** | 7608 * so this default should be at least that. |
| 7579 * iframes have a default z-index of 100, so this default should be at least | |
| 7580 * that. | |
| 7581 * @private {number} | 7609 * @private {number} |
| 7582 */ | 7610 */ |
| 7583 this._minimumZ = 101; | 7611 this._minimumZ = 101; |
| 7584 | 7612 |
| 7585 this._backdrops = []; | 7613 /** |
| 7586 | 7614 * Memoized backdrop element. |
| 7615 * @private {Element|null} |
| 7616 */ |
| 7587 this._backdropElement = null; | 7617 this._backdropElement = null; |
| 7588 Object.defineProperty(this, 'backdropElement', { | 7618 |
| 7589 get: function() { | 7619 // Listen to mousedown or touchstart to be sure to be the first to capture |
| 7590 if (!this._backdropElement) { | 7620 // clicks outside the overlay. |
| 7591 this._backdropElement = document.createElement('iron-overlay-backdrop'
); | 7621 var clickEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown'; |
| 7592 } | 7622 document.addEventListener(clickEvent, this._onCaptureClick.bind(this), true)
; |
| 7593 return this._backdropElement; | 7623 document.addEventListener('focus', this._onCaptureFocus.bind(this), true); |
| 7594 }.bind(this) | 7624 document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true
); |
| 7595 }); | 7625 }; |
| 7626 |
| 7627 Polymer.IronOverlayManagerClass.prototype = { |
| 7628 |
| 7629 constructor: Polymer.IronOverlayManagerClass, |
| 7630 |
| 7631 /** |
| 7632 * The shared backdrop element. |
| 7633 * @type {Element} backdropElement |
| 7634 */ |
| 7635 get backdropElement() { |
| 7636 if (!this._backdropElement) { |
| 7637 this._backdropElement = document.createElement('iron-overlay-backdrop'); |
| 7638 } |
| 7639 return this._backdropElement; |
| 7640 }, |
| 7596 | 7641 |
| 7597 /** | 7642 /** |
| 7598 * The deepest active element. | 7643 * The deepest active element. |
| 7599 * returns {?Node} element the active element | 7644 * @type {Element} activeElement the active element |
| 7600 */ | 7645 */ |
| 7601 this.deepActiveElement = null; | 7646 get deepActiveElement() { |
| 7602 Object.defineProperty(this, 'deepActiveElement', { | 7647 // document.activeElement can be null |
| 7603 get: function() { | 7648 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement |
| 7604 var active = document.activeElement; | 7649 // In case of null, default it to document.body. |
| 7605 // document.activeElement can be null | 7650 var active = document.activeElement || document.body; |
| 7606 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeEleme
nt | 7651 while (active.root && Polymer.dom(active.root).activeElement) { |
| 7607 while (active && active.root && Polymer.dom(active.root).activeElement)
{ | 7652 active = Polymer.dom(active.root).activeElement; |
| 7608 active = Polymer.dom(active.root).activeElement; | 7653 } |
| 7609 } | 7654 return active; |
| 7610 return active; | 7655 }, |
| 7611 }.bind(this) | 7656 |
| 7612 }); | 7657 /** |
| 7613 }; | 7658 * Brings the overlay at the specified index to the front. |
| 7614 | 7659 * @param {number} i |
| 7615 /** | 7660 * @private |
| 7616 * If a node is contained in an overlay. | 7661 */ |
| 7617 * @private | 7662 _bringOverlayAtIndexToFront: function(i) { |
| 7618 * @param {Node} node | 7663 var overlay = this._overlays[i]; |
| 7619 * @returns {Boolean} | 7664 var lastI = this._overlays.length - 1; |
| 7620 */ | 7665 // Ensure always-on-top overlay stays on top. |
| 7621 Polymer.IronOverlayManagerClass.prototype._isChildOfOverlay = function(node) { | 7666 if (!overlay.alwaysOnTop && this._overlays[lastI].alwaysOnTop) { |
| 7622 while (node && node !== document.body) { | 7667 lastI--; |
| 7623 // Use logical parentNode, or native ShadowRoot host. | 7668 } |
| 7624 node = Polymer.dom(node).parentNode || node.host; | 7669 // If already the top element, return. |
| 7625 // Check if it is an overlay. | 7670 if (!overlay || i >= lastI) { |
| 7626 if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBe
haviorImpl) !== -1) { | 7671 return; |
| 7627 return true; | 7672 } |
| 7628 } | 7673 // Update z-index to be on top. |
| 7629 } | 7674 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
| 7630 return false; | 7675 if (this._getZ(overlay) <= minimumZ) { |
| 7631 }; | 7676 this._applyOverlayZ(overlay, minimumZ); |
| 7632 | 7677 } |
| 7633 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { | 7678 |
| 7634 this._setZ(overlay, aboveZ + 2); | 7679 // Shift other overlays behind the new on top. |
| 7635 }; | 7680 while (i < lastI) { |
| 7636 | 7681 this._overlays[i] = this._overlays[i + 1]; |
| 7637 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { | 7682 i++; |
| 7638 element.style.zIndex = z; | 7683 } |
| 7639 }; | 7684 this._overlays[lastI] = overlay; |
| 7640 | 7685 }, |
| 7641 /** | 7686 |
| 7642 * track overlays for z-index and focus managemant | 7687 /** |
| 7643 */ | 7688 * Adds the overlay and updates its z-index if it's opened, or removes it if
it's closed. |
| 7644 Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { | 7689 * Also updates the backdrop z-index. |
| 7645 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | 7690 * @param {Element} overlay |
| 7646 this._overlays.push(overlay); | 7691 */ |
| 7647 var newZ = this.currentOverlayZ(); | 7692 addOrRemoveOverlay: function(overlay) { |
| 7648 if (newZ <= minimumZ) { | 7693 if (overlay.opened) { |
| 7649 this._applyOverlayZ(overlay, minimumZ); | 7694 this.addOverlay(overlay); |
| 7650 } | 7695 } else { |
| 7651 var element = this.deepActiveElement; | 7696 this.removeOverlay(overlay); |
| 7652 // If already in other overlay, don't reset focus there. | 7697 } |
| 7653 if (this._isChildOfOverlay(element)) { | 7698 this.trackBackdrop(); |
| 7654 element = null; | 7699 }, |
| 7655 } | 7700 |
| 7656 this._lastFocusedNodes.push(element); | 7701 /** |
| 7657 }; | 7702 * Tracks overlays for z-index and focus management. |
| 7658 | 7703 * Ensures the last added overlay with always-on-top remains on top. |
| 7659 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { | 7704 * @param {Element} overlay |
| 7660 var i = this._overlays.indexOf(overlay); | 7705 */ |
| 7661 if (i >= 0) { | 7706 addOverlay: function(overlay) { |
| 7707 var i = this._overlays.indexOf(overlay); |
| 7708 if (i >= 0) { |
| 7709 this._bringOverlayAtIndexToFront(i); |
| 7710 return; |
| 7711 } |
| 7712 var insertionIndex = this._overlays.length; |
| 7713 var currentOverlay = this._overlays[insertionIndex - 1]; |
| 7714 var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); |
| 7715 var newZ = this._getZ(overlay); |
| 7716 |
| 7717 // Ensure always-on-top overlay stays on top. |
| 7718 if (currentOverlay && currentOverlay.alwaysOnTop && !overlay.alwaysOnTop)
{ |
| 7719 // This bumps the z-index of +2. |
| 7720 this._applyOverlayZ(currentOverlay, minimumZ); |
| 7721 insertionIndex--; |
| 7722 // Update minimumZ to match previous overlay's z-index. |
| 7723 var previousOverlay = this._overlays[insertionIndex - 1]; |
| 7724 minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); |
| 7725 } |
| 7726 |
| 7727 // Update z-index and insert overlay. |
| 7728 if (newZ <= minimumZ) { |
| 7729 this._applyOverlayZ(overlay, minimumZ); |
| 7730 } |
| 7731 this._overlays.splice(insertionIndex, 0, overlay); |
| 7732 |
| 7733 // Get focused node. |
| 7734 var element = this.deepActiveElement; |
| 7735 overlay.restoreFocusNode = this._overlayParent(element) ? null : element; |
| 7736 }, |
| 7737 |
| 7738 /** |
| 7739 * @param {Element} overlay |
| 7740 */ |
| 7741 removeOverlay: function(overlay) { |
| 7742 var i = this._overlays.indexOf(overlay); |
| 7743 if (i === -1) { |
| 7744 return; |
| 7745 } |
| 7662 this._overlays.splice(i, 1); | 7746 this._overlays.splice(i, 1); |
| 7663 this._setZ(overlay, ''); | 7747 |
| 7664 | 7748 var node = overlay.restoreFocusOnClose ? overlay.restoreFocusNode : null; |
| 7665 var node = this._lastFocusedNodes[i]; | 7749 overlay.restoreFocusNode = null; |
| 7666 // Focus only if still contained in document.body | 7750 // Focus back only if still contained in document.body |
| 7667 if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deep
Contains(node)) { | 7751 if (node && Polymer.dom(document.body).deepContains(node)) { |
| 7668 node.focus(); | 7752 node.focus(); |
| 7669 } | 7753 } |
| 7670 this._lastFocusedNodes.splice(i, 1); | 7754 }, |
| 7755 |
| 7756 /** |
| 7757 * Returns the current overlay. |
| 7758 * @return {Element|undefined} |
| 7759 */ |
| 7760 currentOverlay: function() { |
| 7761 var i = this._overlays.length - 1; |
| 7762 return this._overlays[i]; |
| 7763 }, |
| 7764 |
| 7765 /** |
| 7766 * Returns the current overlay z-index. |
| 7767 * @return {number} |
| 7768 */ |
| 7769 currentOverlayZ: function() { |
| 7770 return this._getZ(this.currentOverlay()); |
| 7771 }, |
| 7772 |
| 7773 /** |
| 7774 * Ensures that the minimum z-index of new overlays is at least `minimumZ`. |
| 7775 * This does not effect the z-index of any existing overlays. |
| 7776 * @param {number} minimumZ |
| 7777 */ |
| 7778 ensureMinimumZ: function(minimumZ) { |
| 7779 this._minimumZ = Math.max(this._minimumZ, minimumZ); |
| 7780 }, |
| 7781 |
| 7782 focusOverlay: function() { |
| 7783 var current = /** @type {?} */ (this.currentOverlay()); |
| 7784 // We have to be careful to focus the next overlay _after_ any current |
| 7785 // transitions are complete (due to the state being toggled prior to the |
| 7786 // transition). Otherwise, we risk infinite recursion when a transitioning |
| 7787 // (closed) overlay becomes the current overlay. |
| 7788 // |
| 7789 // NOTE: We make the assumption that any overlay that completes a transiti
on |
| 7790 // will call into focusOverlay to kick the process back off. Currently: |
| 7791 // transitionend -> _applyFocus -> focusOverlay. |
| 7792 if (current && !current.transitioning) { |
| 7793 current._applyFocus(); |
| 7794 } |
| 7795 }, |
| 7796 |
| 7797 /** |
| 7798 * Updates the backdrop z-index. |
| 7799 */ |
| 7800 trackBackdrop: function() { |
| 7801 this.backdropElement.style.zIndex = this.backdropZ(); |
| 7802 }, |
| 7803 |
| 7804 /** |
| 7805 * @return {Array<Element>} |
| 7806 */ |
| 7807 getBackdrops: function() { |
| 7808 var backdrops = []; |
| 7809 for (var i = 0; i < this._overlays.length; i++) { |
| 7810 if (this._overlays[i].withBackdrop) { |
| 7811 backdrops.push(this._overlays[i]); |
| 7812 } |
| 7813 } |
| 7814 return backdrops; |
| 7815 }, |
| 7816 |
| 7817 /** |
| 7818 * Returns the z-index for the backdrop. |
| 7819 * @return {number} |
| 7820 */ |
| 7821 backdropZ: function() { |
| 7822 return this._getZ(this._overlayWithBackdrop()) - 1; |
| 7823 }, |
| 7824 |
| 7825 /** |
| 7826 * Returns the first opened overlay that has a backdrop. |
| 7827 * @return {Element|undefined} |
| 7828 * @private |
| 7829 */ |
| 7830 _overlayWithBackdrop: function() { |
| 7831 for (var i = 0; i < this._overlays.length; i++) { |
| 7832 if (this._overlays[i].withBackdrop) { |
| 7833 return this._overlays[i]; |
| 7834 } |
| 7835 } |
| 7836 }, |
| 7837 |
| 7838 /** |
| 7839 * Calculates the minimum z-index for the overlay. |
| 7840 * @param {Element=} overlay |
| 7841 * @private |
| 7842 */ |
| 7843 _getZ: function(overlay) { |
| 7844 var z = this._minimumZ; |
| 7845 if (overlay) { |
| 7846 var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay)
.zIndex); |
| 7847 // Check if is a number |
| 7848 // Number.isNaN not supported in IE 10+ |
| 7849 if (z1 === z1) { |
| 7850 z = z1; |
| 7851 } |
| 7852 } |
| 7853 return z; |
| 7854 }, |
| 7855 |
| 7856 /** |
| 7857 * @param {Element} element |
| 7858 * @param {number|string} z |
| 7859 * @private |
| 7860 */ |
| 7861 _setZ: function(element, z) { |
| 7862 element.style.zIndex = z; |
| 7863 }, |
| 7864 |
| 7865 /** |
| 7866 * @param {Element} overlay |
| 7867 * @param {number} aboveZ |
| 7868 * @private |
| 7869 */ |
| 7870 _applyOverlayZ: function(overlay, aboveZ) { |
| 7871 this._setZ(overlay, aboveZ + 2); |
| 7872 }, |
| 7873 |
| 7874 /** |
| 7875 * Returns the overlay containing the provided node. If the node is an overl
ay, |
| 7876 * it returns the node. |
| 7877 * @param {Element=} node |
| 7878 * @return {Element|undefined} |
| 7879 * @private |
| 7880 */ |
| 7881 _overlayParent: function(node) { |
| 7882 while (node && node !== document.body) { |
| 7883 // Check if it is an overlay. |
| 7884 if (node._manager === this) { |
| 7885 return node; |
| 7886 } |
| 7887 // Use logical parentNode, or native ShadowRoot host. |
| 7888 node = Polymer.dom(node).parentNode || node.host; |
| 7889 } |
| 7890 }, |
| 7891 |
| 7892 /** |
| 7893 * Returns the deepest overlay in the path. |
| 7894 * @param {Array<Element>=} path |
| 7895 * @return {Element|undefined} |
| 7896 * @private |
| 7897 */ |
| 7898 _overlayInPath: function(path) { |
| 7899 path = path || []; |
| 7900 for (var i = 0; i < path.length; i++) { |
| 7901 if (path[i]._manager === this) { |
| 7902 return path[i]; |
| 7903 } |
| 7904 } |
| 7905 }, |
| 7906 |
| 7907 /** |
| 7908 * Ensures the click event is delegated to the right overlay. |
| 7909 * @param {!Event} event |
| 7910 * @private |
| 7911 */ |
| 7912 _onCaptureClick: function(event) { |
| 7913 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7914 // Check if clicked outside of top overlay. |
| 7915 if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { |
| 7916 overlay._onCaptureClick(event); |
| 7917 } |
| 7918 }, |
| 7919 |
| 7920 /** |
| 7921 * Ensures the focus event is delegated to the right overlay. |
| 7922 * @param {!Event} event |
| 7923 * @private |
| 7924 */ |
| 7925 _onCaptureFocus: function(event) { |
| 7926 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7927 if (overlay) { |
| 7928 overlay._onCaptureFocus(event); |
| 7929 } |
| 7930 }, |
| 7931 |
| 7932 /** |
| 7933 * Ensures TAB and ESC keyboard events are delegated to the right overlay. |
| 7934 * @param {!Event} event |
| 7935 * @private |
| 7936 */ |
| 7937 _onCaptureKeyDown: function(event) { |
| 7938 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7939 if (overlay) { |
| 7940 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc'))
{ |
| 7941 overlay._onCaptureEsc(event); |
| 7942 } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,
'tab')) { |
| 7943 overlay._onCaptureTab(event); |
| 7944 } |
| 7945 } |
| 7671 } | 7946 } |
| 7672 }; | 7947 }; |
| 7673 | 7948 |
| 7674 Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { | |
| 7675 var i = this._overlays.length - 1; | |
| 7676 while (this._overlays[i] && !this._overlays[i].opened) { | |
| 7677 --i; | |
| 7678 } | |
| 7679 return this._overlays[i]; | |
| 7680 }; | |
| 7681 | |
| 7682 Polymer.IronOverlayManagerClass.prototype.currentOverlayZ = function() { | |
| 7683 return this._getOverlayZ(this.currentOverlay()); | |
| 7684 }; | |
| 7685 | |
| 7686 /** | |
| 7687 * Ensures that the minimum z-index of new overlays is at least `minimumZ`. | |
| 7688 * This does not effect the z-index of any existing overlays. | |
| 7689 * | |
| 7690 * @param {number} minimumZ | |
| 7691 */ | |
| 7692 Polymer.IronOverlayManagerClass.prototype.ensureMinimumZ = function(minimumZ)
{ | |
| 7693 this._minimumZ = Math.max(this._minimumZ, minimumZ); | |
| 7694 }; | |
| 7695 | |
| 7696 Polymer.IronOverlayManagerClass.prototype.focusOverlay = function() { | |
| 7697 var current = this.currentOverlay(); | |
| 7698 // We have to be careful to focus the next overlay _after_ any current | |
| 7699 // transitions are complete (due to the state being toggled prior to the | |
| 7700 // transition). Otherwise, we risk infinite recursion when a transitioning | |
| 7701 // (closed) overlay becomes the current overlay. | |
| 7702 // | |
| 7703 // NOTE: We make the assumption that any overlay that completes a transition | |
| 7704 // will call into focusOverlay to kick the process back off. Currently: | |
| 7705 // transitionend -> _applyFocus -> focusOverlay. | |
| 7706 if (current && !current.transitioning) { | |
| 7707 current._applyFocus(); | |
| 7708 } | |
| 7709 }; | |
| 7710 | |
| 7711 Polymer.IronOverlayManagerClass.prototype.trackBackdrop = function(element) { | |
| 7712 // backdrops contains the overlays with a backdrop that are currently | |
| 7713 // visible | |
| 7714 var index = this._backdrops.indexOf(element); | |
| 7715 if (element.opened && element.withBackdrop) { | |
| 7716 // no duplicates | |
| 7717 if (index === -1) { | |
| 7718 this._backdrops.push(element); | |
| 7719 } | |
| 7720 } else if (index >= 0) { | |
| 7721 this._backdrops.splice(index, 1); | |
| 7722 } | |
| 7723 }; | |
| 7724 | |
| 7725 Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { | |
| 7726 return this._backdrops; | |
| 7727 }; | |
| 7728 | |
| 7729 /** | |
| 7730 * Returns the z-index for the backdrop. | |
| 7731 */ | |
| 7732 Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { | |
| 7733 return this._getOverlayZ(this._overlayWithBackdrop()) - 1; | |
| 7734 }; | |
| 7735 | |
| 7736 /** | |
| 7737 * Returns the first opened overlay that has a backdrop. | |
| 7738 */ | |
| 7739 Polymer.IronOverlayManagerClass.prototype._overlayWithBackdrop = function() { | |
| 7740 for (var i = 0; i < this._overlays.length; i++) { | |
| 7741 if (this._overlays[i].opened && this._overlays[i].withBackdrop) { | |
| 7742 return this._overlays[i]; | |
| 7743 } | |
| 7744 } | |
| 7745 }; | |
| 7746 | |
| 7747 /** | |
| 7748 * Calculates the minimum z-index for the overlay. | |
| 7749 */ | |
| 7750 Polymer.IronOverlayManagerClass.prototype._getOverlayZ = function(overlay) { | |
| 7751 var z = this._minimumZ; | |
| 7752 if (overlay) { | |
| 7753 var z1 = Number(window.getComputedStyle(overlay).zIndex); | |
| 7754 // Check if is a number | |
| 7755 // Number.isNaN not supported in IE 10+ | |
| 7756 if (z1 === z1) { | |
| 7757 z = z1; | |
| 7758 } | |
| 7759 } | |
| 7760 return z; | |
| 7761 }; | |
| 7762 | |
| 7763 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); | 7949 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); |
| 7764 (function() { | 7950 (function() { |
| 7765 | 7951 |
| 7766 Polymer({ | 7952 Polymer({ |
| 7767 | 7953 |
| 7768 is: 'iron-overlay-backdrop', | 7954 is: 'iron-overlay-backdrop', |
| 7769 | 7955 |
| 7770 properties: { | 7956 properties: { |
| 7771 | 7957 |
| 7772 /** | 7958 /** |
| (...skipping 14 matching lines...) Expand all Loading... |
| 7787 }, | 7973 }, |
| 7788 | 7974 |
| 7789 listeners: { | 7975 listeners: { |
| 7790 'transitionend' : '_onTransitionend' | 7976 'transitionend' : '_onTransitionend' |
| 7791 }, | 7977 }, |
| 7792 | 7978 |
| 7793 /** | 7979 /** |
| 7794 * Appends the backdrop to document body and sets its `z-index` to be below
the latest overlay. | 7980 * Appends the backdrop to document body and sets its `z-index` to be below
the latest overlay. |
| 7795 */ | 7981 */ |
| 7796 prepare: function() { | 7982 prepare: function() { |
| 7797 // Always update z-index | |
| 7798 this.style.zIndex = this._manager.backdropZ(); | |
| 7799 if (!this.parentNode) { | 7983 if (!this.parentNode) { |
| 7800 Polymer.dom(document.body).appendChild(this); | 7984 Polymer.dom(document.body).appendChild(this); |
| 7801 } | 7985 } |
| 7802 }, | 7986 }, |
| 7803 | 7987 |
| 7804 /** | 7988 /** |
| 7805 * Shows the backdrop if needed. | 7989 * Shows the backdrop if needed. |
| 7806 */ | 7990 */ |
| 7807 open: function() { | 7991 open: function() { |
| 7808 // only need to make the backdrop visible if this is called by the first o
verlay with a backdrop | 7992 // only need to make the backdrop visible if this is called by the first o
verlay with a backdrop |
| 7809 if (this._manager.getBackdrops().length < 2) { | 7993 if (this._manager.getBackdrops().length < 2) { |
| 7810 this._setOpened(true); | 7994 this._setOpened(true); |
| 7811 } | 7995 } |
| 7812 }, | 7996 }, |
| 7813 | 7997 |
| 7814 /** | 7998 /** |
| 7815 * Hides the backdrop if needed. | 7999 * Hides the backdrop if needed. |
| 7816 */ | 8000 */ |
| 7817 close: function() { | 8001 close: function() { |
| 7818 // Always update z-index | |
| 7819 this.style.zIndex = this._manager.backdropZ(); | |
| 7820 // close only if no element with backdrop is left | 8002 // close only if no element with backdrop is left |
| 7821 if (this._manager.getBackdrops().length === 0) { | 8003 if (this._manager.getBackdrops().length === 0) { |
| 7822 // Read style before setting opened. | 8004 // Read style before setting opened. |
| 7823 var cs = getComputedStyle(this); | 8005 var cs = getComputedStyle(this); |
| 7824 var noAnimation = (cs.transitionDuration === '0s' || cs.opacity == 0); | 8006 var noAnimation = (cs.transitionDuration === '0s' || cs.opacity == 0); |
| 7825 this._setOpened(false); | 8007 this._setOpened(false); |
| 7826 // In case of no animations, complete | 8008 // In case of no animations, complete |
| 7827 if (noAnimation) { | 8009 if (noAnimation) { |
| 7828 this.complete(); | 8010 this.complete(); |
| 7829 } | 8011 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 7842 | 8024 |
| 7843 _onTransitionend: function (event) { | 8025 _onTransitionend: function (event) { |
| 7844 if (event && event.target === this) { | 8026 if (event && event.target === this) { |
| 7845 this.complete(); | 8027 this.complete(); |
| 7846 } | 8028 } |
| 7847 } | 8029 } |
| 7848 | 8030 |
| 7849 }); | 8031 }); |
| 7850 | 8032 |
| 7851 })(); | 8033 })(); |
| 8034 // IIFE to help scripts concatenation. |
| 8035 (function() { |
| 8036 'use strict'; |
| 8037 |
| 7852 /** | 8038 /** |
| 7853 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays | 8039 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays |
| 7854 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety | 8040 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety |
| 7855 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. | 8041 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. |
| 7856 | 8042 |
| 7857 ### Closing and canceling | 8043 ### Closing and canceling |
| 7858 | 8044 |
| 7859 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user | 8045 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user |
| 7860 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, | 8046 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, |
| 7861 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is | 8047 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7945 /** | 8131 /** |
| 7946 * Returns the reason this dialog was last closed. | 8132 * Returns the reason this dialog was last closed. |
| 7947 */ | 8133 */ |
| 7948 closingReason: { | 8134 closingReason: { |
| 7949 // was a getter before, but needs to be a property so other | 8135 // was a getter before, but needs to be a property so other |
| 7950 // behaviors can override this. | 8136 // behaviors can override this. |
| 7951 type: Object | 8137 type: Object |
| 7952 }, | 8138 }, |
| 7953 | 8139 |
| 7954 /** | 8140 /** |
| 7955 * The HTMLElement that will be firing relevant KeyboardEvents. | |
| 7956 * Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`. | |
| 7957 */ | |
| 7958 keyEventTarget: { | |
| 7959 type: Object, | |
| 7960 value: document | |
| 7961 }, | |
| 7962 | |
| 7963 /** | |
| 7964 * Set to true to enable restoring of focus when overlay is closed. | 8141 * Set to true to enable restoring of focus when overlay is closed. |
| 7965 */ | 8142 */ |
| 7966 restoreFocusOnClose: { | 8143 restoreFocusOnClose: { |
| 7967 type: Boolean, | 8144 type: Boolean, |
| 7968 value: false | 8145 value: false |
| 7969 }, | 8146 }, |
| 7970 | 8147 |
| 8148 /** |
| 8149 * Set to true to keep overlay always on top. |
| 8150 */ |
| 8151 alwaysOnTop: { |
| 8152 type: Boolean |
| 8153 }, |
| 8154 |
| 8155 /** |
| 8156 * Shortcut to access to the overlay manager. |
| 8157 * @private |
| 8158 * @type {Polymer.IronOverlayManagerClass} |
| 8159 */ |
| 7971 _manager: { | 8160 _manager: { |
| 7972 type: Object, | 8161 type: Object, |
| 7973 value: Polymer.IronOverlayManager | 8162 value: Polymer.IronOverlayManager |
| 7974 }, | 8163 }, |
| 7975 | 8164 |
| 7976 _boundOnCaptureClick: { | |
| 7977 type: Function, | |
| 7978 value: function() { | |
| 7979 return this._onCaptureClick.bind(this); | |
| 7980 } | |
| 7981 }, | |
| 7982 | |
| 7983 _boundOnCaptureFocus: { | |
| 7984 type: Function, | |
| 7985 value: function() { | |
| 7986 return this._onCaptureFocus.bind(this); | |
| 7987 } | |
| 7988 }, | |
| 7989 | |
| 7990 /** | 8165 /** |
| 7991 * The node being focused. | 8166 * The node being focused. |
| 7992 * @type {?Node} | 8167 * @type {?Node} |
| 7993 */ | 8168 */ |
| 7994 _focusedChild: { | 8169 _focusedChild: { |
| 7995 type: Object | 8170 type: Object |
| 7996 } | 8171 } |
| 7997 | 8172 |
| 7998 }, | 8173 }, |
| 7999 | 8174 |
| 8000 keyBindings: { | |
| 8001 'esc': '__onEsc', | |
| 8002 'tab': '__onTab' | |
| 8003 }, | |
| 8004 | |
| 8005 listeners: { | 8175 listeners: { |
| 8006 'iron-resize': '_onIronResize' | 8176 'iron-resize': '_onIronResize' |
| 8007 }, | 8177 }, |
| 8008 | 8178 |
| 8009 /** | 8179 /** |
| 8010 * The backdrop element. | 8180 * The backdrop element. |
| 8011 * @type {Node} | 8181 * @type {Element} |
| 8012 */ | 8182 */ |
| 8013 get backdropElement() { | 8183 get backdropElement() { |
| 8014 return this._manager.backdropElement; | 8184 return this._manager.backdropElement; |
| 8015 }, | 8185 }, |
| 8016 | 8186 |
| 8017 /** | 8187 /** |
| 8018 * Returns the node to give focus to. | 8188 * Returns the node to give focus to. |
| 8019 * @type {Node} | 8189 * @type {Node} |
| 8020 */ | 8190 */ |
| 8021 get _focusNode() { | 8191 get _focusNode() { |
| 8022 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; | 8192 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; |
| 8023 }, | 8193 }, |
| 8024 | 8194 |
| 8025 /** | 8195 /** |
| 8026 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. | 8196 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. |
| 8027 * This is used to retrieve which is the first and last focusable nodes in o
rder | 8197 * This is used to retrieve which is the first and last focusable nodes in o
rder |
| 8028 * to wrap the focus for overlays `with-backdrop`. | 8198 * to wrap the focus for overlays `with-backdrop`. |
| 8029 * | 8199 * |
| 8030 * If you know what is your content (specifically the first and last focusab
le children), | 8200 * If you know what is your content (specifically the first and last focusab
le children), |
| 8031 * you can override this method to return only `[firstFocusable, lastFocusab
le];` | 8201 * you can override this method to return only `[firstFocusable, lastFocusab
le];` |
| 8032 * @type {[Node]} | 8202 * @type {Array<Node>} |
| 8033 * @protected | 8203 * @protected |
| 8034 */ | 8204 */ |
| 8035 get _focusableNodes() { | 8205 get _focusableNodes() { |
| 8036 // Elements that can be focused even if they have [disabled] attribute. | 8206 // Elements that can be focused even if they have [disabled] attribute. |
| 8037 var FOCUSABLE_WITH_DISABLED = [ | 8207 var FOCUSABLE_WITH_DISABLED = [ |
| 8038 'a[href]', | 8208 'a[href]', |
| 8039 'area[href]', | 8209 'area[href]', |
| 8040 'iframe', | 8210 'iframe', |
| 8041 '[tabindex]', | 8211 '[tabindex]', |
| 8042 '[contentEditable=true]' | 8212 '[contentEditable=true]' |
| (...skipping 25 matching lines...) Expand all Loading... |
| 8068 return 0; | 8238 return 0; |
| 8069 } | 8239 } |
| 8070 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { | 8240 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { |
| 8071 return 1; | 8241 return 1; |
| 8072 } | 8242 } |
| 8073 return -1; | 8243 return -1; |
| 8074 }); | 8244 }); |
| 8075 }, | 8245 }, |
| 8076 | 8246 |
| 8077 ready: function() { | 8247 ready: function() { |
| 8248 // Used to skip calls to notifyResize and refit while the overlay is anima
ting. |
| 8249 this.__isAnimating = false; |
| 8078 // with-backdrop needs tabindex to be set in order to trap the focus. | 8250 // with-backdrop needs tabindex to be set in order to trap the focus. |
| 8079 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. | 8251 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 8080 this.__shouldRemoveTabIndex = false; | 8252 this.__shouldRemoveTabIndex = false; |
| 8081 // Used for wrapping the focus on TAB / Shift+TAB. | 8253 // Used for wrapping the focus on TAB / Shift+TAB. |
| 8082 this.__firstFocusableNode = this.__lastFocusableNode = null; | 8254 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| 8255 // Used for requestAnimationFrame when opened changes. |
| 8256 this.__openChangedAsync = null; |
| 8257 // Used for requestAnimationFrame when iron-resize is fired. |
| 8258 this.__onIronResizeAsync = null; |
| 8083 this._ensureSetup(); | 8259 this._ensureSetup(); |
| 8084 }, | 8260 }, |
| 8085 | 8261 |
| 8086 attached: function() { | 8262 attached: function() { |
| 8087 // Call _openedChanged here so that position can be computed correctly. | 8263 // Call _openedChanged here so that position can be computed correctly. |
| 8088 if (this.opened) { | 8264 if (this.opened) { |
| 8089 this._openedChanged(); | 8265 this._openedChanged(); |
| 8090 } | 8266 } |
| 8091 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); | 8267 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); |
| 8092 }, | 8268 }, |
| 8093 | 8269 |
| 8094 detached: function() { | 8270 detached: function() { |
| 8095 Polymer.dom(this).unobserveNodes(this._observer); | 8271 Polymer.dom(this).unobserveNodes(this._observer); |
| 8096 this._observer = null; | 8272 this._observer = null; |
| 8097 this.opened = false; | 8273 this.opened = false; |
| 8098 this._manager.trackBackdrop(this); | 8274 if (this.withBackdrop) { |
| 8099 this._manager.removeOverlay(this); | 8275 // Allow user interactions right away. |
| 8276 this.backdropElement.close(); |
| 8277 } |
| 8100 }, | 8278 }, |
| 8101 | 8279 |
| 8102 /** | 8280 /** |
| 8103 * Toggle the opened state of the overlay. | 8281 * Toggle the opened state of the overlay. |
| 8104 */ | 8282 */ |
| 8105 toggle: function() { | 8283 toggle: function() { |
| 8106 this._setCanceled(false); | 8284 this._setCanceled(false); |
| 8107 this.opened = !this.opened; | 8285 this.opened = !this.opened; |
| 8108 }, | 8286 }, |
| 8109 | 8287 |
| 8110 /** | 8288 /** |
| 8111 * Open the overlay. | 8289 * Open the overlay. |
| 8112 */ | 8290 */ |
| 8113 open: function() { | 8291 open: function() { |
| 8114 this._setCanceled(false); | 8292 this._setCanceled(false); |
| 8115 this.opened = true; | 8293 this.opened = true; |
| 8116 }, | 8294 }, |
| 8117 | 8295 |
| 8118 /** | 8296 /** |
| 8119 * Close the overlay. | 8297 * Close the overlay. |
| 8120 */ | 8298 */ |
| 8121 close: function() { | 8299 close: function() { |
| 8122 this._setCanceled(false); | 8300 this._setCanceled(false); |
| 8123 this.opened = false; | 8301 this.opened = false; |
| 8124 }, | 8302 }, |
| 8125 | 8303 |
| 8126 /** | 8304 /** |
| 8127 * Cancels the overlay. | 8305 * Cancels the overlay. |
| 8128 * @param {?Event} event The original event | 8306 * @param {Event=} event The original event |
| 8129 */ | 8307 */ |
| 8130 cancel: function(event) { | 8308 cancel: function(event) { |
| 8131 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); | 8309 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); |
| 8132 if (cancelEvent.defaultPrevented) { | 8310 if (cancelEvent.defaultPrevented) { |
| 8133 return; | 8311 return; |
| 8134 } | 8312 } |
| 8135 | 8313 |
| 8136 this._setCanceled(true); | 8314 this._setCanceled(true); |
| 8137 this.opened = false; | 8315 this.opened = false; |
| 8138 }, | 8316 }, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 8151 this.removeAttribute('aria-hidden'); | 8329 this.removeAttribute('aria-hidden'); |
| 8152 } else { | 8330 } else { |
| 8153 this.setAttribute('aria-hidden', 'true'); | 8331 this.setAttribute('aria-hidden', 'true'); |
| 8154 } | 8332 } |
| 8155 | 8333 |
| 8156 // wait to call after ready only if we're initially open | 8334 // wait to call after ready only if we're initially open |
| 8157 if (!this._overlaySetup) { | 8335 if (!this._overlaySetup) { |
| 8158 return; | 8336 return; |
| 8159 } | 8337 } |
| 8160 | 8338 |
| 8161 this._manager.trackBackdrop(this); | 8339 this._manager.addOrRemoveOverlay(this); |
| 8162 | 8340 |
| 8341 this.__isAnimating = true; |
| 8342 |
| 8343 // requestAnimationFrame for non-blocking rendering |
| 8344 if (this.__openChangedAsync) { |
| 8345 cancelAnimationFrame(this.__openChangedAsync); |
| 8346 } |
| 8163 if (this.opened) { | 8347 if (this.opened) { |
| 8164 this._prepareRenderOpened(); | 8348 if (this.withBackdrop) { |
| 8349 this.backdropElement.prepare(); |
| 8350 } |
| 8351 this.__openChangedAsync = requestAnimationFrame(function() { |
| 8352 this.__openChangedAsync = null; |
| 8353 this._prepareRenderOpened(); |
| 8354 this._renderOpened(); |
| 8355 }.bind(this)); |
| 8356 } else { |
| 8357 this._renderClosed(); |
| 8165 } | 8358 } |
| 8166 | |
| 8167 if (this._openChangedAsync) { | |
| 8168 this.cancelAsync(this._openChangedAsync); | |
| 8169 } | |
| 8170 // Async here to allow overlay layer to become visible, and to avoid | |
| 8171 // listeners to immediately close via a click. | |
| 8172 this._openChangedAsync = this.async(function() { | |
| 8173 // overlay becomes visible here | |
| 8174 this.style.display = ''; | |
| 8175 // Force layout to ensure transition will go. Set offsetWidth to itself | |
| 8176 // so that compilers won't remove it. | |
| 8177 this.offsetWidth = this.offsetWidth; | |
| 8178 if (this.opened) { | |
| 8179 this._renderOpened(); | |
| 8180 } else { | |
| 8181 this._renderClosed(); | |
| 8182 } | |
| 8183 this._toggleListeners(); | |
| 8184 this._openChangedAsync = null; | |
| 8185 }, 1); | |
| 8186 }, | 8359 }, |
| 8187 | 8360 |
| 8188 _canceledChanged: function() { | 8361 _canceledChanged: function() { |
| 8189 this.closingReason = this.closingReason || {}; | 8362 this.closingReason = this.closingReason || {}; |
| 8190 this.closingReason.canceled = this.canceled; | 8363 this.closingReason.canceled = this.canceled; |
| 8191 }, | 8364 }, |
| 8192 | 8365 |
| 8193 _withBackdropChanged: function() { | 8366 _withBackdropChanged: function() { |
| 8194 // If tabindex is already set, no need to override it. | 8367 // If tabindex is already set, no need to override it. |
| 8195 if (this.withBackdrop && !this.hasAttribute('tabindex')) { | 8368 if (this.withBackdrop && !this.hasAttribute('tabindex')) { |
| 8196 this.setAttribute('tabindex', '-1'); | 8369 this.setAttribute('tabindex', '-1'); |
| 8197 this.__shouldRemoveTabIndex = true; | 8370 this.__shouldRemoveTabIndex = true; |
| 8198 } else if (this.__shouldRemoveTabIndex) { | 8371 } else if (this.__shouldRemoveTabIndex) { |
| 8199 this.removeAttribute('tabindex'); | 8372 this.removeAttribute('tabindex'); |
| 8200 this.__shouldRemoveTabIndex = false; | 8373 this.__shouldRemoveTabIndex = false; |
| 8201 } | 8374 } |
| 8202 if (this.opened) { | 8375 if (this.opened) { |
| 8203 this._manager.trackBackdrop(this); | 8376 this._manager.trackBackdrop(); |
| 8204 if (this.withBackdrop) { | 8377 if (this.withBackdrop) { |
| 8205 this.backdropElement.prepare(); | 8378 this.backdropElement.prepare(); |
| 8206 // Give time to be added to document. | 8379 // Give time to be added to document. |
| 8207 this.async(function(){ | 8380 this.async(function(){ |
| 8208 this.backdropElement.open(); | 8381 this.backdropElement.open(); |
| 8209 }, 1); | 8382 }, 1); |
| 8210 } else { | 8383 } else { |
| 8211 this.backdropElement.close(); | 8384 this.backdropElement.close(); |
| 8212 } | 8385 } |
| 8213 } | 8386 } |
| 8214 }, | 8387 }, |
| 8215 | 8388 |
| 8216 _toggleListener: function(enable, node, event, boundListener, capture) { | 8389 /** |
| 8217 if (enable) { | 8390 * tasks which must occur before opening; e.g. making the element visible. |
| 8218 // enable document-wide tap recognizer | 8391 * @protected |
| 8219 if (event === 'tap') { | 8392 */ |
| 8220 Polymer.Gestures.add(document, 'tap', null); | |
| 8221 } | |
| 8222 node.addEventListener(event, boundListener, capture); | |
| 8223 } else { | |
| 8224 // disable document-wide tap recognizer | |
| 8225 if (event === 'tap') { | |
| 8226 Polymer.Gestures.remove(document, 'tap', null); | |
| 8227 } | |
| 8228 node.removeEventListener(event, boundListener, capture); | |
| 8229 } | |
| 8230 }, | |
| 8231 | |
| 8232 _toggleListeners: function() { | |
| 8233 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); | |
| 8234 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); | |
| 8235 }, | |
| 8236 | |
| 8237 // tasks which must occur before opening; e.g. making the element visible | |
| 8238 _prepareRenderOpened: function() { | 8393 _prepareRenderOpened: function() { |
| 8239 | 8394 |
| 8240 this._manager.addOverlay(this); | |
| 8241 | |
| 8242 // Needed to calculate the size of the overlay so that transitions on its
size | 8395 // Needed to calculate the size of the overlay so that transitions on its
size |
| 8243 // will have the correct starting points. | 8396 // will have the correct starting points. |
| 8244 this._preparePositioning(); | 8397 this._preparePositioning(); |
| 8245 this.fit(); | 8398 this.refit(); |
| 8246 this._finishPositioning(); | 8399 this._finishPositioning(); |
| 8247 | 8400 |
| 8248 if (this.withBackdrop) { | |
| 8249 this.backdropElement.prepare(); | |
| 8250 } | |
| 8251 | |
| 8252 // Safari will apply the focus to the autofocus element when displayed for
the first time, | 8401 // Safari will apply the focus to the autofocus element when displayed for
the first time, |
| 8253 // so we blur it. Later, _applyFocus will set the focus if necessary. | 8402 // so we blur it. Later, _applyFocus will set the focus if necessary. |
| 8254 if (this.noAutoFocus && document.activeElement === this._focusNode) { | 8403 if (this.noAutoFocus && document.activeElement === this._focusNode) { |
| 8255 this._focusNode.blur(); | 8404 this._focusNode.blur(); |
| 8256 } | 8405 } |
| 8257 }, | 8406 }, |
| 8258 | 8407 |
| 8259 // tasks which cause the overlay to actually open; typically play an | 8408 /** |
| 8260 // animation | 8409 * Tasks which cause the overlay to actually open; typically play an animati
on. |
| 8410 * @protected |
| 8411 */ |
| 8261 _renderOpened: function() { | 8412 _renderOpened: function() { |
| 8262 if (this.withBackdrop) { | 8413 if (this.withBackdrop) { |
| 8263 this.backdropElement.open(); | 8414 this.backdropElement.open(); |
| 8264 } | 8415 } |
| 8265 this._finishRenderOpened(); | 8416 this._finishRenderOpened(); |
| 8266 }, | 8417 }, |
| 8267 | 8418 |
| 8419 /** |
| 8420 * Tasks which cause the overlay to actually close; typically play an animat
ion. |
| 8421 * @protected |
| 8422 */ |
| 8268 _renderClosed: function() { | 8423 _renderClosed: function() { |
| 8269 if (this.withBackdrop) { | 8424 if (this.withBackdrop) { |
| 8270 this.backdropElement.close(); | 8425 this.backdropElement.close(); |
| 8271 } | 8426 } |
| 8272 this._finishRenderClosed(); | 8427 this._finishRenderClosed(); |
| 8273 }, | 8428 }, |
| 8274 | 8429 |
| 8430 /** |
| 8431 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. |
| 8432 * @protected |
| 8433 */ |
| 8275 _finishRenderOpened: function() { | 8434 _finishRenderOpened: function() { |
| 8276 // This ensures the overlay is visible before we set the focus | |
| 8277 // (by calling _onIronResize -> refit). | |
| 8278 this.notifyResize(); | |
| 8279 // Focus the child node with [autofocus] | 8435 // Focus the child node with [autofocus] |
| 8280 this._applyFocus(); | 8436 this._applyFocus(); |
| 8281 | 8437 |
| 8438 this.notifyResize(); |
| 8439 this.__isAnimating = false; |
| 8282 this.fire('iron-overlay-opened'); | 8440 this.fire('iron-overlay-opened'); |
| 8283 }, | 8441 }, |
| 8284 | 8442 |
| 8443 /** |
| 8444 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. |
| 8445 * @protected |
| 8446 */ |
| 8285 _finishRenderClosed: function() { | 8447 _finishRenderClosed: function() { |
| 8286 // Hide the overlay and remove the backdrop. | 8448 // Hide the overlay and remove the backdrop. |
| 8287 this.resetFit(); | |
| 8288 this.style.display = 'none'; | 8449 this.style.display = 'none'; |
| 8289 this._manager.removeOverlay(this); | 8450 // Reset z-index only at the end of the animation. |
| 8451 this.style.zIndex = ''; |
| 8290 | 8452 |
| 8291 this._applyFocus(); | 8453 this._applyFocus(); |
| 8454 |
| 8292 this.notifyResize(); | 8455 this.notifyResize(); |
| 8293 | 8456 this.__isAnimating = false; |
| 8294 this.fire('iron-overlay-closed', this.closingReason); | 8457 this.fire('iron-overlay-closed', this.closingReason); |
| 8295 }, | 8458 }, |
| 8296 | 8459 |
| 8297 _preparePositioning: function() { | 8460 _preparePositioning: function() { |
| 8298 this.style.transition = this.style.webkitTransition = 'none'; | 8461 this.style.transition = this.style.webkitTransition = 'none'; |
| 8299 this.style.transform = this.style.webkitTransform = 'none'; | 8462 this.style.transform = this.style.webkitTransform = 'none'; |
| 8300 this.style.display = ''; | 8463 this.style.display = ''; |
| 8301 }, | 8464 }, |
| 8302 | 8465 |
| 8303 _finishPositioning: function() { | 8466 _finishPositioning: function() { |
| 8467 // First, make it invisible & reactivate animations. |
| 8304 this.style.display = 'none'; | 8468 this.style.display = 'none'; |
| 8469 // Force reflow before re-enabling animations so that they don't start. |
| 8470 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 8471 this.scrollTop = this.scrollTop; |
| 8472 this.style.transition = this.style.webkitTransition = ''; |
| 8305 this.style.transform = this.style.webkitTransform = ''; | 8473 this.style.transform = this.style.webkitTransform = ''; |
| 8306 // Force layout layout to avoid application of transform. | 8474 // Now that animations are enabled, make it visible again |
| 8307 // Set offsetWidth to itself so that compilers won't remove it. | 8475 this.style.display = ''; |
| 8308 this.offsetWidth = this.offsetWidth; | 8476 // Force reflow, so that following animations are properly started. |
| 8309 this.style.transition = this.style.webkitTransition = ''; | 8477 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 8478 this.scrollTop = this.scrollTop; |
| 8310 }, | 8479 }, |
| 8311 | 8480 |
| 8481 /** |
| 8482 * Applies focus according to the opened state. |
| 8483 * @protected |
| 8484 */ |
| 8312 _applyFocus: function() { | 8485 _applyFocus: function() { |
| 8313 if (this.opened) { | 8486 if (this.opened) { |
| 8314 if (!this.noAutoFocus) { | 8487 if (!this.noAutoFocus) { |
| 8315 this._focusNode.focus(); | 8488 this._focusNode.focus(); |
| 8316 } | 8489 } |
| 8317 } else { | 8490 } else { |
| 8318 this._focusNode.blur(); | 8491 this._focusNode.blur(); |
| 8319 this._focusedChild = null; | 8492 this._focusedChild = null; |
| 8320 this._manager.focusOverlay(); | 8493 this._manager.focusOverlay(); |
| 8321 } | 8494 } |
| 8322 }, | 8495 }, |
| 8323 | 8496 |
| 8324 _onCaptureClick: function(event) { | |
| 8325 if (this._manager.currentOverlay() === this && | |
| 8326 Polymer.dom(event).path.indexOf(this) === -1) { | |
| 8327 if (this.noCancelOnOutsideClick) { | |
| 8328 this._applyFocus(); | |
| 8329 } else { | |
| 8330 this.cancel(event); | |
| 8331 } | |
| 8332 } | |
| 8333 }, | |
| 8334 | |
| 8335 _onCaptureFocus: function (event) { | |
| 8336 if (this._manager.currentOverlay() === this && this.withBackdrop) { | |
| 8337 var path = Polymer.dom(event).path; | |
| 8338 if (path.indexOf(this) === -1) { | |
| 8339 event.stopPropagation(); | |
| 8340 this._applyFocus(); | |
| 8341 } else { | |
| 8342 this._focusedChild = path[0]; | |
| 8343 } | |
| 8344 } | |
| 8345 }, | |
| 8346 | |
| 8347 _onIronResize: function() { | |
| 8348 if (this.opened) { | |
| 8349 this.refit(); | |
| 8350 } | |
| 8351 }, | |
| 8352 | |
| 8353 /** | 8497 /** |
| 8498 * Cancels (closes) the overlay. Call when click happens outside the overlay
. |
| 8499 * @param {!Event} event |
| 8354 * @protected | 8500 * @protected |
| 8355 * Will call notifyResize if overlay is opened. | |
| 8356 * Can be overridden in order to avoid multiple observers on the same node. | |
| 8357 */ | 8501 */ |
| 8358 _onNodesChange: function() { | 8502 _onCaptureClick: function(event) { |
| 8359 if (this.opened) { | 8503 if (!this.noCancelOnOutsideClick) { |
| 8360 this.notifyResize(); | 8504 this.cancel(event); |
| 8361 } | 8505 } |
| 8362 // Store it so we don't query too much. | |
| 8363 var focusableNodes = this._focusableNodes; | |
| 8364 this.__firstFocusableNode = focusableNodes[0]; | |
| 8365 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; | |
| 8366 }, | 8506 }, |
| 8367 | 8507 |
| 8368 __onEsc: function(event) { | 8508 /** |
| 8369 // Not opened or not on top, so return. | 8509 * Keeps track of the focused child. If withBackdrop, traps focus within ove
rlay. |
| 8370 if (this._manager.currentOverlay() !== this) { | 8510 * @param {!Event} event |
| 8511 * @protected |
| 8512 */ |
| 8513 _onCaptureFocus: function (event) { |
| 8514 if (!this.withBackdrop) { |
| 8371 return; | 8515 return; |
| 8372 } | 8516 } |
| 8517 var path = Polymer.dom(event).path; |
| 8518 if (path.indexOf(this) === -1) { |
| 8519 event.stopPropagation(); |
| 8520 this._applyFocus(); |
| 8521 } else { |
| 8522 this._focusedChild = path[0]; |
| 8523 } |
| 8524 }, |
| 8525 |
| 8526 /** |
| 8527 * Handles the ESC key event and cancels (closes) the overlay. |
| 8528 * @param {!Event} event |
| 8529 * @protected |
| 8530 */ |
| 8531 _onCaptureEsc: function(event) { |
| 8373 if (!this.noCancelOnEscKey) { | 8532 if (!this.noCancelOnEscKey) { |
| 8374 this.cancel(event); | 8533 this.cancel(event); |
| 8375 } | 8534 } |
| 8376 }, | 8535 }, |
| 8377 | 8536 |
| 8378 __onTab: function(event) { | 8537 /** |
| 8379 // Not opened or not on top, so return. | 8538 * Handles TAB key events to track focus changes. |
| 8380 if (this._manager.currentOverlay() !== this) { | 8539 * Will wrap focus for overlays withBackdrop. |
| 8381 return; | 8540 * @param {!Event} event |
| 8382 } | 8541 * @protected |
| 8542 */ |
| 8543 _onCaptureTab: function(event) { |
| 8383 // TAB wraps from last to first focusable. | 8544 // TAB wraps from last to first focusable. |
| 8384 // Shift + TAB wraps from first to last focusable. | 8545 // Shift + TAB wraps from first to last focusable. |
| 8385 var shift = event.detail.keyboardEvent.shiftKey; | 8546 var shift = event.shiftKey; |
| 8386 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; | 8547 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 8387 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; | 8548 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 8388 if (this.withBackdrop && this._focusedChild === nodeToCheck) { | 8549 if (this.withBackdrop && this._focusedChild === nodeToCheck) { |
| 8389 // We set here the _focusedChild so that _onCaptureFocus will handle the | 8550 // We set here the _focusedChild so that _onCaptureFocus will handle the |
| 8390 // wrapping of the focus (the next event after tab is focus). | 8551 // wrapping of the focus (the next event after tab is focus). |
| 8391 this._focusedChild = nodeToSet; | 8552 this._focusedChild = nodeToSet; |
| 8392 } | 8553 } |
| 8554 }, |
| 8555 |
| 8556 /** |
| 8557 * Refits if the overlay is opened and not animating. |
| 8558 * @protected |
| 8559 */ |
| 8560 _onIronResize: function() { |
| 8561 if (this.__onIronResizeAsync) { |
| 8562 cancelAnimationFrame(this.__onIronResizeAsync); |
| 8563 this.__onIronResizeAsync = null; |
| 8564 } |
| 8565 if (this.opened && !this.__isAnimating) { |
| 8566 this.__onIronResizeAsync = requestAnimationFrame(function() { |
| 8567 this.__onIronResizeAsync = null; |
| 8568 this.refit(); |
| 8569 }.bind(this)); |
| 8570 } |
| 8571 }, |
| 8572 |
| 8573 /** |
| 8574 * Will call notifyResize if overlay is opened. |
| 8575 * Can be overridden in order to avoid multiple observers on the same node. |
| 8576 * @protected |
| 8577 */ |
| 8578 _onNodesChange: function() { |
| 8579 if (this.opened && !this.__isAnimating) { |
| 8580 this.notifyResize(); |
| 8581 } |
| 8582 // Store it so we don't query too much. |
| 8583 var focusableNodes = this._focusableNodes; |
| 8584 this.__firstFocusableNode = focusableNodes[0]; |
| 8585 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 8393 } | 8586 } |
| 8394 }; | 8587 }; |
| 8395 | 8588 |
| 8396 /** @polymerBehavior */ | 8589 /** @polymerBehavior */ |
| 8397 Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBe
havior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; | 8590 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; |
| 8398 | 8591 |
| 8399 /** | 8592 /** |
| 8400 * Fired after the `iron-overlay` opens. | 8593 * Fired after the `iron-overlay` opens. |
| 8401 * @event iron-overlay-opened | 8594 * @event iron-overlay-opened |
| 8402 */ | 8595 */ |
| 8403 | 8596 |
| 8404 /** | 8597 /** |
| 8405 * Fired when the `iron-overlay` is canceled, but before it is closed. | 8598 * Fired when the `iron-overlay` is canceled, but before it is closed. |
| 8406 * Cancel the event to prevent the `iron-overlay` from closing. | 8599 * Cancel the event to prevent the `iron-overlay` from closing. |
| 8407 * @event iron-overlay-canceled | 8600 * @event iron-overlay-canceled |
| 8408 * @param {Event} event The closing of the `iron-overlay` can be prevented | 8601 * @param {Event} event The closing of the `iron-overlay` can be prevented |
| 8409 * by calling `event.preventDefault()`. The `event.detail` is the original even
t that originated | 8602 * by calling `event.preventDefault()`. The `event.detail` is the original even
t that originated |
| 8410 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). | 8603 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). |
| 8411 */ | 8604 */ |
| 8412 | 8605 |
| 8413 /** | 8606 /** |
| 8414 * Fired after the `iron-overlay` closes. | 8607 * Fired after the `iron-overlay` closes. |
| 8415 * @event iron-overlay-closed | 8608 * @event iron-overlay-closed |
| 8416 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). | 8609 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). |
| 8417 */ | 8610 */ |
| 8611 |
| 8612 })(); |
| 8418 /** | 8613 /** |
| 8419 * Use `Polymer.NeonAnimationBehavior` to implement an animation. | 8614 * Use `Polymer.NeonAnimationBehavior` to implement an animation. |
| 8420 * @polymerBehavior | 8615 * @polymerBehavior |
| 8421 */ | 8616 */ |
| 8422 Polymer.NeonAnimationBehavior = { | 8617 Polymer.NeonAnimationBehavior = { |
| 8423 | 8618 |
| 8424 properties: { | 8619 properties: { |
| 8425 | 8620 |
| 8426 /** | 8621 /** |
| 8427 * Defines the animation timing. | 8622 * Defines the animation timing. |
| 8428 */ | 8623 */ |
| 8429 animationTiming: { | 8624 animationTiming: { |
| 8430 type: Object, | 8625 type: Object, |
| 8431 value: function() { | 8626 value: function() { |
| 8432 return { | 8627 return { |
| 8433 duration: 500, | 8628 duration: 500, |
| 8434 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', | 8629 easing: 'cubic-bezier(0.4, 0, 0.2, 1)', |
| 8435 fill: 'both' | 8630 fill: 'both' |
| 8436 } | 8631 } |
| 8437 } | 8632 } |
| 8438 } | 8633 } |
| 8439 | 8634 |
| 8440 }, | 8635 }, |
| 8441 | 8636 |
| 8442 registered: function() { | 8637 /** |
| 8443 new Polymer.IronMeta({type: 'animation', key: this.is, value: this.constru
ctor}); | 8638 * Can be used to determine that elements implement this behavior. |
| 8444 }, | 8639 */ |
| 8640 isNeonAnimation: true, |
| 8445 | 8641 |
| 8446 /** | 8642 /** |
| 8447 * Do any animation configuration here. | 8643 * Do any animation configuration here. |
| 8448 */ | 8644 */ |
| 8449 // configure: function(config) { | 8645 // configure: function(config) { |
| 8450 // }, | 8646 // }, |
| 8451 | 8647 |
| 8452 /** | 8648 /** |
| 8453 * Returns the animation timing by mixing in properties from `config` to the
defaults defined | 8649 * Returns the animation timing by mixing in properties from `config` to the
defaults defined |
| 8454 * by the animation. | 8650 * by the animation. |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8653 }; | 8849 }; |
| 8654 /** | 8850 /** |
| 8655 * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. | 8851 * `Polymer.NeonAnimationRunnerBehavior` adds a method to run animations. |
| 8656 * | 8852 * |
| 8657 * @polymerBehavior Polymer.NeonAnimationRunnerBehavior | 8853 * @polymerBehavior Polymer.NeonAnimationRunnerBehavior |
| 8658 */ | 8854 */ |
| 8659 Polymer.NeonAnimationRunnerBehaviorImpl = { | 8855 Polymer.NeonAnimationRunnerBehaviorImpl = { |
| 8660 | 8856 |
| 8661 properties: { | 8857 properties: { |
| 8662 | 8858 |
| 8663 _animationMeta: { | |
| 8664 type: Object, | |
| 8665 value: function() { | |
| 8666 return new Polymer.IronMeta({type: 'animation'}); | |
| 8667 } | |
| 8668 }, | |
| 8669 | |
| 8670 /** @type {?Object} */ | 8859 /** @type {?Object} */ |
| 8671 _player: { | 8860 _player: { |
| 8672 type: Object | 8861 type: Object |
| 8673 } | 8862 } |
| 8674 | 8863 |
| 8675 }, | 8864 }, |
| 8676 | 8865 |
| 8677 _configureAnimationEffects: function(allConfigs) { | 8866 _configureAnimationEffects: function(allConfigs) { |
| 8678 var allAnimations = []; | 8867 var allAnimations = []; |
| 8679 if (allConfigs.length > 0) { | 8868 if (allConfigs.length > 0) { |
| 8680 for (var config, index = 0; config = allConfigs[index]; index++) { | 8869 for (var config, index = 0; config = allConfigs[index]; index++) { |
| 8681 var animationConstructor = this._animationMeta.byKey(config.name); | 8870 var animation = document.createElement(config.name); |
| 8682 if (animationConstructor) { | 8871 // is this element actually a neon animation? |
| 8683 var animation = animationConstructor && new animationConstructor(); | 8872 if (animation.isNeonAnimation) { |
| 8684 var effect = animation.configure(config); | 8873 var effect = animation.configure(config); |
| 8685 if (effect) { | 8874 if (effect) { |
| 8686 allAnimations.push({ | 8875 allAnimations.push({ |
| 8687 animation: animation, | 8876 animation: animation, |
| 8688 config: config, | 8877 config: config, |
| 8689 effect: effect | 8878 effect: effect |
| 8690 }); | 8879 }); |
| 8691 } | 8880 } |
| 8692 } else { | 8881 } else { |
| 8693 console.warn(this.is + ':', config.name, 'not found!'); | 8882 console.warn(this.is + ':', config.name, 'not found!'); |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8911 if (this._composedTreeContains(distributedNodes[nodeIndex], child))
{ | 9100 if (this._composedTreeContains(distributedNodes[nodeIndex], child))
{ |
| 8912 return true; | 9101 return true; |
| 8913 } | 9102 } |
| 8914 } | 9103 } |
| 8915 } | 9104 } |
| 8916 | 9105 |
| 8917 return false; | 9106 return false; |
| 8918 }, | 9107 }, |
| 8919 | 9108 |
| 8920 _scrollInteractionHandler: function(event) { | 9109 _scrollInteractionHandler: function(event) { |
| 9110 var scrolledElement = |
| 9111 /** @type {HTMLElement} */(Polymer.dom(event).rootTarget); |
| 8921 if (Polymer | 9112 if (Polymer |
| 8922 .IronDropdownScrollManager | 9113 .IronDropdownScrollManager |
| 8923 .elementIsScrollLocked(Polymer.dom(event).rootTarget)) { | 9114 .elementIsScrollLocked(scrolledElement)) { |
| 8924 if (event.type === 'keydown' && | 9115 if (event.type === 'keydown' && |
| 8925 !Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) { | 9116 !Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) { |
| 8926 return; | 9117 return; |
| 8927 } | 9118 } |
| 8928 | 9119 |
| 8929 event.preventDefault(); | 9120 event.preventDefault(); |
| 8930 } | 9121 } |
| 8931 }, | 9122 }, |
| 8932 | 9123 |
| 8933 _lockScrollInteractions: function() { | 9124 _lockScrollInteractions: function() { |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9043 type: Number, | 9234 type: Number, |
| 9044 value: 0, | 9235 value: 0, |
| 9045 notify: true | 9236 notify: true |
| 9046 }, | 9237 }, |
| 9047 | 9238 |
| 9048 /** | 9239 /** |
| 9049 * The element that should be used to position the dropdown when | 9240 * The element that should be used to position the dropdown when |
| 9050 * it is opened. | 9241 * it is opened. |
| 9051 */ | 9242 */ |
| 9052 positionTarget: { | 9243 positionTarget: { |
| 9053 type: Object, | 9244 type: Object |
| 9054 observer: '_positionTargetChanged' | |
| 9055 }, | 9245 }, |
| 9056 | 9246 |
| 9057 /** | 9247 /** |
| 9058 * An animation config. If provided, this will be used to animate the | 9248 * An animation config. If provided, this will be used to animate the |
| 9059 * opening of the dropdown. | 9249 * opening of the dropdown. |
| 9060 */ | 9250 */ |
| 9061 openAnimationConfig: { | 9251 openAnimationConfig: { |
| 9062 type: Object | 9252 type: Object |
| 9063 }, | 9253 }, |
| 9064 | 9254 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 9089 | 9279 |
| 9090 /** | 9280 /** |
| 9091 * By default, the dropdown will constrain scrolling on the page | 9281 * By default, the dropdown will constrain scrolling on the page |
| 9092 * to itself when opened. | 9282 * to itself when opened. |
| 9093 * Set to true in order to prevent scroll from being constrained | 9283 * Set to true in order to prevent scroll from being constrained |
| 9094 * to the dropdown when it opens. | 9284 * to the dropdown when it opens. |
| 9095 */ | 9285 */ |
| 9096 allowOutsideScroll: { | 9286 allowOutsideScroll: { |
| 9097 type: Boolean, | 9287 type: Boolean, |
| 9098 value: false | 9288 value: false |
| 9099 }, | |
| 9100 | |
| 9101 /** | |
| 9102 * We memoize the positionTarget bounding rectangle so that we can | |
| 9103 * limit the number of times it is queried per resize / relayout. | |
| 9104 * @type {?Object} | |
| 9105 */ | |
| 9106 _positionRectMemo: { | |
| 9107 type: Object | |
| 9108 } | 9289 } |
| 9109 }, | 9290 }, |
| 9110 | 9291 |
| 9111 listeners: { | 9292 listeners: { |
| 9112 'neon-animation-finish': '_onNeonAnimationFinish' | 9293 'neon-animation-finish': '_onNeonAnimationFinish' |
| 9113 }, | 9294 }, |
| 9114 | 9295 |
| 9115 observers: [ | 9296 observers: [ |
| 9116 '_updateOverlayPosition(verticalAlign, horizontalAlign, verticalOffset
, horizontalOffset)' | 9297 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' |
| 9117 ], | 9298 ], |
| 9118 | 9299 |
| 9119 attached: function() { | 9300 attached: function() { |
| 9120 if (this.positionTarget === undefined) { | 9301 this.positionTarget = this.positionTarget || this._defaultPositionTarg
et; |
| 9121 this.positionTarget = this._defaultPositionTarget; | 9302 // Memoize this to avoid expensive calculations & relayouts. |
| 9122 } | 9303 this._isRTL = window.getComputedStyle(this).direction == 'rtl'; |
| 9123 }, | 9304 }, |
| 9124 | 9305 |
| 9125 /** | 9306 /** |
| 9126 * The element that is contained by the dropdown, if any. | 9307 * The element that is contained by the dropdown, if any. |
| 9127 */ | 9308 */ |
| 9128 get containedElement() { | 9309 get containedElement() { |
| 9129 return Polymer.dom(this.$.content).getDistributedNodes()[0]; | 9310 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| 9130 }, | 9311 }, |
| 9131 | 9312 |
| 9132 /** | 9313 /** |
| 9133 * The element that should be focused when the dropdown opens. | 9314 * The element that should be focused when the dropdown opens. |
| 9134 * @deprecated | 9315 * @deprecated |
| 9135 */ | 9316 */ |
| 9136 get _focusTarget() { | 9317 get _focusTarget() { |
| 9137 return this.focusTarget || this.containedElement; | 9318 return this.focusTarget || this.containedElement; |
| 9138 }, | 9319 }, |
| 9139 | 9320 |
| 9140 /** | 9321 /** |
| 9141 * Whether the text direction is RTL | |
| 9142 */ | |
| 9143 _isRTL: function() { | |
| 9144 return window.getComputedStyle(this).direction == 'rtl'; | |
| 9145 }, | |
| 9146 | |
| 9147 /** | |
| 9148 * The element that should be used to position the dropdown when | 9322 * The element that should be used to position the dropdown when |
| 9149 * it opens, if no position target is configured. | 9323 * it opens, if no position target is configured. |
| 9150 */ | 9324 */ |
| 9151 get _defaultPositionTarget() { | 9325 get _defaultPositionTarget() { |
| 9152 var parent = Polymer.dom(this).parentNode; | 9326 var parent = Polymer.dom(this).parentNode; |
| 9153 | 9327 |
| 9154 if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | 9328 if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { |
| 9155 parent = parent.host; | 9329 parent = parent.host; |
| 9156 } | 9330 } |
| 9157 | 9331 |
| 9158 return parent; | 9332 return parent; |
| 9159 }, | 9333 }, |
| 9160 | 9334 |
| 9161 /** | 9335 /** |
| 9162 * The bounding rect of the position target. | 9336 * The horizontal align value, accounting for the RTL/LTR text direction
. |
| 9163 */ | 9337 */ |
| 9164 get _positionRect() { | 9338 get _localeHorizontalAlign() { |
| 9165 if (!this._positionRectMemo && this.positionTarget) { | 9339 // In RTL, "left" becomes "right". |
| 9166 this._positionRectMemo = this.positionTarget.getBoundingClientRect()
; | 9340 if (this._isRTL) { |
| 9341 return this.horizontalAlign === 'right' ? 'left' : 'right'; |
| 9342 } else { |
| 9343 return this.horizontalAlign; |
| 9167 } | 9344 } |
| 9168 | |
| 9169 return this._positionRectMemo; | |
| 9170 }, | 9345 }, |
| 9171 | 9346 |
| 9172 /** | 9347 /** |
| 9173 * The horizontal offset value used to position the dropdown. | 9348 * The horizontal offset value used to position the dropdown. |
| 9349 * @param {ClientRect} dropdownRect |
| 9350 * @param {ClientRect} positionRect |
| 9351 * @param {boolean=false} fromRight |
| 9352 * @return {number} pixels |
| 9353 * @private |
| 9174 */ | 9354 */ |
| 9175 get _horizontalAlignTargetValue() { | 9355 _horizontalAlignTargetValue: function(dropdownRect, positionRect, fromRi
ght) { |
| 9176 var target; | 9356 var target; |
| 9177 | 9357 if (fromRight) { |
| 9178 // In RTL, the direction flips, so what is "right" in LTR becomes "lef
t". | 9358 target = document.documentElement.clientWidth - positionRect.right -
(this._fitWidth - dropdownRect.right); |
| 9179 var isRTL = this._isRTL(); | |
| 9180 if ((!isRTL && this.horizontalAlign === 'right') || | |
| 9181 (isRTL && this.horizontalAlign === 'left')) { | |
| 9182 target = document.documentElement.clientWidth - this._positionRect.r
ight; | |
| 9183 } else { | 9359 } else { |
| 9184 target = this._positionRect.left; | 9360 target = positionRect.left - dropdownRect.left; |
| 9185 } | 9361 } |
| 9186 | |
| 9187 target += this.horizontalOffset; | 9362 target += this.horizontalOffset; |
| 9188 | 9363 |
| 9189 return Math.max(target, 0); | 9364 return Math.max(target, 0); |
| 9190 }, | 9365 }, |
| 9191 | 9366 |
| 9192 /** | 9367 /** |
| 9193 * The vertical offset value used to position the dropdown. | 9368 * The vertical offset value used to position the dropdown. |
| 9369 * @param {ClientRect} dropdownRect |
| 9370 * @param {ClientRect} positionRect |
| 9371 * @param {boolean=false} fromBottom |
| 9372 * @return {number} pixels |
| 9373 * @private |
| 9194 */ | 9374 */ |
| 9195 get _verticalAlignTargetValue() { | 9375 _verticalAlignTargetValue: function(dropdownRect, positionRect, fromBott
om) { |
| 9196 var target; | 9376 var target; |
| 9197 | 9377 if (fromBottom) { |
| 9198 if (this.verticalAlign === 'bottom') { | 9378 target = document.documentElement.clientHeight - positionRect.bottom
- (this._fitHeight - dropdownRect.bottom); |
| 9199 target = document.documentElement.clientHeight - this._positionRect.
bottom; | |
| 9200 } else { | 9379 } else { |
| 9201 target = this._positionRect.top; | 9380 target = positionRect.top - dropdownRect.top; |
| 9202 } | 9381 } |
| 9203 | |
| 9204 target += this.verticalOffset; | 9382 target += this.verticalOffset; |
| 9205 | 9383 |
| 9206 return Math.max(target, 0); | 9384 return Math.max(target, 0); |
| 9207 }, | 9385 }, |
| 9208 | 9386 |
| 9209 /** | 9387 /** |
| 9210 * The horizontal align value, accounting for the RTL/LTR text direction
. | |
| 9211 */ | |
| 9212 get _localeHorizontalAlign() { | |
| 9213 // In RTL, "left" becomes "right". | |
| 9214 if (this._isRTL()) { | |
| 9215 return this.horizontalAlign === 'right' ? 'left' : 'right'; | |
| 9216 } else { | |
| 9217 return this.horizontalAlign; | |
| 9218 } | |
| 9219 }, | |
| 9220 | |
| 9221 /** | |
| 9222 * Called when the value of `opened` changes. | 9388 * Called when the value of `opened` changes. |
| 9223 * | 9389 * |
| 9224 * @param {boolean} opened True if the dropdown is opened. | 9390 * @param {boolean} opened True if the dropdown is opened. |
| 9225 */ | 9391 */ |
| 9226 _openedChanged: function(opened) { | 9392 _openedChanged: function(opened) { |
| 9227 if (opened && this.disabled) { | 9393 if (opened && this.disabled) { |
| 9228 this.cancel(); | 9394 this.cancel(); |
| 9229 } else { | 9395 } else { |
| 9230 this.cancelAnimation(); | 9396 this.cancelAnimation(); |
| 9231 this._prepareDropdown(); | 9397 this.sizingTarget = this.containedElement || this.sizingTarget; |
| 9398 this._updateAnimationConfig(); |
| 9232 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); | 9399 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); |
| 9233 } | 9400 } |
| 9234 }, | 9401 }, |
| 9235 | 9402 |
| 9236 /** | 9403 /** |
| 9237 * Overridden from `IronOverlayBehavior`. | 9404 * Overridden from `IronOverlayBehavior`. |
| 9238 */ | 9405 */ |
| 9239 _renderOpened: function() { | 9406 _renderOpened: function() { |
| 9240 if (!this.allowOutsideScroll) { | 9407 if (!this.allowOutsideScroll) { |
| 9241 Polymer.IronDropdownScrollManager.pushScrollLock(this); | 9408 Polymer.IronDropdownScrollManager.pushScrollLock(this); |
| 9242 } | 9409 } |
| 9243 | 9410 |
| 9244 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { | 9411 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { |
| 9412 if (this.withBackdrop) { |
| 9413 this.backdropElement.open(); |
| 9414 } |
| 9245 this.$.contentWrapper.classList.add('animating'); | 9415 this.$.contentWrapper.classList.add('animating'); |
| 9246 this.playAnimation('open'); | 9416 this.playAnimation('open'); |
| 9247 } else { | 9417 } else { |
| 9248 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; | 9418 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; |
| 9249 } | 9419 } |
| 9250 }, | 9420 }, |
| 9251 | 9421 |
| 9252 /** | 9422 /** |
| 9253 * Overridden from `IronOverlayBehavior`. | 9423 * Overridden from `IronOverlayBehavior`. |
| 9254 */ | 9424 */ |
| 9255 _renderClosed: function() { | 9425 _renderClosed: function() { |
| 9256 Polymer.IronDropdownScrollManager.removeScrollLock(this); | 9426 Polymer.IronDropdownScrollManager.removeScrollLock(this); |
| 9257 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { | 9427 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { |
| 9428 if (this.withBackdrop) { |
| 9429 this.backdropElement.close(); |
| 9430 } |
| 9258 this.$.contentWrapper.classList.add('animating'); | 9431 this.$.contentWrapper.classList.add('animating'); |
| 9259 this.playAnimation('close'); | 9432 this.playAnimation('close'); |
| 9260 } else { | 9433 } else { |
| 9261 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; | 9434 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; |
| 9262 } | 9435 } |
| 9263 }, | 9436 }, |
| 9264 | 9437 |
| 9265 /** | 9438 /** |
| 9266 * Called when animation finishes on the dropdown (when opening or | 9439 * Called when animation finishes on the dropdown (when opening or |
| 9267 * closing). Responsible for "completing" the process of opening or | 9440 * closing). Responsible for "completing" the process of opening or |
| 9268 * closing the dropdown by positioning it or setting its display to | 9441 * closing the dropdown by positioning it or setting its display to |
| 9269 * none. | 9442 * none. |
| 9270 */ | 9443 */ |
| 9271 _onNeonAnimationFinish: function() { | 9444 _onNeonAnimationFinish: function() { |
| 9272 this.$.contentWrapper.classList.remove('animating'); | 9445 this.$.contentWrapper.classList.remove('animating'); |
| 9273 if (this.opened) { | 9446 if (this.opened) { |
| 9274 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this); | 9447 Polymer.IronOverlayBehaviorImpl._finishRenderOpened.apply(this); |
| 9275 } else { | 9448 } else { |
| 9276 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this); | 9449 Polymer.IronOverlayBehaviorImpl._finishRenderClosed.apply(this); |
| 9277 } | 9450 } |
| 9278 }, | 9451 }, |
| 9279 | 9452 |
| 9280 /** | 9453 /** |
| 9281 * Called when an `iron-resize` event fires. | |
| 9282 */ | |
| 9283 _onIronResize: function() { | |
| 9284 var containedElement = this.containedElement; | |
| 9285 var scrollTop; | |
| 9286 var scrollLeft; | |
| 9287 | |
| 9288 if (this.opened && containedElement) { | |
| 9289 scrollTop = containedElement.scrollTop; | |
| 9290 scrollLeft = containedElement.scrollLeft; | |
| 9291 } | |
| 9292 | |
| 9293 if (this.opened) { | |
| 9294 this._updateOverlayPosition(); | |
| 9295 } | |
| 9296 | |
| 9297 Polymer.IronOverlayBehaviorImpl._onIronResize.apply(this, arguments); | |
| 9298 | |
| 9299 if (this.opened && containedElement) { | |
| 9300 containedElement.scrollTop = scrollTop; | |
| 9301 containedElement.scrollLeft = scrollLeft; | |
| 9302 } | |
| 9303 }, | |
| 9304 | |
| 9305 /** | |
| 9306 * Called when the `positionTarget` property changes. | |
| 9307 */ | |
| 9308 _positionTargetChanged: function() { | |
| 9309 this._updateOverlayPosition(); | |
| 9310 }, | |
| 9311 | |
| 9312 /** | |
| 9313 * Constructs the final animation config from different properties used | 9454 * Constructs the final animation config from different properties used |
| 9314 * to configure specific parts of the opening and closing animations. | 9455 * to configure specific parts of the opening and closing animations. |
| 9315 */ | 9456 */ |
| 9316 _updateAnimationConfig: function() { | 9457 _updateAnimationConfig: function() { |
| 9317 var animationConfig = {}; | 9458 var animationConfig = {}; |
| 9318 var animations = []; | 9459 var animations = []; |
| 9319 | 9460 |
| 9320 if (this.openAnimationConfig) { | 9461 if (this.openAnimationConfig) { |
| 9321 // NOTE(cdata): When making `display:none` elements visible in Safar
i, | 9462 // NOTE(cdata): When making `display:none` elements visible in Safar
i, |
| 9322 // the element will paint once in a fully visible state, causing the | 9463 // the element will paint once in a fully visible state, causing the |
| (...skipping 11 matching lines...) Expand all Loading... |
| 9334 } | 9475 } |
| 9335 | 9476 |
| 9336 animations.forEach(function(animation) { | 9477 animations.forEach(function(animation) { |
| 9337 animation.node = this.containedElement; | 9478 animation.node = this.containedElement; |
| 9338 }, this); | 9479 }, this); |
| 9339 | 9480 |
| 9340 this.animationConfig = animationConfig; | 9481 this.animationConfig = animationConfig; |
| 9341 }, | 9482 }, |
| 9342 | 9483 |
| 9343 /** | 9484 /** |
| 9344 * Prepares the dropdown for opening by updating measured layout | |
| 9345 * values. | |
| 9346 */ | |
| 9347 _prepareDropdown: function() { | |
| 9348 this.sizingTarget = this.containedElement || this.sizingTarget; | |
| 9349 this._updateAnimationConfig(); | |
| 9350 this._updateOverlayPosition(); | |
| 9351 }, | |
| 9352 | |
| 9353 /** | |
| 9354 * Updates the overlay position based on configured horizontal | 9485 * Updates the overlay position based on configured horizontal |
| 9355 * and vertical alignment, and re-memoizes these values for the sake | 9486 * and vertical alignment. |
| 9356 * of behavior in `IronFitBehavior`. | |
| 9357 */ | 9487 */ |
| 9358 _updateOverlayPosition: function() { | 9488 _updateOverlayPosition: function() { |
| 9359 this._positionRectMemo = null; | 9489 if (this.isAttached) { |
| 9360 | 9490 // This triggers iron-resize, and iron-overlay-behavior will call re
fit if needed. |
| 9361 if (!this.positionTarget) { | 9491 this.notifyResize(); |
| 9362 return; | |
| 9363 } | |
| 9364 | |
| 9365 this.style[this._localeHorizontalAlign] = | |
| 9366 this._horizontalAlignTargetValue + 'px'; | |
| 9367 | |
| 9368 this.style[this.verticalAlign] = | |
| 9369 this._verticalAlignTargetValue + 'px'; | |
| 9370 | |
| 9371 // NOTE(cdata): We re-memoize inline styles here, otherwise | |
| 9372 // calling `refit` from `IronFitBehavior` will reset inline styles | |
| 9373 // to whatever they were when the dropdown first opened. | |
| 9374 if (this._fitInfo) { | |
| 9375 this._fitInfo.inlineStyle[this.horizontalAlign] = | |
| 9376 this.style[this.horizontalAlign]; | |
| 9377 | |
| 9378 this._fitInfo.inlineStyle[this.verticalAlign] = | |
| 9379 this.style[this.verticalAlign]; | |
| 9380 } | 9492 } |
| 9381 }, | 9493 }, |
| 9382 | 9494 |
| 9383 /** | 9495 /** |
| 9496 * Useful to call this after the element, the window, or the `fitInfo` |
| 9497 * element has been resized. Will maintain the scroll position. |
| 9498 */ |
| 9499 refit: function () { |
| 9500 if (!this.opened) { |
| 9501 return |
| 9502 } |
| 9503 var containedElement = this.containedElement; |
| 9504 var scrollTop; |
| 9505 var scrollLeft; |
| 9506 |
| 9507 if (containedElement) { |
| 9508 scrollTop = containedElement.scrollTop; |
| 9509 scrollLeft = containedElement.scrollLeft; |
| 9510 } |
| 9511 Polymer.IronFitBehavior.refit.apply(this, arguments); |
| 9512 |
| 9513 if (containedElement) { |
| 9514 containedElement.scrollTop = scrollTop; |
| 9515 containedElement.scrollLeft = scrollLeft; |
| 9516 } |
| 9517 }, |
| 9518 |
| 9519 /** |
| 9520 * Resets the target element's position and size constraints, and clear |
| 9521 * the memoized data. |
| 9522 */ |
| 9523 resetFit: function() { |
| 9524 Polymer.IronFitBehavior.resetFit.apply(this, arguments); |
| 9525 |
| 9526 var hAlign = this._localeHorizontalAlign; |
| 9527 var vAlign = this.verticalAlign; |
| 9528 // Set to 0, 0 in order to discover any offset caused by parent stacki
ng contexts. |
| 9529 this.style[hAlign] = this.style[vAlign] = '0px'; |
| 9530 |
| 9531 var dropdownRect = this.getBoundingClientRect(); |
| 9532 var positionRect = this.positionTarget.getBoundingClientRect(); |
| 9533 var horizontalValue = this._horizontalAlignTargetValue(dropdownRect, p
ositionRect, hAlign === 'right'); |
| 9534 var verticalValue = this._verticalAlignTargetValue(dropdownRect, posit
ionRect, vAlign === 'bottom'); |
| 9535 |
| 9536 this.style[hAlign] = horizontalValue + 'px'; |
| 9537 this.style[vAlign] = verticalValue + 'px'; |
| 9538 }, |
| 9539 |
| 9540 /** |
| 9541 * Overridden from `IronFitBehavior`. |
| 9542 * Ensure positionedBy has correct values for horizontally & vertically. |
| 9543 */ |
| 9544 _discoverInfo: function() { |
| 9545 Polymer.IronFitBehavior._discoverInfo.apply(this, arguments); |
| 9546 // Note(valdrin): in Firefox, an element with style `position: fixed;
bottom: 90vh; height: 20vh` |
| 9547 // would have `getComputedStyle(element).top < 0` (instead of being `a
uto`) http://jsbin.com/cofired/3/edit?html,output |
| 9548 // This would cause IronFitBehavior's `constrain` to wrongly calculate
sizes |
| 9549 // (it would use `top` instead of `bottom`), so we ensure we give the
correct values. |
| 9550 this._fitInfo.positionedBy.horizontally = this._localeHorizontalAlign; |
| 9551 this._fitInfo.positionedBy.vertically = this.verticalAlign; |
| 9552 }, |
| 9553 |
| 9554 /** |
| 9384 * Apply focus to focusTarget or containedElement | 9555 * Apply focus to focusTarget or containedElement |
| 9385 */ | 9556 */ |
| 9386 _applyFocus: function () { | 9557 _applyFocus: function () { |
| 9387 var focusTarget = this.focusTarget || this.containedElement; | 9558 var focusTarget = this.focusTarget || this.containedElement; |
| 9388 if (focusTarget && this.opened && !this.noAutoFocus) { | 9559 if (focusTarget && this.opened && !this.noAutoFocus) { |
| 9389 focusTarget.focus(); | 9560 focusTarget.focus(); |
| 9390 } else { | 9561 } else { |
| 9391 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); | 9562 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); |
| 9392 } | 9563 } |
| 9393 } | 9564 } |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9515 transform: 'translateY(0)' | 9686 transform: 'translateY(0)' |
| 9516 }, { | 9687 }, { |
| 9517 height: height / 2 + 'px', | 9688 height: height / 2 + 'px', |
| 9518 transform: 'translateY(-20px)' | 9689 transform: 'translateY(-20px)' |
| 9519 }], this.timingFromConfig(config)); | 9690 }], this.timingFromConfig(config)); |
| 9520 | 9691 |
| 9521 return this._effect; | 9692 return this._effect; |
| 9522 } | 9693 } |
| 9523 }); | 9694 }); |
| 9524 (function() { | 9695 (function() { |
| 9525 'use strict'; | 9696 'use strict'; |
| 9526 | 9697 |
| 9527 var PaperMenuButton = Polymer({ | 9698 var PaperMenuButton = Polymer({ |
| 9528 is: 'paper-menu-button', | 9699 is: 'paper-menu-button', |
| 9529 | 9700 |
| 9530 /** | 9701 /** |
| 9531 * Fired when the dropdown opens. | 9702 * Fired when the dropdown opens. |
| 9532 * | 9703 * |
| 9533 * @event paper-dropdown-open | 9704 * @event paper-dropdown-open |
| 9534 */ | 9705 */ |
| 9535 | 9706 |
| 9536 /** | 9707 /** |
| 9537 * Fired when the dropdown closes. | 9708 * Fired when the dropdown closes. |
| 9538 * | 9709 * |
| 9539 * @event paper-dropdown-close | 9710 * @event paper-dropdown-close |
| 9540 */ | 9711 */ |
| 9541 | 9712 |
| 9542 behaviors: [ | 9713 behaviors: [ |
| 9543 Polymer.IronA11yKeysBehavior, | 9714 Polymer.IronA11yKeysBehavior, |
| 9544 Polymer.IronControlState | 9715 Polymer.IronControlState |
| 9545 ], | 9716 ], |
| 9546 | 9717 |
| 9547 properties: { | 9718 properties: { |
| 9548 | 9719 /** |
| 9549 /** | 9720 * True if the content is currently displayed. |
| 9550 * True if the content is currently displayed. | 9721 */ |
| 9551 */ | 9722 opened: { |
| 9552 opened: { | 9723 type: Boolean, |
| 9553 type: Boolean, | 9724 value: false, |
| 9554 value: false, | 9725 notify: true, |
| 9555 notify: true, | 9726 observer: '_openedChanged' |
| 9556 observer: '_openedChanged' | 9727 }, |
| 9557 }, | 9728 |
| 9558 | 9729 /** |
| 9559 /** | 9730 * The orientation against which to align the menu dropdown |
| 9560 * The orientation against which to align the menu dropdown | 9731 * horizontally relative to the dropdown trigger. |
| 9561 * horizontally relative to the dropdown trigger. | 9732 */ |
| 9562 */ | 9733 horizontalAlign: { |
| 9563 horizontalAlign: { | 9734 type: String, |
| 9564 type: String, | 9735 value: 'left', |
| 9565 value: 'left', | 9736 reflectToAttribute: true |
| 9566 reflectToAttribute: true | 9737 }, |
| 9567 }, | 9738 |
| 9568 | 9739 /** |
| 9569 /** | 9740 * The orientation against which to align the menu dropdown |
| 9570 * The orientation against which to align the menu dropdown | 9741 * vertically relative to the dropdown trigger. |
| 9571 * vertically relative to the dropdown trigger. | 9742 */ |
| 9572 */ | 9743 verticalAlign: { |
| 9573 verticalAlign: { | 9744 type: String, |
| 9574 type: String, | 9745 value: 'top', |
| 9575 value: 'top', | 9746 reflectToAttribute: true |
| 9576 reflectToAttribute: true | 9747 }, |
| 9577 }, | 9748 |
| 9578 | 9749 /** |
| 9579 /** | 9750 * A pixel value that will be added to the position calculated for the |
| 9580 * A pixel value that will be added to the position calculated for the | 9751 * given `horizontalAlign`. Use a negative value to offset to the |
| 9581 * given `horizontalAlign`. Use a negative value to offset to the | 9752 * left, or a positive value to offset to the right. |
| 9582 * left, or a positive value to offset to the right. | 9753 */ |
| 9583 */ | 9754 horizontalOffset: { |
| 9584 horizontalOffset: { | 9755 type: Number, |
| 9585 type: Number, | 9756 value: 0, |
| 9586 value: 0, | 9757 notify: true |
| 9587 notify: true | 9758 }, |
| 9588 }, | 9759 |
| 9589 | 9760 /** |
| 9590 /** | 9761 * A pixel value that will be added to the position calculated for the |
| 9591 * A pixel value that will be added to the position calculated for the | 9762 * given `verticalAlign`. Use a negative value to offset towards the |
| 9592 * given `verticalAlign`. Use a negative value to offset towards the | 9763 * top, or a positive value to offset towards the bottom. |
| 9593 * top, or a positive value to offset towards the bottom. | 9764 */ |
| 9594 */ | 9765 verticalOffset: { |
| 9595 verticalOffset: { | 9766 type: Number, |
| 9596 type: Number, | 9767 value: 0, |
| 9597 value: 0, | 9768 notify: true |
| 9598 notify: true | 9769 }, |
| 9599 }, | 9770 |
| 9600 | 9771 /** |
| 9601 /** | 9772 * Set to true to disable animations when opening and closing the |
| 9602 * Set to true to disable animations when opening and closing the | 9773 * dropdown. |
| 9774 */ |
| 9775 noAnimations: { |
| 9776 type: Boolean, |
| 9777 value: false |
| 9778 }, |
| 9779 |
| 9780 /** |
| 9781 * Set to true to disable automatically closing the dropdown after |
| 9782 * a selection has been made. |
| 9783 */ |
| 9784 ignoreSelect: { |
| 9785 type: Boolean, |
| 9786 value: false |
| 9787 }, |
| 9788 |
| 9789 /** |
| 9790 * An animation config. If provided, this will be used to animate the |
| 9791 * opening of the dropdown. |
| 9792 */ |
| 9793 openAnimationConfig: { |
| 9794 type: Object, |
| 9795 value: function() { |
| 9796 return [{ |
| 9797 name: 'fade-in-animation', |
| 9798 timing: { |
| 9799 delay: 100, |
| 9800 duration: 200 |
| 9801 } |
| 9802 }, { |
| 9803 name: 'paper-menu-grow-width-animation', |
| 9804 timing: { |
| 9805 delay: 100, |
| 9806 duration: 150, |
| 9807 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9808 } |
| 9809 }, { |
| 9810 name: 'paper-menu-grow-height-animation', |
| 9811 timing: { |
| 9812 delay: 100, |
| 9813 duration: 275, |
| 9814 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9815 } |
| 9816 }]; |
| 9817 } |
| 9818 }, |
| 9819 |
| 9820 /** |
| 9821 * An animation config. If provided, this will be used to animate the |
| 9822 * closing of the dropdown. |
| 9823 */ |
| 9824 closeAnimationConfig: { |
| 9825 type: Object, |
| 9826 value: function() { |
| 9827 return [{ |
| 9828 name: 'fade-out-animation', |
| 9829 timing: { |
| 9830 duration: 150 |
| 9831 } |
| 9832 }, { |
| 9833 name: 'paper-menu-shrink-width-animation', |
| 9834 timing: { |
| 9835 delay: 100, |
| 9836 duration: 50, |
| 9837 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9838 } |
| 9839 }, { |
| 9840 name: 'paper-menu-shrink-height-animation', |
| 9841 timing: { |
| 9842 duration: 200, |
| 9843 easing: 'ease-in' |
| 9844 } |
| 9845 }]; |
| 9846 } |
| 9847 }, |
| 9848 |
| 9849 /** |
| 9850 * This is the element intended to be bound as the focus target |
| 9851 * for the `iron-dropdown` contained by `paper-menu-button`. |
| 9852 */ |
| 9853 _dropdownContent: { |
| 9854 type: Object |
| 9855 } |
| 9856 }, |
| 9857 |
| 9858 hostAttributes: { |
| 9859 role: 'group', |
| 9860 'aria-haspopup': 'true' |
| 9861 }, |
| 9862 |
| 9863 listeners: { |
| 9864 'iron-select': '_onIronSelect' |
| 9865 }, |
| 9866 |
| 9867 /** |
| 9868 * The content element that is contained by the menu button, if any. |
| 9869 */ |
| 9870 get contentElement() { |
| 9871 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| 9872 }, |
| 9873 |
| 9874 /** |
| 9875 * Toggles the drowpdown content between opened and closed. |
| 9876 */ |
| 9877 toggle: function() { |
| 9878 if (this.opened) { |
| 9879 this.close(); |
| 9880 } else { |
| 9881 this.open(); |
| 9882 } |
| 9883 }, |
| 9884 |
| 9885 /** |
| 9886 * Make the dropdown content appear as an overlay positioned relative |
| 9887 * to the dropdown trigger. |
| 9888 */ |
| 9889 open: function() { |
| 9890 if (this.disabled) { |
| 9891 return; |
| 9892 } |
| 9893 |
| 9894 this.$.dropdown.open(); |
| 9895 }, |
| 9896 |
| 9897 /** |
| 9898 * Hide the dropdown content. |
| 9899 */ |
| 9900 close: function() { |
| 9901 this.$.dropdown.close(); |
| 9902 }, |
| 9903 |
| 9904 /** |
| 9905 * When an `iron-select` event is received, the dropdown should |
| 9906 * automatically close on the assumption that a value has been chosen. |
| 9907 * |
| 9908 * @param {CustomEvent} event A CustomEvent instance with type |
| 9909 * set to `"iron-select"`. |
| 9910 */ |
| 9911 _onIronSelect: function(event) { |
| 9912 if (!this.ignoreSelect) { |
| 9913 this.close(); |
| 9914 } |
| 9915 }, |
| 9916 |
| 9917 /** |
| 9918 * When the dropdown opens, the `paper-menu-button` fires `paper-open`. |
| 9919 * When the dropdown closes, the `paper-menu-button` fires `paper-close`
. |
| 9920 * |
| 9921 * @param {boolean} opened True if the dropdown is opened, otherwise fal
se. |
| 9922 * @param {boolean} oldOpened The previous value of `opened`. |
| 9923 */ |
| 9924 _openedChanged: function(opened, oldOpened) { |
| 9925 if (opened) { |
| 9926 // TODO(cdata): Update this when we can measure changes in distribut
ed |
| 9927 // children in an idiomatic way. |
| 9928 // We poke this property in case the element has changed. This will |
| 9929 // cause the focus target for the `iron-dropdown` to be updated as |
| 9930 // necessary: |
| 9931 this._dropdownContent = this.contentElement; |
| 9932 this.fire('paper-dropdown-open'); |
| 9933 } else if (oldOpened != null) { |
| 9934 this.fire('paper-dropdown-close'); |
| 9935 } |
| 9936 }, |
| 9937 |
| 9938 /** |
| 9939 * If the dropdown is open when disabled becomes true, close the |
| 9603 * dropdown. | 9940 * dropdown. |
| 9604 */ | 9941 * |
| 9605 noAnimations: { | 9942 * @param {boolean} disabled True if disabled, otherwise false. |
| 9606 type: Boolean, | 9943 */ |
| 9607 value: false | 9944 _disabledChanged: function(disabled) { |
| 9608 }, | 9945 Polymer.IronControlState._disabledChanged.apply(this, arguments); |
| 9609 | 9946 if (disabled && this.opened) { |
| 9610 /** | 9947 this.close(); |
| 9611 * Set to true to disable automatically closing the dropdown after | 9948 } |
| 9612 * a selection has been made. | 9949 }, |
| 9613 */ | 9950 |
| 9614 ignoreSelect: { | 9951 __onIronOverlayCanceled: function(event) { |
| 9615 type: Boolean, | 9952 var uiEvent = event.detail; |
| 9616 value: false | 9953 var target = Polymer.dom(uiEvent).rootTarget; |
| 9617 }, | 9954 var trigger = this.$.trigger; |
| 9618 | 9955 var path = Polymer.dom(uiEvent).path; |
| 9619 /** | 9956 |
| 9620 * An animation config. If provided, this will be used to animate the | 9957 if (path.indexOf(trigger) > -1) { |
| 9621 * opening of the dropdown. | 9958 event.preventDefault(); |
| 9622 */ | 9959 } |
| 9623 openAnimationConfig: { | |
| 9624 type: Object, | |
| 9625 value: function() { | |
| 9626 return [{ | |
| 9627 name: 'fade-in-animation', | |
| 9628 timing: { | |
| 9629 delay: 100, | |
| 9630 duration: 200 | |
| 9631 } | |
| 9632 }, { | |
| 9633 name: 'paper-menu-grow-width-animation', | |
| 9634 timing: { | |
| 9635 delay: 100, | |
| 9636 duration: 150, | |
| 9637 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
| 9638 } | |
| 9639 }, { | |
| 9640 name: 'paper-menu-grow-height-animation', | |
| 9641 timing: { | |
| 9642 delay: 100, | |
| 9643 duration: 275, | |
| 9644 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
| 9645 } | |
| 9646 }]; | |
| 9647 } | |
| 9648 }, | |
| 9649 | |
| 9650 /** | |
| 9651 * An animation config. If provided, this will be used to animate the | |
| 9652 * closing of the dropdown. | |
| 9653 */ | |
| 9654 closeAnimationConfig: { | |
| 9655 type: Object, | |
| 9656 value: function() { | |
| 9657 return [{ | |
| 9658 name: 'fade-out-animation', | |
| 9659 timing: { | |
| 9660 duration: 150 | |
| 9661 } | |
| 9662 }, { | |
| 9663 name: 'paper-menu-shrink-width-animation', | |
| 9664 timing: { | |
| 9665 delay: 100, | |
| 9666 duration: 50, | |
| 9667 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER | |
| 9668 } | |
| 9669 }, { | |
| 9670 name: 'paper-menu-shrink-height-animation', | |
| 9671 timing: { | |
| 9672 duration: 200, | |
| 9673 easing: 'ease-in' | |
| 9674 } | |
| 9675 }]; | |
| 9676 } | |
| 9677 }, | |
| 9678 | |
| 9679 /** | |
| 9680 * This is the element intended to be bound as the focus target | |
| 9681 * for the `iron-dropdown` contained by `paper-menu-button`. | |
| 9682 */ | |
| 9683 _dropdownContent: { | |
| 9684 type: Object | |
| 9685 } | 9960 } |
| 9686 }, | 9961 }); |
| 9687 | 9962 |
| 9688 hostAttributes: { | 9963 PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; |
| 9689 role: 'group', | 9964 PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; |
| 9690 'aria-haspopup': 'true' | 9965 |
| 9691 }, | 9966 Polymer.PaperMenuButton = PaperMenuButton; |
| 9692 | 9967 })(); |
| 9693 listeners: { | |
| 9694 'iron-select': '_onIronSelect' | |
| 9695 }, | |
| 9696 | |
| 9697 /** | |
| 9698 * The content element that is contained by the menu button, if any. | |
| 9699 */ | |
| 9700 get contentElement() { | |
| 9701 return Polymer.dom(this.$.content).getDistributedNodes()[0]; | |
| 9702 }, | |
| 9703 | |
| 9704 /** | |
| 9705 * Make the dropdown content appear as an overlay positioned relative | |
| 9706 * to the dropdown trigger. | |
| 9707 */ | |
| 9708 open: function() { | |
| 9709 if (this.disabled) { | |
| 9710 return; | |
| 9711 } | |
| 9712 | |
| 9713 this.$.dropdown.open(); | |
| 9714 }, | |
| 9715 | |
| 9716 /** | |
| 9717 * Hide the dropdown content. | |
| 9718 */ | |
| 9719 close: function() { | |
| 9720 this.$.dropdown.close(); | |
| 9721 }, | |
| 9722 | |
| 9723 /** | |
| 9724 * When an `iron-select` event is received, the dropdown should | |
| 9725 * automatically close on the assumption that a value has been chosen. | |
| 9726 * | |
| 9727 * @param {CustomEvent} event A CustomEvent instance with type | |
| 9728 * set to `"iron-select"`. | |
| 9729 */ | |
| 9730 _onIronSelect: function(event) { | |
| 9731 if (!this.ignoreSelect) { | |
| 9732 this.close(); | |
| 9733 } | |
| 9734 }, | |
| 9735 | |
| 9736 /** | |
| 9737 * When the dropdown opens, the `paper-menu-button` fires `paper-open`. | |
| 9738 * When the dropdown closes, the `paper-menu-button` fires `paper-close`. | |
| 9739 * | |
| 9740 * @param {boolean} opened True if the dropdown is opened, otherwise false
. | |
| 9741 * @param {boolean} oldOpened The previous value of `opened`. | |
| 9742 */ | |
| 9743 _openedChanged: function(opened, oldOpened) { | |
| 9744 if (opened) { | |
| 9745 // TODO(cdata): Update this when we can measure changes in distributed | |
| 9746 // children in an idiomatic way. | |
| 9747 // We poke this property in case the element has changed. This will | |
| 9748 // cause the focus target for the `iron-dropdown` to be updated as | |
| 9749 // necessary: | |
| 9750 this._dropdownContent = this.contentElement; | |
| 9751 this.fire('paper-dropdown-open'); | |
| 9752 } else if (oldOpened != null) { | |
| 9753 this.fire('paper-dropdown-close'); | |
| 9754 } | |
| 9755 }, | |
| 9756 | |
| 9757 /** | |
| 9758 * If the dropdown is open when disabled becomes true, close the | |
| 9759 * dropdown. | |
| 9760 * | |
| 9761 * @param {boolean} disabled True if disabled, otherwise false. | |
| 9762 */ | |
| 9763 _disabledChanged: function(disabled) { | |
| 9764 Polymer.IronControlState._disabledChanged.apply(this, arguments); | |
| 9765 if (disabled && this.opened) { | |
| 9766 this.close(); | |
| 9767 } | |
| 9768 } | |
| 9769 }); | |
| 9770 | |
| 9771 PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; | |
| 9772 PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; | |
| 9773 | |
| 9774 Polymer.PaperMenuButton = PaperMenuButton; | |
| 9775 })(); | |
| 9776 Polymer({ | 9968 Polymer({ |
| 9777 is: 'paper-icon-button', | 9969 is: 'paper-icon-button', |
| 9778 | 9970 |
| 9779 hostAttributes: { | 9971 hostAttributes: { |
| 9780 role: 'button', | 9972 role: 'button', |
| 9781 tabindex: '0' | 9973 tabindex: '0' |
| 9782 }, | 9974 }, |
| 9783 | 9975 |
| 9784 behaviors: [ | 9976 behaviors: [ |
| 9785 Polymer.PaperInkyFocusBehavior | 9977 Polymer.PaperInkyFocusBehavior |
| (...skipping 28 matching lines...) Expand all Loading... |
| 9814 | 10006 |
| 9815 _altChanged: function(newValue, oldValue) { | 10007 _altChanged: function(newValue, oldValue) { |
| 9816 var label = this.getAttribute('aria-label'); | 10008 var label = this.getAttribute('aria-label'); |
| 9817 | 10009 |
| 9818 // Don't stomp over a user-set aria-label. | 10010 // Don't stomp over a user-set aria-label. |
| 9819 if (!label || oldValue == label) { | 10011 if (!label || oldValue == label) { |
| 9820 this.setAttribute('aria-label', newValue); | 10012 this.setAttribute('aria-label', newValue); |
| 9821 } | 10013 } |
| 9822 } | 10014 } |
| 9823 }); | 10015 }); |
| 10016 (function() { |
| 10017 'use strict'; |
| 10018 |
| 10019 Polymer.IronA11yAnnouncer = Polymer({ |
| 10020 is: 'iron-a11y-announcer', |
| 10021 |
| 10022 properties: { |
| 10023 |
| 10024 /** |
| 10025 * The value of mode is used to set the `aria-live` attribute |
| 10026 * for the element that will be announced. Valid values are: `off`, |
| 10027 * `polite` and `assertive`. |
| 10028 */ |
| 10029 mode: { |
| 10030 type: String, |
| 10031 value: 'polite' |
| 10032 }, |
| 10033 |
| 10034 _text: { |
| 10035 type: String, |
| 10036 value: '' |
| 10037 } |
| 10038 }, |
| 10039 |
| 10040 created: function() { |
| 10041 if (!Polymer.IronA11yAnnouncer.instance) { |
| 10042 Polymer.IronA11yAnnouncer.instance = this; |
| 10043 } |
| 10044 |
| 10045 document.body.addEventListener('iron-announce', this._onIronAnnounce.b
ind(this)); |
| 10046 }, |
| 10047 |
| 10048 /** |
| 10049 * Cause a text string to be announced by screen readers. |
| 10050 * |
| 10051 * @param {string} text The text that should be announced. |
| 10052 */ |
| 10053 announce: function(text) { |
| 10054 this._text = ''; |
| 10055 this.async(function() { |
| 10056 this._text = text; |
| 10057 }, 100); |
| 10058 }, |
| 10059 |
| 10060 _onIronAnnounce: function(event) { |
| 10061 if (event.detail && event.detail.text) { |
| 10062 this.announce(event.detail.text); |
| 10063 } |
| 10064 } |
| 10065 }); |
| 10066 |
| 10067 Polymer.IronA11yAnnouncer.instance = null; |
| 10068 |
| 10069 Polymer.IronA11yAnnouncer.requestAvailability = function() { |
| 10070 if (!Polymer.IronA11yAnnouncer.instance) { |
| 10071 Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y
-announcer'); |
| 10072 } |
| 10073 |
| 10074 document.body.appendChild(Polymer.IronA11yAnnouncer.instance); |
| 10075 }; |
| 10076 })(); |
| 9824 /** | 10077 /** |
| 9825 * `Use Polymer.IronValidatableBehavior` to implement an element that validate
s user input. | 10078 * `Use Polymer.IronValidatableBehavior` to implement an element that validate
s user input. |
| 9826 * Use the related `Polymer.IronValidatorBehavior` to add custom validation lo
gic to an iron-input. | 10079 * Use the related `Polymer.IronValidatorBehavior` to add custom validation lo
gic to an iron-input. |
| 9827 * | 10080 * |
| 9828 * By default, an `<iron-form>` element validates its fields when the user pre
sses the submit button. | 10081 * By default, an `<iron-form>` element validates its fields when the user pre
sses the submit button. |
| 9829 * To validate a form imperatively, call the form's `validate()` method, which
in turn will | 10082 * To validate a form imperatively, call the form's `validate()` method, which
in turn will |
| 9830 * call `validate()` on all its children. By using `Polymer.IronValidatableBeh
avior`, your | 10083 * call `validate()` on all its children. By using `Polymer.IronValidatableBeh
avior`, your |
| 9831 * custom element will get a public `validate()`, which | 10084 * custom element will get a public `validate()`, which |
| 9832 * will return the validity of the element, and a corresponding `invalid` attr
ibute, | 10085 * will return the validity of the element, and a corresponding `invalid` attr
ibute, |
| 9833 * which can be used for styling. | 10086 * which can be used for styling. |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9986 | 10239 |
| 9987 /** | 10240 /** |
| 9988 * Use this property instead of `value` for two-way data binding. | 10241 * Use this property instead of `value` for two-way data binding. |
| 9989 */ | 10242 */ |
| 9990 bindValue: { | 10243 bindValue: { |
| 9991 observer: '_bindValueChanged', | 10244 observer: '_bindValueChanged', |
| 9992 type: String | 10245 type: String |
| 9993 }, | 10246 }, |
| 9994 | 10247 |
| 9995 /** | 10248 /** |
| 9996 * Set to true to prevent the user from entering invalid input. The new in
put characters are | 10249 * Set to true to prevent the user from entering invalid input. If `allowe
dPattern` is set, |
| 9997 * matched with `allowedPattern` if it is set, otherwise it will use the `
type` attribute (only | 10250 * any character typed by the user will be matched against that pattern, a
nd rejected if it's not a match. |
| 9998 * supported for `type=number`). | 10251 * Pasted input will have each character checked individually; if any char
acter |
| 10252 * doesn't match `allowedPattern`, the entire pasted string will be reject
ed. |
| 10253 * If `allowedPattern` is not set, it will use the `type` attribute (only
supported for `type=number`). |
| 9999 */ | 10254 */ |
| 10000 preventInvalidInput: { | 10255 preventInvalidInput: { |
| 10001 type: Boolean | 10256 type: Boolean |
| 10002 }, | 10257 }, |
| 10003 | 10258 |
| 10004 /** | 10259 /** |
| 10005 * Regular expression expressing a set of characters to enforce the validi
ty of input characters. | 10260 * Regular expression that list the characters allowed as input. |
| 10006 * The recommended value should follow this format: `[a-ZA-Z0-9.+-!;:]` th
at list the characters | 10261 * This pattern represents the allowed characters for the field; as the us
er inputs text, |
| 10007 * allowed as input. | 10262 * each individual character will be checked against the pattern (rather t
han checking |
| 10263 * the entire value as a whole). The recommended format should be a list o
f allowed characters; |
| 10264 * for example, `[a-zA-Z0-9.+-!;:]` |
| 10008 */ | 10265 */ |
| 10009 allowedPattern: { | 10266 allowedPattern: { |
| 10010 type: String, | 10267 type: String, |
| 10011 observer: "_allowedPatternChanged" | 10268 observer: "_allowedPatternChanged" |
| 10012 }, | 10269 }, |
| 10013 | 10270 |
| 10014 _previousValidInput: { | 10271 _previousValidInput: { |
| 10015 type: String, | 10272 type: String, |
| 10016 value: '' | 10273 value: '' |
| 10017 }, | 10274 }, |
| 10018 | 10275 |
| 10019 _patternAlreadyChecked: { | 10276 _patternAlreadyChecked: { |
| 10020 type: Boolean, | 10277 type: Boolean, |
| 10021 value: false | 10278 value: false |
| 10022 } | 10279 } |
| 10023 | 10280 |
| 10024 }, | 10281 }, |
| 10025 | 10282 |
| 10026 listeners: { | 10283 listeners: { |
| 10027 'input': '_onInput', | 10284 'input': '_onInput', |
| 10028 'keypress': '_onKeypress' | 10285 'keypress': '_onKeypress' |
| 10029 }, | 10286 }, |
| 10030 | 10287 |
| 10288 registered: function() { |
| 10289 // Feature detect whether we need to patch dispatchEvent (i.e. on FF and I
E). |
| 10290 if (!this._canDispatchEventOnDisabled()) { |
| 10291 this._origDispatchEvent = this.dispatchEvent; |
| 10292 this.dispatchEvent = this._dispatchEventFirefoxIE; |
| 10293 } |
| 10294 }, |
| 10295 |
| 10296 created: function() { |
| 10297 Polymer.IronA11yAnnouncer.requestAvailability(); |
| 10298 }, |
| 10299 |
| 10300 _canDispatchEventOnDisabled: function() { |
| 10301 var input = document.createElement('input'); |
| 10302 var canDispatch = false; |
| 10303 input.disabled = true; |
| 10304 |
| 10305 input.addEventListener('feature-check-dispatch-event', function() { |
| 10306 canDispatch = true; |
| 10307 }); |
| 10308 |
| 10309 try { |
| 10310 input.dispatchEvent(new Event('feature-check-dispatch-event')); |
| 10311 } catch(e) {} |
| 10312 |
| 10313 return canDispatch; |
| 10314 }, |
| 10315 |
| 10316 _dispatchEventFirefoxIE: function() { |
| 10317 // Due to Firefox bug, events fired on disabled form controls can throw |
| 10318 // errors; furthermore, neither IE nor Firefox will actually dispatch |
| 10319 // events from disabled form controls; as such, we toggle disable around |
| 10320 // the dispatch to allow notifying properties to notify |
| 10321 // See issue #47 for details |
| 10322 var disabled = this.disabled; |
| 10323 this.disabled = false; |
| 10324 this._origDispatchEvent.apply(this, arguments); |
| 10325 this.disabled = disabled; |
| 10326 }, |
| 10327 |
| 10031 get _patternRegExp() { | 10328 get _patternRegExp() { |
| 10032 var pattern; | 10329 var pattern; |
| 10033 if (this.allowedPattern) { | 10330 if (this.allowedPattern) { |
| 10034 pattern = new RegExp(this.allowedPattern); | 10331 pattern = new RegExp(this.allowedPattern); |
| 10035 } else { | 10332 } else { |
| 10036 switch (this.type) { | 10333 switch (this.type) { |
| 10037 case 'number': | 10334 case 'number': |
| 10038 pattern = /[0-9.,e-]/; | 10335 pattern = /[0-9.,e-]/; |
| 10039 break; | 10336 break; |
| 10040 } | 10337 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 10061 // Force to prevent invalid input when an `allowed-pattern` is set | 10358 // Force to prevent invalid input when an `allowed-pattern` is set |
| 10062 this.preventInvalidInput = this.allowedPattern ? true : false; | 10359 this.preventInvalidInput = this.allowedPattern ? true : false; |
| 10063 }, | 10360 }, |
| 10064 | 10361 |
| 10065 _onInput: function() { | 10362 _onInput: function() { |
| 10066 // Need to validate each of the characters pasted if they haven't | 10363 // Need to validate each of the characters pasted if they haven't |
| 10067 // been validated inside `_onKeypress` already. | 10364 // been validated inside `_onKeypress` already. |
| 10068 if (this.preventInvalidInput && !this._patternAlreadyChecked) { | 10365 if (this.preventInvalidInput && !this._patternAlreadyChecked) { |
| 10069 var valid = this._checkPatternValidity(); | 10366 var valid = this._checkPatternValidity(); |
| 10070 if (!valid) { | 10367 if (!valid) { |
| 10368 this._announceInvalidCharacter('Invalid string of characters not enter
ed.'); |
| 10071 this.value = this._previousValidInput; | 10369 this.value = this._previousValidInput; |
| 10072 } | 10370 } |
| 10073 } | 10371 } |
| 10074 | 10372 |
| 10075 this.bindValue = this.value; | 10373 this.bindValue = this.value; |
| 10076 this._previousValidInput = this.value; | 10374 this._previousValidInput = this.value; |
| 10077 this._patternAlreadyChecked = false; | 10375 this._patternAlreadyChecked = false; |
| 10078 }, | 10376 }, |
| 10079 | 10377 |
| 10080 _isPrintable: function(event) { | 10378 _isPrintable: function(event) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10121 // Handle special keys and backspace | 10419 // Handle special keys and backspace |
| 10122 if (event.metaKey || event.ctrlKey || event.altKey) | 10420 if (event.metaKey || event.ctrlKey || event.altKey) |
| 10123 return; | 10421 return; |
| 10124 | 10422 |
| 10125 // Check the pattern either here or in `_onInput`, but not in both. | 10423 // Check the pattern either here or in `_onInput`, but not in both. |
| 10126 this._patternAlreadyChecked = true; | 10424 this._patternAlreadyChecked = true; |
| 10127 | 10425 |
| 10128 var thisChar = String.fromCharCode(event.charCode); | 10426 var thisChar = String.fromCharCode(event.charCode); |
| 10129 if (this._isPrintable(event) && !regexp.test(thisChar)) { | 10427 if (this._isPrintable(event) && !regexp.test(thisChar)) { |
| 10130 event.preventDefault(); | 10428 event.preventDefault(); |
| 10429 this._announceInvalidCharacter('Invalid character ' + thisChar + ' not e
ntered.'); |
| 10131 } | 10430 } |
| 10132 }, | 10431 }, |
| 10133 | 10432 |
| 10134 _checkPatternValidity: function() { | 10433 _checkPatternValidity: function() { |
| 10135 var regexp = this._patternRegExp; | 10434 var regexp = this._patternRegExp; |
| 10136 if (!regexp) { | 10435 if (!regexp) { |
| 10137 return true; | 10436 return true; |
| 10138 } | 10437 } |
| 10139 for (var i = 0; i < this.value.length; i++) { | 10438 for (var i = 0; i < this.value.length; i++) { |
| 10140 if (!regexp.test(this.value[i])) { | 10439 if (!regexp.test(this.value[i])) { |
| 10141 return false; | 10440 return false; |
| 10142 } | 10441 } |
| 10143 } | 10442 } |
| 10144 return true; | 10443 return true; |
| 10145 }, | 10444 }, |
| 10146 | 10445 |
| 10147 /** | 10446 /** |
| 10148 * Returns true if `value` is valid. The validator provided in `validator` w
ill be used first, | 10447 * Returns true if `value` is valid. The validator provided in `validator` w
ill be used first, |
| 10149 * then any constraints. | 10448 * then any constraints. |
| 10150 * @return {boolean} True if the value is valid. | 10449 * @return {boolean} True if the value is valid. |
| 10151 */ | 10450 */ |
| 10152 validate: function() { | 10451 validate: function() { |
| 10153 // Empty, non-required input is valid. | 10452 // First, check what the browser thinks. Some inputs (like type=number) |
| 10154 if (!this.required && this.value == '') { | 10453 // behave weirdly and will set the value to "" if something invalid is |
| 10155 this.invalid = false; | 10454 // entered, but will set the validity correctly. |
| 10156 return true; | 10455 var valid = this.checkValidity(); |
| 10456 |
| 10457 // Only do extra checking if the browser thought this was valid. |
| 10458 if (valid) { |
| 10459 // Empty, required input is invalid |
| 10460 if (this.required && this.value === '') { |
| 10461 valid = false; |
| 10462 } else if (this.hasValidator()) { |
| 10463 valid = Polymer.IronValidatableBehavior.validate.call(this, this.value
); |
| 10464 } |
| 10157 } | 10465 } |
| 10158 | 10466 |
| 10159 var valid; | 10467 this.invalid = !valid; |
| 10160 if (this.hasValidator()) { | |
| 10161 valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); | |
| 10162 } else { | |
| 10163 valid = this.checkValidity(); | |
| 10164 this.invalid = !valid; | |
| 10165 } | |
| 10166 this.fire('iron-input-validate'); | 10468 this.fire('iron-input-validate'); |
| 10167 return valid; | 10469 return valid; |
| 10470 }, |
| 10471 |
| 10472 _announceInvalidCharacter: function(message) { |
| 10473 this.fire('iron-announce', { text: message }); |
| 10168 } | 10474 } |
| 10169 | |
| 10170 }); | 10475 }); |
| 10171 | 10476 |
| 10172 /* | 10477 /* |
| 10173 The `iron-input-validate` event is fired whenever `validate()` is called. | 10478 The `iron-input-validate` event is fired whenever `validate()` is called. |
| 10174 @event iron-input-validate | 10479 @event iron-input-validate |
| 10175 */ | 10480 */ |
| 10176 Polymer({ | 10481 Polymer({ |
| 10177 is: 'paper-input-container', | 10482 is: 'paper-input-container', |
| 10178 | 10483 |
| 10179 properties: { | 10484 properties: { |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10296 get _inputElementValue() { | 10601 get _inputElementValue() { |
| 10297 return this._inputElement[this._propertyForValue] || this._inputElement.va
lue; | 10602 return this._inputElement[this._propertyForValue] || this._inputElement.va
lue; |
| 10298 }, | 10603 }, |
| 10299 | 10604 |
| 10300 ready: function() { | 10605 ready: function() { |
| 10301 if (!this._addons) { | 10606 if (!this._addons) { |
| 10302 this._addons = []; | 10607 this._addons = []; |
| 10303 } | 10608 } |
| 10304 this.addEventListener('focus', this._boundOnFocus, true); | 10609 this.addEventListener('focus', this._boundOnFocus, true); |
| 10305 this.addEventListener('blur', this._boundOnBlur, true); | 10610 this.addEventListener('blur', this._boundOnBlur, true); |
| 10611 }, |
| 10612 |
| 10613 attached: function() { |
| 10306 if (this.attrForValue) { | 10614 if (this.attrForValue) { |
| 10307 this._inputElement.addEventListener(this._valueChangedEvent, this._bound
ValueChanged); | 10615 this._inputElement.addEventListener(this._valueChangedEvent, this._bound
ValueChanged); |
| 10308 } else { | 10616 } else { |
| 10309 this.addEventListener('input', this._onInput); | 10617 this.addEventListener('input', this._onInput); |
| 10310 } | 10618 } |
| 10311 }, | |
| 10312 | 10619 |
| 10313 attached: function() { | |
| 10314 // Only validate when attached if the input already has a value. | 10620 // Only validate when attached if the input already has a value. |
| 10315 if (this._inputElementValue != '') { | 10621 if (this._inputElementValue != '') { |
| 10316 this._handleValueAndAutoValidate(this._inputElement); | 10622 this._handleValueAndAutoValidate(this._inputElement); |
| 10317 } else { | 10623 } else { |
| 10318 this._handleValue(this._inputElement); | 10624 this._handleValue(this._inputElement); |
| 10319 } | 10625 } |
| 10320 }, | 10626 }, |
| 10321 | 10627 |
| 10322 _onAddonAttached: function(event) { | 10628 _onAddonAttached: function(event) { |
| 10323 if (!this._addons) { | 10629 if (!this._addons) { |
| (...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10862 // found in the LICENSE file. | 11168 // found in the LICENSE file. |
| 10863 | 11169 |
| 10864 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> | 11170 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> |
| 10865 | 11171 |
| 10866 i18nTemplate.process(document, loadTimeData); | 11172 i18nTemplate.process(document, loadTimeData); |
| 10867 // Copyright 2015 The Chromium Authors. All rights reserved. | 11173 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 10868 // Use of this source code is governed by a BSD-style license that can be | 11174 // Use of this source code is governed by a BSD-style license that can be |
| 10869 // found in the LICENSE file. | 11175 // found in the LICENSE file. |
| 10870 | 11176 |
| 10871 window.addEventListener('load', downloads.Manager.onLoad); | 11177 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |