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

Side by Side Diff: chrome/browser/resources/md_downloads/crisper.js

Issue 1862213002: Roll third_party/polymer. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove obsolete appearance_browsertest.js, result of a previous bad merge. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/browser/resources/md_downloads/vulcanized.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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);
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/md_downloads/vulcanized.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698