| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview PromiseResolver is a helper class that allows creating a | 6 * @fileoverview PromiseResolver is a helper class that allows creating a |
| 7 * Promise that will be fulfilled (resolved or rejected) some time later. | 7 * Promise that will be fulfilled (resolved or rejected) some time later. |
| 8 * | 8 * |
| 9 * Example: | 9 * Example: |
| 10 * var resolver = new PromiseResolver(); | 10 * var resolver = new PromiseResolver(); |
| (...skipping 1413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1424 var elm = document.createElement(type); | 1424 var elm = document.createElement(type); |
| 1425 elm.className = className; | 1425 elm.className = className; |
| 1426 return elm; | 1426 return elm; |
| 1427 } | 1427 } |
| 1428 | 1428 |
| 1429 /** | 1429 /** |
| 1430 * webkitTransitionEnd does not always fire (e.g. when animation is aborted | 1430 * webkitTransitionEnd does not always fire (e.g. when animation is aborted |
| 1431 * or when no paint happens during the animation). This function sets up | 1431 * or when no paint happens during the animation). This function sets up |
| 1432 * a timer and emulate the event if it is not fired when the timer expires. | 1432 * a timer and emulate the event if it is not fired when the timer expires. |
| 1433 * @param {!HTMLElement} el The element to watch for webkitTransitionEnd. | 1433 * @param {!HTMLElement} el The element to watch for webkitTransitionEnd. |
| 1434 * @param {number} timeOut The maximum wait time in milliseconds for the | 1434 * @param {number=} opt_timeOut The maximum wait time in milliseconds for the |
| 1435 * webkitTransitionEnd to happen. | 1435 * webkitTransitionEnd to happen. If not specified, it is fetched from |el| |
| 1436 * using the transitionDuration style value. |
| 1436 */ | 1437 */ |
| 1437 function ensureTransitionEndEvent(el, timeOut) { | 1438 function ensureTransitionEndEvent(el, opt_timeOut) { |
| 1439 if (opt_timeOut === undefined) { |
| 1440 var style = getComputedStyle(el); |
| 1441 opt_timeOut = parseFloat(style.transitionDuration) * 1000; |
| 1442 |
| 1443 // Give an additional 50ms buffer for the animation to complete. |
| 1444 opt_timeOut += 50; |
| 1445 } |
| 1446 |
| 1438 var fired = false; | 1447 var fired = false; |
| 1439 el.addEventListener('webkitTransitionEnd', function f(e) { | 1448 el.addEventListener('webkitTransitionEnd', function f(e) { |
| 1440 el.removeEventListener('webkitTransitionEnd', f); | 1449 el.removeEventListener('webkitTransitionEnd', f); |
| 1441 fired = true; | 1450 fired = true; |
| 1442 }); | 1451 }); |
| 1443 window.setTimeout(function() { | 1452 window.setTimeout(function() { |
| 1444 if (!fired) | 1453 if (!fired) |
| 1445 cr.dispatchSimpleEvent(el, 'webkitTransitionEnd', true); | 1454 cr.dispatchSimpleEvent(el, 'webkitTransitionEnd', true); |
| 1446 }, timeOut); | 1455 }, opt_timeOut); |
| 1447 } | 1456 } |
| 1448 | 1457 |
| 1449 /** | 1458 /** |
| 1450 * Alias for document.scrollTop getter. | 1459 * Alias for document.scrollTop getter. |
| 1451 * @param {!HTMLDocument} doc The document node where information will be | 1460 * @param {!HTMLDocument} doc The document node where information will be |
| 1452 * queried from. | 1461 * queried from. |
| 1453 * @return {number} The Y document scroll offset. | 1462 * @return {number} The Y document scroll offset. |
| 1454 */ | 1463 */ |
| 1455 function scrollTopForDocument(doc) { | 1464 function scrollTopForDocument(doc) { |
| 1456 return doc.documentElement.scrollTop || doc.body.scrollTop; | 1465 return doc.documentElement.scrollTop || doc.body.scrollTop; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1515 } | 1524 } |
| 1516 | 1525 |
| 1517 /** | 1526 /** |
| 1518 * Quote a string so it can be used in a regular expression. | 1527 * Quote a string so it can be used in a regular expression. |
| 1519 * @param {string} str The source string. | 1528 * @param {string} str The source string. |
| 1520 * @return {string} The escaped string. | 1529 * @return {string} The escaped string. |
| 1521 */ | 1530 */ |
| 1522 function quoteString(str) { | 1531 function quoteString(str) { |
| 1523 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); | 1532 return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1'); |
| 1524 }; | 1533 }; |
| 1525 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 1526 // Use of this source code is governed by a BSD-style license that can be | |
| 1527 // found in the LICENSE file. | |
| 1528 | |
| 1529 /** | |
| 1530 * @fileoverview Assertion support. | |
| 1531 */ | |
| 1532 | |
| 1533 /** | |
| 1534 * Verify |condition| is truthy and return |condition| if so. | |
| 1535 * @template T | |
| 1536 * @param {T} condition A condition to check for truthiness. Note that this | |
| 1537 * may be used to test whether a value is defined or not, and we don't want | |
| 1538 * to force a cast to Boolean. | |
| 1539 * @param {string=} opt_message A message to show on failure. | |
| 1540 * @return {T} A non-null |condition|. | |
| 1541 */ | |
| 1542 function assert(condition, opt_message) { | |
| 1543 if (!condition) { | |
| 1544 var message = 'Assertion failed'; | |
| 1545 if (opt_message) | |
| 1546 message = message + ': ' + opt_message; | |
| 1547 var error = new Error(message); | |
| 1548 var global = function() { return this; }(); | |
| 1549 if (global.traceAssertionsForTesting) | |
| 1550 console.warn(error.stack); | |
| 1551 throw error; | |
| 1552 } | |
| 1553 return condition; | |
| 1554 } | |
| 1555 | |
| 1556 /** | |
| 1557 * Call this from places in the code that should never be reached. | |
| 1558 * | |
| 1559 * For example, handling all the values of enum with a switch() like this: | |
| 1560 * | |
| 1561 * function getValueFromEnum(enum) { | |
| 1562 * switch (enum) { | |
| 1563 * case ENUM_FIRST_OF_TWO: | |
| 1564 * return first | |
| 1565 * case ENUM_LAST_OF_TWO: | |
| 1566 * return last; | |
| 1567 * } | |
| 1568 * assertNotReached(); | |
| 1569 * return document; | |
| 1570 * } | |
| 1571 * | |
| 1572 * This code should only be hit in the case of serious programmer error or | |
| 1573 * unexpected input. | |
| 1574 * | |
| 1575 * @param {string=} opt_message A message to show when this is hit. | |
| 1576 */ | |
| 1577 function assertNotReached(opt_message) { | |
| 1578 assert(false, opt_message || 'Unreachable code hit'); | |
| 1579 } | |
| 1580 | |
| 1581 /** | |
| 1582 * @param {*} value The value to check. | |
| 1583 * @param {function(new: T, ...)} type A user-defined constructor. | |
| 1584 * @param {string=} opt_message A message to show when this is hit. | |
| 1585 * @return {T} | |
| 1586 * @template T | |
| 1587 */ | |
| 1588 function assertInstanceof(value, type, opt_message) { | |
| 1589 // We don't use assert immediately here so that we avoid constructing an error | |
| 1590 // message if we don't have to. | |
| 1591 if (!(value instanceof type)) { | |
| 1592 assertNotReached(opt_message || 'Value ' + value + | |
| 1593 ' is not a[n] ' + (type.name || typeof type)); | |
| 1594 } | |
| 1595 return value; | |
| 1596 }; | |
| 1597 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 1598 // Use of this source code is governed by a BSD-style license that can be | |
| 1599 // found in the LICENSE file. | |
| 1600 | |
| 1601 cr.define('downloads', function() { | |
| 1602 /** | |
| 1603 * @param {string} chromeSendName | |
| 1604 * @return {function(string):void} A chrome.send() callback with curried name. | |
| 1605 */ | |
| 1606 function chromeSendWithId(chromeSendName) { | |
| 1607 return function(id) { chrome.send(chromeSendName, [id]); }; | |
| 1608 } | |
| 1609 | |
| 1610 /** @constructor */ | |
| 1611 function ActionService() { | |
| 1612 /** @private {Array<string>} */ | |
| 1613 this.searchTerms_ = []; | |
| 1614 } | |
| 1615 | |
| 1616 /** | |
| 1617 * @param {string} s | |
| 1618 * @return {string} |s| without whitespace at the beginning or end. | |
| 1619 */ | |
| 1620 function trim(s) { return s.trim(); } | |
| 1621 | |
| 1622 /** | |
| 1623 * @param {string|undefined} value | |
| 1624 * @return {boolean} Whether |value| is truthy. | |
| 1625 */ | |
| 1626 function truthy(value) { return !!value; } | |
| 1627 | |
| 1628 /** | |
| 1629 * @param {string} searchText Input typed by the user into a search box. | |
| 1630 * @return {Array<string>} A list of terms extracted from |searchText|. | |
| 1631 */ | |
| 1632 ActionService.splitTerms = function(searchText) { | |
| 1633 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). | |
| 1634 return searchText.split(/"([^"]*)"/).map(trim).filter(truthy); | |
| 1635 }; | |
| 1636 | |
| 1637 ActionService.prototype = { | |
| 1638 /** @param {string} id ID of the download to cancel. */ | |
| 1639 cancel: chromeSendWithId('cancel'), | |
| 1640 | |
| 1641 /** Instructs the browser to clear all finished downloads. */ | |
| 1642 clearAll: function() { | |
| 1643 if (loadTimeData.getBoolean('allowDeletingHistory')) { | |
| 1644 chrome.send('clearAll'); | |
| 1645 this.search(''); | |
| 1646 } | |
| 1647 }, | |
| 1648 | |
| 1649 /** @param {string} id ID of the dangerous download to discard. */ | |
| 1650 discardDangerous: chromeSendWithId('discardDangerous'), | |
| 1651 | |
| 1652 /** @param {string} url URL of a file to download. */ | |
| 1653 download: function(url) { | |
| 1654 var a = document.createElement('a'); | |
| 1655 a.href = url; | |
| 1656 a.setAttribute('download', ''); | |
| 1657 a.click(); | |
| 1658 }, | |
| 1659 | |
| 1660 /** @param {string} id ID of the download that the user started dragging. */ | |
| 1661 drag: chromeSendWithId('drag'), | |
| 1662 | |
| 1663 /** Loads more downloads with the current search terms. */ | |
| 1664 loadMore: function() { | |
| 1665 chrome.send('getDownloads', this.searchTerms_); | |
| 1666 }, | |
| 1667 | |
| 1668 /** | |
| 1669 * @return {boolean} Whether the user is currently searching for downloads | |
| 1670 * (i.e. has a non-empty search term). | |
| 1671 */ | |
| 1672 isSearching: function() { | |
| 1673 return this.searchTerms_.length > 0; | |
| 1674 }, | |
| 1675 | |
| 1676 /** Opens the current local destination for downloads. */ | |
| 1677 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), | |
| 1678 | |
| 1679 /** | |
| 1680 * @param {string} id ID of the download to run locally on the user's box. | |
| 1681 */ | |
| 1682 openFile: chromeSendWithId('openFile'), | |
| 1683 | |
| 1684 /** @param {string} id ID the of the progressing download to pause. */ | |
| 1685 pause: chromeSendWithId('pause'), | |
| 1686 | |
| 1687 /** @param {string} id ID of the finished download to remove. */ | |
| 1688 remove: chromeSendWithId('remove'), | |
| 1689 | |
| 1690 /** @param {string} id ID of the paused download to resume. */ | |
| 1691 resume: chromeSendWithId('resume'), | |
| 1692 | |
| 1693 /** | |
| 1694 * @param {string} id ID of the dangerous download to save despite | |
| 1695 * warnings. | |
| 1696 */ | |
| 1697 saveDangerous: chromeSendWithId('saveDangerous'), | |
| 1698 | |
| 1699 /** @param {string} searchText What to search for. */ | |
| 1700 search: function(searchText) { | |
| 1701 var searchTerms = ActionService.splitTerms(searchText); | |
| 1702 var sameTerms = searchTerms.length == this.searchTerms_.length; | |
| 1703 | |
| 1704 for (var i = 0; sameTerms && i < searchTerms.length; ++i) { | |
| 1705 if (searchTerms[i] != this.searchTerms_[i]) | |
| 1706 sameTerms = false; | |
| 1707 } | |
| 1708 | |
| 1709 if (sameTerms) | |
| 1710 return; | |
| 1711 | |
| 1712 this.searchTerms_ = searchTerms; | |
| 1713 this.loadMore(); | |
| 1714 }, | |
| 1715 | |
| 1716 /** | |
| 1717 * Shows the local folder a finished download resides in. | |
| 1718 * @param {string} id ID of the download to show. | |
| 1719 */ | |
| 1720 show: chromeSendWithId('show'), | |
| 1721 | |
| 1722 /** Undo download removal. */ | |
| 1723 undo: chrome.send.bind(chrome, 'undo'), | |
| 1724 }; | |
| 1725 | |
| 1726 cr.addSingletonGetter(ActionService); | |
| 1727 | |
| 1728 return {ActionService: ActionService}; | |
| 1729 }); | |
| 1730 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 1731 // Use of this source code is governed by a BSD-style license that can be | |
| 1732 // found in the LICENSE file. | |
| 1733 | |
| 1734 cr.define('downloads', function() { | |
| 1735 /** | |
| 1736 * Explains why a download is in DANGEROUS state. | |
| 1737 * @enum {string} | |
| 1738 */ | |
| 1739 var DangerType = { | |
| 1740 NOT_DANGEROUS: 'NOT_DANGEROUS', | |
| 1741 DANGEROUS_FILE: 'DANGEROUS_FILE', | |
| 1742 DANGEROUS_URL: 'DANGEROUS_URL', | |
| 1743 DANGEROUS_CONTENT: 'DANGEROUS_CONTENT', | |
| 1744 UNCOMMON_CONTENT: 'UNCOMMON_CONTENT', | |
| 1745 DANGEROUS_HOST: 'DANGEROUS_HOST', | |
| 1746 POTENTIALLY_UNWANTED: 'POTENTIALLY_UNWANTED', | |
| 1747 }; | |
| 1748 | |
| 1749 /** | |
| 1750 * The states a download can be in. These correspond to states defined in | |
| 1751 * DownloadsDOMHandler::CreateDownloadItemValue | |
| 1752 * @enum {string} | |
| 1753 */ | |
| 1754 var States = { | |
| 1755 IN_PROGRESS: 'IN_PROGRESS', | |
| 1756 CANCELLED: 'CANCELLED', | |
| 1757 COMPLETE: 'COMPLETE', | |
| 1758 PAUSED: 'PAUSED', | |
| 1759 DANGEROUS: 'DANGEROUS', | |
| 1760 INTERRUPTED: 'INTERRUPTED', | |
| 1761 }; | |
| 1762 | |
| 1763 return { | |
| 1764 DangerType: DangerType, | |
| 1765 States: States, | |
| 1766 }; | |
| 1767 }); | |
| 1768 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 1769 // Use of this source code is governed by a BSD-style license that can be | |
| 1770 // found in the LICENSE file. | |
| 1771 | |
| 1772 // Action links are elements that are used to perform an in-page navigation or | |
| 1773 // action (e.g. showing a dialog). | |
| 1774 // | |
| 1775 // They look like normal anchor (<a>) tags as their text color is blue. However, | |
| 1776 // they're subtly different as they're not initially underlined (giving users a | |
| 1777 // clue that underlined links navigate while action links don't). | |
| 1778 // | |
| 1779 // Action links look very similar to normal links when hovered (hand cursor, | |
| 1780 // underlined). This gives the user an idea that clicking this link will do | |
| 1781 // something similar to navigation but in the same page. | |
| 1782 // | |
| 1783 // They can be created in JavaScript like this: | |
| 1784 // | |
| 1785 // var link = document.createElement('a', 'action-link'); // Note second arg. | |
| 1786 // | |
| 1787 // or with a constructor like this: | |
| 1788 // | |
| 1789 // var link = new ActionLink(); | |
| 1790 // | |
| 1791 // They can be used easily from HTML as well, like so: | |
| 1792 // | |
| 1793 // <a is="action-link">Click me!</a> | |
| 1794 // | |
| 1795 // NOTE: <action-link> and document.createElement('action-link') don't work. | |
| 1796 | |
| 1797 /** | |
| 1798 * @constructor | |
| 1799 * @extends {HTMLAnchorElement} | |
| 1800 */ | |
| 1801 var ActionLink = document.registerElement('action-link', { | |
| 1802 prototype: { | |
| 1803 __proto__: HTMLAnchorElement.prototype, | |
| 1804 | |
| 1805 /** @this {ActionLink} */ | |
| 1806 createdCallback: function() { | |
| 1807 // Action links can start disabled (e.g. <a is="action-link" disabled>). | |
| 1808 this.tabIndex = this.disabled ? -1 : 0; | |
| 1809 | |
| 1810 if (!this.hasAttribute('role')) | |
| 1811 this.setAttribute('role', 'link'); | |
| 1812 | |
| 1813 this.addEventListener('keydown', function(e) { | |
| 1814 if (!this.disabled && e.keyIdentifier == 'Enter' && !this.href) { | |
| 1815 // Schedule a click asynchronously because other 'keydown' handlers | |
| 1816 // may still run later (e.g. document.addEventListener('keydown')). | |
| 1817 // Specifically options dialogs break when this timeout isn't here. | |
| 1818 // NOTE: this affects the "trusted" state of the ensuing click. I | |
| 1819 // haven't found anything that breaks because of this (yet). | |
| 1820 window.setTimeout(this.click.bind(this), 0); | |
| 1821 } | |
| 1822 }); | |
| 1823 | |
| 1824 function preventDefault(e) { | |
| 1825 e.preventDefault(); | |
| 1826 } | |
| 1827 | |
| 1828 function removePreventDefault() { | |
| 1829 document.removeEventListener('selectstart', preventDefault); | |
| 1830 document.removeEventListener('mouseup', removePreventDefault); | |
| 1831 } | |
| 1832 | |
| 1833 this.addEventListener('mousedown', function() { | |
| 1834 // This handlers strives to match the behavior of <a href="...">. | |
| 1835 | |
| 1836 // While the mouse is down, prevent text selection from dragging. | |
| 1837 document.addEventListener('selectstart', preventDefault); | |
| 1838 document.addEventListener('mouseup', removePreventDefault); | |
| 1839 | |
| 1840 // If focus started via mouse press, don't show an outline. | |
| 1841 if (document.activeElement != this) | |
| 1842 this.classList.add('no-outline'); | |
| 1843 }); | |
| 1844 | |
| 1845 this.addEventListener('blur', function() { | |
| 1846 this.classList.remove('no-outline'); | |
| 1847 }); | |
| 1848 }, | |
| 1849 | |
| 1850 /** @type {boolean} */ | |
| 1851 set disabled(disabled) { | |
| 1852 if (disabled) | |
| 1853 HTMLAnchorElement.prototype.setAttribute.call(this, 'disabled', ''); | |
| 1854 else | |
| 1855 HTMLAnchorElement.prototype.removeAttribute.call(this, 'disabled'); | |
| 1856 this.tabIndex = disabled ? -1 : 0; | |
| 1857 }, | |
| 1858 get disabled() { | |
| 1859 return this.hasAttribute('disabled'); | |
| 1860 }, | |
| 1861 | |
| 1862 /** @override */ | |
| 1863 setAttribute: function(attr, val) { | |
| 1864 if (attr.toLowerCase() == 'disabled') | |
| 1865 this.disabled = true; | |
| 1866 else | |
| 1867 HTMLAnchorElement.prototype.setAttribute.apply(this, arguments); | |
| 1868 }, | |
| 1869 | |
| 1870 /** @override */ | |
| 1871 removeAttribute: function(attr) { | |
| 1872 if (attr.toLowerCase() == 'disabled') | |
| 1873 this.disabled = false; | |
| 1874 else | |
| 1875 HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments); | |
| 1876 }, | |
| 1877 }, | |
| 1878 | |
| 1879 extends: 'a', | |
| 1880 }); | |
| 1881 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 1882 // Use of this source code is governed by a BSD-style license that can be | |
| 1883 // found in the LICENSE file. | |
| 1884 | |
| 1885 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> | |
| 1886 | |
| 1887 i18nTemplate.process(document, loadTimeData); | |
| 1888 /** | 1534 /** |
| 1889 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to | 1535 * `IronResizableBehavior` is a behavior that can be used in Polymer elements
to |
| 1890 * coordinate the flow of resize events between "resizers" (elements that cont
rol the | 1536 * coordinate the flow of resize events between "resizers" (elements that cont
rol the |
| 1891 * size or hidden state of their children) and "resizables" (elements that nee
d to be | 1537 * size or hidden state of their children) and "resizables" (elements that nee
d to be |
| 1892 * notified when they are resized or un-hidden by their parents in order to ta
ke | 1538 * notified when they are resized or un-hidden by their parents in order to ta
ke |
| 1893 * action on their new measurements). | 1539 * action on their new measurements). |
| 1894 * | |
| 1895 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to | 1540 * Elements that perform measurement should add the `IronResizableBehavior` be
havior to |
| 1896 * their element definition and listen for the `iron-resize` event on themselv
es. | 1541 * their element definition and listen for the `iron-resize` event on themselv
es. |
| 1897 * This event will be fired when they become showing after having been hidden, | 1542 * This event will be fired when they become showing after having been hidden, |
| 1898 * when they are resized explicitly by another resizable, or when the window h
as been | 1543 * when they are resized explicitly by another resizable, or when the window h
as been |
| 1899 * resized. | 1544 * resized. |
| 1900 * | |
| 1901 * Note, the `iron-resize` event is non-bubbling. | 1545 * Note, the `iron-resize` event is non-bubbling. |
| 1902 * | 1546 * |
| 1903 * @polymerBehavior Polymer.IronResizableBehavior | 1547 * @polymerBehavior Polymer.IronResizableBehavior |
| 1904 * @demo demo/index.html | 1548 * @demo demo/index.html |
| 1905 **/ | 1549 **/ |
| 1906 Polymer.IronResizableBehavior = { | 1550 Polymer.IronResizableBehavior = { |
| 1907 properties: { | 1551 properties: { |
| 1908 /** | 1552 /** |
| 1909 * The closest ancestor element that implements `IronResizableBehavior`. | 1553 * The closest ancestor element that implements `IronResizableBehavior`. |
| 1910 */ | 1554 */ |
| (...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2554 window.removeEventListener('scroll', this._boundScrollHandler); | 2198 window.removeEventListener('scroll', this._boundScrollHandler); |
| 2555 } else if (this._oldScrollTarget.removeEventListener) { | 2199 } else if (this._oldScrollTarget.removeEventListener) { |
| 2556 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); | 2200 this._oldScrollTarget.removeEventListener('scroll', this._boundScrollH
andler); |
| 2557 } | 2201 } |
| 2558 this._oldScrollTarget = null; | 2202 this._oldScrollTarget = null; |
| 2559 } | 2203 } |
| 2560 if (isAttached) { | 2204 if (isAttached) { |
| 2561 // Support element id references | 2205 // Support element id references |
| 2562 if (typeof scrollTarget === 'string') { | 2206 if (typeof scrollTarget === 'string') { |
| 2563 | 2207 |
| 2564 var host = this.domHost; | 2208 var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
| 2565 this.scrollTarget = host && host.$ ? host.$[scrollTarget] : | 2209 this.scrollTarget = (ownerRoot && ownerRoot.$) ? |
| 2566 Polymer.dom(this.ownerDocument).querySelector('#' + scrollTarget); | 2210 ownerRoot.$[scrollTarget] : Polymer.dom(this.ownerDocument).queryS
elector('#' + scrollTarget); |
| 2567 | 2211 |
| 2568 } else if (this._scrollHandler) { | 2212 } else if (this._scrollHandler) { |
| 2569 | 2213 |
| 2570 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); | 2214 this._boundScrollHandler = this._boundScrollHandler || this._scrollHan
dler.bind(this); |
| 2571 // Add a new listener | 2215 // Add a new listener |
| 2572 if (scrollTarget === this._doc) { | 2216 if (scrollTarget === this._doc) { |
| 2573 window.addEventListener('scroll', this._boundScrollHandler); | 2217 window.addEventListener('scroll', this._boundScrollHandler); |
| 2574 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { | 2218 if (this._scrollTop !== 0 || this._scrollLeft !== 0) { |
| 2575 this._scrollHandler(); | 2219 this._scrollHandler(); |
| 2576 } | 2220 } |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2655 window.scrollTo(left, window.pageYOffset); | 2299 window.scrollTo(left, window.pageYOffset); |
| 2656 } else if (this._isValidScrollTarget()) { | 2300 } else if (this._isValidScrollTarget()) { |
| 2657 this.scrollTarget.scrollLeft = left; | 2301 this.scrollTarget.scrollLeft = left; |
| 2658 } | 2302 } |
| 2659 }, | 2303 }, |
| 2660 | 2304 |
| 2661 /** | 2305 /** |
| 2662 * Scrolls the content to a particular place. | 2306 * Scrolls the content to a particular place. |
| 2663 * | 2307 * |
| 2664 * @method scroll | 2308 * @method scroll |
| 2309 * @param {number} top The top position |
| 2665 * @param {number} left The left position | 2310 * @param {number} left The left position |
| 2666 * @param {number} top The top position | |
| 2667 */ | 2311 */ |
| 2668 scroll: function(left, top) { | 2312 scroll: function(top, left) { |
| 2669 if (this.scrollTarget === this._doc) { | 2313 if (this.scrollTarget === this._doc) { |
| 2670 window.scrollTo(left, top); | 2314 window.scrollTo(top, left); |
| 2671 } else if (this._isValidScrollTarget()) { | 2315 } else if (this._isValidScrollTarget()) { |
| 2316 this.scrollTarget.scrollTop = top; |
| 2672 this.scrollTarget.scrollLeft = left; | 2317 this.scrollTarget.scrollLeft = left; |
| 2673 this.scrollTarget.scrollTop = top; | |
| 2674 } | 2318 } |
| 2675 }, | 2319 }, |
| 2676 | 2320 |
| 2677 /** | 2321 /** |
| 2678 * Gets the width of the scroll target. | 2322 * Gets the width of the scroll target. |
| 2679 * | 2323 * |
| 2680 * @type {number} | 2324 * @type {number} |
| 2681 */ | 2325 */ |
| 2682 get _scrollTargetWidth() { | 2326 get _scrollTargetWidth() { |
| 2683 if (this._isValidScrollTarget()) { | 2327 if (this._isValidScrollTarget()) { |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2820 'enter': '_didEnter' | 2464 'enter': '_didEnter' |
| 2821 }, | 2465 }, |
| 2822 | 2466 |
| 2823 /** | 2467 /** |
| 2824 * The ratio of hidden tiles that should remain in the scroll direction. | 2468 * The ratio of hidden tiles that should remain in the scroll direction. |
| 2825 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. | 2469 * Recommended value ~0.5, so it will distribute tiles evely in both directi
ons. |
| 2826 */ | 2470 */ |
| 2827 _ratio: 0.5, | 2471 _ratio: 0.5, |
| 2828 | 2472 |
| 2829 /** | 2473 /** |
| 2830 * The padding-top value for the list. | 2474 * The padding-top value of the `scroller` element |
| 2831 */ | 2475 */ |
| 2832 _scrollerPaddingTop: 0, | 2476 _scrollerPaddingTop: 0, |
| 2833 | 2477 |
| 2834 /** | 2478 /** |
| 2835 * This value is the same as `scrollTop`. | 2479 * This value is the same as `scrollTop`. |
| 2836 */ | 2480 */ |
| 2837 _scrollPosition: 0, | 2481 _scrollPosition: 0, |
| 2838 | 2482 |
| 2839 /** | 2483 /** |
| 2484 * The number of tiles in the DOM. |
| 2485 */ |
| 2486 _physicalCount: 0, |
| 2487 |
| 2488 /** |
| 2489 * The k-th tile that is at the top of the scrolling list. |
| 2490 */ |
| 2491 _physicalStart: 0, |
| 2492 |
| 2493 /** |
| 2494 * The k-th tile that is at the bottom of the scrolling list. |
| 2495 */ |
| 2496 _physicalEnd: 0, |
| 2497 |
| 2498 /** |
| 2840 * The sum of the heights of all the tiles in the DOM. | 2499 * The sum of the heights of all the tiles in the DOM. |
| 2841 */ | 2500 */ |
| 2842 _physicalSize: 0, | 2501 _physicalSize: 0, |
| 2843 | 2502 |
| 2844 /** | 2503 /** |
| 2845 * The average `F` of the tiles observed till now. | 2504 * The average `F` of the tiles observed till now. |
| 2846 */ | 2505 */ |
| 2847 _physicalAverage: 0, | 2506 _physicalAverage: 0, |
| 2848 | 2507 |
| 2849 /** | 2508 /** |
| 2850 * The number of tiles which `offsetHeight` > 0 observed until now. | 2509 * The number of tiles which `offsetHeight` > 0 observed until now. |
| 2851 */ | 2510 */ |
| 2852 _physicalAverageCount: 0, | 2511 _physicalAverageCount: 0, |
| 2853 | 2512 |
| 2854 /** | 2513 /** |
| 2855 * The Y position of the item rendered in the `_physicalStart` | 2514 * The Y position of the item rendered in the `_physicalStart` |
| 2856 * tile relative to the scrolling list. | 2515 * tile relative to the scrolling list. |
| 2857 */ | 2516 */ |
| 2858 _physicalTop: 0, | 2517 _physicalTop: 0, |
| 2859 | 2518 |
| 2860 /** | 2519 /** |
| 2861 * The number of items in the list. | 2520 * The number of items in the list. |
| 2862 */ | 2521 */ |
| 2863 _virtualCount: 0, | 2522 _virtualCount: 0, |
| 2864 | 2523 |
| 2865 /** | 2524 /** |
| 2525 * The n-th item rendered in the `_physicalStart` tile. |
| 2526 */ |
| 2527 _virtualStartVal: 0, |
| 2528 |
| 2529 /** |
| 2866 * A map between an item key and its physical item index | 2530 * A map between an item key and its physical item index |
| 2867 */ | 2531 */ |
| 2868 _physicalIndexForKey: null, | 2532 _physicalIndexForKey: null, |
| 2869 | 2533 |
| 2870 /** | 2534 /** |
| 2871 * The estimated scroll height based on `_physicalAverage` | 2535 * The estimated scroll height based on `_physicalAverage` |
| 2872 */ | 2536 */ |
| 2873 _estScrollHeight: 0, | 2537 _estScrollHeight: 0, |
| 2874 | 2538 |
| 2875 /** | 2539 /** |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2901 */ | 2565 */ |
| 2902 _firstVisibleIndexVal: null, | 2566 _firstVisibleIndexVal: null, |
| 2903 | 2567 |
| 2904 /** | 2568 /** |
| 2905 * A cached value for the last visible index. | 2569 * A cached value for the last visible index. |
| 2906 * See `lastVisibleIndex` | 2570 * See `lastVisibleIndex` |
| 2907 * @type {?number} | 2571 * @type {?number} |
| 2908 */ | 2572 */ |
| 2909 _lastVisibleIndexVal: null, | 2573 _lastVisibleIndexVal: null, |
| 2910 | 2574 |
| 2575 |
| 2911 /** | 2576 /** |
| 2912 * A Polymer collection for the items. | 2577 * A Polymer collection for the items. |
| 2913 * @type {?Polymer.Collection} | 2578 * @type {?Polymer.Collection} |
| 2914 */ | 2579 */ |
| 2915 _collection: null, | 2580 _collection: null, |
| 2916 | 2581 |
| 2917 /** | 2582 /** |
| 2918 * True if the current item list was rendered for the first time | 2583 * True if the current item list was rendered for the first time |
| 2919 * after attached. | 2584 * after attached. |
| 2920 */ | 2585 */ |
| 2921 _itemsRendered: false, | 2586 _itemsRendered: false, |
| 2922 | 2587 |
| 2923 /** | 2588 /** |
| 2924 * The page that is currently rendered. | 2589 * The page that is currently rendered. |
| 2925 */ | 2590 */ |
| 2926 _lastPage: null, | 2591 _lastPage: null, |
| 2927 | 2592 |
| 2928 /** | 2593 /** |
| 2929 * The max number of pages to render. One page is equivalent to the height o
f the list. | 2594 * The max number of pages to render. One page is equivalent to the height o
f the list. |
| 2930 */ | 2595 */ |
| 2931 _maxPages: 3, | 2596 _maxPages: 3, |
| 2932 | 2597 |
| 2933 /** | 2598 /** |
| 2934 * The currently focused physical item. | 2599 * The currently focused item index. |
| 2935 */ | 2600 */ |
| 2936 _focusedItem: null, | 2601 _focusedIndex: 0, |
| 2937 | |
| 2938 /** | |
| 2939 * The index of the `_focusedItem`. | |
| 2940 */ | |
| 2941 _focusedIndex: -1, | |
| 2942 | 2602 |
| 2943 /** | 2603 /** |
| 2944 * The the item that is focused if it is moved offscreen. | 2604 * The the item that is focused if it is moved offscreen. |
| 2945 * @private {?TemplatizerNode} | 2605 * @private {?TemplatizerNode} |
| 2946 */ | 2606 */ |
| 2947 _offscreenFocusedItem: null, | 2607 _offscreenFocusedItem: null, |
| 2948 | 2608 |
| 2949 /** | 2609 /** |
| 2950 * The item that backfills the `_offscreenFocusedItem` in the physical items | 2610 * The item that backfills the `_offscreenFocusedItem` in the physical items |
| 2951 * list when that item is moved offscreen. | 2611 * list when that item is moved offscreen. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 2967 }, | 2627 }, |
| 2968 | 2628 |
| 2969 /** | 2629 /** |
| 2970 * The n-th item rendered in the last physical item. | 2630 * The n-th item rendered in the last physical item. |
| 2971 */ | 2631 */ |
| 2972 get _virtualEnd() { | 2632 get _virtualEnd() { |
| 2973 return this._virtualStart + this._physicalCount - 1; | 2633 return this._virtualStart + this._physicalCount - 1; |
| 2974 }, | 2634 }, |
| 2975 | 2635 |
| 2976 /** | 2636 /** |
| 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 /** | |
| 2991 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. | 2637 * The lowest n-th value for an item such that it can be rendered in `_physi
calStart`. |
| 2992 */ | 2638 */ |
| 2993 _minVirtualStart: 0, | 2639 _minVirtualStart: 0, |
| 2994 | 2640 |
| 2995 /** | 2641 /** |
| 2996 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. | 2642 * The largest n-th value for an item such that it can be rendered in `_phys
icalStart`. |
| 2997 */ | 2643 */ |
| 2998 get _maxVirtualStart() { | 2644 get _maxVirtualStart() { |
| 2999 return Math.max(0, this._virtualCount - this._physicalCount); | 2645 return Math.max(0, this._virtualCount - this._physicalCount); |
| 3000 }, | 2646 }, |
| 3001 | 2647 |
| 3002 /** | 2648 /** |
| 3003 * The n-th item rendered in the `_physicalStart` tile. | 2649 * The height of the physical content that isn't on the screen. |
| 3004 */ | 2650 */ |
| 3005 _virtualStartVal: 0, | 2651 get _hiddenContentSize() { |
| 3006 | 2652 return this._physicalSize - this._viewportSize; |
| 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; | |
| 3013 }, | 2653 }, |
| 3014 | 2654 |
| 3015 /** | 2655 /** |
| 3016 * The k-th tile that is at the top of the scrolling list. | 2656 * The maximum scroll top value. |
| 3017 */ | 2657 */ |
| 3018 _physicalStartVal: 0, | 2658 get _maxScrollTop() { |
| 3019 | 2659 return this._estScrollHeight - this._viewportSize; |
| 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; | |
| 3030 }, | 2660 }, |
| 3031 | 2661 |
| 3032 /** | 2662 /** |
| 3033 * The number of tiles in the DOM. | 2663 * Sets the n-th item rendered in `_physicalStart` |
| 3034 */ | 2664 */ |
| 3035 _physicalCountVal: 0, | 2665 set _virtualStart(val) { |
| 3036 | 2666 // clamp the value so that _minVirtualStart <= val <= _maxVirtualStart |
| 3037 set _physicalCount(val) { | 2667 this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._min
VirtualStart, val)); |
| 3038 this._physicalCountVal = val; | 2668 if (this._physicalCount === 0) { |
| 3039 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this
._physicalCount; | 2669 this._physicalStart = 0; |
| 3040 }, | 2670 this._physicalEnd = 0; |
| 3041 | 2671 } else { |
| 3042 get _physicalCount() { | 2672 this._physicalStart = this._virtualStartVal % this._physicalCount; |
| 3043 return this._physicalCountVal; | 2673 this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % th
is._physicalCount; |
| 2674 } |
| 3044 }, | 2675 }, |
| 3045 | 2676 |
| 3046 /** | 2677 /** |
| 3047 * The k-th tile that is at the bottom of the scrolling list. | 2678 * Gets the n-th item rendered in `_physicalStart` |
| 3048 */ | 2679 */ |
| 3049 _physicalEnd: 0, | 2680 get _virtualStart() { |
| 2681 return this._virtualStartVal; |
| 2682 }, |
| 3050 | 2683 |
| 3051 /** | 2684 /** |
| 3052 * An optimal physical size such that we will have enough physical items | 2685 * An optimal physical size such that we will have enough physical items |
| 3053 * to fill up the viewport and recycle when the user scrolls. | 2686 * to fill up the viewport and recycle when the user scrolls. |
| 3054 * | 2687 * |
| 3055 * This default value assumes that we will at least have the equivalent | 2688 * This default value assumes that we will at least have the equivalent |
| 3056 * to a viewport of physical items above and below the user's viewport. | 2689 * to a viewport of physical items above and below the user's viewport. |
| 3057 */ | 2690 */ |
| 3058 get _optPhysicalSize() { | 2691 get _optPhysicalSize() { |
| 3059 return this._viewportSize * this._maxPages; | 2692 return this._viewportSize * this._maxPages; |
| 3060 }, | 2693 }, |
| 3061 | 2694 |
| 3062 /** | 2695 /** |
| 3063 * True if the current list is visible. | 2696 * True if the current list is visible. |
| 3064 */ | 2697 */ |
| 3065 get _isVisible() { | 2698 get _isVisible() { |
| 3066 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); | 2699 return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.
scrollTarget.offsetHeight); |
| 3067 }, | 2700 }, |
| 3068 | 2701 |
| 3069 /** | 2702 /** |
| 3070 * Gets the index of the first visible item in the viewport. | 2703 * Gets the index of the first visible item in the viewport. |
| 3071 * | 2704 * |
| 3072 * @type {number} | 2705 * @type {number} |
| 3073 */ | 2706 */ |
| 3074 get firstVisibleIndex() { | 2707 get firstVisibleIndex() { |
| 3075 if (this._firstVisibleIndexVal === null) { | 2708 if (this._firstVisibleIndexVal === null) { |
| 3076 var physicalOffset = this._physicalTop + this._scrollerPaddingTop; | 2709 var physicalOffset = this._physicalTop; |
| 3077 | 2710 |
| 3078 this._firstVisibleIndexVal = this._iterateItems( | 2711 this._firstVisibleIndexVal = this._iterateItems( |
| 3079 function(pidx, vidx) { | 2712 function(pidx, vidx) { |
| 3080 physicalOffset += this._physicalSizes[pidx]; | 2713 physicalOffset += this._physicalSizes[pidx]; |
| 2714 |
| 3081 if (physicalOffset > this._scrollPosition) { | 2715 if (physicalOffset > this._scrollPosition) { |
| 3082 return vidx; | 2716 return vidx; |
| 3083 } | 2717 } |
| 3084 }) || 0; | 2718 }) || 0; |
| 3085 } | 2719 } |
| 3086 return this._firstVisibleIndexVal; | 2720 return this._firstVisibleIndexVal; |
| 3087 }, | 2721 }, |
| 3088 | 2722 |
| 3089 /** | 2723 /** |
| 3090 * Gets the index of the last visible item in the viewport. | 2724 * Gets the index of the last visible item in the viewport. |
| 3091 * | 2725 * |
| 3092 * @type {number} | 2726 * @type {number} |
| 3093 */ | 2727 */ |
| 3094 get lastVisibleIndex() { | 2728 get lastVisibleIndex() { |
| 3095 if (this._lastVisibleIndexVal === null) { | 2729 if (this._lastVisibleIndexVal === null) { |
| 3096 var physicalOffset = this._physicalTop; | 2730 var physicalOffset = this._physicalTop; |
| 3097 | 2731 |
| 3098 this._iterateItems(function(pidx, vidx) { | 2732 this._iterateItems(function(pidx, vidx) { |
| 3099 physicalOffset += this._physicalSizes[pidx]; | 2733 physicalOffset += this._physicalSizes[pidx]; |
| 3100 | 2734 |
| 3101 if (physicalOffset <= this._scrollBottom) { | 2735 if(physicalOffset <= this._scrollBottom) { |
| 3102 this._lastVisibleIndexVal = vidx; | 2736 this._lastVisibleIndexVal = vidx; |
| 3103 } | 2737 } |
| 3104 }); | 2738 }); |
| 3105 } | 2739 } |
| 3106 return this._lastVisibleIndexVal; | 2740 return this._lastVisibleIndexVal; |
| 3107 }, | 2741 }, |
| 3108 | 2742 |
| 3109 get _defaultScrollTarget() { | |
| 3110 return this; | |
| 3111 }, | |
| 3112 | |
| 3113 ready: function() { | 2743 ready: function() { |
| 3114 this.addEventListener('focus', this._didFocus.bind(this), true); | 2744 this.addEventListener('focus', this._didFocus.bind(this), true); |
| 3115 }, | 2745 }, |
| 3116 | 2746 |
| 3117 attached: function() { | 2747 attached: function() { |
| 3118 this.updateViewportBoundaries(); | 2748 this.updateViewportBoundaries(); |
| 3119 this._render(); | 2749 this._render(); |
| 3120 }, | 2750 }, |
| 3121 | 2751 |
| 3122 detached: function() { | 2752 detached: function() { |
| 3123 this._itemsRendered = false; | 2753 this._itemsRendered = false; |
| 3124 }, | 2754 }, |
| 3125 | 2755 |
| 2756 get _defaultScrollTarget() { |
| 2757 return this; |
| 2758 }, |
| 2759 |
| 3126 /** | 2760 /** |
| 3127 * Set the overflow property if this element has its own scrolling region | 2761 * Set the overflow property if this element has its own scrolling region |
| 3128 */ | 2762 */ |
| 3129 _setOverflow: function(scrollTarget) { | 2763 _setOverflow: function(scrollTarget) { |
| 3130 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; | 2764 this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : ''; |
| 3131 this.style.overflow = scrollTarget === this ? 'auto' : ''; | 2765 this.style.overflow = scrollTarget === this ? 'auto' : ''; |
| 3132 }, | 2766 }, |
| 3133 | 2767 |
| 3134 /** | 2768 /** |
| 3135 * Invoke this method if you dynamically update the viewport's | 2769 * Invoke this method if you dynamically update the viewport's |
| 3136 * size or CSS padding. | 2770 * size or CSS padding. |
| 3137 * | 2771 * |
| 3138 * @method updateViewportBoundaries | 2772 * @method updateViewportBoundaries |
| 3139 */ | 2773 */ |
| 3140 updateViewportBoundaries: function() { | 2774 updateViewportBoundaries: function() { |
| 3141 this._scrollerPaddingTop = this.scrollTarget === this ? 0 : | 2775 var scrollerStyle = window.getComputedStyle(this.scrollTarget); |
| 3142 parseInt(window.getComputedStyle(this)['padding-top'], 10); | 2776 this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10); |
| 3143 | |
| 3144 this._viewportSize = this._scrollTargetHeight; | 2777 this._viewportSize = this._scrollTargetHeight; |
| 3145 }, | 2778 }, |
| 3146 | 2779 |
| 3147 /** | 2780 /** |
| 3148 * Update the models, the position of the | 2781 * Update the models, the position of the |
| 3149 * items in the viewport and recycle tiles as needed. | 2782 * items in the viewport and recycle tiles as needed. |
| 3150 */ | 2783 */ |
| 3151 _scrollHandler: function() { | 2784 _scrollHandler: function() { |
| 3152 // clamp the `scrollTop` value | 2785 // clamp the `scrollTop` value |
| 2786 // IE 10|11 scrollTop may go above `_maxScrollTop` |
| 2787 // iOS `scrollTop` may go below 0 and above `_maxScrollTop` |
| 3153 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; | 2788 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop))
; |
| 3154 var delta = scrollTop - this._scrollPosition; | |
| 3155 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto
m; | 2789 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBotto
m; |
| 3156 var ratio = this._ratio; | 2790 var ratio = this._ratio; |
| 2791 var delta = scrollTop - this._scrollPosition; |
| 3157 var recycledTiles = 0; | 2792 var recycledTiles = 0; |
| 3158 var hiddenContentSize = this._hiddenContentSize; | 2793 var hiddenContentSize = this._hiddenContentSize; |
| 3159 var currentRatio = ratio; | 2794 var currentRatio = ratio; |
| 3160 var movingUp = []; | 2795 var movingUp = []; |
| 3161 | 2796 |
| 3162 // track the last `scrollTop` | 2797 // track the last `scrollTop` |
| 3163 this._scrollPosition = scrollTop; | 2798 this._scrollPosition = scrollTop; |
| 3164 | 2799 |
| 3165 // clear cached visible indexes | 2800 // clear cached visible index |
| 3166 this._firstVisibleIndexVal = null; | 2801 this._firstVisibleIndexVal = null; |
| 3167 this._lastVisibleIndexVal = null; | 2802 this._lastVisibleIndexVal = null; |
| 3168 | 2803 |
| 3169 scrollBottom = this._scrollBottom; | 2804 scrollBottom = this._scrollBottom; |
| 3170 physicalBottom = this._physicalBottom; | 2805 physicalBottom = this._physicalBottom; |
| 3171 | 2806 |
| 3172 // random access | 2807 // random access |
| 3173 if (Math.abs(delta) > this._physicalSize) { | 2808 if (Math.abs(delta) > this._physicalSize) { |
| 3174 this._physicalTop += delta; | 2809 this._physicalTop += delta; |
| 3175 recycledTiles = Math.round(delta / this._physicalAverage); | 2810 recycledTiles = Math.round(delta / this._physicalAverage); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3242 | 2877 |
| 3243 if (recycledTiles === 0) { | 2878 if (recycledTiles === 0) { |
| 3244 // If the list ever reach this case, the physical average is not signifi
cant enough | 2879 // If the list ever reach this case, the physical average is not signifi
cant enough |
| 3245 // to create all the items needed to cover the entire viewport. | 2880 // to create all the items needed to cover the entire viewport. |
| 3246 // e.g. A few items have a height that differs from the average by serve
ral order of magnitude. | 2881 // e.g. A few items have a height that differs from the average by serve
ral order of magnitude. |
| 3247 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { | 2882 if (physicalBottom < scrollBottom || this._physicalTop > scrollTop) { |
| 3248 this.async(this._increasePool.bind(this, 1)); | 2883 this.async(this._increasePool.bind(this, 1)); |
| 3249 } | 2884 } |
| 3250 } else { | 2885 } else { |
| 3251 this._virtualStart = this._virtualStart + recycledTiles; | 2886 this._virtualStart = this._virtualStart + recycledTiles; |
| 3252 this._physicalStart = this._physicalStart + recycledTiles; | |
| 3253 this._update(recycledTileSet, movingUp); | 2887 this._update(recycledTileSet, movingUp); |
| 3254 } | 2888 } |
| 3255 }, | 2889 }, |
| 3256 | 2890 |
| 3257 /** | 2891 /** |
| 3258 * Update the list of items, starting from the `_virtualStart` item. | 2892 * Update the list of items, starting from the `_virtualStart` item. |
| 3259 * @param {!Array<number>=} itemSet | 2893 * @param {!Array<number>=} itemSet |
| 3260 * @param {!Array<number>=} movingUp | 2894 * @param {!Array<number>=} movingUp |
| 3261 */ | 2895 */ |
| 3262 _update: function(itemSet, movingUp) { | 2896 _update: function(itemSet, movingUp) { |
| 3263 // manage focus | 2897 // manage focus |
| 3264 this._manageFocus(); | 2898 if (this._isIndexRendered(this._focusedIndex)) { |
| 2899 this._restoreFocusedItem(); |
| 2900 } else { |
| 2901 this._createFocusBackfillItem(); |
| 2902 } |
| 3265 // update models | 2903 // update models |
| 3266 this._assignModels(itemSet); | 2904 this._assignModels(itemSet); |
| 3267 // measure heights | 2905 // measure heights |
| 3268 this._updateMetrics(itemSet); | 2906 this._updateMetrics(itemSet); |
| 3269 // adjust offset after measuring | 2907 // adjust offset after measuring |
| 3270 if (movingUp) { | 2908 if (movingUp) { |
| 3271 while (movingUp.length) { | 2909 while (movingUp.length) { |
| 3272 this._physicalTop -= this._physicalSizes[movingUp.pop()]; | 2910 this._physicalTop -= this._physicalSizes[movingUp.pop()]; |
| 3273 } | 2911 } |
| 3274 } | 2912 } |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3321 this._debounceTemplate(this._increasePool.bind(this, 1)); | 2959 this._debounceTemplate(this._increasePool.bind(this, 1)); |
| 3322 } | 2960 } |
| 3323 this._lastPage = currentPage; | 2961 this._lastPage = currentPage; |
| 3324 return true; | 2962 return true; |
| 3325 }, | 2963 }, |
| 3326 | 2964 |
| 3327 /** | 2965 /** |
| 3328 * Increases the pool size. | 2966 * Increases the pool size. |
| 3329 */ | 2967 */ |
| 3330 _increasePool: function(missingItems) { | 2968 _increasePool: function(missingItems) { |
| 2969 // limit the size |
| 3331 var nextPhysicalCount = Math.min( | 2970 var nextPhysicalCount = Math.min( |
| 3332 this._physicalCount + missingItems, | 2971 this._physicalCount + missingItems, |
| 3333 this._virtualCount - this._virtualStart, | 2972 this._virtualCount - this._virtualStart, |
| 3334 MAX_PHYSICAL_COUNT | 2973 MAX_PHYSICAL_COUNT |
| 3335 ); | 2974 ); |
| 3336 var prevPhysicalCount = this._physicalCount; | 2975 var prevPhysicalCount = this._physicalCount; |
| 3337 var delta = nextPhysicalCount - prevPhysicalCount; | 2976 var delta = nextPhysicalCount - prevPhysicalCount; |
| 3338 | 2977 |
| 3339 if (delta <= 0) { | 2978 if (delta > 0) { |
| 3340 return; | 2979 [].push.apply(this._physicalItems, this._createPool(delta)); |
| 2980 [].push.apply(this._physicalSizes, new Array(delta)); |
| 2981 |
| 2982 this._physicalCount = prevPhysicalCount + delta; |
| 2983 // tail call |
| 2984 return this._update(); |
| 3341 } | 2985 } |
| 3342 | |
| 3343 [].push.apply(this._physicalItems, this._createPool(delta)); | |
| 3344 [].push.apply(this._physicalSizes, new Array(delta)); | |
| 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; | |
| 3355 } | |
| 3356 this._update(); | |
| 3357 }, | 2986 }, |
| 3358 | 2987 |
| 3359 /** | 2988 /** |
| 3360 * Render a new list of items. This method does exactly the same as `update`
, | 2989 * Render a new list of items. This method does exactly the same as `update`
, |
| 3361 * but it also ensures that only one `update` cycle is created. | 2990 * but it also ensures that only one `update` cycle is created. |
| 3362 */ | 2991 */ |
| 3363 _render: function() { | 2992 _render: function() { |
| 3364 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; | 2993 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; |
| 3365 | 2994 |
| 3366 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { | 2995 if (this.isAttached && !this._itemsRendered && this._isVisible && requires
Update) { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3435 _forwardParentPath: function(path, value) { | 3064 _forwardParentPath: function(path, value) { |
| 3436 if (this._physicalItems) { | 3065 if (this._physicalItems) { |
| 3437 this._physicalItems.forEach(function(item) { | 3066 this._physicalItems.forEach(function(item) { |
| 3438 item._templateInstance.notifyPath(path, value, true); | 3067 item._templateInstance.notifyPath(path, value, true); |
| 3439 }, this); | 3068 }, this); |
| 3440 } | 3069 } |
| 3441 }, | 3070 }, |
| 3442 | 3071 |
| 3443 /** | 3072 /** |
| 3444 * Called as a side effect of a host items.<key>.<path> path change, | 3073 * Called as a side effect of a host items.<key>.<path> path change, |
| 3445 * responsible for notifying item.<path> changes. | 3074 * responsible for notifying item.<path> changes to row for key. |
| 3446 */ | 3075 */ |
| 3447 _forwardItemPath: function(path, value) { | 3076 _forwardItemPath: function(path, value) { |
| 3448 if (!this._physicalIndexForKey) { | 3077 if (this._physicalIndexForKey) { |
| 3449 return; | 3078 var dot = path.indexOf('.'); |
| 3450 } | 3079 var key = path.substring(0, dot < 0 ? path.length : dot); |
| 3451 var inst; | 3080 var idx = this._physicalIndexForKey[key]; |
| 3452 var dot = path.indexOf('.'); | 3081 var row = this._physicalItems[idx]; |
| 3453 var key = path.substring(0, dot < 0 ? path.length : dot); | |
| 3454 var idx = this._physicalIndexForKey[key]; | |
| 3455 var el = this._physicalItems[idx]; | |
| 3456 | 3082 |
| 3457 | 3083 if (idx === this._focusedIndex && this._offscreenFocusedItem) { |
| 3458 if (idx === this._focusedIndex && this._offscreenFocusedItem) { | 3084 row = this._offscreenFocusedItem; |
| 3459 el = this._offscreenFocusedItem; | 3085 } |
| 3460 } | 3086 if (row) { |
| 3461 if (!el) { | 3087 var inst = row._templateInstance; |
| 3462 return; | 3088 if (dot >= 0) { |
| 3463 } | 3089 path = this.as + '.' + path.substring(dot+1); |
| 3464 | 3090 inst.notifyPath(path, value, true); |
| 3465 inst = el._templateInstance; | 3091 } else { |
| 3466 | 3092 inst[this.as] = value; |
| 3467 if (inst.__key__ !== key) { | 3093 } |
| 3468 return; | 3094 } |
| 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; | |
| 3475 } | 3095 } |
| 3476 }, | 3096 }, |
| 3477 | 3097 |
| 3478 /** | 3098 /** |
| 3479 * Called when the items have changed. That is, ressignments | 3099 * Called when the items have changed. That is, ressignments |
| 3480 * to `items`, splices or updates to a single item. | 3100 * to `items`, splices or updates to a single item. |
| 3481 */ | 3101 */ |
| 3482 _itemsChanged: function(change) { | 3102 _itemsChanged: function(change) { |
| 3483 if (change.path === 'items') { | 3103 if (change.path === 'items') { |
| 3484 // reset items | 3104 |
| 3105 this._restoreFocusedItem(); |
| 3106 // render the new set |
| 3107 this._itemsRendered = false; |
| 3108 // update the whole set |
| 3485 this._virtualStart = 0; | 3109 this._virtualStart = 0; |
| 3486 this._physicalTop = 0; | 3110 this._physicalTop = 0; |
| 3487 this._virtualCount = this.items ? this.items.length : 0; | 3111 this._virtualCount = this.items ? this.items.length : 0; |
| 3112 this._focusedIndex = 0; |
| 3488 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; | 3113 this._collection = this.items ? Polymer.Collection.get(this.items) : nul
l; |
| 3489 this._physicalIndexForKey = {}; | 3114 this._physicalIndexForKey = {}; |
| 3490 | 3115 |
| 3491 this._resetScrollPosition(0); | 3116 this._resetScrollPosition(0); |
| 3492 this._removeFocusedItem(); | |
| 3493 | 3117 |
| 3494 // create the initial physical items | 3118 // create the initial physical items |
| 3495 if (!this._physicalItems) { | 3119 if (!this._physicalItems) { |
| 3496 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); | 3120 this._physicalCount = Math.max(1, Math.min(DEFAULT_PHYSICAL_COUNT, thi
s._virtualCount)); |
| 3497 this._physicalItems = this._createPool(this._physicalCount); | 3121 this._physicalItems = this._createPool(this._physicalCount); |
| 3498 this._physicalSizes = new Array(this._physicalCount); | 3122 this._physicalSizes = new Array(this._physicalCount); |
| 3499 } | 3123 } |
| 3500 | 3124 this._debounceTemplate(this._render); |
| 3501 this._physicalStart = 0; | |
| 3502 | 3125 |
| 3503 } else if (change.path === 'items.splices') { | 3126 } else if (change.path === 'items.splices') { |
| 3127 // render the new set |
| 3128 this._itemsRendered = false; |
| 3504 this._adjustVirtualIndex(change.value.indexSplices); | 3129 this._adjustVirtualIndex(change.value.indexSplices); |
| 3505 this._virtualCount = this.items ? this.items.length : 0; | 3130 this._virtualCount = this.items ? this.items.length : 0; |
| 3506 | 3131 |
| 3132 this._debounceTemplate(this._render); |
| 3133 |
| 3134 if (this._focusedIndex < 0 || this._focusedIndex >= this._virtualCount)
{ |
| 3135 this._focusedIndex = 0; |
| 3136 } |
| 3137 this._debounceTemplate(this._render); |
| 3138 |
| 3507 } else { | 3139 } else { |
| 3508 // update a single item | 3140 // update a single item |
| 3509 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); | 3141 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change.
value); |
| 3510 return; | |
| 3511 } | 3142 } |
| 3512 | |
| 3513 this._itemsRendered = false; | |
| 3514 this._debounceTemplate(this._render); | |
| 3515 }, | 3143 }, |
| 3516 | 3144 |
| 3517 /** | 3145 /** |
| 3518 * @param {!Array<!PolymerSplice>} splices | 3146 * @param {!Array<!PolymerSplice>} splices |
| 3519 */ | 3147 */ |
| 3520 _adjustVirtualIndex: function(splices) { | 3148 _adjustVirtualIndex: function(splices) { |
| 3521 splices.forEach(function(splice) { | 3149 var i, splice, idx; |
| 3150 |
| 3151 for (i = 0; i < splices.length; i++) { |
| 3152 splice = splices[i]; |
| 3153 |
| 3522 // deselect removed items | 3154 // deselect removed items |
| 3523 splice.removed.forEach(this._removeItem, this); | 3155 splice.removed.forEach(this.$.selector.deselect, this.$.selector); |
| 3156 |
| 3157 idx = splice.index; |
| 3524 // We only need to care about changes happening above the current positi
on | 3158 // We only need to care about changes happening above the current positi
on |
| 3525 if (splice.index < this._virtualStart) { | 3159 if (idx >= this._virtualStart) { |
| 3526 var delta = Math.max( | 3160 break; |
| 3527 splice.addedCount - splice.removed.length, | 3161 } |
| 3528 splice.index - this._virtualStart); | |
| 3529 | 3162 |
| 3530 this._virtualStart = this._virtualStart + delta; | 3163 this._virtualStart = this._virtualStart + |
| 3531 | 3164 Math.max(splice.addedCount - splice.removed.length, idx - this._virt
ualStart); |
| 3532 if (this._focusedIndex >= 0) { | |
| 3533 this._focusedIndex = this._focusedIndex + delta; | |
| 3534 } | |
| 3535 } | |
| 3536 }, this); | |
| 3537 }, | |
| 3538 | |
| 3539 _removeItem: function(item) { | |
| 3540 this.$.selector.deselect(item); | |
| 3541 // remove the current focused item | |
| 3542 if (this._focusedItem && this._focusedItem._templateInstance[this.as] ===
item) { | |
| 3543 this._removeFocusedItem(); | |
| 3544 } | 3165 } |
| 3545 }, | 3166 }, |
| 3546 | 3167 |
| 3547 /** | 3168 /** |
| 3548 * Executes a provided function per every physical index in `itemSet` | 3169 * Executes a provided function per every physical index in `itemSet` |
| 3549 * `itemSet` default value is equivalent to the entire set of physical index
es. | 3170 * `itemSet` default value is equivalent to the entire set of physical index
es. |
| 3550 * | 3171 * |
| 3551 * @param {!function(number, number)} fn | 3172 * @param {!function(number, number)} fn |
| 3552 * @param {!Array<number>=} itemSet | 3173 * @param {!Array<number>=} itemSet |
| 3553 */ | 3174 */ |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3586 /** | 3207 /** |
| 3587 * Assigns the data models to a given set of items. | 3208 * Assigns the data models to a given set of items. |
| 3588 * @param {!Array<number>=} itemSet | 3209 * @param {!Array<number>=} itemSet |
| 3589 */ | 3210 */ |
| 3590 _assignModels: function(itemSet) { | 3211 _assignModels: function(itemSet) { |
| 3591 this._iterateItems(function(pidx, vidx) { | 3212 this._iterateItems(function(pidx, vidx) { |
| 3592 var el = this._physicalItems[pidx]; | 3213 var el = this._physicalItems[pidx]; |
| 3593 var inst = el._templateInstance; | 3214 var inst = el._templateInstance; |
| 3594 var item = this.items && this.items[vidx]; | 3215 var item = this.items && this.items[vidx]; |
| 3595 | 3216 |
| 3596 if (item != null) { | 3217 if (item !== undefined && item !== null) { |
| 3597 inst[this.as] = item; | 3218 inst[this.as] = item; |
| 3598 inst.__key__ = this._collection.getKey(item); | 3219 inst.__key__ = this._collection.getKey(item); |
| 3599 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); | 3220 inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.s
elector).isSelected(item); |
| 3600 inst[this.indexAs] = vidx; | 3221 inst[this.indexAs] = vidx; |
| 3601 inst.tabIndex = this._focusedIndex === vidx ? 0 : -1; | 3222 inst.tabIndex = vidx === this._focusedIndex ? 0 : -1; |
| 3223 el.removeAttribute('hidden'); |
| 3602 this._physicalIndexForKey[inst.__key__] = pidx; | 3224 this._physicalIndexForKey[inst.__key__] = pidx; |
| 3603 el.removeAttribute('hidden'); | |
| 3604 } else { | 3225 } else { |
| 3605 inst.__key__ = null; | 3226 inst.__key__ = null; |
| 3606 el.setAttribute('hidden', ''); | 3227 el.setAttribute('hidden', ''); |
| 3607 } | 3228 } |
| 3229 |
| 3608 }, itemSet); | 3230 }, itemSet); |
| 3609 }, | 3231 }, |
| 3610 | 3232 |
| 3611 /** | 3233 /** |
| 3612 * Updates the height for a given set of items. | 3234 * Updates the height for a given set of items. |
| 3613 * | 3235 * |
| 3614 * @param {!Array<number>=} itemSet | 3236 * @param {!Array<number>=} itemSet |
| 3615 */ | 3237 */ |
| 3616 _updateMetrics: function(itemSet) { | 3238 _updateMetrics: function(itemSet) { |
| 3617 // Make sure we distributed all the physical items | 3239 // Make sure we distributed all the physical items |
| (...skipping 27 matching lines...) Expand all Loading... |
| 3645 | 3267 |
| 3646 /** | 3268 /** |
| 3647 * Updates the position of the physical items. | 3269 * Updates the position of the physical items. |
| 3648 */ | 3270 */ |
| 3649 _positionItems: function() { | 3271 _positionItems: function() { |
| 3650 this._adjustScrollPosition(); | 3272 this._adjustScrollPosition(); |
| 3651 | 3273 |
| 3652 var y = this._physicalTop; | 3274 var y = this._physicalTop; |
| 3653 | 3275 |
| 3654 this._iterateItems(function(pidx) { | 3276 this._iterateItems(function(pidx) { |
| 3277 |
| 3655 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); | 3278 this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]); |
| 3656 y += this._physicalSizes[pidx]; | 3279 y += this._physicalSizes[pidx]; |
| 3280 |
| 3657 }); | 3281 }); |
| 3658 }, | 3282 }, |
| 3659 | 3283 |
| 3660 /** | 3284 /** |
| 3661 * Adjusts the scroll position when it was overestimated. | 3285 * Adjusts the scroll position when it was overestimated. |
| 3662 */ | 3286 */ |
| 3663 _adjustScrollPosition: function() { | 3287 _adjustScrollPosition: function() { |
| 3664 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : | 3288 var deltaHeight = this._virtualStart === 0 ? this._physicalTop : |
| 3665 Math.min(this._scrollPosition + this._physicalTop, 0); | 3289 Math.min(this._scrollPosition + this._physicalTop, 0); |
| 3666 | 3290 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 3694 | 3318 |
| 3695 forceUpdate = forceUpdate || this._scrollHeight === 0; | 3319 forceUpdate = forceUpdate || this._scrollHeight === 0; |
| 3696 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; | 3320 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight
- this._physicalSize; |
| 3697 | 3321 |
| 3698 // amortize height adjustment, so it won't trigger repaints very often | 3322 // amortize height adjustment, so it won't trigger repaints very often |
| 3699 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { | 3323 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >=
this._optPhysicalSize) { |
| 3700 this.$.items.style.height = this._estScrollHeight + 'px'; | 3324 this.$.items.style.height = this._estScrollHeight + 'px'; |
| 3701 this._scrollHeight = this._estScrollHeight; | 3325 this._scrollHeight = this._estScrollHeight; |
| 3702 } | 3326 } |
| 3703 }, | 3327 }, |
| 3328 |
| 3704 /** | 3329 /** |
| 3705 * Scroll to a specific item in the virtual list regardless | 3330 * Scroll to a specific item in the virtual list regardless |
| 3706 * of the physical items in the DOM tree. | 3331 * of the physical items in the DOM tree. |
| 3707 * | 3332 * |
| 3708 * @method scrollToIndex | 3333 * @method scrollToIndex |
| 3709 * @param {number} idx The index of the item | 3334 * @param {number} idx The index of the item |
| 3710 */ | 3335 */ |
| 3711 scrollToIndex: function(idx) { | 3336 scrollToIndex: function(idx) { |
| 3712 if (typeof idx !== 'number') { | 3337 if (typeof idx !== 'number') { |
| 3713 return; | 3338 return; |
| 3714 } | 3339 } |
| 3715 | 3340 |
| 3716 Polymer.dom.flush(); | 3341 Polymer.dom.flush(); |
| 3717 | 3342 |
| 3343 var firstVisible = this.firstVisibleIndex; |
| 3718 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); | 3344 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); |
| 3719 // update the virtual start only when needed | 3345 |
| 3720 if (!this._isIndexRendered(idx) || idx >= this._maxVirtualStart) { | 3346 // start at the previous virtual item |
| 3721 this._virtualStart = idx - 1; | 3347 // so we have a item above the first visible item |
| 3722 } | 3348 this._virtualStart = idx - 1; |
| 3723 // manage focus | |
| 3724 this._manageFocus(); | |
| 3725 // assign new models | 3349 // assign new models |
| 3726 this._assignModels(); | 3350 this._assignModels(); |
| 3727 // measure the new sizes | 3351 // measure the new sizes |
| 3728 this._updateMetrics(); | 3352 this._updateMetrics(); |
| 3729 // estimate new physical offset | 3353 // estimate new physical offset |
| 3730 this._physicalTop = this._virtualStart * this._physicalAverage; | 3354 this._physicalTop = this._virtualStart * this._physicalAverage; |
| 3731 | 3355 |
| 3732 var currentTopItem = this._physicalStart; | 3356 var currentTopItem = this._physicalStart; |
| 3733 var currentVirtualItem = this._virtualStart; | 3357 var currentVirtualItem = this._virtualStart; |
| 3734 var targetOffsetTop = 0; | 3358 var targetOffsetTop = 0; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3777 this.updateViewportBoundaries(); | 3401 this.updateViewportBoundaries(); |
| 3778 this.scrollToIndex(this.firstVisibleIndex); | 3402 this.scrollToIndex(this.firstVisibleIndex); |
| 3779 } | 3403 } |
| 3780 }); | 3404 }); |
| 3781 }, | 3405 }, |
| 3782 | 3406 |
| 3783 _getModelFromItem: function(item) { | 3407 _getModelFromItem: function(item) { |
| 3784 var key = this._collection.getKey(item); | 3408 var key = this._collection.getKey(item); |
| 3785 var pidx = this._physicalIndexForKey[key]; | 3409 var pidx = this._physicalIndexForKey[key]; |
| 3786 | 3410 |
| 3787 if (pidx != null) { | 3411 if (pidx !== undefined) { |
| 3788 return this._physicalItems[pidx]._templateInstance; | 3412 return this._physicalItems[pidx]._templateInstance; |
| 3789 } | 3413 } |
| 3790 return null; | 3414 return null; |
| 3791 }, | 3415 }, |
| 3792 | 3416 |
| 3793 /** | 3417 /** |
| 3794 * Gets a valid item instance from its index or the object value. | 3418 * Gets a valid item instance from its index or the object value. |
| 3795 * | 3419 * |
| 3796 * @param {(Object|number)} item The item object or its index | 3420 * @param {(Object|number)} item The item object or its index |
| 3797 */ | 3421 */ |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3915 * Updates the size of an item. | 3539 * Updates the size of an item. |
| 3916 * | 3540 * |
| 3917 * @method updateSizeForItem | 3541 * @method updateSizeForItem |
| 3918 * @param {(Object|number)} item The item object or its index | 3542 * @param {(Object|number)} item The item object or its index |
| 3919 */ | 3543 */ |
| 3920 updateSizeForItem: function(item) { | 3544 updateSizeForItem: function(item) { |
| 3921 item = this._getNormalizedItem(item); | 3545 item = this._getNormalizedItem(item); |
| 3922 var key = this._collection.getKey(item); | 3546 var key = this._collection.getKey(item); |
| 3923 var pidx = this._physicalIndexForKey[key]; | 3547 var pidx = this._physicalIndexForKey[key]; |
| 3924 | 3548 |
| 3925 if (pidx != null) { | 3549 if (pidx !== undefined) { |
| 3926 this._updateMetrics([pidx]); | 3550 this._updateMetrics([pidx]); |
| 3927 this._positionItems(); | 3551 this._positionItems(); |
| 3928 } | 3552 } |
| 3929 }, | 3553 }, |
| 3930 | 3554 |
| 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 | |
| 3956 _isIndexRendered: function(idx) { | 3555 _isIndexRendered: function(idx) { |
| 3957 return idx >= this._virtualStart && idx <= this._virtualEnd; | 3556 return idx >= this._virtualStart && idx <= this._virtualEnd; |
| 3958 }, | 3557 }, |
| 3959 | 3558 |
| 3960 _isIndexVisible: function(idx) { | 3559 _getPhysicalItemForIndex: function(idx, force) { |
| 3961 return idx >= this.firstVisibleIndex && idx <= this.lastVisibleIndex; | 3560 if (!this._collection) { |
| 3962 }, | 3561 return null; |
| 3963 | 3562 } |
| 3964 _getPhysicalIndex: function(idx) { | 3563 if (!this._isIndexRendered(idx)) { |
| 3965 return this._physicalIndexForKey[this._collection.getKey(this._getNormaliz
edItem(idx))]; | 3564 if (force) { |
| 3565 this.scrollToIndex(idx); |
| 3566 return this._getPhysicalItemForIndex(idx, false); |
| 3567 } |
| 3568 return null; |
| 3569 } |
| 3570 var item = this._getNormalizedItem(idx); |
| 3571 var physicalItem = this._physicalItems[this._physicalIndexForKey[this._col
lection.getKey(item)]]; |
| 3572 |
| 3573 return physicalItem || null; |
| 3966 }, | 3574 }, |
| 3967 | 3575 |
| 3968 _focusPhysicalItem: function(idx) { | 3576 _focusPhysicalItem: function(idx) { |
| 3969 if (idx < 0 || idx >= this._virtualCount) { | 3577 this._restoreFocusedItem(); |
| 3578 |
| 3579 var physicalItem = this._getPhysicalItemForIndex(idx, true); |
| 3580 if (!physicalItem) { |
| 3970 return; | 3581 return; |
| 3971 } | 3582 } |
| 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)]; | |
| 3979 var SECRET = ~(Math.random() * 100); | 3583 var SECRET = ~(Math.random() * 100); |
| 3980 var model = physicalItem._templateInstance; | 3584 var model = physicalItem._templateInstance; |
| 3981 var focusable; | 3585 var focusable; |
| 3982 | 3586 |
| 3983 // set a secret tab index | |
| 3984 model.tabIndex = SECRET; | 3587 model.tabIndex = SECRET; |
| 3985 // check if focusable element is the physical item | 3588 // the focusable element could be the entire physical item |
| 3986 if (physicalItem.tabIndex === SECRET) { | 3589 if (physicalItem.tabIndex === SECRET) { |
| 3987 focusable = physicalItem; | 3590 focusable = physicalItem; |
| 3988 } | 3591 } |
| 3989 // search for the element which tabindex is bound to the secret tab index | 3592 // the focusable element could be somewhere within the physical item |
| 3990 if (!focusable) { | 3593 if (!focusable) { |
| 3991 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET + '"]'); | 3594 focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECR
ET + '"]'); |
| 3992 } | 3595 } |
| 3993 // restore the tab index | 3596 // restore the tab index |
| 3994 model.tabIndex = 0; | 3597 model.tabIndex = 0; |
| 3995 // focus the focusable element | |
| 3996 this._focusedIndex = idx; | |
| 3997 focusable && focusable.focus(); | 3598 focusable && focusable.focus(); |
| 3998 }, | 3599 }, |
| 3999 | 3600 |
| 3601 _restoreFocusedItem: function() { |
| 3602 if (!this._offscreenFocusedItem) { |
| 3603 return; |
| 3604 } |
| 3605 var item = this._getNormalizedItem(this._focusedIndex); |
| 3606 var pidx = this._physicalIndexForKey[this._collection.getKey(item)]; |
| 3607 |
| 3608 if (pidx !== undefined) { |
| 3609 this.translate3d(0, HIDDEN_Y, 0, this._physicalItems[pidx]); |
| 3610 this._physicalItems[pidx] = this._offscreenFocusedItem; |
| 3611 } |
| 3612 this._offscreenFocusedItem = null; |
| 3613 }, |
| 3614 |
| 4000 _removeFocusedItem: function() { | 3615 _removeFocusedItem: function() { |
| 4001 if (this._offscreenFocusedItem) { | 3616 if (!this._offscreenFocusedItem) { |
| 4002 Polymer.dom(this).removeChild(this._offscreenFocusedItem); | 3617 return; |
| 4003 } | 3618 } |
| 3619 Polymer.dom(this).removeChild(this._offscreenFocusedItem); |
| 4004 this._offscreenFocusedItem = null; | 3620 this._offscreenFocusedItem = null; |
| 4005 this._focusBackfillItem = null; | 3621 this._focusBackfillItem = null; |
| 4006 this._focusedItem = null; | |
| 4007 this._focusedIndex = -1; | |
| 4008 }, | 3622 }, |
| 4009 | 3623 |
| 4010 _createFocusBackfillItem: function() { | 3624 _createFocusBackfillItem: function() { |
| 4011 var pidx, fidx = this._focusedIndex; | 3625 if (this._offscreenFocusedItem) { |
| 4012 if (this._offscreenFocusedItem || fidx < 0) { | |
| 4013 return; | 3626 return; |
| 4014 } | 3627 } |
| 3628 var item = this._getNormalizedItem(this._focusedIndex); |
| 3629 var pidx = this._physicalIndexForKey[this._collection.getKey(item)]; |
| 3630 |
| 3631 this._offscreenFocusedItem = this._physicalItems[pidx]; |
| 3632 this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem); |
| 3633 |
| 4015 if (!this._focusBackfillItem) { | 3634 if (!this._focusBackfillItem) { |
| 4016 // create a physical item, so that it backfills the focused item. | |
| 4017 var stampedTemplate = this.stamp(null); | 3635 var stampedTemplate = this.stamp(null); |
| 4018 this._focusBackfillItem = stampedTemplate.root.querySelector('*'); | 3636 this._focusBackfillItem = stampedTemplate.root.querySelector('*'); |
| 4019 Polymer.dom(this).appendChild(stampedTemplate.root); | 3637 Polymer.dom(this).appendChild(stampedTemplate.root); |
| 4020 } | 3638 } |
| 4021 // get the physical index for the focused index | 3639 this._physicalItems[pidx] = this._focusBackfillItem; |
| 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 } | |
| 4055 }, | 3640 }, |
| 4056 | 3641 |
| 4057 _didFocus: function(e) { | 3642 _didFocus: function(e) { |
| 4058 var targetModel = this.modelForElement(e.target); | 3643 var targetModel = this.modelForElement(e.target); |
| 4059 var focusedModel = this._focusedItem ? this._focusedItem._templateInstance
: null; | |
| 4060 var hasOffscreenFocusedItem = this._offscreenFocusedItem !== null; | |
| 4061 var fidx = this._focusedIndex; | 3644 var fidx = this._focusedIndex; |
| 4062 | 3645 |
| 4063 if (!targetModel || !focusedModel) { | 3646 if (!targetModel) { |
| 4064 return; | 3647 return; |
| 4065 } | 3648 } |
| 4066 if (focusedModel === targetModel) { | 3649 this._restoreFocusedItem(); |
| 4067 // if the user focused the same item, then bring it into view if it's no
t visible | 3650 |
| 4068 if (!this._isIndexVisible(fidx)) { | 3651 if (this.modelForElement(this._offscreenFocusedItem) === targetModel) { |
| 4069 this.scrollToIndex(fidx); | 3652 this.scrollToIndex(fidx); |
| 4070 } | |
| 4071 } else { | 3653 } else { |
| 4072 this._restoreFocusedItem(); | |
| 4073 // restore tabIndex for the currently focused item | 3654 // restore tabIndex for the currently focused item |
| 4074 focusedModel.tabIndex = -1; | 3655 this._getModelFromItem(this._getNormalizedItem(fidx)).tabIndex = -1; |
| 4075 // set the tabIndex for the next focused item | 3656 // set the tabIndex for the next focused item |
| 4076 targetModel.tabIndex = 0; | 3657 targetModel.tabIndex = 0; |
| 4077 fidx = targetModel[this.indexAs]; | 3658 fidx = /** @type {{index: number}} */(targetModel).index; |
| 4078 this._focusedIndex = fidx; | 3659 this._focusedIndex = fidx; |
| 4079 this._focusedItem = this._physicalItems[this._getPhysicalIndex(fidx)]; | 3660 // bring the item into view |
| 4080 | 3661 if (fidx < this.firstVisibleIndex || fidx > this.lastVisibleIndex) { |
| 4081 if (hasOffscreenFocusedItem && !this._offscreenFocusedItem) { | 3662 this.scrollToIndex(fidx); |
| 3663 } else { |
| 4082 this._update(); | 3664 this._update(); |
| 4083 } | 3665 } |
| 4084 } | 3666 } |
| 4085 }, | 3667 }, |
| 4086 | 3668 |
| 4087 _didMoveUp: function() { | 3669 _didMoveUp: function() { |
| 4088 this._focusPhysicalItem(this._focusedIndex - 1); | 3670 this._focusPhysicalItem(Math.max(0, this._focusedIndex - 1)); |
| 4089 }, | 3671 }, |
| 4090 | 3672 |
| 4091 _didMoveDown: function() { | 3673 _didMoveDown: function() { |
| 4092 this._focusPhysicalItem(this._focusedIndex + 1); | 3674 this._focusPhysicalItem(Math.min(this._virtualCount, this._focusedIndex +
1)); |
| 4093 }, | 3675 }, |
| 4094 | 3676 |
| 4095 _didEnter: function(e) { | 3677 _didEnter: function(e) { |
| 3678 // focus the currently focused physical item |
| 4096 this._focusPhysicalItem(this._focusedIndex); | 3679 this._focusPhysicalItem(this._focusedIndex); |
| 4097 this._selectionHandler(e.detail.keyboardEvent); | 3680 // toggle selection |
| 3681 this._selectionHandler(/** @type {{keyboardEvent: Event}} */(e.detail).key
boardEvent); |
| 4098 } | 3682 } |
| 4099 }); | 3683 }); |
| 4100 | 3684 |
| 4101 })(); | 3685 })(); |
| 3686 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3687 // Use of this source code is governed by a BSD-style license that can be |
| 3688 // found in the LICENSE file. |
| 3689 |
| 3690 /** |
| 3691 * @fileoverview Assertion support. |
| 3692 */ |
| 3693 |
| 3694 /** |
| 3695 * Verify |condition| is truthy and return |condition| if so. |
| 3696 * @template T |
| 3697 * @param {T} condition A condition to check for truthiness. Note that this |
| 3698 * may be used to test whether a value is defined or not, and we don't want |
| 3699 * to force a cast to Boolean. |
| 3700 * @param {string=} opt_message A message to show on failure. |
| 3701 * @return {T} A non-null |condition|. |
| 3702 */ |
| 3703 function assert(condition, opt_message) { |
| 3704 if (!condition) { |
| 3705 var message = 'Assertion failed'; |
| 3706 if (opt_message) |
| 3707 message = message + ': ' + opt_message; |
| 3708 var error = new Error(message); |
| 3709 var global = function() { return this; }(); |
| 3710 if (global.traceAssertionsForTesting) |
| 3711 console.warn(error.stack); |
| 3712 throw error; |
| 3713 } |
| 3714 return condition; |
| 3715 } |
| 3716 |
| 3717 /** |
| 3718 * Call this from places in the code that should never be reached. |
| 3719 * |
| 3720 * For example, handling all the values of enum with a switch() like this: |
| 3721 * |
| 3722 * function getValueFromEnum(enum) { |
| 3723 * switch (enum) { |
| 3724 * case ENUM_FIRST_OF_TWO: |
| 3725 * return first |
| 3726 * case ENUM_LAST_OF_TWO: |
| 3727 * return last; |
| 3728 * } |
| 3729 * assertNotReached(); |
| 3730 * return document; |
| 3731 * } |
| 3732 * |
| 3733 * This code should only be hit in the case of serious programmer error or |
| 3734 * unexpected input. |
| 3735 * |
| 3736 * @param {string=} opt_message A message to show when this is hit. |
| 3737 */ |
| 3738 function assertNotReached(opt_message) { |
| 3739 assert(false, opt_message || 'Unreachable code hit'); |
| 3740 } |
| 3741 |
| 3742 /** |
| 3743 * @param {*} value The value to check. |
| 3744 * @param {function(new: T, ...)} type A user-defined constructor. |
| 3745 * @param {string=} opt_message A message to show when this is hit. |
| 3746 * @return {T} |
| 3747 * @template T |
| 3748 */ |
| 3749 function assertInstanceof(value, type, opt_message) { |
| 3750 // We don't use assert immediately here so that we avoid constructing an error |
| 3751 // message if we don't have to. |
| 3752 if (!(value instanceof type)) { |
| 3753 assertNotReached(opt_message || 'Value ' + value + |
| 3754 ' is not a[n] ' + (type.name || typeof type)); |
| 3755 } |
| 3756 return value; |
| 3757 }; |
| 3758 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 3759 // Use of this source code is governed by a BSD-style license that can be |
| 3760 // found in the LICENSE file. |
| 3761 |
| 3762 cr.define('downloads', function() { |
| 3763 /** |
| 3764 * @param {string} chromeSendName |
| 3765 * @return {function(string):void} A chrome.send() callback with curried name. |
| 3766 */ |
| 3767 function chromeSendWithId(chromeSendName) { |
| 3768 return function(id) { chrome.send(chromeSendName, [id]); }; |
| 3769 } |
| 3770 |
| 3771 /** @constructor */ |
| 3772 function ActionService() { |
| 3773 /** @private {Array<string>} */ |
| 3774 this.searchTerms_ = []; |
| 3775 } |
| 3776 |
| 3777 /** |
| 3778 * @param {string} s |
| 3779 * @return {string} |s| without whitespace at the beginning or end. |
| 3780 */ |
| 3781 function trim(s) { return s.trim(); } |
| 3782 |
| 3783 /** |
| 3784 * @param {string|undefined} value |
| 3785 * @return {boolean} Whether |value| is truthy. |
| 3786 */ |
| 3787 function truthy(value) { return !!value; } |
| 3788 |
| 3789 /** |
| 3790 * @param {string} searchText Input typed by the user into a search box. |
| 3791 * @return {Array<string>} A list of terms extracted from |searchText|. |
| 3792 */ |
| 3793 ActionService.splitTerms = function(searchText) { |
| 3794 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). |
| 3795 return searchText.split(/"([^"]*)"/).map(trim).filter(truthy); |
| 3796 }; |
| 3797 |
| 3798 ActionService.prototype = { |
| 3799 /** @param {string} id ID of the download to cancel. */ |
| 3800 cancel: chromeSendWithId('cancel'), |
| 3801 |
| 3802 /** Instructs the browser to clear all finished downloads. */ |
| 3803 clearAll: function() { |
| 3804 if (loadTimeData.getBoolean('allowDeletingHistory')) { |
| 3805 chrome.send('clearAll'); |
| 3806 this.search(''); |
| 3807 } |
| 3808 }, |
| 3809 |
| 3810 /** @param {string} id ID of the dangerous download to discard. */ |
| 3811 discardDangerous: chromeSendWithId('discardDangerous'), |
| 3812 |
| 3813 /** @param {string} url URL of a file to download. */ |
| 3814 download: function(url) { |
| 3815 var a = document.createElement('a'); |
| 3816 a.href = url; |
| 3817 a.setAttribute('download', ''); |
| 3818 a.click(); |
| 3819 }, |
| 3820 |
| 3821 /** @param {string} id ID of the download that the user started dragging. */ |
| 3822 drag: chromeSendWithId('drag'), |
| 3823 |
| 3824 /** Loads more downloads with the current search terms. */ |
| 3825 loadMore: function() { |
| 3826 chrome.send('getDownloads', this.searchTerms_); |
| 3827 }, |
| 3828 |
| 3829 /** |
| 3830 * @return {boolean} Whether the user is currently searching for downloads |
| 3831 * (i.e. has a non-empty search term). |
| 3832 */ |
| 3833 isSearching: function() { |
| 3834 return this.searchTerms_.length > 0; |
| 3835 }, |
| 3836 |
| 3837 /** Opens the current local destination for downloads. */ |
| 3838 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), |
| 3839 |
| 3840 /** |
| 3841 * @param {string} id ID of the download to run locally on the user's box. |
| 3842 */ |
| 3843 openFile: chromeSendWithId('openFile'), |
| 3844 |
| 3845 /** @param {string} id ID the of the progressing download to pause. */ |
| 3846 pause: chromeSendWithId('pause'), |
| 3847 |
| 3848 /** @param {string} id ID of the finished download to remove. */ |
| 3849 remove: chromeSendWithId('remove'), |
| 3850 |
| 3851 /** @param {string} id ID of the paused download to resume. */ |
| 3852 resume: chromeSendWithId('resume'), |
| 3853 |
| 3854 /** |
| 3855 * @param {string} id ID of the dangerous download to save despite |
| 3856 * warnings. |
| 3857 */ |
| 3858 saveDangerous: chromeSendWithId('saveDangerous'), |
| 3859 |
| 3860 /** @param {string} searchText What to search for. */ |
| 3861 search: function(searchText) { |
| 3862 var searchTerms = ActionService.splitTerms(searchText); |
| 3863 var sameTerms = searchTerms.length == this.searchTerms_.length; |
| 3864 |
| 3865 for (var i = 0; sameTerms && i < searchTerms.length; ++i) { |
| 3866 if (searchTerms[i] != this.searchTerms_[i]) |
| 3867 sameTerms = false; |
| 3868 } |
| 3869 |
| 3870 if (sameTerms) |
| 3871 return; |
| 3872 |
| 3873 this.searchTerms_ = searchTerms; |
| 3874 this.loadMore(); |
| 3875 }, |
| 3876 |
| 3877 /** |
| 3878 * Shows the local folder a finished download resides in. |
| 3879 * @param {string} id ID of the download to show. |
| 3880 */ |
| 3881 show: chromeSendWithId('show'), |
| 3882 |
| 3883 /** Undo download removal. */ |
| 3884 undo: chrome.send.bind(chrome, 'undo'), |
| 3885 }; |
| 3886 |
| 3887 cr.addSingletonGetter(ActionService); |
| 3888 |
| 3889 return {ActionService: ActionService}; |
| 3890 }); |
| 3891 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 3892 // Use of this source code is governed by a BSD-style license that can be |
| 3893 // found in the LICENSE file. |
| 3894 |
| 3895 cr.define('downloads', function() { |
| 3896 /** |
| 3897 * Explains why a download is in DANGEROUS state. |
| 3898 * @enum {string} |
| 3899 */ |
| 3900 var DangerType = { |
| 3901 NOT_DANGEROUS: 'NOT_DANGEROUS', |
| 3902 DANGEROUS_FILE: 'DANGEROUS_FILE', |
| 3903 DANGEROUS_URL: 'DANGEROUS_URL', |
| 3904 DANGEROUS_CONTENT: 'DANGEROUS_CONTENT', |
| 3905 UNCOMMON_CONTENT: 'UNCOMMON_CONTENT', |
| 3906 DANGEROUS_HOST: 'DANGEROUS_HOST', |
| 3907 POTENTIALLY_UNWANTED: 'POTENTIALLY_UNWANTED', |
| 3908 }; |
| 3909 |
| 3910 /** |
| 3911 * The states a download can be in. These correspond to states defined in |
| 3912 * DownloadsDOMHandler::CreateDownloadItemValue |
| 3913 * @enum {string} |
| 3914 */ |
| 3915 var States = { |
| 3916 IN_PROGRESS: 'IN_PROGRESS', |
| 3917 CANCELLED: 'CANCELLED', |
| 3918 COMPLETE: 'COMPLETE', |
| 3919 PAUSED: 'PAUSED', |
| 3920 DANGEROUS: 'DANGEROUS', |
| 3921 INTERRUPTED: 'INTERRUPTED', |
| 3922 }; |
| 3923 |
| 3924 return { |
| 3925 DangerType: DangerType, |
| 3926 States: States, |
| 3927 }; |
| 3928 }); |
| 3929 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 3930 // Use of this source code is governed by a BSD-style license that can be |
| 3931 // found in the LICENSE file. |
| 3932 |
| 3933 // Action links are elements that are used to perform an in-page navigation or |
| 3934 // action (e.g. showing a dialog). |
| 3935 // |
| 3936 // They look like normal anchor (<a>) tags as their text color is blue. However, |
| 3937 // they're subtly different as they're not initially underlined (giving users a |
| 3938 // clue that underlined links navigate while action links don't). |
| 3939 // |
| 3940 // Action links look very similar to normal links when hovered (hand cursor, |
| 3941 // underlined). This gives the user an idea that clicking this link will do |
| 3942 // something similar to navigation but in the same page. |
| 3943 // |
| 3944 // They can be created in JavaScript like this: |
| 3945 // |
| 3946 // var link = document.createElement('a', 'action-link'); // Note second arg. |
| 3947 // |
| 3948 // or with a constructor like this: |
| 3949 // |
| 3950 // var link = new ActionLink(); |
| 3951 // |
| 3952 // They can be used easily from HTML as well, like so: |
| 3953 // |
| 3954 // <a is="action-link">Click me!</a> |
| 3955 // |
| 3956 // NOTE: <action-link> and document.createElement('action-link') don't work. |
| 3957 |
| 3958 /** |
| 3959 * @constructor |
| 3960 * @extends {HTMLAnchorElement} |
| 3961 */ |
| 3962 var ActionLink = document.registerElement('action-link', { |
| 3963 prototype: { |
| 3964 __proto__: HTMLAnchorElement.prototype, |
| 3965 |
| 3966 /** @this {ActionLink} */ |
| 3967 createdCallback: function() { |
| 3968 // Action links can start disabled (e.g. <a is="action-link" disabled>). |
| 3969 this.tabIndex = this.disabled ? -1 : 0; |
| 3970 |
| 3971 if (!this.hasAttribute('role')) |
| 3972 this.setAttribute('role', 'link'); |
| 3973 |
| 3974 this.addEventListener('keydown', function(e) { |
| 3975 if (!this.disabled && e.keyIdentifier == 'Enter' && !this.href) { |
| 3976 // Schedule a click asynchronously because other 'keydown' handlers |
| 3977 // may still run later (e.g. document.addEventListener('keydown')). |
| 3978 // Specifically options dialogs break when this timeout isn't here. |
| 3979 // NOTE: this affects the "trusted" state of the ensuing click. I |
| 3980 // haven't found anything that breaks because of this (yet). |
| 3981 window.setTimeout(this.click.bind(this), 0); |
| 3982 } |
| 3983 }); |
| 3984 |
| 3985 function preventDefault(e) { |
| 3986 e.preventDefault(); |
| 3987 } |
| 3988 |
| 3989 function removePreventDefault() { |
| 3990 document.removeEventListener('selectstart', preventDefault); |
| 3991 document.removeEventListener('mouseup', removePreventDefault); |
| 3992 } |
| 3993 |
| 3994 this.addEventListener('mousedown', function() { |
| 3995 // This handlers strives to match the behavior of <a href="...">. |
| 3996 |
| 3997 // While the mouse is down, prevent text selection from dragging. |
| 3998 document.addEventListener('selectstart', preventDefault); |
| 3999 document.addEventListener('mouseup', removePreventDefault); |
| 4000 |
| 4001 // If focus started via mouse press, don't show an outline. |
| 4002 if (document.activeElement != this) |
| 4003 this.classList.add('no-outline'); |
| 4004 }); |
| 4005 |
| 4006 this.addEventListener('blur', function() { |
| 4007 this.classList.remove('no-outline'); |
| 4008 }); |
| 4009 }, |
| 4010 |
| 4011 /** @type {boolean} */ |
| 4012 set disabled(disabled) { |
| 4013 if (disabled) |
| 4014 HTMLAnchorElement.prototype.setAttribute.call(this, 'disabled', ''); |
| 4015 else |
| 4016 HTMLAnchorElement.prototype.removeAttribute.call(this, 'disabled'); |
| 4017 this.tabIndex = disabled ? -1 : 0; |
| 4018 }, |
| 4019 get disabled() { |
| 4020 return this.hasAttribute('disabled'); |
| 4021 }, |
| 4022 |
| 4023 /** @override */ |
| 4024 setAttribute: function(attr, val) { |
| 4025 if (attr.toLowerCase() == 'disabled') |
| 4026 this.disabled = true; |
| 4027 else |
| 4028 HTMLAnchorElement.prototype.setAttribute.apply(this, arguments); |
| 4029 }, |
| 4030 |
| 4031 /** @override */ |
| 4032 removeAttribute: function(attr) { |
| 4033 if (attr.toLowerCase() == 'disabled') |
| 4034 this.disabled = false; |
| 4035 else |
| 4036 HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments); |
| 4037 }, |
| 4038 }, |
| 4039 |
| 4040 extends: 'a', |
| 4041 }); |
| 4102 (function() { | 4042 (function() { |
| 4103 | 4043 |
| 4104 // monostate data | 4044 // monostate data |
| 4105 var metaDatas = {}; | 4045 var metaDatas = {}; |
| 4106 var metaArrays = {}; | 4046 var metaArrays = {}; |
| 4107 var singleton = null; | 4047 var singleton = null; |
| 4108 | 4048 |
| 4109 Polymer.IronMeta = Polymer({ | 4049 Polymer.IronMeta = Polymer({ |
| 4110 | 4050 |
| 4111 is: 'iron-meta', | 4051 is: 'iron-meta', |
| (...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4407 */ | 4347 */ |
| 4408 src: { | 4348 src: { |
| 4409 type: String, | 4349 type: String, |
| 4410 observer: '_srcChanged' | 4350 observer: '_srcChanged' |
| 4411 }, | 4351 }, |
| 4412 | 4352 |
| 4413 /** | 4353 /** |
| 4414 * @type {!Polymer.IronMeta} | 4354 * @type {!Polymer.IronMeta} |
| 4415 */ | 4355 */ |
| 4416 _meta: { | 4356 _meta: { |
| 4417 value: Polymer.Base.create('iron-meta', {type: 'iconset'}), | 4357 value: Polymer.Base.create('iron-meta', {type: 'iconset'}) |
| 4418 observer: '_updateIcon' | |
| 4419 } | 4358 } |
| 4420 | 4359 |
| 4421 }, | 4360 }, |
| 4422 | 4361 |
| 4423 _DEFAULT_ICONSET: 'icons', | 4362 _DEFAULT_ICONSET: 'icons', |
| 4424 | 4363 |
| 4425 _iconChanged: function(icon) { | 4364 _iconChanged: function(icon) { |
| 4426 var parts = (icon || '').split(':'); | 4365 var parts = (icon || '').split(':'); |
| 4427 this._iconName = parts.pop(); | 4366 this._iconName = parts.pop(); |
| 4428 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; | 4367 this._iconsetName = parts.pop() || this._DEFAULT_ICONSET; |
| 4429 this._updateIcon(); | 4368 this._updateIcon(); |
| 4430 }, | 4369 }, |
| 4431 | 4370 |
| 4432 _srcChanged: function(src) { | 4371 _srcChanged: function(src) { |
| 4433 this._updateIcon(); | 4372 this._updateIcon(); |
| 4434 }, | 4373 }, |
| 4435 | 4374 |
| 4436 _usesIconset: function() { | 4375 _usesIconset: function() { |
| 4437 return this.icon || !this.src; | 4376 return this.icon || !this.src; |
| 4438 }, | 4377 }, |
| 4439 | 4378 |
| 4440 /** @suppress {visibility} */ | 4379 /** @suppress {visibility} */ |
| 4441 _updateIcon: function() { | 4380 _updateIcon: function() { |
| 4442 if (this._usesIconset()) { | 4381 if (this._usesIconset()) { |
| 4443 if (this._img && this._img.parentNode) { | 4382 if (this._iconsetName) { |
| 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) { | |
| 4451 this._iconset = /** @type {?Polymer.Iconset} */ ( | 4383 this._iconset = /** @type {?Polymer.Iconset} */ ( |
| 4452 this._meta.byKey(this._iconsetName)); | 4384 this._meta.byKey(this._iconsetName)); |
| 4453 if (this._iconset) { | 4385 if (this._iconset) { |
| 4454 this._iconset.applyIcon(this, this._iconName, this.theme); | 4386 this._iconset.applyIcon(this, this._iconName, this.theme); |
| 4455 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); | 4387 this.unlisten(window, 'iron-iconset-added', '_updateIcon'); |
| 4456 } else { | 4388 } else { |
| 4457 this.listen(window, 'iron-iconset-added', '_updateIcon'); | 4389 this.listen(window, 'iron-iconset-added', '_updateIcon'); |
| 4458 } | 4390 } |
| 4459 } | 4391 } |
| 4460 } else { | 4392 } else { |
| 4461 if (this._iconset) { | |
| 4462 this._iconset.removeIcon(this); | |
| 4463 } | |
| 4464 if (!this._img) { | 4393 if (!this._img) { |
| 4465 this._img = document.createElement('img'); | 4394 this._img = document.createElement('img'); |
| 4466 this._img.style.width = '100%'; | 4395 this._img.style.width = '100%'; |
| 4467 this._img.style.height = '100%'; | 4396 this._img.style.height = '100%'; |
| 4468 this._img.draggable = false; | 4397 this._img.draggable = false; |
| 4469 } | 4398 } |
| 4470 this._img.src = this.src; | 4399 this._img.src = this.src; |
| 4471 Polymer.dom(this.root).appendChild(this._img); | 4400 Polymer.dom(this.root).appendChild(this._img); |
| 4472 } | 4401 } |
| 4473 } | 4402 } |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4722 } | 4651 } |
| 4723 }, | 4652 }, |
| 4724 | 4653 |
| 4725 _disabledChanged: function(disabled, old) { | 4654 _disabledChanged: function(disabled, old) { |
| 4726 this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); | 4655 this.setAttribute('aria-disabled', disabled ? 'true' : 'false'); |
| 4727 this.style.pointerEvents = disabled ? 'none' : ''; | 4656 this.style.pointerEvents = disabled ? 'none' : ''; |
| 4728 if (disabled) { | 4657 if (disabled) { |
| 4729 this._oldTabIndex = this.tabIndex; | 4658 this._oldTabIndex = this.tabIndex; |
| 4730 this.focused = false; | 4659 this.focused = false; |
| 4731 this.tabIndex = -1; | 4660 this.tabIndex = -1; |
| 4732 this.blur(); | |
| 4733 } else if (this._oldTabIndex !== undefined) { | 4661 } else if (this._oldTabIndex !== undefined) { |
| 4734 this.tabIndex = this._oldTabIndex; | 4662 this.tabIndex = this._oldTabIndex; |
| 4735 } | 4663 } |
| 4736 }, | 4664 }, |
| 4737 | 4665 |
| 4738 _changedControlState: function() { | 4666 _changedControlState: function() { |
| 4739 // _controlStateChanged is abstract, follow-on behaviors may implement it | 4667 // _controlStateChanged is abstract, follow-on behaviors may implement it |
| 4740 if (this._controlStateChanged) { | 4668 if (this._controlStateChanged) { |
| 4741 this._controlStateChanged(); | 4669 this._controlStateChanged(); |
| 4742 } | 4670 } |
| (...skipping 1710 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6453 | 6381 |
| 6454 /** | 6382 /** |
| 6455 * Sets the selection state for a given item to either selected or deselecte
d. | 6383 * Sets the selection state for a given item to either selected or deselecte
d. |
| 6456 * | 6384 * |
| 6457 * @method setItemSelected | 6385 * @method setItemSelected |
| 6458 * @param {*} item The item to select. | 6386 * @param {*} item The item to select. |
| 6459 * @param {boolean} isSelected True for selected, false for deselected. | 6387 * @param {boolean} isSelected True for selected, false for deselected. |
| 6460 */ | 6388 */ |
| 6461 setItemSelected: function(item, isSelected) { | 6389 setItemSelected: function(item, isSelected) { |
| 6462 if (item != null) { | 6390 if (item != null) { |
| 6463 if (isSelected !== this.isSelected(item)) { | 6391 if (isSelected) { |
| 6464 // proceed to update selection only if requested state differs from cu
rrent | 6392 this.selection.push(item); |
| 6465 if (isSelected) { | 6393 } else { |
| 6466 this.selection.push(item); | 6394 var i = this.selection.indexOf(item); |
| 6467 } else { | 6395 if (i >= 0) { |
| 6468 var i = this.selection.indexOf(item); | 6396 this.selection.splice(i, 1); |
| 6469 if (i >= 0) { | |
| 6470 this.selection.splice(i, 1); | |
| 6471 } | |
| 6472 } | 6397 } |
| 6473 if (this.selectCallback) { | 6398 } |
| 6474 this.selectCallback(item, isSelected); | 6399 if (this.selectCallback) { |
| 6475 } | 6400 this.selectCallback(item, isSelected); |
| 6476 } | 6401 } |
| 6477 } | 6402 } |
| 6478 }, | 6403 }, |
| 6479 | 6404 |
| 6480 /** | 6405 /** |
| 6481 * Sets the selection state for a given item. If the `multi` property | 6406 * Sets the selection state for a given item. If the `multi` property |
| 6482 * is true, then the selected state of `item` will be toggled; otherwise | 6407 * is true, then the selected state of `item` will be toggled; otherwise |
| 6483 * the `item` will be selected. | 6408 * the `item` will be selected. |
| 6484 * | 6409 * |
| 6485 * @method select | 6410 * @method select |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6622 type: Object, | 6547 type: Object, |
| 6623 value: function() { | 6548 value: function() { |
| 6624 return { | 6549 return { |
| 6625 'template': 1 | 6550 'template': 1 |
| 6626 }; | 6551 }; |
| 6627 } | 6552 } |
| 6628 } | 6553 } |
| 6629 }, | 6554 }, |
| 6630 | 6555 |
| 6631 observers: [ | 6556 observers: [ |
| 6632 '_updateAttrForSelected(attrForSelected)', | 6557 '_updateSelected(attrForSelected, selected)' |
| 6633 '_updateSelected(selected)' | |
| 6634 ], | 6558 ], |
| 6635 | 6559 |
| 6636 created: function() { | 6560 created: function() { |
| 6637 this._bindFilterItem = this._filterItem.bind(this); | 6561 this._bindFilterItem = this._filterItem.bind(this); |
| 6638 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | 6562 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); |
| 6639 }, | 6563 }, |
| 6640 | 6564 |
| 6641 attached: function() { | 6565 attached: function() { |
| 6642 this._observer = this._observeItems(this); | 6566 this._observer = this._observeItems(this); |
| 6643 this._updateItems(); | 6567 this._updateItems(); |
| 6644 if (!this._shouldUpdateSelection) { | 6568 if (!this._shouldUpdateSelection) { |
| 6645 this._updateSelected(); | 6569 this._updateSelected(this.attrForSelected,this.selected) |
| 6646 } | 6570 } |
| 6647 this._addListener(this.activateEvent); | 6571 this._addListener(this.activateEvent); |
| 6648 }, | 6572 }, |
| 6649 | 6573 |
| 6650 detached: function() { | 6574 detached: function() { |
| 6651 if (this._observer) { | 6575 if (this._observer) { |
| 6652 Polymer.dom(this).unobserveNodes(this._observer); | 6576 Polymer.dom(this).unobserveNodes(this._observer); |
| 6653 } | 6577 } |
| 6654 this._removeListener(this.activateEvent); | 6578 this._removeListener(this.activateEvent); |
| 6655 }, | 6579 }, |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6728 this._removeListener(old); | 6652 this._removeListener(old); |
| 6729 this._addListener(eventName); | 6653 this._addListener(eventName); |
| 6730 }, | 6654 }, |
| 6731 | 6655 |
| 6732 _updateItems: function() { | 6656 _updateItems: function() { |
| 6733 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); | 6657 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); |
| 6734 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); | 6658 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); |
| 6735 this._setItems(nodes); | 6659 this._setItems(nodes); |
| 6736 }, | 6660 }, |
| 6737 | 6661 |
| 6738 _updateAttrForSelected: function() { | |
| 6739 if (this._shouldUpdateSelection) { | |
| 6740 this.selected = this._indexToValue(this.indexOf(this.selectedItem));
| |
| 6741 } | |
| 6742 }, | |
| 6743 | |
| 6744 _updateSelected: function() { | 6662 _updateSelected: function() { |
| 6745 this._selectSelected(this.selected); | 6663 this._selectSelected(this.selected); |
| 6746 }, | 6664 }, |
| 6747 | 6665 |
| 6748 _selectSelected: function(selected) { | 6666 _selectSelected: function(selected) { |
| 6749 this._selection.select(this._valueToItem(this.selected)); | 6667 this._selection.select(this._valueToItem(this.selected)); |
| 6750 }, | 6668 }, |
| 6751 | 6669 |
| 6752 _filterItem: function(node) { | 6670 _filterItem: function(node) { |
| 6753 return !this._excludedLocalNames[node.localName]; | 6671 return !this._excludedLocalNames[node.localName]; |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6803 // observe items change under the given node. | 6721 // observe items change under the given node. |
| 6804 _observeItems: function(node) { | 6722 _observeItems: function(node) { |
| 6805 return Polymer.dom(node).observeNodes(function(mutations) { | 6723 return Polymer.dom(node).observeNodes(function(mutations) { |
| 6806 this._updateItems(); | 6724 this._updateItems(); |
| 6807 | 6725 |
| 6808 if (this._shouldUpdateSelection) { | 6726 if (this._shouldUpdateSelection) { |
| 6809 this._updateSelected(); | 6727 this._updateSelected(); |
| 6810 } | 6728 } |
| 6811 | 6729 |
| 6812 // Let other interested parties know about the change so that | 6730 // Let other interested parties know about the change so that |
| 6813 // we don't have to recreate mutation observers everywhere. | 6731 // we don't have to recreate mutation observers everywher. |
| 6814 this.fire('iron-items-changed', mutations, { | 6732 this.fire('iron-items-changed', mutations, { |
| 6815 bubbles: false, | 6733 bubbles: false, |
| 6816 cancelable: false | 6734 cancelable: false |
| 6817 }); | 6735 }); |
| 6818 }); | 6736 }); |
| 6819 }, | 6737 }, |
| 6820 | 6738 |
| 6821 _activateHandler: function(e) { | 6739 _activateHandler: function(e) { |
| 6822 var t = e.target; | 6740 var t = e.target; |
| 6823 var items = this.items; | 6741 var items = this.items; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 6867 */ | 6785 */ |
| 6868 selectedItems: { | 6786 selectedItems: { |
| 6869 type: Array, | 6787 type: Array, |
| 6870 readOnly: true, | 6788 readOnly: true, |
| 6871 notify: true | 6789 notify: true |
| 6872 }, | 6790 }, |
| 6873 | 6791 |
| 6874 }, | 6792 }, |
| 6875 | 6793 |
| 6876 observers: [ | 6794 observers: [ |
| 6877 '_updateSelected(selectedValues)' | 6795 '_updateSelected(attrForSelected, selectedValues)' |
| 6878 ], | 6796 ], |
| 6879 | 6797 |
| 6880 /** | 6798 /** |
| 6881 * Selects the given value. If the `multi` property is true, then the select
ed state of the | 6799 * Selects the given value. If the `multi` property is true, then the select
ed state of the |
| 6882 * `value` will be toggled; otherwise the `value` will be selected. | 6800 * `value` will be toggled; otherwise the `value` will be selected. |
| 6883 * | 6801 * |
| 6884 * @method select | 6802 * @method select |
| 6885 * @param {string|number} value the value to select. | 6803 * @param {string|number} value the value to select. |
| 6886 */ | 6804 */ |
| 6887 select: function(value) { | 6805 select: function(value) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 6898 | 6816 |
| 6899 multiChanged: function(multi) { | 6817 multiChanged: function(multi) { |
| 6900 this._selection.multi = multi; | 6818 this._selection.multi = multi; |
| 6901 }, | 6819 }, |
| 6902 | 6820 |
| 6903 get _shouldUpdateSelection() { | 6821 get _shouldUpdateSelection() { |
| 6904 return this.selected != null || | 6822 return this.selected != null || |
| 6905 (this.selectedValues != null && this.selectedValues.length); | 6823 (this.selectedValues != null && this.selectedValues.length); |
| 6906 }, | 6824 }, |
| 6907 | 6825 |
| 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 | |
| 6920 _updateSelected: function() { | 6826 _updateSelected: function() { |
| 6921 if (this.multi) { | 6827 if (this.multi) { |
| 6922 this._selectMulti(this.selectedValues); | 6828 this._selectMulti(this.selectedValues); |
| 6923 } else { | 6829 } else { |
| 6924 this._selectSelected(this.selected); | 6830 this._selectSelected(this.selected); |
| 6925 } | 6831 } |
| 6926 }, | 6832 }, |
| 6927 | 6833 |
| 6928 _selectMulti: function(values) { | 6834 _selectMulti: function(values) { |
| 6835 this._selection.clear(); |
| 6929 if (values) { | 6836 if (values) { |
| 6930 var selectedItems = this._valuesToItems(values); | 6837 for (var i = 0; i < values.length; i++) { |
| 6931 // clear all but the current selected items | 6838 this._selection.setItemSelected(this._valueToItem(values[i]), true); |
| 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); | |
| 6936 } | 6839 } |
| 6937 } else { | |
| 6938 this._selection.clear(); | |
| 6939 } | 6840 } |
| 6940 }, | 6841 }, |
| 6941 | 6842 |
| 6942 _selectionChange: function() { | 6843 _selectionChange: function() { |
| 6943 var s = this._selection.get(); | 6844 var s = this._selection.get(); |
| 6944 if (this.multi) { | 6845 if (this.multi) { |
| 6945 this._setSelectedItems(s); | 6846 this._setSelectedItems(s); |
| 6946 } else { | 6847 } else { |
| 6947 this._setSelectedItems([s]); | 6848 this._setSelectedItems([s]); |
| 6948 this._setSelectedItem(s); | 6849 this._setSelectedItem(s); |
| 6949 } | 6850 } |
| 6950 }, | 6851 }, |
| 6951 | 6852 |
| 6952 _toggleSelected: function(value) { | 6853 _toggleSelected: function(value) { |
| 6953 var i = this.selectedValues.indexOf(value); | 6854 var i = this.selectedValues.indexOf(value); |
| 6954 var unselected = i < 0; | 6855 var unselected = i < 0; |
| 6955 if (unselected) { | 6856 if (unselected) { |
| 6956 this.push('selectedValues',value); | 6857 this.push('selectedValues',value); |
| 6957 } else { | 6858 } else { |
| 6958 this.splice('selectedValues',i,1); | 6859 this.splice('selectedValues',i,1); |
| 6959 } | 6860 } |
| 6960 this._selection.setItemSelected(this._valueToItem(value), unselected); | 6861 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); | |
| 6967 } | 6862 } |
| 6968 }; | 6863 }; |
| 6969 | 6864 |
| 6970 /** @polymerBehavior */ | 6865 /** @polymerBehavior */ |
| 6971 Polymer.IronMultiSelectableBehavior = [ | 6866 Polymer.IronMultiSelectableBehavior = [ |
| 6972 Polymer.IronSelectableBehavior, | 6867 Polymer.IronSelectableBehavior, |
| 6973 Polymer.IronMultiSelectableBehaviorImpl | 6868 Polymer.IronMultiSelectableBehaviorImpl |
| 6974 ]; | 6869 ]; |
| 6975 /** | 6870 /** |
| 6976 * `Polymer.IronMenuBehavior` implements accessible menu behavior. | 6871 * `Polymer.IronMenuBehavior` implements accessible menu behavior. |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7190 * Handler that is called when the menu receives focus. | 7085 * Handler that is called when the menu receives focus. |
| 7191 * | 7086 * |
| 7192 * @param {FocusEvent} event A focus event. | 7087 * @param {FocusEvent} event A focus event. |
| 7193 */ | 7088 */ |
| 7194 _onFocus: function(event) { | 7089 _onFocus: function(event) { |
| 7195 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { | 7090 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { |
| 7196 // do not focus the menu itself | 7091 // do not focus the menu itself |
| 7197 return; | 7092 return; |
| 7198 } | 7093 } |
| 7199 | 7094 |
| 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 | |
| 7208 this.blur(); | 7095 this.blur(); |
| 7209 | 7096 |
| 7210 // clear the cached focus item | 7097 // clear the cached focus item |
| 7211 this._defaultFocusAsync = this.async(function() { | 7098 this._defaultFocusAsync = this.async(function() { |
| 7212 // focus the selected item when the menu receives focus, or the first it
em | 7099 // focus the selected item when the menu receives focus, or the first it
em |
| 7213 // if no item is selected | 7100 // if no item is selected |
| 7214 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | 7101 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; |
| 7215 | 7102 |
| 7216 this._setFocusedItem(null); | 7103 this._setFocusedItem(null); |
| 7217 | 7104 |
| 7218 if (selectedItem) { | 7105 if (selectedItem) { |
| 7219 this._setFocusedItem(selectedItem); | 7106 this._setFocusedItem(selectedItem); |
| 7220 } else { | 7107 } else { |
| 7221 this._setFocusedItem(this.items[0]); | 7108 this._setFocusedItem(this.items[0]); |
| 7222 } | 7109 } |
| 7223 // async 1ms to wait for `select` to get called from `_itemActivate` | 7110 // async 1ms to wait for `select` to get called from `_itemActivate` |
| 7224 }, 1); | 7111 }, 1); |
| 7225 }, | 7112 }, |
| 7226 | 7113 |
| 7227 /** | 7114 /** |
| 7228 * Handler that is called when the up key is pressed. | 7115 * Handler that is called when the up key is pressed. |
| 7229 * | 7116 * |
| 7230 * @param {CustomEvent} event A key combination event. | 7117 * @param {CustomEvent} event A key combination event. |
| 7231 */ | 7118 */ |
| 7232 _onUpKey: function(event) { | 7119 _onUpKey: function(event) { |
| 7233 // up and down arrows moves the focus | 7120 // up and down arrows moves the focus |
| 7234 this._focusPrevious(); | 7121 this._focusPrevious(); |
| 7235 event.detail.keyboardEvent.preventDefault(); | |
| 7236 }, | 7122 }, |
| 7237 | 7123 |
| 7238 /** | 7124 /** |
| 7239 * Handler that is called when the down key is pressed. | 7125 * Handler that is called when the down key is pressed. |
| 7240 * | 7126 * |
| 7241 * @param {CustomEvent} event A key combination event. | 7127 * @param {CustomEvent} event A key combination event. |
| 7242 */ | 7128 */ |
| 7243 _onDownKey: function(event) { | 7129 _onDownKey: function(event) { |
| 7244 this._focusNext(); | 7130 this._focusNext(); |
| 7245 event.detail.keyboardEvent.preventDefault(); | |
| 7246 }, | 7131 }, |
| 7247 | 7132 |
| 7248 /** | 7133 /** |
| 7249 * Handler that is called when the esc key is pressed. | 7134 * Handler that is called when the esc key is pressed. |
| 7250 * | 7135 * |
| 7251 * @param {CustomEvent} event A key combination event. | 7136 * @param {CustomEvent} event A key combination event. |
| 7252 */ | 7137 */ |
| 7253 _onEscKey: function(event) { | 7138 _onEscKey: function(event) { |
| 7254 // esc blurs the control | 7139 // esc blurs the control |
| 7255 this.focusedItem.blur(); | 7140 this.focusedItem.blur(); |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7444 left: parseInt(target.marginLeft, 10) || 0 | 7329 left: parseInt(target.marginLeft, 10) || 0 |
| 7445 } | 7330 } |
| 7446 }; | 7331 }; |
| 7447 }, | 7332 }, |
| 7448 | 7333 |
| 7449 /** | 7334 /** |
| 7450 * Resets the target element's position and size constraints, and clear | 7335 * Resets the target element's position and size constraints, and clear |
| 7451 * the memoized data. | 7336 * the memoized data. |
| 7452 */ | 7337 */ |
| 7453 resetFit: function() { | 7338 resetFit: function() { |
| 7339 if (!this._fitInfo || !this._fitInfo.sizedBy.height) { |
| 7340 this.sizingTarget.style.maxHeight = ''; |
| 7341 this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : ''; |
| 7342 } |
| 7454 if (!this._fitInfo || !this._fitInfo.sizedBy.width) { | 7343 if (!this._fitInfo || !this._fitInfo.sizedBy.width) { |
| 7455 this.sizingTarget.style.maxWidth = ''; | 7344 this.sizingTarget.style.maxWidth = ''; |
| 7345 this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : ''; |
| 7456 } | 7346 } |
| 7457 if (!this._fitInfo || !this._fitInfo.sizedBy.height) { | |
| 7458 this.sizingTarget.style.maxHeight = ''; | |
| 7459 } | |
| 7460 this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : ''; | |
| 7461 this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : ''; | |
| 7462 if (this._fitInfo) { | 7347 if (this._fitInfo) { |
| 7463 this.style.position = this._fitInfo.positionedBy.css; | 7348 this.style.position = this._fitInfo.positionedBy.css; |
| 7464 } | 7349 } |
| 7465 this._fitInfo = null; | 7350 this._fitInfo = null; |
| 7466 }, | 7351 }, |
| 7467 | 7352 |
| 7468 /** | 7353 /** |
| 7469 * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
the element, | 7354 * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
the element, |
| 7470 * the window, or the `fitInfo` element has been resized. | 7355 * the window, or the `fitInfo` element has been resized. |
| 7471 */ | 7356 */ |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7512 var offsetExtent = 'offset' + extent; | 7397 var offsetExtent = 'offset' + extent; |
| 7513 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; | 7398 var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent]; |
| 7514 this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingO
ffset) + 'px'; | 7399 this.sizingTarget.style['max' + extent] = (max - margin - offset - sizingO
ffset) + 'px'; |
| 7515 }, | 7400 }, |
| 7516 | 7401 |
| 7517 /** | 7402 /** |
| 7518 * Centers horizontally and vertically if not already positioned. This also
sets | 7403 * Centers horizontally and vertically if not already positioned. This also
sets |
| 7519 * `position:fixed`. | 7404 * `position:fixed`. |
| 7520 */ | 7405 */ |
| 7521 center: function() { | 7406 center: function() { |
| 7522 var positionedBy = this._fitInfo.positionedBy; | 7407 if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.
horizontally) { |
| 7523 if (positionedBy.vertically && positionedBy.horizontally) { | 7408 // need position:fixed to center |
| 7524 // Already positioned. | 7409 this.style.position = 'fixed'; |
| 7525 return; | |
| 7526 } | 7410 } |
| 7527 // Need position:fixed to center | 7411 if (!this._fitInfo.positionedBy.vertically) { |
| 7528 this.style.position = 'fixed'; | 7412 var top = (this._fitHeight - this.offsetHeight) / 2 + this._fitTop; |
| 7529 // Take into account the offset caused by parents that create stacking | 7413 top -= this._fitInfo.margin.top; |
| 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; | |
| 7542 this.style.top = top + 'px'; | 7414 this.style.top = top + 'px'; |
| 7543 } | 7415 } |
| 7544 if (!positionedBy.horizontally) { | 7416 if (!this._fitInfo.positionedBy.horizontally) { |
| 7545 var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2
; | 7417 var left = (this._fitWidth - this.offsetWidth) / 2 + this._fitLeft; |
| 7418 left -= this._fitInfo.margin.left; |
| 7546 this.style.left = left + 'px'; | 7419 this.style.left = left + 'px'; |
| 7547 } | 7420 } |
| 7548 } | 7421 } |
| 7549 | 7422 |
| 7550 }; | 7423 }; |
| 7551 /** | 7424 /** |
| 7552 * @struct | 7425 * @struct |
| 7553 * @constructor | 7426 * @constructor |
| 7554 */ | 7427 */ |
| 7555 Polymer.IronOverlayManagerClass = function() { | 7428 Polymer.IronOverlayManagerClass = function() { |
| 7556 this._overlays = []; | 7429 this._overlays = []; |
| 7557 // Used to keep track of the last focused node before an overlay gets opened
. | |
| 7558 this._lastFocusedNodes = []; | |
| 7559 | |
| 7560 /** | 7430 /** |
| 7561 * iframes have a default z-index of 100, so this default should be at least | 7431 * iframes have a default z-index of 100, so this default should be at least |
| 7562 * that. | 7432 * that. |
| 7563 * @private {number} | 7433 * @private {number} |
| 7564 */ | 7434 */ |
| 7565 this._minimumZ = 101; | 7435 this._minimumZ = 101; |
| 7566 | 7436 |
| 7567 this._backdrops = []; | 7437 this._backdrops = []; |
| 7568 | 7438 } |
| 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 }; | |
| 7614 | 7439 |
| 7615 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { | 7440 Polymer.IronOverlayManagerClass.prototype._applyOverlayZ = function(overlay, a
boveZ) { |
| 7616 this._setZ(overlay, aboveZ + 2); | 7441 this._setZ(overlay, aboveZ + 2); |
| 7617 }; | 7442 }; |
| 7618 | 7443 |
| 7619 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { | 7444 Polymer.IronOverlayManagerClass.prototype._setZ = function(element, z) { |
| 7620 element.style.zIndex = z; | 7445 element.style.zIndex = z; |
| 7621 }; | 7446 }; |
| 7622 | 7447 |
| 7623 /** | 7448 /** |
| 7624 * track overlays for z-index and focus managemant | 7449 * track overlays for z-index and focus managemant |
| 7625 */ | 7450 */ |
| 7626 Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { | 7451 Polymer.IronOverlayManagerClass.prototype.addOverlay = function(overlay) { |
| 7627 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); | 7452 var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ); |
| 7628 this._overlays.push(overlay); | 7453 this._overlays.push(overlay); |
| 7629 var newZ = this.currentOverlayZ(); | 7454 var newZ = this.currentOverlayZ(); |
| 7630 if (newZ <= minimumZ) { | 7455 if (newZ <= minimumZ) { |
| 7631 this._applyOverlayZ(overlay, minimumZ); | 7456 this._applyOverlayZ(overlay, minimumZ); |
| 7632 } | 7457 } |
| 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); | |
| 7639 }; | 7458 }; |
| 7640 | 7459 |
| 7641 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { | 7460 Polymer.IronOverlayManagerClass.prototype.removeOverlay = function(overlay) { |
| 7642 var i = this._overlays.indexOf(overlay); | 7461 var i = this._overlays.indexOf(overlay); |
| 7643 if (i >= 0) { | 7462 if (i >= 0) { |
| 7644 this._overlays.splice(i, 1); | 7463 this._overlays.splice(i, 1); |
| 7645 this._setZ(overlay, ''); | 7464 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); | |
| 7653 } | 7465 } |
| 7654 }; | 7466 }; |
| 7655 | 7467 |
| 7656 Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { | 7468 Polymer.IronOverlayManagerClass.prototype.currentOverlay = function() { |
| 7657 var i = this._overlays.length - 1; | 7469 var i = this._overlays.length - 1; |
| 7658 while (this._overlays[i] && !this._overlays[i].opened) { | 7470 while (this._overlays[i] && !this._overlays[i].opened) { |
| 7659 --i; | 7471 --i; |
| 7660 } | 7472 } |
| 7661 return this._overlays[i]; | 7473 return this._overlays[i]; |
| 7662 }; | 7474 }; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7697 if (element.opened && element.withBackdrop) { | 7509 if (element.opened && element.withBackdrop) { |
| 7698 // no duplicates | 7510 // no duplicates |
| 7699 if (index === -1) { | 7511 if (index === -1) { |
| 7700 this._backdrops.push(element); | 7512 this._backdrops.push(element); |
| 7701 } | 7513 } |
| 7702 } else if (index >= 0) { | 7514 } else if (index >= 0) { |
| 7703 this._backdrops.splice(index, 1); | 7515 this._backdrops.splice(index, 1); |
| 7704 } | 7516 } |
| 7705 }; | 7517 }; |
| 7706 | 7518 |
| 7519 Object.defineProperty(Polymer.IronOverlayManagerClass.prototype, "backdropElem
ent", { |
| 7520 get: function() { |
| 7521 if (!this._backdropElement) { |
| 7522 this._backdropElement = document.createElement('iron-overlay-backdrop'); |
| 7523 } |
| 7524 return this._backdropElement; |
| 7525 } |
| 7526 }); |
| 7527 |
| 7707 Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { | 7528 Polymer.IronOverlayManagerClass.prototype.getBackdrops = function() { |
| 7708 return this._backdrops; | 7529 return this._backdrops; |
| 7709 }; | 7530 }; |
| 7710 | 7531 |
| 7711 /** | 7532 /** |
| 7712 * Returns the z-index for the backdrop. | 7533 * Returns the z-index for the backdrop. |
| 7713 */ | 7534 */ |
| 7714 Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { | 7535 Polymer.IronOverlayManagerClass.prototype.backdropZ = function() { |
| 7715 return this._getOverlayZ(this._overlayWithBackdrop()) - 1; | 7536 return this._getOverlayZ(this._overlayWithBackdrop()) - 1; |
| 7716 }; | 7537 }; |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7926 | 7747 |
| 7927 /** | 7748 /** |
| 7928 * Returns the reason this dialog was last closed. | 7749 * Returns the reason this dialog was last closed. |
| 7929 */ | 7750 */ |
| 7930 closingReason: { | 7751 closingReason: { |
| 7931 // was a getter before, but needs to be a property so other | 7752 // was a getter before, but needs to be a property so other |
| 7932 // behaviors can override this. | 7753 // behaviors can override this. |
| 7933 type: Object | 7754 type: Object |
| 7934 }, | 7755 }, |
| 7935 | 7756 |
| 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 | |
| 7953 _manager: { | 7757 _manager: { |
| 7954 type: Object, | 7758 type: Object, |
| 7955 value: Polymer.IronOverlayManager | 7759 value: Polymer.IronOverlayManager |
| 7956 }, | 7760 }, |
| 7957 | 7761 |
| 7958 _boundOnCaptureClick: { | 7762 _boundOnCaptureClick: { |
| 7959 type: Function, | 7763 type: Function, |
| 7960 value: function() { | 7764 value: function() { |
| 7961 return this._onCaptureClick.bind(this); | 7765 return this._onCaptureClick.bind(this); |
| 7962 } | 7766 } |
| 7963 }, | 7767 }, |
| 7964 | 7768 |
| 7769 _boundOnCaptureKeydown: { |
| 7770 type: Function, |
| 7771 value: function() { |
| 7772 return this._onCaptureKeydown.bind(this); |
| 7773 } |
| 7774 }, |
| 7775 |
| 7965 _boundOnCaptureFocus: { | 7776 _boundOnCaptureFocus: { |
| 7966 type: Function, | 7777 type: Function, |
| 7967 value: function() { | 7778 value: function() { |
| 7968 return this._onCaptureFocus.bind(this); | 7779 return this._onCaptureFocus.bind(this); |
| 7969 } | 7780 } |
| 7970 }, | 7781 }, |
| 7971 | 7782 |
| 7972 /** | 7783 /** @type {?Node} */ |
| 7973 * The node being focused. | |
| 7974 * @type {?Node} | |
| 7975 */ | |
| 7976 _focusedChild: { | 7784 _focusedChild: { |
| 7977 type: Object | 7785 type: Object |
| 7978 } | 7786 } |
| 7979 | 7787 |
| 7980 }, | 7788 }, |
| 7981 | 7789 |
| 7982 keyBindings: { | |
| 7983 'esc': '__onEsc', | |
| 7984 'tab': '__onTab' | |
| 7985 }, | |
| 7986 | |
| 7987 listeners: { | 7790 listeners: { |
| 7988 'iron-resize': '_onIronResize' | 7791 'iron-resize': '_onIronResize' |
| 7989 }, | 7792 }, |
| 7990 | 7793 |
| 7991 /** | 7794 /** |
| 7992 * The backdrop element. | 7795 * The backdrop element. |
| 7993 * @type {Node} | 7796 * @type Node |
| 7994 */ | 7797 */ |
| 7995 get backdropElement() { | 7798 get backdropElement() { |
| 7996 return this._manager.backdropElement; | 7799 return this._manager.backdropElement; |
| 7997 }, | 7800 }, |
| 7998 | 7801 |
| 7999 /** | |
| 8000 * Returns the node to give focus to. | |
| 8001 * @type {Node} | |
| 8002 */ | |
| 8003 get _focusNode() { | 7802 get _focusNode() { |
| 8004 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; | 7803 return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]'
) || this; |
| 8005 }, | 7804 }, |
| 8006 | 7805 |
| 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 | |
| 8059 ready: function() { | 7806 ready: function() { |
| 8060 // with-backdrop needs tabindex to be set in order to trap the focus. | 7807 // with-backdrop need tabindex to be set in order to trap the focus. |
| 8061 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. | 7808 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 8062 this.__shouldRemoveTabIndex = false; | 7809 this.__shouldRemoveTabIndex = false; |
| 8063 // Used for wrapping the focus on TAB / Shift+TAB. | |
| 8064 this.__firstFocusableNode = this.__lastFocusableNode = null; | |
| 8065 this._ensureSetup(); | 7810 this._ensureSetup(); |
| 8066 }, | 7811 }, |
| 8067 | 7812 |
| 8068 attached: function() { | 7813 attached: function() { |
| 8069 // Call _openedChanged here so that position can be computed correctly. | 7814 // Call _openedChanged here so that position can be computed correctly. |
| 8070 if (this.opened) { | 7815 if (this._callOpenedWhenReady) { |
| 8071 this._openedChanged(); | 7816 this._openedChanged(); |
| 8072 } | 7817 } |
| 8073 this._observer = Polymer.dom(this).observeNodes(this._onNodesChange); | |
| 8074 }, | 7818 }, |
| 8075 | 7819 |
| 8076 detached: function() { | 7820 detached: function() { |
| 8077 Polymer.dom(this).unobserveNodes(this._observer); | |
| 8078 this._observer = null; | |
| 8079 this.opened = false; | 7821 this.opened = false; |
| 8080 this._manager.trackBackdrop(this); | 7822 this._manager.trackBackdrop(this); |
| 8081 this._manager.removeOverlay(this); | 7823 this._manager.removeOverlay(this); |
| 8082 }, | 7824 }, |
| 8083 | 7825 |
| 8084 /** | 7826 /** |
| 8085 * Toggle the opened state of the overlay. | 7827 * Toggle the opened state of the overlay. |
| 8086 */ | 7828 */ |
| 8087 toggle: function() { | 7829 toggle: function() { |
| 8088 this._setCanceled(false); | 7830 this._setCanceled(false); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 8100 /** | 7842 /** |
| 8101 * Close the overlay. | 7843 * Close the overlay. |
| 8102 */ | 7844 */ |
| 8103 close: function() { | 7845 close: function() { |
| 8104 this._setCanceled(false); | 7846 this._setCanceled(false); |
| 8105 this.opened = false; | 7847 this.opened = false; |
| 8106 }, | 7848 }, |
| 8107 | 7849 |
| 8108 /** | 7850 /** |
| 8109 * Cancels the overlay. | 7851 * Cancels the overlay. |
| 8110 * @param {?Event} event The original event | |
| 8111 */ | 7852 */ |
| 8112 cancel: function(event) { | 7853 cancel: function() { |
| 8113 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); | 7854 var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelabl
e: true}); |
| 8114 if (cancelEvent.defaultPrevented) { | 7855 if (cancelEvent.defaultPrevented) { |
| 8115 return; | 7856 return; |
| 8116 } | 7857 } |
| 8117 | 7858 |
| 8118 this._setCanceled(true); | 7859 this._setCanceled(true); |
| 8119 this.opened = false; | 7860 this.opened = false; |
| 8120 }, | 7861 }, |
| 8121 | 7862 |
| 8122 _ensureSetup: function() { | 7863 _ensureSetup: function() { |
| 8123 if (this._overlaySetup) { | 7864 if (this._overlaySetup) { |
| 8124 return; | 7865 return; |
| 8125 } | 7866 } |
| 8126 this._overlaySetup = true; | 7867 this._overlaySetup = true; |
| 8127 this.style.outline = 'none'; | 7868 this.style.outline = 'none'; |
| 8128 this.style.display = 'none'; | 7869 this.style.display = 'none'; |
| 8129 }, | 7870 }, |
| 8130 | 7871 |
| 8131 _openedChanged: function() { | 7872 _openedChanged: function() { |
| 8132 if (this.opened) { | 7873 if (this.opened) { |
| 8133 this.removeAttribute('aria-hidden'); | 7874 this.removeAttribute('aria-hidden'); |
| 8134 } else { | 7875 } else { |
| 8135 this.setAttribute('aria-hidden', 'true'); | 7876 this.setAttribute('aria-hidden', 'true'); |
| 7877 Polymer.dom(this).unobserveNodes(this._observer); |
| 8136 } | 7878 } |
| 8137 | 7879 |
| 8138 // wait to call after ready only if we're initially open | 7880 // wait to call after ready only if we're initially open |
| 8139 if (!this._overlaySetup) { | 7881 if (!this._overlaySetup) { |
| 7882 this._callOpenedWhenReady = this.opened; |
| 8140 return; | 7883 return; |
| 8141 } | 7884 } |
| 8142 | 7885 |
| 8143 this._manager.trackBackdrop(this); | 7886 this._manager.trackBackdrop(this); |
| 8144 | 7887 |
| 8145 if (this.opened) { | 7888 if (this.opened) { |
| 8146 this._prepareRenderOpened(); | 7889 this._prepareRenderOpened(); |
| 8147 } | 7890 } |
| 8148 | 7891 |
| 8149 if (this._openChangedAsync) { | 7892 if (this._openChangedAsync) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8204 node.addEventListener(event, boundListener, capture); | 7947 node.addEventListener(event, boundListener, capture); |
| 8205 } else { | 7948 } else { |
| 8206 // disable document-wide tap recognizer | 7949 // disable document-wide tap recognizer |
| 8207 if (event === 'tap') { | 7950 if (event === 'tap') { |
| 8208 Polymer.Gestures.remove(document, 'tap', null); | 7951 Polymer.Gestures.remove(document, 'tap', null); |
| 8209 } | 7952 } |
| 8210 node.removeEventListener(event, boundListener, capture); | 7953 node.removeEventListener(event, boundListener, capture); |
| 8211 } | 7954 } |
| 8212 }, | 7955 }, |
| 8213 | 7956 |
| 8214 _toggleListeners: function() { | 7957 _toggleListeners: function () { |
| 8215 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); | 7958 this._toggleListener(this.opened, document, 'tap', this._boundOnCaptureCli
ck, true); |
| 7959 this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptur
eKeydown, true); |
| 8216 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); | 7960 this._toggleListener(this.opened, document, 'focus', this._boundOnCaptureF
ocus, true); |
| 8217 }, | 7961 }, |
| 8218 | 7962 |
| 8219 // tasks which must occur before opening; e.g. making the element visible | 7963 // tasks which must occur before opening; e.g. making the element visible |
| 8220 _prepareRenderOpened: function() { | 7964 _prepareRenderOpened: function() { |
| 8221 | |
| 8222 this._manager.addOverlay(this); | 7965 this._manager.addOverlay(this); |
| 8223 | 7966 |
| 8224 // Needed to calculate the size of the overlay so that transitions on its
size | |
| 8225 // will have the correct starting points. | |
| 8226 this._preparePositioning(); | 7967 this._preparePositioning(); |
| 8227 this.fit(); | 7968 this.fit(); |
| 8228 this._finishPositioning(); | 7969 this._finishPositioning(); |
| 8229 | 7970 |
| 8230 if (this.withBackdrop) { | 7971 if (this.withBackdrop) { |
| 8231 this.backdropElement.prepare(); | 7972 this.backdropElement.prepare(); |
| 8232 } | 7973 } |
| 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 } | |
| 8239 }, | 7974 }, |
| 8240 | 7975 |
| 8241 // tasks which cause the overlay to actually open; typically play an | 7976 // tasks which cause the overlay to actually open; typically play an |
| 8242 // animation | 7977 // animation |
| 8243 _renderOpened: function() { | 7978 _renderOpened: function() { |
| 8244 if (this.withBackdrop) { | 7979 if (this.withBackdrop) { |
| 8245 this.backdropElement.open(); | 7980 this.backdropElement.open(); |
| 8246 } | 7981 } |
| 8247 this._finishRenderOpened(); | 7982 this._finishRenderOpened(); |
| 8248 }, | 7983 }, |
| 8249 | 7984 |
| 8250 _renderClosed: function() { | 7985 _renderClosed: function() { |
| 8251 if (this.withBackdrop) { | 7986 if (this.withBackdrop) { |
| 8252 this.backdropElement.close(); | 7987 this.backdropElement.close(); |
| 8253 } | 7988 } |
| 8254 this._finishRenderClosed(); | 7989 this._finishRenderClosed(); |
| 8255 }, | 7990 }, |
| 8256 | 7991 |
| 8257 _finishRenderOpened: function() { | 7992 _finishRenderOpened: function() { |
| 8258 // This ensures the overlay is visible before we set the focus | 7993 // focus the child node with [autofocus] |
| 8259 // (by calling _onIronResize -> refit). | |
| 8260 this.notifyResize(); | |
| 8261 // Focus the child node with [autofocus] | |
| 8262 this._applyFocus(); | 7994 this._applyFocus(); |
| 8263 | 7995 |
| 7996 this._observer = Polymer.dom(this).observeNodes(this.notifyResize); |
| 8264 this.fire('iron-overlay-opened'); | 7997 this.fire('iron-overlay-opened'); |
| 8265 }, | 7998 }, |
| 8266 | 7999 |
| 8267 _finishRenderClosed: function() { | 8000 _finishRenderClosed: function() { |
| 8268 // Hide the overlay and remove the backdrop. | 8001 // hide the overlay and remove the backdrop |
| 8269 this.resetFit(); | 8002 this.resetFit(); |
| 8270 this.style.display = 'none'; | 8003 this.style.display = 'none'; |
| 8271 this._manager.removeOverlay(this); | 8004 this._manager.removeOverlay(this); |
| 8272 | 8005 |
| 8006 this._focusedChild = null; |
| 8273 this._applyFocus(); | 8007 this._applyFocus(); |
| 8008 |
| 8274 this.notifyResize(); | 8009 this.notifyResize(); |
| 8275 | |
| 8276 this.fire('iron-overlay-closed', this.closingReason); | 8010 this.fire('iron-overlay-closed', this.closingReason); |
| 8277 }, | 8011 }, |
| 8278 | 8012 |
| 8279 _preparePositioning: function() { | 8013 _preparePositioning: function() { |
| 8280 this.style.transition = this.style.webkitTransition = 'none'; | 8014 this.style.transition = this.style.webkitTransition = 'none'; |
| 8281 this.style.transform = this.style.webkitTransform = 'none'; | 8015 this.style.transform = this.style.webkitTransform = 'none'; |
| 8282 this.style.display = ''; | 8016 this.style.display = ''; |
| 8283 }, | 8017 }, |
| 8284 | 8018 |
| 8285 _finishPositioning: function() { | 8019 _finishPositioning: function() { |
| 8286 this.style.display = 'none'; | 8020 this.style.display = 'none'; |
| 8287 this.style.transform = this.style.webkitTransform = ''; | 8021 this.style.transform = this.style.webkitTransform = ''; |
| 8288 // Force layout layout to avoid application of transform. | 8022 // force layout to avoid application of transform |
| 8289 // Set offsetWidth to itself so that compilers won't remove it. | 8023 /** @suppress {suspiciousCode} */ this.offsetWidth; |
| 8290 this.offsetWidth = this.offsetWidth; | |
| 8291 this.style.transition = this.style.webkitTransition = ''; | 8024 this.style.transition = this.style.webkitTransition = ''; |
| 8292 }, | 8025 }, |
| 8293 | 8026 |
| 8294 _applyFocus: function() { | 8027 _applyFocus: function() { |
| 8295 if (this.opened) { | 8028 if (this.opened) { |
| 8296 if (!this.noAutoFocus) { | 8029 if (!this.noAutoFocus) { |
| 8297 this._focusNode.focus(); | 8030 this._focusNode.focus(); |
| 8298 } | 8031 } |
| 8299 } else { | 8032 } else { |
| 8300 this._focusNode.blur(); | 8033 this._focusNode.blur(); |
| 8301 this._focusedChild = null; | |
| 8302 this._manager.focusOverlay(); | 8034 this._manager.focusOverlay(); |
| 8303 } | 8035 } |
| 8304 }, | 8036 }, |
| 8305 | 8037 |
| 8306 _onCaptureClick: function(event) { | 8038 _onCaptureClick: function(event) { |
| 8307 if (this._manager.currentOverlay() === this && | 8039 if (this._manager.currentOverlay() === this && |
| 8308 Polymer.dom(event).path.indexOf(this) === -1) { | 8040 Polymer.dom(event).path.indexOf(this) === -1) { |
| 8309 if (this.noCancelOnOutsideClick) { | 8041 if (this.noCancelOnOutsideClick) { |
| 8310 this._applyFocus(); | 8042 this._applyFocus(); |
| 8311 } else { | 8043 } else { |
| 8312 this.cancel(event); | 8044 this.cancel(); |
| 8313 } | 8045 } |
| 8314 } | 8046 } |
| 8315 }, | 8047 }, |
| 8316 | 8048 |
| 8049 _onCaptureKeydown: function(event) { |
| 8050 var ESC = 27; |
| 8051 if (this._manager.currentOverlay() === this && |
| 8052 !this.noCancelOnEscKey && |
| 8053 event.keyCode === ESC) { |
| 8054 this.cancel(); |
| 8055 } |
| 8056 }, |
| 8057 |
| 8317 _onCaptureFocus: function (event) { | 8058 _onCaptureFocus: function (event) { |
| 8318 if (this._manager.currentOverlay() === this && this.withBackdrop) { | 8059 if (this._manager.currentOverlay() === this && |
| 8060 this.withBackdrop) { |
| 8319 var path = Polymer.dom(event).path; | 8061 var path = Polymer.dom(event).path; |
| 8320 if (path.indexOf(this) === -1) { | 8062 if (path.indexOf(this) === -1) { |
| 8321 event.stopPropagation(); | 8063 event.stopPropagation(); |
| 8322 this._applyFocus(); | 8064 this._applyFocus(); |
| 8323 } else { | 8065 } else { |
| 8324 this._focusedChild = path[0]; | 8066 this._focusedChild = path[0]; |
| 8325 } | 8067 } |
| 8326 } | 8068 } |
| 8327 }, | 8069 }, |
| 8328 | 8070 |
| 8329 _onIronResize: function() { | 8071 _onIronResize: function() { |
| 8330 if (this.opened) { | 8072 if (this.opened) { |
| 8331 this.refit(); | 8073 this.refit(); |
| 8332 } | 8074 } |
| 8333 }, | 8075 } |
| 8334 | 8076 |
| 8335 /** | 8077 /** |
| 8336 * @protected | 8078 * Fired after the `iron-overlay` opens. |
| 8337 * Will call notifyResize if overlay is opened. | 8079 * @event iron-overlay-opened |
| 8338 * Can be overridden in order to avoid multiple observers on the same node. | 8080 */ |
| 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 | 8081 |
| 8350 __onEsc: function(event) { | 8082 /** |
| 8351 // Not opened or not on top, so return. | 8083 * Fired when the `iron-overlay` is canceled, but before it is closed. |
| 8352 if (this._manager.currentOverlay() !== this) { | 8084 * Cancel the event to prevent the `iron-overlay` from closing. |
| 8353 return; | 8085 * @event iron-overlay-canceled |
| 8354 } | 8086 */ |
| 8355 if (!this.noCancelOnEscKey) { | |
| 8356 this.cancel(event); | |
| 8357 } | |
| 8358 }, | |
| 8359 | 8087 |
| 8360 __onTab: function(event) { | 8088 /** |
| 8361 // Not opened or not on top, so return. | 8089 * Fired after the `iron-overlay` closes. |
| 8362 if (this._manager.currentOverlay() !== this) { | 8090 * @event iron-overlay-closed |
| 8363 return; | 8091 * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute |
| 8364 } | 8092 */ |
| 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 } | |
| 8375 } | |
| 8376 }; | 8093 }; |
| 8377 | 8094 |
| 8378 /** @polymerBehavior */ | 8095 /** @polymerBehavior */ |
| 8379 Polymer.IronOverlayBehavior = [Polymer.IronA11yKeysBehavior, Polymer.IronFitBe
havior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl]; | 8096 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, 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 */ | |
| 8400 /** | 8097 /** |
| 8401 * Use `Polymer.NeonAnimationBehavior` to implement an animation. | 8098 * Use `Polymer.NeonAnimationBehavior` to implement an animation. |
| 8402 * @polymerBehavior | 8099 * @polymerBehavior |
| 8403 */ | 8100 */ |
| 8404 Polymer.NeonAnimationBehavior = { | 8101 Polymer.NeonAnimationBehavior = { |
| 8405 | 8102 |
| 8406 properties: { | 8103 properties: { |
| 8407 | 8104 |
| 8408 /** | 8105 /** |
| 8409 * Defines the animation timing. | 8106 * Defines the animation timing. |
| (...skipping 1530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9940 You can use custom validators that implement `Polymer.IronValidatorBehavior` wit
h `<iron-input>`. | 9637 You can use custom validators that implement `Polymer.IronValidatorBehavior` wit
h `<iron-input>`. |
| 9941 | 9638 |
| 9942 <input is="iron-input" validator="my-custom-validator"> | 9639 <input is="iron-input" validator="my-custom-validator"> |
| 9943 | 9640 |
| 9944 ### Stopping invalid input | 9641 ### Stopping invalid input |
| 9945 | 9642 |
| 9946 It may be desirable to only allow users to enter certain characters. You can use
the | 9643 It may be desirable to only allow users to enter certain characters. You can use
the |
| 9947 `prevent-invalid-input` and `allowed-pattern` attributes together to accomplish
this. This feature | 9644 `prevent-invalid-input` and `allowed-pattern` attributes together to accomplish
this. This feature |
| 9948 is separate from validation, and `allowed-pattern` does not affect how the input
is validated. | 9645 is separate from validation, and `allowed-pattern` does not affect how the input
is validated. |
| 9949 | 9646 |
| 9950 <!-- only allow characters that match [0-9] --> | 9647 \x3c!-- only allow characters that match [0-9] --\x3e |
| 9951 <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> | 9648 <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> |
| 9952 | 9649 |
| 9953 @hero hero.svg | 9650 @hero hero.svg |
| 9954 @demo demo/index.html | 9651 @demo demo/index.html |
| 9955 */ | 9652 */ |
| 9956 | 9653 |
| 9957 Polymer({ | 9654 Polymer({ |
| 9958 | 9655 |
| 9959 is: 'iron-input', | 9656 is: 'iron-input', |
| 9960 | 9657 |
| (...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10470 | 10167 |
| 10471 /** | 10168 /** |
| 10472 * Returns the value of the search field. | 10169 * Returns the value of the search field. |
| 10473 * @return {string} | 10170 * @return {string} |
| 10474 */ | 10171 */ |
| 10475 getValue: function() { | 10172 getValue: function() { |
| 10476 var searchInput = this.getSearchInput_(); | 10173 var searchInput = this.getSearchInput_(); |
| 10477 return searchInput ? searchInput.value : ''; | 10174 return searchInput ? searchInput.value : ''; |
| 10478 }, | 10175 }, |
| 10479 | 10176 |
| 10177 /** |
| 10178 * Sets the value of the search field, if it exists. |
| 10179 * @param {string} value |
| 10180 */ |
| 10181 setValue: function(value) { |
| 10182 var searchInput = this.getSearchInput_(); |
| 10183 if (searchInput) |
| 10184 searchInput.value = value; |
| 10185 }, |
| 10186 |
| 10480 /** @param {SearchFieldDelegate} delegate */ | 10187 /** @param {SearchFieldDelegate} delegate */ |
| 10481 setDelegate: function(delegate) { | 10188 setDelegate: function(delegate) { |
| 10482 this.delegate_ = delegate; | 10189 this.delegate_ = delegate; |
| 10483 }, | 10190 }, |
| 10484 | 10191 |
| 10192 /** @return {Promise<boolean>} */ |
| 10485 showAndFocus: function() { | 10193 showAndFocus: function() { |
| 10486 this.showingSearch_ = true; | 10194 this.showingSearch_ = true; |
| 10487 this.focus_(); | 10195 return this.focus_(); |
| 10488 }, | |
| 10489 | |
| 10490 /** @private */ | |
| 10491 focus_: function() { | |
| 10492 this.async(function() { | |
| 10493 if (!this.showingSearch_) | |
| 10494 return; | |
| 10495 | |
| 10496 var searchInput = this.getSearchInput_(); | |
| 10497 if (searchInput) | |
| 10498 searchInput.focus(); | |
| 10499 }); | |
| 10500 }, | 10196 }, |
| 10501 | 10197 |
| 10502 /** | 10198 /** |
| 10199 * @return {Promise<boolean>} |
| 10200 * @private |
| 10201 */ |
| 10202 focus_: function() { |
| 10203 return new Promise(function(resolve) { |
| 10204 this.async(function() { |
| 10205 if (this.showingSearch_) { |
| 10206 var searchInput = this.getSearchInput_(); |
| 10207 if (searchInput) |
| 10208 searchInput.focus(); |
| 10209 } |
| 10210 resolve(this.showingSearch_); |
| 10211 }); |
| 10212 }.bind(this)); |
| 10213 }, |
| 10214 |
| 10215 /** |
| 10503 * @return {?Element} | 10216 * @return {?Element} |
| 10504 * @private | 10217 * @private |
| 10505 */ | 10218 */ |
| 10506 getSearchInput_: function() { | 10219 getSearchInput_: function() { |
| 10507 return this.$$('#search-input'); | 10220 return this.$$('#search-input'); |
| 10508 }, | 10221 }, |
| 10509 | 10222 |
| 10510 /** @private */ | 10223 /** @private */ |
| 10511 onSearchTermSearch_: function() { | 10224 onSearchTermSearch_: function() { |
| 10512 if (this.delegate_) | 10225 if (this.delegate_) |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10816 Manager.removeItem = function(index) { | 10529 Manager.removeItem = function(index) { |
| 10817 Manager.get().removeItem_(index); | 10530 Manager.get().removeItem_(index); |
| 10818 }; | 10531 }; |
| 10819 | 10532 |
| 10820 Manager.updateItem = function(index, data) { | 10533 Manager.updateItem = function(index, data) { |
| 10821 Manager.get().updateItem_(index, data); | 10534 Manager.get().updateItem_(index, data); |
| 10822 }; | 10535 }; |
| 10823 | 10536 |
| 10824 return {Manager: Manager}; | 10537 return {Manager: Manager}; |
| 10825 }); | 10538 }); |
| 10539 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 10540 // Use of this source code is governed by a BSD-style license that can be |
| 10541 // found in the LICENSE file. |
| 10542 |
| 10543 // <include src="../../../../ui/webui/resources/js/i18n_template_no_process.js"> |
| 10544 |
| 10545 i18nTemplate.process(document, loadTimeData); |
| 10826 // Copyright 2015 The Chromium Authors. All rights reserved. | 10546 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 10827 // Use of this source code is governed by a BSD-style license that can be | 10547 // Use of this source code is governed by a BSD-style license that can be |
| 10828 // found in the LICENSE file. | 10548 // found in the LICENSE file. |
| 10829 | 10549 |
| 10830 window.addEventListener('load', downloads.Manager.onLoad); | 10550 window.addEventListener('load', downloads.Manager.onLoad); |
| OLD | NEW |