` has `tabIndex = -1`\n * @param {!HTMLElement} element\n * @return {!number}\n * @private\n */\n _normalizedTabIndex: function(element) {\n if (this.isFocusable(element)) {\n var tabIndex = element.getAttribute('tabindex') || 0;\n return Number(tabIndex);\n }\n return -1;\n },\n\n /**\n * Searches for nodes that are tabbable and adds them to the `result` array.\n * Returns if the `result` array needs to be sorted by tabindex.\n * @param {!Node} node The starting point for the search; added to `result`\n * if tabbable.\n * @param {!Array} result\n * @return {boolean}\n * @private\n */\n _collectTabbableNodes: function(node, result) {\n // If not an element or not visible, no need to explore children.\n if (node.nodeType !== Node.ELEMENT_NODE || !this._isVisible(node)) {\n return false;\n }\n var element = /** @type {!HTMLElement} */ (node);\n var tabIndex = this._normalizedTabIndex(element);\n var needsSort = tabIndex > 0;\n if (tabIndex >= 0) {\n result.push(element);\n }\n\n // In ShadowDOM v1, tab order is affected by the order of distrubution.\n // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];\n // in ShadowDOM v0 tab order is not affected by the distrubution order,\n // in fact getTabbableNodes(#root) returns [#B, #A].\n //
\n // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.\n var children;\n if (element.localName === 'content' || element.localName === 'slot') {\n children = dom(element).getDistributedNodes();\n } else {\n // Use shadow root if possible, will check for distributed nodes.\n children = dom(element.root || element).children;\n }\n for (var i = 0; i < children.length; i++) {\n // Ensure method is always invoked to collect tabbable children.\n needsSort = this._collectTabbableNodes(children[i], result) || needsSort;\n }\n return needsSort;\n },\n\n /**\n * Returns false if the element has `visibility: hidden` or `display: none`\n * @param {!HTMLElement} element\n * @return {boolean}\n * @private\n */\n _isVisible: function(element) {\n // Check inline style first to save a re-flow. If looks good, check also\n // computed style.\n var style = element.style;\n if (style.visibility !== 'hidden' && style.display !== 'none') {\n style = window.getComputedStyle(element);\n return (style.visibility !== 'hidden' && style.display !== 'none');\n }\n return false;\n },\n\n /**\n * Sorts an array of tabbable elements by tabindex. Returns a new array.\n * @param {!Array} tabbables\n * @return {!Array}\n * @private\n */\n _sortByTabIndex: function(tabbables) {\n // Implement a merge sort as Array.prototype.sort does a non-stable sort\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\n var len = tabbables.length;\n if (len < 2) {\n return tabbables;\n }\n var pivot = Math.ceil(len / 2);\n var left = this._sortByTabIndex(tabbables.slice(0, pivot));\n var right = this._sortByTabIndex(tabbables.slice(pivot));\n return this._mergeSortByTabIndex(left, right);\n },\n\n /**\n * Merge sort iterator, merges the two arrays into one, sorted by tab index.\n * @param {!Array} left\n * @param {!Array} right\n * @return {!Array}\n * @private\n */\n _mergeSortByTabIndex: function(left, right) {\n var result = [];\n while ((left.length > 0) && (right.length > 0)) {\n if (this._hasLowerTabOrder(left[0], right[0])) {\n result.push(right.shift());\n } else {\n result.push(left.shift());\n }\n }\n\n return result.concat(left, right);\n },\n\n /**\n * Returns if element `a` has lower tab order compared to element `b`\n * (both elements are assumed to be focusable and tabbable).\n * Elements with tabindex = 0 have lower tab order compared to elements\n * with tabindex > 0.\n * If both have same tabindex, it returns false.\n * @param {!HTMLElement} a\n * @param {!HTMLElement} b\n * @return {boolean}\n * @private\n */\n _hasLowerTabOrder: function(a, b) {\n // Normalize tabIndexes\n // e.g. in Firefox `
` has `tabIndex = -1`\n var ati = Math.max(a.tabIndex, 0);\n var bti = Math.max(b.tabIndex, 0);\n return (ati === 0 || bti === 0) ? bti > ati : ati > bti;\n }\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\n\n/**\n`Polymer.IronFitBehavior` fits an element in another element using `max-height`\nand `max-width`, and optionally centers it in the window or another element.\n\nThe element will only be sized and/or positioned if it has not already been\nsized and/or positioned by CSS.\n\nCSS properties | Action\n--------------------------|-------------------------------------------\n`position` set | Element is not centered horizontally or vertically\n`top` or `bottom` set | Element is not vertically centered\n`left` or `right` set | Element is not horizontally centered\n`max-height` set | Element respects `max-height`\n`max-width` set | Element respects `max-width`\n\n`Polymer.IronFitBehavior` can position an element into another element using\n`verticalAlign` and `horizontalAlign`. This will override the element's css\nposition.\n\n
\n \n Positioned into the container\n \n
\n\nUse `noOverlap` to position the element around another element without\noverlapping it.\n\n
\n \n Positioned around the container\n \n
\n\nUse `horizontalOffset, verticalOffset` to offset the element from its\n`positionTarget`; `Polymer.IronFitBehavior` will collapse these in order to\nkeep the element within `fitInto` boundaries, while preserving the element's\nCSS margin values.\n\n
\n \n With vertical offset\n \n
\n\n@demo demo/index.html\n@polymerBehavior\n*/\nexport const IronFitBehavior = {\n\n properties: {\n\n /**\n * The element that will receive a `max-height`/`width`. By default it is\n * the same as `this`, but it can be set to a child element. This is useful,\n * for example, for implementing a scrolling region inside the element.\n * @type {!Element}\n */\n sizingTarget: {\n type: Object,\n value: function() {\n return this;\n }\n },\n\n /**\n * The element to fit `this` into.\n */\n fitInto: {type: Object, value: window},\n\n /**\n * Will position the element around the positionTarget without overlapping\n * it.\n */\n noOverlap: {type: Boolean},\n\n /**\n * The element that should be used to position the element. If not set, it\n * will default to the parent node.\n * @type {!Element}\n */\n positionTarget: {type: Element},\n\n /**\n * The orientation against which to align the element horizontally\n * relative to the `positionTarget`. Possible values are \"left\", \"right\",\n * \"center\", \"auto\".\n */\n horizontalAlign: {type: String},\n\n /**\n * The orientation against which to align the element vertically\n * relative to the `positionTarget`. Possible values are \"top\", \"bottom\",\n * \"middle\", \"auto\".\n */\n verticalAlign: {type: String},\n\n /**\n * If true, it will use `horizontalAlign` and `verticalAlign` values as\n * preferred alignment and if there's not enough space, it will pick the\n * values which minimize the cropping.\n */\n dynamicAlign: {type: Boolean},\n\n /**\n * A pixel value that will be added to the position calculated for the\n * given `horizontalAlign`, in the direction of alignment. You can think\n * of it as increasing or decreasing the distance to the side of the\n * screen given by `horizontalAlign`.\n *\n * If `horizontalAlign` is \"left\" or \"center\", this offset will increase or\n * decrease the distance to the left side of the screen: a negative offset\n * will move the dropdown to the left; a positive one, to the right.\n *\n * Conversely if `horizontalAlign` is \"right\", this offset will increase\n * or decrease the distance to the right side of the screen: a negative\n * offset will move the dropdown to the right; a positive one, to the left.\n */\n horizontalOffset: {type: Number, value: 0, notify: true},\n\n /**\n * A pixel value that will be added to the position calculated for the\n * given `verticalAlign`, in the direction of alignment. You can think\n * of it as increasing or decreasing the distance to the side of the\n * screen given by `verticalAlign`.\n *\n * If `verticalAlign` is \"top\" or \"middle\", this offset will increase or\n * decrease the distance to the top side of the screen: a negative offset\n * will move the dropdown upwards; a positive one, downwards.\n *\n * Conversely if `verticalAlign` is \"bottom\", this offset will increase\n * or decrease the distance to the bottom side of the screen: a negative\n * offset will move the dropdown downwards; a positive one, upwards.\n */\n verticalOffset: {type: Number, value: 0, notify: true},\n\n /**\n * Set to true to auto-fit on attach.\n */\n autoFitOnAttach: {type: Boolean, value: false},\n\n /** @type {?Object} */\n _fitInfo: {type: Object}\n },\n\n get _fitWidth() {\n var fitWidth;\n if (this.fitInto === window) {\n fitWidth = this.fitInto.innerWidth;\n } else {\n fitWidth = this.fitInto.getBoundingClientRect().width;\n }\n return fitWidth;\n },\n\n get _fitHeight() {\n var fitHeight;\n if (this.fitInto === window) {\n fitHeight = this.fitInto.innerHeight;\n } else {\n fitHeight = this.fitInto.getBoundingClientRect().height;\n }\n return fitHeight;\n },\n\n get _fitLeft() {\n var fitLeft;\n if (this.fitInto === window) {\n fitLeft = 0;\n } else {\n fitLeft = this.fitInto.getBoundingClientRect().left;\n }\n return fitLeft;\n },\n\n get _fitTop() {\n var fitTop;\n if (this.fitInto === window) {\n fitTop = 0;\n } else {\n fitTop = this.fitInto.getBoundingClientRect().top;\n }\n return fitTop;\n },\n\n /**\n * The element that should be used to position the element,\n * if no position target is configured.\n */\n get _defaultPositionTarget() {\n var parent = dom(this).parentNode;\n\n if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n parent = parent.host;\n }\n\n return parent;\n },\n\n /**\n * The horizontal align value, accounting for the RTL/LTR text direction.\n */\n get _localeHorizontalAlign() {\n if (this._isRTL) {\n // In RTL, \"left\" becomes \"right\".\n if (this.horizontalAlign === 'right') {\n return 'left';\n }\n if (this.horizontalAlign === 'left') {\n return 'right';\n }\n }\n return this.horizontalAlign;\n },\n\n /**\n * True if the element should be positioned instead of centered.\n * @private\n */\n get __shouldPosition() {\n return (this.horizontalAlign || this.verticalAlign) && this.positionTarget;\n },\n\n attached: function() {\n // Memoize this to avoid expensive calculations & relayouts.\n // Make sure we do it only once\n if (typeof this._isRTL === 'undefined') {\n this._isRTL = window.getComputedStyle(this).direction == 'rtl';\n }\n this.positionTarget = this.positionTarget || this._defaultPositionTarget;\n if (this.autoFitOnAttach) {\n if (window.getComputedStyle(this).display === 'none') {\n setTimeout(function() {\n this.fit();\n }.bind(this));\n } else {\n // NOTE: shadydom applies distribution asynchronously\n // for performance reasons webcomponents/shadydom#120\n // Flush to get correct layout info.\n window.ShadyDOM && ShadyDOM.flush();\n this.fit();\n }\n }\n },\n\n detached: function() {\n if (this.__deferredFit) {\n clearTimeout(this.__deferredFit);\n this.__deferredFit = null;\n }\n },\n\n /**\n * Positions and fits the element into the `fitInto` element.\n */\n fit: function() {\n this.position();\n this.constrain();\n this.center();\n },\n\n /**\n * Memoize information needed to position and size the target element.\n * @suppress {deprecated}\n */\n _discoverInfo: function() {\n if (this._fitInfo) {\n return;\n }\n var target = window.getComputedStyle(this);\n var sizer = window.getComputedStyle(this.sizingTarget);\n\n this._fitInfo = {\n inlineStyle: {\n top: this.style.top || '',\n left: this.style.left || '',\n position: this.style.position || ''\n },\n sizerInlineStyle: {\n maxWidth: this.sizingTarget.style.maxWidth || '',\n maxHeight: this.sizingTarget.style.maxHeight || '',\n boxSizing: this.sizingTarget.style.boxSizing || ''\n },\n positionedBy: {\n vertically: target.top !== 'auto' ?\n 'top' :\n (target.bottom !== 'auto' ? 'bottom' : null),\n horizontally: target.left !== 'auto' ?\n 'left' :\n (target.right !== 'auto' ? 'right' : null)\n },\n sizedBy: {\n height: sizer.maxHeight !== 'none',\n width: sizer.maxWidth !== 'none',\n minWidth: parseInt(sizer.minWidth, 10) || 0,\n minHeight: parseInt(sizer.minHeight, 10) || 0\n },\n margin: {\n top: parseInt(target.marginTop, 10) || 0,\n right: parseInt(target.marginRight, 10) || 0,\n bottom: parseInt(target.marginBottom, 10) || 0,\n left: parseInt(target.marginLeft, 10) || 0\n }\n };\n },\n\n /**\n * Resets the target element's position and size constraints, and clear\n * the memoized data.\n */\n resetFit: function() {\n var info = this._fitInfo || {};\n for (var property in info.sizerInlineStyle) {\n this.sizingTarget.style[property] = info.sizerInlineStyle[property];\n }\n for (var property in info.inlineStyle) {\n this.style[property] = info.inlineStyle[property];\n }\n\n this._fitInfo = null;\n },\n\n /**\n * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after\n * the element or the `fitInto` element has been resized, or if any of the\n * positioning properties (e.g. `horizontalAlign, verticalAlign`) is updated.\n * It preserves the scroll position of the sizingTarget.\n */\n refit: function() {\n var scrollLeft = this.sizingTarget.scrollLeft;\n var scrollTop = this.sizingTarget.scrollTop;\n this.resetFit();\n this.fit();\n this.sizingTarget.scrollLeft = scrollLeft;\n this.sizingTarget.scrollTop = scrollTop;\n },\n\n /**\n * Positions the element according to `horizontalAlign, verticalAlign`.\n */\n position: function() {\n if (!this.__shouldPosition) {\n // needs to be centered, and it is done after constrain.\n return;\n }\n this._discoverInfo();\n\n this.style.position = 'fixed';\n // Need border-box for margin/padding.\n this.sizingTarget.style.boxSizing = 'border-box';\n // Set to 0, 0 in order to discover any offset caused by parent stacking\n // contexts.\n this.style.left = '0px';\n this.style.top = '0px';\n\n var rect = this.getBoundingClientRect();\n var positionRect = this.__getNormalizedRect(this.positionTarget);\n var fitRect = this.__getNormalizedRect(this.fitInto);\n\n var margin = this._fitInfo.margin;\n\n // Consider the margin as part of the size for position calculations.\n var size = {\n width: rect.width + margin.left + margin.right,\n height: rect.height + margin.top + margin.bottom\n };\n\n var position = this.__getPosition(\n this._localeHorizontalAlign,\n this.verticalAlign,\n size,\n rect,\n positionRect,\n fitRect);\n\n var left = position.left + margin.left;\n var top = position.top + margin.top;\n\n // We first limit right/bottom within fitInto respecting the margin,\n // then use those values to limit top/left.\n var right = Math.min(fitRect.right - margin.right, left + rect.width);\n var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height);\n\n // Keep left/top within fitInto respecting the margin.\n left = Math.max(\n fitRect.left + margin.left,\n Math.min(left, right - this._fitInfo.sizedBy.minWidth));\n top = Math.max(\n fitRect.top + margin.top,\n Math.min(top, bottom - this._fitInfo.sizedBy.minHeight));\n\n // Use right/bottom to set maxWidth/maxHeight, and respect\n // minWidth/minHeight.\n this.sizingTarget.style.maxWidth =\n Math.max(right - left, this._fitInfo.sizedBy.minWidth) + 'px';\n this.sizingTarget.style.maxHeight =\n Math.max(bottom - top, this._fitInfo.sizedBy.minHeight) + 'px';\n\n // Remove the offset caused by any stacking context.\n this.style.left = (left - rect.left) + 'px';\n this.style.top = (top - rect.top) + 'px';\n },\n\n /**\n * Constrains the size of the element to `fitInto` by setting `max-height`\n * and/or `max-width`.\n */\n constrain: function() {\n if (this.__shouldPosition) {\n return;\n }\n this._discoverInfo();\n\n var info = this._fitInfo;\n // position at (0px, 0px) if not already positioned, so we can measure the\n // natural size.\n if (!info.positionedBy.vertically) {\n this.style.position = 'fixed';\n this.style.top = '0px';\n }\n if (!info.positionedBy.horizontally) {\n this.style.position = 'fixed';\n this.style.left = '0px';\n }\n\n // need border-box for margin/padding\n this.sizingTarget.style.boxSizing = 'border-box';\n // constrain the width and height if not already set\n var rect = this.getBoundingClientRect();\n if (!info.sizedBy.height) {\n this.__sizeDimension(\n rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');\n }\n if (!info.sizedBy.width) {\n this.__sizeDimension(\n rect, info.positionedBy.horizontally, 'left', 'right', 'Width');\n }\n },\n\n /**\n * @protected\n * @deprecated\n */\n _sizeDimension: function(rect, positionedBy, start, end, extent) {\n this.__sizeDimension(rect, positionedBy, start, end, extent);\n },\n\n /**\n * @private\n */\n __sizeDimension: function(rect, positionedBy, start, end, extent) {\n var info = this._fitInfo;\n var fitRect = this.__getNormalizedRect(this.fitInto);\n var max = extent === 'Width' ? fitRect.width : fitRect.height;\n var flip = (positionedBy === end);\n var offset = flip ? max - rect[end] : rect[start];\n var margin = info.margin[flip ? start : end];\n var offsetExtent = 'offset' + extent;\n var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent];\n this.sizingTarget.style['max' + extent] =\n (max - margin - offset - sizingOffset) + 'px';\n },\n\n /**\n * Centers horizontally and vertically if not already positioned. This also\n * sets `position:fixed`.\n */\n center: function() {\n if (this.__shouldPosition) {\n return;\n }\n this._discoverInfo();\n\n var positionedBy = this._fitInfo.positionedBy;\n if (positionedBy.vertically && positionedBy.horizontally) {\n // Already positioned.\n return;\n }\n // Need position:fixed to center\n this.style.position = 'fixed';\n // Take into account the offset caused by parents that create stacking\n // contexts (e.g. with transform: translate3d). Translate to 0,0 and\n // measure the bounding rect.\n if (!positionedBy.vertically) {\n this.style.top = '0px';\n }\n if (!positionedBy.horizontally) {\n this.style.left = '0px';\n }\n // It will take in consideration margins and transforms\n var rect = this.getBoundingClientRect();\n var fitRect = this.__getNormalizedRect(this.fitInto);\n if (!positionedBy.vertically) {\n var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;\n this.style.top = top + 'px';\n }\n if (!positionedBy.horizontally) {\n var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;\n this.style.left = left + 'px';\n }\n },\n\n __getNormalizedRect: function(target) {\n if (target === document.documentElement || target === window) {\n return {\n top: 0,\n left: 0,\n width: window.innerWidth,\n height: window.innerHeight,\n right: window.innerWidth,\n bottom: window.innerHeight\n };\n }\n return target.getBoundingClientRect();\n },\n\n __getOffscreenArea: function(position, size, fitRect) {\n var verticalCrop = Math.min(0, position.top) +\n Math.min(0, fitRect.bottom - (position.top + size.height));\n var horizontalCrop = Math.min(0, position.left) +\n Math.min(0, fitRect.right - (position.left + size.width));\n return Math.abs(verticalCrop) * size.width +\n Math.abs(horizontalCrop) * size.height;\n },\n\n\n __getPosition: function(\n hAlign, vAlign, size, sizeNoMargins, positionRect, fitRect) {\n // All the possible configurations.\n // Ordered as top-left, top-right, bottom-left, bottom-right.\n var positions = [\n {\n verticalAlign: 'top',\n horizontalAlign: 'left',\n top: positionRect.top + this.verticalOffset,\n left: positionRect.left + this.horizontalOffset\n },\n {\n verticalAlign: 'top',\n horizontalAlign: 'right',\n top: positionRect.top + this.verticalOffset,\n left: positionRect.right - size.width - this.horizontalOffset\n },\n {\n verticalAlign: 'bottom',\n horizontalAlign: 'left',\n top: positionRect.bottom - size.height - this.verticalOffset,\n left: positionRect.left + this.horizontalOffset\n },\n {\n verticalAlign: 'bottom',\n horizontalAlign: 'right',\n top: positionRect.bottom - size.height - this.verticalOffset,\n left: positionRect.right - size.width - this.horizontalOffset\n }\n ];\n\n if (this.noOverlap) {\n // Duplicate.\n for (var i = 0, l = positions.length; i < l; i++) {\n var copy = {};\n for (var key in positions[i]) {\n copy[key] = positions[i][key];\n }\n positions.push(copy);\n }\n // Horizontal overlap only.\n positions[0].top = positions[1].top += positionRect.height;\n positions[2].top = positions[3].top -= positionRect.height;\n // Vertical overlap only.\n positions[4].left = positions[6].left += positionRect.width;\n positions[5].left = positions[7].left -= positionRect.width;\n }\n\n // Consider auto as null for coding convenience.\n vAlign = vAlign === 'auto' ? null : vAlign;\n hAlign = hAlign === 'auto' ? null : hAlign;\n\n if (!hAlign || hAlign === 'center') {\n positions.push({\n verticalAlign: 'top',\n horizontalAlign: 'center',\n top: positionRect.top + this.verticalOffset +\n (this.noOverlap ? positionRect.height : 0),\n left: positionRect.left - sizeNoMargins.width / 2 +\n positionRect.width / 2 + this.horizontalOffset\n });\n positions.push({\n verticalAlign: 'bottom',\n horizontalAlign: 'center',\n top: positionRect.bottom - size.height - this.verticalOffset -\n (this.noOverlap ? positionRect.height : 0),\n left: positionRect.left - sizeNoMargins.width / 2 +\n positionRect.width / 2 + this.horizontalOffset\n });\n }\n\n if (!vAlign || vAlign === 'middle') {\n positions.push({\n verticalAlign: 'middle',\n horizontalAlign: 'left',\n top: positionRect.top - sizeNoMargins.height / 2 +\n positionRect.height / 2 + this.verticalOffset,\n left: positionRect.left + this.horizontalOffset +\n (this.noOverlap ? positionRect.width : 0)\n });\n positions.push({\n verticalAlign: 'middle',\n horizontalAlign: 'right',\n top: positionRect.top - sizeNoMargins.height / 2 +\n positionRect.height / 2 + this.verticalOffset,\n left: positionRect.right - size.width - this.horizontalOffset -\n (this.noOverlap ? positionRect.width : 0)\n });\n }\n\n if (vAlign === 'middle' && hAlign === 'center') {\n positions.push({\n verticalAlign: 'middle',\n horizontalAlign: 'center',\n top: positionRect.top - sizeNoMargins.height / 2 +\n positionRect.height / 2 + this.verticalOffset,\n left: positionRect.left - sizeNoMargins.width / 2 +\n positionRect.width / 2 + this.horizontalOffset\n });\n }\n\n var position;\n for (var i = 0; i < positions.length; i++) {\n var candidate = positions[i];\n var vAlignOk = candidate.verticalAlign === vAlign;\n var hAlignOk = candidate.horizontalAlign === hAlign;\n\n // If both vAlign and hAlign are defined, return exact match.\n // For dynamicAlign and noOverlap we'll have more than one candidate, so\n // we'll have to check the offscreenArea to make the best choice.\n if (!this.dynamicAlign && !this.noOverlap && vAlignOk && hAlignOk) {\n position = candidate;\n break;\n }\n\n // Align is ok if alignment preferences are respected. If no preferences,\n // it is considered ok.\n var alignOk = (!vAlign || vAlignOk) && (!hAlign || hAlignOk);\n\n // Filter out elements that don't match the alignment (if defined).\n // With dynamicAlign, we need to consider all the positions to find the\n // one that minimizes the cropped area.\n if (!this.dynamicAlign && !alignOk) {\n continue;\n }\n\n candidate.offscreenArea =\n this.__getOffscreenArea(candidate, size, fitRect);\n // If not cropped and respects the align requirements, keep it.\n // This allows to prefer positions overlapping horizontally over the\n // ones overlapping vertically.\n if (candidate.offscreenArea === 0 && alignOk) {\n position = candidate;\n break;\n }\n position = position || candidate;\n var diff = candidate.offscreenArea - position.offscreenArea;\n // Check which crops less. If it crops equally, check if at least one\n // align setting is ok.\n if (diff < 0 || (diff === 0 && (vAlignOk || hAlignOk))) {\n position = candidate;\n }\n }\n\n return position;\n }\n\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/*\n`iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It\nshould be a singleton.\n\n### Styling\n\nThe following custom properties and mixins are available for styling.\n\nCustom property | Description | Default\n-------------------------------------------|------------------------|---------\n`--iron-overlay-backdrop-background-color` | Backdrop background color | #000\n`--iron-overlay-backdrop-opacity` | Backdrop opacity | 0.6\n`--iron-overlay-backdrop` | Mixin applied to `iron-overlay-backdrop`. | {}\n`--iron-overlay-backdrop-opened` | Mixin applied to `iron-overlay-backdrop` when it is displayed | {}\n*/\nPolymer({\n _template: html`\n \n\n
\n`,\n\n is: 'iron-overlay-backdrop',\n\n properties: {\n\n /**\n * Returns true if the backdrop is opened.\n */\n opened: {\n reflectToAttribute: true,\n type: Boolean,\n value: false,\n observer: '_openedChanged',\n }\n\n },\n\n listeners: {\n 'transitionend': '_onTransitionend',\n },\n\n created: function() {\n // Used to cancel previous requestAnimationFrame calls when opened changes.\n this.__openedRaf = null;\n },\n\n attached: function() {\n this.opened && this._openedChanged(this.opened);\n },\n\n /**\n * Appends the backdrop to document body if needed.\n */\n prepare: function() {\n if (this.opened && !this.parentNode) {\n dom(document.body).appendChild(this);\n }\n },\n\n /**\n * Shows the backdrop.\n */\n open: function() {\n this.opened = true;\n },\n\n /**\n * Hides the backdrop.\n */\n close: function() {\n this.opened = false;\n },\n\n /**\n * Removes the backdrop from document body if needed.\n */\n complete: function() {\n if (!this.opened && this.parentNode === document.body) {\n dom(this.parentNode).removeChild(this);\n }\n },\n\n _onTransitionend: function(event) {\n if (event && event.target === this) {\n this.complete();\n }\n },\n\n /**\n * @param {boolean} opened\n * @private\n */\n _openedChanged: function(opened) {\n if (opened) {\n // Auto-attach.\n this.prepare();\n } else {\n // Animation might be disabled via the mixin or opacity custom property.\n // If it is disabled in other ways, it's up to the user to call complete.\n var cs = window.getComputedStyle(this);\n if (cs.transitionDuration === '0s' || cs.opacity == 0) {\n this.complete();\n }\n }\n\n if (!this.isAttached) {\n return;\n }\n\n // Always cancel previous requestAnimationFrame.\n if (this.__openedRaf) {\n window.cancelAnimationFrame(this.__openedRaf);\n this.__openedRaf = null;\n }\n // Force relayout to ensure proper transitions.\n this.scrollTop = this.scrollTop;\n this.__openedRaf = window.requestAnimationFrame(function() {\n this.__openedRaf = null;\n this.toggleClass('opened', this.opened);\n }.bind(this));\n }\n});\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport './iron-overlay-backdrop.js';\n\nimport {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport * as gestures from '@polymer/polymer/lib/utils/gestures.js';\n\n/**\n * @struct\n * @constructor\n * @private\n */\nexport const IronOverlayManagerClass = function() {\n /**\n * Used to keep track of the opened overlays.\n * @private {!Array}\n */\n this._overlays = [];\n\n /**\n * iframes have a default z-index of 100,\n * so this default should be at least that.\n * @private {number}\n */\n this._minimumZ = 101;\n\n /**\n * Memoized backdrop element.\n * @private {Element|null}\n */\n this._backdropElement = null;\n\n // Enable document-wide tap recognizer.\n // NOTE: Use useCapture=true to avoid accidentally prevention of the closing\n // of an overlay via event.stopPropagation(). The only way to prevent\n // closing of an overlay should be through its APIs.\n // NOTE: enable tap on to workaround Polymer/polymer#4459\n // Pass no-op function because MSEdge 15 doesn't handle null as 2nd argument\n // https://github.com/Microsoft/ChakraCore/issues/3863\n gestures.add(document.documentElement, 'tap', function() {});\n document.addEventListener('tap', this._onCaptureClick.bind(this), true);\n document.addEventListener('focus', this._onCaptureFocus.bind(this), true);\n document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);\n};\n\nIronOverlayManagerClass.prototype = {\n\n constructor: IronOverlayManagerClass,\n\n /**\n * The shared backdrop element.\n * @return {!Element} backdropElement\n */\n get backdropElement() {\n if (!this._backdropElement) {\n this._backdropElement = document.createElement('iron-overlay-backdrop');\n }\n return this._backdropElement;\n },\n\n /**\n * The deepest active element.\n * @return {!Element} activeElement the active element\n */\n get deepActiveElement() {\n var active = document.activeElement;\n // document.activeElement can be null\n // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement\n // In IE 11, it can also be an object when operating in iframes.\n // In these cases, default it to document.body.\n if (!active || active instanceof Element === false) {\n active = document.body;\n }\n while (active.root && dom(active.root).activeElement) {\n active = dom(active.root).activeElement;\n }\n return active;\n },\n\n /**\n * Brings the overlay at the specified index to the front.\n * @param {number} i\n * @private\n */\n _bringOverlayAtIndexToFront: function(i) {\n var overlay = this._overlays[i];\n if (!overlay) {\n return;\n }\n var lastI = this._overlays.length - 1;\n var currentOverlay = this._overlays[lastI];\n // Ensure always-on-top overlay stays on top.\n if (currentOverlay &&\n this._shouldBeBehindOverlay(overlay, currentOverlay)) {\n lastI--;\n }\n // If already the top element, return.\n if (i >= lastI) {\n return;\n }\n // Update z-index to be on top.\n var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);\n if (this._getZ(overlay) <= minimumZ) {\n this._applyOverlayZ(overlay, minimumZ);\n }\n\n // Shift other overlays behind the new on top.\n while (i < lastI) {\n this._overlays[i] = this._overlays[i + 1];\n i++;\n }\n this._overlays[lastI] = overlay;\n },\n\n /**\n * Adds the overlay and updates its z-index if it's opened, or removes it if\n * it's closed. Also updates the backdrop z-index.\n * @param {!Element} overlay\n */\n addOrRemoveOverlay: function(overlay) {\n if (overlay.opened) {\n this.addOverlay(overlay);\n } else {\n this.removeOverlay(overlay);\n }\n },\n\n /**\n * Tracks overlays for z-index and focus management.\n * Ensures the last added overlay with always-on-top remains on top.\n * @param {!Element} overlay\n */\n addOverlay: function(overlay) {\n var i = this._overlays.indexOf(overlay);\n if (i >= 0) {\n this._bringOverlayAtIndexToFront(i);\n this.trackBackdrop();\n return;\n }\n var insertionIndex = this._overlays.length;\n var currentOverlay = this._overlays[insertionIndex - 1];\n var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);\n var newZ = this._getZ(overlay);\n\n // Ensure always-on-top overlay stays on top.\n if (currentOverlay &&\n this._shouldBeBehindOverlay(overlay, currentOverlay)) {\n // This bumps the z-index of +2.\n this._applyOverlayZ(currentOverlay, minimumZ);\n insertionIndex--;\n // Update minimumZ to match previous overlay's z-index.\n var previousOverlay = this._overlays[insertionIndex - 1];\n minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);\n }\n\n // Update z-index and insert overlay.\n if (newZ <= minimumZ) {\n this._applyOverlayZ(overlay, minimumZ);\n }\n this._overlays.splice(insertionIndex, 0, overlay);\n\n this.trackBackdrop();\n },\n\n /**\n * @param {!Element} overlay\n */\n removeOverlay: function(overlay) {\n var i = this._overlays.indexOf(overlay);\n if (i === -1) {\n return;\n }\n this._overlays.splice(i, 1);\n\n this.trackBackdrop();\n },\n\n /**\n * Returns the current overlay.\n * @return {!Element|undefined}\n */\n currentOverlay: function() {\n var i = this._overlays.length - 1;\n return this._overlays[i];\n },\n\n /**\n * Returns the current overlay z-index.\n * @return {number}\n */\n currentOverlayZ: function() {\n return this._getZ(this.currentOverlay());\n },\n\n /**\n * Ensures that the minimum z-index of new overlays is at least `minimumZ`.\n * This does not effect the z-index of any existing overlays.\n * @param {number} minimumZ\n */\n ensureMinimumZ: function(minimumZ) {\n this._minimumZ = Math.max(this._minimumZ, minimumZ);\n },\n\n focusOverlay: function() {\n var current = /** @type {?} */ (this.currentOverlay());\n if (current) {\n current._applyFocus();\n }\n },\n\n /**\n * Updates the backdrop z-index.\n */\n trackBackdrop: function() {\n var overlay = this._overlayWithBackdrop();\n // Avoid creating the backdrop if there is no overlay with backdrop.\n if (!overlay && !this._backdropElement) {\n return;\n }\n this.backdropElement.style.zIndex = this._getZ(overlay) - 1;\n this.backdropElement.opened = !!overlay;\n // Property observers are not fired until element is attached\n // in Polymer 2.x, so we ensure element is attached if needed.\n // https://github.com/Polymer/polymer/issues/4526\n this.backdropElement.prepare();\n },\n\n /**\n * @return {!Array}\n */\n getBackdrops: function() {\n var backdrops = [];\n for (var i = 0; i < this._overlays.length; i++) {\n if (this._overlays[i].withBackdrop) {\n backdrops.push(this._overlays[i]);\n }\n }\n return backdrops;\n },\n\n /**\n * Returns the z-index for the backdrop.\n * @return {number}\n */\n backdropZ: function() {\n return this._getZ(this._overlayWithBackdrop()) - 1;\n },\n\n /**\n * Returns the top opened overlay that has a backdrop.\n * @return {!Element|undefined}\n * @private\n */\n _overlayWithBackdrop: function() {\n for (var i = this._overlays.length - 1; i >= 0; i--) {\n if (this._overlays[i].withBackdrop) {\n return this._overlays[i];\n }\n }\n },\n\n /**\n * Calculates the minimum z-index for the overlay.\n * @param {Element=} overlay\n * @private\n */\n _getZ: function(overlay) {\n var z = this._minimumZ;\n if (overlay) {\n var z1 = Number(\n overlay.style.zIndex || window.getComputedStyle(overlay).zIndex);\n // Check if is a number\n // Number.isNaN not supported in IE 10+\n if (z1 === z1) {\n z = z1;\n }\n }\n return z;\n },\n\n /**\n * @param {!Element} element\n * @param {number|string} z\n * @private\n */\n _setZ: function(element, z) {\n element.style.zIndex = z;\n },\n\n /**\n * @param {!Element} overlay\n * @param {number} aboveZ\n * @private\n */\n _applyOverlayZ: function(overlay, aboveZ) {\n this._setZ(overlay, aboveZ + 2);\n },\n\n /**\n * Returns the deepest overlay in the path.\n * @param {!Array=} path\n * @return {!Element|undefined}\n * @suppress {missingProperties}\n * @private\n */\n _overlayInPath: function(path) {\n path = path || [];\n for (var i = 0; i < path.length; i++) {\n if (path[i]._manager === this) {\n return path[i];\n }\n }\n },\n\n /**\n * Ensures the click event is delegated to the right overlay.\n * @param {!Event} event\n * @private\n */\n _onCaptureClick: function(event) {\n var i = this._overlays.length - 1;\n if (i === -1)\n return;\n var path = /** @type {!Array} */ (dom(event).path);\n var overlay;\n // Check if clicked outside of overlay.\n while ((overlay = /** @type {?} */ (this._overlays[i])) &&\n this._overlayInPath(path) !== overlay) {\n overlay._onCaptureClick(event);\n if (overlay.allowClickThrough) {\n i--;\n } else {\n break;\n }\n }\n },\n\n /**\n * Ensures the focus event is delegated to the right overlay.\n * @param {!Event} event\n * @private\n */\n _onCaptureFocus: function(event) {\n var overlay = /** @type {?} */ (this.currentOverlay());\n if (overlay) {\n overlay._onCaptureFocus(event);\n }\n },\n\n /**\n * Ensures TAB and ESC keyboard events are delegated to the right overlay.\n * @param {!Event} event\n * @private\n */\n _onCaptureKeyDown: function(event) {\n var overlay = /** @type {?} */ (this.currentOverlay());\n if (overlay) {\n if (IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {\n overlay._onCaptureEsc(event);\n } else if (IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) {\n overlay._onCaptureTab(event);\n }\n }\n },\n\n /**\n * Returns if the overlay1 should be behind overlay2.\n * @param {!Element} overlay1\n * @param {!Element} overlay2\n * @return {boolean}\n * @suppress {missingProperties}\n * @private\n */\n _shouldBeBehindOverlay: function(overlay1, overlay2) {\n return !overlay1.alwaysOnTop && overlay2.alwaysOnTop;\n }\n};\n\nexport const IronOverlayManager = new IronOverlayManagerClass();\n","/**\n@license\nCopyright (c) 2017 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\n/**\n * Used to calculate the scroll direction during touch events.\n * @type {!Object}\n */\nvar lastTouchPosition = {pageX: 0, pageY: 0};\n/**\n * Used to avoid computing event.path and filter scrollable nodes (better perf).\n * @type {?EventTarget}\n */\nvar lastRootTarget = null;\n/**\n * @type {!Array}\n */\nvar lastScrollableNodes = [];\n/**\n * @type {!Array
}\n */\nvar scrollEvents = [\n // Modern `wheel` event for mouse wheel scrolling:\n 'wheel',\n // Older, non-standard `mousewheel` event for some FF:\n 'mousewheel',\n // IE:\n 'DOMMouseScroll',\n // Touch enabled devices\n 'touchstart',\n 'touchmove'\n];\n// must be defined for modulizer\nvar _boundScrollHandler;\nvar currentLockingElement;\n\n/**\n * The IronScrollManager is intended to provide a central source\n * of authority and control over which elements in a document are currently\n * allowed to scroll.\n *\n */\n`TODO(modulizer): A namespace named Polymer.IronScrollManager was\ndeclared here. The surrounding comments should be reviewed,\nand this string can then be deleted`;\n\n/**\n * The current element that defines the DOM boundaries of the\n * scroll lock. This is always the most recently locking element.\n *\n * @return {!Node|undefined}\n */\nexport {currentLockingElement};\n\n/**\n * Returns true if the provided element is \"scroll locked\", which is to\n * say that it cannot be scrolled via pointer or keyboard interactions.\n *\n * @param {!HTMLElement} element An HTML element instance which may or may\n * not be scroll locked.\n */\nexport function elementIsScrollLocked(element) {\n var lockingElement = currentLockingElement;\n\n if (lockingElement === undefined) {\n return false;\n }\n\n var scrollLocked;\n\n if (_hasCachedLockedElement(element)) {\n return true;\n }\n\n if (_hasCachedUnlockedElement(element)) {\n return false;\n }\n\n scrollLocked = !!lockingElement && lockingElement !== element &&\n !_composedTreeContains(lockingElement, element);\n\n if (scrollLocked) {\n _lockedElementCache.push(element);\n } else {\n _unlockedElementCache.push(element);\n }\n\n return scrollLocked;\n}\n\n/**\n * Push an element onto the current scroll lock stack. The most recently\n * pushed element and its children will be considered scrollable. All\n * other elements will not be scrollable.\n *\n * Scroll locking is implemented as a stack so that cases such as\n * dropdowns within dropdowns are handled well.\n *\n * @param {!HTMLElement} element The element that should lock scroll.\n */\nexport function pushScrollLock(element) {\n // Prevent pushing the same element twice\n if (_lockingElements.indexOf(element) >= 0) {\n return;\n }\n\n if (_lockingElements.length === 0) {\n _lockScrollInteractions();\n }\n\n _lockingElements.push(element);\n currentLockingElement = _lockingElements[_lockingElements.length - 1];\n\n _lockedElementCache = [];\n _unlockedElementCache = [];\n}\n\n/**\n * Remove an element from the scroll lock stack. The element being\n * removed does not need to be the most recently pushed element. However,\n * the scroll lock constraints only change when the most recently pushed\n * element is removed.\n *\n * @param {!HTMLElement} element The element to remove from the scroll\n * lock stack.\n */\nexport function removeScrollLock(element) {\n var index = _lockingElements.indexOf(element);\n\n if (index === -1) {\n return;\n }\n\n _lockingElements.splice(index, 1);\n currentLockingElement = _lockingElements[_lockingElements.length - 1];\n\n _lockedElementCache = [];\n _unlockedElementCache = [];\n\n if (_lockingElements.length === 0) {\n _unlockScrollInteractions();\n }\n}\n\nexport const _lockingElements = [];\nexport let _lockedElementCache = null;\nexport let _unlockedElementCache = null;\n\nexport function _hasCachedLockedElement(element) {\n return _lockedElementCache.indexOf(element) > -1;\n}\n\nexport function _hasCachedUnlockedElement(element) {\n return _unlockedElementCache.indexOf(element) > -1;\n}\n\nexport function _composedTreeContains(element, child) {\n // NOTE(cdata): This method iterates over content elements and their\n // corresponding distributed nodes to implement a contains-like method\n // that pierces through the composed tree of the ShadowDOM. Results of\n // this operation are cached (elsewhere) on a per-scroll-lock basis, to\n // guard against potentially expensive lookups happening repeatedly as\n // a user scrolls / touchmoves.\n var contentElements;\n var distributedNodes;\n var contentIndex;\n var nodeIndex;\n\n if (element.contains(child)) {\n return true;\n }\n\n contentElements = dom(element).querySelectorAll('content,slot');\n\n for (contentIndex = 0; contentIndex < contentElements.length;\n ++contentIndex) {\n distributedNodes = dom(contentElements[contentIndex]).getDistributedNodes();\n\n for (nodeIndex = 0; nodeIndex < distributedNodes.length; ++nodeIndex) {\n // Polymer 2.x returns slot.assignedNodes which can contain text nodes.\n if (distributedNodes[nodeIndex].nodeType !== Node.ELEMENT_NODE)\n continue;\n\n if (_composedTreeContains(distributedNodes[nodeIndex], child)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport function _scrollInteractionHandler(event) {\n // Avoid canceling an event with cancelable=false, e.g. scrolling is in\n // progress and cannot be interrupted.\n if (event.cancelable && _shouldPreventScrolling(event)) {\n event.preventDefault();\n }\n // If event has targetTouches (touch event), update last touch position.\n if (event.targetTouches) {\n var touch = event.targetTouches[0];\n lastTouchPosition.pageX = touch.pageX;\n lastTouchPosition.pageY = touch.pageY;\n }\n}\n\n/**\n * @private\n */\nexport {_boundScrollHandler};\n\nexport function _lockScrollInteractions() {\n _boundScrollHandler =\n _boundScrollHandler || _scrollInteractionHandler.bind(undefined);\n for (var i = 0, l = scrollEvents.length; i < l; i++) {\n // NOTE: browsers that don't support objects as third arg will\n // interpret it as boolean, hence useCapture = true in this case.\n document.addEventListener(\n scrollEvents[i], _boundScrollHandler, {capture: true, passive: false});\n }\n}\n\nexport function _unlockScrollInteractions() {\n for (var i = 0, l = scrollEvents.length; i < l; i++) {\n // NOTE: browsers that don't support objects as third arg will\n // interpret it as boolean, hence useCapture = true in this case.\n document.removeEventListener(\n scrollEvents[i], _boundScrollHandler, {capture: true, passive: false});\n }\n}\n\n/**\n * Returns true if the event causes scroll outside the current locking\n * element, e.g. pointer/keyboard interactions, or scroll \"leaking\"\n * outside the locking element when it is already at its scroll boundaries.\n * @param {!Event} event\n * @return {boolean}\n * @private\n */\nexport function _shouldPreventScrolling(event) {\n // Update if root target changed. For touch events, ensure we don't\n // update during touchmove.\n var target = dom(event).rootTarget;\n if (event.type !== 'touchmove' && lastRootTarget !== target) {\n lastRootTarget = target;\n lastScrollableNodes = _getScrollableNodes(dom(event).path);\n }\n\n // Prevent event if no scrollable nodes.\n if (!lastScrollableNodes.length) {\n return true;\n }\n // Don't prevent touchstart event inside the locking element when it has\n // scrollable nodes.\n if (event.type === 'touchstart') {\n return false;\n }\n // Get deltaX/Y.\n var info = _getScrollInfo(event);\n // Prevent if there is no child that can scroll.\n return !_getScrollingNode(lastScrollableNodes, info.deltaX, info.deltaY);\n}\n\n/**\n * Returns an array of scrollable nodes up to the current locking element,\n * which is included too if scrollable.\n * @param {!Array} nodes\n * @return {!Array} scrollables\n * @private\n */\nexport function _getScrollableNodes(nodes) {\n var scrollables = [];\n var lockingIndex = nodes.indexOf(currentLockingElement);\n // Loop from root target to locking element (included).\n for (var i = 0; i <= lockingIndex; i++) {\n // Skip non-Element nodes.\n if (nodes[i].nodeType !== Node.ELEMENT_NODE) {\n continue;\n }\n var node = /** @type {!Element} */ (nodes[i]);\n // Check inline style before checking computed style.\n var style = node.style;\n if (style.overflow !== 'scroll' && style.overflow !== 'auto') {\n style = window.getComputedStyle(node);\n }\n if (style.overflow === 'scroll' || style.overflow === 'auto') {\n scrollables.push(node);\n }\n }\n return scrollables;\n}\n\n/**\n * Returns the node that is scrolling. If there is no scrolling,\n * returns undefined.\n * @param {!Array} nodes\n * @param {number} deltaX Scroll delta on the x-axis\n * @param {number} deltaY Scroll delta on the y-axis\n * @return {!Node|undefined}\n * @private\n */\nexport function _getScrollingNode(nodes, deltaX, deltaY) {\n // No scroll.\n if (!deltaX && !deltaY) {\n return;\n }\n // Check only one axis according to where there is more scroll.\n // Prefer vertical to horizontal.\n var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX);\n for (var i = 0; i < nodes.length; i++) {\n var node = nodes[i];\n var canScroll = false;\n if (verticalScroll) {\n // delta < 0 is scroll up, delta > 0 is scroll down.\n canScroll = deltaY < 0 ?\n node.scrollTop > 0 :\n node.scrollTop < node.scrollHeight - node.clientHeight;\n } else {\n // delta < 0 is scroll left, delta > 0 is scroll right.\n canScroll = deltaX < 0 ?\n node.scrollLeft > 0 :\n node.scrollLeft < node.scrollWidth - node.clientWidth;\n }\n if (canScroll) {\n return node;\n }\n }\n}\n\n/**\n * Returns scroll `deltaX` and `deltaY`.\n * @param {!Event} event The scroll event\n * @return {{deltaX: number, deltaY: number}} Object containing the\n * x-axis scroll delta (positive: scroll right, negative: scroll left,\n * 0: no scroll), and the y-axis scroll delta (positive: scroll down,\n * negative: scroll up, 0: no scroll).\n * @private\n */\nexport function _getScrollInfo(event) {\n var info = {deltaX: event.deltaX, deltaY: event.deltaY};\n // Already available.\n if ('deltaX' in event) {\n // do nothing, values are already good.\n }\n // Safari has scroll info in `wheelDeltaX/Y`.\n else if ('wheelDeltaX' in event && 'wheelDeltaY' in event) {\n info.deltaX = -event.wheelDeltaX;\n info.deltaY = -event.wheelDeltaY;\n }\n // IE10 has only vertical scroll info in `wheelDelta`.\n else if ('wheelDelta' in event) {\n info.deltaX = 0;\n info.deltaY = -event.wheelDelta;\n }\n // Firefox has scroll info in `detail` and `axis`.\n else if ('axis' in event) {\n info.deltaX = event.axis === 1 ? event.detail : 0;\n info.deltaY = event.axis === 2 ? event.detail : 0;\n }\n // On mobile devices, calculate scroll direction.\n else if (event.targetTouches) {\n var touch = event.targetTouches[0];\n // Touch moves from right to left => scrolling goes right.\n info.deltaX = lastTouchPosition.pageX - touch.pageX;\n // Touch moves from down to up => scrolling goes down.\n info.deltaY = lastTouchPosition.pageY - touch.pageY;\n }\n return info;\n}\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronFitBehavior} from '@polymer/iron-fit-behavior/iron-fit-behavior.js';\nimport {IronResizableBehavior} from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {useShadow} from '@polymer/polymer/lib/utils/settings.js';\n\nimport {IronFocusablesHelper} from './iron-focusables-helper.js';\nimport {IronOverlayManager} from './iron-overlay-manager.js';\nimport {pushScrollLock, removeScrollLock} from './iron-scroll-manager.js';\n\n/** @polymerBehavior */\nexport const IronOverlayBehaviorImpl = {\n\n properties: {\n\n /**\n * True if the overlay is currently displayed.\n */\n opened:\n {observer: '_openedChanged', type: Boolean, value: false, notify: true},\n\n /**\n * True if the overlay was canceled when it was last closed.\n */\n canceled: {\n observer: '_canceledChanged',\n readOnly: true,\n type: Boolean,\n value: false\n },\n\n /**\n * Set to true to display a backdrop behind the overlay. It traps the focus\n * within the light DOM of the overlay.\n */\n withBackdrop: {\n observer: '_withBackdropChanged',\n type: Boolean,\n },\n\n /**\n * Set to true to disable auto-focusing the overlay or child nodes with\n * the `autofocus` attribute` when the overlay is opened.\n */\n noAutoFocus: {\n type: Boolean,\n value: false,\n },\n\n /**\n * Set to true to disable canceling the overlay with the ESC key.\n */\n noCancelOnEscKey: {\n type: Boolean,\n value: false,\n },\n\n /**\n * Set to true to disable canceling the overlay by clicking outside it.\n */\n noCancelOnOutsideClick: {\n type: Boolean,\n value: false,\n },\n\n /**\n * Contains the reason(s) this overlay was last closed (see\n * `iron-overlay-closed`). `IronOverlayBehavior` provides the `canceled`\n * reason; implementers of the behavior can provide other reasons in\n * addition to `canceled`.\n */\n closingReason: {\n // was a getter before, but needs to be a property so other\n // behaviors can override this.\n type: Object,\n },\n\n /**\n * Set to true to enable restoring of focus when overlay is closed.\n */\n restoreFocusOnClose: {\n type: Boolean,\n value: false,\n },\n\n /**\n * Set to true to allow clicks to go through overlays.\n * When the user clicks outside this overlay, the click may\n * close the overlay below.\n */\n allowClickThrough: {\n type: Boolean,\n },\n\n /**\n * Set to true to keep overlay always on top.\n */\n alwaysOnTop: {\n type: Boolean,\n },\n\n /**\n * Determines which action to perform when scroll outside an opened overlay\n * happens. Possible values: lock - blocks scrolling from happening, refit -\n * computes the new position on the overlay cancel - causes the overlay to\n * close\n */\n scrollAction: {\n type: String,\n },\n\n /**\n * Shortcut to access to the overlay manager.\n * @private\n * @type {!IronOverlayManagerClass}\n */\n _manager: {\n type: Object,\n value: IronOverlayManager,\n },\n\n /**\n * The node being focused.\n * @type {?Node}\n */\n _focusedChild: {\n type: Object,\n }\n\n },\n\n listeners: {'iron-resize': '_onIronResize'},\n\n observers: ['__updateScrollObservers(isAttached, opened, scrollAction)'],\n\n /**\n * The backdrop element.\n * @return {!Element}\n */\n get backdropElement() {\n return this._manager.backdropElement;\n },\n\n /**\n * Returns the node to give focus to.\n * @return {!Node}\n */\n get _focusNode() {\n return this._focusedChild || dom(this).querySelector('[autofocus]') || this;\n },\n\n /**\n * Array of nodes that can receive focus (overlay included), ordered by\n * `tabindex`. This is used to retrieve which is the first and last focusable\n * nodes in order to wrap the focus for overlays `with-backdrop`.\n *\n * If you know what is your content (specifically the first and last focusable\n * children), you can override this method to return only `[firstFocusable,\n * lastFocusable];`\n * @return {!Array}\n * @protected\n */\n get _focusableNodes() {\n return IronFocusablesHelper.getTabbableNodes(this);\n },\n\n /**\n * @return {void}\n */\n ready: function() {\n // Used to skip calls to notifyResize and refit while the overlay is\n // animating.\n this.__isAnimating = false;\n // with-backdrop needs tabindex to be set in order to trap the focus.\n // If it is not set, IronOverlayBehavior will set it, and remove it if\n // with-backdrop = false.\n this.__shouldRemoveTabIndex = false;\n // Used for wrapping the focus on TAB / Shift+TAB.\n this.__firstFocusableNode = this.__lastFocusableNode = null;\n // Used by to keep track of the RAF callbacks.\n this.__rafs = {};\n // Focused node before overlay gets opened. Can be restored on close.\n this.__restoreFocusNode = null;\n // Scroll info to be restored.\n this.__scrollTop = this.__scrollLeft = null;\n this.__onCaptureScroll = this.__onCaptureScroll.bind(this);\n // Root nodes hosting the overlay, used to listen for scroll events on them.\n this.__rootNodes = null;\n this._ensureSetup();\n },\n\n attached: function() {\n // Call _openedChanged here so that position can be computed correctly.\n if (this.opened) {\n this._openedChanged(this.opened);\n }\n this._observer = dom(this).observeNodes(this._onNodesChange);\n },\n\n detached: function() {\n dom(this).unobserveNodes(this._observer);\n this._observer = null;\n for (var cb in this.__rafs) {\n if (this.__rafs[cb] !== null) {\n cancelAnimationFrame(this.__rafs[cb]);\n }\n }\n this.__rafs = {};\n this._manager.removeOverlay(this);\n\n // We got detached while animating, ensure we show/hide the overlay\n // and fire iron-overlay-opened/closed event!\n if (this.__isAnimating) {\n if (this.opened) {\n this._finishRenderOpened();\n } else {\n // Restore the focus if necessary.\n this._applyFocus();\n this._finishRenderClosed();\n }\n }\n },\n\n /**\n * Toggle the opened state of the overlay.\n */\n toggle: function() {\n this._setCanceled(false);\n this.opened = !this.opened;\n },\n\n /**\n * Open the overlay.\n */\n open: function() {\n this._setCanceled(false);\n this.opened = true;\n },\n\n /**\n * Close the overlay.\n */\n close: function() {\n this._setCanceled(false);\n this.opened = false;\n },\n\n /**\n * Cancels the overlay.\n * @param {Event=} event The original event\n */\n cancel: function(event) {\n var cancelEvent =\n this.fire('iron-overlay-canceled', event, {cancelable: true});\n if (cancelEvent.defaultPrevented) {\n return;\n }\n\n this._setCanceled(true);\n this.opened = false;\n },\n\n /**\n * Invalidates the cached tabbable nodes. To be called when any of the\n * focusable content changes (e.g. a button is disabled).\n */\n invalidateTabbables: function() {\n this.__firstFocusableNode = this.__lastFocusableNode = null;\n },\n\n _ensureSetup: function() {\n if (this._overlaySetup) {\n return;\n }\n this._overlaySetup = true;\n this.style.outline = 'none';\n this.style.display = 'none';\n },\n\n /**\n * Called when `opened` changes.\n * @param {boolean=} opened\n * @protected\n */\n _openedChanged: function(opened) {\n if (opened) {\n this.removeAttribute('aria-hidden');\n } else {\n this.setAttribute('aria-hidden', 'true');\n }\n\n // Defer any animation-related code on attached\n // (_openedChanged gets called again on attached).\n if (!this.isAttached) {\n return;\n }\n\n this.__isAnimating = true;\n\n // Deraf for non-blocking rendering.\n this.__deraf('__openedChanged', this.__openedChanged);\n },\n\n _canceledChanged: function() {\n this.closingReason = this.closingReason || {};\n this.closingReason.canceled = this.canceled;\n },\n\n _withBackdropChanged: function() {\n // If tabindex is already set, no need to override it.\n if (this.withBackdrop && !this.hasAttribute('tabindex')) {\n this.setAttribute('tabindex', '-1');\n this.__shouldRemoveTabIndex = true;\n } else if (this.__shouldRemoveTabIndex) {\n this.removeAttribute('tabindex');\n this.__shouldRemoveTabIndex = false;\n }\n if (this.opened && this.isAttached) {\n this._manager.trackBackdrop();\n }\n },\n\n /**\n * tasks which must occur before opening; e.g. making the element visible.\n * @protected\n */\n _prepareRenderOpened: function() {\n // Store focused node.\n this.__restoreFocusNode = this._manager.deepActiveElement;\n\n // Needed to calculate the size of the overlay so that transitions on its\n // size will have the correct starting points.\n this._preparePositioning();\n this.refit();\n this._finishPositioning();\n\n // Safari will apply the focus to the autofocus element when displayed\n // for the first time, so we make sure to return the focus where it was.\n if (this.noAutoFocus && document.activeElement === this._focusNode) {\n this._focusNode.blur();\n this.__restoreFocusNode.focus();\n }\n },\n\n /**\n * Tasks which cause the overlay to actually open; typically play an\n * animation.\n * @protected\n */\n _renderOpened: function() {\n this._finishRenderOpened();\n },\n\n /**\n * Tasks which cause the overlay to actually close; typically play an\n * animation.\n * @protected\n */\n _renderClosed: function() {\n this._finishRenderClosed();\n },\n\n /**\n * Tasks to be performed at the end of open action. Will fire\n * `iron-overlay-opened`.\n * @protected\n */\n _finishRenderOpened: function() {\n this.notifyResize();\n this.__isAnimating = false;\n\n this.fire('iron-overlay-opened');\n },\n\n /**\n * Tasks to be performed at the end of close action. Will fire\n * `iron-overlay-closed`.\n * @protected\n */\n _finishRenderClosed: function() {\n // Hide the overlay.\n this.style.display = 'none';\n // Reset z-index only at the end of the animation.\n this.style.zIndex = '';\n this.notifyResize();\n this.__isAnimating = false;\n this.fire('iron-overlay-closed', this.closingReason);\n },\n\n _preparePositioning: function() {\n this.style.transition = this.style.webkitTransition = 'none';\n this.style.transform = this.style.webkitTransform = 'none';\n this.style.display = '';\n },\n\n _finishPositioning: function() {\n // First, make it invisible & reactivate animations.\n this.style.display = 'none';\n // Force reflow before re-enabling animations so that they don't start.\n // Set scrollTop to itself so that Closure Compiler doesn't remove this.\n this.scrollTop = this.scrollTop;\n this.style.transition = this.style.webkitTransition = '';\n this.style.transform = this.style.webkitTransform = '';\n // Now that animations are enabled, make it visible again\n this.style.display = '';\n // Force reflow, so that following animations are properly started.\n // Set scrollTop to itself so that Closure Compiler doesn't remove this.\n this.scrollTop = this.scrollTop;\n },\n\n /**\n * Applies focus according to the opened state.\n * @protected\n */\n _applyFocus: function() {\n if (this.opened) {\n if (!this.noAutoFocus) {\n this._focusNode.focus();\n }\n } else {\n // Restore focus.\n if (this.restoreFocusOnClose && this.__restoreFocusNode) {\n // If the activeElement is `` or inside the overlay,\n // we are allowed to restore the focus. In all the other\n // cases focus might have been moved elsewhere by another\n // component or by an user interaction (e.g. click on a\n // button outside the overlay).\n var activeElement = this._manager.deepActiveElement;\n if (activeElement === document.body ||\n dom(this).deepContains(activeElement)) {\n this.__restoreFocusNode.focus();\n }\n }\n this.__restoreFocusNode = null;\n this._focusNode.blur();\n this._focusedChild = null;\n }\n },\n\n /**\n * Cancels (closes) the overlay. Call when click happens outside the overlay.\n * @param {!Event} event\n * @protected\n */\n _onCaptureClick: function(event) {\n if (!this.noCancelOnOutsideClick) {\n this.cancel(event);\n }\n },\n\n /**\n * Keeps track of the focused child. If withBackdrop, traps focus within\n * overlay.\n * @param {!Event} event\n * @protected\n */\n _onCaptureFocus: function(event) {\n if (!this.withBackdrop) {\n return;\n }\n var path = dom(event).path;\n if (path.indexOf(this) === -1) {\n event.stopPropagation();\n this._applyFocus();\n } else {\n this._focusedChild = path[0];\n }\n },\n\n /**\n * Handles the ESC key event and cancels (closes) the overlay.\n * @param {!Event} event\n * @protected\n */\n _onCaptureEsc: function(event) {\n if (!this.noCancelOnEscKey) {\n this.cancel(event);\n }\n },\n\n /**\n * Handles TAB key events to track focus changes.\n * Will wrap focus for overlays withBackdrop.\n * @param {!Event} event\n * @protected\n */\n _onCaptureTab: function(event) {\n if (!this.withBackdrop) {\n return;\n }\n this.__ensureFirstLastFocusables();\n // TAB wraps from last to first focusable.\n // Shift + TAB wraps from first to last focusable.\n var shift = event.shiftKey;\n var nodeToCheck =\n shift ? this.__firstFocusableNode : this.__lastFocusableNode;\n var nodeToSet =\n shift ? this.__lastFocusableNode : this.__firstFocusableNode;\n var shouldWrap = false;\n if (nodeToCheck === nodeToSet) {\n // If nodeToCheck is the same as nodeToSet, it means we have an overlay\n // with 0 or 1 focusables; in either case we still need to trap the\n // focus within the overlay.\n shouldWrap = true;\n } else {\n // In dom=shadow, the manager will receive focus changes on the main\n // root but not the ones within other shadow roots, so we can't rely on\n // _focusedChild, but we should check the deepest active element.\n var focusedNode = this._manager.deepActiveElement;\n // If the active element is not the nodeToCheck but the overlay itself,\n // it means the focus is about to go outside the overlay, hence we\n // should prevent that (e.g. user opens the overlay and hit Shift+TAB).\n shouldWrap = (focusedNode === nodeToCheck || focusedNode === this);\n }\n\n if (shouldWrap) {\n // When the overlay contains the last focusable element of the document\n // and it's already focused, pressing TAB would move the focus outside\n // the document (e.g. to the browser search bar). Similarly, when the\n // overlay contains the first focusable element of the document and it's\n // already focused, pressing Shift+TAB would move the focus outside the\n // document (e.g. to the browser search bar).\n // In both cases, we would not receive a focus event, but only a blur.\n // In order to achieve focus wrapping, we prevent this TAB event and\n // force the focus. This will also prevent the focus to temporarily move\n // outside the overlay, which might cause scrolling.\n event.preventDefault();\n this._focusedChild = nodeToSet;\n this._applyFocus();\n }\n },\n\n /**\n * Refits if the overlay is opened and not animating.\n * @protected\n */\n _onIronResize: function() {\n if (this.opened && !this.__isAnimating) {\n this.__deraf('refit', this.refit);\n }\n },\n\n /**\n * Will call notifyResize if overlay is opened.\n * Can be overridden in order to avoid multiple observers on the same node.\n * @protected\n */\n _onNodesChange: function() {\n if (this.opened && !this.__isAnimating) {\n // It might have added focusable nodes, so invalidate cached values.\n this.invalidateTabbables();\n this.notifyResize();\n }\n },\n\n /**\n * Updates the references to the first and last focusable nodes.\n * @private\n */\n __ensureFirstLastFocusables: function() {\n var focusableNodes = this._focusableNodes;\n this.__firstFocusableNode = focusableNodes[0];\n this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];\n },\n\n /**\n * Tasks executed when opened changes: prepare for the opening, move the\n * focus, update the manager, render opened/closed.\n * @private\n */\n __openedChanged: function() {\n if (this.opened) {\n // Make overlay visible, then add it to the manager.\n this._prepareRenderOpened();\n this._manager.addOverlay(this);\n // Move the focus to the child node with [autofocus].\n this._applyFocus();\n\n this._renderOpened();\n } else {\n // Remove overlay, then restore the focus before actually closing.\n this._manager.removeOverlay(this);\n this._applyFocus();\n\n this._renderClosed();\n }\n },\n\n /**\n * Debounces the execution of a callback to the next animation frame.\n * @param {!string} jobname\n * @param {!Function} callback Always bound to `this`\n * @private\n */\n __deraf: function(jobname, callback) {\n var rafs = this.__rafs;\n if (rafs[jobname] !== null) {\n cancelAnimationFrame(rafs[jobname]);\n }\n rafs[jobname] = requestAnimationFrame(function nextAnimationFrame() {\n rafs[jobname] = null;\n callback.call(this);\n }.bind(this));\n },\n\n /**\n * @param {boolean} isAttached\n * @param {boolean} opened\n * @param {string=} scrollAction\n * @private\n */\n __updateScrollObservers: function(isAttached, opened, scrollAction) {\n if (!isAttached || !opened || !this.__isValidScrollAction(scrollAction)) {\n removeScrollLock(this);\n this.__removeScrollListeners();\n } else {\n if (scrollAction === 'lock') {\n this.__saveScrollPosition();\n pushScrollLock(this);\n }\n this.__addScrollListeners();\n }\n },\n\n /**\n * @private\n */\n __addScrollListeners: function() {\n if (!this.__rootNodes) {\n this.__rootNodes = [];\n // Listen for scroll events in all shadowRoots hosting this overlay only\n // when in native ShadowDOM.\n if (useShadow) {\n var node = this;\n while (node) {\n if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && node.host) {\n this.__rootNodes.push(node);\n }\n node = node.host || node.assignedSlot || node.parentNode;\n }\n }\n this.__rootNodes.push(document);\n }\n this.__rootNodes.forEach(function(el) {\n el.addEventListener('scroll', this.__onCaptureScroll, {\n capture: true,\n passive: true,\n });\n }, this);\n },\n\n /**\n * @private\n */\n __removeScrollListeners: function() {\n if (this.__rootNodes) {\n this.__rootNodes.forEach(function(el) {\n el.removeEventListener('scroll', this.__onCaptureScroll, {\n capture: true,\n passive: true,\n });\n }, this);\n }\n if (!this.isAttached) {\n this.__rootNodes = null;\n }\n },\n\n /**\n * @param {string=} scrollAction\n * @return {boolean}\n * @private\n */\n __isValidScrollAction: function(scrollAction) {\n return scrollAction === 'lock' || scrollAction === 'refit' ||\n scrollAction === 'cancel';\n },\n\n /**\n * @private\n */\n __onCaptureScroll: function(event) {\n if (this.__isAnimating) {\n return;\n }\n // Check if scroll outside the overlay.\n if (dom(event).path.indexOf(this) >= 0) {\n return;\n }\n switch (this.scrollAction) {\n case 'lock':\n // NOTE: scrolling might happen if a scroll event is not cancellable, or\n // if user pressed keys that cause scrolling (they're not prevented in\n // order not to break a11y features like navigate with arrow keys).\n this.__restoreScrollPosition();\n break;\n case 'refit':\n this.__deraf('refit', this.refit);\n break;\n case 'cancel':\n this.cancel(event);\n break;\n }\n },\n\n /**\n * Memoizes the scroll position of the outside scrolling element.\n * @private\n */\n __saveScrollPosition: function() {\n if (document.scrollingElement) {\n this.__scrollTop = document.scrollingElement.scrollTop;\n this.__scrollLeft = document.scrollingElement.scrollLeft;\n } else {\n // Since we don't know if is the body or html, get max.\n this.__scrollTop =\n Math.max(document.documentElement.scrollTop, document.body.scrollTop);\n this.__scrollLeft = Math.max(\n document.documentElement.scrollLeft, document.body.scrollLeft);\n }\n },\n\n /**\n * Resets the scroll position of the outside scrolling element.\n * @private\n */\n __restoreScrollPosition: function() {\n if (document.scrollingElement) {\n document.scrollingElement.scrollTop = this.__scrollTop;\n document.scrollingElement.scrollLeft = this.__scrollLeft;\n } else {\n // Since we don't know if is the body or html, set both.\n document.documentElement.scrollTop = document.body.scrollTop =\n this.__scrollTop;\n document.documentElement.scrollLeft = document.body.scrollLeft =\n this.__scrollLeft;\n }\n },\n\n};\n\n/**\n Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden\n or shown, and displays on top of other content. It includes an optional\n backdrop, and can be used to implement a variety of UI controls including\n dialogs and drop downs. Multiple overlays may be displayed at once.\n\n See the [demo source\n code](https://github.com/PolymerElements/iron-overlay-behavior/blob/master/demo/simple-overlay.html)\n for an example.\n\n ### Closing and canceling\n\n An overlay may be hidden by closing or canceling. The difference between close\n and cancel is user intent. Closing generally implies that the user\n acknowledged the content on the overlay. By default, it will cancel whenever\n the user taps outside it or presses the escape key. This behavior is\n configurable with the `no-cancel-on-esc-key` and the\n `no-cancel-on-outside-click` properties. `close()` should be called explicitly\n by the implementer when the user interacts with a control in the overlay\n element. When the dialog is canceled, the overlay fires an\n 'iron-overlay-canceled' event. Call `preventDefault` on this event to prevent\n the overlay from closing.\n\n ### Positioning\n\n By default the element is sized and positioned to fit and centered inside the\n window. You can position and size it manually using CSS. See\n `Polymer.IronFitBehavior`.\n\n ### Backdrop\n\n Set the `with-backdrop` attribute to display a backdrop behind the overlay.\n The backdrop is appended to `` and is of type ``.\n See its doc page for styling options.\n\n In addition, `with-backdrop` will wrap the focus within the content in the\n light DOM. Override the [`_focusableNodes`\n getter](#Polymer.IronOverlayBehavior:property-_focusableNodes) to achieve a\n different behavior.\n\n ### Limitations\n\n The element is styled to appear on top of other content by setting its\n `z-index` property. You must ensure no element has a stacking context with a\n higher `z-index` than its parent stacking context. You should place this\n element as a child of `` whenever possible.\n\n @demo demo/index.html\n @polymerBehavior\n */\nexport const IronOverlayBehavior =\n [IronFitBehavior, IronResizableBehavior, IronOverlayBehaviorImpl];\n\n/**\n * Fired after the overlay opens.\n * @event iron-overlay-opened\n */\n\n/**\n * Fired when the overlay is canceled, but before it is closed.\n * @event iron-overlay-canceled\n * @param {Event} event The closing of the overlay can be prevented\n * by calling `event.preventDefault()`. The `event.detail` is the original event\n * that originated the canceling (e.g. ESC keyboard event or click event outside\n * the overlay).\n */\n\n/**\n * Fired after the overlay closes.\n * @event iron-overlay-closed\n * @param {Event} event The `event.detail` is the `closingReason` property\n * (contains `canceled`, whether the overlay was canceled).\n */\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronSelectableBehavior} from './iron-selectable.js';\n\n/**\n * @polymerBehavior IronMultiSelectableBehavior\n */\nexport const IronMultiSelectableBehaviorImpl = {\n properties: {\n\n /**\n * If true, multiple selections are allowed.\n */\n multi: {type: Boolean, value: false, observer: 'multiChanged'},\n\n /**\n * Gets or sets the selected elements. This is used instead of `selected`\n * when `multi` is true.\n */\n selectedValues: {\n type: Array,\n notify: true,\n value: function() {\n return [];\n }\n },\n\n /**\n * Returns an array of currently selected items.\n */\n selectedItems: {\n type: Array,\n readOnly: true,\n notify: true,\n value: function() {\n return [];\n }\n },\n\n },\n\n observers: ['_updateSelected(selectedValues.splices)'],\n\n /**\n * Selects the given value. If the `multi` property is true, then the selected\n * state of the `value` will be toggled; otherwise the `value` will be\n * selected.\n *\n * @method select\n * @param {string|number} value the value to select.\n */\n select: function(value) {\n if (this.multi) {\n this._toggleSelected(value);\n } else {\n this.selected = value;\n }\n },\n\n multiChanged: function(multi) {\n this._selection.multi = multi;\n this._updateSelected();\n },\n\n // UNUSED, FOR API COMPATIBILITY\n get _shouldUpdateSelection() {\n return this.selected != null ||\n (this.selectedValues != null && this.selectedValues.length);\n },\n\n _updateAttrForSelected: function() {\n if (!this.multi) {\n IronSelectableBehavior._updateAttrForSelected.apply(this);\n } else if (this.selectedItems && this.selectedItems.length > 0) {\n this.selectedValues =\n this.selectedItems\n .map(\n function(selectedItem) {\n return this._indexToValue(this.indexOf(selectedItem));\n },\n this)\n .filter(function(unfilteredValue) {\n return unfilteredValue != null;\n }, this);\n }\n },\n\n _updateSelected: function() {\n if (this.multi) {\n this._selectMulti(this.selectedValues);\n } else {\n this._selectSelected(this.selected);\n }\n },\n\n _selectMulti: function(values) {\n values = values || [];\n\n var selectedItems =\n (this._valuesToItems(values) || []).filter(function(item) {\n return item !== null && item !== undefined;\n });\n\n // clear all but the current selected items\n this._selection.clear(selectedItems);\n\n // select only those not selected yet\n for (var i = 0; i < selectedItems.length; i++) {\n this._selection.setItemSelected(selectedItems[i], true);\n }\n\n // Check for items, since this array is populated only when attached\n if (this.fallbackSelection && !this._selection.get().length) {\n var fallback = this._valueToItem(this.fallbackSelection);\n if (fallback) {\n this.select(this.fallbackSelection);\n }\n }\n },\n\n _selectionChange: function() {\n var s = this._selection.get();\n if (this.multi) {\n this._setSelectedItems(s);\n this._setSelectedItem(s.length ? s[0] : null);\n } else {\n if (s !== null && s !== undefined) {\n this._setSelectedItems([s]);\n this._setSelectedItem(s);\n } else {\n this._setSelectedItems([]);\n this._setSelectedItem(null);\n }\n }\n },\n\n _toggleSelected: function(value) {\n var i = this.selectedValues.indexOf(value);\n var unselected = i < 0;\n if (unselected) {\n this.push('selectedValues', value);\n } else {\n this.splice('selectedValues', i, 1);\n }\n },\n\n _valuesToItems: function(values) {\n return (values == null) ? null : values.map(function(value) {\n return this._valueToItem(value);\n }, this);\n }\n};\n\n/** @polymerBehavior */\nexport const IronMultiSelectableBehavior =\n [IronSelectableBehavior, IronMultiSelectableBehaviorImpl];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';\nimport {IronMultiSelectableBehavior, IronMultiSelectableBehaviorImpl} from '@polymer/iron-selector/iron-multi-selectable.js';\nimport {IronSelectableBehavior} from '@polymer/iron-selector/iron-selectable.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\n\n/**\n * `IronMenuBehavior` implements accessible menu behavior.\n *\n * @demo demo/index.html\n * @polymerBehavior IronMenuBehavior\n */\nexport const IronMenuBehaviorImpl = {\n\n properties: {\n\n /**\n * Returns the currently focused item.\n * @type {?Object}\n */\n focusedItem:\n {observer: '_focusedItemChanged', readOnly: true, type: Object},\n\n /**\n * The attribute to use on menu items to look up the item title. Typing the\n * first letter of an item when the menu is open focuses that item. If\n * unset, `textContent` will be used.\n */\n attrForItemTitle: {type: String},\n\n /**\n * @type {boolean}\n */\n disabled: {\n type: Boolean,\n value: false,\n observer: '_disabledChanged',\n },\n },\n\n /**\n * The list of keys has been taken from\n * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState\n * @private\n */\n _MODIFIER_KEYS: [\n 'Alt',\n 'AltGraph',\n 'CapsLock',\n 'Control',\n 'Fn',\n 'FnLock',\n 'Hyper',\n 'Meta',\n 'NumLock',\n 'OS',\n 'ScrollLock',\n 'Shift',\n 'Super',\n 'Symbol',\n 'SymbolLock'\n ],\n\n /** @private */\n _SEARCH_RESET_TIMEOUT_MS: 1000,\n\n /** @private */\n _previousTabIndex: 0,\n\n hostAttributes: {\n 'role': 'menu',\n },\n\n observers: ['_updateMultiselectable(multi)'],\n\n listeners: {\n 'focus': '_onFocus',\n 'keydown': '_onKeydown',\n 'iron-items-changed': '_onIronItemsChanged'\n },\n\n /**\n * @type {!Object}\n */\n keyBindings: {\n 'up': '_onUpKey',\n 'down': '_onDownKey',\n 'esc': '_onEscKey',\n 'shift+tab:keydown': '_onShiftTabDown'\n },\n\n attached: function() {\n this._resetTabindices();\n },\n\n /**\n * Selects the given value. If the `multi` property is true, then the selected\n * state of the `value` will be toggled; otherwise the `value` will be\n * selected.\n *\n * @param {string|number} value the value to select.\n */\n select: function(value) {\n // Cancel automatically focusing a default item if the menu received focus\n // through a user action selecting a particular item.\n if (this._defaultFocusAsync) {\n this.cancelAsync(this._defaultFocusAsync);\n this._defaultFocusAsync = null;\n }\n var item = this._valueToItem(value);\n if (item && item.hasAttribute('disabled'))\n return;\n this._setFocusedItem(item);\n IronMultiSelectableBehaviorImpl.select.apply(this, arguments);\n },\n\n /**\n * Resets all tabindex attributes to the appropriate value based on the\n * current selection state. The appropriate value is `0` (focusable) for\n * the default selected item, and `-1` (not keyboard focusable) for all\n * other items. Also sets the correct initial values for aria-selected\n * attribute, true for default selected item and false for others.\n */\n _resetTabindices: function() {\n var firstSelectedItem = this.multi ?\n (this.selectedItems && this.selectedItems[0]) :\n this.selectedItem;\n\n this.items.forEach(function(item) {\n item.setAttribute('tabindex', item === firstSelectedItem ? '0' : '-1');\n item.setAttribute('aria-selected', this._selection.isSelected(item));\n }, this);\n },\n\n /**\n * Sets appropriate ARIA based on whether or not the menu is meant to be\n * multi-selectable.\n *\n * @param {boolean} multi True if the menu should be multi-selectable.\n */\n _updateMultiselectable: function(multi) {\n if (multi) {\n this.setAttribute('aria-multiselectable', 'true');\n } else {\n this.removeAttribute('aria-multiselectable');\n }\n },\n\n /**\n * Given a KeyboardEvent, this method will focus the appropriate item in the\n * menu (if there is a relevant item, and it is possible to focus it).\n *\n * @param {KeyboardEvent} event A KeyboardEvent.\n */\n _focusWithKeyboardEvent: function(event) {\n // Make sure that the key pressed is not a modifier key.\n // getModifierState is not being used, as it is not available in Safari\n // earlier than 10.0.2 (https://trac.webkit.org/changeset/206725/webkit)\n if (this._MODIFIER_KEYS.indexOf(event.key) !== -1)\n return;\n\n this.cancelDebouncer('_clearSearchText');\n\n var searchText = this._searchText || '';\n var key = event.key && event.key.length == 1 ?\n event.key :\n String.fromCharCode(event.keyCode);\n searchText += key.toLocaleLowerCase();\n\n var searchLength = searchText.length;\n\n for (var i = 0, item; item = this.items[i]; i++) {\n if (item.hasAttribute('disabled')) {\n continue;\n }\n\n var attr = this.attrForItemTitle || 'textContent';\n var title = (item[attr] || item.getAttribute(attr) || '').trim();\n\n if (title.length < searchLength) {\n continue;\n }\n\n if (title.slice(0, searchLength).toLocaleLowerCase() == searchText) {\n this._setFocusedItem(item);\n break;\n }\n }\n\n this._searchText = searchText;\n this.debounce(\n '_clearSearchText',\n this._clearSearchText,\n this._SEARCH_RESET_TIMEOUT_MS);\n },\n\n _clearSearchText: function() {\n this._searchText = '';\n },\n\n /**\n * Focuses the previous item (relative to the currently focused item) in the\n * menu, disabled items will be skipped.\n * Loop until length + 1 to handle case of single item in menu.\n */\n _focusPrevious: function() {\n var length = this.items.length;\n var curFocusIndex = Number(this.indexOf(this.focusedItem));\n\n for (var i = 1; i < length + 1; i++) {\n var item = this.items[(curFocusIndex - i + length) % length];\n if (!item.hasAttribute('disabled')) {\n var owner = dom(item).getOwnerRoot() || document;\n this._setFocusedItem(item);\n\n // Focus might not have worked, if the element was hidden or not\n // focusable. In that case, try again.\n if (dom(owner).activeElement == item) {\n return;\n }\n }\n }\n },\n\n /**\n * Focuses the next item (relative to the currently focused item) in the\n * menu, disabled items will be skipped.\n * Loop until length + 1 to handle case of single item in menu.\n */\n _focusNext: function() {\n var length = this.items.length;\n var curFocusIndex = Number(this.indexOf(this.focusedItem));\n\n for (var i = 1; i < length + 1; i++) {\n var item = this.items[(curFocusIndex + i) % length];\n if (!item.hasAttribute('disabled')) {\n var owner = dom(item).getOwnerRoot() || document;\n this._setFocusedItem(item);\n\n // Focus might not have worked, if the element was hidden or not\n // focusable. In that case, try again.\n if (dom(owner).activeElement == item) {\n return;\n }\n }\n }\n },\n\n /**\n * Mutates items in the menu based on provided selection details, so that\n * all items correctly reflect selection state.\n *\n * @param {Element} item An item in the menu.\n * @param {boolean} isSelected True if the item should be shown in a\n * selected state, otherwise false.\n */\n _applySelection: function(item, isSelected) {\n if (isSelected) {\n item.setAttribute('aria-selected', 'true');\n } else {\n item.setAttribute('aria-selected', 'false');\n }\n IronSelectableBehavior._applySelection.apply(this, arguments);\n },\n\n /**\n * Discretely updates tabindex values among menu items as the focused item\n * changes.\n *\n * @param {Element} focusedItem The element that is currently focused.\n * @param {?Element} old The last element that was considered focused, if\n * applicable.\n */\n _focusedItemChanged: function(focusedItem, old) {\n old && old.setAttribute('tabindex', '-1');\n if (focusedItem && !focusedItem.hasAttribute('disabled') &&\n !this.disabled) {\n focusedItem.setAttribute('tabindex', '0');\n focusedItem.focus();\n }\n },\n\n /**\n * A handler that responds to mutation changes related to the list of items\n * in the menu.\n *\n * @param {CustomEvent} event An event containing mutation records as its\n * detail.\n */\n _onIronItemsChanged: function(event) {\n if (event.detail.addedNodes.length) {\n this._resetTabindices();\n }\n },\n\n /**\n * Handler that is called when a shift+tab keypress is detected by the menu.\n *\n * @param {CustomEvent} event A key combination event.\n */\n _onShiftTabDown: function(event) {\n var oldTabIndex = this.getAttribute('tabindex');\n\n IronMenuBehaviorImpl._shiftTabPressed = true;\n\n this._setFocusedItem(null);\n\n this.setAttribute('tabindex', '-1');\n\n this.async(function() {\n this.setAttribute('tabindex', oldTabIndex);\n IronMenuBehaviorImpl._shiftTabPressed = false;\n // NOTE(cdata): polymer/polymer#1305\n }, 1);\n },\n\n /**\n * Handler that is called when the menu receives focus.\n *\n * @param {FocusEvent} event A focus event.\n */\n _onFocus: function(event) {\n if (IronMenuBehaviorImpl._shiftTabPressed) {\n // do not focus the menu itself\n return;\n }\n\n // Do not focus the selected tab if the deepest target is part of the\n // menu element's local DOM and is focusable.\n var rootTarget =\n /** @type {?HTMLElement} */ (dom(event).rootTarget);\n if (rootTarget !== this && typeof rootTarget.tabIndex !== 'undefined' &&\n !this.isLightDescendant(rootTarget)) {\n return;\n }\n\n // clear the cached focus item\n this._defaultFocusAsync = this.async(function() {\n // focus the selected item when the menu receives focus, or the first item\n // if no item is selected\n var firstSelectedItem = this.multi ?\n (this.selectedItems && this.selectedItems[0]) :\n this.selectedItem;\n\n this._setFocusedItem(null);\n\n if (firstSelectedItem) {\n this._setFocusedItem(firstSelectedItem);\n } else if (this.items[0]) {\n // We find the first none-disabled item (if one exists)\n this._focusNext();\n }\n });\n },\n\n /**\n * Handler that is called when the up key is pressed.\n *\n * @param {CustomEvent} event A key combination event.\n */\n _onUpKey: function(event) {\n // up and down arrows moves the focus\n this._focusPrevious();\n event.detail.keyboardEvent.preventDefault();\n },\n\n /**\n * Handler that is called when the down key is pressed.\n *\n * @param {CustomEvent} event A key combination event.\n */\n _onDownKey: function(event) {\n this._focusNext();\n event.detail.keyboardEvent.preventDefault();\n },\n\n /**\n * Handler that is called when the esc key is pressed.\n *\n * @param {CustomEvent} event A key combination event.\n */\n _onEscKey: function(event) {\n var focusedItem = this.focusedItem;\n if (focusedItem) {\n focusedItem.blur();\n }\n },\n\n /**\n * Handler that is called when a keydown event is detected.\n *\n * @param {KeyboardEvent} event A keyboard event.\n */\n _onKeydown: function(event) {\n if (!this.keyboardEventMatchesKeys(event, 'up down esc')) {\n // all other keys focus the menu item starting with that character\n this._focusWithKeyboardEvent(event);\n }\n event.stopPropagation();\n },\n\n // override _activateHandler\n _activateHandler: function(event) {\n IronSelectableBehavior._activateHandler.call(this, event);\n event.stopPropagation();\n },\n\n /**\n * Updates this element's tab index when it's enabled/disabled.\n * @param {boolean} disabled\n */\n _disabledChanged: function(disabled) {\n if (disabled) {\n this._previousTabIndex =\n this.hasAttribute('tabindex') ? this.tabIndex : 0;\n this.removeAttribute(\n 'tabindex'); // No tabindex means not tab-able or select-able.\n } else if (!this.hasAttribute('tabindex')) {\n this.setAttribute('tabindex', this._previousTabIndex);\n }\n }\n};\n\nIronMenuBehaviorImpl._shiftTabPressed = false;\n\n/** @polymerBehavior */\nexport const IronMenuBehavior =\n [IronMultiSelectableBehavior, IronA11yKeysBehavior, IronMenuBehaviorImpl];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nexport class IronSelection {\n /**\n * @param {!Function} selectCallback\n * @suppress {missingProvide}\n */\n constructor(selectCallback) {\n this.selection = [];\n this.selectCallback = selectCallback;\n }\n\n /**\n * Retrieves the selected item(s).\n *\n * @returns Returns the selected item(s). If the multi property is true,\n * `get` will return an array, otherwise it will return\n * the selected item or undefined if there is no selection.\n */\n get() {\n return this.multi ? this.selection.slice() : this.selection[0];\n }\n\n /**\n * Clears all the selection except the ones indicated.\n *\n * @param {Array} excludes items to be excluded.\n */\n clear(excludes) {\n this.selection.slice().forEach(function(item) {\n if (!excludes || excludes.indexOf(item) < 0) {\n this.setItemSelected(item, false);\n }\n }, this);\n }\n\n /**\n * Indicates if a given item is selected.\n *\n * @param {*} item The item whose selection state should be checked.\n * @return {boolean} Returns true if `item` is selected.\n */\n isSelected(item) {\n return this.selection.indexOf(item) >= 0;\n }\n\n /**\n * Sets the selection state for a given item to either selected or deselected.\n *\n * @param {*} item The item to select.\n * @param {boolean} isSelected True for selected, false for deselected.\n */\n setItemSelected(item, isSelected) {\n if (item != null) {\n if (isSelected !== this.isSelected(item)) {\n // proceed to update selection only if requested state differs from\n // current\n if (isSelected) {\n this.selection.push(item);\n } else {\n var i = this.selection.indexOf(item);\n if (i >= 0) {\n this.selection.splice(i, 1);\n }\n }\n if (this.selectCallback) {\n this.selectCallback(item, isSelected);\n }\n }\n }\n }\n\n /**\n * Sets the selection state for a given item. If the `multi` property\n * is true, then the selected state of `item` will be toggled; otherwise\n * the `item` will be selected.\n *\n * @param {*} item The item to select.\n */\n select(item) {\n if (this.multi) {\n this.toggle(item);\n } else if (this.get() !== item) {\n this.setItemSelected(this.get(), false);\n this.setItemSelected(item, true);\n }\n }\n\n /**\n * Toggles the selection state for `item`.\n *\n * @param {*} item The item to toggle.\n */\n toggle(item) {\n this.setItemSelected(item, !this.isSelected(item));\n }\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {dashToCamelCase} from '@polymer/polymer/lib/utils/case-map.js';\n\nimport {IronSelection} from './iron-selection.js';\n\n/**\n * @polymerBehavior\n */\nexport const IronSelectableBehavior = {\n\n /**\n * Fired when iron-selector is activated (selected or deselected).\n * It is fired before the selected items are changed.\n * Cancel the event to abort selection.\n *\n * @event iron-activate\n */\n\n /**\n * Fired when an item is selected\n *\n * @event iron-select\n */\n\n /**\n * Fired when an item is deselected\n *\n * @event iron-deselect\n */\n\n /**\n * Fired when the list of selectable items changes (e.g., items are\n * added or removed). The detail of the event is a mutation record that\n * describes what changed.\n *\n * @event iron-items-changed\n */\n\n properties: {\n\n /**\n * If you want to use an attribute value or property of an element for\n * `selected` instead of the index, set this to the name of the attribute\n * or property. Hyphenated values are converted to camel case when used to\n * look up the property of a selectable element. Camel cased values are\n * *not* converted to hyphenated values for attribute lookup. It's\n * recommended that you provide the hyphenated form of the name so that\n * selection works in both cases. (Use `attr-or-property-name` instead of\n * `attrOrPropertyName`.)\n */\n attrForSelected: {type: String, value: null},\n\n /**\n * Gets or sets the selected element. The default is to use the index of the\n * item.\n * @type {string|number}\n */\n selected: {type: String, notify: true},\n\n /**\n * Returns the currently selected item.\n *\n * @type {?Object}\n */\n selectedItem: {type: Object, readOnly: true, notify: true},\n\n /**\n * The event that fires from items when they are selected. Selectable\n * will listen for this event from items and update the selection state.\n * Set to empty string to listen to no events.\n */\n activateEvent:\n {type: String, value: 'tap', observer: '_activateEventChanged'},\n\n /**\n * This is a CSS selector string. If this is set, only items that match the\n * CSS selector are selectable.\n */\n selectable: String,\n\n /**\n * The class to set on elements when selected.\n */\n selectedClass: {type: String, value: 'iron-selected'},\n\n /**\n * The attribute to set on elements when selected.\n */\n selectedAttribute: {type: String, value: null},\n\n /**\n * Default fallback if the selection based on selected with\n * `attrForSelected` is not found.\n */\n fallbackSelection: {type: String, value: null},\n\n /**\n * The list of items from which a selection can be made.\n */\n items: {\n type: Array,\n readOnly: true,\n notify: true,\n value: function() {\n return [];\n }\n },\n\n /**\n * The set of excluded elements where the key is the `localName`\n * of the element that will be ignored from the item list.\n *\n * @default {template: 1}\n */\n _excludedLocalNames: {\n type: Object,\n value: function() {\n return {\n 'template': 1,\n 'dom-bind': 1,\n 'dom-if': 1,\n 'dom-repeat': 1,\n };\n }\n }\n },\n\n observers: [\n '_updateAttrForSelected(attrForSelected)',\n '_updateSelected(selected)',\n '_checkFallback(fallbackSelection)'\n ],\n\n created: function() {\n this._bindFilterItem = this._filterItem.bind(this);\n this._selection = new IronSelection(this._applySelection.bind(this));\n },\n\n attached: function() {\n this._observer = this._observeItems(this);\n this._addListener(this.activateEvent);\n },\n\n detached: function() {\n if (this._observer) {\n dom(this).unobserveNodes(this._observer);\n }\n this._removeListener(this.activateEvent);\n },\n\n /**\n * Returns the index of the given item.\n *\n * @method indexOf\n * @param {Object} item\n * @returns Returns the index of the item\n */\n indexOf: function(item) {\n return this.items ? this.items.indexOf(item) : -1;\n },\n\n /**\n * Selects the given value.\n *\n * @method select\n * @param {string|number} value the value to select.\n */\n select: function(value) {\n this.selected = value;\n },\n\n /**\n * Selects the previous item.\n *\n * @method selectPrevious\n */\n selectPrevious: function() {\n var length = this.items.length;\n var index = length - 1;\n if (this.selected !== undefined) {\n index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length;\n }\n this.selected = this._indexToValue(index);\n },\n\n /**\n * Selects the next item.\n *\n * @method selectNext\n */\n selectNext: function() {\n var index = 0;\n if (this.selected !== undefined) {\n index =\n (Number(this._valueToIndex(this.selected)) + 1) % this.items.length;\n }\n this.selected = this._indexToValue(index);\n },\n\n /**\n * Selects the item at the given index.\n *\n * @method selectIndex\n */\n selectIndex: function(index) {\n this.select(this._indexToValue(index));\n },\n\n /**\n * Force a synchronous update of the `items` property.\n *\n * NOTE: Consider listening for the `iron-items-changed` event to respond to\n * updates to the set of selectable items after updates to the DOM list and\n * selection state have been made.\n *\n * WARNING: If you are using this method, you should probably consider an\n * alternate approach. Synchronously querying for items is potentially\n * slow for many use cases. The `items` property will update asynchronously\n * on its own to reflect selectable items in the DOM.\n */\n forceSynchronousItemUpdate: function() {\n if (this._observer && typeof this._observer.flush === 'function') {\n // NOTE(bicknellr): `dom.flush` above is no longer sufficient to trigger\n // `observeNodes` callbacks. Polymer 2.x returns an object from\n // `observeNodes` with a `flush` that synchronously gives the callback any\n // pending MutationRecords (retrieved with `takeRecords`). Any case where\n // ShadyDOM flushes were expected to synchronously trigger item updates\n // will now require calling `forceSynchronousItemUpdate`.\n this._observer.flush();\n } else {\n this._updateItems();\n }\n },\n\n // UNUSED, FOR API COMPATIBILITY\n get _shouldUpdateSelection() {\n return this.selected != null;\n },\n\n _checkFallback: function() {\n this._updateSelected();\n },\n\n _addListener: function(eventName) {\n this.listen(this, eventName, '_activateHandler');\n },\n\n _removeListener: function(eventName) {\n this.unlisten(this, eventName, '_activateHandler');\n },\n\n _activateEventChanged: function(eventName, old) {\n this._removeListener(old);\n this._addListener(eventName);\n },\n\n _updateItems: function() {\n var nodes = dom(this).queryDistributedElements(this.selectable || '*');\n nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);\n this._setItems(nodes);\n },\n\n _updateAttrForSelected: function() {\n if (this.selectedItem) {\n this.selected = this._valueForItem(this.selectedItem);\n }\n },\n\n _updateSelected: function() {\n this._selectSelected(this.selected);\n },\n\n _selectSelected: function(selected) {\n if (!this.items) {\n return;\n }\n\n var item = this._valueToItem(this.selected);\n if (item) {\n this._selection.select(item);\n } else {\n this._selection.clear();\n }\n // Check for items, since this array is populated only when attached\n // Since Number(0) is falsy, explicitly check for undefined\n if (this.fallbackSelection && this.items.length &&\n (this._selection.get() === undefined)) {\n this.selected = this.fallbackSelection;\n }\n },\n\n _filterItem: function(node) {\n return !this._excludedLocalNames[node.localName];\n },\n\n _valueToItem: function(value) {\n return (value == null) ? null : this.items[this._valueToIndex(value)];\n },\n\n _valueToIndex: function(value) {\n if (this.attrForSelected) {\n for (var i = 0, item; item = this.items[i]; i++) {\n if (this._valueForItem(item) == value) {\n return i;\n }\n }\n } else {\n return Number(value);\n }\n },\n\n _indexToValue: function(index) {\n if (this.attrForSelected) {\n var item = this.items[index];\n if (item) {\n return this._valueForItem(item);\n }\n } else {\n return index;\n }\n },\n\n _valueForItem: function(item) {\n if (!item) {\n return null;\n }\n if (!this.attrForSelected) {\n var i = this.indexOf(item);\n return i === -1 ? null : i;\n }\n var propValue = item[dashToCamelCase(this.attrForSelected)];\n return propValue != undefined ? propValue :\n item.getAttribute(this.attrForSelected);\n },\n\n _applySelection: function(item, isSelected) {\n if (this.selectedClass) {\n this.toggleClass(this.selectedClass, isSelected, item);\n }\n if (this.selectedAttribute) {\n this.toggleAttribute(this.selectedAttribute, isSelected, item);\n }\n this._selectionChange();\n this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item});\n },\n\n _selectionChange: function() {\n this._setSelectedItem(this._selection.get());\n },\n\n // observe items change under the given node.\n _observeItems: function(node) {\n return dom(node).observeNodes(function(mutation) {\n this._updateItems();\n this._updateSelected();\n\n // Let other interested parties know about the change so that\n // we don't have to recreate mutation observers everywhere.\n this.fire(\n 'iron-items-changed', mutation, {bubbles: false, cancelable: false});\n });\n },\n\n _activateHandler: function(e) {\n var t = e.target;\n var items = this.items;\n while (t && t != this) {\n var i = items.indexOf(t);\n if (i >= 0) {\n var value = this._indexToValue(i);\n this._itemActivate(value, t);\n return;\n }\n t = t.parentNode;\n }\n },\n\n _itemActivate: function(value, item) {\n if (!this.fire('iron-activate', {selected: value, item: item}, {\n cancelable: true\n })\n .defaultPrevented) {\n this.select(value);\n }\n }\n\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\n/**\n * `NeonAnimatableBehavior` is implemented by elements containing\n * animations for use with elements implementing\n * `NeonAnimationRunnerBehavior`.\n * @polymerBehavior\n */\nexport const NeonAnimatableBehavior = {\n\n properties: {\n\n /**\n * Animation configuration. See README for more info.\n */\n animationConfig: {type: Object},\n\n /**\n * Convenience property for setting an 'entry' animation. Do not set\n * `animationConfig.entry` manually if using this. The animated node is set\n * to `this` if using this property.\n */\n entryAnimation: {\n observer: '_entryAnimationChanged',\n type: String,\n },\n\n /**\n * Convenience property for setting an 'exit' animation. Do not set\n * `animationConfig.exit` manually if using this. The animated node is set\n * to `this` if using this property.\n */\n exitAnimation: {\n observer: '_exitAnimationChanged',\n type: String,\n },\n\n },\n\n _entryAnimationChanged: function() {\n this.animationConfig = this.animationConfig || {};\n this.animationConfig['entry'] = [{name: this.entryAnimation, node: this}];\n },\n\n _exitAnimationChanged: function() {\n this.animationConfig = this.animationConfig || {};\n this.animationConfig['exit'] = [{name: this.exitAnimation, node: this}];\n },\n\n _copyProperties: function(config1, config2) {\n // shallowly copy properties from config2 to config1\n for (var property in config2) {\n config1[property] = config2[property];\n }\n },\n\n _cloneConfig: function(config) {\n var clone = {isClone: true};\n this._copyProperties(clone, config);\n return clone;\n },\n\n _getAnimationConfigRecursive: function(type, map, allConfigs) {\n if (!this.animationConfig) {\n return;\n }\n\n if (this.animationConfig.value &&\n typeof this.animationConfig.value === 'function') {\n this._warn(this._logf(\n 'playAnimation',\n 'Please put \\'animationConfig\\' inside of your components \\'properties\\' object instead of outside of it.'));\n return;\n }\n\n // type is optional\n var thisConfig;\n if (type) {\n thisConfig = this.animationConfig[type];\n } else {\n thisConfig = this.animationConfig;\n }\n\n if (!Array.isArray(thisConfig)) {\n thisConfig = [thisConfig];\n }\n\n // iterate animations and recurse to process configurations from child nodes\n if (thisConfig) {\n for (var config, index = 0; config = thisConfig[index]; index++) {\n if (config.animatable) {\n config.animatable._getAnimationConfigRecursive(\n config.type || type, map, allConfigs);\n } else {\n if (config.id) {\n var cachedConfig = map[config.id];\n if (cachedConfig) {\n // merge configurations with the same id, making a clone lazily\n if (!cachedConfig.isClone) {\n map[config.id] = this._cloneConfig(cachedConfig);\n cachedConfig = map[config.id];\n }\n this._copyProperties(cachedConfig, config);\n } else {\n // put any configs with an id into a map\n map[config.id] = config;\n }\n } else {\n allConfigs.push(config);\n }\n }\n }\n }\n },\n\n /**\n * An element implementing `NeonAnimationRunnerBehavior` calls this\n * method to configure an animation with an optional type. Elements\n * implementing `NeonAnimatableBehavior` should define the property\n * `animationConfig`, which is either a configuration object or a map of\n * animation type to array of configuration objects.\n */\n getAnimationConfig: function(type) {\n var map = {};\n var allConfigs = [];\n this._getAnimationConfigRecursive(type, map, allConfigs);\n // append the configurations saved in the map to the array\n for (var key in map) {\n allConfigs.push(map[key]);\n }\n return allConfigs;\n }\n\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {NeonAnimatableBehavior} from './neon-animatable-behavior.js';\n\n/**\n * `NeonAnimationRunnerBehavior` adds a method to run animations.\n *\n * @polymerBehavior NeonAnimationRunnerBehavior\n */\nexport const NeonAnimationRunnerBehaviorImpl = {\n\n _configureAnimations: function(configs) {\n var results = [];\n var resultsToPlay = [];\n\n if (configs.length > 0) {\n for (let config, index = 0; config = configs[index]; index++) {\n let neonAnimation = document.createElement(config.name);\n // is this element actually a neon animation?\n if (neonAnimation.isNeonAnimation) {\n let result = null;\n // Closure compiler does not work well with a try / catch here.\n // .configure needs to be explicitly defined\n if (!neonAnimation.configure) {\n /**\n * @param {Object} config\n * @return {AnimationEffectReadOnly}\n */\n neonAnimation.configure = function(config) {\n return null;\n }\n }\n\n result = neonAnimation.configure(config);\n resultsToPlay.push({\n result: result,\n config: config,\n neonAnimation: neonAnimation,\n });\n } else {\n console.warn(this.is + ':', config.name, 'not found!');\n }\n }\n }\n\n for (var i = 0; i < resultsToPlay.length; i++) {\n let result = resultsToPlay[i].result;\n let config = resultsToPlay[i].config;\n let neonAnimation = resultsToPlay[i].neonAnimation;\n // configuration or play could fail if polyfills aren't loaded\n try {\n // Check if we have an Effect rather than an Animation\n if (typeof result.cancel != 'function') {\n result = document.timeline.play(result);\n }\n } catch (e) {\n result = null;\n console.warn('Couldnt play', '(', config.name, ').', e);\n }\n\n if (result) {\n results.push({\n neonAnimation: neonAnimation,\n config: config,\n animation: result,\n });\n }\n }\n\n return results;\n },\n\n _shouldComplete: function(activeEntries) {\n var finished = true;\n for (var i = 0; i < activeEntries.length; i++) {\n if (activeEntries[i].animation.playState != 'finished') {\n finished = false;\n break;\n }\n }\n return finished;\n },\n\n _complete: function(activeEntries) {\n for (var i = 0; i < activeEntries.length; i++) {\n activeEntries[i].neonAnimation.complete(activeEntries[i].config);\n }\n for (var i = 0; i < activeEntries.length; i++) {\n activeEntries[i].animation.cancel();\n }\n },\n\n /**\n * Plays an animation with an optional `type`.\n * @param {string=} type\n * @param {!Object=} cookie\n */\n playAnimation: function(type, cookie) {\n var configs = this.getAnimationConfig(type);\n if (!configs) {\n return;\n }\n this._active = this._active || {};\n if (this._active[type]) {\n this._complete(this._active[type]);\n delete this._active[type];\n }\n\n var activeEntries = this._configureAnimations(configs);\n\n if (activeEntries.length == 0) {\n this.fire('neon-animation-finish', cookie, {bubbles: false});\n return;\n }\n\n this._active[type] = activeEntries;\n\n for (var i = 0; i < activeEntries.length; i++) {\n activeEntries[i].animation.onfinish = function() {\n if (this._shouldComplete(activeEntries)) {\n this._complete(activeEntries);\n delete this._active[type];\n this.fire('neon-animation-finish', cookie, {bubbles: false});\n }\n }.bind(this);\n }\n },\n\n /**\n * Cancels the currently running animations.\n */\n cancelAnimation: function() {\n for (var k in this._active) {\n var entries = this._active[k]\n\n for (var j in entries) {\n entries[j].animation.cancel();\n }\n }\n\n this._active = {};\n }\n};\n\n/** @polymerBehavior */\nexport const NeonAnimationRunnerBehavior =\n [NeonAnimatableBehavior, NeonAnimationRunnerBehaviorImpl];\n","/**\n@license\nCopyright (c) 2014 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\nvar Utility = {\n distance: function(x1, y1, x2, y2) {\n var xDelta = (x1 - x2);\n var yDelta = (y1 - y2);\n\n return Math.sqrt(xDelta * xDelta + yDelta * yDelta);\n },\n\n now: window.performance && window.performance.now ?\n window.performance.now.bind(window.performance) :\n Date.now\n};\n\n/**\n * @param {HTMLElement} element\n * @constructor\n */\nfunction ElementMetrics(element) {\n this.element = element;\n this.width = this.boundingRect.width;\n this.height = this.boundingRect.height;\n\n this.size = Math.max(this.width, this.height);\n}\n\nElementMetrics.prototype = {\n get boundingRect() {\n return this.element.getBoundingClientRect();\n },\n\n furthestCornerDistanceFrom: function(x, y) {\n var topLeft = Utility.distance(x, y, 0, 0);\n var topRight = Utility.distance(x, y, this.width, 0);\n var bottomLeft = Utility.distance(x, y, 0, this.height);\n var bottomRight = Utility.distance(x, y, this.width, this.height);\n\n return Math.max(topLeft, topRight, bottomLeft, bottomRight);\n }\n};\n\n/**\n * @param {HTMLElement} element\n * @constructor\n */\nfunction Ripple(element) {\n this.element = element;\n this.color = window.getComputedStyle(element).color;\n\n this.wave = document.createElement('div');\n this.waveContainer = document.createElement('div');\n this.wave.style.backgroundColor = this.color;\n this.wave.classList.add('wave');\n this.waveContainer.classList.add('wave-container');\n dom(this.waveContainer).appendChild(this.wave);\n\n this.resetInteractionState();\n}\n\nRipple.MAX_RADIUS = 300;\n\nRipple.prototype = {\n get recenters() {\n return this.element.recenters;\n },\n\n get center() {\n return this.element.center;\n },\n\n get mouseDownElapsed() {\n var elapsed;\n\n if (!this.mouseDownStart) {\n return 0;\n }\n\n elapsed = Utility.now() - this.mouseDownStart;\n\n if (this.mouseUpStart) {\n elapsed -= this.mouseUpElapsed;\n }\n\n return elapsed;\n },\n\n get mouseUpElapsed() {\n return this.mouseUpStart ? Utility.now() - this.mouseUpStart : 0;\n },\n\n get mouseDownElapsedSeconds() {\n return this.mouseDownElapsed / 1000;\n },\n\n get mouseUpElapsedSeconds() {\n return this.mouseUpElapsed / 1000;\n },\n\n get mouseInteractionSeconds() {\n return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;\n },\n\n get initialOpacity() {\n return this.element.initialOpacity;\n },\n\n get opacityDecayVelocity() {\n return this.element.opacityDecayVelocity;\n },\n\n get radius() {\n var width2 = this.containerMetrics.width * this.containerMetrics.width;\n var height2 = this.containerMetrics.height * this.containerMetrics.height;\n var waveRadius =\n Math.min(Math.sqrt(width2 + height2), Ripple.MAX_RADIUS) * 1.1 + 5;\n\n var duration = 1.1 - 0.2 * (waveRadius / Ripple.MAX_RADIUS);\n var timeNow = this.mouseInteractionSeconds / duration;\n var size = waveRadius * (1 - Math.pow(80, -timeNow));\n\n return Math.abs(size);\n },\n\n get opacity() {\n if (!this.mouseUpStart) {\n return this.initialOpacity;\n }\n\n return Math.max(\n 0,\n this.initialOpacity -\n this.mouseUpElapsedSeconds * this.opacityDecayVelocity);\n },\n\n get outerOpacity() {\n // Linear increase in background opacity, capped at the opacity\n // of the wavefront (waveOpacity).\n var outerOpacity = this.mouseUpElapsedSeconds * 0.3;\n var waveOpacity = this.opacity;\n\n return Math.max(0, Math.min(outerOpacity, waveOpacity));\n },\n\n get isOpacityFullyDecayed() {\n return this.opacity < 0.01 &&\n this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);\n },\n\n get isRestingAtMaxRadius() {\n return this.opacity >= this.initialOpacity &&\n this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);\n },\n\n get isAnimationComplete() {\n return this.mouseUpStart ? this.isOpacityFullyDecayed :\n this.isRestingAtMaxRadius;\n },\n\n get translationFraction() {\n return Math.min(\n 1, this.radius / this.containerMetrics.size * 2 / Math.sqrt(2));\n },\n\n get xNow() {\n if (this.xEnd) {\n return this.xStart + this.translationFraction * (this.xEnd - this.xStart);\n }\n\n return this.xStart;\n },\n\n get yNow() {\n if (this.yEnd) {\n return this.yStart + this.translationFraction * (this.yEnd - this.yStart);\n }\n\n return this.yStart;\n },\n\n get isMouseDown() {\n return this.mouseDownStart && !this.mouseUpStart;\n },\n\n resetInteractionState: function() {\n this.maxRadius = 0;\n this.mouseDownStart = 0;\n this.mouseUpStart = 0;\n\n this.xStart = 0;\n this.yStart = 0;\n this.xEnd = 0;\n this.yEnd = 0;\n this.slideDistance = 0;\n\n this.containerMetrics = new ElementMetrics(this.element);\n },\n\n draw: function() {\n var scale;\n var dx;\n var dy;\n\n this.wave.style.opacity = this.opacity;\n\n scale = this.radius / (this.containerMetrics.size / 2);\n dx = this.xNow - (this.containerMetrics.width / 2);\n dy = this.yNow - (this.containerMetrics.height / 2);\n\n\n // 2d transform for safari because of border-radius and overflow:hidden\n // clipping bug. https://bugs.webkit.org/show_bug.cgi?id=98538\n this.waveContainer.style.webkitTransform =\n 'translate(' + dx + 'px, ' + dy + 'px)';\n this.waveContainer.style.transform =\n 'translate3d(' + dx + 'px, ' + dy + 'px, 0)';\n this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')';\n this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)';\n },\n\n /** @param {Event=} event */\n downAction: function(event) {\n var xCenter = this.containerMetrics.width / 2;\n var yCenter = this.containerMetrics.height / 2;\n\n this.resetInteractionState();\n this.mouseDownStart = Utility.now();\n\n if (this.center) {\n this.xStart = xCenter;\n this.yStart = yCenter;\n this.slideDistance =\n Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd);\n } else {\n this.xStart = event ?\n event.detail.x - this.containerMetrics.boundingRect.left :\n this.containerMetrics.width / 2;\n this.yStart = event ?\n event.detail.y - this.containerMetrics.boundingRect.top :\n this.containerMetrics.height / 2;\n }\n\n if (this.recenters) {\n this.xEnd = xCenter;\n this.yEnd = yCenter;\n this.slideDistance =\n Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd);\n }\n\n this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(\n this.xStart, this.yStart);\n\n this.waveContainer.style.top =\n (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px';\n this.waveContainer.style.left =\n (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';\n\n this.waveContainer.style.width = this.containerMetrics.size + 'px';\n this.waveContainer.style.height = this.containerMetrics.size + 'px';\n },\n\n /** @param {Event=} event */\n upAction: function(event) {\n if (!this.isMouseDown) {\n return;\n }\n\n this.mouseUpStart = Utility.now();\n },\n\n remove: function() {\n dom(this.waveContainer.parentNode).removeChild(this.waveContainer);\n }\n};\n\n/**\nMaterial design: [Surface\nreaction](https://www.google.com/design/spec/animation/responsive-interaction.html#responsive-interaction-surface-reaction)\n\n`paper-ripple` provides a visual effect that other paper elements can\nuse to simulate a rippling effect emanating from the point of contact. The\neffect can be visualized as a concentric circle with motion.\n\nExample:\n\n \n\nNote, it's important that the parent container of the ripple be relative\nposition, otherwise the ripple will emanate outside of the desired container.\n\n`paper-ripple` listens to \"mousedown\" and \"mouseup\" events so it would display\nripple effect when touches on it. You can also defeat the default behavior and\nmanually route the down and up actions to the ripple element. Note that it is\nimportant if you call `downAction()` you will have to make sure to call\n`upAction()` so that `paper-ripple` would end the animation loop.\n\nExample:\n\n \n ...\n downAction: function(e) {\n this.$.ripple.downAction(e.detail);\n },\n upAction: function(e) {\n this.$.ripple.upAction();\n }\n\nStyling ripple effect:\n\n Use CSS color property to style the ripple:\n\n paper-ripple {\n color: #4285f4;\n }\n\n Note that CSS color property is inherited so it is not required to set it on\n the `paper-ripple` element directly.\n\nBy default, the ripple is centered on the point of contact. Apply the\n`recenters` attribute to have the ripple grow toward the center of its\ncontainer.\n\n \n\nYou can also center the ripple inside its container from the start.\n\n \n\nApply `circle` class to make the rippling effect within a circle.\n\n \n\n@group Paper Elements\n@element paper-ripple\n@hero hero.svg\n@demo demo/index.html\n*/\nPolymer({\n _template: html`\n \n\n \n \n`,\n\n is: 'paper-ripple',\n behaviors: [IronA11yKeysBehavior],\n\n properties: {\n /**\n * The initial opacity set on the wave.\n *\n * @attribute initialOpacity\n * @type number\n * @default 0.25\n */\n initialOpacity: {type: Number, value: 0.25},\n\n /**\n * How fast (opacity per second) the wave fades out.\n *\n * @attribute opacityDecayVelocity\n * @type number\n * @default 0.8\n */\n opacityDecayVelocity: {type: Number, value: 0.8},\n\n /**\n * If true, ripples will exhibit a gravitational pull towards\n * the center of their container as they fade away.\n *\n * @attribute recenters\n * @type boolean\n * @default false\n */\n recenters: {type: Boolean, value: false},\n\n /**\n * If true, ripples will center inside its container\n *\n * @attribute recenters\n * @type boolean\n * @default false\n */\n center: {type: Boolean, value: false},\n\n /**\n * A list of the visual ripples.\n *\n * @attribute ripples\n * @type Array\n * @default []\n */\n ripples: {\n type: Array,\n value: function() {\n return [];\n }\n },\n\n /**\n * True when there are visible ripples animating within the\n * element.\n */\n animating:\n {type: Boolean, readOnly: true, reflectToAttribute: true, value: false},\n\n /**\n * If true, the ripple will remain in the \"down\" state until `holdDown`\n * is set to false again.\n */\n holdDown: {type: Boolean, value: false, observer: '_holdDownChanged'},\n\n /**\n * If true, the ripple will not generate a ripple effect\n * via pointer interaction.\n * Calling ripple's imperative api like `simulatedRipple` will\n * still generate the ripple effect.\n */\n noink: {type: Boolean, value: false},\n\n _animating: {type: Boolean},\n\n _boundAnimate: {\n type: Function,\n value: function() {\n return this.animate.bind(this);\n }\n }\n },\n\n get target() {\n return this.keyEventTarget;\n },\n\n /**\n * @type {!Object}\n */\n keyBindings: {\n 'enter:keydown': '_onEnterKeydown',\n 'space:keydown': '_onSpaceKeydown',\n 'space:keyup': '_onSpaceKeyup'\n },\n\n attached: function() {\n // Set up a11yKeysBehavior to listen to key events on the target,\n // so that space and enter activate the ripple even if the target doesn't\n // handle key events. The key handlers deal with `noink` themselves.\n if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE\n this.keyEventTarget = dom(this).getOwnerRoot().host;\n } else {\n this.keyEventTarget = this.parentNode;\n }\n var keyEventTarget = /** @type {!EventTarget} */ (this.keyEventTarget);\n this.listen(keyEventTarget, 'up', 'uiUpAction');\n this.listen(keyEventTarget, 'down', 'uiDownAction');\n },\n\n detached: function() {\n this.unlisten(this.keyEventTarget, 'up', 'uiUpAction');\n this.unlisten(this.keyEventTarget, 'down', 'uiDownAction');\n this.keyEventTarget = null;\n },\n\n get shouldKeepAnimating() {\n for (var index = 0; index < this.ripples.length; ++index) {\n if (!this.ripples[index].isAnimationComplete) {\n return true;\n }\n }\n\n return false;\n },\n\n simulatedRipple: function() {\n this.downAction(null);\n\n // Please see polymer/polymer#1305\n this.async(function() {\n this.upAction();\n }, 1);\n },\n\n /**\n * Provokes a ripple down effect via a UI event,\n * respecting the `noink` property.\n * @param {Event=} event\n */\n uiDownAction: function(event) {\n if (!this.noink) {\n this.downAction(event);\n }\n },\n\n /**\n * Provokes a ripple down effect via a UI event,\n * *not* respecting the `noink` property.\n * @param {Event=} event\n */\n downAction: function(event) {\n if (this.holdDown && this.ripples.length > 0) {\n return;\n }\n\n var ripple = this.addRipple();\n\n ripple.downAction(event);\n\n if (!this._animating) {\n this._animating = true;\n this.animate();\n }\n },\n\n /**\n * Provokes a ripple up effect via a UI event,\n * respecting the `noink` property.\n * @param {Event=} event\n */\n uiUpAction: function(event) {\n if (!this.noink) {\n this.upAction(event);\n }\n },\n\n /**\n * Provokes a ripple up effect via a UI event,\n * *not* respecting the `noink` property.\n * @param {Event=} event\n */\n upAction: function(event) {\n if (this.holdDown) {\n return;\n }\n\n this.ripples.forEach(function(ripple) {\n ripple.upAction(event);\n });\n\n this._animating = true;\n this.animate();\n },\n\n onAnimationComplete: function() {\n this._animating = false;\n this.$.background.style.backgroundColor = null;\n this.fire('transitionend');\n },\n\n addRipple: function() {\n var ripple = new Ripple(this);\n\n dom(this.$.waves).appendChild(ripple.waveContainer);\n this.$.background.style.backgroundColor = ripple.color;\n this.ripples.push(ripple);\n\n this._setAnimating(true);\n\n return ripple;\n },\n\n removeRipple: function(ripple) {\n var rippleIndex = this.ripples.indexOf(ripple);\n\n if (rippleIndex < 0) {\n return;\n }\n\n this.ripples.splice(rippleIndex, 1);\n\n ripple.remove();\n\n if (!this.ripples.length) {\n this._setAnimating(false);\n }\n },\n\n /**\n * Deprecated. Please use animateRipple() instead.\n *\n * This method name conflicts with Element#animate().\n * https://developer.mozilla.org/en-US/docs/Web/API/Element/animate.\n *\n * @suppress {checkTypes}\n */\n animate: function() {\n if (!this._animating) {\n return;\n }\n var index;\n var ripple;\n\n for (index = 0; index < this.ripples.length; ++index) {\n ripple = this.ripples[index];\n\n ripple.draw();\n\n this.$.background.style.opacity = ripple.outerOpacity;\n\n if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {\n this.removeRipple(ripple);\n }\n }\n\n if (!this.shouldKeepAnimating && this.ripples.length === 0) {\n this.onAnimationComplete();\n } else {\n window.requestAnimationFrame(this._boundAnimate);\n }\n },\n\n /**\n * An alias for animate() whose name does not conflict with the platform\n * Element.animate() method.\n */\n animateRipple: function() {\n return this.animate();\n },\n\n _onEnterKeydown: function() {\n this.uiDownAction();\n this.async(this.uiUpAction, 1);\n },\n\n _onSpaceKeydown: function() {\n this.uiDownAction();\n },\n\n _onSpaceKeyup: function() {\n this.uiUpAction();\n },\n\n // note: holdDown does not respect noink since it can be a focus based\n // effect.\n _holdDownChanged: function(newVal, oldVal) {\n if (oldVal === undefined) {\n return;\n }\n if (newVal) {\n this.downAction();\n } else {\n this.upAction();\n }\n }\n\n /**\n Fired when the animation finishes.\n This is useful if you want to wait until\n the ripple animation finishes to perform some action.\n\n @event transitionend\n @param {{node: Object}} detail Contains the animated node.\n */\n});\n","/**\n * @license\n * Copyright 2016 Google Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nimport * as tslib_1 from \"tslib\";\nimport { MDCFoundation } from '@material/base/foundation';\nimport { cssClasses, numbers, strings } from './constants';\nimport { getNormalizedEventCoords } from './util';\n// Activation events registered on the root element of each instance for activation\nvar ACTIVATION_EVENT_TYPES = [\n 'touchstart', 'pointerdown', 'mousedown', 'keydown',\n];\n// Deactivation events registered on documentElement when a pointer-related down event occurs\nvar POINTER_DEACTIVATION_EVENT_TYPES = [\n 'touchend', 'pointerup', 'mouseup', 'contextmenu',\n];\n// simultaneous nested activations\nvar activatedTargets = [];\nvar MDCRippleFoundation = /** @class */ (function (_super) {\n tslib_1.__extends(MDCRippleFoundation, _super);\n function MDCRippleFoundation(adapter) {\n var _this = _super.call(this, tslib_1.__assign({}, MDCRippleFoundation.defaultAdapter, adapter)) || this;\n _this.activationAnimationHasEnded_ = false;\n _this.activationTimer_ = 0;\n _this.fgDeactivationRemovalTimer_ = 0;\n _this.fgScale_ = '0';\n _this.frame_ = { width: 0, height: 0 };\n _this.initialSize_ = 0;\n _this.layoutFrame_ = 0;\n _this.maxRadius_ = 0;\n _this.unboundedCoords_ = { left: 0, top: 0 };\n _this.activationState_ = _this.defaultActivationState_();\n _this.activationTimerCallback_ = function () {\n _this.activationAnimationHasEnded_ = true;\n _this.runDeactivationUXLogicIfReady_();\n };\n _this.activateHandler_ = function (e) { return _this.activate_(e); };\n _this.deactivateHandler_ = function () { return _this.deactivate_(); };\n _this.focusHandler_ = function () { return _this.handleFocus(); };\n _this.blurHandler_ = function () { return _this.handleBlur(); };\n _this.resizeHandler_ = function () { return _this.layout(); };\n return _this;\n }\n Object.defineProperty(MDCRippleFoundation, \"cssClasses\", {\n get: function () {\n return cssClasses;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(MDCRippleFoundation, \"strings\", {\n get: function () {\n return strings;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(MDCRippleFoundation, \"numbers\", {\n get: function () {\n return numbers;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(MDCRippleFoundation, \"defaultAdapter\", {\n get: function () {\n return {\n addClass: function () { return undefined; },\n browserSupportsCssVars: function () { return true; },\n computeBoundingRect: function () { return ({ top: 0, right: 0, bottom: 0, left: 0, width: 0, height: 0 }); },\n containsEventTarget: function () { return true; },\n deregisterDocumentInteractionHandler: function () { return undefined; },\n deregisterInteractionHandler: function () { return undefined; },\n deregisterResizeHandler: function () { return undefined; },\n getWindowPageOffset: function () { return ({ x: 0, y: 0 }); },\n isSurfaceActive: function () { return true; },\n isSurfaceDisabled: function () { return true; },\n isUnbounded: function () { return true; },\n registerDocumentInteractionHandler: function () { return undefined; },\n registerInteractionHandler: function () { return undefined; },\n registerResizeHandler: function () { return undefined; },\n removeClass: function () { return undefined; },\n updateCssVariable: function () { return undefined; },\n };\n },\n enumerable: true,\n configurable: true\n });\n MDCRippleFoundation.prototype.init = function () {\n var _this = this;\n var supportsPressRipple = this.supportsPressRipple_();\n this.registerRootHandlers_(supportsPressRipple);\n if (supportsPressRipple) {\n var _a = MDCRippleFoundation.cssClasses, ROOT_1 = _a.ROOT, UNBOUNDED_1 = _a.UNBOUNDED;\n requestAnimationFrame(function () {\n _this.adapter_.addClass(ROOT_1);\n if (_this.adapter_.isUnbounded()) {\n _this.adapter_.addClass(UNBOUNDED_1);\n // Unbounded ripples need layout logic applied immediately to set coordinates for both shade and ripple\n _this.layoutInternal_();\n }\n });\n }\n };\n MDCRippleFoundation.prototype.destroy = function () {\n var _this = this;\n if (this.supportsPressRipple_()) {\n if (this.activationTimer_) {\n clearTimeout(this.activationTimer_);\n this.activationTimer_ = 0;\n this.adapter_.removeClass(MDCRippleFoundation.cssClasses.FG_ACTIVATION);\n }\n if (this.fgDeactivationRemovalTimer_) {\n clearTimeout(this.fgDeactivationRemovalTimer_);\n this.fgDeactivationRemovalTimer_ = 0;\n this.adapter_.removeClass(MDCRippleFoundation.cssClasses.FG_DEACTIVATION);\n }\n var _a = MDCRippleFoundation.cssClasses, ROOT_2 = _a.ROOT, UNBOUNDED_2 = _a.UNBOUNDED;\n requestAnimationFrame(function () {\n _this.adapter_.removeClass(ROOT_2);\n _this.adapter_.removeClass(UNBOUNDED_2);\n _this.removeCssVars_();\n });\n }\n this.deregisterRootHandlers_();\n this.deregisterDeactivationHandlers_();\n };\n /**\n * @param evt Optional event containing position information.\n */\n MDCRippleFoundation.prototype.activate = function (evt) {\n this.activate_(evt);\n };\n MDCRippleFoundation.prototype.deactivate = function () {\n this.deactivate_();\n };\n MDCRippleFoundation.prototype.layout = function () {\n var _this = this;\n if (this.layoutFrame_) {\n cancelAnimationFrame(this.layoutFrame_);\n }\n this.layoutFrame_ = requestAnimationFrame(function () {\n _this.layoutInternal_();\n _this.layoutFrame_ = 0;\n });\n };\n MDCRippleFoundation.prototype.setUnbounded = function (unbounded) {\n var UNBOUNDED = MDCRippleFoundation.cssClasses.UNBOUNDED;\n if (unbounded) {\n this.adapter_.addClass(UNBOUNDED);\n }\n else {\n this.adapter_.removeClass(UNBOUNDED);\n }\n };\n MDCRippleFoundation.prototype.handleFocus = function () {\n var _this = this;\n requestAnimationFrame(function () {\n return _this.adapter_.addClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);\n });\n };\n MDCRippleFoundation.prototype.handleBlur = function () {\n var _this = this;\n requestAnimationFrame(function () {\n return _this.adapter_.removeClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);\n });\n };\n /**\n * We compute this property so that we are not querying information about the client\n * until the point in time where the foundation requests it. This prevents scenarios where\n * client-side feature-detection may happen too early, such as when components are rendered on the server\n * and then initialized at mount time on the client.\n */\n MDCRippleFoundation.prototype.supportsPressRipple_ = function () {\n return this.adapter_.browserSupportsCssVars();\n };\n MDCRippleFoundation.prototype.defaultActivationState_ = function () {\n return {\n activationEvent: undefined,\n hasDeactivationUXRun: false,\n isActivated: false,\n isProgrammatic: false,\n wasActivatedByPointer: false,\n wasElementMadeActive: false,\n };\n };\n /**\n * supportsPressRipple Passed from init to save a redundant function call\n */\n MDCRippleFoundation.prototype.registerRootHandlers_ = function (supportsPressRipple) {\n var _this = this;\n if (supportsPressRipple) {\n ACTIVATION_EVENT_TYPES.forEach(function (evtType) {\n _this.adapter_.registerInteractionHandler(evtType, _this.activateHandler_);\n });\n if (this.adapter_.isUnbounded()) {\n this.adapter_.registerResizeHandler(this.resizeHandler_);\n }\n }\n this.adapter_.registerInteractionHandler('focus', this.focusHandler_);\n this.adapter_.registerInteractionHandler('blur', this.blurHandler_);\n };\n MDCRippleFoundation.prototype.registerDeactivationHandlers_ = function (evt) {\n var _this = this;\n if (evt.type === 'keydown') {\n this.adapter_.registerInteractionHandler('keyup', this.deactivateHandler_);\n }\n else {\n POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {\n _this.adapter_.registerDocumentInteractionHandler(evtType, _this.deactivateHandler_);\n });\n }\n };\n MDCRippleFoundation.prototype.deregisterRootHandlers_ = function () {\n var _this = this;\n ACTIVATION_EVENT_TYPES.forEach(function (evtType) {\n _this.adapter_.deregisterInteractionHandler(evtType, _this.activateHandler_);\n });\n this.adapter_.deregisterInteractionHandler('focus', this.focusHandler_);\n this.adapter_.deregisterInteractionHandler('blur', this.blurHandler_);\n if (this.adapter_.isUnbounded()) {\n this.adapter_.deregisterResizeHandler(this.resizeHandler_);\n }\n };\n MDCRippleFoundation.prototype.deregisterDeactivationHandlers_ = function () {\n var _this = this;\n this.adapter_.deregisterInteractionHandler('keyup', this.deactivateHandler_);\n POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {\n _this.adapter_.deregisterDocumentInteractionHandler(evtType, _this.deactivateHandler_);\n });\n };\n MDCRippleFoundation.prototype.removeCssVars_ = function () {\n var _this = this;\n var rippleStrings = MDCRippleFoundation.strings;\n var keys = Object.keys(rippleStrings);\n keys.forEach(function (key) {\n if (key.indexOf('VAR_') === 0) {\n _this.adapter_.updateCssVariable(rippleStrings[key], null);\n }\n });\n };\n MDCRippleFoundation.prototype.activate_ = function (evt) {\n var _this = this;\n if (this.adapter_.isSurfaceDisabled()) {\n return;\n }\n var activationState = this.activationState_;\n if (activationState.isActivated) {\n return;\n }\n // Avoid reacting to follow-on events fired by touch device after an already-processed user interaction\n var previousActivationEvent = this.previousActivationEvent_;\n var isSameInteraction = previousActivationEvent && evt !== undefined && previousActivationEvent.type !== evt.type;\n if (isSameInteraction) {\n return;\n }\n activationState.isActivated = true;\n activationState.isProgrammatic = evt === undefined;\n activationState.activationEvent = evt;\n activationState.wasActivatedByPointer = activationState.isProgrammatic ? false : evt !== undefined && (evt.type === 'mousedown' || evt.type === 'touchstart' || evt.type === 'pointerdown');\n var hasActivatedChild = evt !== undefined && activatedTargets.length > 0 && activatedTargets.some(function (target) { return _this.adapter_.containsEventTarget(target); });\n if (hasActivatedChild) {\n // Immediately reset activation state, while preserving logic that prevents touch follow-on events\n this.resetActivationState_();\n return;\n }\n if (evt !== undefined) {\n activatedTargets.push(evt.target);\n this.registerDeactivationHandlers_(evt);\n }\n activationState.wasElementMadeActive = this.checkElementMadeActive_(evt);\n if (activationState.wasElementMadeActive) {\n this.animateActivation_();\n }\n requestAnimationFrame(function () {\n // Reset array on next frame after the current event has had a chance to bubble to prevent ancestor ripples\n activatedTargets = [];\n if (!activationState.wasElementMadeActive\n && evt !== undefined\n && (evt.key === ' ' || evt.keyCode === 32)) {\n // If space was pressed, try again within an rAF call to detect :active, because different UAs report\n // active states inconsistently when they're called within event handling code:\n // - https://bugs.chromium.org/p/chromium/issues/detail?id=635971\n // - https://bugzilla.mozilla.org/show_bug.cgi?id=1293741\n // We try first outside rAF to support Edge, which does not exhibit this problem, but will crash if a CSS\n // variable is set within a rAF callback for a submit button interaction (#2241).\n activationState.wasElementMadeActive = _this.checkElementMadeActive_(evt);\n if (activationState.wasElementMadeActive) {\n _this.animateActivation_();\n }\n }\n if (!activationState.wasElementMadeActive) {\n // Reset activation state immediately if element was not made active.\n _this.activationState_ = _this.defaultActivationState_();\n }\n });\n };\n MDCRippleFoundation.prototype.checkElementMadeActive_ = function (evt) {\n return (evt !== undefined && evt.type === 'keydown') ? this.adapter_.isSurfaceActive() : true;\n };\n MDCRippleFoundation.prototype.animateActivation_ = function () {\n var _this = this;\n var _a = MDCRippleFoundation.strings, VAR_FG_TRANSLATE_START = _a.VAR_FG_TRANSLATE_START, VAR_FG_TRANSLATE_END = _a.VAR_FG_TRANSLATE_END;\n var _b = MDCRippleFoundation.cssClasses, FG_DEACTIVATION = _b.FG_DEACTIVATION, FG_ACTIVATION = _b.FG_ACTIVATION;\n var DEACTIVATION_TIMEOUT_MS = MDCRippleFoundation.numbers.DEACTIVATION_TIMEOUT_MS;\n this.layoutInternal_();\n var translateStart = '';\n var translateEnd = '';\n if (!this.adapter_.isUnbounded()) {\n var _c = this.getFgTranslationCoordinates_(), startPoint = _c.startPoint, endPoint = _c.endPoint;\n translateStart = startPoint.x + \"px, \" + startPoint.y + \"px\";\n translateEnd = endPoint.x + \"px, \" + endPoint.y + \"px\";\n }\n this.adapter_.updateCssVariable(VAR_FG_TRANSLATE_START, translateStart);\n this.adapter_.updateCssVariable(VAR_FG_TRANSLATE_END, translateEnd);\n // Cancel any ongoing activation/deactivation animations\n clearTimeout(this.activationTimer_);\n clearTimeout(this.fgDeactivationRemovalTimer_);\n this.rmBoundedActivationClasses_();\n this.adapter_.removeClass(FG_DEACTIVATION);\n // Force layout in order to re-trigger the animation.\n this.adapter_.computeBoundingRect();\n this.adapter_.addClass(FG_ACTIVATION);\n this.activationTimer_ = setTimeout(function () { return _this.activationTimerCallback_(); }, DEACTIVATION_TIMEOUT_MS);\n };\n MDCRippleFoundation.prototype.getFgTranslationCoordinates_ = function () {\n var _a = this.activationState_, activationEvent = _a.activationEvent, wasActivatedByPointer = _a.wasActivatedByPointer;\n var startPoint;\n if (wasActivatedByPointer) {\n startPoint = getNormalizedEventCoords(activationEvent, this.adapter_.getWindowPageOffset(), this.adapter_.computeBoundingRect());\n }\n else {\n startPoint = {\n x: this.frame_.width / 2,\n y: this.frame_.height / 2,\n };\n }\n // Center the element around the start point.\n startPoint = {\n x: startPoint.x - (this.initialSize_ / 2),\n y: startPoint.y - (this.initialSize_ / 2),\n };\n var endPoint = {\n x: (this.frame_.width / 2) - (this.initialSize_ / 2),\n y: (this.frame_.height / 2) - (this.initialSize_ / 2),\n };\n return { startPoint: startPoint, endPoint: endPoint };\n };\n MDCRippleFoundation.prototype.runDeactivationUXLogicIfReady_ = function () {\n var _this = this;\n // This method is called both when a pointing device is released, and when the activation animation ends.\n // The deactivation animation should only run after both of those occur.\n var FG_DEACTIVATION = MDCRippleFoundation.cssClasses.FG_DEACTIVATION;\n var _a = this.activationState_, hasDeactivationUXRun = _a.hasDeactivationUXRun, isActivated = _a.isActivated;\n var activationHasEnded = hasDeactivationUXRun || !isActivated;\n if (activationHasEnded && this.activationAnimationHasEnded_) {\n this.rmBoundedActivationClasses_();\n this.adapter_.addClass(FG_DEACTIVATION);\n this.fgDeactivationRemovalTimer_ = setTimeout(function () {\n _this.adapter_.removeClass(FG_DEACTIVATION);\n }, numbers.FG_DEACTIVATION_MS);\n }\n };\n MDCRippleFoundation.prototype.rmBoundedActivationClasses_ = function () {\n var FG_ACTIVATION = MDCRippleFoundation.cssClasses.FG_ACTIVATION;\n this.adapter_.removeClass(FG_ACTIVATION);\n this.activationAnimationHasEnded_ = false;\n this.adapter_.computeBoundingRect();\n };\n MDCRippleFoundation.prototype.resetActivationState_ = function () {\n var _this = this;\n this.previousActivationEvent_ = this.activationState_.activationEvent;\n this.activationState_ = this.defaultActivationState_();\n // Touch devices may fire additional events for the same interaction within a short time.\n // Store the previous event until it's safe to assume that subsequent events are for new interactions.\n setTimeout(function () { return _this.previousActivationEvent_ = undefined; }, MDCRippleFoundation.numbers.TAP_DELAY_MS);\n };\n MDCRippleFoundation.prototype.deactivate_ = function () {\n var _this = this;\n var activationState = this.activationState_;\n // This can happen in scenarios such as when you have a keyup event that blurs the element.\n if (!activationState.isActivated) {\n return;\n }\n var state = tslib_1.__assign({}, activationState);\n if (activationState.isProgrammatic) {\n requestAnimationFrame(function () { return _this.animateDeactivation_(state); });\n this.resetActivationState_();\n }\n else {\n this.deregisterDeactivationHandlers_();\n requestAnimationFrame(function () {\n _this.activationState_.hasDeactivationUXRun = true;\n _this.animateDeactivation_(state);\n _this.resetActivationState_();\n });\n }\n };\n MDCRippleFoundation.prototype.animateDeactivation_ = function (_a) {\n var wasActivatedByPointer = _a.wasActivatedByPointer, wasElementMadeActive = _a.wasElementMadeActive;\n if (wasActivatedByPointer || wasElementMadeActive) {\n this.runDeactivationUXLogicIfReady_();\n }\n };\n MDCRippleFoundation.prototype.layoutInternal_ = function () {\n var _this = this;\n this.frame_ = this.adapter_.computeBoundingRect();\n var maxDim = Math.max(this.frame_.height, this.frame_.width);\n // Surface diameter is treated differently for unbounded vs. bounded ripples.\n // Unbounded ripple diameter is calculated smaller since the surface is expected to already be padded appropriately\n // to extend the hitbox, and the ripple is expected to meet the edges of the padded hitbox (which is typically\n // square). Bounded ripples, on the other hand, are fully expected to expand beyond the surface's longest diameter\n // (calculated based on the diagonal plus a constant padding), and are clipped at the surface's border via\n // `overflow: hidden`.\n var getBoundedRadius = function () {\n var hypotenuse = Math.sqrt(Math.pow(_this.frame_.width, 2) + Math.pow(_this.frame_.height, 2));\n return hypotenuse + MDCRippleFoundation.numbers.PADDING;\n };\n this.maxRadius_ = this.adapter_.isUnbounded() ? maxDim : getBoundedRadius();\n // Ripple is sized as a fraction of the largest dimension of the surface, then scales up using a CSS scale transform\n this.initialSize_ = Math.floor(maxDim * MDCRippleFoundation.numbers.INITIAL_ORIGIN_SCALE);\n this.fgScale_ = \"\" + this.maxRadius_ / this.initialSize_;\n this.updateLayoutCssVars_();\n };\n MDCRippleFoundation.prototype.updateLayoutCssVars_ = function () {\n var _a = MDCRippleFoundation.strings, VAR_FG_SIZE = _a.VAR_FG_SIZE, VAR_LEFT = _a.VAR_LEFT, VAR_TOP = _a.VAR_TOP, VAR_FG_SCALE = _a.VAR_FG_SCALE;\n this.adapter_.updateCssVariable(VAR_FG_SIZE, this.initialSize_ + \"px\");\n this.adapter_.updateCssVariable(VAR_FG_SCALE, this.fgScale_);\n if (this.adapter_.isUnbounded()) {\n this.unboundedCoords_ = {\n left: Math.round((this.frame_.width / 2) - (this.initialSize_ / 2)),\n top: Math.round((this.frame_.height / 2) - (this.initialSize_ / 2)),\n };\n this.adapter_.updateCssVariable(VAR_LEFT, this.unboundedCoords_.left + \"px\");\n this.adapter_.updateCssVariable(VAR_TOP, this.unboundedCoords_.top + \"px\");\n }\n };\n return MDCRippleFoundation;\n}(MDCFoundation));\nexport { MDCRippleFoundation };\n// tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier.\nexport default MDCRippleFoundation;\n//# sourceMappingURL=foundation.js.map","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nconst $_documentContainer = document.createElement('template');\n$_documentContainer.setAttribute('style', 'display: none;');\n\n$_documentContainer.innerHTML = `\n \n \n \n`;\n\ndocument.head.appendChild($_documentContainer.content);\n\nexport {};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\n/** @polymerBehavior */\nexport const PaperSpinnerBehavior = {\n\n properties: {\n /**\n * Displays the spinner.\n */\n active: {\n type: Boolean,\n value: false,\n reflectToAttribute: true,\n observer: '__activeChanged'\n },\n\n /**\n * Alternative text content for accessibility support.\n * If alt is present, it will add an aria-label whose content matches alt\n * when active. If alt is not present, it will default to 'loading' as the\n * alt value.\n */\n alt: {type: String, value: 'loading', observer: '__altChanged'},\n\n __coolingDown: {type: Boolean, value: false}\n },\n\n __computeContainerClasses: function(active, coolingDown) {\n return [\n active || coolingDown ? 'active' : '',\n coolingDown ? 'cooldown' : ''\n ].join(' ');\n },\n\n __activeChanged: function(active, old) {\n this.__setAriaHidden(!active);\n this.__coolingDown = !active && old;\n },\n\n __altChanged: function(alt) {\n // user-provided `aria-label` takes precedence over prototype default\n if (alt === 'loading') {\n this.alt = this.getAttribute('aria-label') || alt;\n } else {\n this.__setAriaHidden(alt === '');\n this.setAttribute('aria-label', alt);\n }\n },\n\n __setAriaHidden: function(hidden) {\n var attr = 'aria-hidden';\n if (hidden) {\n this.setAttribute(attr, 'true');\n } else {\n this.removeAttribute(attr);\n }\n },\n\n __reset: function() {\n this.active = false;\n this.__coolingDown = false;\n }\n};\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\n\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\nimport {AppLayoutBehavior} from '../app-layout-behavior/app-layout-behavior.js';\n\n/**\napp-header-layout is a wrapper element that positions an app-header and other\ncontent. This element uses the document scroll by default, but it can also\ndefine its own scrolling region.\n\nUsing the document scroll:\n\n```html\n\n \n \n App name
\n \n \n \n main content\n
\n\n```\n\nUsing an own scrolling region:\n\n```html\n\n \n \n App name
\n \n \n \n main content\n
\n\n```\n\nAdd the `fullbleed` attribute to app-header-layout to make it fit the size of\nits container:\n\n```html\n\n ...\n\n```\n\n@group App Elements\n@element app-header-layout\n@demo app-header-layout/demo/simple.html Simple Demo\n@demo app-header-layout/demo/scrolling-region.html Scrolling Region\n@demo app-header-layout/demo/music.html Music Demo\n@demo app-header-layout/demo/footer.html Footer Demo\n*/\nPolymer({\n _template: html`\n \n\n \n`,\n\n is: 'app-header-layout',\n behaviors: [AppLayoutBehavior],\n\n properties: {\n /**\n * If true, the current element will have its own scrolling region.\n * Otherwise, it will use the document scroll to control the header.\n */\n hasScrollingRegion: {type: Boolean, value: false, reflectToAttribute: true}\n },\n\n observers: ['resetLayout(isAttached, hasScrollingRegion)'],\n\n /**\n * A reference to the app-header element.\n *\n * @property header\n */\n get header() {\n return dom(this.$.headerSlot).getDistributedNodes()[0];\n },\n\n _updateLayoutStates: function() {\n var header = this.header;\n if (!this.isAttached || !header) {\n return;\n }\n // Remove the initializing class, which staticly positions the header and\n // the content until the height of the header can be read.\n this.$.wrapper.classList.remove('initializing');\n // Update scroll target.\n header.scrollTarget = this.hasScrollingRegion ?\n this.$.contentContainer :\n this.ownerDocument.documentElement;\n // Get header height here so that style reads are batched together before\n // style writes (i.e. getBoundingClientRect() below).\n var headerHeight = header.offsetHeight;\n // Update the header position.\n if (!this.hasScrollingRegion) {\n requestAnimationFrame(function() {\n var rect = this.getBoundingClientRect();\n var rightOffset = document.documentElement.clientWidth - rect.right;\n header.style.left = rect.left + 'px';\n header.style.right = rightOffset + 'px';\n }.bind(this));\n } else {\n header.style.left = '';\n header.style.right = '';\n }\n // Update the content container position.\n var containerStyle = this.$.contentContainer.style;\n if (header.fixed && !header.condenses && this.hasScrollingRegion) {\n // If the header size does not change and we're using a scrolling region,\n // exclude the header area from the scrolling region so that the header\n // doesn't overlap the scrollbar.\n containerStyle.marginTop = headerHeight + 'px';\n containerStyle.paddingTop = '';\n } else {\n containerStyle.paddingTop = headerHeight + 'px';\n containerStyle.marginTop = '';\n }\n }\n});\n","/**\n@license\nCopyright (c) 2016 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronResizableBehavior} from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {animationFrame} from '@polymer/polymer/lib/utils/async.js';\nimport {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';\nimport {enqueueDebouncer} from '@polymer/polymer/lib/utils/flush.js';\n\n/**\n * @polymerBehavior\n */\nexport const AppLayoutBehavior = [\n IronResizableBehavior,\n {\n\n listeners: {\n 'app-reset-layout': '_appResetLayoutHandler',\n 'iron-resize': 'resetLayout'\n },\n\n attached: function() {\n this.fire('app-reset-layout');\n },\n\n _appResetLayoutHandler: function(e) {\n if (dom(e).path[0] === this) {\n return;\n }\n this.resetLayout();\n e.stopPropagation();\n },\n\n _updateLayoutStates: function() {\n console.error('unimplemented');\n },\n\n /**\n * Resets the layout. If you changed the size of this element via CSS\n * you can notify the changes by either firing the `iron-resize` event\n * or calling `resetLayout` directly.\n *\n * @method resetLayout\n */\n resetLayout: function() {\n var self = this;\n var cb = this._updateLayoutStates.bind(this);\n this._layoutDebouncer =\n Debouncer.debounce(this._layoutDebouncer, animationFrame, cb);\n enqueueDebouncer(this._layoutDebouncer);\n this._notifyDescendantResize();\n },\n\n _notifyLayoutChanged: function() {\n var self = this;\n // TODO: the event `app-reset-layout` can be fired synchronously\n // as long as `_updateLayoutStates` waits for all the microtasks after\n // rAF. E.g. requestAnimationFrame(setTimeOut())\n requestAnimationFrame(function() {\n self.fire('app-reset-layout');\n });\n },\n\n _notifyDescendantResize: function() {\n if (!this.isAttached) {\n return;\n }\n this._interestedResizables.forEach(function(resizable) {\n if (this.resizerShouldNotify(resizable)) {\n this._notifyDescendant(resizable);\n }\n }, this);\n }\n }\n];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\n\nimport {IronButtonState} from '@polymer/iron-behaviors/iron-button-state.js';\nimport {IronControlState} from '@polymer/iron-behaviors/iron-control-state.js';\nimport {PaperRippleBehavior} from '@polymer/paper-behaviors/paper-ripple-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/*\n`paper-tab` is styled to look like a tab. It should be used in conjunction with\n`paper-tabs`.\n\nExample:\n\n \n TAB 1\n TAB 2\n TAB 3\n \n\n### Styling\n\nThe following custom properties and mixins are available for styling:\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-tab-ink` | Ink color | `--paper-yellow-a100`\n`--paper-tab` | Mixin applied to the tab | `{}`\n`--paper-tab-content` | Mixin applied to the tab content | `{}`\n`--paper-tab-content-focused` | Mixin applied to the tab content when the tab is focused | `{}`\n`--paper-tab-content-unselected` | Mixin applied to the tab content when the tab is not selected | `{}`\n\nThis element applies the mixin `--paper-font-common-base` but does not import\n`paper-styles/typography.html`. In order to apply the `Roboto` font to this\nelement, make sure you've imported `paper-styles/typography.html`.\n*/\nPolymer({\n _template: html`\n \n\n \n \n
\n`,\n\n is: 'paper-tab',\n\n behaviors: [IronControlState, IronButtonState, PaperRippleBehavior],\n\n properties: {\n\n /**\n * If true, the tab will forward keyboard clicks (enter/space) to\n * the first anchor element found in its descendants\n */\n link: {type: Boolean, value: false, reflectToAttribute: true}\n\n },\n\n /** @private */\n hostAttributes: {role: 'tab'},\n\n listeners: {down: '_updateNoink', tap: '_onTap'},\n\n attached: function() {\n this._updateNoink();\n },\n\n get _parentNoink() {\n var parent = dom(this).parentNode;\n return !!parent && !!parent.noink;\n },\n\n _updateNoink: function() {\n this.noink = !!this.noink || !!this._parentNoink;\n },\n\n _onTap: function(event) {\n if (this.link) {\n var anchor = this.queryEffectiveChildren('a');\n\n if (!anchor) {\n return;\n }\n\n // Don't get stuck in a loop delegating\n // the listener from the child anchor\n if (event.target === anchor) {\n return;\n }\n\n anchor.click();\n }\n }\n});\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronMenuBehavior} from './iron-menu-behavior.js';\n\n/**\n * `IronMenubarBehavior` implements accessible menubar behavior.\n *\n * @polymerBehavior IronMenubarBehavior\n */\nexport const IronMenubarBehaviorImpl = {\n\n hostAttributes: {'role': 'menubar'},\n\n /**\n * @type {!Object}\n */\n keyBindings: {'left': '_onLeftKey', 'right': '_onRightKey'},\n\n _onUpKey: function(event) {\n this.focusedItem.click();\n event.detail.keyboardEvent.preventDefault();\n },\n\n _onDownKey: function(event) {\n this.focusedItem.click();\n event.detail.keyboardEvent.preventDefault();\n },\n\n get _isRTL() {\n return window.getComputedStyle(this)['direction'] === 'rtl';\n },\n\n _onLeftKey: function(event) {\n if (this._isRTL) {\n this._focusNext();\n } else {\n this._focusPrevious();\n }\n event.detail.keyboardEvent.preventDefault();\n },\n\n _onRightKey: function(event) {\n if (this._isRTL) {\n this._focusPrevious();\n } else {\n this._focusNext();\n }\n event.detail.keyboardEvent.preventDefault();\n },\n\n _onKeydown: function(event) {\n if (this.keyboardEventMatchesKeys(event, 'up down left right esc')) {\n return;\n }\n\n // all other keys focus the menu item starting with that character\n this._focusWithKeyboardEvent(event);\n }\n\n};\n\n/** @polymerBehavior */\nexport const IronMenubarBehavior = [IronMenuBehavior, IronMenubarBehaviorImpl];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt\nThe complete set of authors may be found at http://polymer.github.io/AUTHORS.txt\nThe complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt\nCode distributed by Google as part of the polymer project is also\nsubject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/paper-styles/default-theme.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\n\nimport {PaperCheckedElementBehavior} from '@polymer/paper-behaviors/paper-checked-element-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\nimport {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js';\n\nconst template = html`\n\n\n\n\n
`;\ntemplate.setAttribute('strip-whitespace', '');\n\n/**\nMaterial design: [Radio button](https://www.google.com/design/spec/components/selection-controls.html#selection-controls-radio-button)\n\n`paper-radio-button` is a button that can be either checked or unchecked. The\nuser can tap the radio button to check or uncheck it.\n\nUse a `` to group a set of radio buttons. When radio buttons\nare inside a radio group, exactly one radio button in the group can be checked\nat any time.\n\nExample:\n\n \n Item label\n\n### Styling\n\nThe following custom properties and mixins are available for styling:\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-radio-button-unchecked-background-color` | Radio button background color when the input is not checked | `transparent`\n`--paper-radio-button-unchecked-color` | Radio button color when the input is not checked | `--primary-text-color`\n`--paper-radio-button-unchecked-ink-color` | Selected/focus ripple color when the input is not checked | `--primary-text-color`\n`--paper-radio-button-checked-color` | Radio button color when the input is checked | `--primary-color`\n`--paper-radio-button-checked-ink-color` | Selected/focus ripple color when the input is checked | `--primary-color`\n`--paper-radio-button-size` | Size of the radio button | `16px`\n`--paper-radio-button-ink-size` | Size of the ripple | `48px`\n`--paper-radio-button-label-color` | Label color | `--primary-text-color`\n`--paper-radio-button-label-spacing` | Spacing between the label and the button | `10px`\n`--paper-radio-button-radio-container` | A mixin applied to the internal radio container | `{}`\n`--paper-radio-button-label` | A mixin applied to the internal label | `{}`\n`--paper-radio-button-label-checked` | A mixin applied to the internal label when the radio button is checked | `{}`\n\nThis element applies the mixin `--paper-font-common-base` but does not import\n`paper-styles/typography.html`. In order to apply the `Roboto` font to this\nelement, make sure you've imported `paper-styles/typography.html`.\n\n@group Paper Elements\n@element paper-radio-button\n@demo demo/index.html\n*/\nPolymer({\n _template: template,\n\n is: 'paper-radio-button',\n\n behaviors: [PaperCheckedElementBehavior],\n\n hostAttributes: {role: 'radio', 'aria-checked': false, tabindex: 0},\n\n properties: {\n /**\n * Fired when the checked state changes due to user interaction.\n *\n * @event change\n */\n\n /**\n * Fired when the checked state changes.\n *\n * @event iron-change\n */\n\n ariaActiveAttribute: {type: String, value: 'aria-checked'}\n },\n\n ready: function() {\n this._rippleContainer = this.$.radioContainer;\n },\n\n attached: function() {\n // Wait until styles have resolved to check for the default sentinel.\n // See polymer#4009 for more details.\n afterNextRender(this, function() {\n var inkSize =\n this.getComputedStyleValue('--calculated-paper-radio-button-ink-size')\n .trim();\n // If unset, compute and set the default `--paper-radio-button-ink-size`.\n if (inkSize === '-1px') {\n var size = parseFloat(\n this.getComputedStyleValue('--calculated-paper-radio-button-size')\n .trim());\n var defaultInkSize = Math.floor(3 * size);\n\n // The button and ripple need to have the same parity so that their\n // centers align.\n if (defaultInkSize % 2 !== size % 2) {\n defaultInkSize++;\n }\n\n this.updateStyles({\n '--paper-radio-button-ink-size': defaultInkSize + 'px',\n });\n }\n });\n },\n})\n","/**\n * @license\n * Copyright 2019 Google Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n/**\n * Stores result from applyPassive to avoid redundant processing to detect\n * passive event listener support.\n */\nvar supportsPassive_;\n/**\n * Determine whether the current browser supports passive event listeners, and\n * if so, use them.\n */\nexport function applyPassive(globalObj, forceRefresh) {\n if (globalObj === void 0) { globalObj = window; }\n if (forceRefresh === void 0) { forceRefresh = false; }\n if (supportsPassive_ === undefined || forceRefresh) {\n var isSupported_1 = false;\n try {\n globalObj.document.addEventListener('test', function () { return undefined; }, {\n get passive() {\n isSupported_1 = true;\n return isSupported_1;\n },\n });\n }\n catch (e) {\n } // tslint:disable-line:no-empty cannot throw error due to tests. tslint also disables console.log.\n supportsPassive_ = isSupported_1;\n }\n return supportsPassive_ ? { passive: true } : false;\n}\n//# sourceMappingURL=events.js.map","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n/**\n * @fileoverview A \"ponyfill\" is a polyfill that doesn't modify the global prototype chain.\n * This makes ponyfills safer than traditional polyfills, especially for libraries like MDC.\n */\nexport function closest(element, selector) {\n if (element.closest) {\n return element.closest(selector);\n }\n var el = element;\n while (el) {\n if (matches(el, selector)) {\n return el;\n }\n el = el.parentElement;\n }\n return null;\n}\nexport function matches(element, selector) {\n var nativeMatches = element.matches\n || element.webkitMatchesSelector\n || element.msMatchesSelector;\n return nativeMatches.call(element, selector);\n}\n//# sourceMappingURL=ponyfill.js.map","/**\n * Stores result from supportsCssVariables to avoid redundant processing to\n * detect CSS custom variable support.\n */\nvar supportsCssVariables_;\nfunction detectEdgePseudoVarBug(windowObj) {\n // Detect versions of Edge with buggy var() support\n // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11495448/\n var document = windowObj.document;\n var node = document.createElement('div');\n node.className = 'mdc-ripple-surface--test-edge-var-bug';\n // Append to head instead of body because this script might be invoked in the\n // head, in which case the body doesn't exist yet. The probe works either way.\n document.head.appendChild(node);\n // The bug exists if ::before style ends up propagating to the parent element.\n // Additionally, getComputedStyle returns null in iframes with display: \"none\" in Firefox,\n // but Firefox is known to support CSS custom properties correctly.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=548397\n var computedStyle = windowObj.getComputedStyle(node);\n var hasPseudoVarBug = computedStyle !== null && computedStyle.borderTopStyle === 'solid';\n if (node.parentNode) {\n node.parentNode.removeChild(node);\n }\n return hasPseudoVarBug;\n}\nexport function supportsCssVariables(windowObj, forceRefresh) {\n if (forceRefresh === void 0) { forceRefresh = false; }\n var CSS = windowObj.CSS;\n var supportsCssVars = supportsCssVariables_;\n if (typeof supportsCssVariables_ === 'boolean' && !forceRefresh) {\n return supportsCssVariables_;\n }\n var supportsFunctionPresent = CSS && typeof CSS.supports === 'function';\n if (!supportsFunctionPresent) {\n return false;\n }\n var explicitlySupportsCssVars = CSS.supports('--css-vars', 'yes');\n // See: https://bugs.webkit.org/show_bug.cgi?id=154669\n // See: README section on Safari\n var weAreFeatureDetectingSafari10plus = (CSS.supports('(--css-vars: yes)') &&\n CSS.supports('color', '#00000000'));\n if (explicitlySupportsCssVars || weAreFeatureDetectingSafari10plus) {\n supportsCssVars = !detectEdgePseudoVarBug(windowObj);\n }\n else {\n supportsCssVars = false;\n }\n if (!forceRefresh) {\n supportsCssVariables_ = supportsCssVars;\n }\n return supportsCssVars;\n}\nexport function getNormalizedEventCoords(evt, pageOffset, clientRect) {\n if (!evt) {\n return { x: 0, y: 0 };\n }\n var x = pageOffset.x, y = pageOffset.y;\n var documentX = x + clientRect.left;\n var documentY = y + clientRect.top;\n var normalizedX;\n var normalizedY;\n // Determine touch point relative to the ripple container.\n if (evt.type === 'touchstart') {\n var touchEvent = evt;\n normalizedX = touchEvent.changedTouches[0].pageX - documentX;\n normalizedY = touchEvent.changedTouches[0].pageY - documentY;\n }\n else {\n var mouseEvent = evt;\n normalizedX = mouseEvent.pageX - documentX;\n normalizedY = mouseEvent.pageY - documentY;\n }\n return { x: normalizedX, y: normalizedY };\n}\n//# sourceMappingURL=util.js.map","/**\n * @license\n * Copyright 2016 Google Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nexport var cssClasses = {\n // Ripple is a special case where the \"root\" component is really a \"mixin\" of sorts,\n // given that it's an 'upgrade' to an existing component. That being said it is the root\n // CSS class that all other CSS classes derive from.\n BG_FOCUSED: 'mdc-ripple-upgraded--background-focused',\n FG_ACTIVATION: 'mdc-ripple-upgraded--foreground-activation',\n FG_DEACTIVATION: 'mdc-ripple-upgraded--foreground-deactivation',\n ROOT: 'mdc-ripple-upgraded',\n UNBOUNDED: 'mdc-ripple-upgraded--unbounded',\n};\nexport var strings = {\n VAR_FG_SCALE: '--mdc-ripple-fg-scale',\n VAR_FG_SIZE: '--mdc-ripple-fg-size',\n VAR_FG_TRANSLATE_END: '--mdc-ripple-fg-translate-end',\n VAR_FG_TRANSLATE_START: '--mdc-ripple-fg-translate-start',\n VAR_LEFT: '--mdc-ripple-left',\n VAR_TOP: '--mdc-ripple-top',\n};\nexport var numbers = {\n DEACTIVATION_TIMEOUT_MS: 225,\n FG_DEACTIVATION_MS: 150,\n INITIAL_ORIGIN_SCALE: 0.6,\n PADDING: 10,\n TAP_DELAY_MS: 300,\n};\n//# sourceMappingURL=constants.js.map","/**\n@license\nCopyright 2018 Google Inc. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {css} from 'lit-element';\n\nexport const style = css`@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}`;\n","/**\n@license\nCopyright 2018 Google Inc. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\nimport {applyPassive} from '@material/dom/events';\nimport {matches} from '@material/dom/ponyfill';\nimport {EventType, SpecificEventListener} from '@material/mwc-base/base-element.js';\nimport {MDCRippleAdapter} from '@material/ripple/adapter.js';\nimport MDCRippleFoundation from '@material/ripple/foundation.js';\nimport {supportsCssVariables} from '@material/ripple/util.js';\nimport {directive, noChange, NodePart, PropertyPart, templateFactory} from 'lit-html';\n\nimport {style} from './mwc-ripple-global-css.js';\n\nconst supportsCssVariablesWin = supportsCssVariables(window);\n\nexport interface RippleOptions {\n interactionNode?: HTMLElement;\n unbounded?: boolean;\n disabled?: boolean;\n active?: boolean;\n}\n\nexport interface RippleNodeOptions extends RippleOptions {\n surfaceNode: HTMLElement;\n}\n\ndeclare global {\n interface Element {\n // This is not a super great thing to do, adding a new property onto\n // arbitrary elements...\n ripple?: unknown;\n }\n}\n\n// NOTE: This is a workaround for\n// https://bugs.webkit.org/show_bug.cgi?id=173027. Since keyframes on\n// pseudo-elements (:after) are not supported in Shadow DOM, we put the keyframe\n// style into the element.\nconst isSafari = navigator.userAgent.match(/Safari/);\nlet didApplyRippleStyle = false;\nconst applyRippleStyle = () => {\n didApplyRippleStyle = true;\n const part = new NodePart({templateFactory});\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n part.appendInto(document.head!);\n part.setValue(style);\n part.commit();\n};\n\n/**\n * Applied a ripple to the node specified by {surfaceNode}.\n * @param options {RippleNodeOptions}\n */\nexport const rippleNode = (options: RippleNodeOptions) => {\n if (isSafari && !didApplyRippleStyle) {\n applyRippleStyle();\n }\n // TODO(sorvell): This directive requires bringing css yourself. We probably\n // need to do this because of ShadyCSS, but on Safari, the keyframes styling\n // must be global. Perhaps this directive could fix that.\n const surfaceNode = options.surfaceNode;\n const interactionNode = options.interactionNode || surfaceNode;\n // only style interaction node if not in the same root\n if (interactionNode.getRootNode() !== surfaceNode.getRootNode()) {\n if (interactionNode.style.position === '') {\n interactionNode.style.position = 'relative';\n }\n }\n const adapter: MDCRippleAdapter = {\n browserSupportsCssVars: () => supportsCssVariablesWin,\n isUnbounded: () =>\n options.unbounded === undefined ? true : options.unbounded,\n isSurfaceActive: () => matches(interactionNode, ':active'),\n isSurfaceDisabled: () => Boolean(options.disabled),\n addClass: (className: string) => surfaceNode.classList.add(className),\n removeClass: (className: string) => surfaceNode.classList.remove(className),\n containsEventTarget: (target: HTMLElement) =>\n interactionNode.contains(target),\n registerInteractionHandler:\n (type: K, handler: SpecificEventListener) =>\n interactionNode.addEventListener(type, handler, applyPassive()),\n deregisterInteractionHandler:\n (type: K, handler: SpecificEventListener) =>\n interactionNode.removeEventListener(type, handler, applyPassive()),\n registerDocumentInteractionHandler:\n (evtType: K, handler: SpecificEventListener) =>\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n document.documentElement!.addEventListener(\n evtType, handler, applyPassive()),\n deregisterDocumentInteractionHandler: (\n evtType: string, handler: SpecificEventListener) =>\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n document.documentElement!.removeEventListener(\n evtType, handler as EventListenerOrEventListenerObject, applyPassive()),\n registerResizeHandler: (handler: SpecificEventListener<'resize'>) =>\n window.addEventListener('resize', handler),\n deregisterResizeHandler: (handler: SpecificEventListener<'resize'>) =>\n window.removeEventListener('resize', handler),\n updateCssVariable: (varName: string, value: string) =>\n surfaceNode.style.setProperty(varName, value),\n computeBoundingRect: () => surfaceNode.getBoundingClientRect(),\n getWindowPageOffset: () => ({x: window.pageXOffset, y: window.pageYOffset}),\n };\n const rippleFoundation = new MDCRippleFoundation(adapter);\n rippleFoundation.init();\n return rippleFoundation;\n};\n\nconst rippleInteractionNodes = new WeakMap();\n\n/**\n * A directive that applies a Material ripple to a part node. The directive\n * should be applied to a PropertyPart.\n * @param options {RippleOptions}\n */\nexport const ripple =\n directive((options: RippleOptions = {}) => (part: PropertyPart) => {\n const surfaceNode = part.committer.element as HTMLElement;\n const interactionNode = options.interactionNode || surfaceNode;\n let rippleFoundation =\n part.value as MDCRippleFoundation | typeof noChange;\n // if the interaction node changes, destroy and invalidate the foundation.\n const existingInteractionNode =\n rippleInteractionNodes.get(rippleFoundation);\n if (existingInteractionNode !== undefined &&\n existingInteractionNode !== interactionNode) {\n (rippleFoundation as MDCRippleFoundation).destroy();\n rippleFoundation = noChange;\n }\n // make the ripple, if needed\n if (rippleFoundation === noChange) {\n rippleFoundation =\n rippleNode(Object.assign({}, options, {surfaceNode}));\n rippleInteractionNodes.set(rippleFoundation, interactionNode);\n part.setValue(rippleFoundation);\n // otherwise update settings as needed.\n } else {\n if (options.unbounded !== undefined) {\n (rippleFoundation as MDCRippleFoundation)\n .setUnbounded(options.unbounded);\n }\n if (options.disabled !== undefined) {\n (rippleFoundation as MDCRippleFoundation)\n .setUnbounded(options.disabled);\n }\n }\n if (options.active === true) {\n (rippleFoundation as MDCRippleFoundation).activate();\n } else if (options.active === false) {\n (rippleFoundation as MDCRippleFoundation).deactivate();\n }\n });\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';\nimport {IronControlState} from '@polymer/iron-behaviors/iron-control-state.js';\nimport {IronOverlayBehavior, IronOverlayBehaviorImpl} from '@polymer/iron-overlay-behavior/iron-overlay-behavior.js';\nimport {NeonAnimationRunnerBehavior} from '@polymer/neon-animation/neon-animation-runner-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/**\n`` is a generalized element that is useful when you have\nhidden content (`dropdown-content`) that is revealed due to some change in\nstate that should cause it to do so.\n\nNote that this is a low-level element intended to be used as part of other\ncomposite elements that cause dropdowns to be revealed.\n\nExamples of elements that might be implemented using an `iron-dropdown`\ninclude comboboxes, menubuttons, selects. The list goes on.\n\nThe `` element exposes attributes that allow the position\nof the `dropdown-content` relative to the `dropdown-trigger` to be\nconfigured.\n\n \n Hello!
\n \n\nIn the above example, the `