| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @fileoverview Assertion support. |
| 7 */ |
| 8 |
| 9 /** |
| 10 * Verify |condition| is truthy and return |condition| if so. |
| 11 * @template T |
| 12 * @param {T} condition A condition to check for truthiness. Note that this |
| 13 * may be used to test whether a value is defined or not, and we don't want |
| 14 * to force a cast to Boolean. |
| 15 * @param {string=} opt_message A message to show on failure. |
| 16 * @return {T} A non-null |condition|. |
| 17 */ |
| 18 function assert(condition, opt_message) { |
| 19 if (!condition) { |
| 20 var message = 'Assertion failed'; |
| 21 if (opt_message) |
| 22 message = message + ': ' + opt_message; |
| 23 var error = new Error(message); |
| 24 var global = function() { return this; }(); |
| 25 if (global.traceAssertionsForTesting) |
| 26 console.warn(error.stack); |
| 27 throw error; |
| 28 } |
| 29 return condition; |
| 30 } |
| 31 |
| 32 /** |
| 33 * Call this from places in the code that should never be reached. |
| 34 * |
| 35 * For example, handling all the values of enum with a switch() like this: |
| 36 * |
| 37 * function getValueFromEnum(enum) { |
| 38 * switch (enum) { |
| 39 * case ENUM_FIRST_OF_TWO: |
| 40 * return first |
| 41 * case ENUM_LAST_OF_TWO: |
| 42 * return last; |
| 43 * } |
| 44 * assertNotReached(); |
| 45 * return document; |
| 46 * } |
| 47 * |
| 48 * This code should only be hit in the case of serious programmer error or |
| 49 * unexpected input. |
| 50 * |
| 51 * @param {string=} opt_message A message to show when this is hit. |
| 52 */ |
| 53 function assertNotReached(opt_message) { |
| 54 assert(false, opt_message || 'Unreachable code hit'); |
| 55 } |
| 56 |
| 57 /** |
| 58 * @param {*} value The value to check. |
| 59 * @param {function(new: T, ...)} type A user-defined constructor. |
| 60 * @param {string=} opt_message A message to show when this is hit. |
| 61 * @return {T} |
| 62 * @template T |
| 63 */ |
| 64 function assertInstanceof(value, type, opt_message) { |
| 65 // We don't use assert immediately here so that we avoid constructing an error |
| 66 // message if we don't have to. |
| 67 if (!(value instanceof type)) { |
| 68 assertNotReached(opt_message || 'Value ' + value + |
| 69 ' is not a[n] ' + (type.name || typeof type)); |
| 70 } |
| 71 return value; |
| 72 }; |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 73 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 74 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 75 // found in the LICENSE file. |
| 4 | 76 |
| 5 /** | 77 /** |
| 6 * @fileoverview PromiseResolver is a helper class that allows creating a | 78 * @fileoverview PromiseResolver is a helper class that allows creating a |
| 7 * Promise that will be fulfilled (resolved or rejected) some time later. | 79 * Promise that will be fulfilled (resolved or rejected) some time later. |
| 8 * | 80 * |
| 9 * Example: | 81 * Example: |
| 10 * var resolver = new PromiseResolver(); | 82 * var resolver = new PromiseResolver(); |
| 11 * resolver.promise.then(function(result) { | 83 * resolver.promise.then(function(result) { |
| 12 * console.log('resolved with', result); | 84 * console.log('resolved with', result); |
| 13 * }); | 85 * }); |
| 14 * ... | 86 * ... |
| 15 * ... | 87 * ... |
| 16 * resolver.resolve({hello: 'world'}); | 88 * resolver.resolve({hello: 'world'}); |
| 17 */ | 89 */ |
| 18 | 90 |
| 19 /** | 91 /** |
| 20 * @constructor @struct | 92 * @constructor @struct |
| 21 * @template T | 93 * @template T |
| 22 */ | 94 */ |
| 23 function PromiseResolver() { | 95 function PromiseResolver() { |
| 24 /** @type {function(T): void} */ | 96 /** @private {function(T): void} */ |
| 25 this.resolve; | 97 this.resolve_; |
| 26 | 98 |
| 27 /** @type {function(*=): void} */ | 99 /** @private {function(*=): void} */ |
| 28 this.reject; | 100 this.reject_; |
| 29 | 101 |
| 30 /** @type {!Promise<T>} */ | 102 /** @private {!Promise<T>} */ |
| 31 this.promise = new Promise(function(resolve, reject) { | 103 this.promise_ = new Promise(function(resolve, reject) { |
| 32 this.resolve = resolve; | 104 this.resolve_ = resolve; |
| 33 this.reject = reject; | 105 this.reject_ = reject; |
| 34 }.bind(this)); | 106 }.bind(this)); |
| 107 } |
| 108 |
| 109 PromiseResolver.prototype = { |
| 110 /** @return {!Promise<T>} */ |
| 111 get promise() { return this.promise_; }, |
| 112 set promise(p) { assertNotReached(); }, |
| 113 |
| 114 /** @return {function(T): void} */ |
| 115 get resolve() { return this.resolve_; }, |
| 116 set resolve(r) { assertNotReached(); }, |
| 117 |
| 118 /** @return {function(*=): void} */ |
| 119 get reject() { return this.reject_; }, |
| 120 set reject(s) { assertNotReached(); }, |
| 35 }; | 121 }; |
| 36 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 122 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 37 // Use of this source code is governed by a BSD-style license that can be | 123 // Use of this source code is governed by a BSD-style license that can be |
| 38 // found in the LICENSE file. | 124 // found in the LICENSE file. |
| 39 | 125 |
| 40 /** | 126 /** |
| 41 * The global object. | 127 * The global object. |
| 42 * @type {!Object} | 128 * @type {!Object} |
| 43 * @const | 129 * @const |
| 44 */ | 130 */ |
| (...skipping 2124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2169 * @demo demo/scrolling-region.html Scrolling Region | 2255 * @demo demo/scrolling-region.html Scrolling Region |
| 2170 * @demo demo/document.html Document Element | 2256 * @demo demo/document.html Document Element |
| 2171 * @polymerBehavior | 2257 * @polymerBehavior |
| 2172 */ | 2258 */ |
| 2173 Polymer.IronScrollTargetBehavior = { | 2259 Polymer.IronScrollTargetBehavior = { |
| 2174 | 2260 |
| 2175 properties: { | 2261 properties: { |
| 2176 | 2262 |
| 2177 /** | 2263 /** |
| 2178 * Specifies the element that will handle the scroll event | 2264 * 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`, | 2265 * on the behalf of the current element. This is typically a reference to
an element, |
| 2180 * but there are a few more posibilities: | 2266 * but there are a few more posibilities: |
| 2181 * | 2267 * |
| 2182 * ### Elements id | 2268 * ### Elements id |
| 2183 * | 2269 * |
| 2184 *```html | 2270 *```html |
| 2185 * <div id="scrollable-element" style="overflow-y: auto;"> | 2271 * <div id="scrollable-element" style="overflow: auto;"> |
| 2186 * <x-element scroll-target="scrollable-element"> | 2272 * <x-element scroll-target="scrollable-element"> |
| 2187 * Content | 2273 * \x3c!-- Content--\x3e |
| 2188 * </x-element> | 2274 * </x-element> |
| 2189 * </div> | 2275 * </div> |
| 2190 *``` | 2276 *``` |
| 2191 * In this case, `scrollTarget` will point to the outer div element. Alter
natively, | 2277 * In this case, the `scrollTarget` will point to the outer div element. |
| 2192 * you can set the property programatically: | 2278 * |
| 2279 * ### Document scrolling |
| 2280 * |
| 2281 * For document scrolling, you can use the reserved word `document`: |
| 2282 * |
| 2283 *```html |
| 2284 * <x-element scroll-target="document"> |
| 2285 * \x3c!-- Content --\x3e |
| 2286 * </x-element> |
| 2287 *``` |
| 2288 * |
| 2289 * ### Elements reference |
| 2193 * | 2290 * |
| 2194 *```js | 2291 *```js |
| 2195 * appHeader.scrollTarget = document.querySelector('#scrollable-element'); | 2292 * appHeader.scrollTarget = document.querySelector('#scrollable-element'); |
| 2196 *``` | 2293 *``` |
| 2197 * | 2294 * |
| 2198 * @type {HTMLElement} | 2295 * @type {HTMLElement} |
| 2199 */ | 2296 */ |
| 2200 scrollTarget: { | 2297 scrollTarget: { |
| 2201 type: HTMLElement, | 2298 type: HTMLElement, |
| 2202 value: function() { | 2299 value: function() { |
| 2203 return this._defaultScrollTarget; | 2300 return this._defaultScrollTarget; |
| 2204 } | 2301 } |
| 2205 } | 2302 } |
| 2206 }, | 2303 }, |
| 2207 | 2304 |
| 2208 observers: [ | 2305 observers: [ |
| 2209 '_scrollTargetChanged(scrollTarget, isAttached)' | 2306 '_scrollTargetChanged(scrollTarget, isAttached)' |
| 2210 ], | 2307 ], |
| 2211 | 2308 |
| 2212 _scrollTargetChanged: function(scrollTarget, isAttached) { | 2309 _scrollTargetChanged: function(scrollTarget, isAttached) { |
| 2213 // Remove lister to the current scroll target | 2310 var eventTarget; |
| 2311 |
| 2214 if (this._oldScrollTarget) { | 2312 if (this._oldScrollTarget) { |
| 2215 if (this._oldScrollTarget === this._doc) { | 2313 eventTarget = this._oldScrollTarget === this._doc ? window : this._oldSc
rollTarget; |
| 2216 window.removeEventListener('scroll', this._boundScrollHandler); | 2314 eventTarget.removeEventListener('scroll', this._boundScrollHandler); |
| 2217 } else if (this._oldScrollTarget.removeEventListener) { | |
| 2218 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); | |
| 2219 } | |
| 2220 this._oldScrollTarget = null; | 2315 this._oldScrollTarget = null; |
| 2221 } | 2316 } |
| 2222 if (isAttached) { | |
| 2223 // Support element id references | |
| 2224 if (typeof scrollTarget === 'string') { | |
| 2225 | 2317 |
| 2226 var host = this.domHost; | 2318 if (!isAttached) { |
| 2227 this.scrollTarget = host && host.$ ? host.$[scrollTarget] : | 2319 return; |
| 2228 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); | 2320 } |
| 2321 // Support element id references |
| 2322 if (scrollTarget === 'document') { |
| 2229 | 2323 |
| 2230 } else if (this._scrollHandler) { | 2324 this.scrollTarget = this._doc; |
| 2231 | 2325 |
| 2232 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); | 2326 } else if (typeof scrollTarget === 'string') { |
| 2233 // Add a new listener | 2327 |
| 2234 if (scrollTarget === this._doc) { | 2328 this.scrollTarget = this.domHost ? this.domHost.$[scrollTarget] : |
| 2235 window.addEventListener('scroll', this._boundScrollHandler); | 2329 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); |
| 2236 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { | 2330 |
| 2237 this._scrollHandler(); | 2331 } else if (this._isValidScrollTarget()) { |
| 2238 } | 2332 |
| 2239 } else if (scrollTarget && scrollTarget.addEventListener) { | 2333 eventTarget = scrollTarget === this._doc ? window : scrollTarget; |
| 2240 scrollTarget.addEventListener('scroll', this._boundScrollHandler); | 2334 this._boundScrollHandler = this._boundScrollHandler || this._scrollHandl
er.bind(this); |
| 2241 } | 2335 this._oldScrollTarget = scrollTarget; |
| 2242 this._oldScrollTarget = scrollTarget; | 2336 |
| 2243 } | 2337 eventTarget.addEventListener('scroll', this._boundScrollHandler); |
| 2244 } | 2338 } |
| 2245 }, | 2339 }, |
| 2246 | 2340 |
| 2247 /** | 2341 /** |
| 2248 * Runs on every scroll event. Consumer of this behavior may want to overrid
e this method. | 2342 * Runs on every scroll event. Consumer of this behavior may override this m
ethod. |
| 2249 * | 2343 * |
| 2250 * @protected | 2344 * @protected |
| 2251 */ | 2345 */ |
| 2252 _scrollHandler: function scrollHandler() {}, | 2346 _scrollHandler: function scrollHandler() {}, |
| 2253 | 2347 |
| 2254 /** | 2348 /** |
| 2255 * The default scroll target. Consumers of this behavior may want to customi
ze | 2349 * The default scroll target. Consumers of this behavior may want to customi
ze |
| 2256 * the default scroll target. | 2350 * the default scroll target. |
| 2257 * | 2351 * |
| 2258 * @type {Element} | 2352 * @type {Element} |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2465 '_setOverflow(scrollTarget)' | 2559 '_setOverflow(scrollTarget)' |
| 2466 ], | 2560 ], |
| 2467 | 2561 |
| 2468 behaviors: [ | 2562 behaviors: [ |
| 2469 Polymer.Templatizer, | 2563 Polymer.Templatizer, |
| 2470 Polymer.IronResizableBehavior, | 2564 Polymer.IronResizableBehavior, |
| 2471 Polymer.IronA11yKeysBehavior, | 2565 Polymer.IronA11yKeysBehavior, |
| 2472 Polymer.IronScrollTargetBehavior | 2566 Polymer.IronScrollTargetBehavior |
| 2473 ], | 2567 ], |
| 2474 | 2568 |
| 2475 listeners: { | |
| 2476 'iron-resize': '_resizeHandler' | |
| 2477 }, | |
| 2478 | |
| 2479 keyBindings: { | 2569 keyBindings: { |
| 2480 'up': '_didMoveUp', | 2570 'up': '_didMoveUp', |
| 2481 'down': '_didMoveDown', | 2571 'down': '_didMoveDown', |
| 2482 'enter': '_didEnter' | 2572 'enter': '_didEnter' |
| 2483 }, | 2573 }, |
| 2484 | 2574 |
| 2485 /** | 2575 /** |
| 2486 * The ratio of hidden tiles that should remain in the scroll direction. | 2576 * 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. | 2577 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. |
| 2488 */ | 2578 */ |
| (...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2772 return this; | 2862 return this; |
| 2773 }, | 2863 }, |
| 2774 | 2864 |
| 2775 ready: function() { | 2865 ready: function() { |
| 2776 this.addEventListener('focus', this._didFocus.bind(this), true); | 2866 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 2777 }, | 2867 }, |
| 2778 | 2868 |
| 2779 attached: function() { | 2869 attached: function() { |
| 2780 this.updateViewportBoundaries(); | 2870 this.updateViewportBoundaries(); |
| 2781 this._render(); | 2871 this._render(); |
| 2872 // `iron-resize` is fired when the list is attached if the event is added |
| 2873 // before attached causing unnecessary work. |
| 2874 this.listen(this, 'iron-resize', '_resizeHandler'); |
| 2782 }, | 2875 }, |
| 2783 | 2876 |
| 2784 detached: function() { | 2877 detached: function() { |
| 2785 this._itemsRendered = false; | 2878 this._itemsRendered = false; |
| 2879 this.unlisten(this, 'iron-resize', '_resizeHandler'); |
| 2786 }, | 2880 }, |
| 2787 | 2881 |
| 2788 /** | 2882 /** |
| 2789 * Set the overflow property if this element has its own scrolling region | 2883 * Set the overflow property if this element has its own scrolling region |
| 2790 */ | 2884 */ |
| 2791 _setOverflow: function(scrollTarget) { | 2885 _setOverflow: function(scrollTarget) { |
| 2792 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; | 2886 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; |
| 2793 this.style.overflow = scrollTarget === this ? 'auto' : ''; | 2887 this.style.overflow = scrollTarget === this ? 'auto' : ''; |
| 2794 }, | 2888 }, |
| 2795 | 2889 |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2896 currentRatio += tileHeight / hiddenContentSize; | 2990 currentRatio += tileHeight / hiddenContentSize; |
| 2897 | 2991 |
| 2898 this._physicalTop += tileHeight; | 2992 this._physicalTop += tileHeight; |
| 2899 recycledTileSet.push(kth); | 2993 recycledTileSet.push(kth); |
| 2900 recycledTiles++; | 2994 recycledTiles++; |
| 2901 kth = (kth + 1) % this._physicalCount; | 2995 kth = (kth + 1) % this._physicalCount; |
| 2902 } | 2996 } |
| 2903 } | 2997 } |
| 2904 | 2998 |
| 2905 if (recycledTiles === 0) { | 2999 if (recycledTiles === 0) { |
| 2906 // If the list ever reach this case, the physical average is not signifi
cant enough | 3000 // 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) { | 3001 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
| 2910 this.async(this._increasePool.bind(this, 1)); | 3002 this._increasePoolIfNeeded(); |
| 2911 } | 3003 } |
| 2912 } else { | 3004 } else { |
| 2913 this._virtualStart = this._virtualStart + recycledTiles; | 3005 this._virtualStart = this._virtualStart + recycledTiles; |
| 2914 this._physicalStart = this._physicalStart + recycledTiles; | 3006 this._physicalStart = this._physicalStart + recycledTiles; |
| 2915 this._update(recycledTileSet, movingUp); | 3007 this._update(recycledTileSet, movingUp); |
| 2916 } | 3008 } |
| 2917 }, | 3009 }, |
| 2918 | 3010 |
| 2919 /** | 3011 /** |
| 2920 * Update the list of items, starting from the `_virtualStart` item. | 3012 * 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] | 3047 // First element child is item; Safari doesn't support children[0] |
| 2956 // on a doc fragment | 3048 // on a doc fragment |
| 2957 physicalItems[i] = inst.root.querySelector('*'); | 3049 physicalItems[i] = inst.root.querySelector('*'); |
| 2958 Polymer.dom(this).appendChild(inst.root); | 3050 Polymer.dom(this).appendChild(inst.root); |
| 2959 } | 3051 } |
| 2960 return physicalItems; | 3052 return physicalItems; |
| 2961 }, | 3053 }, |
| 2962 | 3054 |
| 2963 /** | 3055 /** |
| 2964 * Increases the pool of physical items only if needed. | 3056 * Increases the pool of physical items only if needed. |
| 2965 * This function will allocate additional physical items | 3057 * |
| 2966 * if the physical size is shorter than `_optPhysicalSize` | 3058 * @return {boolean} True if the pool was increased. |
| 2967 */ | 3059 */ |
| 2968 _increasePoolIfNeeded: function() { | 3060 _increasePoolIfNeeded: function() { |
| 2969 if (this._viewportSize === 0 || this._physicalSize >= this._optPhysicalSiz
e) { | 3061 // Base case 1: the list has no size. |
| 3062 if (this._viewportSize === 0) { |
| 2970 return false; | 3063 return false; |
| 2971 } | 3064 } |
| 2972 // 0 <= `currentPage` <= `_maxPages` | 3065 // Base case 2: If the physical size is optimal and the list's client heig
ht is full |
| 3066 // with physical items, don't increase the pool. |
| 3067 var isClientHeightFull = this._physicalBottom >= this._scrollBottom && thi
s._physicalTop <= this._scrollPosition; |
| 3068 if (this._physicalSize >= this._optPhysicalSize && isClientHeightFull) { |
| 3069 return false; |
| 3070 } |
| 3071 // this value should range between [0 <= `currentPage` <= `_maxPages`] |
| 2973 var currentPage = Math.floor(this._physicalSize / this._viewportSize); | 3072 var currentPage = Math.floor(this._physicalSize / this._viewportSize); |
| 3073 |
| 2974 if (currentPage === 0) { | 3074 if (currentPage === 0) { |
| 2975 // fill the first page | 3075 // fill the first page |
| 2976 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); | 3076 this._debounceTemplate(this._increasePool.bind(this, Math.round(this._ph
ysicalCount * 0.5))); |
| 2977 } else if (this._lastPage !== currentPage) { | 3077 } else if (this._lastPage !== currentPage && isClientHeightFull) { |
| 2978 // paint the page and defer the next increase | 3078 // paint the page and defer the next increase |
| 2979 // wait 16ms which is rough enough to get paint cycle. | 3079 // wait 16ms which is rough enough to get paint cycle. |
| 2980 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, 1), 16)); | 3080 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increa
sePool.bind(this, 1), 16)); |
| 2981 } else { | 3081 } else { |
| 2982 // fill the rest of the pages | 3082 // fill the rest of the pages |
| 2983 this._debounceTemplate(this._increasePool.bind(this, 1)); | 3083 this._debounceTemplate(this._increasePool.bind(this, 1)); |
| 2984 } | 3084 } |
| 3085 |
| 2985 this._lastPage = currentPage; | 3086 this._lastPage = currentPage; |
| 3087 |
| 2986 return true; | 3088 return true; |
| 2987 }, | 3089 }, |
| 2988 | 3090 |
| 2989 /** | 3091 /** |
| 2990 * Increases the pool size. | 3092 * Increases the pool size. |
| 2991 */ | 3093 */ |
| 2992 _increasePool: function(missingItems) { | 3094 _increasePool: function(missingItems) { |
| 2993 var nextPhysicalCount = Math.min( | 3095 var nextPhysicalCount = Math.min( |
| 2994 this._physicalCount + missingItems, | 3096 this._physicalCount + missingItems, |
| 2995 this._virtualCount - this._virtualStart, | 3097 this._virtualCount - this._virtualStart, |
| (...skipping 25 matching lines...) Expand all Loading... |
| 3021 /** | 3123 /** |
| 3022 * Render a new list of items. This method does exactly the same as `update`
, | 3124 * 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. | 3125 * but it also ensures that only one `update` cycle is created. |
| 3024 */ | 3126 */ |
| 3025 _render: function() { | 3127 _render: function() { |
| 3026 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; | 3128 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
| 3027 | 3129 |
| 3028 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { | 3130 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { |
| 3029 this._lastPage = 0; | 3131 this._lastPage = 0; |
| 3030 this._update(); | 3132 this._update(); |
| 3031 this._scrollHandler(); | |
| 3032 this._itemsRendered = true; | 3133 this._itemsRendered = true; |
| 3033 } | 3134 } |
| 3034 }, | 3135 }, |
| 3035 | 3136 |
| 3036 /** | 3137 /** |
| 3037 * Templetizes the user template. | 3138 * Templetizes the user template. |
| 3038 */ | 3139 */ |
| 3039 _ensureTemplatized: function() { | 3140 _ensureTemplatized: function() { |
| 3040 if (!this.ctor) { | 3141 if (!this.ctor) { |
| 3041 // Template instance props that should be excluded from forwarding | 3142 // 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]; | 3217 var idx = this._physicalIndexForKey[key]; |
| 3117 var el = this._physicalItems[idx]; | 3218 var el = this._physicalItems[idx]; |
| 3118 | 3219 |
| 3119 | 3220 |
| 3120 if (idx === this._focusedIndex && this._offscreenFocusedItem) { | 3221 if (idx === this._focusedIndex && this._offscreenFocusedItem) { |
| 3121 el = this._offscreenFocusedItem; | 3222 el = this._offscreenFocusedItem; |
| 3122 } | 3223 } |
| 3123 if (!el) { | 3224 if (!el) { |
| 3124 return; | 3225 return; |
| 3125 } | 3226 } |
| 3126 | 3227 |
| 3127 inst = el._templateInstance; | 3228 inst = el._templateInstance; |
| 3128 | 3229 |
| 3129 if (inst.__key__ !== key) { | 3230 if (inst.__key__ !== key) { |
| 3130 return; | 3231 return; |
| 3131 } | 3232 } |
| 3132 if (dot >= 0) { | 3233 if (dot >= 0) { |
| 3133 path = this.as + '.' + path.substring(dot+1); | 3234 path = this.as + '.' + path.substring(dot+1); |
| 3134 inst.notifyPath(path, value, true); | 3235 inst.notifyPath(path, value, true); |
| 3135 } else { | 3236 } else { |
| 3136 inst[this.as] = value; | 3237 inst[this.as] = value; |
| (...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3400 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { | 3501 while (currentVirtualItem < idx && targetOffsetTop < hiddenContentSize) { |
| 3401 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; | 3502 targetOffsetTop = targetOffsetTop + this._physicalSizes[currentTopItem]; |
| 3402 currentTopItem = (currentTopItem + 1) % this._physicalCount; | 3503 currentTopItem = (currentTopItem + 1) % this._physicalCount; |
| 3403 currentVirtualItem++; | 3504 currentVirtualItem++; |
| 3404 } | 3505 } |
| 3405 // update the scroller size | 3506 // update the scroller size |
| 3406 this._updateScrollerSize(true); | 3507 this._updateScrollerSize(true); |
| 3407 // update the position of the items | 3508 // update the position of the items |
| 3408 this._positionItems(); | 3509 this._positionItems(); |
| 3409 // set the new scroll position | 3510 // set the new scroll position |
| 3410 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop + 1); | 3511 this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + t
argetOffsetTop); |
| 3411 // increase the pool of physical items if needed | 3512 // increase the pool of physical items if needed |
| 3412 this._increasePoolIfNeeded(); | 3513 this._increasePoolIfNeeded(); |
| 3413 // clear cached visible index | 3514 // clear cached visible index |
| 3414 this._firstVisibleIndexVal = null; | 3515 this._firstVisibleIndexVal = null; |
| 3415 this._lastVisibleIndexVal = null; | 3516 this._lastVisibleIndexVal = null; |
| 3416 }, | 3517 }, |
| 3417 | 3518 |
| 3418 /** | 3519 /** |
| 3419 * Reset the physical average and the average count. | 3520 * Reset the physical average and the average count. |
| 3420 */ | 3521 */ |
| 3421 _resetAverage: function() { | 3522 _resetAverage: function() { |
| 3422 this._physicalAverage = 0; | 3523 this._physicalAverage = 0; |
| 3423 this._physicalAverageCount = 0; | 3524 this._physicalAverageCount = 0; |
| 3424 }, | 3525 }, |
| 3425 | 3526 |
| 3426 /** | 3527 /** |
| 3427 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` | 3528 * A handler for the `iron-resize` event triggered by `IronResizableBehavior
` |
| 3428 * when the element is resized. | 3529 * when the element is resized. |
| 3429 */ | 3530 */ |
| 3430 _resizeHandler: function() { | 3531 _resizeHandler: function() { |
| 3431 // iOS fires the resize event when the address bar slides up | 3532 // iOS fires the resize event when the address bar slides up |
| 3432 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100)
{ | 3533 if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100)
{ |
| 3433 return; | 3534 return; |
| 3434 } | 3535 } |
| 3435 this._debounceTemplate(function() { | 3536 // In Desktop Safari 9.0.3, if the scroll bars are always shown, |
| 3436 this._render(); | 3537 // changing the scroll position from a resize handler would result in |
| 3437 if (this._itemsRendered && this._physicalItems && this._isVisible) { | 3538 // the scroll position being reset. Waiting 1ms fixes the issue. |
| 3438 this._resetAverage(); | 3539 Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', |
| 3439 this.updateViewportBoundaries(); | 3540 function() { |
| 3440 this.scrollToIndex(this.firstVisibleIndex); | 3541 this._render(); |
| 3441 } | 3542 |
| 3442 }); | 3543 if (this._itemsRendered && this._physicalItems && this._isVisible) { |
| 3544 this._resetAverage(); |
| 3545 this.updateViewportBoundaries(); |
| 3546 this.scrollToIndex(this.firstVisibleIndex); |
| 3547 } |
| 3548 }.bind(this), 1)); |
| 3443 }, | 3549 }, |
| 3444 | 3550 |
| 3445 _getModelFromItem: function(item) { | 3551 _getModelFromItem: function(item) { |
| 3446 var key = this._collection.getKey(item); | 3552 var key = this._collection.getKey(item); |
| 3447 var pidx = this._physicalIndexForKey[key]; | 3553 var pidx = this._physicalIndexForKey[key]; |
| 3448 | 3554 |
| 3449 if (pidx != null) { | 3555 if (pidx != null) { |
| 3450 return this._physicalItems[pidx]._templateInstance; | 3556 return this._physicalItems[pidx]._templateInstance; |
| 3451 } | 3557 } |
| 3452 return null; | 3558 return null; |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3754 this._focusPhysicalItem(this._focusedIndex + 1); | 3860 this._focusPhysicalItem(this._focusedIndex + 1); |
| 3755 }, | 3861 }, |
| 3756 | 3862 |
| 3757 _didEnter: function(e) { | 3863 _didEnter: function(e) { |
| 3758 this._focusPhysicalItem(this._focusedIndex); | 3864 this._focusPhysicalItem(this._focusedIndex); |
| 3759 this._selectionHandler(e.detail.keyboardEvent); | 3865 this._selectionHandler(e.detail.keyboardEvent); |
| 3760 } | 3866 } |
| 3761 }); | 3867 }); |
| 3762 | 3868 |
| 3763 })(); | 3869 })(); |
| 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. | 3870 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 3837 // Use of this source code is governed by a BSD-style license that can be | 3871 // Use of this source code is governed by a BSD-style license that can be |
| 3838 // found in the LICENSE file. | 3872 // found in the LICENSE file. |
| 3839 | 3873 |
| 3840 cr.define('downloads', function() { | 3874 cr.define('downloads', function() { |
| 3841 /** | 3875 /** |
| 3842 * @param {string} chromeSendName | 3876 * @param {string} chromeSendName |
| 3843 * @return {function(string):void} A chrome.send() callback with curried name. | 3877 * @return {function(string):void} A chrome.send() callback with curried name. |
| 3844 */ | 3878 */ |
| 3845 function chromeSendWithId(chromeSendName) { | 3879 function chromeSendWithId(chromeSendName) { |
| (...skipping 2704 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6550 * Fired when the list of selectable items changes (e.g., items are | 6584 * 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 | 6585 * added or removed). The detail of the event is a list of mutation |
| 6552 * records that describe what changed. | 6586 * records that describe what changed. |
| 6553 * | 6587 * |
| 6554 * @event iron-items-changed | 6588 * @event iron-items-changed |
| 6555 */ | 6589 */ |
| 6556 | 6590 |
| 6557 properties: { | 6591 properties: { |
| 6558 | 6592 |
| 6559 /** | 6593 /** |
| 6560 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | 6594 * If you want to use an attribute value or property of an element for |
| 6561 * set this to the name of the attribute. | 6595 * `selected` instead of the index, set this to the name of the attribute |
| 6596 * or property. Hyphenated values are converted to camel case when used to |
| 6597 * look up the property of a selectable element. Camel cased values are |
| 6598 * *not* converted to hyphenated values for attribute lookup. It's |
| 6599 * recommended that you provide the hyphenated form of the name so that |
| 6600 * selection works in both cases. (Use `attr-or-property-name` instead of |
| 6601 * `attrOrPropertyName`.) |
| 6562 */ | 6602 */ |
| 6563 attrForSelected: { | 6603 attrForSelected: { |
| 6564 type: String, | 6604 type: String, |
| 6565 value: null | 6605 value: null |
| 6566 }, | 6606 }, |
| 6567 | 6607 |
| 6568 /** | 6608 /** |
| 6569 * Gets or sets the selected element. The default is to use the index of t
he item. | 6609 * Gets or sets the selected element. The default is to use the index of t
he item. |
| 6570 * @type {string|number} | 6610 * @type {string|number} |
| 6571 */ | 6611 */ |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6612 | 6652 |
| 6613 /** | 6653 /** |
| 6614 * The attribute to set on elements when selected. | 6654 * The attribute to set on elements when selected. |
| 6615 */ | 6655 */ |
| 6616 selectedAttribute: { | 6656 selectedAttribute: { |
| 6617 type: String, | 6657 type: String, |
| 6618 value: null | 6658 value: null |
| 6619 }, | 6659 }, |
| 6620 | 6660 |
| 6621 /** | 6661 /** |
| 6662 * Default fallback if the selection based on selected with `attrForSelect
ed` |
| 6663 * is not found. |
| 6664 */ |
| 6665 fallbackSelection: { |
| 6666 type: String, |
| 6667 value: null |
| 6668 }, |
| 6669 |
| 6670 /** |
| 6622 * The list of items from which a selection can be made. | 6671 * The list of items from which a selection can be made. |
| 6623 */ | 6672 */ |
| 6624 items: { | 6673 items: { |
| 6625 type: Array, | 6674 type: Array, |
| 6626 readOnly: true, | 6675 readOnly: true, |
| 6627 notify: true, | 6676 notify: true, |
| 6628 value: function() { | 6677 value: function() { |
| 6629 return []; | 6678 return []; |
| 6630 } | 6679 } |
| 6631 }, | 6680 }, |
| 6632 | 6681 |
| 6633 /** | 6682 /** |
| 6634 * The set of excluded elements where the key is the `localName` | 6683 * The set of excluded elements where the key is the `localName` |
| 6635 * of the element that will be ignored from the item list. | 6684 * of the element that will be ignored from the item list. |
| 6636 * | 6685 * |
| 6637 * @default {template: 1} | 6686 * @default {template: 1} |
| 6638 */ | 6687 */ |
| 6639 _excludedLocalNames: { | 6688 _excludedLocalNames: { |
| 6640 type: Object, | 6689 type: Object, |
| 6641 value: function() { | 6690 value: function() { |
| 6642 return { | 6691 return { |
| 6643 'template': 1 | 6692 'template': 1 |
| 6644 }; | 6693 }; |
| 6645 } | 6694 } |
| 6646 } | 6695 } |
| 6647 }, | 6696 }, |
| 6648 | 6697 |
| 6649 observers: [ | 6698 observers: [ |
| 6650 '_updateAttrForSelected(attrForSelected)', | 6699 '_updateAttrForSelected(attrForSelected)', |
| 6651 '_updateSelected(selected)' | 6700 '_updateSelected(selected)', |
| 6701 '_checkFallback(fallbackSelection)' |
| 6652 ], | 6702 ], |
| 6653 | 6703 |
| 6654 created: function() { | 6704 created: function() { |
| 6655 this._bindFilterItem = this._filterItem.bind(this); | 6705 this._bindFilterItem = this._filterItem.bind(this); |
| 6656 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | 6706 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); |
| 6657 }, | 6707 }, |
| 6658 | 6708 |
| 6659 attached: function() { | 6709 attached: function() { |
| 6660 this._observer = this._observeItems(this); | 6710 this._observer = this._observeItems(this); |
| 6661 this._updateItems(); | 6711 this._updateItems(); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6727 * on its own to reflect selectable items in the DOM. | 6777 * on its own to reflect selectable items in the DOM. |
| 6728 */ | 6778 */ |
| 6729 forceSynchronousItemUpdate: function() { | 6779 forceSynchronousItemUpdate: function() { |
| 6730 this._updateItems(); | 6780 this._updateItems(); |
| 6731 }, | 6781 }, |
| 6732 | 6782 |
| 6733 get _shouldUpdateSelection() { | 6783 get _shouldUpdateSelection() { |
| 6734 return this.selected != null; | 6784 return this.selected != null; |
| 6735 }, | 6785 }, |
| 6736 | 6786 |
| 6787 _checkFallback: function() { |
| 6788 if (this._shouldUpdateSelection) { |
| 6789 this._updateSelected(); |
| 6790 } |
| 6791 }, |
| 6792 |
| 6737 _addListener: function(eventName) { | 6793 _addListener: function(eventName) { |
| 6738 this.listen(this, eventName, '_activateHandler'); | 6794 this.listen(this, eventName, '_activateHandler'); |
| 6739 }, | 6795 }, |
| 6740 | 6796 |
| 6741 _removeListener: function(eventName) { | 6797 _removeListener: function(eventName) { |
| 6742 this.unlisten(this, eventName, '_activateHandler'); | 6798 this.unlisten(this, eventName, '_activateHandler'); |
| 6743 }, | 6799 }, |
| 6744 | 6800 |
| 6745 _activateEventChanged: function(eventName, old) { | 6801 _activateEventChanged: function(eventName, old) { |
| 6746 this._removeListener(old); | 6802 this._removeListener(old); |
| 6747 this._addListener(eventName); | 6803 this._addListener(eventName); |
| 6748 }, | 6804 }, |
| 6749 | 6805 |
| 6750 _updateItems: function() { | 6806 _updateItems: function() { |
| 6751 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); | 6807 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); |
| 6752 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); | 6808 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); |
| 6753 this._setItems(nodes); | 6809 this._setItems(nodes); |
| 6754 }, | 6810 }, |
| 6755 | 6811 |
| 6756 _updateAttrForSelected: function() { | 6812 _updateAttrForSelected: function() { |
| 6757 if (this._shouldUpdateSelection) { | 6813 if (this._shouldUpdateSelection) { |
| 6758 this.selected = this._indexToValue(this.indexOf(this.selectedItem));
| 6814 this.selected = this._indexToValue(this.indexOf(this.selectedItem)); |
| 6759 } | 6815 } |
| 6760 }, | 6816 }, |
| 6761 | 6817 |
| 6762 _updateSelected: function() { | 6818 _updateSelected: function() { |
| 6763 this._selectSelected(this.selected); | 6819 this._selectSelected(this.selected); |
| 6764 }, | 6820 }, |
| 6765 | 6821 |
| 6766 _selectSelected: function(selected) { | 6822 _selectSelected: function(selected) { |
| 6767 this._selection.select(this._valueToItem(this.selected)); | 6823 this._selection.select(this._valueToItem(this.selected)); |
| 6824 // Check for items, since this array is populated only when attached |
| 6825 // Since Number(0) is falsy, explicitly check for undefined |
| 6826 if (this.fallbackSelection && this.items.length && (this._selection.get()
=== undefined)) { |
| 6827 this.selected = this.fallbackSelection; |
| 6828 } |
| 6768 }, | 6829 }, |
| 6769 | 6830 |
| 6770 _filterItem: function(node) { | 6831 _filterItem: function(node) { |
| 6771 return !this._excludedLocalNames[node.localName]; | 6832 return !this._excludedLocalNames[node.localName]; |
| 6772 }, | 6833 }, |
| 6773 | 6834 |
| 6774 _valueToItem: function(value) { | 6835 _valueToItem: function(value) { |
| 6775 return (value == null) ? null : this.items[this._valueToIndex(value)]; | 6836 return (value == null) ? null : this.items[this._valueToIndex(value)]; |
| 6776 }, | 6837 }, |
| 6777 | 6838 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 6792 var item = this.items[index]; | 6853 var item = this.items[index]; |
| 6793 if (item) { | 6854 if (item) { |
| 6794 return this._valueForItem(item); | 6855 return this._valueForItem(item); |
| 6795 } | 6856 } |
| 6796 } else { | 6857 } else { |
| 6797 return index; | 6858 return index; |
| 6798 } | 6859 } |
| 6799 }, | 6860 }, |
| 6800 | 6861 |
| 6801 _valueForItem: function(item) { | 6862 _valueForItem: function(item) { |
| 6802 var propValue = item[this.attrForSelected]; | 6863 var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)
]; |
| 6803 return propValue != undefined ? propValue : item.getAttribute(this.attrFor
Selected); | 6864 return propValue != undefined ? propValue : item.getAttribute(this.attrFor
Selected); |
| 6804 }, | 6865 }, |
| 6805 | 6866 |
| 6806 _applySelection: function(item, isSelected) { | 6867 _applySelection: function(item, isSelected) { |
| 6807 if (this.selectedClass) { | 6868 if (this.selectedClass) { |
| 6808 this.toggleClass(this.selectedClass, isSelected, item); | 6869 this.toggleClass(this.selectedClass, isSelected, item); |
| 6809 } | 6870 } |
| 6810 if (this.selectedAttribute) { | 6871 if (this.selectedAttribute) { |
| 6811 this.toggleAttribute(this.selectedAttribute, isSelected, item); | 6872 this.toggleAttribute(this.selectedAttribute, isSelected, item); |
| 6812 } | 6873 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6885 */ | 6946 */ |
| 6886 selectedItems: { | 6947 selectedItems: { |
| 6887 type: Array, | 6948 type: Array, |
| 6888 readOnly: true, | 6949 readOnly: true, |
| 6889 notify: true | 6950 notify: true |
| 6890 }, | 6951 }, |
| 6891 | 6952 |
| 6892 }, | 6953 }, |
| 6893 | 6954 |
| 6894 observers: [ | 6955 observers: [ |
| 6895 '_updateSelected(selectedValues)' | 6956 '_updateSelected(selectedValues.splices)' |
| 6896 ], | 6957 ], |
| 6897 | 6958 |
| 6898 /** | 6959 /** |
| 6899 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 6960 * 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. | 6961 * `value` will be toggled; otherwise the `value` will be selected. |
| 6901 * | 6962 * |
| 6902 * @method select | 6963 * @method select |
| 6903 * @param {string|number} value the value to select. | 6964 * @param {string|number} value the value to select. |
| 6904 */ | 6965 */ |
| 6905 select: function(value) { | 6966 select: function(value) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 6921 get _shouldUpdateSelection() { | 6982 get _shouldUpdateSelection() { |
| 6922 return this.selected != null || | 6983 return this.selected != null || |
| 6923 (this.selectedValues != null && this.selectedValues.length); | 6984 (this.selectedValues != null && this.selectedValues.length); |
| 6924 }, | 6985 }, |
| 6925 | 6986 |
| 6926 _updateAttrForSelected: function() { | 6987 _updateAttrForSelected: function() { |
| 6927 if (!this.multi) { | 6988 if (!this.multi) { |
| 6928 Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); | 6989 Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); |
| 6929 } else if (this._shouldUpdateSelection) { | 6990 } else if (this._shouldUpdateSelection) { |
| 6930 this.selectedValues = this.selectedItems.map(function(selectedItem) { | 6991 this.selectedValues = this.selectedItems.map(function(selectedItem) { |
| 6931 return this._indexToValue(this.indexOf(selectedItem)); | 6992 return this._indexToValue(this.indexOf(selectedItem)); |
| 6932 }, this).filter(function(unfilteredValue) { | 6993 }, this).filter(function(unfilteredValue) { |
| 6933 return unfilteredValue != null; | 6994 return unfilteredValue != null; |
| 6934 }, this); | 6995 }, this); |
| 6935 } | 6996 } |
| 6936 }, | 6997 }, |
| 6937 | 6998 |
| 6938 _updateSelected: function() { | 6999 _updateSelected: function() { |
| 6939 if (this.multi) { | 7000 if (this.multi) { |
| 6940 this._selectMulti(this.selectedValues); | 7001 this._selectMulti(this.selectedValues); |
| 6941 } else { | 7002 } else { |
| 6942 this._selectSelected(this.selected); | 7003 this._selectSelected(this.selected); |
| 6943 } | 7004 } |
| 6944 }, | 7005 }, |
| 6945 | 7006 |
| 6946 _selectMulti: function(values) { | 7007 _selectMulti: function(values) { |
| 6947 if (values) { | 7008 if (values) { |
| 6948 var selectedItems = this._valuesToItems(values); | 7009 var selectedItems = this._valuesToItems(values); |
| 6949 // clear all but the current selected items | 7010 // clear all but the current selected items |
| 6950 this._selection.clear(selectedItems); | 7011 this._selection.clear(selectedItems); |
| 6951 // select only those not selected yet | 7012 // select only those not selected yet |
| 6952 for (var i = 0; i < selectedItems.length; i++) { | 7013 for (var i = 0; i < selectedItems.length; i++) { |
| 6953 this._selection.setItemSelected(selectedItems[i], true); | 7014 this._selection.setItemSelected(selectedItems[i], true); |
| 6954 } | 7015 } |
| 7016 // Check for items, since this array is populated only when attached |
| 7017 if (this.fallbackSelection && this.items.length && !this._selection.get(
).length) { |
| 7018 var fallback = this._valueToItem(this.fallbackSelection); |
| 7019 if (fallback) { |
| 7020 this.selectedValues = [this.fallbackSelection]; |
| 7021 } |
| 7022 } |
| 6955 } else { | 7023 } else { |
| 6956 this._selection.clear(); | 7024 this._selection.clear(); |
| 6957 } | 7025 } |
| 6958 }, | 7026 }, |
| 6959 | 7027 |
| 6960 _selectionChange: function() { | 7028 _selectionChange: function() { |
| 6961 var s = this._selection.get(); | 7029 var s = this._selection.get(); |
| 6962 if (this.multi) { | 7030 if (this.multi) { |
| 6963 this._setSelectedItems(s); | 7031 this._setSelectedItems(s); |
| 6964 } else { | 7032 } else { |
| 6965 this._setSelectedItems([s]); | 7033 this._setSelectedItems([s]); |
| 6966 this._setSelectedItem(s); | 7034 this._setSelectedItem(s); |
| 6967 } | 7035 } |
| 6968 }, | 7036 }, |
| 6969 | 7037 |
| 6970 _toggleSelected: function(value) { | 7038 _toggleSelected: function(value) { |
| 6971 var i = this.selectedValues.indexOf(value); | 7039 var i = this.selectedValues.indexOf(value); |
| 6972 var unselected = i < 0; | 7040 var unselected = i < 0; |
| 6973 if (unselected) { | 7041 if (unselected) { |
| 6974 this.push('selectedValues',value); | 7042 this.push('selectedValues',value); |
| 6975 } else { | 7043 } else { |
| 6976 this.splice('selectedValues',i,1); | 7044 this.splice('selectedValues',i,1); |
| 6977 } | 7045 } |
| 6978 this._selection.setItemSelected(this._valueToItem(value), unselected); | |
| 6979 }, | 7046 }, |
| 6980 | 7047 |
| 6981 _valuesToItems: function(values) { | 7048 _valuesToItems: function(values) { |
| 6982 return (values == null) ? null : values.map(function(value) { | 7049 return (values == null) ? null : values.map(function(value) { |
| 6983 return this._valueToItem(value); | 7050 return this._valueToItem(value); |
| 6984 }, this); | 7051 }, this); |
| 6985 } | 7052 } |
| 6986 }; | 7053 }; |
| 6987 | 7054 |
| 6988 /** @polymerBehavior */ | 7055 /** @polymerBehavior */ |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7046 this._resetTabindices(); | 7113 this._resetTabindices(); |
| 7047 }, | 7114 }, |
| 7048 | 7115 |
| 7049 /** | 7116 /** |
| 7050 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 7117 * 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. | 7118 * `value` will be toggled; otherwise the `value` will be selected. |
| 7052 * | 7119 * |
| 7053 * @param {string|number} value the value to select. | 7120 * @param {string|number} value the value to select. |
| 7054 */ | 7121 */ |
| 7055 select: function(value) { | 7122 select: function(value) { |
| 7123 // Cancel automatically focusing a default item if the menu received focus |
| 7124 // through a user action selecting a particular item. |
| 7056 if (this._defaultFocusAsync) { | 7125 if (this._defaultFocusAsync) { |
| 7057 this.cancelAsync(this._defaultFocusAsync); | 7126 this.cancelAsync(this._defaultFocusAsync); |
| 7058 this._defaultFocusAsync = null; | 7127 this._defaultFocusAsync = null; |
| 7059 } | 7128 } |
| 7060 var item = this._valueToItem(value); | 7129 var item = this._valueToItem(value); |
| 7061 if (item && item.hasAttribute('disabled')) return; | 7130 if (item && item.hasAttribute('disabled')) return; |
| 7062 this._setFocusedItem(item); | 7131 this._setFocusedItem(item); |
| 7063 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); | 7132 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); |
| 7064 }, | 7133 }, |
| 7065 | 7134 |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7216 } | 7285 } |
| 7217 | 7286 |
| 7218 // Do not focus the selected tab if the deepest target is part of the | 7287 // Do not focus the selected tab if the deepest target is part of the |
| 7219 // menu element's local DOM and is focusable. | 7288 // menu element's local DOM and is focusable. |
| 7220 var rootTarget = /** @type {?HTMLElement} */( | 7289 var rootTarget = /** @type {?HTMLElement} */( |
| 7221 Polymer.dom(event).rootTarget); | 7290 Polymer.dom(event).rootTarget); |
| 7222 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !
this.isLightDescendant(rootTarget)) { | 7291 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !
this.isLightDescendant(rootTarget)) { |
| 7223 return; | 7292 return; |
| 7224 } | 7293 } |
| 7225 | 7294 |
| 7226 this.blur(); | |
| 7227 | |
| 7228 // clear the cached focus item | 7295 // clear the cached focus item |
| 7229 this._defaultFocusAsync = this.async(function() { | 7296 this._defaultFocusAsync = this.async(function() { |
| 7230 // focus the selected item when the menu receives focus, or the first it
em | 7297 // focus the selected item when the menu receives focus, or the first it
em |
| 7231 // if no item is selected | 7298 // if no item is selected |
| 7232 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | 7299 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; |
| 7233 | 7300 |
| 7234 this._setFocusedItem(null); | 7301 this._setFocusedItem(null); |
| 7235 | 7302 |
| 7236 if (selectedItem) { | 7303 if (selectedItem) { |
| 7237 this._setFocusedItem(selectedItem); | 7304 this._setFocusedItem(selectedItem); |
| 7238 } else { | 7305 } else if (this.items[0]) { |
| 7239 this._setFocusedItem(this.items[0]); | 7306 this._setFocusedItem(this.items[0]); |
| 7240 } | 7307 } |
| 7241 // async 1ms to wait for `select` to get called from `_itemActivate` | 7308 }); |
| 7242 }, 1); | |
| 7243 }, | 7309 }, |
| 7244 | 7310 |
| 7245 /** | 7311 /** |
| 7246 * Handler that is called when the up key is pressed. | 7312 * Handler that is called when the up key is pressed. |
| 7247 * | 7313 * |
| 7248 * @param {CustomEvent} event A key combination event. | 7314 * @param {CustomEvent} event A key combination event. |
| 7249 */ | 7315 */ |
| 7250 _onUpKey: function(event) { | 7316 _onUpKey: function(event) { |
| 7251 // up and down arrows moves the focus | 7317 // up and down arrows moves the focus |
| 7252 this._focusPrevious(); | 7318 this._focusPrevious(); |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7562 if (!positionedBy.horizontally) { | 7628 if (!positionedBy.horizontally) { |
| 7563 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; | 7629 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; |
| 7564 this.style.left = left + 'px'; | 7630 this.style.left = left + 'px'; |
| 7565 } | 7631 } |
| 7566 } | 7632 } |
| 7567 | 7633 |
| 7568 }; | 7634 }; |
| 7569 /** | 7635 /** |
| 7570 * @struct | 7636 * @struct |
| 7571 * @constructor | 7637 * @constructor |
| 7638 * @private |
| 7572 */ | 7639 */ |
| 7573 Polymer.IronOverlayManagerClass = function() { | 7640 Polymer.IronOverlayManagerClass = function() { |
| 7641 /** |
| 7642 * Used to keep track of the opened overlays. |
| 7643 * @private {Array<Element>} |
| 7644 */ |
| 7574 this._overlays = []; | 7645 this._overlays = []; |
| 7575 // Used to keep track of the last focused node before an overlay gets opened
. | 7646 |
| 7576 this._lastFocusedNodes = []; | 7647 /** |
| 7577 | 7648 * iframes have a default z-index of 100, |
| 7578 /** | 7649 * 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} | 7650 * @private {number} |
| 7582 */ | 7651 */ |
| 7583 this._minimumZ = 101; | 7652 this._minimumZ = 101; |
| 7584 | 7653 |
| 7585 this._backdrops = []; | 7654 /** |
| 7586 | 7655 * Memoized backdrop element. |
| 7656 * @private {Element|null} |
| 7657 */ |
| 7587 this._backdropElement = null; | 7658 this._backdropElement = null; |
| 7588 Object.defineProperty(this, 'backdropElement', { | 7659 |
| 7589 get: function() { | 7660 // Listen to mousedown or touchstart to be sure to be the first to capture |
| 7590 if (!this._backdropElement) { | 7661 // clicks outside the overlay. |
| 7591 this._backdropElement = document.createElement('iron-overlay-backdrop'
); | 7662 var clickEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown'; |
| 7592 } | 7663 document.addEventListener(clickEvent, this._onCaptureClick.bind(this), true)
; |
| 7593 return this._backdropElement; | 7664 document.addEventListener('focus', this._onCaptureFocus.bind(this), true); |
| 7594 }.bind(this) | 7665 document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true
); |
| 7595 }); | 7666 }; |
| 7667 |
| 7668 Polymer.IronOverlayManagerClass.prototype = { |
| 7669 |
| 7670 constructor: Polymer.IronOverlayManagerClass, |
| 7671 |
| 7672 /** |
| 7673 * The shared backdrop element. |
| 7674 * @type {Element} backdropElement |
| 7675 */ |
| 7676 get backdropElement() { |
| 7677 if (!this._backdropElement) { |
| 7678 this._backdropElement = document.createElement('iron-overlay-backdrop'); |
| 7679 } |
| 7680 return this._backdropElement; |
| 7681 }, |
| 7596 | 7682 |
| 7597 /** | 7683 /** |
| 7598 * The deepest active element. | 7684 * The deepest active element. |
| 7599 * returns {?Node} element the active element | 7685 * @type {Element} activeElement the active element |
| 7600 */ | 7686 */ |
| 7601 this.deepActiveElement = null; | 7687 get deepActiveElement() { |
| 7602 Object.defineProperty(this, 'deepActiveElement', { | 7688 // document.activeElement can be null |
| 7603 get: function() { | 7689 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement |
| 7604 var active = document.activeElement; | 7690 // In case of null, default it to document.body. |
| 7605 // document.activeElement can be null | 7691 var active = document.activeElement || document.body; |
| 7606 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeEleme
nt | 7692 while (active.root && Polymer.dom(active.root).activeElement) { |
| 7607 while (active && active.root && Polymer.dom(active.root).activeElement)
{ | 7693 active = Polymer.dom(active.root).activeElement; |
| 7608 active = Polymer.dom(active.root).activeElement; | 7694 } |
| 7609 } | 7695 return active; |
| 7610 return active; | 7696 }, |
| 7611 }.bind(this) | 7697 |
| 7612 }); | 7698 /** |
| 7613 }; | 7699 * Brings the overlay at the specified index to the front. |
| 7614 | 7700 * @param {number} i |
| 7615 /** | 7701 * @private |
| 7616 * If a node is contained in an overlay. | 7702 */ |
| 7617 * @private | 7703 _bringOverlayAtIndexToFront: function(i) { |
| 7618 * @param {Node} node | 7704 var overlay = this._overlays[i]; |
| 7619 * @returns {Boolean} | 7705 var lastI = this._overlays.length - 1; |
| 7620 */ | 7706 // Ensure always-on-top overlay stays on top. |
| 7621 Polymer.IronOverlayManagerClass.prototype._isChildOfOverlay = function(node) { | 7707 if (!overlay.alwaysOnTop && this._overlays[lastI].alwaysOnTop) { |
| 7622 while (node && node !== document.body) { | 7708 lastI--; |
| 7623 // Use logical parentNode, or native ShadowRoot host. | 7709 } |
| 7624 node = Polymer.dom(node).parentNode || node.host; | 7710 // If already the top element, return. |
| 7625 // Check if it is an overlay. | 7711 if (!overlay || i >= lastI) { |
| 7626 if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBe
haviorImpl) !== -1) { | 7712 return; |
| 7627 return true; | 7713 } |
| 7628 } | 7714 // Update z-index to be on top. |
| 7629 } | 7715 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
| 7630 return false; | 7716 if (this._getZ(overlay) <= minimumZ) { |
| 7631 }; | 7717 this._applyOverlayZ(overlay, minimumZ); |
| 7632 | 7718 } |
| 7633 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { | 7719 |
| 7634 this._setZ(overlay, aboveZ + 2); | 7720 // Shift other overlays behind the new on top. |
| 7635 }; | 7721 while (i < lastI) { |
| 7636 | 7722 this._overlays[i] = this._overlays[i + 1]; |
| 7637 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { | 7723 i++; |
| 7638 element.style.zIndex = z; | 7724 } |
| 7639 }; | 7725 this._overlays[lastI] = overlay; |
| 7640 | 7726 }, |
| 7641 /** | 7727 |
| 7642 * track overlays for z-index and focus managemant | 7728 /** |
| 7643 */ | 7729 * 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) { | 7730 * Also updates the backdrop z-index. |
| 7645 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | 7731 * @param {Element} overlay |
| 7646 this._overlays.push(overlay); | 7732 */ |
| 7647 var newZ = this.currentOverlayZ(); | 7733 addOrRemoveOverlay: function(overlay) { |
| 7648 if (newZ <= minimumZ) { | 7734 if (overlay.opened) { |
| 7649 this._applyOverlayZ(overlay, minimumZ); | 7735 this.addOverlay(overlay); |
| 7650 } | 7736 } else { |
| 7651 var element = this.deepActiveElement; | 7737 this.removeOverlay(overlay); |
| 7652 // If already in other overlay, don't reset focus there. | 7738 } |
| 7653 if (this._isChildOfOverlay(element)) { | 7739 this.trackBackdrop(); |
| 7654 element = null; | 7740 }, |
| 7655 } | 7741 |
| 7656 this._lastFocusedNodes.push(element); | 7742 /** |
| 7657 }; | 7743 * Tracks overlays for z-index and focus management. |
| 7658 | 7744 * Ensures the last added overlay with always-on-top remains on top. |
| 7659 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { | 7745 * @param {Element} overlay |
| 7660 var i = this._overlays.indexOf(overlay); | 7746 */ |
| 7661 if (i >= 0) { | 7747 addOverlay: function(overlay) { |
| 7748 var i = this._overlays.indexOf(overlay); |
| 7749 if (i >= 0) { |
| 7750 this._bringOverlayAtIndexToFront(i); |
| 7751 return; |
| 7752 } |
| 7753 var insertionIndex = this._overlays.length; |
| 7754 var currentOverlay = this._overlays[insertionIndex - 1]; |
| 7755 var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ); |
| 7756 var newZ = this._getZ(overlay); |
| 7757 |
| 7758 // Ensure always-on-top overlay stays on top. |
| 7759 if (currentOverlay && currentOverlay.alwaysOnTop && !overlay.alwaysOnTop)
{ |
| 7760 // This bumps the z-index of +2. |
| 7761 this._applyOverlayZ(currentOverlay, minimumZ); |
| 7762 insertionIndex--; |
| 7763 // Update minimumZ to match previous overlay's z-index. |
| 7764 var previousOverlay = this._overlays[insertionIndex - 1]; |
| 7765 minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ); |
| 7766 } |
| 7767 |
| 7768 // Update z-index and insert overlay. |
| 7769 if (newZ <= minimumZ) { |
| 7770 this._applyOverlayZ(overlay, minimumZ); |
| 7771 } |
| 7772 this._overlays.splice(insertionIndex, 0, overlay); |
| 7773 |
| 7774 // Get focused node. |
| 7775 var element = this.deepActiveElement; |
| 7776 overlay.restoreFocusNode = this._overlayParent(element) ? null : element; |
| 7777 }, |
| 7778 |
| 7779 /** |
| 7780 * @param {Element} overlay |
| 7781 */ |
| 7782 removeOverlay: function(overlay) { |
| 7783 var i = this._overlays.indexOf(overlay); |
| 7784 if (i === -1) { |
| 7785 return; |
| 7786 } |
| 7662 this._overlays.splice(i, 1); | 7787 this._overlays.splice(i, 1); |
| 7663 this._setZ(overlay, ''); | 7788 |
| 7664 | 7789 var node = overlay.restoreFocusOnClose ? overlay.restoreFocusNode : null; |
| 7665 var node = this._lastFocusedNodes[i]; | 7790 overlay.restoreFocusNode = null; |
| 7666 // Focus only if still contained in document.body | 7791 // Focus back only if still contained in document.body |
| 7667 if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deep
Contains(node)) { | 7792 if (node && Polymer.dom(document.body).deepContains(node)) { |
| 7668 node.focus(); | 7793 node.focus(); |
| 7669 } | 7794 } |
| 7670 this._lastFocusedNodes.splice(i, 1); | 7795 }, |
| 7796 |
| 7797 /** |
| 7798 * Returns the current overlay. |
| 7799 * @return {Element|undefined} |
| 7800 */ |
| 7801 currentOverlay: function() { |
| 7802 var i = this._overlays.length - 1; |
| 7803 return this._overlays[i]; |
| 7804 }, |
| 7805 |
| 7806 /** |
| 7807 * Returns the current overlay z-index. |
| 7808 * @return {number} |
| 7809 */ |
| 7810 currentOverlayZ: function() { |
| 7811 return this._getZ(this.currentOverlay()); |
| 7812 }, |
| 7813 |
| 7814 /** |
| 7815 * Ensures that the minimum z-index of new overlays is at least `minimumZ`. |
| 7816 * This does not effect the z-index of any existing overlays. |
| 7817 * @param {number} minimumZ |
| 7818 */ |
| 7819 ensureMinimumZ: function(minimumZ) { |
| 7820 this._minimumZ = Math.max(this._minimumZ, minimumZ); |
| 7821 }, |
| 7822 |
| 7823 focusOverlay: function() { |
| 7824 var current = /** @type {?} */ (this.currentOverlay()); |
| 7825 // We have to be careful to focus the next overlay _after_ any current |
| 7826 // transitions are complete (due to the state being toggled prior to the |
| 7827 // transition). Otherwise, we risk infinite recursion when a transitioning |
| 7828 // (closed) overlay becomes the current overlay. |
| 7829 // |
| 7830 // NOTE: We make the assumption that any overlay that completes a transiti
on |
| 7831 // will call into focusOverlay to kick the process back off. Currently: |
| 7832 // transitionend -> _applyFocus -> focusOverlay. |
| 7833 if (current && !current.transitioning) { |
| 7834 current._applyFocus(); |
| 7835 } |
| 7836 }, |
| 7837 |
| 7838 /** |
| 7839 * Updates the backdrop z-index. |
| 7840 */ |
| 7841 trackBackdrop: function() { |
| 7842 this.backdropElement.style.zIndex = this.backdropZ(); |
| 7843 }, |
| 7844 |
| 7845 /** |
| 7846 * @return {Array<Element>} |
| 7847 */ |
| 7848 getBackdrops: function() { |
| 7849 var backdrops = []; |
| 7850 for (var i = 0; i < this._overlays.length; i++) { |
| 7851 if (this._overlays[i].withBackdrop) { |
| 7852 backdrops.push(this._overlays[i]); |
| 7853 } |
| 7854 } |
| 7855 return backdrops; |
| 7856 }, |
| 7857 |
| 7858 /** |
| 7859 * Returns the z-index for the backdrop. |
| 7860 * @return {number} |
| 7861 */ |
| 7862 backdropZ: function() { |
| 7863 return this._getZ(this._overlayWithBackdrop()) - 1; |
| 7864 }, |
| 7865 |
| 7866 /** |
| 7867 * Returns the first opened overlay that has a backdrop. |
| 7868 * @return {Element|undefined} |
| 7869 * @private |
| 7870 */ |
| 7871 _overlayWithBackdrop: function() { |
| 7872 for (var i = 0; i < this._overlays.length; i++) { |
| 7873 if (this._overlays[i].withBackdrop) { |
| 7874 return this._overlays[i]; |
| 7875 } |
| 7876 } |
| 7877 }, |
| 7878 |
| 7879 /** |
| 7880 * Calculates the minimum z-index for the overlay. |
| 7881 * @param {Element=} overlay |
| 7882 * @private |
| 7883 */ |
| 7884 _getZ: function(overlay) { |
| 7885 var z = this._minimumZ; |
| 7886 if (overlay) { |
| 7887 var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay)
.zIndex); |
| 7888 // Check if is a number |
| 7889 // Number.isNaN not supported in IE 10+ |
| 7890 if (z1 === z1) { |
| 7891 z = z1; |
| 7892 } |
| 7893 } |
| 7894 return z; |
| 7895 }, |
| 7896 |
| 7897 /** |
| 7898 * @param {Element} element |
| 7899 * @param {number|string} z |
| 7900 * @private |
| 7901 */ |
| 7902 _setZ: function(element, z) { |
| 7903 element.style.zIndex = z; |
| 7904 }, |
| 7905 |
| 7906 /** |
| 7907 * @param {Element} overlay |
| 7908 * @param {number} aboveZ |
| 7909 * @private |
| 7910 */ |
| 7911 _applyOverlayZ: function(overlay, aboveZ) { |
| 7912 this._setZ(overlay, aboveZ + 2); |
| 7913 }, |
| 7914 |
| 7915 /** |
| 7916 * Returns the overlay containing the provided node. If the node is an overl
ay, |
| 7917 * it returns the node. |
| 7918 * @param {Element=} node |
| 7919 * @return {Element|undefined} |
| 7920 * @private |
| 7921 */ |
| 7922 _overlayParent: function(node) { |
| 7923 while (node && node !== document.body) { |
| 7924 // Check if it is an overlay. |
| 7925 if (node._manager === this) { |
| 7926 return node; |
| 7927 } |
| 7928 // Use logical parentNode, or native ShadowRoot host. |
| 7929 node = Polymer.dom(node).parentNode || node.host; |
| 7930 } |
| 7931 }, |
| 7932 |
| 7933 /** |
| 7934 * Returns the deepest overlay in the path. |
| 7935 * @param {Array<Element>=} path |
| 7936 * @return {Element|undefined} |
| 7937 * @private |
| 7938 */ |
| 7939 _overlayInPath: function(path) { |
| 7940 path = path || []; |
| 7941 for (var i = 0; i < path.length; i++) { |
| 7942 if (path[i]._manager === this) { |
| 7943 return path[i]; |
| 7944 } |
| 7945 } |
| 7946 }, |
| 7947 |
| 7948 /** |
| 7949 * Ensures the click event is delegated to the right overlay. |
| 7950 * @param {!Event} event |
| 7951 * @private |
| 7952 */ |
| 7953 _onCaptureClick: function(event) { |
| 7954 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7955 // Check if clicked outside of top overlay. |
| 7956 if (overlay && this._overlayInPath(Polymer.dom(event).path) !== overlay) { |
| 7957 overlay._onCaptureClick(event); |
| 7958 } |
| 7959 }, |
| 7960 |
| 7961 /** |
| 7962 * Ensures the focus event is delegated to the right overlay. |
| 7963 * @param {!Event} event |
| 7964 * @private |
| 7965 */ |
| 7966 _onCaptureFocus: function(event) { |
| 7967 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7968 if (overlay) { |
| 7969 overlay._onCaptureFocus(event); |
| 7970 } |
| 7971 }, |
| 7972 |
| 7973 /** |
| 7974 * Ensures TAB and ESC keyboard events are delegated to the right overlay. |
| 7975 * @param {!Event} event |
| 7976 * @private |
| 7977 */ |
| 7978 _onCaptureKeyDown: function(event) { |
| 7979 var overlay = /** @type {?} */ (this.currentOverlay()); |
| 7980 if (overlay) { |
| 7981 if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc'))
{ |
| 7982 overlay._onCaptureEsc(event); |
| 7983 } else if (Polymer.IronA11yKeysBehavior.keyboardEventMatchesKeys(event,
'tab')) { |
| 7984 overlay._onCaptureTab(event); |
| 7985 } |
| 7986 } |
| 7671 } | 7987 } |
| 7672 }; | 7988 }; |
| 7673 | 7989 |
| 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(); | 7990 Polymer.IronOverlayManager = new Polymer.IronOverlayManagerClass(); |
| 7764 (function() { | 7991 (function() { |
| 7765 | 7992 |
| 7766 Polymer({ | 7993 Polymer({ |
| 7767 | 7994 |
| 7768 is: 'iron-overlay-backdrop', | 7995 is: 'iron-overlay-backdrop', |
| 7769 | 7996 |
| 7770 properties: { | 7997 properties: { |
| 7771 | 7998 |
| 7772 /** | 7999 /** |
| (...skipping 14 matching lines...) Expand all Loading... |
| 7787 }, | 8014 }, |
| 7788 | 8015 |
| 7789 listeners: { | 8016 listeners: { |
| 7790 'transitionend' : '_onTransitionend' | 8017 'transitionend' : '_onTransitionend' |
| 7791 }, | 8018 }, |
| 7792 | 8019 |
| 7793 /** | 8020 /** |
| 7794 * Appends the backdrop to document body and sets its `z-index` to be below
the latest overlay. | 8021 * Appends the backdrop to document body and sets its `z-index` to be below
the latest overlay. |
| 7795 */ | 8022 */ |
| 7796 prepare: function() { | 8023 prepare: function() { |
| 7797 // Always update z-index | |
| 7798 this.style.zIndex = this._manager.backdropZ(); | |
| 7799 if (!this.parentNode) { | 8024 if (!this.parentNode) { |
| 7800 Polymer.dom(document.body).appendChild(this); | 8025 Polymer.dom(document.body).appendChild(this); |
| 7801 } | 8026 } |
| 7802 }, | 8027 }, |
| 7803 | 8028 |
| 7804 /** | 8029 /** |
| 7805 * Shows the backdrop if needed. | 8030 * Shows the backdrop if needed. |
| 7806 */ | 8031 */ |
| 7807 open: function() { | 8032 open: function() { |
| 7808 // only need to make the backdrop visible if this is called by the first o
verlay with a backdrop | 8033 // 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) { | 8034 if (this._manager.getBackdrops().length < 2) { |
| 7810 this._setOpened(true); | 8035 this._setOpened(true); |
| 7811 } | 8036 } |
| 7812 }, | 8037 }, |
| 7813 | 8038 |
| 7814 /** | 8039 /** |
| 7815 * Hides the backdrop if needed. | 8040 * Hides the backdrop if needed. |
| 7816 */ | 8041 */ |
| 7817 close: function() { | 8042 close: function() { |
| 7818 // Always update z-index | |
| 7819 this.style.zIndex = this._manager.backdropZ(); | |
| 7820 // close only if no element with backdrop is left | 8043 // close only if no element with backdrop is left |
| 7821 if (this._manager.getBackdrops().length === 0) { | 8044 if (this._manager.getBackdrops().length === 0) { |
| 7822 // Read style before setting opened. | 8045 // Read style before setting opened. |
| 7823 var cs = getComputedStyle(this); | 8046 var cs = getComputedStyle(this); |
| 7824 var noAnimation = (cs.transitionDuration === '0s' || cs.opacity == 0); | 8047 var noAnimation = (cs.transitionDuration === '0s' || cs.opacity == 0); |
| 7825 this._setOpened(false); | 8048 this._setOpened(false); |
| 7826 // In case of no animations, complete | 8049 // In case of no animations, complete |
| 7827 if (noAnimation) { | 8050 if (noAnimation) { |
| 7828 this.complete(); | 8051 this.complete(); |
| 7829 } | 8052 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 7842 | 8065 |
| 7843 _onTransitionend: function (event) { | 8066 _onTransitionend: function (event) { |
| 7844 if (event && event.target === this) { | 8067 if (event && event.target === this) { |
| 7845 this.complete(); | 8068 this.complete(); |
| 7846 } | 8069 } |
| 7847 } | 8070 } |
| 7848 | 8071 |
| 7849 }); | 8072 }); |
| 7850 | 8073 |
| 7851 })(); | 8074 })(); |
| 8075 // IIFE to help scripts concatenation. |
| 8076 (function() { |
| 8077 'use strict'; |
| 8078 |
| 7852 /** | 8079 /** |
| 7853 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays | 8080 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 | 8081 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. | 8082 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. |
| 7856 | 8083 |
| 7857 ### Closing and canceling | 8084 ### Closing and canceling |
| 7858 | 8085 |
| 7859 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user | 8086 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, | 8087 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 | 8088 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 /** | 8172 /** |
| 7946 * Returns the reason this dialog was last closed. | 8173 * Returns the reason this dialog was last closed. |
| 7947 */ | 8174 */ |
| 7948 closingReason: { | 8175 closingReason: { |
| 7949 // was a getter before, but needs to be a property so other | 8176 // was a getter before, but needs to be a property so other |
| 7950 // behaviors can override this. | 8177 // behaviors can override this. |
| 7951 type: Object | 8178 type: Object |
| 7952 }, | 8179 }, |
| 7953 | 8180 |
| 7954 /** | 8181 /** |
| 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. | 8182 * Set to true to enable restoring of focus when overlay is closed. |
| 7965 */ | 8183 */ |
| 7966 restoreFocusOnClose: { | 8184 restoreFocusOnClose: { |
| 7967 type: Boolean, | 8185 type: Boolean, |
| 7968 value: false | 8186 value: false |
| 7969 }, | 8187 }, |
| 7970 | 8188 |
| 8189 /** |
| 8190 * Set to true to keep overlay always on top. |
| 8191 */ |
| 8192 alwaysOnTop: { |
| 8193 type: Boolean |
| 8194 }, |
| 8195 |
| 8196 /** |
| 8197 * Shortcut to access to the overlay manager. |
| 8198 * @private |
| 8199 * @type {Polymer.IronOverlayManagerClass} |
| 8200 */ |
| 7971 _manager: { | 8201 _manager: { |
| 7972 type: Object, | 8202 type: Object, |
| 7973 value: Polymer.IronOverlayManager | 8203 value: Polymer.IronOverlayManager |
| 7974 }, | 8204 }, |
| 7975 | 8205 |
| 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 /** | 8206 /** |
| 7991 * The node being focused. | 8207 * The node being focused. |
| 7992 * @type {?Node} | 8208 * @type {?Node} |
| 7993 */ | 8209 */ |
| 7994 _focusedChild: { | 8210 _focusedChild: { |
| 7995 type: Object | 8211 type: Object |
| 7996 } | 8212 } |
| 7997 | 8213 |
| 7998 }, | 8214 }, |
| 7999 | 8215 |
| 8000 keyBindings: { | |
| 8001 'esc': '__onEsc', | |
| 8002 'tab': '__onTab' | |
| 8003 }, | |
| 8004 | |
| 8005 listeners: { | 8216 listeners: { |
| 8006 'iron-resize': '_onIronResize' | 8217 'iron-resize': '_onIronResize' |
| 8007 }, | 8218 }, |
| 8008 | 8219 |
| 8009 /** | 8220 /** |
| 8010 * The backdrop element. | 8221 * The backdrop element. |
| 8011 * @type {Node} | 8222 * @type {Element} |
| 8012 */ | 8223 */ |
| 8013 get backdropElement() { | 8224 get backdropElement() { |
| 8014 return this._manager.backdropElement; | 8225 return this._manager.backdropElement; |
| 8015 }, | 8226 }, |
| 8016 | 8227 |
| 8017 /** | 8228 /** |
| 8018 * Returns the node to give focus to. | 8229 * Returns the node to give focus to. |
| 8019 * @type {Node} | 8230 * @type {Node} |
| 8020 */ | 8231 */ |
| 8021 get _focusNode() { | 8232 get _focusNode() { |
| 8022 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; | 8233 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; |
| 8023 }, | 8234 }, |
| 8024 | 8235 |
| 8025 /** | 8236 /** |
| 8026 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. | 8237 * 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 | 8238 * 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`. | 8239 * to wrap the focus for overlays `with-backdrop`. |
| 8029 * | 8240 * |
| 8030 * If you know what is your content (specifically the first and last focusab
le children), | 8241 * 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];` | 8242 * you can override this method to return only `[firstFocusable, lastFocusab
le];` |
| 8032 * @type {[Node]} | 8243 * @type {Array<Node>} |
| 8033 * @protected | 8244 * @protected |
| 8034 */ | 8245 */ |
| 8035 get _focusableNodes() { | 8246 get _focusableNodes() { |
| 8036 // Elements that can be focused even if they have [disabled] attribute. | 8247 // Elements that can be focused even if they have [disabled] attribute. |
| 8037 var FOCUSABLE_WITH_DISABLED = [ | 8248 var FOCUSABLE_WITH_DISABLED = [ |
| 8038 'a[href]', | 8249 'a[href]', |
| 8039 'area[href]', | 8250 'area[href]', |
| 8040 'iframe', | 8251 'iframe', |
| 8041 '[tabindex]', | 8252 '[tabindex]', |
| 8042 '[contentEditable=true]' | 8253 '[contentEditable=true]' |
| (...skipping 25 matching lines...) Expand all Loading... |
| 8068 return 0; | 8279 return 0; |
| 8069 } | 8280 } |
| 8070 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { | 8281 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { |
| 8071 return 1; | 8282 return 1; |
| 8072 } | 8283 } |
| 8073 return -1; | 8284 return -1; |
| 8074 }); | 8285 }); |
| 8075 }, | 8286 }, |
| 8076 | 8287 |
| 8077 ready: function() { | 8288 ready: function() { |
| 8289 // Used to skip calls to notifyResize and refit while the overlay is anima
ting. |
| 8290 this.__isAnimating = false; |
| 8078 // with-backdrop needs tabindex to be set in order to trap the focus. | 8291 // 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. | 8292 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 8080 this.__shouldRemoveTabIndex = false; | 8293 this.__shouldRemoveTabIndex = false; |
| 8081 // Used for wrapping the focus on TAB / Shift+TAB. | 8294 // Used for wrapping the focus on TAB / Shift+TAB. |
| 8082 this.__firstFocusableNode = this.__lastFocusableNode = null; | 8295 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| 8296 // Used for requestAnimationFrame when opened changes. |
| 8297 this.__openChangedAsync = null; |
| 8298 // Used for requestAnimationFrame when iron-resize is fired. |
| 8299 this.__onIronResizeAsync = null; |
| 8083 this._ensureSetup(); | 8300 this._ensureSetup(); |
| 8084 }, | 8301 }, |
| 8085 | 8302 |
| 8086 attached: function() { | 8303 attached: function() { |
| 8087 // Call _openedChanged here so that position can be computed correctly. | 8304 // Call _openedChanged here so that position can be computed correctly. |
| 8088 if (this.opened) { | 8305 if (this.opened) { |
| 8089 this._openedChanged(); | 8306 this._openedChanged(); |
| 8090 } | 8307 } |
| 8091 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); | 8308 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); |
| 8092 }, | 8309 }, |
| 8093 | 8310 |
| 8094 detached: function() { | 8311 detached: function() { |
| 8095 Polymer.dom(this).unobserveNodes(this._observer); | 8312 Polymer.dom(this).unobserveNodes(this._observer); |
| 8096 this._observer = null; | 8313 this._observer = null; |
| 8097 this.opened = false; | 8314 this.opened = false; |
| 8098 this._manager.trackBackdrop(this); | 8315 if (this.withBackdrop) { |
| 8099 this._manager.removeOverlay(this); | 8316 // Allow user interactions right away. |
| 8317 this.backdropElement.close(); |
| 8318 } |
| 8100 }, | 8319 }, |
| 8101 | 8320 |
| 8102 /** | 8321 /** |
| 8103 * Toggle the opened state of the overlay. | 8322 * Toggle the opened state of the overlay. |
| 8104 */ | 8323 */ |
| 8105 toggle: function() { | 8324 toggle: function() { |
| 8106 this._setCanceled(false); | 8325 this._setCanceled(false); |
| 8107 this.opened = !this.opened; | 8326 this.opened = !this.opened; |
| 8108 }, | 8327 }, |
| 8109 | 8328 |
| 8110 /** | 8329 /** |
| 8111 * Open the overlay. | 8330 * Open the overlay. |
| 8112 */ | 8331 */ |
| 8113 open: function() { | 8332 open: function() { |
| 8114 this._setCanceled(false); | 8333 this._setCanceled(false); |
| 8115 this.opened = true; | 8334 this.opened = true; |
| 8116 }, | 8335 }, |
| 8117 | 8336 |
| 8118 /** | 8337 /** |
| 8119 * Close the overlay. | 8338 * Close the overlay. |
| 8120 */ | 8339 */ |
| 8121 close: function() { | 8340 close: function() { |
| 8122 this._setCanceled(false); | 8341 this._setCanceled(false); |
| 8123 this.opened = false; | 8342 this.opened = false; |
| 8124 }, | 8343 }, |
| 8125 | 8344 |
| 8126 /** | 8345 /** |
| 8127 * Cancels the overlay. | 8346 * Cancels the overlay. |
| 8128 * @param {?Event} event The original event | 8347 * @param {Event=} event The original event |
| 8129 */ | 8348 */ |
| 8130 cancel: function(event) { | 8349 cancel: function(event) { |
| 8131 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); | 8350 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); |
| 8132 if (cancelEvent.defaultPrevented) { | 8351 if (cancelEvent.defaultPrevented) { |
| 8133 return; | 8352 return; |
| 8134 } | 8353 } |
| 8135 | 8354 |
| 8136 this._setCanceled(true); | 8355 this._setCanceled(true); |
| 8137 this.opened = false; | 8356 this.opened = false; |
| 8138 }, | 8357 }, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 8151 this.removeAttribute('aria-hidden'); | 8370 this.removeAttribute('aria-hidden'); |
| 8152 } else { | 8371 } else { |
| 8153 this.setAttribute('aria-hidden', 'true'); | 8372 this.setAttribute('aria-hidden', 'true'); |
| 8154 } | 8373 } |
| 8155 | 8374 |
| 8156 // wait to call after ready only if we're initially open | 8375 // wait to call after ready only if we're initially open |
| 8157 if (!this._overlaySetup) { | 8376 if (!this._overlaySetup) { |
| 8158 return; | 8377 return; |
| 8159 } | 8378 } |
| 8160 | 8379 |
| 8161 this._manager.trackBackdrop(this); | 8380 this._manager.addOrRemoveOverlay(this); |
| 8162 | 8381 |
| 8382 this.__isAnimating = true; |
| 8383 |
| 8384 // requestAnimationFrame for non-blocking rendering |
| 8385 if (this.__openChangedAsync) { |
| 8386 cancelAnimationFrame(this.__openChangedAsync); |
| 8387 } |
| 8163 if (this.opened) { | 8388 if (this.opened) { |
| 8164 this._prepareRenderOpened(); | 8389 if (this.withBackdrop) { |
| 8390 this.backdropElement.prepare(); |
| 8391 } |
| 8392 this.__openChangedAsync = requestAnimationFrame(function() { |
| 8393 this.__openChangedAsync = null; |
| 8394 this._prepareRenderOpened(); |
| 8395 this._renderOpened(); |
| 8396 }.bind(this)); |
| 8397 } else { |
| 8398 this._renderClosed(); |
| 8165 } | 8399 } |
| 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 }, | 8400 }, |
| 8187 | 8401 |
| 8188 _canceledChanged: function() { | 8402 _canceledChanged: function() { |
| 8189 this.closingReason = this.closingReason || {}; | 8403 this.closingReason = this.closingReason || {}; |
| 8190 this.closingReason.canceled = this.canceled; | 8404 this.closingReason.canceled = this.canceled; |
| 8191 }, | 8405 }, |
| 8192 | 8406 |
| 8193 _withBackdropChanged: function() { | 8407 _withBackdropChanged: function() { |
| 8194 // If tabindex is already set, no need to override it. | 8408 // If tabindex is already set, no need to override it. |
| 8195 if (this.withBackdrop && !this.hasAttribute('tabindex')) { | 8409 if (this.withBackdrop && !this.hasAttribute('tabindex')) { |
| 8196 this.setAttribute('tabindex', '-1'); | 8410 this.setAttribute('tabindex', '-1'); |
| 8197 this.__shouldRemoveTabIndex = true; | 8411 this.__shouldRemoveTabIndex = true; |
| 8198 } else if (this.__shouldRemoveTabIndex) { | 8412 } else if (this.__shouldRemoveTabIndex) { |
| 8199 this.removeAttribute('tabindex'); | 8413 this.removeAttribute('tabindex'); |
| 8200 this.__shouldRemoveTabIndex = false; | 8414 this.__shouldRemoveTabIndex = false; |
| 8201 } | 8415 } |
| 8202 if (this.opened) { | 8416 if (this.opened) { |
| 8203 this._manager.trackBackdrop(this); | 8417 this._manager.trackBackdrop(); |
| 8204 if (this.withBackdrop) { | 8418 if (this.withBackdrop) { |
| 8205 this.backdropElement.prepare(); | 8419 this.backdropElement.prepare(); |
| 8206 // Give time to be added to document. | 8420 // Give time to be added to document. |
| 8207 this.async(function(){ | 8421 this.async(function(){ |
| 8208 this.backdropElement.open(); | 8422 this.backdropElement.open(); |
| 8209 }, 1); | 8423 }, 1); |
| 8210 } else { | 8424 } else { |
| 8211 this.backdropElement.close(); | 8425 this.backdropElement.close(); |
| 8212 } | 8426 } |
| 8213 } | 8427 } |
| 8214 }, | 8428 }, |
| 8215 | 8429 |
| 8216 _toggleListener: function(enable, node, event, boundListener, capture) { | 8430 /** |
| 8217 if (enable) { | 8431 * tasks which must occur before opening; e.g. making the element visible. |
| 8218 // enable document-wide tap recognizer | 8432 * @protected |
| 8219 if (event === 'tap') { | 8433 */ |
| 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() { | 8434 _prepareRenderOpened: function() { |
| 8239 | 8435 |
| 8240 this._manager.addOverlay(this); | |
| 8241 | |
| 8242 // Needed to calculate the size of the overlay so that transitions on its
size | 8436 // Needed to calculate the size of the overlay so that transitions on its
size |
| 8243 // will have the correct starting points. | 8437 // will have the correct starting points. |
| 8244 this._preparePositioning(); | 8438 this._preparePositioning(); |
| 8245 this.fit(); | 8439 this.refit(); |
| 8246 this._finishPositioning(); | 8440 this._finishPositioning(); |
| 8247 | 8441 |
| 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, | 8442 // 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. | 8443 // so we blur it. Later, _applyFocus will set the focus if necessary. |
| 8254 if (this.noAutoFocus && document.activeElement === this._focusNode) { | 8444 if (this.noAutoFocus && document.activeElement === this._focusNode) { |
| 8255 this._focusNode.blur(); | 8445 this._focusNode.blur(); |
| 8256 } | 8446 } |
| 8257 }, | 8447 }, |
| 8258 | 8448 |
| 8259 // tasks which cause the overlay to actually open; typically play an | 8449 /** |
| 8260 // animation | 8450 * Tasks which cause the overlay to actually open; typically play an animati
on. |
| 8451 * @protected |
| 8452 */ |
| 8261 _renderOpened: function() { | 8453 _renderOpened: function() { |
| 8262 if (this.withBackdrop) { | 8454 if (this.withBackdrop) { |
| 8263 this.backdropElement.open(); | 8455 this.backdropElement.open(); |
| 8264 } | 8456 } |
| 8265 this._finishRenderOpened(); | 8457 this._finishRenderOpened(); |
| 8266 }, | 8458 }, |
| 8267 | 8459 |
| 8460 /** |
| 8461 * Tasks which cause the overlay to actually close; typically play an animat
ion. |
| 8462 * @protected |
| 8463 */ |
| 8268 _renderClosed: function() { | 8464 _renderClosed: function() { |
| 8269 if (this.withBackdrop) { | 8465 if (this.withBackdrop) { |
| 8270 this.backdropElement.close(); | 8466 this.backdropElement.close(); |
| 8271 } | 8467 } |
| 8272 this._finishRenderClosed(); | 8468 this._finishRenderClosed(); |
| 8273 }, | 8469 }, |
| 8274 | 8470 |
| 8471 /** |
| 8472 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. |
| 8473 * @protected |
| 8474 */ |
| 8275 _finishRenderOpened: function() { | 8475 _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] | 8476 // Focus the child node with [autofocus] |
| 8280 this._applyFocus(); | 8477 this._applyFocus(); |
| 8281 | 8478 |
| 8479 this.notifyResize(); |
| 8480 this.__isAnimating = false; |
| 8282 this.fire('iron-overlay-opened'); | 8481 this.fire('iron-overlay-opened'); |
| 8283 }, | 8482 }, |
| 8284 | 8483 |
| 8484 /** |
| 8485 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. |
| 8486 * @protected |
| 8487 */ |
| 8285 _finishRenderClosed: function() { | 8488 _finishRenderClosed: function() { |
| 8286 // Hide the overlay and remove the backdrop. | 8489 // Hide the overlay and remove the backdrop. |
| 8287 this.resetFit(); | |
| 8288 this.style.display = 'none'; | 8490 this.style.display = 'none'; |
| 8289 this._manager.removeOverlay(this); | 8491 // Reset z-index only at the end of the animation. |
| 8492 this.style.zIndex = ''; |
| 8290 | 8493 |
| 8291 this._applyFocus(); | 8494 this._applyFocus(); |
| 8495 |
| 8292 this.notifyResize(); | 8496 this.notifyResize(); |
| 8293 | 8497 this.__isAnimating = false; |
| 8294 this.fire('iron-overlay-closed', this.closingReason); | 8498 this.fire('iron-overlay-closed', this.closingReason); |
| 8295 }, | 8499 }, |
| 8296 | 8500 |
| 8297 _preparePositioning: function() { | 8501 _preparePositioning: function() { |
| 8298 this.style.transition = this.style.webkitTransition = 'none'; | 8502 this.style.transition = this.style.webkitTransition = 'none'; |
| 8299 this.style.transform = this.style.webkitTransform = 'none'; | 8503 this.style.transform = this.style.webkitTransform = 'none'; |
| 8300 this.style.display = ''; | 8504 this.style.display = ''; |
| 8301 }, | 8505 }, |
| 8302 | 8506 |
| 8303 _finishPositioning: function() { | 8507 _finishPositioning: function() { |
| 8508 // First, make it invisible & reactivate animations. |
| 8304 this.style.display = 'none'; | 8509 this.style.display = 'none'; |
| 8510 // Force reflow before re-enabling animations so that they don't start. |
| 8511 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 8512 this.scrollTop = this.scrollTop; |
| 8513 this.style.transition = this.style.webkitTransition = ''; |
| 8305 this.style.transform = this.style.webkitTransform = ''; | 8514 this.style.transform = this.style.webkitTransform = ''; |
| 8306 // Force layout layout to avoid application of transform. | 8515 // Now that animations are enabled, make it visible again |
| 8307 // Set offsetWidth to itself so that compilers won't remove it. | 8516 this.style.display = ''; |
| 8308 this.offsetWidth = this.offsetWidth; | 8517 // Force reflow, so that following animations are properly started. |
| 8309 this.style.transition = this.style.webkitTransition = ''; | 8518 // Set scrollTop to itself so that Closure Compiler doesn't remove this. |
| 8519 this.scrollTop = this.scrollTop; |
| 8310 }, | 8520 }, |
| 8311 | 8521 |
| 8522 /** |
| 8523 * Applies focus according to the opened state. |
| 8524 * @protected |
| 8525 */ |
| 8312 _applyFocus: function() { | 8526 _applyFocus: function() { |
| 8313 if (this.opened) { | 8527 if (this.opened) { |
| 8314 if (!this.noAutoFocus) { | 8528 if (!this.noAutoFocus) { |
| 8315 this._focusNode.focus(); | 8529 this._focusNode.focus(); |
| 8316 } | 8530 } |
| 8317 } else { | 8531 } else { |
| 8318 this._focusNode.blur(); | 8532 this._focusNode.blur(); |
| 8319 this._focusedChild = null; | 8533 this._focusedChild = null; |
| 8320 this._manager.focusOverlay(); | 8534 this._manager.focusOverlay(); |
| 8321 } | 8535 } |
| 8322 }, | 8536 }, |
| 8323 | 8537 |
| 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 /** | 8538 /** |
| 8539 * Cancels (closes) the overlay. Call when click happens outside the overlay
. |
| 8540 * @param {!Event} event |
| 8354 * @protected | 8541 * @protected |
| 8355 * Will call notifyResize if overlay is opened. | |
| 8356 * Can be overridden in order to avoid multiple observers on the same node. | |
| 8357 */ | 8542 */ |
| 8358 _onNodesChange: function() { | 8543 _onCaptureClick: function(event) { |
| 8359 if (this.opened) { | 8544 if (!this.noCancelOnOutsideClick) { |
| 8360 this.notifyResize(); | 8545 this.cancel(event); |
| 8361 } | 8546 } |
| 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 }, | 8547 }, |
| 8367 | 8548 |
| 8368 __onEsc: function(event) { | 8549 /** |
| 8369 // Not opened or not on top, so return. | 8550 * Keeps track of the focused child. If withBackdrop, traps focus within ove
rlay. |
| 8370 if (this._manager.currentOverlay() !== this) { | 8551 * @param {!Event} event |
| 8552 * @protected |
| 8553 */ |
| 8554 _onCaptureFocus: function (event) { |
| 8555 if (!this.withBackdrop) { |
| 8371 return; | 8556 return; |
| 8372 } | 8557 } |
| 8558 var path = Polymer.dom(event).path; |
| 8559 if (path.indexOf(this) === -1) { |
| 8560 event.stopPropagation(); |
| 8561 this._applyFocus(); |
| 8562 } else { |
| 8563 this._focusedChild = path[0]; |
| 8564 } |
| 8565 }, |
| 8566 |
| 8567 /** |
| 8568 * Handles the ESC key event and cancels (closes) the overlay. |
| 8569 * @param {!Event} event |
| 8570 * @protected |
| 8571 */ |
| 8572 _onCaptureEsc: function(event) { |
| 8373 if (!this.noCancelOnEscKey) { | 8573 if (!this.noCancelOnEscKey) { |
| 8374 this.cancel(event); | 8574 this.cancel(event); |
| 8375 } | 8575 } |
| 8376 }, | 8576 }, |
| 8377 | 8577 |
| 8378 __onTab: function(event) { | 8578 /** |
| 8379 // Not opened or not on top, so return. | 8579 * Handles TAB key events to track focus changes. |
| 8380 if (this._manager.currentOverlay() !== this) { | 8580 * Will wrap focus for overlays withBackdrop. |
| 8381 return; | 8581 * @param {!Event} event |
| 8382 } | 8582 * @protected |
| 8583 */ |
| 8584 _onCaptureTab: function(event) { |
| 8383 // TAB wraps from last to first focusable. | 8585 // TAB wraps from last to first focusable. |
| 8384 // Shift + TAB wraps from first to last focusable. | 8586 // Shift + TAB wraps from first to last focusable. |
| 8385 var shift = event.detail.keyboardEvent.shiftKey; | 8587 var shift = event.shiftKey; |
| 8386 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; | 8588 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 8387 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; | 8589 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 8388 if (this.withBackdrop && this._focusedChild === nodeToCheck) { | 8590 if (this.withBackdrop && this._focusedChild === nodeToCheck) { |
| 8389 // We set here the _focusedChild so that _onCaptureFocus will handle the | 8591 // We set here the _focusedChild so that _onCaptureFocus will handle the |
| 8390 // wrapping of the focus (the next event after tab is focus). | 8592 // wrapping of the focus (the next event after tab is focus). |
| 8391 this._focusedChild = nodeToSet; | 8593 this._focusedChild = nodeToSet; |
| 8392 } | 8594 } |
| 8595 }, |
| 8596 |
| 8597 /** |
| 8598 * Refits if the overlay is opened and not animating. |
| 8599 * @protected |
| 8600 */ |
| 8601 _onIronResize: function() { |
| 8602 if (this.__onIronResizeAsync) { |
| 8603 cancelAnimationFrame(this.__onIronResizeAsync); |
| 8604 this.__onIronResizeAsync = null; |
| 8605 } |
| 8606 if (this.opened && !this.__isAnimating) { |
| 8607 this.__onIronResizeAsync = requestAnimationFrame(function() { |
| 8608 this.__onIronResizeAsync = null; |
| 8609 this.refit(); |
| 8610 }.bind(this)); |
| 8611 } |
| 8612 }, |
| 8613 |
| 8614 /** |
| 8615 * Will call notifyResize if overlay is opened. |
| 8616 * Can be overridden in order to avoid multiple observers on the same node. |
| 8617 * @protected |
| 8618 */ |
| 8619 _onNodesChange: function() { |
| 8620 if (this.opened && !this.__isAnimating) { |
| 8621 this.notifyResize(); |
| 8622 } |
| 8623 // Store it so we don't query too much. |
| 8624 var focusableNodes = this._focusableNodes; |
| 8625 this.__firstFocusableNode = focusableNodes[0]; |
| 8626 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 8393 } | 8627 } |
| 8394 }; | 8628 }; |
| 8395 | 8629 |
| 8396 /** @polymerBehavior */ | 8630 /** @polymerBehavior */ |
| 8397 Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBe
havior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; | 8631 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; |
| 8398 | 8632 |
| 8399 /** | 8633 /** |
| 8400 * Fired after the `iron-overlay` opens. | 8634 * Fired after the `iron-overlay` opens. |
| 8401 * @event iron-overlay-opened | 8635 * @event iron-overlay-opened |
| 8402 */ | 8636 */ |
| 8403 | 8637 |
| 8404 /** | 8638 /** |
| 8405 * Fired when the `iron-overlay` is canceled, but before it is closed. | 8639 * Fired when the `iron-overlay` is canceled, but before it is closed. |
| 8406 * Cancel the event to prevent the `iron-overlay` from closing. | 8640 * Cancel the event to prevent the `iron-overlay` from closing. |
| 8407 * @event iron-overlay-canceled | 8641 * @event iron-overlay-canceled |
| 8408 * @param {Event} event The closing of the `iron-overlay` can be prevented | 8642 * @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 | 8643 * 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`). | 8644 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). |
| 8411 */ | 8645 */ |
| 8412 | 8646 |
| 8413 /** | 8647 /** |
| 8414 * Fired after the `iron-overlay` closes. | 8648 * Fired after the `iron-overlay` closes. |
| 8415 * @event iron-overlay-closed | 8649 * @event iron-overlay-closed |
| 8416 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). | 8650 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). |
| 8417 */ | 8651 */ |
| 8652 |
| 8653 })(); |
| 8418 /** | 8654 /** |
| 8419 * Use `Polymer.NeonAnimationBehavior` to implement an animation. | 8655 * Use `Polymer.NeonAnimationBehavior` to implement an animation. |
| 8420 * @polymerBehavior | 8656 * @polymerBehavior |
| 8421 */ | 8657 */ |
| 8422 Polymer.NeonAnimationBehavior = { | 8658 Polymer.NeonAnimationBehavior = { |
| 8423 | 8659 |
| 8424 properties: { | 8660 properties: { |
| 8425 | 8661 |
| 8426 /** | 8662 /** |
| 8427 * Defines the animation timing. | 8663 * Defines the animation timing. |
| (...skipping 483 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8911 if (this._composedTreeContains(distributedNodes[nodeIndex], child))
{ | 9147 if (this._composedTreeContains(distributedNodes[nodeIndex], child))
{ |
| 8912 return true; | 9148 return true; |
| 8913 } | 9149 } |
| 8914 } | 9150 } |
| 8915 } | 9151 } |
| 8916 | 9152 |
| 8917 return false; | 9153 return false; |
| 8918 }, | 9154 }, |
| 8919 | 9155 |
| 8920 _scrollInteractionHandler: function(event) { | 9156 _scrollInteractionHandler: function(event) { |
| 9157 var scrolledElement = |
| 9158 /** @type {HTMLElement} */(Polymer.dom(event).rootTarget); |
| 8921 if (Polymer | 9159 if (Polymer |
| 8922 .IronDropdownScrollManager | 9160 .IronDropdownScrollManager |
| 8923 .elementIsScrollLocked(Polymer.dom(event).rootTarget)) { | 9161 .elementIsScrollLocked(scrolledElement)) { |
| 8924 if (event.type === 'keydown' && | 9162 if (event.type === 'keydown' && |
| 8925 !Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) { | 9163 !Polymer.IronDropdownScrollManager._isScrollingKeypress(event)) { |
| 8926 return; | 9164 return; |
| 8927 } | 9165 } |
| 8928 | 9166 |
| 8929 event.preventDefault(); | 9167 event.preventDefault(); |
| 8930 } | 9168 } |
| 8931 }, | 9169 }, |
| 8932 | 9170 |
| 8933 _lockScrollInteractions: function() { | 9171 _lockScrollInteractions: function() { |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9043 type: Number, | 9281 type: Number, |
| 9044 value: 0, | 9282 value: 0, |
| 9045 notify: true | 9283 notify: true |
| 9046 }, | 9284 }, |
| 9047 | 9285 |
| 9048 /** | 9286 /** |
| 9049 * The element that should be used to position the dropdown when | 9287 * The element that should be used to position the dropdown when |
| 9050 * it is opened. | 9288 * it is opened. |
| 9051 */ | 9289 */ |
| 9052 positionTarget: { | 9290 positionTarget: { |
| 9053 type: Object, | 9291 type: Object |
| 9054 observer: '_positionTargetChanged' | |
| 9055 }, | 9292 }, |
| 9056 | 9293 |
| 9057 /** | 9294 /** |
| 9058 * An animation config. If provided, this will be used to animate the | 9295 * An animation config. If provided, this will be used to animate the |
| 9059 * opening of the dropdown. | 9296 * opening of the dropdown. |
| 9060 */ | 9297 */ |
| 9061 openAnimationConfig: { | 9298 openAnimationConfig: { |
| 9062 type: Object | 9299 type: Object |
| 9063 }, | 9300 }, |
| 9064 | 9301 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 9089 | 9326 |
| 9090 /** | 9327 /** |
| 9091 * By default, the dropdown will constrain scrolling on the page | 9328 * By default, the dropdown will constrain scrolling on the page |
| 9092 * to itself when opened. | 9329 * to itself when opened. |
| 9093 * Set to true in order to prevent scroll from being constrained | 9330 * Set to true in order to prevent scroll from being constrained |
| 9094 * to the dropdown when it opens. | 9331 * to the dropdown when it opens. |
| 9095 */ | 9332 */ |
| 9096 allowOutsideScroll: { | 9333 allowOutsideScroll: { |
| 9097 type: Boolean, | 9334 type: Boolean, |
| 9098 value: false | 9335 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 } | 9336 } |
| 9109 }, | 9337 }, |
| 9110 | 9338 |
| 9111 listeners: { | 9339 listeners: { |
| 9112 'neon-animation-finish': '_onNeonAnimationFinish' | 9340 'neon-animation-finish': '_onNeonAnimationFinish' |
| 9113 }, | 9341 }, |
| 9114 | 9342 |
| 9115 observers: [ | 9343 observers: [ |
| 9116 '_updateOverlayPosition(verticalAlign, horizontalAlign, verticalOffset
, horizontalOffset)' | 9344 '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign
, verticalOffset, horizontalOffset)' |
| 9117 ], | 9345 ], |
| 9118 | 9346 |
| 9119 attached: function() { | 9347 attached: function() { |
| 9120 if (this.positionTarget === undefined) { | 9348 this.positionTarget = this.positionTarget || this._defaultPositionTarg
et; |
| 9121 this.positionTarget = this._defaultPositionTarget; | 9349 // Memoize this to avoid expensive calculations & relayouts. |
| 9122 } | 9350 this._isRTL = window.getComputedStyle(this).direction == 'rtl'; |
| 9123 }, | 9351 }, |
| 9124 | 9352 |
| 9125 /** | 9353 /** |
| 9126 * The element that is contained by the dropdown, if any. | 9354 * The element that is contained by the dropdown, if any. |
| 9127 */ | 9355 */ |
| 9128 get containedElement() { | 9356 get containedElement() { |
| 9129 return Polymer.dom(this.$.content).getDistributedNodes()[0]; | 9357 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| 9130 }, | 9358 }, |
| 9131 | 9359 |
| 9132 /** | 9360 /** |
| 9133 * The element that should be focused when the dropdown opens. | 9361 * The element that should be focused when the dropdown opens. |
| 9134 * @deprecated | 9362 * @deprecated |
| 9135 */ | 9363 */ |
| 9136 get _focusTarget() { | 9364 get _focusTarget() { |
| 9137 return this.focusTarget || this.containedElement; | 9365 return this.focusTarget || this.containedElement; |
| 9138 }, | 9366 }, |
| 9139 | 9367 |
| 9140 /** | 9368 /** |
| 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 | 9369 * The element that should be used to position the dropdown when |
| 9149 * it opens, if no position target is configured. | 9370 * it opens, if no position target is configured. |
| 9150 */ | 9371 */ |
| 9151 get _defaultPositionTarget() { | 9372 get _defaultPositionTarget() { |
| 9152 var parent = Polymer.dom(this).parentNode; | 9373 var parent = Polymer.dom(this).parentNode; |
| 9153 | 9374 |
| 9154 if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { | 9375 if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { |
| 9155 parent = parent.host; | 9376 parent = parent.host; |
| 9156 } | 9377 } |
| 9157 | 9378 |
| 9158 return parent; | 9379 return parent; |
| 9159 }, | 9380 }, |
| 9160 | 9381 |
| 9161 /** | 9382 /** |
| 9162 * The bounding rect of the position target. | 9383 * The horizontal align value, accounting for the RTL/LTR text direction
. |
| 9163 */ | 9384 */ |
| 9164 get _positionRect() { | 9385 get _localeHorizontalAlign() { |
| 9165 if (!this._positionRectMemo && this.positionTarget) { | 9386 // In RTL, "left" becomes "right". |
| 9166 this._positionRectMemo = this.positionTarget.getBoundingClientRect()
; | 9387 if (this._isRTL) { |
| 9388 return this.horizontalAlign === 'right' ? 'left' : 'right'; |
| 9389 } else { |
| 9390 return this.horizontalAlign; |
| 9167 } | 9391 } |
| 9168 | |
| 9169 return this._positionRectMemo; | |
| 9170 }, | 9392 }, |
| 9171 | 9393 |
| 9172 /** | 9394 /** |
| 9173 * The horizontal offset value used to position the dropdown. | 9395 * The horizontal offset value used to position the dropdown. |
| 9396 * @param {ClientRect} dropdownRect |
| 9397 * @param {ClientRect} positionRect |
| 9398 * @param {boolean=false} fromRight |
| 9399 * @return {number} pixels |
| 9400 * @private |
| 9174 */ | 9401 */ |
| 9175 get _horizontalAlignTargetValue() { | 9402 _horizontalAlignTargetValue: function(dropdownRect, positionRect, fromRi
ght) { |
| 9176 var target; | 9403 var target; |
| 9177 | 9404 if (fromRight) { |
| 9178 // In RTL, the direction flips, so what is "right" in LTR becomes "lef
t". | 9405 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 { | 9406 } else { |
| 9184 target = this._positionRect.left; | 9407 target = positionRect.left - dropdownRect.left; |
| 9185 } | 9408 } |
| 9186 | |
| 9187 target += this.horizontalOffset; | 9409 target += this.horizontalOffset; |
| 9188 | 9410 |
| 9189 return Math.max(target, 0); | 9411 return Math.max(target, 0); |
| 9190 }, | 9412 }, |
| 9191 | 9413 |
| 9192 /** | 9414 /** |
| 9193 * The vertical offset value used to position the dropdown. | 9415 * The vertical offset value used to position the dropdown. |
| 9416 * @param {ClientRect} dropdownRect |
| 9417 * @param {ClientRect} positionRect |
| 9418 * @param {boolean=false} fromBottom |
| 9419 * @return {number} pixels |
| 9420 * @private |
| 9194 */ | 9421 */ |
| 9195 get _verticalAlignTargetValue() { | 9422 _verticalAlignTargetValue: function(dropdownRect, positionRect, fromBott
om) { |
| 9196 var target; | 9423 var target; |
| 9197 | 9424 if (fromBottom) { |
| 9198 if (this.verticalAlign === 'bottom') { | 9425 target = document.documentElement.clientHeight - positionRect.bottom
- (this._fitHeight - dropdownRect.bottom); |
| 9199 target = document.documentElement.clientHeight - this._positionRect.
bottom; | |
| 9200 } else { | 9426 } else { |
| 9201 target = this._positionRect.top; | 9427 target = positionRect.top - dropdownRect.top; |
| 9202 } | 9428 } |
| 9203 | |
| 9204 target += this.verticalOffset; | 9429 target += this.verticalOffset; |
| 9205 | 9430 |
| 9206 return Math.max(target, 0); | 9431 return Math.max(target, 0); |
| 9207 }, | 9432 }, |
| 9208 | 9433 |
| 9209 /** | 9434 /** |
| 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. | 9435 * Called when the value of `opened` changes. |
| 9223 * | 9436 * |
| 9224 * @param {boolean} opened True if the dropdown is opened. | 9437 * @param {boolean} opened True if the dropdown is opened. |
| 9225 */ | 9438 */ |
| 9226 _openedChanged: function(opened) { | 9439 _openedChanged: function(opened) { |
| 9227 if (opened && this.disabled) { | 9440 if (opened && this.disabled) { |
| 9228 this.cancel(); | 9441 this.cancel(); |
| 9229 } else { | 9442 } else { |
| 9230 this.cancelAnimation(); | 9443 this.cancelAnimation(); |
| 9231 this._prepareDropdown(); | 9444 this.sizingTarget = this.containedElement || this.sizingTarget; |
| 9445 this._updateAnimationConfig(); |
| 9232 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); | 9446 Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments
); |
| 9233 } | 9447 } |
| 9234 }, | 9448 }, |
| 9235 | 9449 |
| 9236 /** | 9450 /** |
| 9237 * Overridden from `IronOverlayBehavior`. | 9451 * Overridden from `IronOverlayBehavior`. |
| 9238 */ | 9452 */ |
| 9239 _renderOpened: function() { | 9453 _renderOpened: function() { |
| 9240 if (!this.allowOutsideScroll) { | 9454 if (!this.allowOutsideScroll) { |
| 9241 Polymer.IronDropdownScrollManager.pushScrollLock(this); | 9455 Polymer.IronDropdownScrollManager.pushScrollLock(this); |
| 9242 } | 9456 } |
| 9243 | 9457 |
| 9244 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { | 9458 if (!this.noAnimations && this.animationConfig && this.animationConfig
.open) { |
| 9459 if (this.withBackdrop) { |
| 9460 this.backdropElement.open(); |
| 9461 } |
| 9245 this.$.contentWrapper.classList.add('animating'); | 9462 this.$.contentWrapper.classList.add('animating'); |
| 9246 this.playAnimation('open'); | 9463 this.playAnimation('open'); |
| 9247 } else { | 9464 } else { |
| 9248 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; | 9465 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments)
; |
| 9249 } | 9466 } |
| 9250 }, | 9467 }, |
| 9251 | 9468 |
| 9252 /** | 9469 /** |
| 9253 * Overridden from `IronOverlayBehavior`. | 9470 * Overridden from `IronOverlayBehavior`. |
| 9254 */ | 9471 */ |
| 9255 _renderClosed: function() { | 9472 _renderClosed: function() { |
| 9256 Polymer.IronDropdownScrollManager.removeScrollLock(this); | 9473 Polymer.IronDropdownScrollManager.removeScrollLock(this); |
| 9257 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { | 9474 if (!this.noAnimations && this.animationConfig && this.animationConfig
.close) { |
| 9475 if (this.withBackdrop) { |
| 9476 this.backdropElement.close(); |
| 9477 } |
| 9258 this.$.contentWrapper.classList.add('animating'); | 9478 this.$.contentWrapper.classList.add('animating'); |
| 9259 this.playAnimation('close'); | 9479 this.playAnimation('close'); |
| 9260 } else { | 9480 } else { |
| 9261 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; | 9481 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments)
; |
| 9262 } | 9482 } |
| 9263 }, | 9483 }, |
| 9264 | 9484 |
| 9265 /** | 9485 /** |
| 9266 * Called when animation finishes on the dropdown (when opening or | 9486 * Called when animation finishes on the dropdown (when opening or |
| 9267 * closing). Responsible for "completing" the process of opening or | 9487 * closing). Responsible for "completing" the process of opening or |
| 9268 * closing the dropdown by positioning it or setting its display to | 9488 * closing the dropdown by positioning it or setting its display to |
| 9269 * none. | 9489 * none. |
| 9270 */ | 9490 */ |
| 9271 _onNeonAnimationFinish: function() { | 9491 _onNeonAnimationFinish: function() { |
| 9272 this.$.contentWrapper.classList.remove('animating'); | 9492 this.$.contentWrapper.classList.remove('animating'); |
| 9273 if (this.opened) { | 9493 if (this.opened) { |
| 9274 Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this); | 9494 Polymer.IronOverlayBehaviorImpl._finishRenderOpened.apply(this); |
| 9275 } else { | 9495 } else { |
| 9276 Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this); | 9496 Polymer.IronOverlayBehaviorImpl._finishRenderClosed.apply(this); |
| 9277 } | 9497 } |
| 9278 }, | 9498 }, |
| 9279 | 9499 |
| 9280 /** | 9500 /** |
| 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 | 9501 * Constructs the final animation config from different properties used |
| 9314 * to configure specific parts of the opening and closing animations. | 9502 * to configure specific parts of the opening and closing animations. |
| 9315 */ | 9503 */ |
| 9316 _updateAnimationConfig: function() { | 9504 _updateAnimationConfig: function() { |
| 9317 var animationConfig = {}; | 9505 var animationConfig = {}; |
| 9318 var animations = []; | 9506 var animations = []; |
| 9319 | 9507 |
| 9320 if (this.openAnimationConfig) { | 9508 if (this.openAnimationConfig) { |
| 9321 // NOTE(cdata): When making `display:none` elements visible in Safar
i, | 9509 // NOTE(cdata): When making `display:none` elements visible in Safar
i, |
| 9322 // the element will paint once in a fully visible state, causing the | 9510 // the element will paint once in a fully visible state, causing the |
| (...skipping 11 matching lines...) Expand all Loading... |
| 9334 } | 9522 } |
| 9335 | 9523 |
| 9336 animations.forEach(function(animation) { | 9524 animations.forEach(function(animation) { |
| 9337 animation.node = this.containedElement; | 9525 animation.node = this.containedElement; |
| 9338 }, this); | 9526 }, this); |
| 9339 | 9527 |
| 9340 this.animationConfig = animationConfig; | 9528 this.animationConfig = animationConfig; |
| 9341 }, | 9529 }, |
| 9342 | 9530 |
| 9343 /** | 9531 /** |
| 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 | 9532 * Updates the overlay position based on configured horizontal |
| 9355 * and vertical alignment, and re-memoizes these values for the sake | 9533 * and vertical alignment. |
| 9356 * of behavior in `IronFitBehavior`. | |
| 9357 */ | 9534 */ |
| 9358 _updateOverlayPosition: function() { | 9535 _updateOverlayPosition: function() { |
| 9359 this._positionRectMemo = null; | 9536 if (this.isAttached) { |
| 9360 | 9537 // This triggers iron-resize, and iron-overlay-behavior will call re
fit if needed. |
| 9361 if (!this.positionTarget) { | 9538 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 } | 9539 } |
| 9381 }, | 9540 }, |
| 9382 | 9541 |
| 9383 /** | 9542 /** |
| 9543 * Useful to call this after the element, the window, or the `fitInfo` |
| 9544 * element has been resized. Will maintain the scroll position. |
| 9545 */ |
| 9546 refit: function () { |
| 9547 if (!this.opened) { |
| 9548 return |
| 9549 } |
| 9550 var containedElement = this.containedElement; |
| 9551 var scrollTop; |
| 9552 var scrollLeft; |
| 9553 |
| 9554 if (containedElement) { |
| 9555 scrollTop = containedElement.scrollTop; |
| 9556 scrollLeft = containedElement.scrollLeft; |
| 9557 } |
| 9558 Polymer.IronFitBehavior.refit.apply(this, arguments); |
| 9559 |
| 9560 if (containedElement) { |
| 9561 containedElement.scrollTop = scrollTop; |
| 9562 containedElement.scrollLeft = scrollLeft; |
| 9563 } |
| 9564 }, |
| 9565 |
| 9566 /** |
| 9567 * Resets the target element's position and size constraints, and clear |
| 9568 * the memoized data. |
| 9569 */ |
| 9570 resetFit: function() { |
| 9571 Polymer.IronFitBehavior.resetFit.apply(this, arguments); |
| 9572 |
| 9573 var hAlign = this._localeHorizontalAlign; |
| 9574 var vAlign = this.verticalAlign; |
| 9575 // Set to 0, 0 in order to discover any offset caused by parent stacki
ng contexts. |
| 9576 this.style[hAlign] = this.style[vAlign] = '0px'; |
| 9577 |
| 9578 var dropdownRect = this.getBoundingClientRect(); |
| 9579 var positionRect = this.positionTarget.getBoundingClientRect(); |
| 9580 var horizontalValue = this._horizontalAlignTargetValue(dropdownRect, p
ositionRect, hAlign === 'right'); |
| 9581 var verticalValue = this._verticalAlignTargetValue(dropdownRect, posit
ionRect, vAlign === 'bottom'); |
| 9582 |
| 9583 this.style[hAlign] = horizontalValue + 'px'; |
| 9584 this.style[vAlign] = verticalValue + 'px'; |
| 9585 }, |
| 9586 |
| 9587 /** |
| 9588 * Overridden from `IronFitBehavior`. |
| 9589 * Ensure positionedBy has correct values for horizontally & vertically. |
| 9590 */ |
| 9591 _discoverInfo: function() { |
| 9592 Polymer.IronFitBehavior._discoverInfo.apply(this, arguments); |
| 9593 // Note(valdrin): in Firefox, an element with style `position: fixed;
bottom: 90vh; height: 20vh` |
| 9594 // would have `getComputedStyle(element).top < 0` (instead of being `a
uto`) http://jsbin.com/cofired/3/edit?html,output |
| 9595 // This would cause IronFitBehavior's `constrain` to wrongly calculate
sizes |
| 9596 // (it would use `top` instead of `bottom`), so we ensure we give the
correct values. |
| 9597 this._fitInfo.positionedBy.horizontally = this._localeHorizontalAlign; |
| 9598 this._fitInfo.positionedBy.vertically = this.verticalAlign; |
| 9599 }, |
| 9600 |
| 9601 /** |
| 9384 * Apply focus to focusTarget or containedElement | 9602 * Apply focus to focusTarget or containedElement |
| 9385 */ | 9603 */ |
| 9386 _applyFocus: function () { | 9604 _applyFocus: function () { |
| 9387 var focusTarget = this.focusTarget || this.containedElement; | 9605 var focusTarget = this.focusTarget || this.containedElement; |
| 9388 if (focusTarget && this.opened && !this.noAutoFocus) { | 9606 if (focusTarget && this.opened && !this.noAutoFocus) { |
| 9389 focusTarget.focus(); | 9607 focusTarget.focus(); |
| 9390 } else { | 9608 } else { |
| 9391 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); | 9609 Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); |
| 9392 } | 9610 } |
| 9393 } | 9611 } |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9515 transform: 'translateY(0)' | 9733 transform: 'translateY(0)' |
| 9516 }, { | 9734 }, { |
| 9517 height: height / 2 + 'px', | 9735 height: height / 2 + 'px', |
| 9518 transform: 'translateY(-20px)' | 9736 transform: 'translateY(-20px)' |
| 9519 }], this.timingFromConfig(config)); | 9737 }], this.timingFromConfig(config)); |
| 9520 | 9738 |
| 9521 return this._effect; | 9739 return this._effect; |
| 9522 } | 9740 } |
| 9523 }); | 9741 }); |
| 9524 (function() { | 9742 (function() { |
| 9525 'use strict'; | 9743 'use strict'; |
| 9526 | 9744 |
| 9527 var PaperMenuButton = Polymer({ | 9745 var PaperMenuButton = Polymer({ |
| 9528 is: 'paper-menu-button', | 9746 is: 'paper-menu-button', |
| 9529 | 9747 |
| 9530 /** | 9748 /** |
| 9531 * Fired when the dropdown opens. | 9749 * Fired when the dropdown opens. |
| 9532 * | 9750 * |
| 9533 * @event paper-dropdown-open | 9751 * @event paper-dropdown-open |
| 9534 */ | 9752 */ |
| 9535 | 9753 |
| 9536 /** | 9754 /** |
| 9537 * Fired when the dropdown closes. | 9755 * Fired when the dropdown closes. |
| 9538 * | 9756 * |
| 9539 * @event paper-dropdown-close | 9757 * @event paper-dropdown-close |
| 9540 */ | 9758 */ |
| 9541 | 9759 |
| 9542 behaviors: [ | 9760 behaviors: [ |
| 9543 Polymer.IronA11yKeysBehavior, | 9761 Polymer.IronA11yKeysBehavior, |
| 9544 Polymer.IronControlState | 9762 Polymer.IronControlState |
| 9545 ], | 9763 ], |
| 9546 | 9764 |
| 9547 properties: { | 9765 properties: { |
| 9548 | 9766 /** |
| 9549 /** | 9767 * True if the content is currently displayed. |
| 9550 * True if the content is currently displayed. | 9768 */ |
| 9551 */ | 9769 opened: { |
| 9552 opened: { | 9770 type: Boolean, |
| 9553 type: Boolean, | 9771 value: false, |
| 9554 value: false, | 9772 notify: true, |
| 9555 notify: true, | 9773 observer: '_openedChanged' |
| 9556 observer: '_openedChanged' | 9774 }, |
| 9557 }, | 9775 |
| 9558 | 9776 /** |
| 9559 /** | 9777 * The orientation against which to align the menu dropdown |
| 9560 * The orientation against which to align the menu dropdown | 9778 * horizontally relative to the dropdown trigger. |
| 9561 * horizontally relative to the dropdown trigger. | 9779 */ |
| 9562 */ | 9780 horizontalAlign: { |
| 9563 horizontalAlign: { | 9781 type: String, |
| 9564 type: String, | 9782 value: 'left', |
| 9565 value: 'left', | 9783 reflectToAttribute: true |
| 9566 reflectToAttribute: true | 9784 }, |
| 9567 }, | 9785 |
| 9568 | 9786 /** |
| 9569 /** | 9787 * The orientation against which to align the menu dropdown |
| 9570 * The orientation against which to align the menu dropdown | 9788 * vertically relative to the dropdown trigger. |
| 9571 * vertically relative to the dropdown trigger. | 9789 */ |
| 9572 */ | 9790 verticalAlign: { |
| 9573 verticalAlign: { | 9791 type: String, |
| 9574 type: String, | 9792 value: 'top', |
| 9575 value: 'top', | 9793 reflectToAttribute: true |
| 9576 reflectToAttribute: true | 9794 }, |
| 9577 }, | 9795 |
| 9578 | 9796 /** |
| 9579 /** | 9797 * 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 | 9798 * given `horizontalAlign`. Use a negative value to offset to the |
| 9581 * given `horizontalAlign`. Use a negative value to offset to the | 9799 * left, or a positive value to offset to the right. |
| 9582 * left, or a positive value to offset to the right. | 9800 */ |
| 9583 */ | 9801 horizontalOffset: { |
| 9584 horizontalOffset: { | 9802 type: Number, |
| 9585 type: Number, | 9803 value: 0, |
| 9586 value: 0, | 9804 notify: true |
| 9587 notify: true | 9805 }, |
| 9588 }, | 9806 |
| 9589 | 9807 /** |
| 9590 /** | 9808 * 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 | 9809 * given `verticalAlign`. Use a negative value to offset towards the |
| 9592 * given `verticalAlign`. Use a negative value to offset towards the | 9810 * top, or a positive value to offset towards the bottom. |
| 9593 * top, or a positive value to offset towards the bottom. | 9811 */ |
| 9594 */ | 9812 verticalOffset: { |
| 9595 verticalOffset: { | 9813 type: Number, |
| 9596 type: Number, | 9814 value: 0, |
| 9597 value: 0, | 9815 notify: true |
| 9598 notify: true | 9816 }, |
| 9599 }, | 9817 |
| 9600 | 9818 /** |
| 9601 /** | 9819 * Set to true to disable animations when opening and closing the |
| 9602 * Set to true to disable animations when opening and closing the | 9820 * dropdown. |
| 9821 */ |
| 9822 noAnimations: { |
| 9823 type: Boolean, |
| 9824 value: false |
| 9825 }, |
| 9826 |
| 9827 /** |
| 9828 * Set to true to disable automatically closing the dropdown after |
| 9829 * a selection has been made. |
| 9830 */ |
| 9831 ignoreSelect: { |
| 9832 type: Boolean, |
| 9833 value: false |
| 9834 }, |
| 9835 |
| 9836 /** |
| 9837 * An animation config. If provided, this will be used to animate the |
| 9838 * opening of the dropdown. |
| 9839 */ |
| 9840 openAnimationConfig: { |
| 9841 type: Object, |
| 9842 value: function() { |
| 9843 return [{ |
| 9844 name: 'fade-in-animation', |
| 9845 timing: { |
| 9846 delay: 100, |
| 9847 duration: 200 |
| 9848 } |
| 9849 }, { |
| 9850 name: 'paper-menu-grow-width-animation', |
| 9851 timing: { |
| 9852 delay: 100, |
| 9853 duration: 150, |
| 9854 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9855 } |
| 9856 }, { |
| 9857 name: 'paper-menu-grow-height-animation', |
| 9858 timing: { |
| 9859 delay: 100, |
| 9860 duration: 275, |
| 9861 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9862 } |
| 9863 }]; |
| 9864 } |
| 9865 }, |
| 9866 |
| 9867 /** |
| 9868 * An animation config. If provided, this will be used to animate the |
| 9869 * closing of the dropdown. |
| 9870 */ |
| 9871 closeAnimationConfig: { |
| 9872 type: Object, |
| 9873 value: function() { |
| 9874 return [{ |
| 9875 name: 'fade-out-animation', |
| 9876 timing: { |
| 9877 duration: 150 |
| 9878 } |
| 9879 }, { |
| 9880 name: 'paper-menu-shrink-width-animation', |
| 9881 timing: { |
| 9882 delay: 100, |
| 9883 duration: 50, |
| 9884 easing: PaperMenuButton.ANIMATION_CUBIC_BEZIER |
| 9885 } |
| 9886 }, { |
| 9887 name: 'paper-menu-shrink-height-animation', |
| 9888 timing: { |
| 9889 duration: 200, |
| 9890 easing: 'ease-in' |
| 9891 } |
| 9892 }]; |
| 9893 } |
| 9894 }, |
| 9895 |
| 9896 /** |
| 9897 * This is the element intended to be bound as the focus target |
| 9898 * for the `iron-dropdown` contained by `paper-menu-button`. |
| 9899 */ |
| 9900 _dropdownContent: { |
| 9901 type: Object |
| 9902 } |
| 9903 }, |
| 9904 |
| 9905 hostAttributes: { |
| 9906 role: 'group', |
| 9907 'aria-haspopup': 'true' |
| 9908 }, |
| 9909 |
| 9910 listeners: { |
| 9911 'iron-select': '_onIronSelect' |
| 9912 }, |
| 9913 |
| 9914 /** |
| 9915 * The content element that is contained by the menu button, if any. |
| 9916 */ |
| 9917 get contentElement() { |
| 9918 return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| 9919 }, |
| 9920 |
| 9921 /** |
| 9922 * Toggles the drowpdown content between opened and closed. |
| 9923 */ |
| 9924 toggle: function() { |
| 9925 if (this.opened) { |
| 9926 this.close(); |
| 9927 } else { |
| 9928 this.open(); |
| 9929 } |
| 9930 }, |
| 9931 |
| 9932 /** |
| 9933 * Make the dropdown content appear as an overlay positioned relative |
| 9934 * to the dropdown trigger. |
| 9935 */ |
| 9936 open: function() { |
| 9937 if (this.disabled) { |
| 9938 return; |
| 9939 } |
| 9940 |
| 9941 this.$.dropdown.open(); |
| 9942 }, |
| 9943 |
| 9944 /** |
| 9945 * Hide the dropdown content. |
| 9946 */ |
| 9947 close: function() { |
| 9948 this.$.dropdown.close(); |
| 9949 }, |
| 9950 |
| 9951 /** |
| 9952 * When an `iron-select` event is received, the dropdown should |
| 9953 * automatically close on the assumption that a value has been chosen. |
| 9954 * |
| 9955 * @param {CustomEvent} event A CustomEvent instance with type |
| 9956 * set to `"iron-select"`. |
| 9957 */ |
| 9958 _onIronSelect: function(event) { |
| 9959 if (!this.ignoreSelect) { |
| 9960 this.close(); |
| 9961 } |
| 9962 }, |
| 9963 |
| 9964 /** |
| 9965 * When the dropdown opens, the `paper-menu-button` fires `paper-open`. |
| 9966 * When the dropdown closes, the `paper-menu-button` fires `paper-close`
. |
| 9967 * |
| 9968 * @param {boolean} opened True if the dropdown is opened, otherwise fal
se. |
| 9969 * @param {boolean} oldOpened The previous value of `opened`. |
| 9970 */ |
| 9971 _openedChanged: function(opened, oldOpened) { |
| 9972 if (opened) { |
| 9973 // TODO(cdata): Update this when we can measure changes in distribut
ed |
| 9974 // children in an idiomatic way. |
| 9975 // We poke this property in case the element has changed. This will |
| 9976 // cause the focus target for the `iron-dropdown` to be updated as |
| 9977 // necessary: |
| 9978 this._dropdownContent = this.contentElement; |
| 9979 this.fire('paper-dropdown-open'); |
| 9980 } else if (oldOpened != null) { |
| 9981 this.fire('paper-dropdown-close'); |
| 9982 } |
| 9983 }, |
| 9984 |
| 9985 /** |
| 9986 * If the dropdown is open when disabled becomes true, close the |
| 9603 * dropdown. | 9987 * dropdown. |
| 9604 */ | 9988 * |
| 9605 noAnimations: { | 9989 * @param {boolean} disabled True if disabled, otherwise false. |
| 9606 type: Boolean, | 9990 */ |
| 9607 value: false | 9991 _disabledChanged: function(disabled) { |
| 9608 }, | 9992 Polymer.IronControlState._disabledChanged.apply(this, arguments); |
| 9609 | 9993 if (disabled && this.opened) { |
| 9610 /** | 9994 this.close(); |
| 9611 * Set to true to disable automatically closing the dropdown after | 9995 } |
| 9612 * a selection has been made. | 9996 }, |
| 9613 */ | 9997 |
| 9614 ignoreSelect: { | 9998 __onIronOverlayCanceled: function(event) { |
| 9615 type: Boolean, | 9999 var uiEvent = event.detail; |
| 9616 value: false | 10000 var target = Polymer.dom(uiEvent).rootTarget; |
| 9617 }, | 10001 var trigger = this.$.trigger; |
| 9618 | 10002 var path = Polymer.dom(uiEvent).path; |
| 9619 /** | 10003 |
| 9620 * An animation config. If provided, this will be used to animate the | 10004 if (path.indexOf(trigger) > -1) { |
| 9621 * opening of the dropdown. | 10005 event.preventDefault(); |
| 9622 */ | 10006 } |
| 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 } | 10007 } |
| 9686 }, | 10008 }); |
| 9687 | 10009 |
| 9688 hostAttributes: { | 10010 PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; |
| 9689 role: 'group', | 10011 PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; |
| 9690 'aria-haspopup': 'true' | 10012 |
| 9691 }, | 10013 Polymer.PaperMenuButton = PaperMenuButton; |
| 9692 | 10014 })(); |
| 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({ | 10015 Polymer({ |
| 9777 is: 'paper-icon-button', | 10016 is: 'paper-icon-button', |
| 9778 | 10017 |
| 9779 hostAttributes: { | 10018 hostAttributes: { |
| 9780 role: 'button', | 10019 role: 'button', |
| 9781 tabindex: '0' | 10020 tabindex: '0' |
| 9782 }, | 10021 }, |
| 9783 | 10022 |
| 9784 behaviors: [ | 10023 behaviors: [ |
| 9785 Polymer.PaperInkyFocusBehavior | 10024 Polymer.PaperInkyFocusBehavior |
| (...skipping 28 matching lines...) Expand all Loading... |
| 9814 | 10053 |
| 9815 _altChanged: function(newValue, oldValue) { | 10054 _altChanged: function(newValue, oldValue) { |
| 9816 var label = this.getAttribute('aria-label'); | 10055 var label = this.getAttribute('aria-label'); |
| 9817 | 10056 |
| 9818 // Don't stomp over a user-set aria-label. | 10057 // Don't stomp over a user-set aria-label. |
| 9819 if (!label || oldValue == label) { | 10058 if (!label || oldValue == label) { |
| 9820 this.setAttribute('aria-label', newValue); | 10059 this.setAttribute('aria-label', newValue); |
| 9821 } | 10060 } |
| 9822 } | 10061 } |
| 9823 }); | 10062 }); |
| 10063 (function() { |
| 10064 'use strict'; |
| 10065 |
| 10066 Polymer.IronA11yAnnouncer = Polymer({ |
| 10067 is: 'iron-a11y-announcer', |
| 10068 |
| 10069 properties: { |
| 10070 |
| 10071 /** |
| 10072 * The value of mode is used to set the `aria-live` attribute |
| 10073 * for the element that will be announced. Valid values are: `off`, |
| 10074 * `polite` and `assertive`. |
| 10075 */ |
| 10076 mode: { |
| 10077 type: String, |
| 10078 value: 'polite' |
| 10079 }, |
| 10080 |
| 10081 _text: { |
| 10082 type: String, |
| 10083 value: '' |
| 10084 } |
| 10085 }, |
| 10086 |
| 10087 created: function() { |
| 10088 if (!Polymer.IronA11yAnnouncer.instance) { |
| 10089 Polymer.IronA11yAnnouncer.instance = this; |
| 10090 } |
| 10091 |
| 10092 document.body.addEventListener('iron-announce', this._onIronAnnounce.b
ind(this)); |
| 10093 }, |
| 10094 |
| 10095 /** |
| 10096 * Cause a text string to be announced by screen readers. |
| 10097 * |
| 10098 * @param {string} text The text that should be announced. |
| 10099 */ |
| 10100 announce: function(text) { |
| 10101 this._text = ''; |
| 10102 this.async(function() { |
| 10103 this._text = text; |
| 10104 }, 100); |
| 10105 }, |
| 10106 |
| 10107 _onIronAnnounce: function(event) { |
| 10108 if (event.detail && event.detail.text) { |
| 10109 this.announce(event.detail.text); |
| 10110 } |
| 10111 } |
| 10112 }); |
| 10113 |
| 10114 Polymer.IronA11yAnnouncer.instance = null; |
| 10115 |
| 10116 Polymer.IronA11yAnnouncer.requestAvailability = function() { |
| 10117 if (!Polymer.IronA11yAnnouncer.instance) { |
| 10118 Polymer.IronA11yAnnouncer.instance = document.createElement('iron-a11y
-announcer'); |
| 10119 } |
| 10120 |
| 10121 document.body.appendChild(Polymer.IronA11yAnnouncer.instance); |
| 10122 }; |
| 10123 })(); |
| 9824 /** | 10124 /** |
| 9825 * `Use Polymer.IronValidatableBehavior` to implement an element that validate
s user input. | 10125 * `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. | 10126 * Use the related `Polymer.IronValidatorBehavior` to add custom validation lo
gic to an iron-input. |
| 9827 * | 10127 * |
| 9828 * By default, an `<iron-form>` element validates its fields when the user pre
sses the submit button. | 10128 * 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 | 10129 * 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 | 10130 * call `validate()` on all its children. By using `Polymer.IronValidatableBeh
avior`, your |
| 9831 * custom element will get a public `validate()`, which | 10131 * custom element will get a public `validate()`, which |
| 9832 * will return the validity of the element, and a corresponding `invalid` attr
ibute, | 10132 * will return the validity of the element, and a corresponding `invalid` attr
ibute, |
| 9833 * which can be used for styling. | 10133 * which can be used for styling. |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9986 | 10286 |
| 9987 /** | 10287 /** |
| 9988 * Use this property instead of `value` for two-way data binding. | 10288 * Use this property instead of `value` for two-way data binding. |
| 9989 */ | 10289 */ |
| 9990 bindValue: { | 10290 bindValue: { |
| 9991 observer: '_bindValueChanged', | 10291 observer: '_bindValueChanged', |
| 9992 type: String | 10292 type: String |
| 9993 }, | 10293 }, |
| 9994 | 10294 |
| 9995 /** | 10295 /** |
| 9996 * Set to true to prevent the user from entering invalid input. The new in
put characters are | 10296 * 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 | 10297 * 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`). | 10298 * Pasted input will have each character checked individually; if any char
acter |
| 10299 * doesn't match `allowedPattern`, the entire pasted string will be reject
ed. |
| 10300 * If `allowedPattern` is not set, it will use the `type` attribute (only
supported for `type=number`). |
| 9999 */ | 10301 */ |
| 10000 preventInvalidInput: { | 10302 preventInvalidInput: { |
| 10001 type: Boolean | 10303 type: Boolean |
| 10002 }, | 10304 }, |
| 10003 | 10305 |
| 10004 /** | 10306 /** |
| 10005 * Regular expression expressing a set of characters to enforce the validi
ty of input characters. | 10307 * 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 | 10308 * This pattern represents the allowed characters for the field; as the us
er inputs text, |
| 10007 * allowed as input. | 10309 * each individual character will be checked against the pattern (rather t
han checking |
| 10310 * the entire value as a whole). The recommended format should be a list o
f allowed characters; |
| 10311 * for example, `[a-zA-Z0-9.+-!;:]` |
| 10008 */ | 10312 */ |
| 10009 allowedPattern: { | 10313 allowedPattern: { |
| 10010 type: String, | 10314 type: String, |
| 10011 observer: "_allowedPatternChanged" | 10315 observer: "_allowedPatternChanged" |
| 10012 }, | 10316 }, |
| 10013 | 10317 |
| 10014 _previousValidInput: { | 10318 _previousValidInput: { |
| 10015 type: String, | 10319 type: String, |
| 10016 value: '' | 10320 value: '' |
| 10017 }, | 10321 }, |
| 10018 | 10322 |
| 10019 _patternAlreadyChecked: { | 10323 _patternAlreadyChecked: { |
| 10020 type: Boolean, | 10324 type: Boolean, |
| 10021 value: false | 10325 value: false |
| 10022 } | 10326 } |
| 10023 | 10327 |
| 10024 }, | 10328 }, |
| 10025 | 10329 |
| 10026 listeners: { | 10330 listeners: { |
| 10027 'input': '_onInput', | 10331 'input': '_onInput', |
| 10028 'keypress': '_onKeypress' | 10332 'keypress': '_onKeypress' |
| 10029 }, | 10333 }, |
| 10030 | 10334 |
| 10335 /** @suppress {checkTypes} */ |
| 10336 registered: function() { |
| 10337 // Feature detect whether we need to patch dispatchEvent (i.e. on FF and I
E). |
| 10338 if (!this._canDispatchEventOnDisabled()) { |
| 10339 this._origDispatchEvent = this.dispatchEvent; |
| 10340 this.dispatchEvent = this._dispatchEventFirefoxIE; |
| 10341 } |
| 10342 }, |
| 10343 |
| 10344 created: function() { |
| 10345 Polymer.IronA11yAnnouncer.requestAvailability(); |
| 10346 }, |
| 10347 |
| 10348 _canDispatchEventOnDisabled: function() { |
| 10349 var input = document.createElement('input'); |
| 10350 var canDispatch = false; |
| 10351 input.disabled = true; |
| 10352 |
| 10353 input.addEventListener('feature-check-dispatch-event', function() { |
| 10354 canDispatch = true; |
| 10355 }); |
| 10356 |
| 10357 try { |
| 10358 input.dispatchEvent(new Event('feature-check-dispatch-event')); |
| 10359 } catch(e) {} |
| 10360 |
| 10361 return canDispatch; |
| 10362 }, |
| 10363 |
| 10364 _dispatchEventFirefoxIE: function() { |
| 10365 // Due to Firefox bug, events fired on disabled form controls can throw |
| 10366 // errors; furthermore, neither IE nor Firefox will actually dispatch |
| 10367 // events from disabled form controls; as such, we toggle disable around |
| 10368 // the dispatch to allow notifying properties to notify |
| 10369 // See issue #47 for details |
| 10370 var disabled = this.disabled; |
| 10371 this.disabled = false; |
| 10372 this._origDispatchEvent.apply(this, arguments); |
| 10373 this.disabled = disabled; |
| 10374 }, |
| 10375 |
| 10031 get _patternRegExp() { | 10376 get _patternRegExp() { |
| 10032 var pattern; | 10377 var pattern; |
| 10033 if (this.allowedPattern) { | 10378 if (this.allowedPattern) { |
| 10034 pattern = new RegExp(this.allowedPattern); | 10379 pattern = new RegExp(this.allowedPattern); |
| 10035 } else { | 10380 } else { |
| 10036 switch (this.type) { | 10381 switch (this.type) { |
| 10037 case 'number': | 10382 case 'number': |
| 10038 pattern = /[0-9.,e-]/; | 10383 pattern = /[0-9.,e-]/; |
| 10039 break; | 10384 break; |
| 10040 } | 10385 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 10061 // Force to prevent invalid input when an `allowed-pattern` is set | 10406 // Force to prevent invalid input when an `allowed-pattern` is set |
| 10062 this.preventInvalidInput = this.allowedPattern ? true : false; | 10407 this.preventInvalidInput = this.allowedPattern ? true : false; |
| 10063 }, | 10408 }, |
| 10064 | 10409 |
| 10065 _onInput: function() { | 10410 _onInput: function() { |
| 10066 // Need to validate each of the characters pasted if they haven't | 10411 // Need to validate each of the characters pasted if they haven't |
| 10067 // been validated inside `_onKeypress` already. | 10412 // been validated inside `_onKeypress` already. |
| 10068 if (this.preventInvalidInput && !this._patternAlreadyChecked) { | 10413 if (this.preventInvalidInput && !this._patternAlreadyChecked) { |
| 10069 var valid = this._checkPatternValidity(); | 10414 var valid = this._checkPatternValidity(); |
| 10070 if (!valid) { | 10415 if (!valid) { |
| 10416 this._announceInvalidCharacter('Invalid string of characters not enter
ed.'); |
| 10071 this.value = this._previousValidInput; | 10417 this.value = this._previousValidInput; |
| 10072 } | 10418 } |
| 10073 } | 10419 } |
| 10074 | 10420 |
| 10075 this.bindValue = this.value; | 10421 this.bindValue = this.value; |
| 10076 this._previousValidInput = this.value; | 10422 this._previousValidInput = this.value; |
| 10077 this._patternAlreadyChecked = false; | 10423 this._patternAlreadyChecked = false; |
| 10078 }, | 10424 }, |
| 10079 | 10425 |
| 10080 _isPrintable: function(event) { | 10426 _isPrintable: function(event) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10121 // Handle special keys and backspace | 10467 // Handle special keys and backspace |
| 10122 if (event.metaKey || event.ctrlKey || event.altKey) | 10468 if (event.metaKey || event.ctrlKey || event.altKey) |
| 10123 return; | 10469 return; |
| 10124 | 10470 |
| 10125 // Check the pattern either here or in `_onInput`, but not in both. | 10471 // Check the pattern either here or in `_onInput`, but not in both. |
| 10126 this._patternAlreadyChecked = true; | 10472 this._patternAlreadyChecked = true; |
| 10127 | 10473 |
| 10128 var thisChar = String.fromCharCode(event.charCode); | 10474 var thisChar = String.fromCharCode(event.charCode); |
| 10129 if (this._isPrintable(event) && !regexp.test(thisChar)) { | 10475 if (this._isPrintable(event) && !regexp.test(thisChar)) { |
| 10130 event.preventDefault(); | 10476 event.preventDefault(); |
| 10477 this._announceInvalidCharacter('Invalid character ' + thisChar + ' not e
ntered.'); |
| 10131 } | 10478 } |
| 10132 }, | 10479 }, |
| 10133 | 10480 |
| 10134 _checkPatternValidity: function() { | 10481 _checkPatternValidity: function() { |
| 10135 var regexp = this._patternRegExp; | 10482 var regexp = this._patternRegExp; |
| 10136 if (!regexp) { | 10483 if (!regexp) { |
| 10137 return true; | 10484 return true; |
| 10138 } | 10485 } |
| 10139 for (var i = 0; i < this.value.length; i++) { | 10486 for (var i = 0; i < this.value.length; i++) { |
| 10140 if (!regexp.test(this.value[i])) { | 10487 if (!regexp.test(this.value[i])) { |
| 10141 return false; | 10488 return false; |
| 10142 } | 10489 } |
| 10143 } | 10490 } |
| 10144 return true; | 10491 return true; |
| 10145 }, | 10492 }, |
| 10146 | 10493 |
| 10147 /** | 10494 /** |
| 10148 * Returns true if `value` is valid. The validator provided in `validator` w
ill be used first, | 10495 * Returns true if `value` is valid. The validator provided in `validator` w
ill be used first, |
| 10149 * then any constraints. | 10496 * then any constraints. |
| 10150 * @return {boolean} True if the value is valid. | 10497 * @return {boolean} True if the value is valid. |
| 10151 */ | 10498 */ |
| 10152 validate: function() { | 10499 validate: function() { |
| 10153 // Empty, non-required input is valid. | 10500 // First, check what the browser thinks. Some inputs (like type=number) |
| 10154 if (!this.required && this.value == '') { | 10501 // behave weirdly and will set the value to "" if something invalid is |
| 10155 this.invalid = false; | 10502 // entered, but will set the validity correctly. |
| 10156 return true; | 10503 var valid = this.checkValidity(); |
| 10504 |
| 10505 // Only do extra checking if the browser thought this was valid. |
| 10506 if (valid) { |
| 10507 // Empty, required input is invalid |
| 10508 if (this.required && this.value === '') { |
| 10509 valid = false; |
| 10510 } else if (this.hasValidator()) { |
| 10511 valid = Polymer.IronValidatableBehavior.validate.call(this, this.value
); |
| 10512 } |
| 10157 } | 10513 } |
| 10158 | 10514 |
| 10159 var valid; | 10515 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'); | 10516 this.fire('iron-input-validate'); |
| 10167 return valid; | 10517 return valid; |
| 10518 }, |
| 10519 |
| 10520 _announceInvalidCharacter: function(message) { |
| 10521 this.fire('iron-announce', { text: message }); |
| 10168 } | 10522 } |
| 10169 | |
| 10170 }); | 10523 }); |
| 10171 | 10524 |
| 10172 /* | 10525 /* |
| 10173 The `iron-input-validate` event is fired whenever `validate()` is called. | 10526 The `iron-input-validate` event is fired whenever `validate()` is called. |
| 10174 @event iron-input-validate | 10527 @event iron-input-validate |
| 10175 */ | 10528 */ |
| 10176 Polymer({ | 10529 Polymer({ |
| 10177 is: 'paper-input-container', | 10530 is: 'paper-input-container', |
| 10178 | 10531 |
| 10179 properties: { | 10532 properties: { |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10296 get _inputElementValue() { | 10649 get _inputElementValue() { |
| 10297 return this._inputElement[this._propertyForValue] || this._inputElement.va
lue; | 10650 return this._inputElement[this._propertyForValue] || this._inputElement.va
lue; |
| 10298 }, | 10651 }, |
| 10299 | 10652 |
| 10300 ready: function() { | 10653 ready: function() { |
| 10301 if (!this._addons) { | 10654 if (!this._addons) { |
| 10302 this._addons = []; | 10655 this._addons = []; |
| 10303 } | 10656 } |
| 10304 this.addEventListener('focus', this._boundOnFocus, true); | 10657 this.addEventListener('focus', this._boundOnFocus, true); |
| 10305 this.addEventListener('blur', this._boundOnBlur, true); | 10658 this.addEventListener('blur', this._boundOnBlur, true); |
| 10659 }, |
| 10660 |
| 10661 attached: function() { |
| 10306 if (this.attrForValue) { | 10662 if (this.attrForValue) { |
| 10307 this._inputElement.addEventListener(this._valueChangedEvent, this._bound
ValueChanged); | 10663 this._inputElement.addEventListener(this._valueChangedEvent, this._bound
ValueChanged); |
| 10308 } else { | 10664 } else { |
| 10309 this.addEventListener('input', this._onInput); | 10665 this.addEventListener('input', this._onInput); |
| 10310 } | 10666 } |
| 10311 }, | |
| 10312 | 10667 |
| 10313 attached: function() { | |
| 10314 // Only validate when attached if the input already has a value. | 10668 // Only validate when attached if the input already has a value. |
| 10315 if (this._inputElementValue != '') { | 10669 if (this._inputElementValue != '') { |
| 10316 this._handleValueAndAutoValidate(this._inputElement); | 10670 this._handleValueAndAutoValidate(this._inputElement); |
| 10317 } else { | 10671 } else { |
| 10318 this._handleValue(this._inputElement); | 10672 this._handleValue(this._inputElement); |
| 10319 } | 10673 } |
| 10320 }, | 10674 }, |
| 10321 | 10675 |
| 10322 _onAddonAttached: function(event) { | 10676 _onAddonAttached: function(event) { |
| 10323 if (!this._addons) { | 10677 if (!this._addons) { |
| (...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10862 // found in the LICENSE file. | 11216 // found in the LICENSE file. |
| 10863 | 11217 |
| 10864 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> | 11218 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> |
| 10865 | 11219 |
| 10866 i18nTemplate.process(document, loadTimeData); | 11220 i18nTemplate.process(document, loadTimeData); |
| 10867 // Copyright 2015 The Chromium Authors. All rights reserved. | 11221 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 10868 // Use of this source code is governed by a BSD-style license that can be | 11222 // Use of this source code is governed by a BSD-style license that can be |
| 10869 // found in the LICENSE file. | 11223 // found in the LICENSE file. |
| 10870 | 11224 |
| 10871 window.addEventListener('load', downloads.Manager.onLoad); | 11225 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |