| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @fileoverview PromiseResolver is a helper class that allows creating a |
| 7 * Promise that will be fulfilled (resolved or rejected) some time later. |
| 8 * |
| 9 * Example: |
| 10 * var resolver = new PromiseResolver(); |
| 11 * resolver.promise.then(function(result) { |
| 12 * console.log('resolved with', result); |
| 13 * }); |
| 14 * ... |
| 15 * ... |
| 16 * resolver.resolve({hello: 'world'}); |
| 17 */ |
| 18 |
| 19 /** |
| 20 * @constructor @struct |
| 21 * @template T |
| 22 */ |
| 23 function PromiseResolver() { |
| 24 /** @type {function(T): void} */ |
| 25 this.resolve; |
| 26 |
| 27 /** @type {function(*=): void} */ |
| 28 this.reject; |
| 29 |
| 30 /** @type {!Promise<T>} */ |
| 31 this.promise = new Promise(function(resolve, reject) { |
| 32 this.resolve = resolve; |
| 33 this.reject = reject; |
| 34 }.bind(this)); |
| 35 }; |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 36 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 37 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 38 // found in the LICENSE file. |
| 4 | 39 |
| 5 /** | 40 /** |
| 6 * The global object. | 41 * The global object. |
| 7 * @type {!Object} | 42 * @type {!Object} |
| 8 * @const | 43 * @const |
| 9 */ | 44 */ |
| 10 var global = this; | 45 var global = this; |
| 11 | 46 |
| 12 /** @typedef {{eventName: string, uid: number}} */ | 47 /** @typedef {{eventName: string, uid: number}} */ |
| 13 var WebUIListener; | 48 var WebUIListener; |
| 14 | 49 |
| 15 /** Platform, package, object property, and Event support. **/ | 50 /** Platform, package, object property, and Event support. **/ |
| 16 var cr = function() { | 51 var cr = cr || function() { |
| 17 'use strict'; | 52 'use strict'; |
| 18 | 53 |
| 19 /** | 54 /** |
| 20 * Builds an object structure for the provided namespace path, | 55 * Builds an object structure for the provided namespace path, |
| 21 * ensuring that names that already exist are not overwritten. For | 56 * ensuring that names that already exist are not overwritten. For |
| 22 * example: | 57 * example: |
| 23 * "a.b.c" -> a = {};a.b={};a.b.c={}; | 58 * "a.b.c" -> a = {};a.b={};a.b.c={}; |
| 24 * @param {string} name Name of the object that this file defines. | 59 * @param {string} name Name of the object that this file defines. |
| 25 * @param {*=} opt_object The object to expose at the end of the path. | 60 * @param {*=} opt_object The object to expose at the end of the path. |
| 26 * @param {Object=} opt_objectToExportTo The object to add the path to; | 61 * @param {Object=} opt_objectToExportTo The object to add the path to; |
| 27 * default is {@code global}. | 62 * default is {@code global}. |
| 63 * @return {!Object} The last object exported (i.e. exportPath('cr.ui') |
| 64 * returns a reference to the ui property of window.cr). |
| 28 * @private | 65 * @private |
| 29 */ | 66 */ |
| 30 function exportPath(name, opt_object, opt_objectToExportTo) { | 67 function exportPath(name, opt_object, opt_objectToExportTo) { |
| 31 var parts = name.split('.'); | 68 var parts = name.split('.'); |
| 32 var cur = opt_objectToExportTo || global; | 69 var cur = opt_objectToExportTo || global; |
| 33 | 70 |
| 34 for (var part; parts.length && (part = parts.shift());) { | 71 for (var part; parts.length && (part = parts.shift());) { |
| 35 if (!parts.length && opt_object !== undefined) { | 72 if (!parts.length && opt_object !== undefined) { |
| 36 // last part and we have an object; use it | 73 // last part and we have an object; use it |
| 37 cur[part] = opt_object; | 74 cur[part] = opt_object; |
| 38 } else if (part in cur) { | 75 } else if (part in cur) { |
| 39 cur = cur[part]; | 76 cur = cur[part]; |
| 40 } else { | 77 } else { |
| 41 cur = cur[part] = {}; | 78 cur = cur[part] = {}; |
| 42 } | 79 } |
| 43 } | 80 } |
| 44 return cur; | 81 return cur; |
| 45 }; | 82 } |
| 46 | 83 |
| 47 /** | 84 /** |
| 48 * Fires a property change event on the target. | 85 * Fires a property change event on the target. |
| 49 * @param {EventTarget} target The target to dispatch the event on. | 86 * @param {EventTarget} target The target to dispatch the event on. |
| 50 * @param {string} propertyName The name of the property that changed. | 87 * @param {string} propertyName The name of the property that changed. |
| 51 * @param {*} newValue The new value for the property. | 88 * @param {*} newValue The new value for the property. |
| 52 * @param {*} oldValue The old value for the property. | 89 * @param {*} oldValue The old value for the property. |
| 53 */ | 90 */ |
| 54 function dispatchPropertyChange(target, propertyName, newValue, oldValue) { | 91 function dispatchPropertyChange(target, propertyName, newValue, oldValue) { |
| 55 var e = new Event(propertyName + 'Change'); | 92 var e = new Event(propertyName + 'Change'); |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 var target = opt_target ? document.getElementById(opt_target) : | 348 var target = opt_target ? document.getElementById(opt_target) : |
| 312 ctor.getInstance(); | 349 ctor.getInstance(); |
| 313 return target[method + '_'].apply(target, arguments); | 350 return target[method + '_'].apply(target, arguments); |
| 314 }; | 351 }; |
| 315 }); | 352 }); |
| 316 } | 353 } |
| 317 | 354 |
| 318 /** | 355 /** |
| 319 * The mapping used by the sendWithPromise mechanism to tie the Promise | 356 * The mapping used by the sendWithPromise mechanism to tie the Promise |
| 320 * returned to callers with the corresponding WebUI response. The mapping is | 357 * returned to callers with the corresponding WebUI response. The mapping is |
| 321 * from ID to the Promise resolver function; the ID is generated by | 358 * from ID to the PromiseResolver helper; the ID is generated by |
| 322 * sendWithPromise and is unique across all invocations of said method. | 359 * sendWithPromise and is unique across all invocations of said method. |
| 323 * @type {!Object<!Function>} | 360 * @type {!Object<!PromiseResolver>} |
| 324 */ | 361 */ |
| 325 var chromeSendResolverMap = {}; | 362 var chromeSendResolverMap = {}; |
| 326 | 363 |
| 327 /** | 364 /** |
| 328 * The named method the WebUI handler calls directly in response to a | 365 * The named method the WebUI handler calls directly in response to a |
| 329 * chrome.send call that expects a response. The handler requires no knowledge | 366 * chrome.send call that expects a response. The handler requires no knowledge |
| 330 * of the specific name of this method, as the name is passed to the handler | 367 * of the specific name of this method, as the name is passed to the handler |
| 331 * as the first argument in the arguments list of chrome.send. The handler | 368 * as the first argument in the arguments list of chrome.send. The handler |
| 332 * must pass the ID, also sent via the chrome.send arguments list, as the | 369 * must pass the ID, also sent via the chrome.send arguments list, as the |
| 333 * first argument of the JS invocation; additionally, the handler may | 370 * first argument of the JS invocation; additionally, the handler may |
| 334 * supply any number of other arguments that will be included in the response. | 371 * supply any number of other arguments that will be included in the response. |
| 335 * @param {string} id The unique ID identifying the Promise this response is | 372 * @param {string} id The unique ID identifying the Promise this response is |
| 336 * tied to. | 373 * tied to. |
| 374 * @param {boolean} isSuccess Whether the request was successful. |
| 337 * @param {*} response The response as sent from C++. | 375 * @param {*} response The response as sent from C++. |
| 338 */ | 376 */ |
| 339 function webUIResponse(id, response) { | 377 function webUIResponse(id, isSuccess, response) { |
| 340 var resolverFn = chromeSendResolverMap[id]; | 378 var resolver = chromeSendResolverMap[id]; |
| 341 delete chromeSendResolverMap[id]; | 379 delete chromeSendResolverMap[id]; |
| 342 resolverFn(response); | 380 |
| 381 if (isSuccess) |
| 382 resolver.resolve(response); |
| 383 else |
| 384 resolver.reject(response); |
| 343 } | 385 } |
| 344 | 386 |
| 345 /** | 387 /** |
| 346 * A variation of chrome.send, suitable for messages that expect a single | 388 * A variation of chrome.send, suitable for messages that expect a single |
| 347 * response from C++. | 389 * response from C++. |
| 348 * @param {string} methodName The name of the WebUI handler API. | 390 * @param {string} methodName The name of the WebUI handler API. |
| 349 * @param {...*} var_args Varibale number of arguments to be forwarded to the | 391 * @param {...*} var_args Varibale number of arguments to be forwarded to the |
| 350 * C++ call. | 392 * C++ call. |
| 351 * @return {!Promise} | 393 * @return {!Promise} |
| 352 */ | 394 */ |
| 353 function sendWithPromise(methodName, var_args) { | 395 function sendWithPromise(methodName, var_args) { |
| 354 var args = Array.prototype.slice.call(arguments, 1); | 396 var args = Array.prototype.slice.call(arguments, 1); |
| 355 return new Promise(function(resolve, reject) { | 397 var promiseResolver = new PromiseResolver(); |
| 356 var id = methodName + '_' + createUid(); | 398 var id = methodName + '_' + createUid(); |
| 357 chromeSendResolverMap[id] = resolve; | 399 chromeSendResolverMap[id] = promiseResolver; |
| 358 chrome.send(methodName, [id].concat(args)); | 400 chrome.send(methodName, [id].concat(args)); |
| 359 }); | 401 return promiseResolver.promise; |
| 360 } | 402 } |
| 361 | 403 |
| 362 /** | 404 /** |
| 363 * A map of maps associating event names with listeners. The 2nd level map | 405 * A map of maps associating event names with listeners. The 2nd level map |
| 364 * associates a listener ID with the callback function, such that individual | 406 * associates a listener ID with the callback function, such that individual |
| 365 * listeners can be removed from an event without affecting other listeners of | 407 * listeners can be removed from an event without affecting other listeners of |
| 366 * the same event. | 408 * the same event. |
| 367 * @type {!Object<!Object<!Function>>} | 409 * @type {!Object<!Object<!Function>>} |
| 368 */ | 410 */ |
| 369 var webUIListenerMap = {}; | 411 var webUIListenerMap = {}; |
| (...skipping 1093 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1463 * @param {string} original The original string. | 1505 * @param {string} original The original string. |
| 1464 * @param {number} maxLength The maximum length allowed for the string. | 1506 * @param {number} maxLength The maximum length allowed for the string. |
| 1465 * @return {string} The original string if its length does not exceed | 1507 * @return {string} The original string if its length does not exceed |
| 1466 * |maxLength|. Otherwise the first |maxLength| - 1 characters with '...' | 1508 * |maxLength|. Otherwise the first |maxLength| - 1 characters with '...' |
| 1467 * appended. | 1509 * appended. |
| 1468 */ | 1510 */ |
| 1469 function elide(original, maxLength) { | 1511 function elide(original, maxLength) { |
| 1470 if (original.length <= maxLength) | 1512 if (original.length <= maxLength) |
| 1471 return original; | 1513 return original; |
| 1472 return original.substring(0, maxLength - 1) + '\u2026'; | 1514 return original.substring(0, maxLength - 1) + '\u2026'; |
| 1515 } |
| 1516 |
| 1517 /** |
| 1518 * Quote a string so it can be used in a regular expression. |
| 1519 * @param {string} str The source string. |
| 1520 * @return {string} The escaped string. |
| 1521 */ |
| 1522 function quoteString(str) { |
| 1523 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); |
| 1473 }; | 1524 }; |
| 1474 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1525 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 1475 // Use of this source code is governed by a BSD-style license that can be | 1526 // Use of this source code is governed by a BSD-style license that can be |
| 1476 // found in the LICENSE file. | 1527 // found in the LICENSE file. |
| 1477 | 1528 |
| 1478 /** | 1529 /** |
| 1479 * @fileoverview Assertion support. | 1530 * @fileoverview Assertion support. |
| 1480 */ | 1531 */ |
| 1481 | 1532 |
| 1482 /** | 1533 /** |
| (...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1833 | 1884 |
| 1834 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> | 1885 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> |
| 1835 | 1886 |
| 1836 i18nTemplate.process(document, loadTimeData); | 1887 i18nTemplate.process(document, loadTimeData); |
| 1837 /** | 1888 /** |
| 1838 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to | 1889 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to |
| 1839 * coordinate the flow of resize events between "resizers" (elements that cont
rol the | 1890 * coordinate the flow of resize events between "resizers" (elements that cont
rol the |
| 1840 * size or hidden state of their children) and "resizables" (elements that nee
d to be | 1891 * size or hidden state of their children) and "resizables" (elements that nee
d to be |
| 1841 * notified when they are resized or un-hidden by their parents in order to ta
ke | 1892 * notified when they are resized or un-hidden by their parents in order to ta
ke |
| 1842 * action on their new measurements). | 1893 * action on their new measurements). |
| 1894 * |
| 1843 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to | 1895 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to |
| 1844 * their element definition and listen for the `iron-resize` event on themselv
es. | 1896 * their element definition and listen for the `iron-resize` event on themselv
es. |
| 1845 * This event will be fired when they become showing after having been hidden, | 1897 * This event will be fired when they become showing after having been hidden, |
| 1846 * when they are resized explicitly by another resizable, or when the window h
as been | 1898 * when they are resized explicitly by another resizable, or when the window h
as been |
| 1847 * resized. | 1899 * resized. |
| 1900 * |
| 1848 * Note, the `iron-resize` event is non-bubbling. | 1901 * Note, the `iron-resize` event is non-bubbling. |
| 1849 * | 1902 * |
| 1850 * @polymerBehavior Polymer.IronResizableBehavior | 1903 * @polymerBehavior Polymer.IronResizableBehavior |
| 1851 * @demo demo/index.html | 1904 * @demo demo/index.html |
| 1852 **/ | 1905 **/ |
| 1853 Polymer.IronResizableBehavior = { | 1906 Polymer.IronResizableBehavior = { |
| 1854 properties: { | 1907 properties: { |
| 1855 /** | 1908 /** |
| 1856 * The closest ancestor element that implements `IronResizableBehavior`. | 1909 * The closest ancestor element that implements `IronResizableBehavior`. |
| 1857 */ | 1910 */ |
| (...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2501 window.removeEventListener('scroll', this._boundScrollHandler); | 2554 window.removeEventListener('scroll', this._boundScrollHandler); |
| 2502 } else if (this._oldScrollTarget.removeEventListener) { | 2555 } else if (this._oldScrollTarget.removeEventListener) { |
| 2503 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); | 2556 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); |
| 2504 } | 2557 } |
| 2505 this._oldScrollTarget = null; | 2558 this._oldScrollTarget = null; |
| 2506 } | 2559 } |
| 2507 if (isAttached) { | 2560 if (isAttached) { |
| 2508 // Support element id references | 2561 // Support element id references |
| 2509 if (typeof scrollTarget === 'string') { | 2562 if (typeof scrollTarget === 'string') { |
| 2510 | 2563 |
| 2511 var ownerRoot = Polymer.dom(this).getOwnerRoot(); | 2564 var host = this.domHost; |
| 2512 this.scrollTarget = (ownerRoot && ownerRoot.$) ? | 2565 this.scrollTarget = host && host.$ ? host.$[scrollTarget] : |
| 2513 ownerRoot.$[scrollTarget] : Polymer.dom(this.ownerDocument).queryS
elector('#' + scrollTarget); | 2566 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); |
| 2514 | 2567 |
| 2515 } else if (this._scrollHandler) { | 2568 } else if (this._scrollHandler) { |
| 2516 | 2569 |
| 2517 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); | 2570 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); |
| 2518 // Add a new listener | 2571 // Add a new listener |
| 2519 if (scrollTarget === this._doc) { | 2572 if (scrollTarget === this._doc) { |
| 2520 window.addEventListener('scroll', this._boundScrollHandler); | 2573 window.addEventListener('scroll', this._boundScrollHandler); |
| 2521 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { | 2574 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { |
| 2522 this._scrollHandler(); | 2575 this._scrollHandler(); |
| 2523 } | 2576 } |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2602 window.scrollTo(left, window.pageYOffset); | 2655 window.scrollTo(left, window.pageYOffset); |
| 2603 } else if (this._isValidScrollTarget()) { | 2656 } else if (this._isValidScrollTarget()) { |
| 2604 this.scrollTarget.scrollLeft = left; | 2657 this.scrollTarget.scrollLeft = left; |
| 2605 } | 2658 } |
| 2606 }, | 2659 }, |
| 2607 | 2660 |
| 2608 /** | 2661 /** |
| 2609 * Scrolls the content to a particular place. | 2662 * Scrolls the content to a particular place. |
| 2610 * | 2663 * |
| 2611 * @method scroll | 2664 * @method scroll |
| 2665 * @param {number} left The left position |
| 2612 * @param {number} top The top position | 2666 * @param {number} top The top position |
| 2613 * @param {number} left The left position | |
| 2614 */ | 2667 */ |
| 2615 scroll: function(top, left) { | 2668 scroll: function(left, top) { |
| 2616 if (this.scrollTarget === this._doc) { | 2669 if (this.scrollTarget === this._doc) { |
| 2617 window.scrollTo(top, left); | 2670 window.scrollTo(left, top); |
| 2618 } else if (this._isValidScrollTarget()) { | 2671 } else if (this._isValidScrollTarget()) { |
| 2672 this.scrollTarget.scrollLeft = left; |
| 2619 this.scrollTarget.scrollTop = top; | 2673 this.scrollTarget.scrollTop = top; |
| 2620 this.scrollTarget.scrollLeft = left; | |
| 2621 } | 2674 } |
| 2622 }, | 2675 }, |
| 2623 | 2676 |
| 2624 /** | 2677 /** |
| 2625 * Gets the width of the scroll target. | 2678 * Gets the width of the scroll target. |
| 2626 * | 2679 * |
| 2627 * @type {number} | 2680 * @type {number} |
| 2628 */ | 2681 */ |
| 2629 get _scrollTargetWidth() { | 2682 get _scrollTargetWidth() { |
| 2630 if (this._isValidScrollTarget()) { | 2683 if (this._isValidScrollTarget()) { |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2767 'enter': '_didEnter' | 2820 'enter': '_didEnter' |
| 2768 }, | 2821 }, |
| 2769 | 2822 |
| 2770 /** | 2823 /** |
| 2771 * The ratio of hidden tiles that should remain in the scroll direction. | 2824 * The ratio of hidden tiles that should remain in the scroll direction. |
| 2772 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. | 2825 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. |
| 2773 */ | 2826 */ |
| 2774 _ratio: 0.5, | 2827 _ratio: 0.5, |
| 2775 | 2828 |
| 2776 /** | 2829 /** |
| 2777 * The padding-top value of the `scroller` element | 2830 * The padding-top value for the list. |
| 2778 */ | 2831 */ |
| 2779 _scrollerPaddingTop: 0, | 2832 _scrollerPaddingTop: 0, |
| 2780 | 2833 |
| 2781 /** | 2834 /** |
| 2782 * This value is the same as `scrollTop`. | 2835 * This value is the same as `scrollTop`. |
| 2783 */ | 2836 */ |
| 2784 _scrollPosition: 0, | 2837 _scrollPosition: 0, |
| 2785 | 2838 |
| 2786 /** | 2839 /** |
| 2787 * The number of tiles in the DOM. | |
| 2788 */ | |
| 2789 _physicalCount: 0, | |
| 2790 | |
| 2791 /** | |
| 2792 * The k-th tile that is at the top of the scrolling list. | |
| 2793 */ | |
| 2794 _physicalStart: 0, | |
| 2795 | |
| 2796 /** | |
| 2797 * The k-th tile that is at the bottom of the scrolling list. | |
| 2798 */ | |
| 2799 _physicalEnd: 0, | |
| 2800 | |
| 2801 /** | |
| 2802 * The sum of the heights of all the tiles in the DOM. | 2840 * The sum of the heights of all the tiles in the DOM. |
| 2803 */ | 2841 */ |
| 2804 _physicalSize: 0, | 2842 _physicalSize: 0, |
| 2805 | 2843 |
| 2806 /** | 2844 /** |
| 2807 * The average `F` of the tiles observed till now. | 2845 * The average `F` of the tiles observed till now. |
| 2808 */ | 2846 */ |
| 2809 _physicalAverage: 0, | 2847 _physicalAverage: 0, |
| 2810 | 2848 |
| 2811 /** | 2849 /** |
| 2812 * The number of tiles which `offsetHeight` > 0 observed until now. | 2850 * The number of tiles which `offsetHeight` > 0 observed until now. |
| 2813 */ | 2851 */ |
| 2814 _physicalAverageCount: 0, | 2852 _physicalAverageCount: 0, |
| 2815 | 2853 |
| 2816 /** | 2854 /** |
| 2817 * The Y position of the item rendered in the `_physicalStart` | 2855 * The Y position of the item rendered in the `_physicalStart` |
| 2818 * tile relative to the scrolling list. | 2856 * tile relative to the scrolling list. |
| 2819 */ | 2857 */ |
| 2820 _physicalTop: 0, | 2858 _physicalTop: 0, |
| 2821 | 2859 |
| 2822 /** | 2860 /** |
| 2823 * The number of items in the list. | 2861 * The number of items in the list. |
| 2824 */ | 2862 */ |
| 2825 _virtualCount: 0, | 2863 _virtualCount: 0, |
| 2826 | 2864 |
| 2827 /** | 2865 /** |
| 2828 * The n-th item rendered in the `_physicalStart` tile. | |
| 2829 */ | |
| 2830 _virtualStartVal: 0, | |
| 2831 | |
| 2832 /** | |
| 2833 * A map between an item key and its physical item index | 2866 * A map between an item key and its physical item index |
| 2834 */ | 2867 */ |
| 2835 _physicalIndexForKey: null, | 2868 _physicalIndexForKey: null, |
| 2836 | 2869 |
| 2837 /** | 2870 /** |
| 2838 * The estimated scroll height based on `_physicalAverage` | 2871 * The estimated scroll height based on `_physicalAverage` |
| 2839 */ | 2872 */ |
| 2840 _estScrollHeight: 0, | 2873 _estScrollHeight: 0, |
| 2841 | 2874 |
| 2842 /** | 2875 /** |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2868 */ | 2901 */ |
| 2869 _firstVisibleIndexVal: null, | 2902 _firstVisibleIndexVal: null, |
| 2870 | 2903 |
| 2871 /** | 2904 /** |
| 2872 * A cached value for the last visible index. | 2905 * A cached value for the last visible index. |
| 2873 * See `lastVisibleIndex` | 2906 * See `lastVisibleIndex` |
| 2874 * @type {?number} | 2907 * @type {?number} |
| 2875 */ | 2908 */ |
| 2876 _lastVisibleIndexVal: null, | 2909 _lastVisibleIndexVal: null, |
| 2877 | 2910 |
| 2878 | |
| 2879 /** | 2911 /** |
| 2880 * A Polymer collection for the items. | 2912 * A Polymer collection for the items. |
| 2881 * @type {?Polymer.Collection} | 2913 * @type {?Polymer.Collection} |
| 2882 */ | 2914 */ |
| 2883 _collection: null, | 2915 _collection: null, |
| 2884 | 2916 |
| 2885 /** | 2917 /** |
| 2886 * True if the current item list was rendered for the first time | 2918 * True if the current item list was rendered for the first time |
| 2887 * after attached. | 2919 * after attached. |
| 2888 */ | 2920 */ |
| 2889 _itemsRendered: false, | 2921 _itemsRendered: false, |
| 2890 | 2922 |
| 2891 /** | 2923 /** |
| 2892 * The page that is currently rendered. | 2924 * The page that is currently rendered. |
| 2893 */ | 2925 */ |
| 2894 _lastPage: null, | 2926 _lastPage: null, |
| 2895 | 2927 |
| 2896 /** | 2928 /** |
| 2897 * The max number of pages to render. One page is equivalent to the height o
f the list. | 2929 * The max number of pages to render. One page is equivalent to the height o
f the list. |
| 2898 */ | 2930 */ |
| 2899 _maxPages: 3, | 2931 _maxPages: 3, |
| 2900 | 2932 |
| 2901 /** | 2933 /** |
| 2902 * The currently focused item index. | 2934 * The currently focused physical item. |
| 2903 */ | 2935 */ |
| 2904 _focusedIndex: 0, | 2936 _focusedItem: null, |
| 2937 |
| 2938 /** |
| 2939 * The index of the `_focusedItem`. |
| 2940 */ |
| 2941 _focusedIndex: -1, |
| 2905 | 2942 |
| 2906 /** | 2943 /** |
| 2907 * The the item that is focused if it is moved offscreen. | 2944 * The the item that is focused if it is moved offscreen. |
| 2908 * @private {?TemplatizerNode} | 2945 * @private {?TemplatizerNode} |
| 2909 */ | 2946 */ |
| 2910 _offscreenFocusedItem: null, | 2947 _offscreenFocusedItem: null, |
| 2911 | 2948 |
| 2912 /** | 2949 /** |
| 2913 * The item that backfills the `_offscreenFocusedItem` in the physical items | 2950 * The item that backfills the `_offscreenFocusedItem` in the physical items |
| 2914 * list when that item is moved offscreen. | 2951 * list when that item is moved offscreen. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 2930 }, | 2967 }, |
| 2931 | 2968 |
| 2932 /** | 2969 /** |
| 2933 * The n-th item rendered in the last physical item. | 2970 * The n-th item rendered in the last physical item. |
| 2934 */ | 2971 */ |
| 2935 get _virtualEnd() { | 2972 get _virtualEnd() { |
| 2936 return this._virtualStart + this._physicalCount - 1; | 2973 return this._virtualStart + this._physicalCount - 1; |
| 2937 }, | 2974 }, |
| 2938 | 2975 |
| 2939 /** | 2976 /** |
| 2977 * The height of the physical content that isn't on the screen. |
| 2978 */ |
| 2979 get _hiddenContentSize() { |
| 2980 return this._physicalSize - this._viewportSize; |
| 2981 }, |
| 2982 |
| 2983 /** |
| 2984 * The maximum scroll top value. |
| 2985 */ |
| 2986 get _maxScrollTop() { |
| 2987 return this._estScrollHeight - this._viewportSize + this._scrollerPaddingT
op; |
| 2988 }, |
| 2989 |
| 2990 /** |
| 2940 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. | 2991 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. |
| 2941 */ | 2992 */ |
| 2942 _minVirtualStart: 0, | 2993 _minVirtualStart: 0, |
| 2943 | 2994 |
| 2944 /** | 2995 /** |
| 2945 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. | 2996 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. |
| 2946 */ | 2997 */ |
| 2947 get _maxVirtualStart() { | 2998 get _maxVirtualStart() { |
| 2948 return Math.max(0, this._virtualCount - this._physicalCount); | 2999 return Math.max(0, this._virtualCount - this._physicalCount); |
| 2949 }, | 3000 }, |
| 2950 | 3001 |
| 2951 /** | 3002 /** |
| 2952 * The height of the physical content that isn't on the screen. | 3003 * The n-th item rendered in the `_physicalStart` tile. |
| 2953 */ | 3004 */ |
| 2954 get _hiddenContentSize() { | 3005 _virtualStartVal: 0, |
| 2955 return this._physicalSize - this._viewportSize; | 3006 |
| 3007 set _virtualStart(val) { |
| 3008 this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._min
VirtualStart, val)); |
| 3009 }, |
| 3010 |
| 3011 get _virtualStart() { |
| 3012 return this._virtualStartVal || 0; |
| 2956 }, | 3013 }, |
| 2957 | 3014 |
| 2958 /** | 3015 /** |
| 2959 * The maximum scroll top value. | 3016 * The k-th tile that is at the top of the scrolling list. |
| 2960 */ | 3017 */ |
| 2961 get _maxScrollTop() { | 3018 _physicalStartVal: 0, |
| 2962 return this._estScrollHeight - this._viewportSize; | 3019 |
| 3020 set _physicalStart(val) { |
| 3021 this._physicalStartVal = val % this._physicalCount; |
| 3022 if (this._physicalStartVal < 0) { |
| 3023 this._physicalStartVal = this._physicalCount + this._physicalStartVal; |
| 3024 } |
| 3025 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this
._physicalCount; |
| 3026 }, |
| 3027 |
| 3028 get _physicalStart() { |
| 3029 return this._physicalStartVal || 0; |
| 2963 }, | 3030 }, |
| 2964 | 3031 |
| 2965 /** | 3032 /** |
| 2966 * Sets the n-th item rendered in `_physicalStart` | 3033 * The number of tiles in the DOM. |
| 2967 */ | 3034 */ |
| 2968 set _virtualStart(val) { | 3035 _physicalCountVal: 0, |
| 2969 // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart | 3036 |
| 2970 this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._min
VirtualStart, val)); | 3037 set _physicalCount(val) { |
| 2971 if (this._physicalCount === 0) { | 3038 this._physicalCountVal = val; |
| 2972 this._physicalStart = 0; | 3039 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this
._physicalCount; |
| 2973 this._physicalEnd = 0; | 3040 }, |
| 2974 } else { | 3041 |
| 2975 this._physicalStart = this._virtualStartVal % this._physicalCount; | 3042 get _physicalCount() { |
| 2976 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % th
is._physicalCount; | 3043 return this._physicalCountVal; |
| 2977 } | |
| 2978 }, | 3044 }, |
| 2979 | 3045 |
| 2980 /** | 3046 /** |
| 2981 * Gets the n-th item rendered in `_physicalStart` | 3047 * The k-th tile that is at the bottom of the scrolling list. |
| 2982 */ | 3048 */ |
| 2983 get _virtualStart() { | 3049 _physicalEnd: 0, |
| 2984 return this._virtualStartVal; | |
| 2985 }, | |
| 2986 | 3050 |
| 2987 /** | 3051 /** |
| 2988 * An optimal physical size such that we will have enough physical items | 3052 * An optimal physical size such that we will have enough physical items |
| 2989 * to fill up the viewport and recycle when the user scrolls. | 3053 * to fill up the viewport and recycle when the user scrolls. |
| 2990 * | 3054 * |
| 2991 * This default value assumes that we will at least have the equivalent | 3055 * This default value assumes that we will at least have the equivalent |
| 2992 * to a viewport of physical items above and below the user's viewport. | 3056 * to a viewport of physical items above and below the user's viewport. |
| 2993 */ | 3057 */ |
| 2994 get _optPhysicalSize() { | 3058 get _optPhysicalSize() { |
| 2995 return this._viewportSize * this._maxPages; | 3059 return this._viewportSize * this._maxPages; |
| 2996 }, | 3060 }, |
| 2997 | 3061 |
| 2998 /** | 3062 /** |
| 2999 * True if the current list is visible. | 3063 * True if the current list is visible. |
| 3000 */ | 3064 */ |
| 3001 get _isVisible() { | 3065 get _isVisible() { |
| 3002 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); | 3066 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); |
| 3003 }, | 3067 }, |
| 3004 | 3068 |
| 3005 /** | 3069 /** |
| 3006 * Gets the index of the first visible item in the viewport. | 3070 * Gets the index of the first visible item in the viewport. |
| 3007 * | 3071 * |
| 3008 * @type {number} | 3072 * @type {number} |
| 3009 */ | 3073 */ |
| 3010 get firstVisibleIndex() { | 3074 get firstVisibleIndex() { |
| 3011 if (this._firstVisibleIndexVal === null) { | 3075 if (this._firstVisibleIndexVal === null) { |
| 3012 var physicalOffset = this._physicalTop; | 3076 var physicalOffset = this._physicalTop + this._scrollerPaddingTop; |
| 3013 | 3077 |
| 3014 this._firstVisibleIndexVal = this._iterateItems( | 3078 this._firstVisibleIndexVal = this._iterateItems( |
| 3015 function(pidx, vidx) { | 3079 function(pidx, vidx) { |
| 3016 physicalOffset += this._physicalSizes[pidx]; | 3080 physicalOffset += this._physicalSizes[pidx]; |
| 3017 | |
| 3018 if (physicalOffset > this._scrollPosition) { | 3081 if (physicalOffset > this._scrollPosition) { |
| 3019 return vidx; | 3082 return vidx; |
| 3020 } | 3083 } |
| 3021 }) || 0; | 3084 }) || 0; |
| 3022 } | 3085 } |
| 3023 return this._firstVisibleIndexVal; | 3086 return this._firstVisibleIndexVal; |
| 3024 }, | 3087 }, |
| 3025 | 3088 |
| 3026 /** | 3089 /** |
| 3027 * Gets the index of the last visible item in the viewport. | 3090 * Gets the index of the last visible item in the viewport. |
| 3028 * | 3091 * |
| 3029 * @type {number} | 3092 * @type {number} |
| 3030 */ | 3093 */ |
| 3031 get lastVisibleIndex() { | 3094 get lastVisibleIndex() { |
| 3032 if (this._lastVisibleIndexVal === null) { | 3095 if (this._lastVisibleIndexVal === null) { |
| 3033 var physicalOffset = this._physicalTop; | 3096 var physicalOffset = this._physicalTop; |
| 3034 | 3097 |
| 3035 this._iterateItems(function(pidx, vidx) { | 3098 this._iterateItems(function(pidx, vidx) { |
| 3036 physicalOffset += this._physicalSizes[pidx]; | 3099 physicalOffset += this._physicalSizes[pidx]; |
| 3037 | 3100 |
| 3038 if(physicalOffset <= this._scrollBottom) { | 3101 if (physicalOffset <= this._scrollBottom) { |
| 3039 this._lastVisibleIndexVal = vidx; | 3102 this._lastVisibleIndexVal = vidx; |
| 3040 } | 3103 } |
| 3041 }); | 3104 }); |
| 3042 } | 3105 } |
| 3043 return this._lastVisibleIndexVal; | 3106 return this._lastVisibleIndexVal; |
| 3044 }, | 3107 }, |
| 3045 | 3108 |
| 3109 get _defaultScrollTarget() { |
| 3110 return this; |
| 3111 }, |
| 3112 |
| 3046 ready: function() { | 3113 ready: function() { |
| 3047 this.addEventListener('focus', this._didFocus.bind(this), true); | 3114 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 3048 }, | 3115 }, |
| 3049 | 3116 |
| 3050 attached: function() { | 3117 attached: function() { |
| 3051 this.updateViewportBoundaries(); | 3118 this.updateViewportBoundaries(); |
| 3052 this._render(); | 3119 this._render(); |
| 3053 }, | 3120 }, |
| 3054 | 3121 |
| 3055 detached: function() { | 3122 detached: function() { |
| 3056 this._itemsRendered = false; | 3123 this._itemsRendered = false; |
| 3057 }, | 3124 }, |
| 3058 | 3125 |
| 3059 get _defaultScrollTarget() { | |
| 3060 return this; | |
| 3061 }, | |
| 3062 | |
| 3063 /** | 3126 /** |
| 3064 * Set the overflow property if this element has its own scrolling region | 3127 * Set the overflow property if this element has its own scrolling region |
| 3065 */ | 3128 */ |
| 3066 _setOverflow: function(scrollTarget) { | 3129 _setOverflow: function(scrollTarget) { |
| 3067 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; | 3130 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; |
| 3068 this.style.overflow = scrollTarget === this ? 'auto' : ''; | 3131 this.style.overflow = scrollTarget === this ? 'auto' : ''; |
| 3069 }, | 3132 }, |
| 3070 | 3133 |
| 3071 /** | 3134 /** |
| 3072 * Invoke this method if you dynamically update the viewport's | 3135 * Invoke this method if you dynamically update the viewport's |
| 3073 * size or CSS padding. | 3136 * size or CSS padding. |
| 3074 * | 3137 * |
| 3075 * @method updateViewportBoundaries | 3138 * @method updateViewportBoundaries |
| 3076 */ | 3139 */ |
| 3077 updateViewportBoundaries: function() { | 3140 updateViewportBoundaries: function() { |
| 3078 var scrollerStyle = window.getComputedStyle(this.scrollTarget); | 3141 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : |
| 3079 this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10); | 3142 parseInt(window.getComputedStyle(this)['padding-top'], 10); |
| 3143 |
| 3080 this._viewportSize = this._scrollTargetHeight; | 3144 this._viewportSize = this._scrollTargetHeight; |
| 3081 }, | 3145 }, |
| 3082 | 3146 |
| 3083 /** | 3147 /** |
| 3084 * Update the models, the position of the | 3148 * Update the models, the position of the |
| 3085 * items in the viewport and recycle tiles as needed. | 3149 * items in the viewport and recycle tiles as needed. |
| 3086 */ | 3150 */ |
| 3087 _scrollHandler: function() { | 3151 _scrollHandler: function() { |
| 3088 // clamp the `scrollTop` value | 3152 // clamp the `scrollTop` value |
| 3089 // IE 10|11 scrollTop may go above `_maxScrollTop` | |
| 3090 // iOS `scrollTop` may go below 0 and above `_maxScrollTop` | |
| 3091 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; | 3153 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; |
| 3154 var delta = scrollTop - this._scrollPosition; |
| 3092 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto
m; | 3155 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto
m; |
| 3093 var ratio = this._ratio; | 3156 var ratio = this._ratio; |
| 3094 var delta = scrollTop - this._scrollPosition; | |
| 3095 var recycledTiles = 0; | 3157 var recycledTiles = 0; |
| 3096 var hiddenContentSize = this._hiddenContentSize; | 3158 var hiddenContentSize = this._hiddenContentSize; |
| 3097 var currentRatio = ratio; | 3159 var currentRatio = ratio; |
| 3098 var movingUp = []; | 3160 var movingUp = []; |
| 3099 | 3161 |
| 3100 // track the last `scrollTop` | 3162 // track the last `scrollTop` |
| 3101 this._scrollPosition = scrollTop; | 3163 this._scrollPosition = scrollTop; |
| 3102 | 3164 |
| 3103 // clear cached visible index | 3165 // clear cached visible indexes |
| 3104 this._firstVisibleIndexVal = null; | 3166 this._firstVisibleIndexVal = null; |
| 3105 this._lastVisibleIndexVal = null; | 3167 this._lastVisibleIndexVal = null; |
| 3106 | 3168 |
| 3107 scrollBottom = this._scrollBottom; | 3169 scrollBottom = this._scrollBottom; |
| 3108 physicalBottom = this._physicalBottom; | 3170 physicalBottom = this._physicalBottom; |
| 3109 | 3171 |
| 3110 // random access | 3172 // random access |
| 3111 if (Math.abs(delta) > this._physicalSize) { | 3173 if (Math.abs(delta) > this._physicalSize) { |
| 3112 this._physicalTop += delta; | 3174 this._physicalTop += delta; |
| 3113 recycledTiles = Math.round(delta / this._physicalAverage); | 3175 recycledTiles = Math.round(delta / this._physicalAverage); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3180 | 3242 |
| 3181 if (recycledTiles === 0) { | 3243 if (recycledTiles === 0) { |
| 3182 // If the list ever reach this case, the physical average is not signifi
cant enough | 3244 // If the list ever reach this case, the physical average is not signifi
cant enough |
| 3183 // to create all the items needed to cover the entire viewport. | 3245 // to create all the items needed to cover the entire viewport. |
| 3184 // e.g. A few items have a height that differs from the average by serve
ral order of magnitude. | 3246 // e.g. A few items have a height that differs from the average by serve
ral order of magnitude. |
| 3185 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { | 3247 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
| 3186 this.async(this._increasePool.bind(this, 1)); | 3248 this.async(this._increasePool.bind(this, 1)); |
| 3187 } | 3249 } |
| 3188 } else { | 3250 } else { |
| 3189 this._virtualStart = this._virtualStart + recycledTiles; | 3251 this._virtualStart = this._virtualStart + recycledTiles; |
| 3252 this._physicalStart = this._physicalStart + recycledTiles; |
| 3190 this._update(recycledTileSet, movingUp); | 3253 this._update(recycledTileSet, movingUp); |
| 3191 } | 3254 } |
| 3192 }, | 3255 }, |
| 3193 | 3256 |
| 3194 /** | 3257 /** |
| 3195 * Update the list of items, starting from the `_virtualStart` item. | 3258 * Update the list of items, starting from the `_virtualStart` item. |
| 3196 * @param {!Array<number>=} itemSet | 3259 * @param {!Array<number>=} itemSet |
| 3197 * @param {!Array<number>=} movingUp | 3260 * @param {!Array<number>=} movingUp |
| 3198 */ | 3261 */ |
| 3199 _update: function(itemSet, movingUp) { | 3262 _update: function(itemSet, movingUp) { |
| 3200 // manage focus | 3263 // manage focus |
| 3201 if (this._isIndexRendered(this._focusedIndex)) { | 3264 this._manageFocus(); |
| 3202 this._restoreFocusedItem(); | |
| 3203 } else { | |
| 3204 this._createFocusBackfillItem(); | |
| 3205 } | |
| 3206 // update models | 3265 // update models |
| 3207 this._assignModels(itemSet); | 3266 this._assignModels(itemSet); |
| 3208 // measure heights | 3267 // measure heights |
| 3209 this._updateMetrics(itemSet); | 3268 this._updateMetrics(itemSet); |
| 3210 // adjust offset after measuring | 3269 // adjust offset after measuring |
| 3211 if (movingUp) { | 3270 if (movingUp) { |
| 3212 while (movingUp.length) { | 3271 while (movingUp.length) { |
| 3213 this._physicalTop -= this._physicalSizes[movingUp.pop()]; | 3272 this._physicalTop -= this._physicalSizes[movingUp.pop()]; |
| 3214 } | 3273 } |
| 3215 } | 3274 } |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3262 this._debounceTemplate(this._increasePool.bind(this, 1)); | 3321 this._debounceTemplate(this._increasePool.bind(this, 1)); |
| 3263 } | 3322 } |
| 3264 this._lastPage = currentPage; | 3323 this._lastPage = currentPage; |
| 3265 return true; | 3324 return true; |
| 3266 }, | 3325 }, |
| 3267 | 3326 |
| 3268 /** | 3327 /** |
| 3269 * Increases the pool size. | 3328 * Increases the pool size. |
| 3270 */ | 3329 */ |
| 3271 _increasePool: function(missingItems) { | 3330 _increasePool: function(missingItems) { |
| 3272 // limit the size | |
| 3273 var nextPhysicalCount = Math.min( | 3331 var nextPhysicalCount = Math.min( |
| 3274 this._physicalCount + missingItems, | 3332 this._physicalCount + missingItems, |
| 3275 this._virtualCount - this._virtualStart, | 3333 this._virtualCount - this._virtualStart, |
| 3276 MAX_PHYSICAL_COUNT | 3334 MAX_PHYSICAL_COUNT |
| 3277 ); | 3335 ); |
| 3278 var prevPhysicalCount = this._physicalCount; | 3336 var prevPhysicalCount = this._physicalCount; |
| 3279 var delta = nextPhysicalCount - prevPhysicalCount; | 3337 var delta = nextPhysicalCount - prevPhysicalCount; |
| 3280 | 3338 |
| 3281 if (delta > 0) { | 3339 if (delta <= 0) { |
| 3282 [].push.apply(this._physicalItems, this._createPool(delta)); | 3340 return; |
| 3283 [].push.apply(this._physicalSizes, new Array(delta)); | 3341 } |
| 3284 | 3342 |
| 3285 this._physicalCount = prevPhysicalCount + delta; | 3343 [].push.apply(this._physicalItems, this._createPool(delta)); |
| 3286 // tail call | 3344 [].push.apply(this._physicalSizes, new Array(delta)); |
| 3287 return this._update(); | 3345 |
| 3346 this._physicalCount = prevPhysicalCount + delta; |
| 3347 |
| 3348 // update the physical start if we need to preserve the model of the focus
ed item. |
| 3349 // In this situation, the focused item is currently rendered and its model
would |
| 3350 // have changed after increasing the pool if the physical start remained u
nchanged. |
| 3351 if (this._physicalStart > this._physicalEnd && |
| 3352 this._isIndexRendered(this._focusedIndex) && |
| 3353 this._getPhysicalIndex(this._focusedIndex) < this._physicalEnd) { |
| 3354 this._physicalStart = this._physicalStart + delta; |
| 3288 } | 3355 } |
| 3356 this._update(); |
| 3289 }, | 3357 }, |
| 3290 | 3358 |
| 3291 /** | 3359 /** |
| 3292 * Render a new list of items. This method does exactly the same as `update`
, | 3360 * Render a new list of items. This method does exactly the same as `update`
, |
| 3293 * but it also ensures that only one `update` cycle is created. | 3361 * but it also ensures that only one `update` cycle is created. |
| 3294 */ | 3362 */ |
| 3295 _render: function() { | 3363 _render: function() { |
| 3296 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; | 3364 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
| 3297 | 3365 |
| 3298 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { | 3366 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3367 _forwardParentPath: function(path, value) { | 3435 _forwardParentPath: function(path, value) { |
| 3368 if (this._physicalItems) { | 3436 if (this._physicalItems) { |
| 3369 this._physicalItems.forEach(function(item) { | 3437 this._physicalItems.forEach(function(item) { |
| 3370 item._templateInstance.notifyPath(path, value, true); | 3438 item._templateInstance.notifyPath(path, value, true); |
| 3371 }, this); | 3439 }, this); |
| 3372 } | 3440 } |
| 3373 }, | 3441 }, |
| 3374 | 3442 |
| 3375 /** | 3443 /** |
| 3376 * Called as a side effect of a host items.<key>.<path> path change, | 3444 * Called as a side effect of a host items.<key>.<path> path change, |
| 3377 * responsible for notifying item.<path> changes to row for key. | 3445 * responsible for notifying item.<path> changes. |
| 3378 */ | 3446 */ |
| 3379 _forwardItemPath: function(path, value) { | 3447 _forwardItemPath: function(path, value) { |
| 3380 if (this._physicalIndexForKey) { | 3448 if (!this._physicalIndexForKey) { |
| 3381 var dot = path.indexOf('.'); | 3449 return; |
| 3382 var key = path.substring(0, dot < 0 ? path.length : dot); | 3450 } |
| 3383 var idx = this._physicalIndexForKey[key]; | 3451 var inst; |
| 3384 var row = this._physicalItems[idx]; | 3452 var dot = path.indexOf('.'); |
| 3453 var key = path.substring(0, dot < 0 ? path.length : dot); |
| 3454 var idx = this._physicalIndexForKey[key]; |
| 3455 var el = this._physicalItems[idx]; |
| 3385 | 3456 |
| 3386 if (idx === this._focusedIndex && this._offscreenFocusedItem) { | 3457 |
| 3387 row = this._offscreenFocusedItem; | 3458 if (idx === this._focusedIndex && this._offscreenFocusedItem) { |
| 3388 } | 3459 el = this._offscreenFocusedItem; |
| 3389 if (row) { | 3460 } |
| 3390 var inst = row._templateInstance; | 3461 if (!el) { |
| 3391 if (dot >= 0) { | 3462 return; |
| 3392 path = this.as + '.' + path.substring(dot+1); | 3463 } |
| 3393 inst.notifyPath(path, value, true); | 3464 |
| 3394 } else { | 3465 inst = el._templateInstance; |
| 3395 inst[this.as] = value; | 3466 |
| 3396 } | 3467 if (inst.__key__ !== key) { |
| 3397 } | 3468 return; |
| 3469 } |
| 3470 if (dot >= 0) { |
| 3471 path = this.as + '.' + path.substring(dot+1); |
| 3472 inst.notifyPath(path, value, true); |
| 3473 } else { |
| 3474 inst[this.as] = value; |
| 3398 } | 3475 } |
| 3399 }, | 3476 }, |
| 3400 | 3477 |
| 3401 /** | 3478 /** |
| 3402 * Called when the items have changed. That is, ressignments | 3479 * Called when the items have changed. That is, ressignments |
| 3403 * to `items`, splices or updates to a single item. | 3480 * to `items`, splices or updates to a single item. |
| 3404 */ | 3481 */ |
| 3405 _itemsChanged: function(change) { | 3482 _itemsChanged: function(change) { |
| 3406 if (change.path === 'items') { | 3483 if (change.path === 'items') { |
| 3407 | 3484 // reset items |
| 3408 this._restoreFocusedItem(); | |
| 3409 // render the new set | |
| 3410 this._itemsRendered = false; | |
| 3411 // update the whole set | |
| 3412 this._virtualStart = 0; | 3485 this._virtualStart = 0; |
| 3413 this._physicalTop = 0; | 3486 this._physicalTop = 0; |
| 3414 this._virtualCount = this.items ? this.items.length : 0; | 3487 this._virtualCount = this.items ? this.items.length : 0; |
| 3415 this._focusedIndex = 0; | |
| 3416 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; | 3488 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; |
| 3417 this._physicalIndexForKey = {}; | 3489 this._physicalIndexForKey = {}; |
| 3418 | 3490 |
| 3419 this._resetScrollPosition(0); | 3491 this._resetScrollPosition(0); |
| 3492 this._removeFocusedItem(); |
| 3420 | 3493 |
| 3421 // create the initial physical items | 3494 // create the initial physical items |
| 3422 if (!this._physicalItems) { | 3495 if (!this._physicalItems) { |
| 3423 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); | 3496 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); |
| 3424 this._physicalItems = this._createPool(this._physicalCount); | 3497 this._physicalItems = this._createPool(this._physicalCount); |
| 3425 this._physicalSizes = new Array(this._physicalCount); | 3498 this._physicalSizes = new Array(this._physicalCount); |
| 3426 } | 3499 } |
| 3427 this._debounceTemplate(this._render); | 3500 |
| 3501 this._physicalStart = 0; |
| 3428 | 3502 |
| 3429 } else if (change.path === 'items.splices') { | 3503 } else if (change.path === 'items.splices') { |
| 3430 // render the new set | |
| 3431 this._itemsRendered = false; | |
| 3432 this._adjustVirtualIndex(change.value.indexSplices); | 3504 this._adjustVirtualIndex(change.value.indexSplices); |
| 3433 this._virtualCount = this.items ? this.items.length : 0; | 3505 this._virtualCount = this.items ? this.items.length : 0; |
| 3434 | 3506 |
| 3435 this._debounceTemplate(this._render); | |
| 3436 | |
| 3437 if (this._focusedIndex < 0 || this._focusedIndex >= this._virtualCount)
{ | |
| 3438 this._focusedIndex = 0; | |
| 3439 } | |
| 3440 this._debounceTemplate(this._render); | |
| 3441 | |
| 3442 } else { | 3507 } else { |
| 3443 // update a single item | 3508 // update a single item |
| 3444 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); | 3509 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); |
| 3510 return; |
| 3445 } | 3511 } |
| 3512 |
| 3513 this._itemsRendered = false; |
| 3514 this._debounceTemplate(this._render); |
| 3446 }, | 3515 }, |
| 3447 | 3516 |
| 3448 /** | 3517 /** |
| 3449 * @param {!Array<!PolymerSplice>} splices | 3518 * @param {!Array<!PolymerSplice>} splices |
| 3450 */ | 3519 */ |
| 3451 _adjustVirtualIndex: function(splices) { | 3520 _adjustVirtualIndex: function(splices) { |
| 3452 var i, splice, idx; | 3521 splices.forEach(function(splice) { |
| 3522 // deselect removed items |
| 3523 splice.removed.forEach(this._removeItem, this); |
| 3524 // We only need to care about changes happening above the current positi
on |
| 3525 if (splice.index < this._virtualStart) { |
| 3526 var delta = Math.max( |
| 3527 splice.addedCount - splice.removed.length, |
| 3528 splice.index - this._virtualStart); |
| 3453 | 3529 |
| 3454 for (i = 0; i < splices.length; i++) { | 3530 this._virtualStart = this._virtualStart + delta; |
| 3455 splice = splices[i]; | |
| 3456 | 3531 |
| 3457 // deselect removed items | 3532 if (this._focusedIndex >= 0) { |
| 3458 splice.removed.forEach(this.$.selector.deselect, this.$.selector); | 3533 this._focusedIndex = this._focusedIndex + delta; |
| 3534 } |
| 3535 } |
| 3536 }, this); |
| 3537 }, |
| 3459 | 3538 |
| 3460 idx = splice.index; | 3539 _removeItem: function(item) { |
| 3461 // We only need to care about changes happening above the current positi
on | 3540 this.$.selector.deselect(item); |
| 3462 if (idx >= this._virtualStart) { | 3541 // remove the current focused item |
| 3463 break; | 3542 if (this._focusedItem && this._focusedItem._templateInstance[this.as] ===
item) { |
| 3464 } | 3543 this._removeFocusedItem(); |
| 3465 | |
| 3466 this._virtualStart = this._virtualStart + | |
| 3467 Math.max(splice.addedCount - splice.removed.length, idx - this._virt
ualStart); | |
| 3468 } | 3544 } |
| 3469 }, | 3545 }, |
| 3470 | 3546 |
| 3471 /** | 3547 /** |
| 3472 * Executes a provided function per every physical index in `itemSet` | 3548 * Executes a provided function per every physical index in `itemSet` |
| 3473 * `itemSet` default value is equivalent to the entire set of physical index
es. | 3549 * `itemSet` default value is equivalent to the entire set of physical index
es. |
| 3474 * | 3550 * |
| 3475 * @param {!function(number, number)} fn | 3551 * @param {!function(number, number)} fn |
| 3476 * @param {!Array<number>=} itemSet | 3552 * @param {!Array<number>=} itemSet |
| 3477 */ | 3553 */ |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3510 /** | 3586 /** |
| 3511 * Assigns the data models to a given set of items. | 3587 * Assigns the data models to a given set of items. |
| 3512 * @param {!Array<number>=} itemSet | 3588 * @param {!Array<number>=} itemSet |
| 3513 */ | 3589 */ |
| 3514 _assignModels: function(itemSet) { | 3590 _assignModels: function(itemSet) { |
| 3515 this._iterateItems(function(pidx, vidx) { | 3591 this._iterateItems(function(pidx, vidx) { |
| 3516 var el = this._physicalItems[pidx]; | 3592 var el = this._physicalItems[pidx]; |
| 3517 var inst = el._templateInstance; | 3593 var inst = el._templateInstance; |
| 3518 var item = this.items && this.items[vidx]; | 3594 var item = this.items && this.items[vidx]; |
| 3519 | 3595 |
| 3520 if (item !== undefined && item !== null) { | 3596 if (item != null) { |
| 3521 inst[this.as] = item; | 3597 inst[this.as] = item; |
| 3522 inst.__key__ = this._collection.getKey(item); | 3598 inst.__key__ = this._collection.getKey(item); |
| 3523 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); | 3599 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); |
| 3524 inst[this.indexAs] = vidx; | 3600 inst[this.indexAs] = vidx; |
| 3525 inst.tabIndex = vidx === this._focusedIndex ? 0 : -1; | 3601 inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; |
| 3602 this._physicalIndexForKey[inst.__key__] = pidx; |
| 3526 el.removeAttribute('hidden'); | 3603 el.removeAttribute('hidden'); |
| 3527 this._physicalIndexForKey[inst.__key__] = pidx; | |
| 3528 } else { | 3604 } else { |
| 3529 inst.__key__ = null; | 3605 inst.__key__ = null; |
| 3530 el.setAttribute('hidden', ''); | 3606 el.setAttribute('hidden', ''); |
| 3531 } | 3607 } |
| 3532 | |
| 3533 }, itemSet); | 3608 }, itemSet); |
| 3534 }, | 3609 }, |
| 3535 | 3610 |
| 3536 /** | 3611 /** |
| 3537 * Updates the height for a given set of items. | 3612 * Updates the height for a given set of items. |
| 3538 * | 3613 * |
| 3539 * @param {!Array<number>=} itemSet | 3614 * @param {!Array<number>=} itemSet |
| 3540 */ | 3615 */ |
| 3541 _updateMetrics: function(itemSet) { | 3616 _updateMetrics: function(itemSet) { |
| 3542 // Make sure we distributed all the physical items | 3617 // Make sure we distributed all the physical items |
| (...skipping 27 matching lines...) Expand all Loading... |
| 3570 | 3645 |
| 3571 /** | 3646 /** |
| 3572 * Updates the position of the physical items. | 3647 * Updates the position of the physical items. |
| 3573 */ | 3648 */ |
| 3574 _positionItems: function() { | 3649 _positionItems: function() { |
| 3575 this._adjustScrollPosition(); | 3650 this._adjustScrollPosition(); |
| 3576 | 3651 |
| 3577 var y = this._physicalTop; | 3652 var y = this._physicalTop; |
| 3578 | 3653 |
| 3579 this._iterateItems(function(pidx) { | 3654 this._iterateItems(function(pidx) { |
| 3580 | |
| 3581 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); | 3655 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); |
| 3582 y += this._physicalSizes[pidx]; | 3656 y += this._physicalSizes[pidx]; |
| 3583 | |
| 3584 }); | 3657 }); |
| 3585 }, | 3658 }, |
| 3586 | 3659 |
| 3587 /** | 3660 /** |
| 3588 * Adjusts the scroll position when it was overestimated. | 3661 * Adjusts the scroll position when it was overestimated. |
| 3589 */ | 3662 */ |
| 3590 _adjustScrollPosition: function() { | 3663 _adjustScrollPosition: function() { |
| 3591 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : | 3664 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : |
| 3592 Math.min(this._scrollPosition + this._physicalTop, 0); | 3665 Math.min(this._scrollPosition + this._physicalTop, 0); |
| 3593 | 3666 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 3621 | 3694 |
| 3622 forceUpdate = forceUpdate || this._scrollHeight === 0; | 3695 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 3623 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; | 3696 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 3624 | 3697 |
| 3625 // amortize height adjustment, so it won't trigger repaints very often | 3698 // amortize height adjustment, so it won't trigger repaints very often |
| 3626 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { | 3699 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| 3627 this.$.items.style.height = this._estScrollHeight + 'px'; | 3700 this.$.items.style.height = this._estScrollHeight + 'px'; |
| 3628 this._scrollHeight = this._estScrollHeight; | 3701 this._scrollHeight = this._estScrollHeight; |
| 3629 } | 3702 } |
| 3630 }, | 3703 }, |
| 3631 | |
| 3632 /** | 3704 /** |
| 3633 * Scroll to a specific item in the virtual list regardless | 3705 * Scroll to a specific item in the virtual list regardless |
| 3634 * of the physical items in the DOM tree. | 3706 * of the physical items in the DOM tree. |
| 3635 * | 3707 * |
| 3636 * @method scrollToIndex | 3708 * @method scrollToIndex |
| 3637 * @param {number} idx The index of the item | 3709 * @param {number} idx The index of the item |
| 3638 */ | 3710 */ |
| 3639 scrollToIndex: function(idx) { | 3711 scrollToIndex: function(idx) { |
| 3640 if (typeof idx !== 'number') { | 3712 if (typeof idx !== 'number') { |
| 3641 return; | 3713 return; |
| 3642 } | 3714 } |
| 3643 | 3715 |
| 3644 Polymer.dom.flush(); | 3716 Polymer.dom.flush(); |
| 3645 | 3717 |
| 3646 var firstVisible = this.firstVisibleIndex; | |
| 3647 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); | 3718 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
| 3648 | 3719 // update the virtual start only when needed |
| 3649 // start at the previous virtual item | 3720 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { |
| 3650 // so we have a item above the first visible item | 3721 this._virtualStart = idx - 1; |
| 3651 this._virtualStart = idx - 1; | 3722 } |
| 3723 // manage focus |
| 3724 this._manageFocus(); |
| 3652 // assign new models | 3725 // assign new models |
| 3653 this._assignModels(); | 3726 this._assignModels(); |
| 3654 // measure the new sizes | 3727 // measure the new sizes |
| 3655 this._updateMetrics(); | 3728 this._updateMetrics(); |
| 3656 // estimate new physical offset | 3729 // estimate new physical offset |
| 3657 this._physicalTop = this._virtualStart * this._physicalAverage; | 3730 this._physicalTop = this._virtualStart * this._physicalAverage; |
| 3658 | 3731 |
| 3659 var currentTopItem = this._physicalStart; | 3732 var currentTopItem = this._physicalStart; |
| 3660 var currentVirtualItem = this._virtualStart; | 3733 var currentVirtualItem = this._virtualStart; |
| 3661 var targetOffsetTop = 0; | 3734 var targetOffsetTop = 0; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3704 this.updateViewportBoundaries(); | 3777 this.updateViewportBoundaries(); |
| 3705 this.scrollToIndex(this.firstVisibleIndex); | 3778 this.scrollToIndex(this.firstVisibleIndex); |
| 3706 } | 3779 } |
| 3707 }); | 3780 }); |
| 3708 }, | 3781 }, |
| 3709 | 3782 |
| 3710 _getModelFromItem: function(item) { | 3783 _getModelFromItem: function(item) { |
| 3711 var key = this._collection.getKey(item); | 3784 var key = this._collection.getKey(item); |
| 3712 var pidx = this._physicalIndexForKey[key]; | 3785 var pidx = this._physicalIndexForKey[key]; |
| 3713 | 3786 |
| 3714 if (pidx !== undefined) { | 3787 if (pidx != null) { |
| 3715 return this._physicalItems[pidx]._templateInstance; | 3788 return this._physicalItems[pidx]._templateInstance; |
| 3716 } | 3789 } |
| 3717 return null; | 3790 return null; |
| 3718 }, | 3791 }, |
| 3719 | 3792 |
| 3720 /** | 3793 /** |
| 3721 * Gets a valid item instance from its index or the object value. | 3794 * Gets a valid item instance from its index or the object value. |
| 3722 * | 3795 * |
| 3723 * @param {(Object|number)} item The item object or its index | 3796 * @param {(Object|number)} item The item object or its index |
| 3724 */ | 3797 */ |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3842 * Updates the size of an item. | 3915 * Updates the size of an item. |
| 3843 * | 3916 * |
| 3844 * @method updateSizeForItem | 3917 * @method updateSizeForItem |
| 3845 * @param {(Object|number)} item The item object or its index | 3918 * @param {(Object|number)} item The item object or its index |
| 3846 */ | 3919 */ |
| 3847 updateSizeForItem: function(item) { | 3920 updateSizeForItem: function(item) { |
| 3848 item = this._getNormalizedItem(item); | 3921 item = this._getNormalizedItem(item); |
| 3849 var key = this._collection.getKey(item); | 3922 var key = this._collection.getKey(item); |
| 3850 var pidx = this._physicalIndexForKey[key]; | 3923 var pidx = this._physicalIndexForKey[key]; |
| 3851 | 3924 |
| 3852 if (pidx !== undefined) { | 3925 if (pidx != null) { |
| 3853 this._updateMetrics([pidx]); | 3926 this._updateMetrics([pidx]); |
| 3854 this._positionItems(); | 3927 this._positionItems(); |
| 3855 } | 3928 } |
| 3856 }, | 3929 }, |
| 3857 | 3930 |
| 3931 /** |
| 3932 * Creates a temporary backfill item in the rendered pool of physical items |
| 3933 * to replace the main focused item. The focused item has tabIndex = 0 |
| 3934 * and might be currently focused by the user. |
| 3935 * |
| 3936 * This dynamic replacement helps to preserve the focus state. |
| 3937 */ |
| 3938 _manageFocus: function() { |
| 3939 var fidx = this._focusedIndex; |
| 3940 |
| 3941 if (fidx >= 0 && fidx < this._virtualCount) { |
| 3942 // if it's a valid index, check if that index is rendered |
| 3943 // in a physical item. |
| 3944 if (this._isIndexRendered(fidx)) { |
| 3945 this._restoreFocusedItem(); |
| 3946 } else { |
| 3947 this._createFocusBackfillItem(); |
| 3948 } |
| 3949 } else if (this._virtualCount > 0 && this._physicalCount > 0) { |
| 3950 // otherwise, assign the initial focused index. |
| 3951 this._focusedIndex = this._virtualStart; |
| 3952 this._focusedItem = this._physicalItems[this._physicalStart]; |
| 3953 } |
| 3954 }, |
| 3955 |
| 3858 _isIndexRendered: function(idx) { | 3956 _isIndexRendered: function(idx) { |
| 3859 return idx >= this._virtualStart && idx <= this._virtualEnd; | 3957 return idx >= this._virtualStart && idx <= this._virtualEnd; |
| 3860 }, | 3958 }, |
| 3861 | 3959 |
| 3862 _getPhysicalItemForIndex: function(idx, force) { | 3960 _isIndexVisible: function(idx) { |
| 3863 if (!this._collection) { | 3961 return idx >= this.firstVisibleIndex && idx <= this.lastVisibleIndex; |
| 3864 return null; | 3962 }, |
| 3865 } | |
| 3866 if (!this._isIndexRendered(idx)) { | |
| 3867 if (force) { | |
| 3868 this.scrollToIndex(idx); | |
| 3869 return this._getPhysicalItemForIndex(idx, false); | |
| 3870 } | |
| 3871 return null; | |
| 3872 } | |
| 3873 var item = this._getNormalizedItem(idx); | |
| 3874 var physicalItem = this._physicalItems[this._physicalIndexForKey[this._col
lection.getKey(item)]]; | |
| 3875 | 3963 |
| 3876 return physicalItem || null; | 3964 _getPhysicalIndex: function(idx) { |
| 3965 return this._physicalIndexForKey[this._collection.getKey(this._getNormaliz
edItem(idx))]; |
| 3877 }, | 3966 }, |
| 3878 | 3967 |
| 3879 _focusPhysicalItem: function(idx) { | 3968 _focusPhysicalItem: function(idx) { |
| 3880 this._restoreFocusedItem(); | 3969 if (idx < 0 || idx >= this._virtualCount) { |
| 3881 | |
| 3882 var physicalItem = this._getPhysicalItemForIndex(idx, true); | |
| 3883 if (!physicalItem) { | |
| 3884 return; | 3970 return; |
| 3885 } | 3971 } |
| 3972 this._restoreFocusedItem(); |
| 3973 // scroll to index to make sure it's rendered |
| 3974 if (!this._isIndexRendered(idx)) { |
| 3975 this.scrollToIndex(idx); |
| 3976 } |
| 3977 |
| 3978 var physicalItem = this._physicalItems[this._getPhysicalIndex(idx)]; |
| 3886 var SECRET = ~(Math.random() * 100); | 3979 var SECRET = ~(Math.random() * 100); |
| 3887 var model = physicalItem._templateInstance; | 3980 var model = physicalItem._templateInstance; |
| 3888 var focusable; | 3981 var focusable; |
| 3889 | 3982 |
| 3983 // set a secret tab index |
| 3890 model.tabIndex = SECRET; | 3984 model.tabIndex = SECRET; |
| 3891 // the focusable element could be the entire physical item | 3985 // check if focusable element is the physical item |
| 3892 if (physicalItem.tabIndex === SECRET) { | 3986 if (physicalItem.tabIndex === SECRET) { |
| 3893 focusable = physicalItem; | 3987 focusable = physicalItem; |
| 3894 } | 3988 } |
| 3895 // the focusable element could be somewhere within the physical item | 3989 // search for the element which tabindex is bound to the secret tab index |
| 3896 if (!focusable) { | 3990 if (!focusable) { |
| 3897 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET + '"]'); | 3991 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET + '"]'); |
| 3898 } | 3992 } |
| 3899 // restore the tab index | 3993 // restore the tab index |
| 3900 model.tabIndex = 0; | 3994 model.tabIndex = 0; |
| 3995 // focus the focusable element |
| 3996 this._focusedIndex = idx; |
| 3901 focusable && focusable.focus(); | 3997 focusable && focusable.focus(); |
| 3902 }, | 3998 }, |
| 3903 | 3999 |
| 3904 _restoreFocusedItem: function() { | 4000 _removeFocusedItem: function() { |
| 3905 if (!this._offscreenFocusedItem) { | 4001 if (this._offscreenFocusedItem) { |
| 3906 return; | 4002 Polymer.dom(this).removeChild(this._offscreenFocusedItem); |
| 3907 } | |
| 3908 var item = this._getNormalizedItem(this._focusedIndex); | |
| 3909 var pidx = this._physicalIndexForKey[this._collection.getKey(item)]; | |
| 3910 | |
| 3911 if (pidx !== undefined) { | |
| 3912 this.translate3d(0, HIDDEN_Y, 0, this._physicalItems[pidx]); | |
| 3913 this._physicalItems[pidx] = this._offscreenFocusedItem; | |
| 3914 } | 4003 } |
| 3915 this._offscreenFocusedItem = null; | 4004 this._offscreenFocusedItem = null; |
| 3916 }, | |
| 3917 | |
| 3918 _removeFocusedItem: function() { | |
| 3919 if (!this._offscreenFocusedItem) { | |
| 3920 return; | |
| 3921 } | |
| 3922 Polymer.dom(this).removeChild(this._offscreenFocusedItem); | |
| 3923 this._offscreenFocusedItem = null; | |
| 3924 this._focusBackfillItem = null; | 4005 this._focusBackfillItem = null; |
| 4006 this._focusedItem = null; |
| 4007 this._focusedIndex = -1; |
| 3925 }, | 4008 }, |
| 3926 | 4009 |
| 3927 _createFocusBackfillItem: function() { | 4010 _createFocusBackfillItem: function() { |
| 3928 if (this._offscreenFocusedItem) { | 4011 var pidx, fidx = this._focusedIndex; |
| 4012 if (this._offscreenFocusedItem || fidx < 0) { |
| 3929 return; | 4013 return; |
| 3930 } | 4014 } |
| 3931 var item = this._getNormalizedItem(this._focusedIndex); | |
| 3932 var pidx = this._physicalIndexForKey[this._collection.getKey(item)]; | |
| 3933 | |
| 3934 this._offscreenFocusedItem = this._physicalItems[pidx]; | |
| 3935 this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); | |
| 3936 | |
| 3937 if (!this._focusBackfillItem) { | 4015 if (!this._focusBackfillItem) { |
| 4016 // create a physical item, so that it backfills the focused item. |
| 3938 var stampedTemplate = this.stamp(null); | 4017 var stampedTemplate = this.stamp(null); |
| 3939 this._focusBackfillItem = stampedTemplate.root.querySelector('*'); | 4018 this._focusBackfillItem = stampedTemplate.root.querySelector('*'); |
| 3940 Polymer.dom(this).appendChild(stampedTemplate.root); | 4019 Polymer.dom(this).appendChild(stampedTemplate.root); |
| 3941 } | 4020 } |
| 3942 this._physicalItems[pidx] = this._focusBackfillItem; | 4021 // get the physical index for the focused index |
| 4022 pidx = this._getPhysicalIndex(fidx); |
| 4023 |
| 4024 if (pidx != null) { |
| 4025 // set the offcreen focused physical item |
| 4026 this._offscreenFocusedItem = this._physicalItems[pidx]; |
| 4027 // backfill the focused physical item |
| 4028 this._physicalItems[pidx] = this._focusBackfillItem; |
| 4029 // hide the focused physical |
| 4030 this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); |
| 4031 } |
| 4032 }, |
| 4033 |
| 4034 _restoreFocusedItem: function() { |
| 4035 var pidx, fidx = this._focusedIndex; |
| 4036 |
| 4037 if (!this._offscreenFocusedItem || this._focusedIndex < 0) { |
| 4038 return; |
| 4039 } |
| 4040 // assign models to the focused index |
| 4041 this._assignModels(); |
| 4042 // get the new physical index for the focused index |
| 4043 pidx = this._getPhysicalIndex(fidx); |
| 4044 |
| 4045 if (pidx != null) { |
| 4046 // flip the focus backfill |
| 4047 this._focusBackfillItem = this._physicalItems[pidx]; |
| 4048 // restore the focused physical item |
| 4049 this._physicalItems[pidx] = this._offscreenFocusedItem; |
| 4050 // reset the offscreen focused item |
| 4051 this._offscreenFocusedItem = null; |
| 4052 // hide the physical item that backfills |
| 4053 this.translate3d(0, HIDDEN_Y, 0, this._focusBackfillItem); |
| 4054 } |
| 3943 }, | 4055 }, |
| 3944 | 4056 |
| 3945 _didFocus: function(e) { | 4057 _didFocus: function(e) { |
| 3946 var targetModel = this.modelForElement(e.target); | 4058 var targetModel = this.modelForElement(e.target); |
| 4059 var focusedModel = this._focusedItem ? this._focusedItem._templateInstance
: null; |
| 4060 var hasOffscreenFocusedItem = this._offscreenFocusedItem !== null; |
| 3947 var fidx = this._focusedIndex; | 4061 var fidx = this._focusedIndex; |
| 3948 | 4062 |
| 3949 if (!targetModel) { | 4063 if (!targetModel || !focusedModel) { |
| 3950 return; | 4064 return; |
| 3951 } | 4065 } |
| 3952 this._restoreFocusedItem(); | 4066 if (focusedModel === targetModel) { |
| 3953 | 4067 // if the user focused the same item, then bring it into view if it's no
t visible |
| 3954 if (this.modelForElement(this._offscreenFocusedItem) === targetModel) { | 4068 if (!this._isIndexVisible(fidx)) { |
| 3955 this.scrollToIndex(fidx); | 4069 this.scrollToIndex(fidx); |
| 4070 } |
| 3956 } else { | 4071 } else { |
| 4072 this._restoreFocusedItem(); |
| 3957 // restore tabIndex for the currently focused item | 4073 // restore tabIndex for the currently focused item |
| 3958 this._getModelFromItem(this._getNormalizedItem(fidx)).tabIndex = -1; | 4074 focusedModel.tabIndex = -1; |
| 3959 // set the tabIndex for the next focused item | 4075 // set the tabIndex for the next focused item |
| 3960 targetModel.tabIndex = 0; | 4076 targetModel.tabIndex = 0; |
| 3961 fidx = /** @type {{index: number}} */(targetModel).index; | 4077 fidx = targetModel[this.indexAs]; |
| 3962 this._focusedIndex = fidx; | 4078 this._focusedIndex = fidx; |
| 3963 // bring the item into view | 4079 this._focusedItem = this._physicalItems[this._getPhysicalIndex(fidx)]; |
| 3964 if (fidx < this.firstVisibleIndex || fidx > this.lastVisibleIndex) { | 4080 |
| 3965 this.scrollToIndex(fidx); | 4081 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { |
| 3966 } else { | |
| 3967 this._update(); | 4082 this._update(); |
| 3968 } | 4083 } |
| 3969 } | 4084 } |
| 3970 }, | 4085 }, |
| 3971 | 4086 |
| 3972 _didMoveUp: function() { | 4087 _didMoveUp: function() { |
| 3973 this._focusPhysicalItem(Math.max(0, this._focusedIndex - 1)); | 4088 this._focusPhysicalItem(this._focusedIndex - 1); |
| 3974 }, | 4089 }, |
| 3975 | 4090 |
| 3976 _didMoveDown: function() { | 4091 _didMoveDown: function() { |
| 3977 this._focusPhysicalItem(Math.min(this._virtualCount, this._focusedIndex +
1)); | 4092 this._focusPhysicalItem(this._focusedIndex + 1); |
| 3978 }, | 4093 }, |
| 3979 | 4094 |
| 3980 _didEnter: function(e) { | 4095 _didEnter: function(e) { |
| 3981 // focus the currently focused physical item | |
| 3982 this._focusPhysicalItem(this._focusedIndex); | 4096 this._focusPhysicalItem(this._focusedIndex); |
| 3983 // toggle selection | 4097 this._selectionHandler(e.detail.keyboardEvent); |
| 3984 this._selectionHandler(/** @type {{keyboardEvent: Event}} */(e.detail).key
boardEvent); | |
| 3985 } | 4098 } |
| 3986 }); | 4099 }); |
| 3987 | 4100 |
| 3988 })(); | 4101 })(); |
| 3989 (function() { | 4102 (function() { |
| 3990 | 4103 |
| 3991 // monostate data | 4104 // monostate data |
| 3992 var metaDatas = {}; | 4105 var metaDatas = {}; |
| 3993 var metaArrays = {}; | 4106 var metaArrays = {}; |
| 3994 var singleton = null; | 4107 var singleton = null; |
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4294 */ | 4407 */ |
| 4295 src: { | 4408 src: { |
| 4296 type: String, | 4409 type: String, |
| 4297 observer: '_srcChanged' | 4410 observer: '_srcChanged' |
| 4298 }, | 4411 }, |
| 4299 | 4412 |
| 4300 /** | 4413 /** |
| 4301 * @type {!Polymer.IronMeta} | 4414 * @type {!Polymer.IronMeta} |
| 4302 */ | 4415 */ |
| 4303 _meta: { | 4416 _meta: { |
| 4304 value: Polymer.Base.create('iron-meta', {type: 'iconset'}) | 4417 value: Polymer.Base.create('iron-meta', {type: 'iconset'}), |
| 4418 observer: '_updateIcon' |
| 4305 } | 4419 } |
| 4306 | 4420 |
| 4307 }, | 4421 }, |
| 4308 | 4422 |
| 4309 _DEFAULT_ICONSET: 'icons', | 4423 _DEFAULT_ICONSET: 'icons', |
| 4310 | 4424 |
| 4311 _iconChanged: function(icon) { | 4425 _iconChanged: function(icon) { |
| 4312 var parts = (icon || '').split(':'); | 4426 var parts = (icon || '').split(':'); |
| 4313 this._iconName = parts.pop(); | 4427 this._iconName = parts.pop(); |
| 4314 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; | 4428 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; |
| 4315 this._updateIcon(); | 4429 this._updateIcon(); |
| 4316 }, | 4430 }, |
| 4317 | 4431 |
| 4318 _srcChanged: function(src) { | 4432 _srcChanged: function(src) { |
| 4319 this._updateIcon(); | 4433 this._updateIcon(); |
| 4320 }, | 4434 }, |
| 4321 | 4435 |
| 4322 _usesIconset: function() { | 4436 _usesIconset: function() { |
| 4323 return this.icon || !this.src; | 4437 return this.icon || !this.src; |
| 4324 }, | 4438 }, |
| 4325 | 4439 |
| 4326 /** @suppress {visibility} */ | 4440 /** @suppress {visibility} */ |
| 4327 _updateIcon: function() { | 4441 _updateIcon: function() { |
| 4328 if (this._usesIconset()) { | 4442 if (this._usesIconset()) { |
| 4329 if (this._iconsetName) { | 4443 if (this._img && this._img.parentNode) { |
| 4444 Polymer.dom(this.root).removeChild(this._img); |
| 4445 } |
| 4446 if (this._iconName === "") { |
| 4447 if (this._iconset) { |
| 4448 this._iconset.removeIcon(this); |
| 4449 } |
| 4450 } else if (this._iconsetName && this._meta) { |
| 4330 this._iconset = /** @type {?Polymer.Iconset} */ ( | 4451 this._iconset = /** @type {?Polymer.Iconset} */ ( |
| 4331 this._meta.byKey(this._iconsetName)); | 4452 this._meta.byKey(this._iconsetName)); |
| 4332 if (this._iconset) { | 4453 if (this._iconset) { |
| 4333 this._iconset.applyIcon(this, this._iconName, this.theme); | 4454 this._iconset.applyIcon(this, this._iconName, this.theme); |
| 4334 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); | 4455 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); |
| 4335 } else { | 4456 } else { |
| 4336 this.listen(window, 'iron-iconset-added', '_updateIcon'); | 4457 this.listen(window, 'iron-iconset-added', '_updateIcon'); |
| 4337 } | 4458 } |
| 4338 } | 4459 } |
| 4339 } else { | 4460 } else { |
| 4461 if (this._iconset) { |
| 4462 this._iconset.removeIcon(this); |
| 4463 } |
| 4340 if (!this._img) { | 4464 if (!this._img) { |
| 4341 this._img = document.createElement('img'); | 4465 this._img = document.createElement('img'); |
| 4342 this._img.style.width = '100%'; | 4466 this._img.style.width = '100%'; |
| 4343 this._img.style.height = '100%'; | 4467 this._img.style.height = '100%'; |
| 4344 this._img.draggable = false; | 4468 this._img.draggable = false; |
| 4345 } | 4469 } |
| 4346 this._img.src = this.src; | 4470 this._img.src = this.src; |
| 4347 Polymer.dom(this.root).appendChild(this._img); | 4471 Polymer.dom(this.root).appendChild(this._img); |
| 4348 } | 4472 } |
| 4349 } | 4473 } |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4598 } | 4722 } |
| 4599 }, | 4723 }, |
| 4600 | 4724 |
| 4601 _disabledChanged: function(disabled, old) { | 4725 _disabledChanged: function(disabled, old) { |
| 4602 this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); | 4726 this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); |
| 4603 this.style.pointerEvents = disabled ? 'none' : ''; | 4727 this.style.pointerEvents = disabled ? 'none' : ''; |
| 4604 if (disabled) { | 4728 if (disabled) { |
| 4605 this._oldTabIndex = this.tabIndex; | 4729 this._oldTabIndex = this.tabIndex; |
| 4606 this.focused = false; | 4730 this.focused = false; |
| 4607 this.tabIndex = -1; | 4731 this.tabIndex = -1; |
| 4732 this.blur(); |
| 4608 } else if (this._oldTabIndex !== undefined) { | 4733 } else if (this._oldTabIndex !== undefined) { |
| 4609 this.tabIndex = this._oldTabIndex; | 4734 this.tabIndex = this._oldTabIndex; |
| 4610 } | 4735 } |
| 4611 }, | 4736 }, |
| 4612 | 4737 |
| 4613 _changedControlState: function() { | 4738 _changedControlState: function() { |
| 4614 // _controlStateChanged is abstract, follow-on behaviors may implement it | 4739 // _controlStateChanged is abstract, follow-on behaviors may implement it |
| 4615 if (this._controlStateChanged) { | 4740 if (this._controlStateChanged) { |
| 4616 this._controlStateChanged(); | 4741 this._controlStateChanged(); |
| 4617 } | 4742 } |
| (...skipping 1710 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6328 | 6453 |
| 6329 /** | 6454 /** |
| 6330 * Sets the selection state for a given item to either selected or deselecte
d. | 6455 * Sets the selection state for a given item to either selected or deselecte
d. |
| 6331 * | 6456 * |
| 6332 * @method setItemSelected | 6457 * @method setItemSelected |
| 6333 * @param {*} item The item to select. | 6458 * @param {*} item The item to select. |
| 6334 * @param {boolean} isSelected True for selected, false for deselected. | 6459 * @param {boolean} isSelected True for selected, false for deselected. |
| 6335 */ | 6460 */ |
| 6336 setItemSelected: function(item, isSelected) { | 6461 setItemSelected: function(item, isSelected) { |
| 6337 if (item != null) { | 6462 if (item != null) { |
| 6338 if (isSelected) { | 6463 if (isSelected !== this.isSelected(item)) { |
| 6339 this.selection.push(item); | 6464 // proceed to update selection only if requested state differs from cu
rrent |
| 6340 } else { | 6465 if (isSelected) { |
| 6341 var i = this.selection.indexOf(item); | 6466 this.selection.push(item); |
| 6342 if (i >= 0) { | 6467 } else { |
| 6343 this.selection.splice(i, 1); | 6468 var i = this.selection.indexOf(item); |
| 6469 if (i >= 0) { |
| 6470 this.selection.splice(i, 1); |
| 6471 } |
| 6344 } | 6472 } |
| 6345 } | 6473 if (this.selectCallback) { |
| 6346 if (this.selectCallback) { | 6474 this.selectCallback(item, isSelected); |
| 6347 this.selectCallback(item, isSelected); | 6475 } |
| 6348 } | 6476 } |
| 6349 } | 6477 } |
| 6350 }, | 6478 }, |
| 6351 | 6479 |
| 6352 /** | 6480 /** |
| 6353 * Sets the selection state for a given item. If the `multi` property | 6481 * Sets the selection state for a given item. If the `multi` property |
| 6354 * is true, then the selected state of `item` will be toggled; otherwise | 6482 * is true, then the selected state of `item` will be toggled; otherwise |
| 6355 * the `item` will be selected. | 6483 * the `item` will be selected. |
| 6356 * | 6484 * |
| 6357 * @method select | 6485 * @method select |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6494 type: Object, | 6622 type: Object, |
| 6495 value: function() { | 6623 value: function() { |
| 6496 return { | 6624 return { |
| 6497 'template': 1 | 6625 'template': 1 |
| 6498 }; | 6626 }; |
| 6499 } | 6627 } |
| 6500 } | 6628 } |
| 6501 }, | 6629 }, |
| 6502 | 6630 |
| 6503 observers: [ | 6631 observers: [ |
| 6504 '_updateSelected(attrForSelected, selected)' | 6632 '_updateAttrForSelected(attrForSelected)', |
| 6633 '_updateSelected(selected)' |
| 6505 ], | 6634 ], |
| 6506 | 6635 |
| 6507 created: function() { | 6636 created: function() { |
| 6508 this._bindFilterItem = this._filterItem.bind(this); | 6637 this._bindFilterItem = this._filterItem.bind(this); |
| 6509 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | 6638 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); |
| 6510 }, | 6639 }, |
| 6511 | 6640 |
| 6512 attached: function() { | 6641 attached: function() { |
| 6513 this._observer = this._observeItems(this); | 6642 this._observer = this._observeItems(this); |
| 6514 this._updateItems(); | 6643 this._updateItems(); |
| 6515 if (!this._shouldUpdateSelection) { | 6644 if (!this._shouldUpdateSelection) { |
| 6516 this._updateSelected(this.attrForSelected,this.selected) | 6645 this._updateSelected(); |
| 6517 } | 6646 } |
| 6518 this._addListener(this.activateEvent); | 6647 this._addListener(this.activateEvent); |
| 6519 }, | 6648 }, |
| 6520 | 6649 |
| 6521 detached: function() { | 6650 detached: function() { |
| 6522 if (this._observer) { | 6651 if (this._observer) { |
| 6523 Polymer.dom(this).unobserveNodes(this._observer); | 6652 Polymer.dom(this).unobserveNodes(this._observer); |
| 6524 } | 6653 } |
| 6525 this._removeListener(this.activateEvent); | 6654 this._removeListener(this.activateEvent); |
| 6526 }, | 6655 }, |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6599 this._removeListener(old); | 6728 this._removeListener(old); |
| 6600 this._addListener(eventName); | 6729 this._addListener(eventName); |
| 6601 }, | 6730 }, |
| 6602 | 6731 |
| 6603 _updateItems: function() { | 6732 _updateItems: function() { |
| 6604 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); | 6733 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); |
| 6605 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); | 6734 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); |
| 6606 this._setItems(nodes); | 6735 this._setItems(nodes); |
| 6607 }, | 6736 }, |
| 6608 | 6737 |
| 6738 _updateAttrForSelected: function() { |
| 6739 if (this._shouldUpdateSelection) { |
| 6740 this.selected = this._indexToValue(this.indexOf(this.selectedItem));
|
| 6741 } |
| 6742 }, |
| 6743 |
| 6609 _updateSelected: function() { | 6744 _updateSelected: function() { |
| 6610 this._selectSelected(this.selected); | 6745 this._selectSelected(this.selected); |
| 6611 }, | 6746 }, |
| 6612 | 6747 |
| 6613 _selectSelected: function(selected) { | 6748 _selectSelected: function(selected) { |
| 6614 this._selection.select(this._valueToItem(this.selected)); | 6749 this._selection.select(this._valueToItem(this.selected)); |
| 6615 }, | 6750 }, |
| 6616 | 6751 |
| 6617 _filterItem: function(node) { | 6752 _filterItem: function(node) { |
| 6618 return !this._excludedLocalNames[node.localName]; | 6753 return !this._excludedLocalNames[node.localName]; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6668 // observe items change under the given node. | 6803 // observe items change under the given node. |
| 6669 _observeItems: function(node) { | 6804 _observeItems: function(node) { |
| 6670 return Polymer.dom(node).observeNodes(function(mutations) { | 6805 return Polymer.dom(node).observeNodes(function(mutations) { |
| 6671 this._updateItems(); | 6806 this._updateItems(); |
| 6672 | 6807 |
| 6673 if (this._shouldUpdateSelection) { | 6808 if (this._shouldUpdateSelection) { |
| 6674 this._updateSelected(); | 6809 this._updateSelected(); |
| 6675 } | 6810 } |
| 6676 | 6811 |
| 6677 // Let other interested parties know about the change so that | 6812 // Let other interested parties know about the change so that |
| 6678 // we don't have to recreate mutation observers everywher. | 6813 // we don't have to recreate mutation observers everywhere. |
| 6679 this.fire('iron-items-changed', mutations, { | 6814 this.fire('iron-items-changed', mutations, { |
| 6680 bubbles: false, | 6815 bubbles: false, |
| 6681 cancelable: false | 6816 cancelable: false |
| 6682 }); | 6817 }); |
| 6683 }); | 6818 }); |
| 6684 }, | 6819 }, |
| 6685 | 6820 |
| 6686 _activateHandler: function(e) { | 6821 _activateHandler: function(e) { |
| 6687 var t = e.target; | 6822 var t = e.target; |
| 6688 var items = this.items; | 6823 var items = this.items; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6732 */ | 6867 */ |
| 6733 selectedItems: { | 6868 selectedItems: { |
| 6734 type: Array, | 6869 type: Array, |
| 6735 readOnly: true, | 6870 readOnly: true, |
| 6736 notify: true | 6871 notify: true |
| 6737 }, | 6872 }, |
| 6738 | 6873 |
| 6739 }, | 6874 }, |
| 6740 | 6875 |
| 6741 observers: [ | 6876 observers: [ |
| 6742 '_updateSelected(attrForSelected, selectedValues)' | 6877 '_updateSelected(selectedValues)' |
| 6743 ], | 6878 ], |
| 6744 | 6879 |
| 6745 /** | 6880 /** |
| 6746 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 6881 * Selects the given value. If the `multi` property is true, then the select
ed state of the |
| 6747 * `value` will be toggled; otherwise the `value` will be selected. | 6882 * `value` will be toggled; otherwise the `value` will be selected. |
| 6748 * | 6883 * |
| 6749 * @method select | 6884 * @method select |
| 6750 * @param {string|number} value the value to select. | 6885 * @param {string|number} value the value to select. |
| 6751 */ | 6886 */ |
| 6752 select: function(value) { | 6887 select: function(value) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 6763 | 6898 |
| 6764 multiChanged: function(multi) { | 6899 multiChanged: function(multi) { |
| 6765 this._selection.multi = multi; | 6900 this._selection.multi = multi; |
| 6766 }, | 6901 }, |
| 6767 | 6902 |
| 6768 get _shouldUpdateSelection() { | 6903 get _shouldUpdateSelection() { |
| 6769 return this.selected != null || | 6904 return this.selected != null || |
| 6770 (this.selectedValues != null && this.selectedValues.length); | 6905 (this.selectedValues != null && this.selectedValues.length); |
| 6771 }, | 6906 }, |
| 6772 | 6907 |
| 6908 _updateAttrForSelected: function() { |
| 6909 if (!this.multi) { |
| 6910 Polymer.IronSelectableBehavior._updateAttrForSelected.apply(this); |
| 6911 } else if (this._shouldUpdateSelection) { |
| 6912 this.selectedValues = this.selectedItems.map(function(selectedItem) { |
| 6913 return this._indexToValue(this.indexOf(selectedItem)); |
| 6914 }, this).filter(function(unfilteredValue) { |
| 6915 return unfilteredValue != null; |
| 6916 }, this); |
| 6917 } |
| 6918 }, |
| 6919 |
| 6773 _updateSelected: function() { | 6920 _updateSelected: function() { |
| 6774 if (this.multi) { | 6921 if (this.multi) { |
| 6775 this._selectMulti(this.selectedValues); | 6922 this._selectMulti(this.selectedValues); |
| 6776 } else { | 6923 } else { |
| 6777 this._selectSelected(this.selected); | 6924 this._selectSelected(this.selected); |
| 6778 } | 6925 } |
| 6779 }, | 6926 }, |
| 6780 | 6927 |
| 6781 _selectMulti: function(values) { | 6928 _selectMulti: function(values) { |
| 6782 this._selection.clear(); | |
| 6783 if (values) { | 6929 if (values) { |
| 6784 for (var i = 0; i < values.length; i++) { | 6930 var selectedItems = this._valuesToItems(values); |
| 6785 this._selection.setItemSelected(this._valueToItem(values[i]), true); | 6931 // clear all but the current selected items |
| 6932 this._selection.clear(selectedItems); |
| 6933 // select only those not selected yet |
| 6934 for (var i = 0; i < selectedItems.length; i++) { |
| 6935 this._selection.setItemSelected(selectedItems[i], true); |
| 6786 } | 6936 } |
| 6937 } else { |
| 6938 this._selection.clear(); |
| 6787 } | 6939 } |
| 6788 }, | 6940 }, |
| 6789 | 6941 |
| 6790 _selectionChange: function() { | 6942 _selectionChange: function() { |
| 6791 var s = this._selection.get(); | 6943 var s = this._selection.get(); |
| 6792 if (this.multi) { | 6944 if (this.multi) { |
| 6793 this._setSelectedItems(s); | 6945 this._setSelectedItems(s); |
| 6794 } else { | 6946 } else { |
| 6795 this._setSelectedItems([s]); | 6947 this._setSelectedItems([s]); |
| 6796 this._setSelectedItem(s); | 6948 this._setSelectedItem(s); |
| 6797 } | 6949 } |
| 6798 }, | 6950 }, |
| 6799 | 6951 |
| 6800 _toggleSelected: function(value) { | 6952 _toggleSelected: function(value) { |
| 6801 var i = this.selectedValues.indexOf(value); | 6953 var i = this.selectedValues.indexOf(value); |
| 6802 var unselected = i < 0; | 6954 var unselected = i < 0; |
| 6803 if (unselected) { | 6955 if (unselected) { |
| 6804 this.push('selectedValues',value); | 6956 this.push('selectedValues',value); |
| 6805 } else { | 6957 } else { |
| 6806 this.splice('selectedValues',i,1); | 6958 this.splice('selectedValues',i,1); |
| 6807 } | 6959 } |
| 6808 this._selection.setItemSelected(this._valueToItem(value), unselected); | 6960 this._selection.setItemSelected(this._valueToItem(value), unselected); |
| 6961 }, |
| 6962 |
| 6963 _valuesToItems: function(values) { |
| 6964 return (values == null) ? null : values.map(function(value) { |
| 6965 return this._valueToItem(value); |
| 6966 }, this); |
| 6809 } | 6967 } |
| 6810 }; | 6968 }; |
| 6811 | 6969 |
| 6812 /** @polymerBehavior */ | 6970 /** @polymerBehavior */ |
| 6813 Polymer.IronMultiSelectableBehavior = [ | 6971 Polymer.IronMultiSelectableBehavior = [ |
| 6814 Polymer.IronSelectableBehavior, | 6972 Polymer.IronSelectableBehavior, |
| 6815 Polymer.IronMultiSelectableBehaviorImpl | 6973 Polymer.IronMultiSelectableBehaviorImpl |
| 6816 ]; | 6974 ]; |
| 6817 /** | 6975 /** |
| 6818 * `Polymer.IronMenuBehavior` implements accessible menu behavior. | 6976 * `Polymer.IronMenuBehavior` implements accessible menu behavior. |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7032 * Handler that is called when the menu receives focus. | 7190 * Handler that is called when the menu receives focus. |
| 7033 * | 7191 * |
| 7034 * @param {FocusEvent} event A focus event. | 7192 * @param {FocusEvent} event A focus event. |
| 7035 */ | 7193 */ |
| 7036 _onFocus: function(event) { | 7194 _onFocus: function(event) { |
| 7037 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { | 7195 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { |
| 7038 // do not focus the menu itself | 7196 // do not focus the menu itself |
| 7039 return; | 7197 return; |
| 7040 } | 7198 } |
| 7041 | 7199 |
| 7200 // Do not focus the selected tab if the deepest target is part of the |
| 7201 // menu element's local DOM and is focusable. |
| 7202 var rootTarget = /** @type {?HTMLElement} */( |
| 7203 Polymer.dom(event).rootTarget); |
| 7204 if (rootTarget !== this && typeof rootTarget.tabIndex !== "undefined" && !
this.isLightDescendant(rootTarget)) { |
| 7205 return; |
| 7206 } |
| 7207 |
| 7042 this.blur(); | 7208 this.blur(); |
| 7043 | 7209 |
| 7044 // clear the cached focus item | 7210 // clear the cached focus item |
| 7045 this._defaultFocusAsync = this.async(function() { | 7211 this._defaultFocusAsync = this.async(function() { |
| 7046 // focus the selected item when the menu receives focus, or the first it
em | 7212 // focus the selected item when the menu receives focus, or the first it
em |
| 7047 // if no item is selected | 7213 // if no item is selected |
| 7048 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | 7214 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; |
| 7049 | 7215 |
| 7050 this._setFocusedItem(null); | 7216 this._setFocusedItem(null); |
| 7051 | 7217 |
| 7052 if (selectedItem) { | 7218 if (selectedItem) { |
| 7053 this._setFocusedItem(selectedItem); | 7219 this._setFocusedItem(selectedItem); |
| 7054 } else { | 7220 } else { |
| 7055 this._setFocusedItem(this.items[0]); | 7221 this._setFocusedItem(this.items[0]); |
| 7056 } | 7222 } |
| 7057 // async 1ms to wait for `select` to get called from `_itemActivate` | 7223 // async 1ms to wait for `select` to get called from `_itemActivate` |
| 7058 }, 1); | 7224 }, 1); |
| 7059 }, | 7225 }, |
| 7060 | 7226 |
| 7061 /** | 7227 /** |
| 7062 * Handler that is called when the up key is pressed. | 7228 * Handler that is called when the up key is pressed. |
| 7063 * | 7229 * |
| 7064 * @param {CustomEvent} event A key combination event. | 7230 * @param {CustomEvent} event A key combination event. |
| 7065 */ | 7231 */ |
| 7066 _onUpKey: function(event) { | 7232 _onUpKey: function(event) { |
| 7067 // up and down arrows moves the focus | 7233 // up and down arrows moves the focus |
| 7068 this._focusPrevious(); | 7234 this._focusPrevious(); |
| 7235 event.detail.keyboardEvent.preventDefault(); |
| 7069 }, | 7236 }, |
| 7070 | 7237 |
| 7071 /** | 7238 /** |
| 7072 * Handler that is called when the down key is pressed. | 7239 * Handler that is called when the down key is pressed. |
| 7073 * | 7240 * |
| 7074 * @param {CustomEvent} event A key combination event. | 7241 * @param {CustomEvent} event A key combination event. |
| 7075 */ | 7242 */ |
| 7076 _onDownKey: function(event) { | 7243 _onDownKey: function(event) { |
| 7077 this._focusNext(); | 7244 this._focusNext(); |
| 7245 event.detail.keyboardEvent.preventDefault(); |
| 7078 }, | 7246 }, |
| 7079 | 7247 |
| 7080 /** | 7248 /** |
| 7081 * Handler that is called when the esc key is pressed. | 7249 * Handler that is called when the esc key is pressed. |
| 7082 * | 7250 * |
| 7083 * @param {CustomEvent} event A key combination event. | 7251 * @param {CustomEvent} event A key combination event. |
| 7084 */ | 7252 */ |
| 7085 _onEscKey: function(event) { | 7253 _onEscKey: function(event) { |
| 7086 // esc blurs the control | 7254 // esc blurs the control |
| 7087 this.focusedItem.blur(); | 7255 this.focusedItem.blur(); |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7276 left: parseInt(target.marginLeft, 10) || 0 | 7444 left: parseInt(target.marginLeft, 10) || 0 |
| 7277 } | 7445 } |
| 7278 }; | 7446 }; |
| 7279 }, | 7447 }, |
| 7280 | 7448 |
| 7281 /** | 7449 /** |
| 7282 * Resets the target element's position and size constraints, and clear | 7450 * Resets the target element's position and size constraints, and clear |
| 7283 * the memoized data. | 7451 * the memoized data. |
| 7284 */ | 7452 */ |
| 7285 resetFit: function() { | 7453 resetFit: function() { |
| 7454 if (!this._fitInfo || !this._fitInfo.sizedBy.width) { |
| 7455 this.sizingTarget.style.maxWidth = ''; |
| 7456 } |
| 7286 if (!this._fitInfo || !this._fitInfo.sizedBy.height) { | 7457 if (!this._fitInfo || !this._fitInfo.sizedBy.height) { |
| 7287 this.sizingTarget.style.maxHeight = ''; | 7458 this.sizingTarget.style.maxHeight = ''; |
| 7288 this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : ''; | |
| 7289 } | 7459 } |
| 7290 if (!this._fitInfo || !this._fitInfo.sizedBy.width) { | 7460 this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : ''; |
| 7291 this.sizingTarget.style.maxWidth = ''; | 7461 this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : ''; |
| 7292 this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : ''; | |
| 7293 } | |
| 7294 if (this._fitInfo) { | 7462 if (this._fitInfo) { |
| 7295 this.style.position = this._fitInfo.positionedBy.css; | 7463 this.style.position = this._fitInfo.positionedBy.css; |
| 7296 } | 7464 } |
| 7297 this._fitInfo = null; | 7465 this._fitInfo = null; |
| 7298 }, | 7466 }, |
| 7299 | 7467 |
| 7300 /** | 7468 /** |
| 7301 * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
the element, | 7469 * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
the element, |
| 7302 * the window, or the `fitInfo` element has been resized. | 7470 * the window, or the `fitInfo` element has been resized. |
| 7303 */ | 7471 */ |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7344 var offsetExtent = 'offset' + extent; | 7512 var offsetExtent = 'offset' + extent; |
| 7345 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; | 7513 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; |
| 7346 this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingO
ffset) + 'px'; | 7514 this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingO
ffset) + 'px'; |
| 7347 }, | 7515 }, |
| 7348 | 7516 |
| 7349 /** | 7517 /** |
| 7350 * Centers horizontally and vertically if not already positioned. This also
sets | 7518 * Centers horizontally and vertically if not already positioned. This also
sets |
| 7351 * `position:fixed`. | 7519 * `position:fixed`. |
| 7352 */ | 7520 */ |
| 7353 center: function() { | 7521 center: function() { |
| 7354 if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.
horizontally) { | 7522 var positionedBy = this._fitInfo.positionedBy; |
| 7355 // need position:fixed to center | 7523 if (positionedBy.vertically && positionedBy.horizontally) { |
| 7356 this.style.position = 'fixed'; | 7524 // Already positioned. |
| 7525 return; |
| 7357 } | 7526 } |
| 7358 if (!this._fitInfo.positionedBy.vertically) { | 7527 // Need position:fixed to center |
| 7359 var top = (this._fitHeight - this.offsetHeight) / 2 + this._fitTop; | 7528 this.style.position = 'fixed'; |
| 7360 top -= this._fitInfo.margin.top; | 7529 // Take into account the offset caused by parents that create stacking |
| 7530 // contexts (e.g. with transform: translate3d). Translate to 0,0 and |
| 7531 // measure the bounding rect. |
| 7532 if (!positionedBy.vertically) { |
| 7533 this.style.top = '0px'; |
| 7534 } |
| 7535 if (!positionedBy.horizontally) { |
| 7536 this.style.left = '0px'; |
| 7537 } |
| 7538 // It will take in consideration margins and transforms |
| 7539 var rect = this.getBoundingClientRect(); |
| 7540 if (!positionedBy.vertically) { |
| 7541 var top = this._fitTop - rect.top + (this._fitHeight - rect.height) / 2; |
| 7361 this.style.top = top + 'px'; | 7542 this.style.top = top + 'px'; |
| 7362 } | 7543 } |
| 7363 if (!this._fitInfo.positionedBy.horizontally) { | 7544 if (!positionedBy.horizontally) { |
| 7364 var left = (this._fitWidth - this.offsetWidth) / 2 + this._fitLeft; | 7545 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; |
| 7365 left -= this._fitInfo.margin.left; | |
| 7366 this.style.left = left + 'px'; | 7546 this.style.left = left + 'px'; |
| 7367 } | 7547 } |
| 7368 } | 7548 } |
| 7369 | 7549 |
| 7370 }; | 7550 }; |
| 7371 /** | 7551 /** |
| 7372 * @struct | 7552 * @struct |
| 7373 * @constructor | 7553 * @constructor |
| 7374 */ | 7554 */ |
| 7375 Polymer.IronOverlayManagerClass = function() { | 7555 Polymer.IronOverlayManagerClass = function() { |
| 7376 this._overlays = []; | 7556 this._overlays = []; |
| 7557 // Used to keep track of the last focused node before an overlay gets opened
. |
| 7558 this._lastFocusedNodes = []; |
| 7559 |
| 7377 /** | 7560 /** |
| 7378 * iframes have a default z-index of 100, so this default should be at least | 7561 * iframes have a default z-index of 100, so this default should be at least |
| 7379 * that. | 7562 * that. |
| 7380 * @private {number} | 7563 * @private {number} |
| 7381 */ | 7564 */ |
| 7382 this._minimumZ = 101; | 7565 this._minimumZ = 101; |
| 7383 | 7566 |
| 7384 this._backdrops = []; | 7567 this._backdrops = []; |
| 7385 } | 7568 |
| 7569 this._backdropElement = null; |
| 7570 Object.defineProperty(this, 'backdropElement', { |
| 7571 get: function() { |
| 7572 if (!this._backdropElement) { |
| 7573 this._backdropElement = document.createElement('iron-overlay-backdrop'
); |
| 7574 } |
| 7575 return this._backdropElement; |
| 7576 }.bind(this) |
| 7577 }); |
| 7578 |
| 7579 /** |
| 7580 * The deepest active element. |
| 7581 * returns {?Node} element the active element |
| 7582 */ |
| 7583 this.deepActiveElement = null; |
| 7584 Object.defineProperty(this, 'deepActiveElement', { |
| 7585 get: function() { |
| 7586 var active = document.activeElement; |
| 7587 // document.activeElement can be null |
| 7588 // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeEleme
nt |
| 7589 while (active && active.root && Polymer.dom(active.root).activeElement)
{ |
| 7590 active = Polymer.dom(active.root).activeElement; |
| 7591 } |
| 7592 return active; |
| 7593 }.bind(this) |
| 7594 }); |
| 7595 }; |
| 7596 |
| 7597 /** |
| 7598 * If a node is contained in an overlay. |
| 7599 * @private |
| 7600 * @param {Node} node |
| 7601 * @returns {Boolean} |
| 7602 */ |
| 7603 Polymer.IronOverlayManagerClass.prototype._isChildOfOverlay = function(node) { |
| 7604 while (node && node !== document.body) { |
| 7605 // Use logical parentNode, or native ShadowRoot host. |
| 7606 node = Polymer.dom(node).parentNode || node.host; |
| 7607 // Check if it is an overlay. |
| 7608 if (node && node.behaviors && node.behaviors.indexOf(Polymer.IronOverlayBe
haviorImpl) !== -1) { |
| 7609 return true; |
| 7610 } |
| 7611 } |
| 7612 return false; |
| 7613 }; |
| 7386 | 7614 |
| 7387 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { | 7615 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { |
| 7388 this._setZ(overlay, aboveZ + 2); | 7616 this._setZ(overlay, aboveZ + 2); |
| 7389 }; | 7617 }; |
| 7390 | 7618 |
| 7391 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { | 7619 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { |
| 7392 element.style.zIndex = z; | 7620 element.style.zIndex = z; |
| 7393 }; | 7621 }; |
| 7394 | 7622 |
| 7395 /** | 7623 /** |
| 7396 * track overlays for z-index and focus managemant | 7624 * track overlays for z-index and focus managemant |
| 7397 */ | 7625 */ |
| 7398 Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { | 7626 Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { |
| 7399 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | 7627 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
| 7400 this._overlays.push(overlay); | 7628 this._overlays.push(overlay); |
| 7401 var newZ = this.currentOverlayZ(); | 7629 var newZ = this.currentOverlayZ(); |
| 7402 if (newZ <= minimumZ) { | 7630 if (newZ <= minimumZ) { |
| 7403 this._applyOverlayZ(overlay, minimumZ); | 7631 this._applyOverlayZ(overlay, minimumZ); |
| 7404 } | 7632 } |
| 7633 var element = this.deepActiveElement; |
| 7634 // If already in other overlay, don't reset focus there. |
| 7635 if (this._isChildOfOverlay(element)) { |
| 7636 element = null; |
| 7637 } |
| 7638 this._lastFocusedNodes.push(element); |
| 7405 }; | 7639 }; |
| 7406 | 7640 |
| 7407 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { | 7641 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { |
| 7408 var i = this._overlays.indexOf(overlay); | 7642 var i = this._overlays.indexOf(overlay); |
| 7409 if (i >= 0) { | 7643 if (i >= 0) { |
| 7410 this._overlays.splice(i, 1); | 7644 this._overlays.splice(i, 1); |
| 7411 this._setZ(overlay, ''); | 7645 this._setZ(overlay, ''); |
| 7646 |
| 7647 var node = this._lastFocusedNodes[i]; |
| 7648 // Focus only if still contained in document.body |
| 7649 if (overlay.restoreFocusOnClose && node && Polymer.dom(document.body).deep
Contains(node)) { |
| 7650 node.focus(); |
| 7651 } |
| 7652 this._lastFocusedNodes.splice(i, 1); |
| 7412 } | 7653 } |
| 7413 }; | 7654 }; |
| 7414 | 7655 |
| 7415 Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { | 7656 Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { |
| 7416 var i = this._overlays.length - 1; | 7657 var i = this._overlays.length - 1; |
| 7417 while (this._overlays[i] && !this._overlays[i].opened) { | 7658 while (this._overlays[i] && !this._overlays[i].opened) { |
| 7418 --i; | 7659 --i; |
| 7419 } | 7660 } |
| 7420 return this._overlays[i]; | 7661 return this._overlays[i]; |
| 7421 }; | 7662 }; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7456 if (element.opened && element.withBackdrop) { | 7697 if (element.opened && element.withBackdrop) { |
| 7457 // no duplicates | 7698 // no duplicates |
| 7458 if (index === -1) { | 7699 if (index === -1) { |
| 7459 this._backdrops.push(element); | 7700 this._backdrops.push(element); |
| 7460 } | 7701 } |
| 7461 } else if (index >= 0) { | 7702 } else if (index >= 0) { |
| 7462 this._backdrops.splice(index, 1); | 7703 this._backdrops.splice(index, 1); |
| 7463 } | 7704 } |
| 7464 }; | 7705 }; |
| 7465 | 7706 |
| 7466 Object.defineProperty(Polymer.IronOverlayManagerClass.prototype, "backdropElem
ent", { | |
| 7467 get: function() { | |
| 7468 if (!this._backdropElement) { | |
| 7469 this._backdropElement = document.createElement('iron-overlay-backdrop'); | |
| 7470 } | |
| 7471 return this._backdropElement; | |
| 7472 } | |
| 7473 }); | |
| 7474 | |
| 7475 Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { | 7707 Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { |
| 7476 return this._backdrops; | 7708 return this._backdrops; |
| 7477 }; | 7709 }; |
| 7478 | 7710 |
| 7479 /** | 7711 /** |
| 7480 * Returns the z-index for the backdrop. | 7712 * Returns the z-index for the backdrop. |
| 7481 */ | 7713 */ |
| 7482 Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { | 7714 Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { |
| 7483 return this._getOverlayZ(this._overlayWithBackdrop()) - 1; | 7715 return this._getOverlayZ(this._overlayWithBackdrop()) - 1; |
| 7484 }; | 7716 }; |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7694 | 7926 |
| 7695 /** | 7927 /** |
| 7696 * Returns the reason this dialog was last closed. | 7928 * Returns the reason this dialog was last closed. |
| 7697 */ | 7929 */ |
| 7698 closingReason: { | 7930 closingReason: { |
| 7699 // was a getter before, but needs to be a property so other | 7931 // was a getter before, but needs to be a property so other |
| 7700 // behaviors can override this. | 7932 // behaviors can override this. |
| 7701 type: Object | 7933 type: Object |
| 7702 }, | 7934 }, |
| 7703 | 7935 |
| 7936 /** |
| 7937 * The HTMLElement that will be firing relevant KeyboardEvents. |
| 7938 * Used for capturing esc and tab. Overridden from `IronA11yKeysBehavior`. |
| 7939 */ |
| 7940 keyEventTarget: { |
| 7941 type: Object, |
| 7942 value: document |
| 7943 }, |
| 7944 |
| 7945 /** |
| 7946 * Set to true to enable restoring of focus when overlay is closed. |
| 7947 */ |
| 7948 restoreFocusOnClose: { |
| 7949 type: Boolean, |
| 7950 value: false |
| 7951 }, |
| 7952 |
| 7704 _manager: { | 7953 _manager: { |
| 7705 type: Object, | 7954 type: Object, |
| 7706 value: Polymer.IronOverlayManager | 7955 value: Polymer.IronOverlayManager |
| 7707 }, | 7956 }, |
| 7708 | 7957 |
| 7709 _boundOnCaptureClick: { | 7958 _boundOnCaptureClick: { |
| 7710 type: Function, | 7959 type: Function, |
| 7711 value: function() { | 7960 value: function() { |
| 7712 return this._onCaptureClick.bind(this); | 7961 return this._onCaptureClick.bind(this); |
| 7713 } | 7962 } |
| 7714 }, | 7963 }, |
| 7715 | 7964 |
| 7716 _boundOnCaptureKeydown: { | |
| 7717 type: Function, | |
| 7718 value: function() { | |
| 7719 return this._onCaptureKeydown.bind(this); | |
| 7720 } | |
| 7721 }, | |
| 7722 | |
| 7723 _boundOnCaptureFocus: { | 7965 _boundOnCaptureFocus: { |
| 7724 type: Function, | 7966 type: Function, |
| 7725 value: function() { | 7967 value: function() { |
| 7726 return this._onCaptureFocus.bind(this); | 7968 return this._onCaptureFocus.bind(this); |
| 7727 } | 7969 } |
| 7728 }, | 7970 }, |
| 7729 | 7971 |
| 7730 /** @type {?Node} */ | 7972 /** |
| 7973 * The node being focused. |
| 7974 * @type {?Node} |
| 7975 */ |
| 7731 _focusedChild: { | 7976 _focusedChild: { |
| 7732 type: Object | 7977 type: Object |
| 7733 } | 7978 } |
| 7734 | 7979 |
| 7735 }, | 7980 }, |
| 7736 | 7981 |
| 7982 keyBindings: { |
| 7983 'esc': '__onEsc', |
| 7984 'tab': '__onTab' |
| 7985 }, |
| 7986 |
| 7737 listeners: { | 7987 listeners: { |
| 7738 'iron-resize': '_onIronResize' | 7988 'iron-resize': '_onIronResize' |
| 7739 }, | 7989 }, |
| 7740 | 7990 |
| 7741 /** | 7991 /** |
| 7742 * The backdrop element. | 7992 * The backdrop element. |
| 7743 * @type Node | 7993 * @type {Node} |
| 7744 */ | 7994 */ |
| 7745 get backdropElement() { | 7995 get backdropElement() { |
| 7746 return this._manager.backdropElement; | 7996 return this._manager.backdropElement; |
| 7747 }, | 7997 }, |
| 7748 | 7998 |
| 7999 /** |
| 8000 * Returns the node to give focus to. |
| 8001 * @type {Node} |
| 8002 */ |
| 7749 get _focusNode() { | 8003 get _focusNode() { |
| 7750 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; | 8004 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; |
| 7751 }, | 8005 }, |
| 7752 | 8006 |
| 8007 /** |
| 8008 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. |
| 8009 * This is used to retrieve which is the first and last focusable nodes in o
rder |
| 8010 * to wrap the focus for overlays `with-backdrop`. |
| 8011 * |
| 8012 * If you know what is your content (specifically the first and last focusab
le children), |
| 8013 * you can override this method to return only `[firstFocusable, lastFocusab
le];` |
| 8014 * @type {[Node]} |
| 8015 * @protected |
| 8016 */ |
| 8017 get _focusableNodes() { |
| 8018 // Elements that can be focused even if they have [disabled] attribute. |
| 8019 var FOCUSABLE_WITH_DISABLED = [ |
| 8020 'a[href]', |
| 8021 'area[href]', |
| 8022 'iframe', |
| 8023 '[tabindex]', |
| 8024 '[contentEditable=true]' |
| 8025 ]; |
| 8026 |
| 8027 // Elements that cannot be focused if they have [disabled] attribute. |
| 8028 var FOCUSABLE_WITHOUT_DISABLED = [ |
| 8029 'input', |
| 8030 'select', |
| 8031 'textarea', |
| 8032 'button' |
| 8033 ]; |
| 8034 |
| 8035 // Discard elements with tabindex=-1 (makes them not focusable). |
| 8036 var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + |
| 8037 ':not([tabindex="-1"]),' + |
| 8038 FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),'
) + |
| 8039 ':not([disabled]):not([tabindex="-1"])'; |
| 8040 |
| 8041 var focusables = Polymer.dom(this).querySelectorAll(selector); |
| 8042 if (this.tabIndex >= 0) { |
| 8043 // Insert at the beginning because we might have all elements with tabIn
dex = 0, |
| 8044 // and the overlay should be the first of the list. |
| 8045 focusables.splice(0, 0, this); |
| 8046 } |
| 8047 // Sort by tabindex. |
| 8048 return focusables.sort(function (a, b) { |
| 8049 if (a.tabIndex === b.tabIndex) { |
| 8050 return 0; |
| 8051 } |
| 8052 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { |
| 8053 return 1; |
| 8054 } |
| 8055 return -1; |
| 8056 }); |
| 8057 }, |
| 8058 |
| 7753 ready: function() { | 8059 ready: function() { |
| 7754 // with-backdrop need tabindex to be set in order to trap the focus. | 8060 // with-backdrop needs tabindex to be set in order to trap the focus. |
| 7755 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. | 8061 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 7756 this.__shouldRemoveTabIndex = false; | 8062 this.__shouldRemoveTabIndex = false; |
| 8063 // Used for wrapping the focus on TAB / Shift+TAB. |
| 8064 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| 7757 this._ensureSetup(); | 8065 this._ensureSetup(); |
| 7758 }, | 8066 }, |
| 7759 | 8067 |
| 7760 attached: function() { | 8068 attached: function() { |
| 7761 // Call _openedChanged here so that position can be computed correctly. | 8069 // Call _openedChanged here so that position can be computed correctly. |
| 7762 if (this._callOpenedWhenReady) { | 8070 if (this.opened) { |
| 7763 this._openedChanged(); | 8071 this._openedChanged(); |
| 7764 } | 8072 } |
| 8073 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); |
| 7765 }, | 8074 }, |
| 7766 | 8075 |
| 7767 detached: function() { | 8076 detached: function() { |
| 8077 Polymer.dom(this).unobserveNodes(this._observer); |
| 8078 this._observer = null; |
| 7768 this.opened = false; | 8079 this.opened = false; |
| 7769 this._manager.trackBackdrop(this); | 8080 this._manager.trackBackdrop(this); |
| 7770 this._manager.removeOverlay(this); | 8081 this._manager.removeOverlay(this); |
| 7771 }, | 8082 }, |
| 7772 | 8083 |
| 7773 /** | 8084 /** |
| 7774 * Toggle the opened state of the overlay. | 8085 * Toggle the opened state of the overlay. |
| 7775 */ | 8086 */ |
| 7776 toggle: function() { | 8087 toggle: function() { |
| 7777 this._setCanceled(false); | 8088 this._setCanceled(false); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 7789 /** | 8100 /** |
| 7790 * Close the overlay. | 8101 * Close the overlay. |
| 7791 */ | 8102 */ |
| 7792 close: function() { | 8103 close: function() { |
| 7793 this._setCanceled(false); | 8104 this._setCanceled(false); |
| 7794 this.opened = false; | 8105 this.opened = false; |
| 7795 }, | 8106 }, |
| 7796 | 8107 |
| 7797 /** | 8108 /** |
| 7798 * Cancels the overlay. | 8109 * Cancels the overlay. |
| 8110 * @param {?Event} event The original event |
| 7799 */ | 8111 */ |
| 7800 cancel: function() { | 8112 cancel: function(event) { |
| 7801 var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelabl
e: true}); | 8113 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); |
| 7802 if (cancelEvent.defaultPrevented) { | 8114 if (cancelEvent.defaultPrevented) { |
| 7803 return; | 8115 return; |
| 7804 } | 8116 } |
| 7805 | 8117 |
| 7806 this._setCanceled(true); | 8118 this._setCanceled(true); |
| 7807 this.opened = false; | 8119 this.opened = false; |
| 7808 }, | 8120 }, |
| 7809 | 8121 |
| 7810 _ensureSetup: function() { | 8122 _ensureSetup: function() { |
| 7811 if (this._overlaySetup) { | 8123 if (this._overlaySetup) { |
| 7812 return; | 8124 return; |
| 7813 } | 8125 } |
| 7814 this._overlaySetup = true; | 8126 this._overlaySetup = true; |
| 7815 this.style.outline = 'none'; | 8127 this.style.outline = 'none'; |
| 7816 this.style.display = 'none'; | 8128 this.style.display = 'none'; |
| 7817 }, | 8129 }, |
| 7818 | 8130 |
| 7819 _openedChanged: function() { | 8131 _openedChanged: function() { |
| 7820 if (this.opened) { | 8132 if (this.opened) { |
| 7821 this.removeAttribute('aria-hidden'); | 8133 this.removeAttribute('aria-hidden'); |
| 7822 } else { | 8134 } else { |
| 7823 this.setAttribute('aria-hidden', 'true'); | 8135 this.setAttribute('aria-hidden', 'true'); |
| 7824 Polymer.dom(this).unobserveNodes(this._observer); | |
| 7825 } | 8136 } |
| 7826 | 8137 |
| 7827 // wait to call after ready only if we're initially open | 8138 // wait to call after ready only if we're initially open |
| 7828 if (!this._overlaySetup) { | 8139 if (!this._overlaySetup) { |
| 7829 this._callOpenedWhenReady = this.opened; | |
| 7830 return; | 8140 return; |
| 7831 } | 8141 } |
| 7832 | 8142 |
| 7833 this._manager.trackBackdrop(this); | 8143 this._manager.trackBackdrop(this); |
| 7834 | 8144 |
| 7835 if (this.opened) { | 8145 if (this.opened) { |
| 7836 this._prepareRenderOpened(); | 8146 this._prepareRenderOpened(); |
| 7837 } | 8147 } |
| 7838 | 8148 |
| 7839 if (this._openChangedAsync) { | 8149 if (this._openChangedAsync) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7894 node.addEventListener(event, boundListener, capture); | 8204 node.addEventListener(event, boundListener, capture); |
| 7895 } else { | 8205 } else { |
| 7896 // disable document-wide tap recognizer | 8206 // disable document-wide tap recognizer |
| 7897 if (event === 'tap') { | 8207 if (event === 'tap') { |
| 7898 Polymer.Gestures.remove(document, 'tap', null); | 8208 Polymer.Gestures.remove(document, 'tap', null); |
| 7899 } | 8209 } |
| 7900 node.removeEventListener(event, boundListener, capture); | 8210 node.removeEventListener(event, boundListener, capture); |
| 7901 } | 8211 } |
| 7902 }, | 8212 }, |
| 7903 | 8213 |
| 7904 _toggleListeners: function () { | 8214 _toggleListeners: function() { |
| 7905 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); | 8215 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); |
| 7906 this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptur
eKeydown, true); | |
| 7907 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); | 8216 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); |
| 7908 }, | 8217 }, |
| 7909 | 8218 |
| 7910 // tasks which must occur before opening; e.g. making the element visible | 8219 // tasks which must occur before opening; e.g. making the element visible |
| 7911 _prepareRenderOpened: function() { | 8220 _prepareRenderOpened: function() { |
| 8221 |
| 7912 this._manager.addOverlay(this); | 8222 this._manager.addOverlay(this); |
| 7913 | 8223 |
| 8224 // Needed to calculate the size of the overlay so that transitions on its
size |
| 8225 // will have the correct starting points. |
| 7914 this._preparePositioning(); | 8226 this._preparePositioning(); |
| 7915 this.fit(); | 8227 this.fit(); |
| 7916 this._finishPositioning(); | 8228 this._finishPositioning(); |
| 7917 | 8229 |
| 7918 if (this.withBackdrop) { | 8230 if (this.withBackdrop) { |
| 7919 this.backdropElement.prepare(); | 8231 this.backdropElement.prepare(); |
| 7920 } | 8232 } |
| 8233 |
| 8234 // Safari will apply the focus to the autofocus element when displayed for
the first time, |
| 8235 // so we blur it. Later, _applyFocus will set the focus if necessary. |
| 8236 if (this.noAutoFocus && document.activeElement === this._focusNode) { |
| 8237 this._focusNode.blur(); |
| 8238 } |
| 7921 }, | 8239 }, |
| 7922 | 8240 |
| 7923 // tasks which cause the overlay to actually open; typically play an | 8241 // tasks which cause the overlay to actually open; typically play an |
| 7924 // animation | 8242 // animation |
| 7925 _renderOpened: function() { | 8243 _renderOpened: function() { |
| 7926 if (this.withBackdrop) { | 8244 if (this.withBackdrop) { |
| 7927 this.backdropElement.open(); | 8245 this.backdropElement.open(); |
| 7928 } | 8246 } |
| 7929 this._finishRenderOpened(); | 8247 this._finishRenderOpened(); |
| 7930 }, | 8248 }, |
| 7931 | 8249 |
| 7932 _renderClosed: function() { | 8250 _renderClosed: function() { |
| 7933 if (this.withBackdrop) { | 8251 if (this.withBackdrop) { |
| 7934 this.backdropElement.close(); | 8252 this.backdropElement.close(); |
| 7935 } | 8253 } |
| 7936 this._finishRenderClosed(); | 8254 this._finishRenderClosed(); |
| 7937 }, | 8255 }, |
| 7938 | 8256 |
| 7939 _finishRenderOpened: function() { | 8257 _finishRenderOpened: function() { |
| 7940 // focus the child node with [autofocus] | 8258 // This ensures the overlay is visible before we set the focus |
| 8259 // (by calling _onIronResize -> refit). |
| 8260 this.notifyResize(); |
| 8261 // Focus the child node with [autofocus] |
| 7941 this._applyFocus(); | 8262 this._applyFocus(); |
| 7942 | 8263 |
| 7943 this._observer = Polymer.dom(this).observeNodes(this.notifyResize); | |
| 7944 this.fire('iron-overlay-opened'); | 8264 this.fire('iron-overlay-opened'); |
| 7945 }, | 8265 }, |
| 7946 | 8266 |
| 7947 _finishRenderClosed: function() { | 8267 _finishRenderClosed: function() { |
| 7948 // hide the overlay and remove the backdrop | 8268 // Hide the overlay and remove the backdrop. |
| 7949 this.resetFit(); | 8269 this.resetFit(); |
| 7950 this.style.display = 'none'; | 8270 this.style.display = 'none'; |
| 7951 this._manager.removeOverlay(this); | 8271 this._manager.removeOverlay(this); |
| 7952 | 8272 |
| 7953 this._focusedChild = null; | |
| 7954 this._applyFocus(); | 8273 this._applyFocus(); |
| 8274 this.notifyResize(); |
| 7955 | 8275 |
| 7956 this.notifyResize(); | |
| 7957 this.fire('iron-overlay-closed', this.closingReason); | 8276 this.fire('iron-overlay-closed', this.closingReason); |
| 7958 }, | 8277 }, |
| 7959 | 8278 |
| 7960 _preparePositioning: function() { | 8279 _preparePositioning: function() { |
| 7961 this.style.transition = this.style.webkitTransition = 'none'; | 8280 this.style.transition = this.style.webkitTransition = 'none'; |
| 7962 this.style.transform = this.style.webkitTransform = 'none'; | 8281 this.style.transform = this.style.webkitTransform = 'none'; |
| 7963 this.style.display = ''; | 8282 this.style.display = ''; |
| 7964 }, | 8283 }, |
| 7965 | 8284 |
| 7966 _finishPositioning: function() { | 8285 _finishPositioning: function() { |
| 7967 this.style.display = 'none'; | 8286 this.style.display = 'none'; |
| 7968 this.style.transform = this.style.webkitTransform = ''; | 8287 this.style.transform = this.style.webkitTransform = ''; |
| 7969 // force layout to avoid application of transform | 8288 // Force layout layout to avoid application of transform. |
| 7970 /** @suppress {suspiciousCode} */ this.offsetWidth; | 8289 // Set offsetWidth to itself so that compilers won't remove it. |
| 8290 this.offsetWidth = this.offsetWidth; |
| 7971 this.style.transition = this.style.webkitTransition = ''; | 8291 this.style.transition = this.style.webkitTransition = ''; |
| 7972 }, | 8292 }, |
| 7973 | 8293 |
| 7974 _applyFocus: function() { | 8294 _applyFocus: function() { |
| 7975 if (this.opened) { | 8295 if (this.opened) { |
| 7976 if (!this.noAutoFocus) { | 8296 if (!this.noAutoFocus) { |
| 7977 this._focusNode.focus(); | 8297 this._focusNode.focus(); |
| 7978 } | 8298 } |
| 7979 } else { | 8299 } else { |
| 7980 this._focusNode.blur(); | 8300 this._focusNode.blur(); |
| 8301 this._focusedChild = null; |
| 7981 this._manager.focusOverlay(); | 8302 this._manager.focusOverlay(); |
| 7982 } | 8303 } |
| 7983 }, | 8304 }, |
| 7984 | 8305 |
| 7985 _onCaptureClick: function(event) { | 8306 _onCaptureClick: function(event) { |
| 7986 if (this._manager.currentOverlay() === this && | 8307 if (this._manager.currentOverlay() === this && |
| 7987 Polymer.dom(event).path.indexOf(this) === -1) { | 8308 Polymer.dom(event).path.indexOf(this) === -1) { |
| 7988 if (this.noCancelOnOutsideClick) { | 8309 if (this.noCancelOnOutsideClick) { |
| 7989 this._applyFocus(); | 8310 this._applyFocus(); |
| 7990 } else { | 8311 } else { |
| 7991 this.cancel(); | 8312 this.cancel(event); |
| 7992 } | 8313 } |
| 7993 } | 8314 } |
| 7994 }, | 8315 }, |
| 7995 | 8316 |
| 7996 _onCaptureKeydown: function(event) { | |
| 7997 var ESC = 27; | |
| 7998 if (this._manager.currentOverlay() === this && | |
| 7999 !this.noCancelOnEscKey && | |
| 8000 event.keyCode === ESC) { | |
| 8001 this.cancel(); | |
| 8002 } | |
| 8003 }, | |
| 8004 | |
| 8005 _onCaptureFocus: function (event) { | 8317 _onCaptureFocus: function (event) { |
| 8006 if (this._manager.currentOverlay() === this && | 8318 if (this._manager.currentOverlay() === this && this.withBackdrop) { |
| 8007 this.withBackdrop) { | |
| 8008 var path = Polymer.dom(event).path; | 8319 var path = Polymer.dom(event).path; |
| 8009 if (path.indexOf(this) === -1) { | 8320 if (path.indexOf(this) === -1) { |
| 8010 event.stopPropagation(); | 8321 event.stopPropagation(); |
| 8011 this._applyFocus(); | 8322 this._applyFocus(); |
| 8012 } else { | 8323 } else { |
| 8013 this._focusedChild = path[0]; | 8324 this._focusedChild = path[0]; |
| 8014 } | 8325 } |
| 8015 } | 8326 } |
| 8016 }, | 8327 }, |
| 8017 | 8328 |
| 8018 _onIronResize: function() { | 8329 _onIronResize: function() { |
| 8019 if (this.opened) { | 8330 if (this.opened) { |
| 8020 this.refit(); | 8331 this.refit(); |
| 8021 } | 8332 } |
| 8333 }, |
| 8334 |
| 8335 /** |
| 8336 * @protected |
| 8337 * Will call notifyResize if overlay is opened. |
| 8338 * Can be overridden in order to avoid multiple observers on the same node. |
| 8339 */ |
| 8340 _onNodesChange: function() { |
| 8341 if (this.opened) { |
| 8342 this.notifyResize(); |
| 8343 } |
| 8344 // Store it so we don't query too much. |
| 8345 var focusableNodes = this._focusableNodes; |
| 8346 this.__firstFocusableNode = focusableNodes[0]; |
| 8347 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 8348 }, |
| 8349 |
| 8350 __onEsc: function(event) { |
| 8351 // Not opened or not on top, so return. |
| 8352 if (this._manager.currentOverlay() !== this) { |
| 8353 return; |
| 8354 } |
| 8355 if (!this.noCancelOnEscKey) { |
| 8356 this.cancel(event); |
| 8357 } |
| 8358 }, |
| 8359 |
| 8360 __onTab: function(event) { |
| 8361 // Not opened or not on top, so return. |
| 8362 if (this._manager.currentOverlay() !== this) { |
| 8363 return; |
| 8364 } |
| 8365 // TAB wraps from last to first focusable. |
| 8366 // Shift + TAB wraps from first to last focusable. |
| 8367 var shift = event.detail.keyboardEvent.shiftKey; |
| 8368 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 8369 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 8370 if (this.withBackdrop && this._focusedChild === nodeToCheck) { |
| 8371 // We set here the _focusedChild so that _onCaptureFocus will handle the |
| 8372 // wrapping of the focus (the next event after tab is focus). |
| 8373 this._focusedChild = nodeToSet; |
| 8374 } |
| 8022 } | 8375 } |
| 8023 | |
| 8024 /** | |
| 8025 * Fired after the `iron-overlay` opens. | |
| 8026 * @event iron-overlay-opened | |
| 8027 */ | |
| 8028 | |
| 8029 /** | |
| 8030 * Fired when the `iron-overlay` is canceled, but before it is closed. | |
| 8031 * Cancel the event to prevent the `iron-overlay` from closing. | |
| 8032 * @event iron-overlay-canceled | |
| 8033 */ | |
| 8034 | |
| 8035 /** | |
| 8036 * Fired after the `iron-overlay` closes. | |
| 8037 * @event iron-overlay-closed | |
| 8038 * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute | |
| 8039 */ | |
| 8040 }; | 8376 }; |
| 8041 | 8377 |
| 8042 /** @polymerBehavior */ | 8378 /** @polymerBehavior */ |
| 8043 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; | 8379 Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBe
havior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; |
| 8380 |
| 8381 /** |
| 8382 * Fired after the `iron-overlay` opens. |
| 8383 * @event iron-overlay-opened |
| 8384 */ |
| 8385 |
| 8386 /** |
| 8387 * Fired when the `iron-overlay` is canceled, but before it is closed. |
| 8388 * Cancel the event to prevent the `iron-overlay` from closing. |
| 8389 * @event iron-overlay-canceled |
| 8390 * @param {Event} event The closing of the `iron-overlay` can be prevented |
| 8391 * by calling `event.preventDefault()`. The `event.detail` is the original even
t that originated |
| 8392 * the canceling (e.g. ESC keyboard event or click event outside the `iron-over
lay`). |
| 8393 */ |
| 8394 |
| 8395 /** |
| 8396 * Fired after the `iron-overlay` closes. |
| 8397 * @event iron-overlay-closed |
| 8398 * @param {{canceled: (boolean|undefined)}} closingReason Contains `canceled` (
whether the overlay was canceled). |
| 8399 */ |
| 8044 /** | 8400 /** |
| 8045 * Use `Polymer.NeonAnimationBehavior` to implement an animation. | 8401 * Use `Polymer.NeonAnimationBehavior` to implement an animation. |
| 8046 * @polymerBehavior | 8402 * @polymerBehavior |
| 8047 */ | 8403 */ |
| 8048 Polymer.NeonAnimationBehavior = { | 8404 Polymer.NeonAnimationBehavior = { |
| 8049 | 8405 |
| 8050 properties: { | 8406 properties: { |
| 8051 | 8407 |
| 8052 /** | 8408 /** |
| 8053 * Defines the animation timing. | 8409 * Defines the animation timing. |
| (...skipping 2411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10465 Manager.get().updateItem_(index, data); | 10821 Manager.get().updateItem_(index, data); |
| 10466 }; | 10822 }; |
| 10467 | 10823 |
| 10468 return {Manager: Manager}; | 10824 return {Manager: Manager}; |
| 10469 }); | 10825 }); |
| 10470 // Copyright 2015 The Chromium Authors. All rights reserved. | 10826 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 10471 // Use of this source code is governed by a BSD-style license that can be | 10827 // Use of this source code is governed by a BSD-style license that can be |
| 10472 // found in the LICENSE file. | 10828 // found in the LICENSE file. |
| 10473 | 10829 |
| 10474 window.addEventListener('load', downloads.Manager.onLoad); | 10830 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |