(function () {
  'use strict';

  const __DEMO__ = false;

  function _defineProperty(obj, key, value) {
    if (key in obj) {
      Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
      });
    } else {
      obj[key] = value;
    }

    return obj;
  }

  function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }

  function _superPropBase(object, property) {
    while (!Object.prototype.hasOwnProperty.call(object, property)) {
      object = _getPrototypeOf(object);
      if (object === null) break;
    }

    return object;
  }

  function _get(target, property, receiver) {
    if (typeof Reflect !== "undefined" && Reflect.get) {
      _get = Reflect.get;
    } else {
      _get = function _get(target, property, receiver) {
        var base = _superPropBase(target, property);

        if (!base) return;
        var desc = Object.getOwnPropertyDescriptor(base, property);

        if (desc.get) {
          return desc.get.call(receiver);
        }

        return desc.value;
      };
    }

    return _get(target, property, receiver || target);
  }

  function _toArray(arr) {
    return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest();
  }

  function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
  }

  function _iterableToArray(iter) {
    if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
  }

  function _unsupportedIterableToArray(o, minLen) {
    if (!o) return;
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
    var n = Object.prototype.toString.call(o).slice(8, -1);
    if (n === "Object" && o.constructor) n = o.constructor.name;
    if (n === "Map" || n === "Set") return Array.from(o);
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
  }

  function _arrayLikeToArray(arr, len) {
    if (len == null || len > arr.length) len = arr.length;

    for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

    return arr2;
  }

  function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  }

  function _toPrimitive(input, hint) {
    if (typeof input !== "object" || input === null) return input;
    var prim = input[Symbol.toPrimitive];

    if (prim !== undefined) {
      var res = prim.call(input, hint || "default");
      if (typeof res !== "object") return res;
      throw new TypeError("@@toPrimitive must return a primitive value.");
    }

    return (hint === "string" ? String : Number)(input);
  }

  function _toPropertyKey(arg) {
    var key = _toPrimitive(arg, "string");

    return typeof key === "symbol" ? key : String(key);
  }

  function _decorate(decorators, factory, superClass, mixins) {
    var api = _getDecoratorsApi();

    if (mixins) {
      for (var i = 0; i < mixins.length; i++) {
        api = mixins[i](api);
      }
    }

    var r = factory(function initialize(O) {
      api.initializeInstanceElements(O, decorated.elements);
    }, superClass);
    var decorated = api.decorateClass(_coalesceClassElements(r.d.map(_createElementDescriptor)), decorators);
    api.initializeClassElements(r.F, decorated.elements);
    return api.runClassFinishers(r.F, decorated.finishers);
  }

  function _getDecoratorsApi() {
    _getDecoratorsApi = function () {
      return api;
    };

    var api = {
      elementsDefinitionOrder: [["method"], ["field"]],
      initializeInstanceElements: function (O, elements) {
        ["method", "field"].forEach(function (kind) {
          elements.forEach(function (element) {
            if (element.kind === kind && element.placement === "own") {
              this.defineClassElement(O, element);
            }
          }, this);
        }, this);
      },
      initializeClassElements: function (F, elements) {
        var proto = F.prototype;
        ["method", "field"].forEach(function (kind) {
          elements.forEach(function (element) {
            var placement = element.placement;

            if (element.kind === kind && (placement === "static" || placement === "prototype")) {
              var receiver = placement === "static" ? F : proto;
              this.defineClassElement(receiver, element);
            }
          }, this);
        }, this);
      },
      defineClassElement: function (receiver, element) {
        var descriptor = element.descriptor;

        if (element.kind === "field") {
          var initializer = element.initializer;
          descriptor = {
            enumerable: descriptor.enumerable,
            writable: descriptor.writable,
            configurable: descriptor.configurable,
            value: initializer === void 0 ? void 0 : initializer.call(receiver)
          };
        }

        Object.defineProperty(receiver, element.key, descriptor);
      },
      decorateClass: function (elements, decorators) {
        var newElements = [];
        var finishers = [];
        var placements = {
          static: [],
          prototype: [],
          own: []
        };
        elements.forEach(function (element) {
          this.addElementPlacement(element, placements);
        }, this);
        elements.forEach(function (element) {
          if (!_hasDecorators(element)) return newElements.push(element);
          var elementFinishersExtras = this.decorateElement(element, placements);
          newElements.push(elementFinishersExtras.element);
          newElements.push.apply(newElements, elementFinishersExtras.extras);
          finishers.push.apply(finishers, elementFinishersExtras.finishers);
        }, this);

        if (!decorators) {
          return {
            elements: newElements,
            finishers: finishers
          };
        }

        var result = this.decorateConstructor(newElements, decorators);
        finishers.push.apply(finishers, result.finishers);
        result.finishers = finishers;
        return result;
      },
      addElementPlacement: function (element, placements, silent) {
        var keys = placements[element.placement];

        if (!silent && keys.indexOf(element.key) !== -1) {
          throw new TypeError("Duplicated element (" + element.key + ")");
        }

        keys.push(element.key);
      },
      decorateElement: function (element, placements) {
        var extras = [];
        var finishers = [];

        for (var decorators = element.decorators, i = decorators.length - 1; i >= 0; i--) {
          var keys = placements[element.placement];
          keys.splice(keys.indexOf(element.key), 1);
          var elementObject = this.fromElementDescriptor(element);
          var elementFinisherExtras = this.toElementFinisherExtras((0, decorators[i])(elementObject) || elementObject);
          element = elementFinisherExtras.element;
          this.addElementPlacement(element, placements);

          if (elementFinisherExtras.finisher) {
            finishers.push(elementFinisherExtras.finisher);
          }

          var newExtras = elementFinisherExtras.extras;

          if (newExtras) {
            for (var j = 0; j < newExtras.length; j++) {
              this.addElementPlacement(newExtras[j], placements);
            }

            extras.push.apply(extras, newExtras);
          }
        }

        return {
          element: element,
          finishers: finishers,
          extras: extras
        };
      },
      decorateConstructor: function (elements, decorators) {
        var finishers = [];

        for (var i = decorators.length - 1; i >= 0; i--) {
          var obj = this.fromClassDescriptor(elements);
          var elementsAndFinisher = this.toClassDescriptor((0, decorators[i])(obj) || obj);

          if (elementsAndFinisher.finisher !== undefined) {
            finishers.push(elementsAndFinisher.finisher);
          }

          if (elementsAndFinisher.elements !== undefined) {
            elements = elementsAndFinisher.elements;

            for (var j = 0; j < elements.length - 1; j++) {
              for (var k = j + 1; k < elements.length; k++) {
                if (elements[j].key === elements[k].key && elements[j].placement === elements[k].placement) {
                  throw new TypeError("Duplicated element (" + elements[j].key + ")");
                }
              }
            }
          }
        }

        return {
          elements: elements,
          finishers: finishers
        };
      },
      fromElementDescriptor: function (element) {
        var obj = {
          kind: element.kind,
          key: element.key,
          placement: element.placement,
          descriptor: element.descriptor
        };
        var desc = {
          value: "Descriptor",
          configurable: true
        };
        Object.defineProperty(obj, Symbol.toStringTag, desc);
        if (element.kind === "field") obj.initializer = element.initializer;
        return obj;
      },
      toElementDescriptors: function (elementObjects) {
        if (elementObjects === undefined) return;
        return _toArray(elementObjects).map(function (elementObject) {
          var element = this.toElementDescriptor(elementObject);
          this.disallowProperty(elementObject, "finisher", "An element descriptor");
          this.disallowProperty(elementObject, "extras", "An element descriptor");
          return element;
        }, this);
      },
      toElementDescriptor: function (elementObject) {
        var kind = String(elementObject.kind);

        if (kind !== "method" && kind !== "field") {
          throw new TypeError('An element descriptor\'s .kind property must be either "method" or' + ' "field", but a decorator created an element descriptor with' + ' .kind "' + kind + '"');
        }

        var key = _toPropertyKey(elementObject.key);

        var placement = String(elementObject.placement);

        if (placement !== "static" && placement !== "prototype" && placement !== "own") {
          throw new TypeError('An element descriptor\'s .placement property must be one of "static",' + ' "prototype" or "own", but a decorator created an element descriptor' + ' with .placement "' + placement + '"');
        }

        var descriptor = elementObject.descriptor;
        this.disallowProperty(elementObject, "elements", "An element descriptor");
        var element = {
          kind: kind,
          key: key,
          placement: placement,
          descriptor: Object.assign({}, descriptor)
        };

        if (kind !== "field") {
          this.disallowProperty(elementObject, "initializer", "A method descriptor");
        } else {
          this.disallowProperty(descriptor, "get", "The property descriptor of a field descriptor");
          this.disallowProperty(descriptor, "set", "The property descriptor of a field descriptor");
          this.disallowProperty(descriptor, "value", "The property descriptor of a field descriptor");
          element.initializer = elementObject.initializer;
        }

        return element;
      },
      toElementFinisherExtras: function (elementObject) {
        var element = this.toElementDescriptor(elementObject);

        var finisher = _optionalCallableProperty(elementObject, "finisher");

        var extras = this.toElementDescriptors(elementObject.extras);
        return {
          element: element,
          finisher: finisher,
          extras: extras
        };
      },
      fromClassDescriptor: function (elements) {
        var obj = {
          kind: "class",
          elements: elements.map(this.fromElementDescriptor, this)
        };
        var desc = {
          value: "Descriptor",
          configurable: true
        };
        Object.defineProperty(obj, Symbol.toStringTag, desc);
        return obj;
      },
      toClassDescriptor: function (obj) {
        var kind = String(obj.kind);

        if (kind !== "class") {
          throw new TypeError('A class descriptor\'s .kind property must be "class", but a decorator' + ' created a class descriptor with .kind "' + kind + '"');
        }

        this.disallowProperty(obj, "key", "A class descriptor");
        this.disallowProperty(obj, "placement", "A class descriptor");
        this.disallowProperty(obj, "descriptor", "A class descriptor");
        this.disallowProperty(obj, "initializer", "A class descriptor");
        this.disallowProperty(obj, "extras", "A class descriptor");

        var finisher = _optionalCallableProperty(obj, "finisher");

        var elements = this.toElementDescriptors(obj.elements);
        return {
          elements: elements,
          finisher: finisher
        };
      },
      runClassFinishers: function (constructor, finishers) {
        for (var i = 0; i < finishers.length; i++) {
          var newConstructor = (0, finishers[i])(constructor);

          if (newConstructor !== undefined) {
            if (typeof newConstructor !== "function") {
              throw new TypeError("Finishers must return a constructor.");
            }

            constructor = newConstructor;
          }
        }

        return constructor;
      },
      disallowProperty: function (obj, name, objectType) {
        if (obj[name] !== undefined) {
          throw new TypeError(objectType + " can't have a ." + name + " property.");
        }
      }
    };
    return api;
  }

  function _createElementDescriptor(def) {
    var key = _toPropertyKey(def.key);

    var descriptor;

    if (def.kind === "method") {
      descriptor = {
        value: def.value,
        writable: true,
        configurable: true,
        enumerable: false
      };
    } else if (def.kind === "get") {
      descriptor = {
        get: def.value,
        configurable: true,
        enumerable: false
      };
    } else if (def.kind === "set") {
      descriptor = {
        set: def.value,
        configurable: true,
        enumerable: false
      };
    } else if (def.kind === "field") {
      descriptor = {
        configurable: true,
        writable: true,
        enumerable: true
      };
    }

    var element = {
      kind: def.kind === "field" ? "field" : "method",
      key: key,
      placement: def.static ? "static" : def.kind === "field" ? "own" : "prototype",
      descriptor: descriptor
    };
    if (def.decorators) element.decorators = def.decorators;
    if (def.kind === "field") element.initializer = def.value;
    return element;
  }

  function _coalesceGetterSetter(element, other) {
    if (element.descriptor.get !== undefined) {
      other.descriptor.get = element.descriptor.get;
    } else {
      other.descriptor.set = element.descriptor.set;
    }
  }

  function _coalesceClassElements(elements) {
    var newElements = [];

    var isSameElement = function (other) {
      return other.kind === "method" && other.key === element.key && other.placement === element.placement;
    };

    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      var other;

      if (element.kind === "method" && (other = newElements.find(isSameElement))) {
        if (_isDataDescriptor(element.descriptor) || _isDataDescriptor(other.descriptor)) {
          if (_hasDecorators(element) || _hasDecorators(other)) {
            throw new ReferenceError("Duplicated methods (" + element.key + ") can't be decorated.");
          }

          other.descriptor = element.descriptor;
        } else {
          if (_hasDecorators(element)) {
            if (_hasDecorators(other)) {
              throw new ReferenceError("Decorators can't be placed on different accessors with for " + "the same property (" + element.key + ").");
            }

            other.decorators = element.decorators;
          }

          _coalesceGetterSetter(element, other);
        }
      } else {
        newElements.push(element);
      }
    }

    return newElements;
  }

  function _hasDecorators(element) {
    return element.decorators && element.decorators.length;
  }

  function _isDataDescriptor(desc) {
    return desc !== undefined && !(desc.value === undefined && desc.writable === undefined);
  }

  function _optionalCallableProperty(obj, name) {
    var value = obj[name];

    if (value !== undefined && typeof value !== "function") {
      throw new TypeError("Expected '" + name + "' to be a function");
    }

    return value;
  }

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */

  /**
   * True if the custom elements polyfill is in use.
   */
  const isCEPolyfill = typeof window !== 'undefined' && window.customElements != null && window.customElements.polyfillWrapFlushCallback !== undefined;
  /**
   * Reparents nodes, starting from `start` (inclusive) to `end` (exclusive),
   * into another container (could be the same container), before `before`. If
   * `before` is null, it appends the nodes to the container.
   */

  const reparentNodes = (container, start, end = null, before = null) => {
    while (start !== end) {
      const n = start.nextSibling;
      container.insertBefore(start, before);
      start = n;
    }
  };
  /**
   * Removes nodes, starting from `start` (inclusive) to `end` (exclusive), from
   * `container`.
   */

  const removeNodes = (container, start, end = null) => {
    while (start !== end) {
      const n = start.nextSibling;
      container.removeChild(start);
      start = n;
    }
  };

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */

  /**
   * An expression marker with embedded unique key to avoid collision with
   * possible text in templates.
   */
  const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
  /**
   * An expression marker used text-positions, multi-binding attributes, and
   * attributes with markup-like text values.
   */

  const nodeMarker = `<!--${marker}-->`;
  const markerRegex = new RegExp(`${marker}|${nodeMarker}`);
  /**
   * Suffix appended to all bound attribute names.
   */

  const boundAttributeSuffix = '$lit$';
  /**
   * An updatable Template that tracks the location of dynamic parts.
   */

  class Template {
    constructor(result, element) {
      this.parts = [];
      this.element = element;
      const nodesToRemove = [];
      const stack = []; // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null

      const walker = document.createTreeWalker(element.content, 133
      /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */
      , null, false); // Keeps track of the last index associated with a part. We try to delete
      // unnecessary nodes, but we never want to associate two different parts
      // to the same index. They must have a constant node between.

      let lastPartIndex = 0;
      let index = -1;
      let partIndex = 0;
      const {
        strings,
        values: {
          length
        }
      } = result;

      while (partIndex < length) {
        const node = walker.nextNode();

        if (node === null) {
          // We've exhausted the content inside a nested template element.
          // Because we still have parts (the outer for-loop), we know:
          // - There is a template in the stack
          // - The walker will find a nextNode outside the template
          walker.currentNode = stack.pop();
          continue;
        }

        index++;

        if (node.nodeType === 1
        /* Node.ELEMENT_NODE */
        ) {
            if (node.hasAttributes()) {
              const attributes = node.attributes;
              const {
                length
              } = attributes; // Per
              // https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap,
              // attributes are not guaranteed to be returned in document order.
              // In particular, Edge/IE can return them out of order, so we cannot
              // assume a correspondence between part index and attribute index.

              let count = 0;

              for (let i = 0; i < length; i++) {
                if (endsWith(attributes[i].name, boundAttributeSuffix)) {
                  count++;
                }
              }

              while (count-- > 0) {
                // Get the template literal section leading up to the first
                // expression in this attribute
                const stringForPart = strings[partIndex]; // Find the attribute name

                const name = lastAttributeNameRegex.exec(stringForPart)[2]; // Find the corresponding attribute
                // All bound attributes have had a suffix added in
                // TemplateResult#getHTML to opt out of special attribute
                // handling. To look up the attribute value we also need to add
                // the suffix.

                const attributeLookupName = name.toLowerCase() + boundAttributeSuffix;
                const attributeValue = node.getAttribute(attributeLookupName);
                node.removeAttribute(attributeLookupName);
                const statics = attributeValue.split(markerRegex);
                this.parts.push({
                  type: 'attribute',
                  index,
                  name,
                  strings: statics
                });
                partIndex += statics.length - 1;
              }
            }

            if (node.tagName === 'TEMPLATE') {
              stack.push(node);
              walker.currentNode = node.content;
            }
          } else if (node.nodeType === 3
        /* Node.TEXT_NODE */
        ) {
            const data = node.data;

            if (data.indexOf(marker) >= 0) {
              const parent = node.parentNode;
              const strings = data.split(markerRegex);
              const lastIndex = strings.length - 1; // Generate a new text node for each literal section
              // These nodes are also used as the markers for node parts

              for (let i = 0; i < lastIndex; i++) {
                let insert;
                let s = strings[i];

                if (s === '') {
                  insert = createMarker();
                } else {
                  const match = lastAttributeNameRegex.exec(s);

                  if (match !== null && endsWith(match[2], boundAttributeSuffix)) {
                    s = s.slice(0, match.index) + match[1] + match[2].slice(0, -boundAttributeSuffix.length) + match[3];
                  }

                  insert = document.createTextNode(s);
                }

                parent.insertBefore(insert, node);
                this.parts.push({
                  type: 'node',
                  index: ++index
                });
              } // If there's no text, we must insert a comment to mark our place.
              // Else, we can trust it will stick around after cloning.


              if (strings[lastIndex] === '') {
                parent.insertBefore(createMarker(), node);
                nodesToRemove.push(node);
              } else {
                node.data = strings[lastIndex];
              } // We have a part for each match found


              partIndex += lastIndex;
            }
          } else if (node.nodeType === 8
        /* Node.COMMENT_NODE */
        ) {
            if (node.data === marker) {
              const parent = node.parentNode; // Add a new marker node to be the startNode of the Part if any of
              // the following are true:
              //  * We don't have a previousSibling
              //  * The previousSibling is already the start of a previous part

              if (node.previousSibling === null || index === lastPartIndex) {
                index++;
                parent.insertBefore(createMarker(), node);
              }

              lastPartIndex = index;
              this.parts.push({
                type: 'node',
                index
              }); // If we don't have a nextSibling, keep this node so we have an end.
              // Else, we can remove it to save future costs.

              if (node.nextSibling === null) {
                node.data = '';
              } else {
                nodesToRemove.push(node);
                index--;
              }

              partIndex++;
            } else {
              let i = -1;

              while ((i = node.data.indexOf(marker, i + 1)) !== -1) {
                // Comment node has a binding marker inside, make an inactive part
                // The binding won't work, but subsequent bindings will
                // TODO (justinfagnani): consider whether it's even worth it to
                // make bindings in comments work
                this.parts.push({
                  type: 'node',
                  index: -1
                });
                partIndex++;
              }
            }
          }
      } // Remove text binding nodes after the walk to not disturb the TreeWalker


      for (const n of nodesToRemove) {
        n.parentNode.removeChild(n);
      }
    }

  }

  const endsWith = (str, suffix) => {
    const index = str.length - suffix.length;
    return index >= 0 && str.slice(index) === suffix;
  };

  const isTemplatePartActive = part => part.index !== -1; // Allows `document.createComment('')` to be renamed for a
  // small manual size-savings.

  const createMarker = () => document.createComment('');
  /**
   * This regex extracts the attribute name preceding an attribute-position
   * expression. It does this by matching the syntax allowed for attributes
   * against the string literal directly preceding the expression, assuming that
   * the expression is in an attribute-value position.
   *
   * See attributes in the HTML spec:
   * https://www.w3.org/TR/html5/syntax.html#elements-attributes
   *
   * " \x09\x0a\x0c\x0d" are HTML space characters:
   * https://www.w3.org/TR/html5/infrastructure.html#space-characters
   *
   * "\0-\x1F\x7F-\x9F" are Unicode control characters, which includes every
   * space character except " ".
   *
   * So an attribute is:
   *  * The name: any character except a control character, space character, ('),
   *    ("), ">", "=", or "/"
   *  * Followed by zero or more space characters
   *  * Followed by "="
   *  * Followed by zero or more space characters
   *  * Followed by:
   *    * Any character except space, ('), ("), "<", ">", "=", (`), or
   *    * (") then any non-("), or
   *    * (') then any non-(')
   */

  const lastAttributeNameRegex = // eslint-disable-next-line no-control-regex
  /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const walkerNodeFilter = 133
  /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */
  ;
  /**
   * Removes the list of nodes from a Template safely. In addition to removing
   * nodes from the Template, the Template part indices are updated to match
   * the mutated Template DOM.
   *
   * As the template is walked the removal state is tracked and
   * part indices are adjusted as needed.
   *
   * div
   *   div#1 (remove) <-- start removing (removing node is div#1)
   *     div
   *       div#2 (remove)  <-- continue removing (removing node is still div#1)
   *         div
   * div <-- stop removing since previous sibling is the removing node (div#1,
   * removed 4 nodes)
   */

  function removeNodesFromTemplate(template, nodesToRemove) {
    const {
      element: {
        content
      },
      parts
    } = template;
    const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
    let partIndex = nextActiveIndexInTemplateParts(parts);
    let part = parts[partIndex];
    let nodeIndex = -1;
    let removeCount = 0;
    const nodesToRemoveInTemplate = [];
    let currentRemovingNode = null;

    while (walker.nextNode()) {
      nodeIndex++;
      const node = walker.currentNode; // End removal if stepped past the removing node

      if (node.previousSibling === currentRemovingNode) {
        currentRemovingNode = null;
      } // A node to remove was found in the template


      if (nodesToRemove.has(node)) {
        nodesToRemoveInTemplate.push(node); // Track node we're removing

        if (currentRemovingNode === null) {
          currentRemovingNode = node;
        }
      } // When removing, increment count by which to adjust subsequent part indices


      if (currentRemovingNode !== null) {
        removeCount++;
      }

      while (part !== undefined && part.index === nodeIndex) {
        // If part is in a removed node deactivate it by setting index to -1 or
        // adjust the index as needed.
        part.index = currentRemovingNode !== null ? -1 : part.index - removeCount; // go to the next active part.

        partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
        part = parts[partIndex];
      }
    }

    nodesToRemoveInTemplate.forEach(n => n.parentNode.removeChild(n));
  }

  const countNodes = node => {
    let count = node.nodeType === 11
    /* Node.DOCUMENT_FRAGMENT_NODE */
    ? 0 : 1;
    const walker = document.createTreeWalker(node, walkerNodeFilter, null, false);

    while (walker.nextNode()) {
      count++;
    }

    return count;
  };

  const nextActiveIndexInTemplateParts = (parts, startIndex = -1) => {
    for (let i = startIndex + 1; i < parts.length; i++) {
      const part = parts[i];

      if (isTemplatePartActive(part)) {
        return i;
      }
    }

    return -1;
  };
  /**
   * Inserts the given node into the Template, optionally before the given
   * refNode. In addition to inserting the node into the Template, the Template
   * part indices are updated to match the mutated Template DOM.
   */


  function insertNodeIntoTemplate(template, node, refNode = null) {
    const {
      element: {
        content
      },
      parts
    } = template; // If there's no refNode, then put node at end of template.
    // No part indices need to be shifted in this case.

    if (refNode === null || refNode === undefined) {
      content.appendChild(node);
      return;
    }

    const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
    let partIndex = nextActiveIndexInTemplateParts(parts);
    let insertCount = 0;
    let walkerIndex = -1;

    while (walker.nextNode()) {
      walkerIndex++;
      const walkerNode = walker.currentNode;

      if (walkerNode === refNode) {
        insertCount = countNodes(node);
        refNode.parentNode.insertBefore(node, refNode);
      }

      while (partIndex !== -1 && parts[partIndex].index === walkerIndex) {
        // If we've inserted the node, simply adjust all subsequent parts
        if (insertCount > 0) {
          while (partIndex !== -1) {
            parts[partIndex].index += insertCount;
            partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
          }

          return;
        }

        partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
      }
    }
  }

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const directives = new WeakMap();
  /**
   * Brands a function as a directive factory function so that lit-html will call
   * the function during template rendering, rather than passing as a value.
   *
   * A _directive_ is a function that takes a Part as an argument. It has the
   * signature: `(part: Part) => void`.
   *
   * A directive _factory_ is a function that takes arguments for data and
   * configuration and returns a directive. Users of directive usually refer to
   * the directive factory as the directive. For example, "The repeat directive".
   *
   * Usually a template author will invoke a directive factory in their template
   * with relevant arguments, which will then return a directive function.
   *
   * Here's an example of using the `repeat()` directive factory that takes an
   * array and a function to render an item:
   *
   * ```js
   * html`<ul><${repeat(items, (item) => html`<li>${item}</li>`)}</ul>`
   * ```
   *
   * When `repeat` is invoked, it returns a directive function that closes over
   * `items` and the template function. When the outer template is rendered, the
   * return directive function is called with the Part for the expression.
   * `repeat` then performs it's custom logic to render multiple items.
   *
   * @param f The directive factory function. Must be a function that returns a
   * function of the signature `(part: Part) => void`. The returned function will
   * be called with the part object.
   *
   * @example
   *
   * import {directive, html} from 'lit-html';
   *
   * const immutable = directive((v) => (part) => {
   *   if (part.value !== v) {
   *     part.setValue(v)
   *   }
   * });
   */

  const directive = f => (...args) => {
    const d = f(...args);
    directives.set(d, true);
    return d;
  };
  const isDirective = o => {
    return typeof o === 'function' && directives.has(o);
  };

  /**
   * @license
   * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */

  /**
   * A sentinel value that signals that a value was handled by a directive and
   * should not be written to the DOM.
   */
  const noChange = {};
  /**
   * A sentinel value that signals a NodePart to fully clear its content.
   */

  const nothing = {};

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  /**
   * An instance of a `Template` that can be attached to the DOM and updated
   * with new values.
   */

  class TemplateInstance {
    constructor(template, processor, options) {
      this.__parts = [];
      this.template = template;
      this.processor = processor;
      this.options = options;
    }

    update(values) {
      let i = 0;

      for (const part of this.__parts) {
        if (part !== undefined) {
          part.setValue(values[i]);
        }

        i++;
      }

      for (const part of this.__parts) {
        if (part !== undefined) {
          part.commit();
        }
      }
    }

    _clone() {
      // There are a number of steps in the lifecycle of a template instance's
      // DOM fragment:
      //  1. Clone - create the instance fragment
      //  2. Adopt - adopt into the main document
      //  3. Process - find part markers and create parts
      //  4. Upgrade - upgrade custom elements
      //  5. Update - set node, attribute, property, etc., values
      //  6. Connect - connect to the document. Optional and outside of this
      //     method.
      //
      // We have a few constraints on the ordering of these steps:
      //  * We need to upgrade before updating, so that property values will pass
      //    through any property setters.
      //  * We would like to process before upgrading so that we're sure that the
      //    cloned fragment is inert and not disturbed by self-modifying DOM.
      //  * We want custom elements to upgrade even in disconnected fragments.
      //
      // Given these constraints, with full custom elements support we would
      // prefer the order: Clone, Process, Adopt, Upgrade, Update, Connect
      //
      // But Safari does not implement CustomElementRegistry#upgrade, so we
      // can not implement that order and still have upgrade-before-update and
      // upgrade disconnected fragments. So we instead sacrifice the
      // process-before-upgrade constraint, since in Custom Elements v1 elements
      // must not modify their light DOM in the constructor. We still have issues
      // when co-existing with CEv0 elements like Polymer 1, and with polyfills
      // that don't strictly adhere to the no-modification rule because shadow
      // DOM, which may be created in the constructor, is emulated by being placed
      // in the light DOM.
      //
      // The resulting order is on native is: Clone, Adopt, Upgrade, Process,
      // Update, Connect. document.importNode() performs Clone, Adopt, and Upgrade
      // in one step.
      //
      // The Custom Elements v1 polyfill supports upgrade(), so the order when
      // polyfilled is the more ideal: Clone, Process, Adopt, Upgrade, Update,
      // Connect.
      const fragment = isCEPolyfill ? this.template.element.content.cloneNode(true) : document.importNode(this.template.element.content, true);
      const stack = [];
      const parts = this.template.parts; // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null

      const walker = document.createTreeWalker(fragment, 133
      /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */
      , null, false);
      let partIndex = 0;
      let nodeIndex = 0;
      let part;
      let node = walker.nextNode(); // Loop through all the nodes and parts of a template

      while (partIndex < parts.length) {
        part = parts[partIndex];

        if (!isTemplatePartActive(part)) {
          this.__parts.push(undefined);

          partIndex++;
          continue;
        } // Progress the tree walker until we find our next part's node.
        // Note that multiple parts may share the same node (attribute parts
        // on a single element), so this loop may not run at all.


        while (nodeIndex < part.index) {
          nodeIndex++;

          if (node.nodeName === 'TEMPLATE') {
            stack.push(node);
            walker.currentNode = node.content;
          }

          if ((node = walker.nextNode()) === null) {
            // We've exhausted the content inside a nested template element.
            // Because we still have parts (the outer for-loop), we know:
            // - There is a template in the stack
            // - The walker will find a nextNode outside the template
            walker.currentNode = stack.pop();
            node = walker.nextNode();
          }
        } // We've arrived at our part's node.


        if (part.type === 'node') {
          const part = this.processor.handleTextExpression(this.options);
          part.insertAfterNode(node.previousSibling);

          this.__parts.push(part);
        } else {
          this.__parts.push(...this.processor.handleAttributeExpressions(node, part.name, part.strings, this.options));
        }

        partIndex++;
      }

      if (isCEPolyfill) {
        document.adoptNode(fragment);
        customElements.upgrade(fragment);
      }

      return fragment;
    }

  }

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const commentMarker = ` ${marker} `;
  /**
   * The return type of `html`, which holds a Template and the values from
   * interpolated expressions.
   */

  class TemplateResult {
    constructor(strings, values, type, processor) {
      this.strings = strings;
      this.values = values;
      this.type = type;
      this.processor = processor;
    }
    /**
     * Returns a string of HTML used to create a `<template>` element.
     */


    getHTML() {
      const l = this.strings.length - 1;
      let html = '';
      let isCommentBinding = false;

      for (let i = 0; i < l; i++) {
        const s = this.strings[i]; // For each binding we want to determine the kind of marker to insert
        // into the template source before it's parsed by the browser's HTML
        // parser. The marker type is based on whether the expression is in an
        // attribute, text, or comment position.
        //   * For node-position bindings we insert a comment with the marker
        //     sentinel as its text content, like <!--{{lit-guid}}-->.
        //   * For attribute bindings we insert just the marker sentinel for the
        //     first binding, so that we support unquoted attribute bindings.
        //     Subsequent bindings can use a comment marker because multi-binding
        //     attributes must be quoted.
        //   * For comment bindings we insert just the marker sentinel so we don't
        //     close the comment.
        //
        // The following code scans the template source, but is *not* an HTML
        // parser. We don't need to track the tree structure of the HTML, only
        // whether a binding is inside a comment, and if not, if it appears to be
        // the first binding in an attribute.

        const commentOpen = s.lastIndexOf('<!--'); // We're in comment position if we have a comment open with no following
        // comment close. Because <-- can appear in an attribute value there can
        // be false positives.

        isCommentBinding = (commentOpen > -1 || isCommentBinding) && s.indexOf('-->', commentOpen + 1) === -1; // Check to see if we have an attribute-like sequence preceding the
        // expression. This can match "name=value" like structures in text,
        // comments, and attribute values, so there can be false-positives.

        const attributeMatch = lastAttributeNameRegex.exec(s);

        if (attributeMatch === null) {
          // We're only in this branch if we don't have a attribute-like
          // preceding sequence. For comments, this guards against unusual
          // attribute values like <div foo="<!--${'bar'}">. Cases like
          // <!-- foo=${'bar'}--> are handled correctly in the attribute branch
          // below.
          html += s + (isCommentBinding ? commentMarker : nodeMarker);
        } else {
          // For attributes we use just a marker sentinel, and also append a
          // $lit$ suffix to the name to opt-out of attribute-specific parsing
          // that IE and Edge do for style and certain SVG attributes.
          html += s.substr(0, attributeMatch.index) + attributeMatch[1] + attributeMatch[2] + boundAttributeSuffix + attributeMatch[3] + marker;
        }
      }

      html += this.strings[l];
      return html;
    }

    getTemplateElement() {
      const template = document.createElement('template');
      template.innerHTML = this.getHTML();
      return template;
    }

  }
  /**
   * A TemplateResult for SVG fragments.
   *
   * This class wraps HTML in an `<svg>` tag in order to parse its contents in the
   * SVG namespace, then modifies the template to remove the `<svg>` tag so that
   * clones only container the original fragment.
   */

  class SVGTemplateResult extends TemplateResult {
    getHTML() {
      return `<svg>${super.getHTML()}</svg>`;
    }

    getTemplateElement() {
      const template = super.getTemplateElement();
      const content = template.content;
      const svgElement = content.firstChild;
      content.removeChild(svgElement);
      reparentNodes(content, svgElement.firstChild);
      return template;
    }

  }

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const isPrimitive = value => {
    return value === null || !(typeof value === 'object' || typeof value === 'function');
  };
  const isIterable = value => {
    return Array.isArray(value) || // eslint-disable-next-line @typescript-eslint/no-explicit-any
    !!(value && value[Symbol.iterator]);
  };
  /**
   * Writes attribute values to the DOM for a group of AttributeParts bound to a
   * single attribute. The value is only set once even if there are multiple parts
   * for an attribute.
   */

  class AttributeCommitter {
    constructor(element, name, strings) {
      this.dirty = true;
      this.element = element;
      this.name = name;
      this.strings = strings;
      this.parts = [];

      for (let i = 0; i < strings.length - 1; i++) {
        this.parts[i] = this._createPart();
      }
    }
    /**
     * Creates a single part. Override this to create a differnt type of part.
     */


    _createPart() {
      return new AttributePart(this);
    }

    _getValue() {
      const strings = this.strings;
      const l = strings.length - 1;
      let text = '';

      for (let i = 0; i < l; i++) {
        text += strings[i];
        const part = this.parts[i];

        if (part !== undefined) {
          const v = part.value;

          if (isPrimitive(v) || !isIterable(v)) {
            text += typeof v === 'string' ? v : String(v);
          } else {
            for (const t of v) {
              text += typeof t === 'string' ? t : String(t);
            }
          }
        }
      }

      text += strings[l];
      return text;
    }

    commit() {
      if (this.dirty) {
        this.dirty = false;
        this.element.setAttribute(this.name, this._getValue());
      }
    }

  }
  /**
   * A Part that controls all or part of an attribute value.
   */

  class AttributePart {
    constructor(committer) {
      this.value = undefined;
      this.committer = committer;
    }

    setValue(value) {
      if (value !== noChange && (!isPrimitive(value) || value !== this.value)) {
        this.value = value; // If the value is a not a directive, dirty the committer so that it'll
        // call setAttribute. If the value is a directive, it'll dirty the
        // committer if it calls setValue().

        if (!isDirective(value)) {
          this.committer.dirty = true;
        }
      }
    }

    commit() {
      while (isDirective(this.value)) {
        const directive = this.value;
        this.value = noChange;
        directive(this);
      }

      if (this.value === noChange) {
        return;
      }

      this.committer.commit();
    }

  }
  /**
   * A Part that controls a location within a Node tree. Like a Range, NodePart
   * has start and end locations and can set and update the Nodes between those
   * locations.
   *
   * NodeParts support several value types: primitives, Nodes, TemplateResults,
   * as well as arrays and iterables of those types.
   */

  class NodePart {
    constructor(options) {
      this.value = undefined;
      this.__pendingValue = undefined;
      this.options = options;
    }
    /**
     * Appends this part into a container.
     *
     * This part must be empty, as its contents are not automatically moved.
     */


    appendInto(container) {
      this.startNode = container.appendChild(createMarker());
      this.endNode = container.appendChild(createMarker());
    }
    /**
     * Inserts this part after the `ref` node (between `ref` and `ref`'s next
     * sibling). Both `ref` and its next sibling must be static, unchanging nodes
     * such as those that appear in a literal section of a template.
     *
     * This part must be empty, as its contents are not automatically moved.
     */


    insertAfterNode(ref) {
      this.startNode = ref;
      this.endNode = ref.nextSibling;
    }
    /**
     * Appends this part into a parent part.
     *
     * This part must be empty, as its contents are not automatically moved.
     */


    appendIntoPart(part) {
      part.__insert(this.startNode = createMarker());

      part.__insert(this.endNode = createMarker());
    }
    /**
     * Inserts this part after the `ref` part.
     *
     * This part must be empty, as its contents are not automatically moved.
     */


    insertAfterPart(ref) {
      ref.__insert(this.startNode = createMarker());

      this.endNode = ref.endNode;
      ref.endNode = this.startNode;
    }

    setValue(value) {
      this.__pendingValue = value;
    }

    commit() {
      if (this.startNode.parentNode === null) {
        return;
      }

      while (isDirective(this.__pendingValue)) {
        const directive = this.__pendingValue;
        this.__pendingValue = noChange;
        directive(this);
      }

      const value = this.__pendingValue;

      if (value === noChange) {
        return;
      }

      if (isPrimitive(value)) {
        if (value !== this.value) {
          this.__commitText(value);
        }
      } else if (value instanceof TemplateResult) {
        this.__commitTemplateResult(value);
      } else if (value instanceof Node) {
        this.__commitNode(value);
      } else if (isIterable(value)) {
        this.__commitIterable(value);
      } else if (value === nothing) {
        this.value = nothing;
        this.clear();
      } else {
        // Fallback, will render the string representation
        this.__commitText(value);
      }
    }

    __insert(node) {
      this.endNode.parentNode.insertBefore(node, this.endNode);
    }

    __commitNode(value) {
      if (this.value === value) {
        return;
      }

      this.clear();

      this.__insert(value);

      this.value = value;
    }

    __commitText(value) {
      const node = this.startNode.nextSibling;
      value = value == null ? '' : value; // If `value` isn't already a string, we explicitly convert it here in case
      // it can't be implicitly converted - i.e. it's a symbol.

      const valueAsString = typeof value === 'string' ? value : String(value);

      if (node === this.endNode.previousSibling && node.nodeType === 3
      /* Node.TEXT_NODE */
      ) {
          // If we only have a single text node between the markers, we can just
          // set its value, rather than replacing it.
          // TODO(justinfagnani): Can we just check if this.value is primitive?
          node.data = valueAsString;
        } else {
        this.__commitNode(document.createTextNode(valueAsString));
      }

      this.value = value;
    }

    __commitTemplateResult(value) {
      const template = this.options.templateFactory(value);

      if (this.value instanceof TemplateInstance && this.value.template === template) {
        this.value.update(value.values);
      } else {
        // Make sure we propagate the template processor from the TemplateResult
        // so that we use its syntax extension, etc. The template factory comes
        // from the render function options so that it can control template
        // caching and preprocessing.
        const instance = new TemplateInstance(template, value.processor, this.options);

        const fragment = instance._clone();

        instance.update(value.values);

        this.__commitNode(fragment);

        this.value = instance;
      }
    }

    __commitIterable(value) {
      // For an Iterable, we create a new InstancePart per item, then set its
      // value to the item. This is a little bit of overhead for every item in
      // an Iterable, but it lets us recurse easily and efficiently update Arrays
      // of TemplateResults that will be commonly returned from expressions like:
      // array.map((i) => html`${i}`), by reusing existing TemplateInstances.
      // If _value is an array, then the previous render was of an
      // iterable and _value will contain the NodeParts from the previous
      // render. If _value is not an array, clear this part and make a new
      // array for NodeParts.
      if (!Array.isArray(this.value)) {
        this.value = [];
        this.clear();
      } // Lets us keep track of how many items we stamped so we can clear leftover
      // items from a previous render


      const itemParts = this.value;
      let partIndex = 0;
      let itemPart;

      for (const item of value) {
        // Try to reuse an existing part
        itemPart = itemParts[partIndex]; // If no existing part, create a new one

        if (itemPart === undefined) {
          itemPart = new NodePart(this.options);
          itemParts.push(itemPart);

          if (partIndex === 0) {
            itemPart.appendIntoPart(this);
          } else {
            itemPart.insertAfterPart(itemParts[partIndex - 1]);
          }
        }

        itemPart.setValue(item);
        itemPart.commit();
        partIndex++;
      }

      if (partIndex < itemParts.length) {
        // Truncate the parts array so _value reflects the current state
        itemParts.length = partIndex;
        this.clear(itemPart && itemPart.endNode);
      }
    }

    clear(startNode = this.startNode) {
      removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode);
    }

  }
  /**
   * Implements a boolean attribute, roughly as defined in the HTML
   * specification.
   *
   * If the value is truthy, then the attribute is present with a value of
   * ''. If the value is falsey, the attribute is removed.
   */

  class BooleanAttributePart {
    constructor(element, name, strings) {
      this.value = undefined;
      this.__pendingValue = undefined;

      if (strings.length !== 2 || strings[0] !== '' || strings[1] !== '') {
        throw new Error('Boolean attributes can only contain a single expression');
      }

      this.element = element;
      this.name = name;
      this.strings = strings;
    }

    setValue(value) {
      this.__pendingValue = value;
    }

    commit() {
      while (isDirective(this.__pendingValue)) {
        const directive = this.__pendingValue;
        this.__pendingValue = noChange;
        directive(this);
      }

      if (this.__pendingValue === noChange) {
        return;
      }

      const value = !!this.__pendingValue;

      if (this.value !== value) {
        if (value) {
          this.element.setAttribute(this.name, '');
        } else {
          this.element.removeAttribute(this.name);
        }

        this.value = value;
      }

      this.__pendingValue = noChange;
    }

  }
  /**
   * Sets attribute values for PropertyParts, so that the value is only set once
   * even if there are multiple parts for a property.
   *
   * If an expression controls the whole property value, then the value is simply
   * assigned to the property under control. If there are string literals or
   * multiple expressions, then the strings are expressions are interpolated into
   * a string first.
   */

  class PropertyCommitter extends AttributeCommitter {
    constructor(element, name, strings) {
      super(element, name, strings);
      this.single = strings.length === 2 && strings[0] === '' && strings[1] === '';
    }

    _createPart() {
      return new PropertyPart(this);
    }

    _getValue() {
      if (this.single) {
        return this.parts[0].value;
      }

      return super._getValue();
    }

    commit() {
      if (this.dirty) {
        this.dirty = false; // eslint-disable-next-line @typescript-eslint/no-explicit-any

        this.element[this.name] = this._getValue();
      }
    }

  }
  class PropertyPart extends AttributePart {} // Detect event listener options support. If the `capture` property is read
  // from the options object, then options are supported. If not, then the third
  // argument to add/removeEventListener is interpreted as the boolean capture
  // value so we should only pass the `capture` property.

  let eventOptionsSupported = false; // Wrap into an IIFE because MS Edge <= v41 does not support having try/catch
  // blocks right into the body of a module

  (() => {
    try {
      const options = {
        get capture() {
          eventOptionsSupported = true;
          return false;
        }

      }; // eslint-disable-next-line @typescript-eslint/no-explicit-any

      window.addEventListener('test', options, options); // eslint-disable-next-line @typescript-eslint/no-explicit-any

      window.removeEventListener('test', options, options);
    } catch (_e) {// event options not supported
    }
  })();

  class EventPart {
    constructor(element, eventName, eventContext) {
      this.value = undefined;
      this.__pendingValue = undefined;
      this.element = element;
      this.eventName = eventName;
      this.eventContext = eventContext;

      this.__boundHandleEvent = e => this.handleEvent(e);
    }

    setValue(value) {
      this.__pendingValue = value;
    }

    commit() {
      while (isDirective(this.__pendingValue)) {
        const directive = this.__pendingValue;
        this.__pendingValue = noChange;
        directive(this);
      }

      if (this.__pendingValue === noChange) {
        return;
      }

      const newListener = this.__pendingValue;
      const oldListener = this.value;
      const shouldRemoveListener = newListener == null || oldListener != null && (newListener.capture !== oldListener.capture || newListener.once !== oldListener.once || newListener.passive !== oldListener.passive);
      const shouldAddListener = newListener != null && (oldListener == null || shouldRemoveListener);

      if (shouldRemoveListener) {
        this.element.removeEventListener(this.eventName, this.__boundHandleEvent, this.__options);
      }

      if (shouldAddListener) {
        this.__options = getOptions(newListener);
        this.element.addEventListener(this.eventName, this.__boundHandleEvent, this.__options);
      }

      this.value = newListener;
      this.__pendingValue = noChange;
    }

    handleEvent(event) {
      if (typeof this.value === 'function') {
        this.value.call(this.eventContext || this.element, event);
      } else {
        this.value.handleEvent(event);
      }
    }

  } // We copy options because of the inconsistent behavior of browsers when reading
  // the third argument of add/removeEventListener. IE11 doesn't support options
  // at all. Chrome 41 only reads `capture` if the argument is an object.

  const getOptions = o => o && (eventOptionsSupported ? {
    capture: o.capture,
    passive: o.passive,
    once: o.once
  } : o.capture);

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  /**
   * The default TemplateFactory which caches Templates keyed on
   * result.type and result.strings.
   */

  function templateFactory(result) {
    let templateCache = templateCaches.get(result.type);

    if (templateCache === undefined) {
      templateCache = {
        stringsArray: new WeakMap(),
        keyString: new Map()
      };
      templateCaches.set(result.type, templateCache);
    }

    let template = templateCache.stringsArray.get(result.strings);

    if (template !== undefined) {
      return template;
    } // If the TemplateStringsArray is new, generate a key from the strings
    // This key is shared between all templates with identical content


    const key = result.strings.join(marker); // Check if we already have a Template for this key

    template = templateCache.keyString.get(key);

    if (template === undefined) {
      // If we have not seen this key before, create a new Template
      template = new Template(result, result.getTemplateElement()); // Cache the Template for this key

      templateCache.keyString.set(key, template);
    } // Cache all future queries for this TemplateStringsArray


    templateCache.stringsArray.set(result.strings, template);
    return template;
  }
  const templateCaches = new Map();

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const parts = new WeakMap();
  /**
   * Renders a template result or other value to a container.
   *
   * To update a container with new values, reevaluate the template literal and
   * call `render` with the new result.
   *
   * @param result Any value renderable by NodePart - typically a TemplateResult
   *     created by evaluating a template tag like `html` or `svg`.
   * @param container A DOM parent to render to. The entire contents are either
   *     replaced, or efficiently updated if the same result type was previous
   *     rendered there.
   * @param options RenderOptions for the entire render tree rendered to this
   *     container. Render options must *not* change between renders to the same
   *     container, as those changes will not effect previously rendered DOM.
   */

  const render = (result, container, options) => {
    let part = parts.get(container);

    if (part === undefined) {
      removeNodes(container, container.firstChild);
      parts.set(container, part = new NodePart(Object.assign({
        templateFactory
      }, options)));
      part.appendInto(container);
    }

    part.setValue(result);
    part.commit();
  };

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  /**
   * Creates Parts when a template is instantiated.
   */

  class DefaultTemplateProcessor {
    /**
     * Create parts for an attribute-position binding, given the event, attribute
     * name, and string literals.
     *
     * @param element The element containing the binding
     * @param name  The attribute name
     * @param strings The string literals. There are always at least two strings,
     *   event for fully-controlled bindings with a single expression.
     */
    handleAttributeExpressions(element, name, strings, options) {
      const prefix = name[0];

      if (prefix === '.') {
        const committer = new PropertyCommitter(element, name.slice(1), strings);
        return committer.parts;
      }

      if (prefix === '@') {
        return [new EventPart(element, name.slice(1), options.eventContext)];
      }

      if (prefix === '?') {
        return [new BooleanAttributePart(element, name.slice(1), strings)];
      }

      const committer = new AttributeCommitter(element, name, strings);
      return committer.parts;
    }
    /**
     * Create parts for a text-position binding.
     * @param templateFactory
     */


    handleTextExpression(options) {
      return new NodePart(options);
    }

  }
  const defaultTemplateProcessor = new DefaultTemplateProcessor();

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  // This line will be used in regexes to search for lit-html usage.
  // TODO(justinfagnani): inject version number at build time

  if (typeof window !== 'undefined') {
    (window['litHtmlVersions'] || (window['litHtmlVersions'] = [])).push('1.2.1');
  }
  /**
   * Interprets a template literal as an HTML template that can efficiently
   * render to and update a container.
   */


  const html = (strings, ...values) => new TemplateResult(strings, values, 'html', defaultTemplateProcessor);
  /**
   * Interprets a template literal as an SVG template that can efficiently
   * render to and update a container.
   */

  const svg = (strings, ...values) => new SVGTemplateResult(strings, values, 'svg', defaultTemplateProcessor);

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */

  const getTemplateCacheKey = (type, scopeName) => `${type}--${scopeName}`;

  let compatibleShadyCSSVersion = true;

  if (typeof window.ShadyCSS === 'undefined') {
    compatibleShadyCSSVersion = false;
  } else if (typeof window.ShadyCSS.prepareTemplateDom === 'undefined') {
    console.warn(`Incompatible ShadyCSS version detected. ` + `Please update to at least @webcomponents/webcomponentsjs@2.0.2 and ` + `@webcomponents/shadycss@1.3.1.`);
    compatibleShadyCSSVersion = false;
  }
  /**
   * Template factory which scopes template DOM using ShadyCSS.
   * @param scopeName {string}
   */


  const shadyTemplateFactory = scopeName => result => {
    const cacheKey = getTemplateCacheKey(result.type, scopeName);
    let templateCache = templateCaches.get(cacheKey);

    if (templateCache === undefined) {
      templateCache = {
        stringsArray: new WeakMap(),
        keyString: new Map()
      };
      templateCaches.set(cacheKey, templateCache);
    }

    let template = templateCache.stringsArray.get(result.strings);

    if (template !== undefined) {
      return template;
    }

    const key = result.strings.join(marker);
    template = templateCache.keyString.get(key);

    if (template === undefined) {
      const element = result.getTemplateElement();

      if (compatibleShadyCSSVersion) {
        window.ShadyCSS.prepareTemplateDom(element, scopeName);
      }

      template = new Template(result, element);
      templateCache.keyString.set(key, template);
    }

    templateCache.stringsArray.set(result.strings, template);
    return template;
  };

  const TEMPLATE_TYPES = ['html', 'svg'];
  /**
   * Removes all style elements from Templates for the given scopeName.
   */

  const removeStylesFromLitTemplates = scopeName => {
    TEMPLATE_TYPES.forEach(type => {
      const templates = templateCaches.get(getTemplateCacheKey(type, scopeName));

      if (templates !== undefined) {
        templates.keyString.forEach(template => {
          const {
            element: {
              content
            }
          } = template; // IE 11 doesn't support the iterable param Set constructor

          const styles = new Set();
          Array.from(content.querySelectorAll('style')).forEach(s => {
            styles.add(s);
          });
          removeNodesFromTemplate(template, styles);
        });
      }
    });
  };

  const shadyRenderSet = new Set();
  /**
   * For the given scope name, ensures that ShadyCSS style scoping is performed.
   * This is done just once per scope name so the fragment and template cannot
   * be modified.
   * (1) extracts styles from the rendered fragment and hands them to ShadyCSS
   * to be scoped and appended to the document
   * (2) removes style elements from all lit-html Templates for this scope name.
   *
   * Note, <style> elements can only be placed into templates for the
   * initial rendering of the scope. If <style> elements are included in templates
   * dynamically rendered to the scope (after the first scope render), they will
   * not be scoped and the <style> will be left in the template and rendered
   * output.
   */

  const prepareTemplateStyles = (scopeName, renderedDOM, template) => {
    shadyRenderSet.add(scopeName); // If `renderedDOM` is stamped from a Template, then we need to edit that
    // Template's underlying template element. Otherwise, we create one here
    // to give to ShadyCSS, which still requires one while scoping.

    const templateElement = !!template ? template.element : document.createElement('template'); // Move styles out of rendered DOM and store.

    const styles = renderedDOM.querySelectorAll('style');
    const {
      length
    } = styles; // If there are no styles, skip unnecessary work

    if (length === 0) {
      // Ensure prepareTemplateStyles is called to support adding
      // styles via `prepareAdoptedCssText` since that requires that
      // `prepareTemplateStyles` is called.
      //
      // ShadyCSS will only update styles containing @apply in the template
      // given to `prepareTemplateStyles`. If no lit Template was given,
      // ShadyCSS will not be able to update uses of @apply in any relevant
      // template. However, this is not a problem because we only create the
      // template for the purpose of supporting `prepareAdoptedCssText`,
      // which doesn't support @apply at all.
      window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName);
      return;
    }

    const condensedStyle = document.createElement('style'); // Collect styles into a single style. This helps us make sure ShadyCSS
    // manipulations will not prevent us from being able to fix up template
    // part indices.
    // NOTE: collecting styles is inefficient for browsers but ShadyCSS
    // currently does this anyway. When it does not, this should be changed.

    for (let i = 0; i < length; i++) {
      const style = styles[i];
      style.parentNode.removeChild(style);
      condensedStyle.textContent += style.textContent;
    } // Remove styles from nested templates in this scope.


    removeStylesFromLitTemplates(scopeName); // And then put the condensed style into the "root" template passed in as
    // `template`.

    const content = templateElement.content;

    if (!!template) {
      insertNodeIntoTemplate(template, condensedStyle, content.firstChild);
    } else {
      content.insertBefore(condensedStyle, content.firstChild);
    } // Note, it's important that ShadyCSS gets the template that `lit-html`
    // will actually render so that it can update the style inside when
    // needed (e.g. @apply native Shadow DOM case).


    window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName);
    const style = content.querySelector('style');

    if (window.ShadyCSS.nativeShadow && style !== null) {
      // When in native Shadow DOM, ensure the style created by ShadyCSS is
      // included in initially rendered output (`renderedDOM`).
      renderedDOM.insertBefore(style.cloneNode(true), renderedDOM.firstChild);
    } else if (!!template) {
      // When no style is left in the template, parts will be broken as a
      // result. To fix this, we put back the style node ShadyCSS removed
      // and then tell lit to remove that node from the template.
      // There can be no style in the template in 2 cases (1) when Shady DOM
      // is in use, ShadyCSS removes all styles, (2) when native Shadow DOM
      // is in use ShadyCSS removes the style if it contains no content.
      // NOTE, ShadyCSS creates its own style so we can safely add/remove
      // `condensedStyle` here.
      content.insertBefore(condensedStyle, content.firstChild);
      const removes = new Set();
      removes.add(condensedStyle);
      removeNodesFromTemplate(template, removes);
    }
  };
  /**
   * Extension to the standard `render` method which supports rendering
   * to ShadowRoots when the ShadyDOM (https://github.com/webcomponents/shadydom)
   * and ShadyCSS (https://github.com/webcomponents/shadycss) polyfills are used
   * or when the webcomponentsjs
   * (https://github.com/webcomponents/webcomponentsjs) polyfill is used.
   *
   * Adds a `scopeName` option which is used to scope element DOM and stylesheets
   * when native ShadowDOM is unavailable. The `scopeName` will be added to
   * the class attribute of all rendered DOM. In addition, any style elements will
   * be automatically re-written with this `scopeName` selector and moved out
   * of the rendered DOM and into the document `<head>`.
   *
   * It is common to use this render method in conjunction with a custom element
   * which renders a shadowRoot. When this is done, typically the element's
   * `localName` should be used as the `scopeName`.
   *
   * In addition to DOM scoping, ShadyCSS also supports a basic shim for css
   * custom properties (needed only on older browsers like IE11) and a shim for
   * a deprecated feature called `@apply` that supports applying a set of css
   * custom properties to a given location.
   *
   * Usage considerations:
   *
   * * Part values in `<style>` elements are only applied the first time a given
   * `scopeName` renders. Subsequent changes to parts in style elements will have
   * no effect. Because of this, parts in style elements should only be used for
   * values that will never change, for example parts that set scope-wide theme
   * values or parts which render shared style elements.
   *
   * * Note, due to a limitation of the ShadyDOM polyfill, rendering in a
   * custom element's `constructor` is not supported. Instead rendering should
   * either done asynchronously, for example at microtask timing (for example
   * `Promise.resolve()`), or be deferred until the first time the element's
   * `connectedCallback` runs.
   *
   * Usage considerations when using shimmed custom properties or `@apply`:
   *
   * * Whenever any dynamic changes are made which affect
   * css custom properties, `ShadyCSS.styleElement(element)` must be called
   * to update the element. There are two cases when this is needed:
   * (1) the element is connected to a new parent, (2) a class is added to the
   * element that causes it to match different custom properties.
   * To address the first case when rendering a custom element, `styleElement`
   * should be called in the element's `connectedCallback`.
   *
   * * Shimmed custom properties may only be defined either for an entire
   * shadowRoot (for example, in a `:host` rule) or via a rule that directly
   * matches an element with a shadowRoot. In other words, instead of flowing from
   * parent to child as do native css custom properties, shimmed custom properties
   * flow only from shadowRoots to nested shadowRoots.
   *
   * * When using `@apply` mixing css shorthand property names with
   * non-shorthand names (for example `border` and `border-width`) is not
   * supported.
   */


  const render$1 = (result, container, options) => {
    if (!options || typeof options !== 'object' || !options.scopeName) {
      throw new Error('The `scopeName` option is required.');
    }

    const scopeName = options.scopeName;
    const hasRendered = parts.has(container);
    const needsScoping = compatibleShadyCSSVersion && container.nodeType === 11
    /* Node.DOCUMENT_FRAGMENT_NODE */
    && !!container.host; // Handle first render to a scope specially...

    const firstScopeRender = needsScoping && !shadyRenderSet.has(scopeName); // On first scope render, render into a fragment; this cannot be a single
    // fragment that is reused since nested renders can occur synchronously.

    const renderContainer = firstScopeRender ? document.createDocumentFragment() : container;
    render(result, renderContainer, Object.assign({
      templateFactory: shadyTemplateFactory(scopeName)
    }, options)); // When performing first scope render,
    // (1) We've rendered into a fragment so that there's a chance to
    // `prepareTemplateStyles` before sub-elements hit the DOM
    // (which might cause them to render based on a common pattern of
    // rendering in a custom element's `connectedCallback`);
    // (2) Scope the template with ShadyCSS one time only for this scope.
    // (3) Render the fragment into the container and make sure the
    // container knows its `part` is the one we just rendered. This ensures
    // DOM will be re-used on subsequent renders.

    if (firstScopeRender) {
      const part = parts.get(renderContainer);
      parts.delete(renderContainer); // ShadyCSS might have style sheets (e.g. from `prepareAdoptedCssText`)
      // that should apply to `renderContainer` even if the rendered value is
      // not a TemplateInstance. However, it will only insert scoped styles
      // into the document if `prepareTemplateStyles` has already been called
      // for the given scope name.

      const template = part.value instanceof TemplateInstance ? part.value.template : undefined;
      prepareTemplateStyles(scopeName, renderContainer, template);
      removeNodes(container, container.firstChild);
      container.appendChild(renderContainer);
      parts.set(container, part);
    } // After elements have hit the DOM, update styling if this is the
    // initial render to this container.
    // This is needed whenever dynamic changes are made so it would be
    // safest to do every render; however, this would regress performance
    // so we leave it up to the user to call `ShadyCSS.styleElement`
    // for dynamic changes.


    if (!hasRendered && needsScoping) {
      window.ShadyCSS.styleElement(container.host);
    }
  };

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  var _a;
  /**
   * When using Closure Compiler, JSCompiler_renameProperty(property, object) is
   * replaced at compile time by the munged name for object[property]. We cannot
   * alias this function, so we have to use a small shim that has the same
   * behavior when not compiling.
   */


  window.JSCompiler_renameProperty = (prop, _obj) => prop;

  const defaultConverter = {
    toAttribute(value, type) {
      switch (type) {
        case Boolean:
          return value ? '' : null;

        case Object:
        case Array:
          // if the value is `null` or `undefined` pass this through
          // to allow removing/no change behavior.
          return value == null ? value : JSON.stringify(value);
      }

      return value;
    },

    fromAttribute(value, type) {
      switch (type) {
        case Boolean:
          return value !== null;

        case Number:
          return value === null ? null : Number(value);

        case Object:
        case Array:
          return JSON.parse(value);
      }

      return value;
    }

  };
  /**
   * Change function that returns true if `value` is different from `oldValue`.
   * This method is used as the default for a property's `hasChanged` function.
   */

  const notEqual = (value, old) => {
    // This ensures (old==NaN, value==NaN) always returns false
    return old !== value && (old === old || value === value);
  };
  const defaultPropertyDeclaration = {
    attribute: true,
    type: String,
    converter: defaultConverter,
    reflect: false,
    hasChanged: notEqual
  };
  const STATE_HAS_UPDATED = 1;
  const STATE_UPDATE_REQUESTED = 1 << 2;
  const STATE_IS_REFLECTING_TO_ATTRIBUTE = 1 << 3;
  const STATE_IS_REFLECTING_TO_PROPERTY = 1 << 4;
  /**
   * The Closure JS Compiler doesn't currently have good support for static
   * property semantics where "this" is dynamic (e.g.
   * https://github.com/google/closure-compiler/issues/3177 and others) so we use
   * this hack to bypass any rewriting by the compiler.
   */

  const finalized = 'finalized';
  /**
   * Base element class which manages element properties and attributes. When
   * properties change, the `update` method is asynchronously called. This method
   * should be supplied by subclassers to render updates as desired.
   */

  class UpdatingElement extends HTMLElement {
    constructor() {
      super();
      this._updateState = 0;
      this._instanceProperties = undefined; // Initialize to an unresolved Promise so we can make sure the element has
      // connected before first update.

      this._updatePromise = new Promise(res => this._enableUpdatingResolver = res);
      /**
       * Map with keys for any properties that have changed since the last
       * update cycle with previous values.
       */

      this._changedProperties = new Map();
      /**
       * Map with keys of properties that should be reflected when updated.
       */

      this._reflectingProperties = undefined;
      this.initialize();
    }
    /**
     * Returns a list of attributes corresponding to the registered properties.
     * @nocollapse
     */


    static get observedAttributes() {
      // note: piggy backing on this to ensure we're finalized.
      this.finalize();
      const attributes = []; // Use forEach so this works even if for/of loops are compiled to for loops
      // expecting arrays

      this._classProperties.forEach((v, p) => {
        const attr = this._attributeNameForProperty(p, v);

        if (attr !== undefined) {
          this._attributeToPropertyMap.set(attr, p);

          attributes.push(attr);
        }
      });

      return attributes;
    }
    /**
     * Ensures the private `_classProperties` property metadata is created.
     * In addition to `finalize` this is also called in `createProperty` to
     * ensure the `@property` decorator can add property metadata.
     */

    /** @nocollapse */


    static _ensureClassProperties() {
      // ensure private storage for property declarations.
      if (!this.hasOwnProperty(JSCompiler_renameProperty('_classProperties', this))) {
        this._classProperties = new Map(); // NOTE: Workaround IE11 not supporting Map constructor argument.

        const superProperties = Object.getPrototypeOf(this)._classProperties;

        if (superProperties !== undefined) {
          superProperties.forEach((v, k) => this._classProperties.set(k, v));
        }
      }
    }
    /**
     * Creates a property accessor on the element prototype if one does not exist
     * and stores a PropertyDeclaration for the property with the given options.
     * The property setter calls the property's `hasChanged` property option
     * or uses a strict identity check to determine whether or not to request
     * an update.
     *
     * This method may be overridden to customize properties; however,
     * when doing so, it's important to call `super.createProperty` to ensure
     * the property is setup correctly. This method calls
     * `getPropertyDescriptor` internally to get a descriptor to install.
     * To customize what properties do when they are get or set, override
     * `getPropertyDescriptor`. To customize the options for a property,
     * implement `createProperty` like this:
     *
     * static createProperty(name, options) {
     *   options = Object.assign(options, {myOption: true});
     *   super.createProperty(name, options);
     * }
     *
     * @nocollapse
     */


    static createProperty(name, options = defaultPropertyDeclaration) {
      // Note, since this can be called by the `@property` decorator which
      // is called before `finalize`, we ensure storage exists for property
      // metadata.
      this._ensureClassProperties();

      this._classProperties.set(name, options); // Do not generate an accessor if the prototype already has one, since
      // it would be lost otherwise and that would never be the user's intention;
      // Instead, we expect users to call `requestUpdate` themselves from
      // user-defined accessors. Note that if the super has an accessor we will
      // still overwrite it


      if (options.noAccessor || this.prototype.hasOwnProperty(name)) {
        return;
      }

      const key = typeof name === 'symbol' ? Symbol() : `__${name}`;
      const descriptor = this.getPropertyDescriptor(name, key, options);

      if (descriptor !== undefined) {
        Object.defineProperty(this.prototype, name, descriptor);
      }
    }
    /**
     * Returns a property descriptor to be defined on the given named property.
     * If no descriptor is returned, the property will not become an accessor.
     * For example,
     *
     *   class MyElement extends LitElement {
     *     static getPropertyDescriptor(name, key, options) {
     *       const defaultDescriptor =
     *           super.getPropertyDescriptor(name, key, options);
     *       const setter = defaultDescriptor.set;
     *       return {
     *         get: defaultDescriptor.get,
     *         set(value) {
     *           setter.call(this, value);
     *           // custom action.
     *         },
     *         configurable: true,
     *         enumerable: true
     *       }
     *     }
     *   }
     *
     * @nocollapse
     */


    static getPropertyDescriptor(name, key, _options) {
      return {
        // tslint:disable-next-line:no-any no symbol in index
        get() {
          return this[key];
        },

        set(value) {
          const oldValue = this[name];
          this[key] = value;

          this._requestUpdate(name, oldValue);
        },

        configurable: true,
        enumerable: true
      };
    }
    /**
     * Returns the property options associated with the given property.
     * These options are defined with a PropertyDeclaration via the `properties`
     * object or the `@property` decorator and are registered in
     * `createProperty(...)`.
     *
     * Note, this method should be considered "final" and not overridden. To
     * customize the options for a given property, override `createProperty`.
     *
     * @nocollapse
     * @final
     */


    static getPropertyOptions(name) {
      return this._classProperties && this._classProperties.get(name) || defaultPropertyDeclaration;
    }
    /**
     * Creates property accessors for registered properties and ensures
     * any superclasses are also finalized.
     * @nocollapse
     */


    static finalize() {
      // finalize any superclasses
      const superCtor = Object.getPrototypeOf(this);

      if (!superCtor.hasOwnProperty(finalized)) {
        superCtor.finalize();
      }

      this[finalized] = true;

      this._ensureClassProperties(); // initialize Map populated in observedAttributes


      this._attributeToPropertyMap = new Map(); // make any properties
      // Note, only process "own" properties since this element will inherit
      // any properties defined on the superClass, and finalization ensures
      // the entire prototype chain is finalized.

      if (this.hasOwnProperty(JSCompiler_renameProperty('properties', this))) {
        const props = this.properties; // support symbols in properties (IE11 does not support this)

        const propKeys = [...Object.getOwnPropertyNames(props), ...(typeof Object.getOwnPropertySymbols === 'function' ? Object.getOwnPropertySymbols(props) : [])]; // This for/of is ok because propKeys is an array

        for (const p of propKeys) {
          // note, use of `any` is due to TypeSript lack of support for symbol in
          // index types
          // tslint:disable-next-line:no-any no symbol in index
          this.createProperty(p, props[p]);
        }
      }
    }
    /**
     * Returns the property name for the given attribute `name`.
     * @nocollapse
     */


    static _attributeNameForProperty(name, options) {
      const attribute = options.attribute;
      return attribute === false ? undefined : typeof attribute === 'string' ? attribute : typeof name === 'string' ? name.toLowerCase() : undefined;
    }
    /**
     * Returns true if a property should request an update.
     * Called when a property value is set and uses the `hasChanged`
     * option for the property if present or a strict identity check.
     * @nocollapse
     */


    static _valueHasChanged(value, old, hasChanged = notEqual) {
      return hasChanged(value, old);
    }
    /**
     * Returns the property value for the given attribute value.
     * Called via the `attributeChangedCallback` and uses the property's
     * `converter` or `converter.fromAttribute` property option.
     * @nocollapse
     */


    static _propertyValueFromAttribute(value, options) {
      const type = options.type;
      const converter = options.converter || defaultConverter;
      const fromAttribute = typeof converter === 'function' ? converter : converter.fromAttribute;
      return fromAttribute ? fromAttribute(value, type) : value;
    }
    /**
     * Returns the attribute value for the given property value. If this
     * returns undefined, the property will *not* be reflected to an attribute.
     * If this returns null, the attribute will be removed, otherwise the
     * attribute will be set to the value.
     * This uses the property's `reflect` and `type.toAttribute` property options.
     * @nocollapse
     */


    static _propertyValueToAttribute(value, options) {
      if (options.reflect === undefined) {
        return;
      }

      const type = options.type;
      const converter = options.converter;
      const toAttribute = converter && converter.toAttribute || defaultConverter.toAttribute;
      return toAttribute(value, type);
    }
    /**
     * Performs element initialization. By default captures any pre-set values for
     * registered properties.
     */


    initialize() {
      this._saveInstanceProperties(); // ensures first update will be caught by an early access of
      // `updateComplete`


      this._requestUpdate();
    }
    /**
     * Fixes any properties set on the instance before upgrade time.
     * Otherwise these would shadow the accessor and break these properties.
     * The properties are stored in a Map which is played back after the
     * constructor runs. Note, on very old versions of Safari (<=9) or Chrome
     * (<=41), properties created for native platform properties like (`id` or
     * `name`) may not have default values set in the element constructor. On
     * these browsers native properties appear on instances and therefore their
     * default value will overwrite any element default (e.g. if the element sets
     * this.id = 'id' in the constructor, the 'id' will become '' since this is
     * the native platform default).
     */


    _saveInstanceProperties() {
      // Use forEach so this works even if for/of loops are compiled to for loops
      // expecting arrays
      this.constructor._classProperties.forEach((_v, p) => {
        if (this.hasOwnProperty(p)) {
          const value = this[p];
          delete this[p];

          if (!this._instanceProperties) {
            this._instanceProperties = new Map();
          }

          this._instanceProperties.set(p, value);
        }
      });
    }
    /**
     * Applies previously saved instance properties.
     */


    _applyInstanceProperties() {
      // Use forEach so this works even if for/of loops are compiled to for loops
      // expecting arrays
      // tslint:disable-next-line:no-any
      this._instanceProperties.forEach((v, p) => this[p] = v);

      this._instanceProperties = undefined;
    }

    connectedCallback() {
      // Ensure first connection completes an update. Updates cannot complete
      // before connection.
      this.enableUpdating();
    }

    enableUpdating() {
      if (this._enableUpdatingResolver !== undefined) {
        this._enableUpdatingResolver();

        this._enableUpdatingResolver = undefined;
      }
    }
    /**
     * Allows for `super.disconnectedCallback()` in extensions while
     * reserving the possibility of making non-breaking feature additions
     * when disconnecting at some point in the future.
     */


    disconnectedCallback() {}
    /**
     * Synchronizes property values when attributes change.
     */


    attributeChangedCallback(name, old, value) {
      if (old !== value) {
        this._attributeToProperty(name, value);
      }
    }

    _propertyToAttribute(name, value, options = defaultPropertyDeclaration) {
      const ctor = this.constructor;

      const attr = ctor._attributeNameForProperty(name, options);

      if (attr !== undefined) {
        const attrValue = ctor._propertyValueToAttribute(value, options); // an undefined value does not change the attribute.


        if (attrValue === undefined) {
          return;
        } // Track if the property is being reflected to avoid
        // setting the property again via `attributeChangedCallback`. Note:
        // 1. this takes advantage of the fact that the callback is synchronous.
        // 2. will behave incorrectly if multiple attributes are in the reaction
        // stack at time of calling. However, since we process attributes
        // in `update` this should not be possible (or an extreme corner case
        // that we'd like to discover).
        // mark state reflecting


        this._updateState = this._updateState | STATE_IS_REFLECTING_TO_ATTRIBUTE;

        if (attrValue == null) {
          this.removeAttribute(attr);
        } else {
          this.setAttribute(attr, attrValue);
        } // mark state not reflecting


        this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE;
      }
    }

    _attributeToProperty(name, value) {
      // Use tracking info to avoid deserializing attribute value if it was
      // just set from a property setter.
      if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) {
        return;
      }

      const ctor = this.constructor; // Note, hint this as an `AttributeMap` so closure clearly understands
      // the type; it has issues with tracking types through statics
      // tslint:disable-next-line:no-unnecessary-type-assertion

      const propName = ctor._attributeToPropertyMap.get(name);

      if (propName !== undefined) {
        const options = ctor.getPropertyOptions(propName); // mark state reflecting

        this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY;
        this[propName] = // tslint:disable-next-line:no-any
        ctor._propertyValueFromAttribute(value, options); // mark state not reflecting

        this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY;
      }
    }
    /**
     * This private version of `requestUpdate` does not access or return the
     * `updateComplete` promise. This promise can be overridden and is therefore
     * not free to access.
     */


    _requestUpdate(name, oldValue) {
      let shouldRequestUpdate = true; // If we have a property key, perform property update steps.

      if (name !== undefined) {
        const ctor = this.constructor;
        const options = ctor.getPropertyOptions(name);

        if (ctor._valueHasChanged(this[name], oldValue, options.hasChanged)) {
          if (!this._changedProperties.has(name)) {
            this._changedProperties.set(name, oldValue);
          } // Add to reflecting properties set.
          // Note, it's important that every change has a chance to add the
          // property to `_reflectingProperties`. This ensures setting
          // attribute + property reflects correctly.


          if (options.reflect === true && !(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) {
            if (this._reflectingProperties === undefined) {
              this._reflectingProperties = new Map();
            }

            this._reflectingProperties.set(name, options);
          }
        } else {
          // Abort the request if the property should not be considered changed.
          shouldRequestUpdate = false;
        }
      }

      if (!this._hasRequestedUpdate && shouldRequestUpdate) {
        this._updatePromise = this._enqueueUpdate();
      }
    }
    /**
     * Requests an update which is processed asynchronously. This should
     * be called when an element should update based on some state not triggered
     * by setting a property. In this case, pass no arguments. It should also be
     * called when manually implementing a property setter. In this case, pass the
     * property `name` and `oldValue` to ensure that any configured property
     * options are honored. Returns the `updateComplete` Promise which is resolved
     * when the update completes.
     *
     * @param name {PropertyKey} (optional) name of requesting property
     * @param oldValue {any} (optional) old value of requesting property
     * @returns {Promise} A Promise that is resolved when the update completes.
     */


    requestUpdate(name, oldValue) {
      this._requestUpdate(name, oldValue);

      return this.updateComplete;
    }
    /**
     * Sets up the element to asynchronously update.
     */


    async _enqueueUpdate() {
      this._updateState = this._updateState | STATE_UPDATE_REQUESTED;

      try {
        // Ensure any previous update has resolved before updating.
        // This `await` also ensures that property changes are batched.
        await this._updatePromise;
      } catch (e) {// Ignore any previous errors. We only care that the previous cycle is
        // done. Any error should have been handled in the previous update.
      }

      const result = this.performUpdate(); // If `performUpdate` returns a Promise, we await it. This is done to
      // enable coordinating updates with a scheduler. Note, the result is
      // checked to avoid delaying an additional microtask unless we need to.

      if (result != null) {
        await result;
      }

      return !this._hasRequestedUpdate;
    }

    get _hasRequestedUpdate() {
      return this._updateState & STATE_UPDATE_REQUESTED;
    }

    get hasUpdated() {
      return this._updateState & STATE_HAS_UPDATED;
    }
    /**
     * Performs an element update. Note, if an exception is thrown during the
     * update, `firstUpdated` and `updated` will not be called.
     *
     * You can override this method to change the timing of updates. If this
     * method is overridden, `super.performUpdate()` must be called.
     *
     * For instance, to schedule updates to occur just before the next frame:
     *
     * ```
     * protected async performUpdate(): Promise<unknown> {
     *   await new Promise((resolve) => requestAnimationFrame(() => resolve()));
     *   super.performUpdate();
     * }
     * ```
     */


    performUpdate() {
      // Mixin instance properties once, if they exist.
      if (this._instanceProperties) {
        this._applyInstanceProperties();
      }

      let shouldUpdate = false;
      const changedProperties = this._changedProperties;

      try {
        shouldUpdate = this.shouldUpdate(changedProperties);

        if (shouldUpdate) {
          this.update(changedProperties);
        } else {
          this._markUpdated();
        }
      } catch (e) {
        // Prevent `firstUpdated` and `updated` from running when there's an
        // update exception.
        shouldUpdate = false; // Ensure element can accept additional updates after an exception.

        this._markUpdated();

        throw e;
      }

      if (shouldUpdate) {
        if (!(this._updateState & STATE_HAS_UPDATED)) {
          this._updateState = this._updateState | STATE_HAS_UPDATED;
          this.firstUpdated(changedProperties);
        }

        this.updated(changedProperties);
      }
    }

    _markUpdated() {
      this._changedProperties = new Map();
      this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED;
    }
    /**
     * Returns a Promise that resolves when the element has completed updating.
     * The Promise value is a boolean that is `true` if the element completed the
     * update without triggering another update. The Promise result is `false` if
     * a property was set inside `updated()`. If the Promise is rejected, an
     * exception was thrown during the update.
     *
     * To await additional asynchronous work, override the `_getUpdateComplete`
     * method. For example, it is sometimes useful to await a rendered element
     * before fulfilling this Promise. To do this, first await
     * `super._getUpdateComplete()`, then any subsequent state.
     *
     * @returns {Promise} The Promise returns a boolean that indicates if the
     * update resolved without triggering another update.
     */


    get updateComplete() {
      return this._getUpdateComplete();
    }
    /**
     * Override point for the `updateComplete` promise.
     *
     * It is not safe to override the `updateComplete` getter directly due to a
     * limitation in TypeScript which means it is not possible to call a
     * superclass getter (e.g. `super.updateComplete.then(...)`) when the target
     * language is ES5 (https://github.com/microsoft/TypeScript/issues/338).
     * This method should be overridden instead. For example:
     *
     *   class MyElement extends LitElement {
     *     async _getUpdateComplete() {
     *       await super._getUpdateComplete();
     *       await this._myChild.updateComplete;
     *     }
     *   }
     */


    _getUpdateComplete() {
      return this._updatePromise;
    }
    /**
     * Controls whether or not `update` should be called when the element requests
     * an update. By default, this method always returns `true`, but this can be
     * customized to control when to update.
     *
     * @param _changedProperties Map of changed properties with old values
     */


    shouldUpdate(_changedProperties) {
      return true;
    }
    /**
     * Updates the element. This method reflects property values to attributes.
     * It can be overridden to render and keep updated element DOM.
     * Setting properties inside this method will *not* trigger
     * another update.
     *
     * @param _changedProperties Map of changed properties with old values
     */


    update(_changedProperties) {
      if (this._reflectingProperties !== undefined && this._reflectingProperties.size > 0) {
        // Use forEach so this works even if for/of loops are compiled to for
        // loops expecting arrays
        this._reflectingProperties.forEach((v, k) => this._propertyToAttribute(k, this[k], v));

        this._reflectingProperties = undefined;
      }

      this._markUpdated();
    }
    /**
     * Invoked whenever the element is updated. Implement to perform
     * post-updating tasks via DOM APIs, for example, focusing an element.
     *
     * Setting properties inside this method will trigger the element to update
     * again after this update cycle completes.
     *
     * @param _changedProperties Map of changed properties with old values
     */


    updated(_changedProperties) {}
    /**
     * Invoked when the element is first updated. Implement to perform one time
     * work on the element after update.
     *
     * Setting properties inside this method will trigger the element to update
     * again after this update cycle completes.
     *
     * @param _changedProperties Map of changed properties with old values
     */


    firstUpdated(_changedProperties) {}

  }
  _a = finalized;
  /**
   * Marks class as having finished creating properties.
   */

  UpdatingElement[_a] = true;

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const legacyCustomElement = (tagName, clazz) => {
    window.customElements.define(tagName, clazz); // Cast as any because TS doesn't recognize the return type as being a
    // subtype of the decorated class when clazz is typed as
    // `Constructor<HTMLElement>` for some reason.
    // `Constructor<HTMLElement>` is helpful to make sure the decorator is
    // applied to elements however.
    // tslint:disable-next-line:no-any

    return clazz;
  };

  const standardCustomElement = (tagName, descriptor) => {
    const {
      kind,
      elements
    } = descriptor;
    return {
      kind,
      elements,

      // This callback is called once the class is otherwise fully defined
      finisher(clazz) {
        window.customElements.define(tagName, clazz);
      }

    };
  };
  /**
   * Class decorator factory that defines the decorated class as a custom element.
   *
   * ```
   * @customElement('my-element')
   * class MyElement {
   *   render() {
   *     return html``;
   *   }
   * }
   * ```
   *
   * @param tagName The name of the custom element to define.
   */


  const customElement = tagName => classOrDescriptor => typeof classOrDescriptor === 'function' ? legacyCustomElement(tagName, classOrDescriptor) : standardCustomElement(tagName, classOrDescriptor);

  const standardProperty = (options, element) => {
    // When decorating an accessor, pass it through and add property metadata.
    // Note, the `hasOwnProperty` check in `createProperty` ensures we don't
    // stomp over the user's accessor.
    if (element.kind === 'method' && element.descriptor && !('value' in element.descriptor)) {
      return Object.assign(Object.assign({}, element), {
        finisher(clazz) {
          clazz.createProperty(element.key, options);
        }

      });
    } else {
      // createProperty() takes care of defining the property, but we still
      // must return some kind of descriptor, so return a descriptor for an
      // unused prototype field. The finisher calls createProperty().
      return {
        kind: 'field',
        key: Symbol(),
        placement: 'own',
        descriptor: {},

        // When @babel/plugin-proposal-decorators implements initializers,
        // do this instead of the initializer below. See:
        // https://github.com/babel/babel/issues/9260 extras: [
        //   {
        //     kind: 'initializer',
        //     placement: 'own',
        //     initializer: descriptor.initializer,
        //   }
        // ],
        initializer() {
          if (typeof element.initializer === 'function') {
            this[element.key] = element.initializer.call(this);
          }
        },

        finisher(clazz) {
          clazz.createProperty(element.key, options);
        }

      };
    }
  };

  const legacyProperty = (options, proto, name) => {
    proto.constructor.createProperty(name, options);
  };
  /**
   * A property decorator which creates a LitElement property which reflects a
   * corresponding attribute value. A `PropertyDeclaration` may optionally be
   * supplied to configure property features.
   *
   * This decorator should only be used for public fields. Private or protected
   * fields should use the internalProperty decorator.
   *
   * @example
   *
   *     class MyElement {
   *       @property({ type: Boolean })
   *       clicked = false;
   *     }
   *
   * @ExportDecoratedItems
   */


  function property(options) {
    // tslint:disable-next-line:no-any decorator
    return (protoOrDescriptor, name) => name !== undefined ? legacyProperty(options, protoOrDescriptor, name) : standardProperty(options, protoOrDescriptor);
  }
  /**
   * Declares a private or protected property that still triggers updates to the
   * element when it changes.
   *
   * Properties declared this way must not be used from HTML or HTML templating
   * systems, they're solely for properties internal to the element. These
   * properties may be renamed by optimization tools like closure compiler.
   */

  function internalProperty(options) {
    return property({
      attribute: false,
      hasChanged: options === null || options === void 0 ? void 0 : options.hasChanged
    });
  }
  /**
   * A property decorator that converts a class property into a getter that
   * executes a querySelector on the element's renderRoot.
   *
   * @param selector A DOMString containing one or more selectors to match.
   *
   * See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
   *
   * @example
   *
   *     class MyElement {
   *       @query('#first')
   *       first;
   *
   *       render() {
   *         return html`
   *           <div id="first"></div>
   *           <div id="second"></div>
   *         `;
   *       }
   *     }
   *
   */

  function query(selector) {
    return (protoOrDescriptor, // tslint:disable-next-line:no-any decorator
    name) => {
      const descriptor = {
        get() {
          return this.renderRoot.querySelector(selector);
        },

        enumerable: true,
        configurable: true
      };
      return name !== undefined ? legacyQuery(descriptor, protoOrDescriptor, name) : standardQuery(descriptor, protoOrDescriptor);
    };
  } // Note, in the future, we may extend this decorator to support the use case
  // where the queried element may need to do work to become ready to interact
  // with (e.g. load some implementation code). If so, we might elect to
  // add a second argument defining a function that can be run to make the
  // queried element loaded/updated/ready.

  /**
   * A property decorator that converts a class property into a getter that
   * returns a promise that resolves to the result of a querySelector on the
   * element's renderRoot done after the element's `updateComplete` promise
   * resolves. When the queried property may change with element state, this
   * decorator can be used instead of requiring users to await the
   * `updateComplete` before accessing the property.
   *
   * @param selector A DOMString containing one or more selectors to match.
   *
   * See: https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
   *
   * @example
   *
   *     class MyElement {
   *       @queryAsync('#first')
   *       first;
   *
   *       render() {
   *         return html`
   *           <div id="first"></div>
   *           <div id="second"></div>
   *         `;
   *       }
   *     }
   *
   *     // external usage
   *     async doSomethingWithFirst() {
   *      (await aMyElement.first).doSomething();
   *     }
   */

  function queryAsync(selector) {
    return (protoOrDescriptor, // tslint:disable-next-line:no-any decorator
    name) => {
      const descriptor = {
        async get() {
          await this.updateComplete;
          return this.renderRoot.querySelector(selector);
        },

        enumerable: true,
        configurable: true
      };
      return name !== undefined ? legacyQuery(descriptor, protoOrDescriptor, name) : standardQuery(descriptor, protoOrDescriptor);
    };
  }

  const legacyQuery = (descriptor, proto, name) => {
    Object.defineProperty(proto, name, descriptor);
  };

  const standardQuery = (descriptor, element) => ({
    kind: 'method',
    placement: 'prototype',
    key: element.key,
    descriptor
  });

  const standardEventOptions = (options, element) => {
    return Object.assign(Object.assign({}, element), {
      finisher(clazz) {
        Object.assign(clazz.prototype[element.key], options);
      }

    });
  };

  const legacyEventOptions = // tslint:disable-next-line:no-any legacy decorator
  (options, proto, name) => {
    Object.assign(proto[name], options);
  };
  /**
   * Adds event listener options to a method used as an event listener in a
   * lit-html template.
   *
   * @param options An object that specifies event listener options as accepted by
   * `EventTarget#addEventListener` and `EventTarget#removeEventListener`.
   *
   * Current browsers support the `capture`, `passive`, and `once` options. See:
   * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters
   *
   * @example
   *
   *     class MyElement {
   *       clicked = false;
   *
   *       render() {
   *         return html`
   *           <div @click=${this._onClick}`>
   *             <button></button>
   *           </div>
   *         `;
   *       }
   *
   *       @eventOptions({capture: true})
   *       _onClick(e) {
   *         this.clicked = true;
   *       }
   *     }
   */


  function eventOptions(options) {
    // Return value typed as any to prevent TypeScript from complaining that
    // standard decorator function signature does not match TypeScript decorator
    // signature
    // TODO(kschaaf): unclear why it was only failing on this decorator and not
    // the others
    return (protoOrDescriptor, name) => name !== undefined ? legacyEventOptions(options, protoOrDescriptor, name) : standardEventOptions(options, protoOrDescriptor);
  }

  /**
  @license
  Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const supportsAdoptingStyleSheets = 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype;
  const constructionToken = Symbol();
  class CSSResult {
    constructor(cssText, safeToken) {
      if (safeToken !== constructionToken) {
        throw new Error('CSSResult is not constructable. Use `unsafeCSS` or `css` instead.');
      }

      this.cssText = cssText;
    } // Note, this is a getter so that it's lazy. In practice, this means
    // stylesheets are not created until the first element instance is made.


    get styleSheet() {
      if (this._styleSheet === undefined) {
        // Note, if `adoptedStyleSheets` is supported then we assume CSSStyleSheet
        // is constructable.
        if (supportsAdoptingStyleSheets) {
          this._styleSheet = new CSSStyleSheet();

          this._styleSheet.replaceSync(this.cssText);
        } else {
          this._styleSheet = null;
        }
      }

      return this._styleSheet;
    }

    toString() {
      return this.cssText;
    }

  }
  /**
   * Wrap a value for interpolation in a css tagged template literal.
   *
   * This is unsafe because untrusted CSS text can be used to phone home
   * or exfiltrate data to an attacker controlled site. Take care to only use
   * this with trusted input.
   */

  const unsafeCSS = value => {
    return new CSSResult(String(value), constructionToken);
  };

  const textFromCSSResult = value => {
    if (value instanceof CSSResult) {
      return value.cssText;
    } else if (typeof value === 'number') {
      return value;
    } else {
      throw new Error(`Value passed to 'css' function must be a 'css' function result: ${value}. Use 'unsafeCSS' to pass non-literal values, but
            take care to ensure page security.`);
    }
  };
  /**
   * Template tag which which can be used with LitElement's `style` property to
   * set element styles. For security reasons, only literal string values may be
   * used. To incorporate non-literal values `unsafeCSS` may be used inside a
   * template string part.
   */


  const css = (strings, ...values) => {
    const cssText = values.reduce((acc, v, idx) => acc + textFromCSSResult(v) + strings[idx + 1], strings[0]);
    return new CSSResult(cssText, constructionToken);
  };

  /**
   * @license
   * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  // This line will be used in regexes to search for LitElement usage.
  // TODO(justinfagnani): inject version number at build time

  (window['litElementVersions'] || (window['litElementVersions'] = [])).push('2.3.1');
  /**
   * Sentinal value used to avoid calling lit-html's render function when
   * subclasses do not implement `render`
   */

  const renderNotImplemented = {};
  class LitElement extends UpdatingElement {
    /**
     * Return the array of styles to apply to the element.
     * Override this method to integrate into a style management system.
     *
     * @nocollapse
     */
    static getStyles() {
      return this.styles;
    }
    /** @nocollapse */


    static _getUniqueStyles() {
      // Only gather styles once per class
      if (this.hasOwnProperty(JSCompiler_renameProperty('_styles', this))) {
        return;
      } // Take care not to call `this.getStyles()` multiple times since this
      // generates new CSSResults each time.
      // TODO(sorvell): Since we do not cache CSSResults by input, any
      // shared styles will generate new stylesheet objects, which is wasteful.
      // This should be addressed when a browser ships constructable
      // stylesheets.


      const userStyles = this.getStyles();

      if (userStyles === undefined) {
        this._styles = [];
      } else if (Array.isArray(userStyles)) {
        // De-duplicate styles preserving the _last_ instance in the set.
        // This is a performance optimization to avoid duplicated styles that can
        // occur especially when composing via subclassing.
        // The last item is kept to try to preserve the cascade order with the
        // assumption that it's most important that last added styles override
        // previous styles.
        const addStyles = (styles, set) => styles.reduceRight((set, s) => // Note: On IE set.add() does not return the set
        Array.isArray(s) ? addStyles(s, set) : (set.add(s), set), set); // Array.from does not work on Set in IE, otherwise return
        // Array.from(addStyles(userStyles, new Set<CSSResult>())).reverse()


        const set = addStyles(userStyles, new Set());
        const styles = [];
        set.forEach(v => styles.unshift(v));
        this._styles = styles;
      } else {
        this._styles = [userStyles];
      }
    }
    /**
     * Performs element initialization. By default this calls `createRenderRoot`
     * to create the element `renderRoot` node and captures any pre-set values for
     * registered properties.
     */


    initialize() {
      super.initialize();

      this.constructor._getUniqueStyles();

      this.renderRoot = this.createRenderRoot(); // Note, if renderRoot is not a shadowRoot, styles would/could apply to the
      // element's getRootNode(). While this could be done, we're choosing not to
      // support this now since it would require different logic around de-duping.

      if (window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot) {
        this.adoptStyles();
      }
    }
    /**
     * Returns the node into which the element should render and by default
     * creates and returns an open shadowRoot. Implement to customize where the
     * element's DOM is rendered. For example, to render into the element's
     * childNodes, return `this`.
     * @returns {Element|DocumentFragment} Returns a node into which to render.
     */


    createRenderRoot() {
      return this.attachShadow({
        mode: 'open'
      });
    }
    /**
     * Applies styling to the element shadowRoot using the `static get styles`
     * property. Styling will apply using `shadowRoot.adoptedStyleSheets` where
     * available and will fallback otherwise. When Shadow DOM is polyfilled,
     * ShadyCSS scopes styles and adds them to the document. When Shadow DOM
     * is available but `adoptedStyleSheets` is not, styles are appended to the
     * end of the `shadowRoot` to [mimic spec
     * behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets).
     */


    adoptStyles() {
      const styles = this.constructor._styles;

      if (styles.length === 0) {
        return;
      } // There are three separate cases here based on Shadow DOM support.
      // (1) shadowRoot polyfilled: use ShadyCSS
      // (2) shadowRoot.adoptedStyleSheets available: use it.
      // (3) shadowRoot.adoptedStyleSheets polyfilled: append styles after
      // rendering


      if (window.ShadyCSS !== undefined && !window.ShadyCSS.nativeShadow) {
        window.ShadyCSS.ScopingShim.prepareAdoptedCssText(styles.map(s => s.cssText), this.localName);
      } else if (supportsAdoptingStyleSheets) {
        this.renderRoot.adoptedStyleSheets = styles.map(s => s.styleSheet);
      } else {
        // This must be done after rendering so the actual style insertion is done
        // in `update`.
        this._needsShimAdoptedStyleSheets = true;
      }
    }

    connectedCallback() {
      super.connectedCallback(); // Note, first update/render handles styleElement so we only call this if
      // connected after first update.

      if (this.hasUpdated && window.ShadyCSS !== undefined) {
        window.ShadyCSS.styleElement(this);
      }
    }
    /**
     * Updates the element. This method reflects property values to attributes
     * and calls `render` to render DOM via lit-html. Setting properties inside
     * this method will *not* trigger another update.
     * @param _changedProperties Map of changed properties with old values
     */


    update(changedProperties) {
      // Setting properties in `render` should not trigger an update. Since
      // updates are allowed after super.update, it's important to call `render`
      // before that.
      const templateResult = this.render();
      super.update(changedProperties); // If render is not implemented by the component, don't call lit-html render

      if (templateResult !== renderNotImplemented) {
        this.constructor.render(templateResult, this.renderRoot, {
          scopeName: this.localName,
          eventContext: this
        });
      } // When native Shadow DOM is used but adoptedStyles are not supported,
      // insert styling after rendering to ensure adoptedStyles have highest
      // priority.


      if (this._needsShimAdoptedStyleSheets) {
        this._needsShimAdoptedStyleSheets = false;

        this.constructor._styles.forEach(s => {
          const style = document.createElement('style');
          style.textContent = s.cssText;
          this.renderRoot.appendChild(style);
        });
      }
    }
    /**
     * Invoked on each update to perform rendering tasks. This method may return
     * any value renderable by lit-html's NodePart - typically a TemplateResult.
     * Setting properties inside this method will *not* trigger the element to
     * update.
     */


    render() {
      return renderNotImplemented;
    }

  }
  /**
   * Ensure this class is marked as `finalized` as an optimization ensuring
   * it will not needlessly try to `finalize`.
   *
   * Note this property name is a string to prevent breaking Closure JS Compiler
   * optimizations. See updating-element.ts for more information.
   */

  LitElement['finalized'] = true;
  /**
   * Render method used to render the value to the element's DOM.
   * @param result The value to render.
   * @param container Node into which to render.
   * @param options Element name.
   * @nocollapse
   */

  LitElement.render = render$1;

  // Polymer legacy event helpers used courtesy of the Polymer project.
  //
  // Copyright (c) 2017 The Polymer Authors. All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are
  // met:
  //
  //    * Redistributions of source code must retain the above copyright
  // notice, this list of conditions and the following disclaimer.
  //    * Redistributions in binary form must reproduce the above
  // copyright notice, this list of conditions and the following disclaimer
  // in the documentation and/or other materials provided with the
  // distribution.
  //    * Neither the name of Google Inc. nor the names of its
  // contributors may be used to endorse or promote products derived from
  // this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  /**
   * Dispatches a custom event with an optional detail value.
   *
   * @param {string} type Name of event type.
   * @param {*=} detail Detail value containing event-specific
   *   payload.
   * @param {{ bubbles: (boolean|undefined),
   *           cancelable: (boolean|undefined),
   *           composed: (boolean|undefined) }=}
   *  options Object specifying options.  These may include:
   *  `bubbles` (boolean, defaults to `true`),
   *  `cancelable` (boolean, defaults to false), and
   *  `node` on which to fire the event (HTMLElement, defaults to `this`).
   * @return {Event} The new event that was fired.
   */
  const fireEvent = (node, type, detail, options) => {
    options = options || {}; // @ts-ignore

    detail = detail === null || detail === undefined ? {} : detail;
    const event = new Event(type, {
      bubbles: options.bubbles === undefined ? true : options.bubbles,
      cancelable: Boolean(options.cancelable),
      composed: options.composed === undefined ? true : options.composed
    });
    event.detail = detail;
    node.dispatchEvent(event);
    return event;
  };

  const navigate = (_node, path, replace = false) => {
    if (__DEMO__) {
      if (replace) {
        history.replaceState(null, "", `${location.pathname}#${path}`);
      } else {
        window.location.hash = path;
      }
    } else if (replace) {
      history.replaceState(null, "", path);
    } else {
      history.pushState(null, "", path);
    }

    fireEvent(window, "location-changed", {
      replace
    });
  };

  const darkStyles = {
    "primary-background-color": "#111111",
    "card-background-color": "#1c1c1c",
    "secondary-background-color": "#1e1e1e",
    "primary-text-color": "#e1e1e1",
    "secondary-text-color": "#9b9b9b",
    "disabled-text-color": "#6f6f6f",
    "app-header-text-color": "#e1e1e1",
    "app-header-background-color": "#1c1c1c",
    "switch-unchecked-button-color": "#999999",
    "switch-unchecked-track-color": "#9b9b9b",
    "divider-color": "rgba(225, 225, 225, .12)",
    "codemirror-keyword": "#C792EA",
    "codemirror-operator": "#89DDFF",
    "codemirror-variable": "#f07178",
    "codemirror-variable-2": "#EEFFFF",
    "codemirror-variable-3": "#DECB6B",
    "codemirror-builtin": "#FFCB6B",
    "codemirror-atom": "#F78C6C",
    "codemirror-number": "#FF5370",
    "codemirror-def": "#82AAFF",
    "codemirror-string": "#C3E88D",
    "codemirror-string-2": "#f07178",
    "codemirror-comment": "#545454",
    "codemirror-tag": "#FF5370",
    "codemirror-meta": "#FFCB6B",
    "codemirror-attribute": "#C792EA",
    "codemirror-property": "#C792EA",
    "codemirror-qualifier": "#DECB6B",
    "codemirror-type": "#DECB6B"
  };
  const derivedStyles = {
    "error-state-color": "var(--error-color)",
    "state-icon-unavailable-color": "var(--disabled-text-color)",
    "sidebar-text-color": "var(--primary-text-color)",
    "sidebar-background-color": "var(--card-background-color)",
    "sidebar-selected-text-color": "var(--primary-color)",
    "sidebar-selected-icon-color": "var(--primary-color)",
    "sidebar-icon-color": "rgba(var(--rgb-primary-text-color), 0.6)",
    "switch-checked-color": "var(--primary-color)",
    "switch-checked-button-color": "var(--switch-checked-color, var(--primary-background-color))",
    "switch-checked-track-color": "var(--switch-checked-color, #000000)",
    "switch-unchecked-button-color": "var(--switch-unchecked-color, var(--primary-background-color))",
    "switch-unchecked-track-color": "var(--switch-unchecked-color, #000000)",
    "slider-color": "var(--primary-color)",
    "slider-secondary-color": "var(--light-primary-color)",
    "slider-bar-color": "var(--disabled-text-color)",
    "label-badge-grey": "var(--paper-grey-500)",
    "label-badge-background-color": "var(--card-background-color)",
    "label-badge-text-color": "rgba(var(--rgb-primary-text-color), 0.8)",
    "paper-listbox-background-color": "var(--card-background-color)",
    "paper-item-icon-color": "var(--state-icon-color)",
    "paper-item-icon-active-color": "var(--state-icon-active-color)",
    "table-row-background-color": "var(--primary-background-color)",
    "table-row-alternative-background-color": "var(--secondary-background-color)",
    "paper-slider-knob-color": "var(--slider-color)",
    "paper-slider-knob-start-color": "var(--slider-color)",
    "paper-slider-pin-color": "var(--slider-color)",
    "paper-slider-active-color": "var(--slider-color)",
    "paper-slider-secondary-color": "var(--slider-secondary-color)",
    "paper-slider-container-color": "var(--slider-bar-color)",
    "data-table-background-color": "var(--card-background-color)",
    "markdown-code-background-color": "var(--primary-background-color)",
    "mdc-theme-primary": "var(--primary-color)",
    "mdc-theme-secondary": "var(--accent-color)",
    "mdc-theme-background": "var(--primary-background-color)",
    "mdc-theme-surface": "var(--card-background-color)",
    "mdc-theme-on-primary": "var(--text-primary-color)",
    "mdc-theme-on-secondary": "var(--text-primary-color)",
    "mdc-theme-on-surface": "var(--primary-text-color)",
    "mdc-theme-text-primary-on-background": "var(--primary-text-color)",
    "app-header-text-color": "var(--text-primary-color)",
    "app-header-background-color": "var(--primary-color)",
    "material-body-text-color": "var(--primary-text-color)",
    "material-background-color": "var(--card-background-color)",
    "material-secondary-background-color": "var(--secondary-background-color)",
    "material-secondary-text-color": "var(--secondary-text-color)",
    "mdc-checkbox-unchecked-color": "rgba(var(--rgb-primary-text-color), 0.54)",
    "mdc-checkbox-disabled-color": "var(--disabled-text-color)",
    "mdc-radio-unchecked-color": "rgba(var(--rgb-primary-text-color), 0.54)",
    "mdc-radio-disabled-color": "var(--disabled-text-color)",
    "mdc-tab-text-label-color-default": "var(--primary-text-color)",
    "mdc-button-disabled-ink-color": "var(--disabled-text-color)"
  };
  const haStyle = css`
  :host {
    font-family: var(--paper-font-body1_-_font-family);
    -webkit-font-smoothing: var(--paper-font-body1_-_-webkit-font-smoothing);
    font-size: var(--paper-font-body1_-_font-size);
    font-weight: var(--paper-font-body1_-_font-weight);
    line-height: var(--paper-font-body1_-_line-height);
  }

  app-header-layout,
  ha-app-layout {
    background-color: var(--primary-background-color);
  }

  app-header,
  app-toolbar {
    background-color: var(--app-header-background-color);
    font-weight: 400;
    color: var(--app-header-text-color, white);
  }

  app-toolbar ha-menu-button + [main-title],
  app-toolbar ha-icon-button-arrow-prev + [main-title],
  app-toolbar ha-icon-button + [main-title] {
    margin-left: 24px;
  }

  h1 {
    font-family: var(--paper-font-headline_-_font-family);
    -webkit-font-smoothing: var(--paper-font-headline_-_-webkit-font-smoothing);
    white-space: var(--paper-font-headline_-_white-space);
    overflow: var(--paper-font-headline_-_overflow);
    text-overflow: var(--paper-font-headline_-_text-overflow);
    font-size: var(--paper-font-headline_-_font-size);
    font-weight: var(--paper-font-headline_-_font-weight);
    line-height: var(--paper-font-headline_-_line-height);
  }

  h2 {
    font-family: var(--paper-font-title_-_font-family);
    -webkit-font-smoothing: var(--paper-font-title_-_-webkit-font-smoothing);
    white-space: var(--paper-font-title_-_white-space);
    overflow: var(--paper-font-title_-_overflow);
    text-overflow: var(--paper-font-title_-_text-overflow);
    font-size: var(--paper-font-title_-_font-size);
    font-weight: var(--paper-font-title_-_font-weight);
    line-height: var(--paper-font-title_-_line-height);
  }

  h3 {
    font-family: var(--paper-font-subhead_-_font-family);
    -webkit-font-smoothing: var(--paper-font-subhead_-_-webkit-font-smoothing);
    white-space: var(--paper-font-subhead_-_white-space);
    overflow: var(--paper-font-subhead_-_overflow);
    text-overflow: var(--paper-font-subhead_-_text-overflow);
    font-size: var(--paper-font-subhead_-_font-size);
    font-weight: var(--paper-font-subhead_-_font-weight);
    line-height: var(--paper-font-subhead_-_line-height);
  }

  a {
    color: var(--primary-color);
  }

  .secondary {
    color: var(--secondary-text-color);
  }

  .error {
    color: var(--error-color);
  }

  .warning {
    color: var(--error-color);
  }

  mwc-button.warning {
    --mdc-theme-primary: var(--error-color);
  }

  button.link {
    background: none;
    color: inherit;
    border: none;
    padding: 0;
    font: inherit;
    text-align: left;
    text-decoration: underline;
    cursor: pointer;
  }

  .card-actions a {
    text-decoration: none;
  }

  .card-actions .warning {
    --mdc-theme-primary: var(--error-color);
  }

  .layout.horizontal,
  .layout.vertical {
    display: flex;
  }
  .layout.inline {
    display: inline-flex;
  }
  .layout.horizontal {
    flex-direction: row;
  }
  .layout.vertical {
    flex-direction: column;
  }
  .layout.wrap {
    flex-wrap: wrap;
  }
  .layout.no-wrap {
    flex-wrap: nowrap;
  }
  .layout.center,
  .layout.center-center {
    align-items: center;
  }
  .layout.bottom {
    align-items: flex-end;
  }
  .layout.center-justified,
  .layout.center-center {
    justify-content: center;
  }
  .flex {
    flex: 1;
    flex-basis: 0.000000001px;
  }
  .flex-auto {
    flex: 1 1 auto;
  }
  .flex-none {
    flex: none;
  }
  .layout.justified {
    justify-content: space-between;
  }
`;
  const haStyleDialog = css`
  /* prevent clipping of positioned elements */
  paper-dialog-scrollable {
    --paper-dialog-scrollable: {
      -webkit-overflow-scrolling: auto;
    }
  }

  /* force smooth scrolling for iOS 10 */
  paper-dialog-scrollable.can-scroll {
    --paper-dialog-scrollable: {
      -webkit-overflow-scrolling: touch;
    }
  }

  .paper-dialog-buttons {
    align-items: flex-end;
    padding: 8px;
    padding-bottom: max(env(safe-area-inset-bottom), 8px);
  }

  @media all and (min-width: 450px) and (min-height: 500px) {
    ha-paper-dialog {
      min-width: 400px;
    }
  }

  @media all and (max-width: 450px), all and (max-height: 500px) {
    paper-dialog,
    ha-paper-dialog {
      margin: 0;
      width: calc(
        100% - env(safe-area-inset-right) - env(safe-area-inset-left)
      ) !important;
      min-width: calc(
        100% - env(safe-area-inset-right) - env(safe-area-inset-left)
      ) !important;
      max-width: calc(
        100% - env(safe-area-inset-right) - env(safe-area-inset-left)
      ) !important;
      max-height: calc(100% - 64px);

      position: fixed !important;
      bottom: 0px;
      left: env(safe-area-inset-left);
      right: env(safe-area-inset-right);
      overflow: scroll;
      border-bottom-left-radius: 0px;
      border-bottom-right-radius: 0px;
    }
  }

  /* mwc-dialog (ha-dialog) styles */
  ha-dialog {
    --mdc-dialog-min-width: 400px;
    --mdc-dialog-max-width: 600px;
    --mdc-dialog-heading-ink-color: var(--primary-text-color);
    --mdc-dialog-content-ink-color: var(--primary-text-color);
    --justify-action-buttons: space-between;
  }

  ha-dialog .form {
    padding-bottom: 24px;
    color: var(--primary-text-color);
  }

  /* make dialog fullscreen on small screens */
  @media all and (max-width: 450px), all and (max-height: 500px) {
    ha-dialog {
      --mdc-dialog-min-width: calc(
        100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
      );
      --mdc-dialog-max-width: calc(
        100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
      );
      --mdc-dialog-min-height: 100%;
      --mdc-dialog-max-height: 100%;
      --mdc-shape-medium: 0px;
      --vertial-align-dialog: flex-end;
    }
  }
  mwc-button.warning {
    --mdc-theme-primary: var(--error-color);
  }
  .error {
    color: var(--error-color);
  }
`;
  const haStyleScrollbar = css`
  .ha-scrollbar::-webkit-scrollbar {
    width: 0.4rem;
    height: 0.4rem;
  }

  .ha-scrollbar::-webkit-scrollbar-thumb {
    -webkit-border-radius: 4px;
    border-radius: 4px;
    background: var(--scrollbar-thumb-color);
  }

  .ha-scrollbar {
    overflow-y: auto;
    scrollbar-color: var(--scrollbar-thumb-color) transparent;
    scrollbar-width: thin;
  }
`;

  const expand_hex = hex => {
    let result = "";

    for (const val of hex) {
      result += val + val;
    }

    return result;
  };

  const rgb_hex = component => {
    const hex = Math.round(Math.min(Math.max(component, 0), 255)).toString(16);
    return hex.length === 1 ? `0${hex}` : hex;
  }; // Conversion between HEX and RGB


  const hex2rgb = hex => {
    hex = hex.replace("#", "");

    if (hex.length === 3 || hex.length === 4) {
      hex = expand_hex(hex);
    }

    return [parseInt(hex.substring(0, 2), 16), parseInt(hex.substring(2, 4), 16), parseInt(hex.substring(4, 6), 16)];
  };
  const rgb2hex = rgb => {
    return `#${rgb_hex(rgb[0])}${rgb_hex(rgb[1])}${rgb_hex(rgb[2])}`;
  }; // Conversion between LAB, XYZ and RGB from https://github.com/gka/chroma.js
  // Copyright (c) 2011-2019, Gregor Aisch
  // Constants for XYZ and LAB conversion

  const Xn = 0.95047;
  const Yn = 1;
  const Zn = 1.08883;
  const t0 = 0.137931034; // 4 / 29

  const t1 = 0.206896552; // 6 / 29

  const t2 = 0.12841855; // 3 * t1 * t1

  const t3 = 0.008856452; // t1 * t1 * t1

  const rgb_xyz = r => {
    r /= 255;

    if (r <= 0.04045) {
      return r / 12.92;
    }

    return ((r + 0.055) / 1.055) ** 2.4;
  };

  const xyz_lab = t => {
    if (t > t3) {
      return t ** (1 / 3);
    }

    return t / t2 + t0;
  };

  const xyz_rgb = r => {
    return 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * r ** (1 / 2.4) - 0.055);
  };

  const lab_xyz = t => {
    return t > t1 ? t * t * t : t2 * (t - t0);
  }; // Conversions between RGB and LAB


  const rgb2xyz = rgb => {
    let [r, g, b] = rgb;
    r = rgb_xyz(r);
    g = rgb_xyz(g);
    b = rgb_xyz(b);
    const x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / Xn);
    const y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.072175 * b) / Yn);
    const z = xyz_lab((0.0193339 * r + 0.119192 * g + 0.9503041 * b) / Zn);
    return [x, y, z];
  };

  const rgb2lab = rgb => {
    const [x, y, z] = rgb2xyz(rgb);
    const l = 116 * y - 16;
    return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)];
  };
  const lab2rgb = lab => {
    const [l, a, b] = lab;
    let y = (l + 16) / 116;
    let x = isNaN(a) ? y : y + a / 500;
    let z = isNaN(b) ? y : y - b / 200;
    y = Yn * lab_xyz(y);
    x = Xn * lab_xyz(x);
    z = Zn * lab_xyz(z);
    const r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z); // D65 -> sRGB

    const g = xyz_rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z);
    const b_ = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);
    return [r, g, b_];
  };
  const lab2hex = lab => {
    const rgb = lab2rgb(lab);
    return rgb2hex(rgb);
  };

  const luminosity = rgb => {
    // http://www.w3.org/TR/WCAG20/#relativeluminancedef
    const lum = [0, 0, 0];

    for (let i = 0; i < rgb.length; i++) {
      const chan = rgb[i] / 255;
      lum[i] = chan <= 0.03928 ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
    }

    return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
  };

  const rgbContrast = (color1, color2) => {
    const lum1 = luminosity(color1);
    const lum2 = luminosity(color2);

    if (lum1 > lum2) {
      return (lum1 + 0.05) / (lum2 + 0.05);
    }

    return (lum2 + 0.05) / (lum1 + 0.05);
  };

  // From https://github.com/gka/chroma.js
  // Copyright (c) 2011-2019, Gregor Aisch
  const labDarken = (lab, amount = 1) => {
    return [lab[0] - 18 * amount, lab[1], lab[2]];
  };
  const labBrighten = (lab, amount = 1) => {
    return labDarken(lab, -amount);
  };

  let PROCESSED_THEMES = {};
  /**
   * Apply a theme to an element by setting the CSS variables on it.
   *
   * element: Element to apply theme on.
   * themes: HASS Theme information
   * selectedTheme: selected theme.
   */

  const applyThemesOnElement = (element, themes, selectedTheme, themeOptions) => {
    let cacheKey = selectedTheme;
    let themeRules = {};

    if (selectedTheme === "default" && themeOptions) {
      if (themeOptions.dark) {
        cacheKey = `${cacheKey}__dark`;
        themeRules = darkStyles;
      }

      if (themeOptions.primaryColor) {
        cacheKey = `${cacheKey}__primary_${themeOptions.primaryColor}`;
        const rgbPrimaryColor = hex2rgb(themeOptions.primaryColor);
        const labPrimaryColor = rgb2lab(rgbPrimaryColor);
        themeRules["primary-color"] = themeOptions.primaryColor;
        const rgbLigthPrimaryColor = lab2rgb(labBrighten(labPrimaryColor));
        themeRules["light-primary-color"] = rgb2hex(rgbLigthPrimaryColor);
        themeRules["dark-primary-color"] = lab2hex(labDarken(labPrimaryColor));
        themeRules["text-primary-color"] = rgbContrast(rgbPrimaryColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
        themeRules["text-light-primary-color"] = rgbContrast(rgbLigthPrimaryColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
        themeRules["state-icon-color"] = themeRules["dark-primary-color"];
      }

      if (themeOptions.accentColor) {
        cacheKey = `${cacheKey}__accent_${themeOptions.accentColor}`;
        themeRules["accent-color"] = themeOptions.accentColor;
        const rgbAccentColor = hex2rgb(themeOptions.accentColor);
        themeRules["text-accent-color"] = rgbContrast(rgbAccentColor, [33, 33, 33]) < 6 ? "#fff" : "#212121";
      }
    }

    if (selectedTheme && themes.themes[selectedTheme]) {
      themeRules = themes.themes[selectedTheme];
    }

    if (!element._themes && !Object.keys(themeRules).length) {
      // No styles to reset, and no styles to set
      return;
    }

    const newTheme = themeRules && cacheKey ? PROCESSED_THEMES[cacheKey] || processTheme(cacheKey, themeRules) : undefined; // Add previous set keys to reset them, and new theme

    const styles = { ...element._themes,
      ...(newTheme === null || newTheme === void 0 ? void 0 : newTheme.styles)
    };
    element._themes = newTheme === null || newTheme === void 0 ? void 0 : newTheme.keys; // Set and/or reset styles

    if (element.updateStyles) {
      element.updateStyles(styles);
    } else if (window.ShadyCSS) {
      // Implement updateStyles() method of Polymer elements
      window.ShadyCSS.styleSubtree(
      /** @type {!HTMLElement} */
      element, styles);
    }
  };

  const processTheme = (cacheKey, theme) => {
    if (!theme || !Object.keys(theme).length) {
      return undefined;
    }

    const combinedTheme = { ...derivedStyles,
      ...theme
    };
    const styles = {};
    const keys = {};

    for (const key of Object.keys(combinedTheme)) {
      const prefixedKey = `--${key}`;
      const value = combinedTheme[key];
      styles[prefixedKey] = value;
      keys[prefixedKey] = ""; // Try to create a rgb value for this key if it is not a var

      if (!value.startsWith("#")) {
        // Can't convert non hex value
        continue;
      }

      const rgbKey = `rgb-${key}`;

      if (combinedTheme[rgbKey] !== undefined) {
        // Theme has it's own rgb value
        continue;
      }

      try {
        const rgbValue = hex2rgb(value).join(",");
        const prefixedRgbKey = `--${rgbKey}`;
        styles[prefixedRgbKey] = rgbValue;
        keys[prefixedRgbKey] = "";
      } catch (e) {
        continue;
      }
    }

    PROCESSED_THEMES[cacheKey] = {
      styles,
      keys
    };
    return {
      styles,
      keys
    };
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
  let nativeCssVariables_;
  /**
   * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings
   */

  function calcCssVariables(settings) {
    if (settings && settings['shimcssproperties']) {
      nativeCssVariables_ = false;
    } else {
      // chrome 49 has semi-working css vars, check if box-shadow works
      // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782
      // However, shim css custom properties are only supported with ShadyDOM enabled,
      // so fall back on native if we do not detect ShadyDOM
      // Edge 15: custom properties used in ::before and ::after will also be used in the parent element
      // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/
      nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/) && window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)'));
    }
  }
  /** @type {string | undefined} */


  let cssBuild;

  if (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) {
    cssBuild = window.ShadyCSS.cssBuild;
  }
  /** @type {boolean} */


  const disableRuntime = Boolean(window.ShadyCSS && window.ShadyCSS.disableRuntime);

  if (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) {
    nativeCssVariables_ = window.ShadyCSS.nativeCss;
  } else if (window.ShadyCSS) {
    calcCssVariables(window.ShadyCSS); // reset window variable to let ShadyCSS API take its place

    window.ShadyCSS = undefined;
  } else {
    calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']);
  } // Hack for type error under new type inference which doesn't like that
  // nativeCssVariables is updated in a function and assigns the type
  // `function(): ?` instead of `boolean`.


  const nativeCssVariables =
  /** @type {boolean} */
  nativeCssVariables_;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /** @unrestricted */

  class StyleNode {
    constructor() {
      /** @type {number} */
      this['start'] = 0;
      /** @type {number} */

      this['end'] = 0;
      /** @type {StyleNode} */

      this['previous'] = null;
      /** @type {StyleNode} */

      this['parent'] = null;
      /** @type {Array<StyleNode>} */

      this['rules'] = null;
      /** @type {string} */

      this['parsedCssText'] = '';
      /** @type {string} */

      this['cssText'] = '';
      /** @type {boolean} */

      this['atRule'] = false;
      /** @type {number} */

      this['type'] = 0;
      /** @type {string} */

      this['keyframesName'] = '';
      /** @type {string} */

      this['selector'] = '';
      /** @type {string} */

      this['parsedSelector'] = '';
    }

  }

  /**
   * @param {string} text
   * @return {StyleNode}
   */

  function parse(text) {
    text = clean(text);
    return parseCss(lex(text), text);
  } // remove stuff we don't care about that may hinder parsing

  /**
   * @param {string} cssText
   * @return {string}
   */

  function clean(cssText) {
    return cssText.replace(RX.comments, '').replace(RX.port, '');
  } // super simple {...} lexer that returns a node tree

  /**
   * @param {string} text
   * @return {!StyleNode}
   */


  function lex(text) {
    let root = new StyleNode();
    root['start'] = 0;
    root['end'] = text.length;
    let n = root;

    for (let i = 0, l = text.length; i < l; i++) {
      if (text[i] === OPEN_BRACE) {
        if (!n['rules']) {
          n['rules'] = [];
        }

        let p = n;
        let previous = p['rules'][p['rules'].length - 1] || null;
        n = new StyleNode();
        n['start'] = i + 1;
        n['parent'] = p;
        n['previous'] = previous;
        p['rules'].push(n);
      } else if (text[i] === CLOSE_BRACE) {
        n['end'] = i + 1;
        n = n['parent'] || root;
      }
    }

    return root;
  } // add selectors/cssText to node tree

  /**
   * @param {StyleNode} node
   * @param {string} text
   * @return {!StyleNode}
   */


  function parseCss(node, text) {
    let t = text.substring(node['start'], node['end'] - 1);
    node['parsedCssText'] = node['cssText'] = t.trim();

    if (node['parent']) {
      let ss = node['previous'] ? node['previous']['end'] : node['parent']['start'];
      t = text.substring(ss, node['start'] - 1);
      t = _expandUnicodeEscapes(t);
      t = t.replace(RX.multipleSpaces, ' '); // TODO(sorvell): ad hoc; make selector include only after last ;
      // helps with mixin syntax

      t = t.substring(t.lastIndexOf(';') + 1);
      let s = node['parsedSelector'] = node['selector'] = t.trim();
      node['atRule'] = s.indexOf(AT_START) === 0; // note, support a subset of rule types...

      if (node['atRule']) {
        if (s.indexOf(MEDIA_START) === 0) {
          node['type'] = types.MEDIA_RULE;
        } else if (s.match(RX.keyframesRule)) {
          node['type'] = types.KEYFRAMES_RULE;
          node['keyframesName'] = node['selector'].split(RX.multipleSpaces).pop();
        }
      } else {
        if (s.indexOf(VAR_START) === 0) {
          node['type'] = types.MIXIN_RULE;
        } else {
          node['type'] = types.STYLE_RULE;
        }
      }
    }

    let r$ = node['rules'];

    if (r$) {
      for (let i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
        parseCss(r, text);
      }
    }

    return node;
  }
  /**
   * conversion of sort unicode escapes with spaces like `\33 ` (and longer) into
   * expanded form that doesn't require trailing space `\000033`
   * @param {string} s
   * @return {string}
   */


  function _expandUnicodeEscapes(s) {
    return s.replace(/\\([0-9a-f]{1,6})\s/gi, function () {
      let code = arguments[1],
          repeat = 6 - code.length;

      while (repeat--) {
        code = '0' + code;
      }

      return '\\' + code;
    });
  }
  /**
   * stringify parsed css.
   * @param {StyleNode} node
   * @param {boolean=} preserveProperties
   * @param {string=} text
   * @return {string}
   */


  function stringify(node, preserveProperties, text = '') {
    // calc rule cssText
    let cssText = '';

    if (node['cssText'] || node['rules']) {
      let r$ = node['rules'];

      if (r$ && !_hasMixinRules(r$)) {
        for (let i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
          cssText = stringify(r, preserveProperties, cssText);
        }
      } else {
        cssText = preserveProperties ? node['cssText'] : removeCustomProps(node['cssText']);
        cssText = cssText.trim();

        if (cssText) {
          cssText = '  ' + cssText + '\n';
        }
      }
    } // emit rule if there is cssText


    if (cssText) {
      if (node['selector']) {
        text += node['selector'] + ' ' + OPEN_BRACE + '\n';
      }

      text += cssText;

      if (node['selector']) {
        text += CLOSE_BRACE + '\n\n';
      }
    }

    return text;
  }
  /**
   * @param {Array<StyleNode>} rules
   * @return {boolean}
   */

  function _hasMixinRules(rules) {
    let r = rules[0];
    return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0;
  }
  /**
   * @param {string} cssText
   * @return {string}
   */


  function removeCustomProps(cssText) {
    cssText = removeCustomPropAssignment(cssText);
    return removeCustomPropApply(cssText);
  }
  /**
   * @param {string} cssText
   * @return {string}
   */


  function removeCustomPropAssignment(cssText) {
    return cssText.replace(RX.customProp, '').replace(RX.mixinProp, '');
  }
  /**
   * @param {string} cssText
   * @return {string}
   */

  function removeCustomPropApply(cssText) {
    return cssText.replace(RX.mixinApply, '').replace(RX.varApply, '');
  }
  /** @enum {number} */


  const types = {
    STYLE_RULE: 1,
    KEYFRAMES_RULE: 7,
    MEDIA_RULE: 4,
    MIXIN_RULE: 1000
  };
  const OPEN_BRACE = '{';
  const CLOSE_BRACE = '}'; // helper regexp's

  const RX = {
    comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
    port: /@import[^;]*;/gim,
    customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,
    mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,
    mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,
    varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,
    keyframesRule: /^@[^\s]*keyframes/,
    multipleSpaces: /\s+/g
  };
  const VAR_START = '--';
  const MEDIA_START = '@media';
  const AT_START = '@';

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const VAR_ASSIGN = /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi;
  const MIXIN_MATCH = /(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi;
  const MEDIA_MATCH = /@media\s(.*)/;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /** @type {!Set<string>} */

  const styleTextSet = new Set();
  const scopingAttribute = 'shady-unscoped';
  /**
   * Add a specifically-marked style to the document directly, and only one copy of that style.
   *
   * @param {!HTMLStyleElement} style
   * @return {undefined}
   */

  function processUnscopedStyle(style) {
    const text = style.textContent;

    if (!styleTextSet.has(text)) {
      styleTextSet.add(text);
      const newStyle = document.createElement('style');
      newStyle.setAttribute('shady-unscoped', '');
      newStyle.textContent = text;
      document.head.appendChild(newStyle);
    }
  }
  /**
   * Check if a style is supposed to be unscoped
   * @param {!HTMLStyleElement} style
   * @return {boolean} true if the style has the unscoping attribute
   */

  function isUnscopedStyle(style) {
    return style.hasAttribute(scopingAttribute);
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @param {string|StyleNode} rules
   * @param {function(StyleNode)=} callback
   * @return {string}
   */

  function toCssText(rules, callback) {
    if (!rules) {
      return '';
    }

    if (typeof rules === 'string') {
      rules = parse(rules);
    }

    if (callback) {
      forEachRule(rules, callback);
    }

    return stringify(rules, nativeCssVariables);
  }
  /**
   * @param {HTMLStyleElement} style
   * @return {StyleNode}
   */

  function rulesForStyle(style) {
    if (!style['__cssRules'] && style.textContent) {
      style['__cssRules'] = parse(style.textContent);
    }

    return style['__cssRules'] || null;
  } // Tests if a rule is a keyframes selector, which looks almost exactly
  /**
   * @param {StyleNode} node
   * @param {Function=} styleRuleCallback
   * @param {Function=} keyframesRuleCallback
   * @param {boolean=} onlyActiveRules
   */

  function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) {
    if (!node) {
      return;
    }

    let skipRules = false;
    let type = node['type'];

    if (onlyActiveRules) {
      if (type === types.MEDIA_RULE) {
        let matchMedia = node['selector'].match(MEDIA_MATCH);

        if (matchMedia) {
          // if rule is a non matching @media rule, skip subrules
          if (!window.matchMedia(matchMedia[1]).matches) {
            skipRules = true;
          }
        }
      }
    }

    if (type === types.STYLE_RULE) {
      styleRuleCallback(node);
    } else if (keyframesRuleCallback && type === types.KEYFRAMES_RULE) {
      keyframesRuleCallback(node);
    } else if (type === types.MIXIN_RULE) {
      skipRules = true;
    }

    let r$ = node['rules'];

    if (r$ && !skipRules) {
      for (let i = 0, l = r$.length, r; i < l && (r = r$[i]); i++) {
        forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules);
      }
    }
  } // add a string of cssText to the document.
  /**
   * Walk from text[start] matching parens and
   * returns position of the outer end paren
   * @param {string} text
   * @param {number} start
   * @return {number}
   */

  function findMatchingParen(text, start) {
    let level = 0;

    for (let i = start, l = text.length; i < l; i++) {
      if (text[i] === '(') {
        level++;
      } else if (text[i] === ')') {
        if (--level === 0) {
          return i;
        }
      }
    }

    return -1;
  }
  /**
   * @param {string} str
   * @param {function(string, string, string, string)} callback
   */

  function processVariableAndFallback(str, callback) {
    // find 'var('
    let start = str.indexOf('var(');

    if (start === -1) {
      // no var?, everything is prefix
      return callback(str, '', '', '');
    } //${prefix}var(${inner})${suffix}


    let end = findMatchingParen(str, start + 3);
    let inner = str.substring(start + 4, end);
    let prefix = str.substring(0, start); // suffix may have other variables

    let suffix = processVariableAndFallback(str.substring(end + 1), callback);
    let comma = inner.indexOf(','); // value and fallback args should be trimmed to match in property lookup

    if (comma === -1) {
      // variable, no fallback
      return callback(prefix, inner.trim(), '', suffix);
    } // var(${value},${fallback})


    let value = inner.substring(0, comma).trim();
    let fallback = inner.substring(comma + 1).trim();
    return callback(prefix, value, fallback, suffix);
  }
  /**
   * @type {function(*):*}
   */

  const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || (node => node);
  /**
   * @param {Element | {is: string, extends: string}} element
   * @return {{is: string, typeExtension: string}}
   */

  function getIsExtends(element) {
    let localName = element['localName'];
    let is = '',
        typeExtension = '';
    /*
    NOTE: technically, this can be wrong for certain svg elements
    with `-` in the name like `<font-face>`
    */

    if (localName) {
      if (localName.indexOf('-') > -1) {
        is = localName;
      } else {
        typeExtension = localName;
        is = element.getAttribute && element.getAttribute('is') || '';
      }
    } else {
      is =
      /** @type {?} */
      element.is;
      typeExtension =
      /** @type {?} */
      element.extends;
    }

    return {
      is,
      typeExtension
    };
  }
  /**
   * @param {Element|DocumentFragment} element
   * @return {string}
   */

  function gatherStyleText(element) {
    /** @type {!Array<string>} */
    const styleTextParts = [];
    const styles =
    /** @type {!NodeList<!HTMLStyleElement>} */
    element.querySelectorAll('style');

    for (let i = 0; i < styles.length; i++) {
      const style = styles[i];

      if (isUnscopedStyle(style)) {
        if (!nativeShadow) {
          processUnscopedStyle(style);
          style.parentNode.removeChild(style);
        }
      } else {
        styleTextParts.push(style.textContent);
        style.parentNode.removeChild(style);
      }
    }

    return styleTextParts.join('').trim();
  }
  const CSS_BUILD_ATTR = 'css-build';
  /**
   * Return the polymer-css-build "build type" applied to this element
   *
   * @param {!HTMLElement} element
   * @return {string} Can be "", "shady", or "shadow"
   */

  function getCssBuild(element) {
    if (cssBuild !== undefined) {
      return (
        /** @type {string} */
        cssBuild
      );
    }

    if (element.__cssBuild === undefined) {
      // try attribute first, as it is the common case
      const attrValue = element.getAttribute(CSS_BUILD_ATTR);

      if (attrValue) {
        element.__cssBuild = attrValue;
      } else {
        const buildComment = getBuildComment(element);

        if (buildComment !== '') {
          // remove build comment so it is not needlessly copied into every element instance
          removeBuildComment(element);
        }

        element.__cssBuild = buildComment;
      }
    }

    return element.__cssBuild || '';
  }
  /**
   * Check if the given element, either a <template> or <style>, has been processed
   * by polymer-css-build.
   *
   * If so, then we can make a number of optimizations:
   * - polymer-css-build will decompose mixins into individual CSS Custom Properties,
   * so the ApplyShim can be skipped entirely.
   * - Under native ShadowDOM, the style text can just be copied into each instance
   * without modification
   * - If the build is "shady" and ShadyDOM is in use, the styling does not need
   * scoping beyond the shimming of CSS Custom Properties
   *
   * @param {!HTMLElement} element
   * @return {boolean}
   */

  function elementHasBuiltCss(element) {
    return getCssBuild(element) !== '';
  }
  /**
   * For templates made with tagged template literals, polymer-css-build will
   * insert a comment of the form `<!--css-build:shadow-->`
   *
   * @param {!HTMLElement} element
   * @return {string}
   */

  function getBuildComment(element) {
    const buildComment = element.localName === 'template' ?
    /** @type {!HTMLTemplateElement} */
    element.content.firstChild : element.firstChild;

    if (buildComment instanceof Comment) {
      const commentParts = buildComment.textContent.trim().split(':');

      if (commentParts[0] === CSS_BUILD_ATTR) {
        return commentParts[1];
      }
    }

    return '';
  }
  /**
   * @param {!HTMLElement} element
   */

  function removeBuildComment(element) {
    const buildComment = element.localName === 'template' ?
    /** @type {!HTMLTemplateElement} */
    element.content.firstChild : element.firstChild;
    buildComment.parentNode.removeChild(buildComment);
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @param {Element} element
   * @param {Object=} properties
   */

  function updateNativeProperties(element, properties) {
    // remove previous properties
    for (let p in properties) {
      // NOTE: for bc with shim, don't apply null values.
      if (p === null) {
        element.style.removeProperty(p);
      } else {
        element.style.setProperty(p, properties[p]);
      }
    }
  }
  /**
   * @param {Element} element
   * @param {string} property
   * @return {string}
   */

  function getComputedStyleValue(element, property) {
    /**
     * @const {string}
     */
    const value = window.getComputedStyle(element).getPropertyValue(property);

    if (!value) {
      return '';
    } else {
      return value.trim();
    }
  }
  /**
   * return true if `cssText` contains a mixin definition or consumption
   * @param {string} cssText
   * @return {boolean}
   */

  function detectMixin(cssText) {
    const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText); // reset state of the regexes

    MIXIN_MATCH.lastIndex = 0;
    VAR_ASSIGN.lastIndex = 0;
    return has;
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  const APPLY_NAME_CLEAN = /;\s*/m;
  const INITIAL_INHERIT = /^\s*(initial)|(inherit)\s*$/;
  const IMPORTANT = /\s*!important/; // separator used between mixin-name and mixin-property-name when producing properties
  // NOTE: plain '-' may cause collisions in user styles

  const MIXIN_VAR_SEP = '_-_';
  // map of mixin to property names
  // --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}}

  class MixinMap {
    constructor() {
      /** @type {!Object<string, !MixinMapEntry>} */
      this._map = {};
    }
    /**
     * @param {string} name
     * @param {!PropertyEntry} props
     */


    set(name, props) {
      name = name.trim();
      this._map[name] = {
        properties: props,
        dependants: {}
      };
    }
    /**
     * @param {string} name
     * @return {MixinMapEntry}
     */


    get(name) {
      name = name.trim();
      return this._map[name] || null;
    }

  }
  /**
   * Callback for when an element is marked invalid
   * @type {?function(string)}
   */


  let invalidCallback = null;
  /** @unrestricted */

  class ApplyShim {
    constructor() {
      /** @type {?string} */
      this._currentElement = null;
      /** @type {HTMLMetaElement} */

      this._measureElement = null;
      this._map = new MixinMap();
    }
    /**
     * return true if `cssText` contains a mixin definition or consumption
     * @param {string} cssText
     * @return {boolean}
     */


    detectMixin(cssText) {
      return detectMixin(cssText);
    }
    /**
     * Gather styles into one style for easier processing
     * @param {!HTMLTemplateElement} template
     * @return {HTMLStyleElement}
     */


    gatherStyles(template) {
      const styleText = gatherStyleText(template.content);

      if (styleText) {
        const style =
        /** @type {!HTMLStyleElement} */
        document.createElement('style');
        style.textContent = styleText;
        template.content.insertBefore(style, template.content.firstChild);
        return style;
      }

      return null;
    }
    /**
     * @param {!HTMLTemplateElement} template
     * @param {string} elementName
     * @return {StyleNode}
     */


    transformTemplate(template, elementName) {
      if (template._gatheredStyle === undefined) {
        template._gatheredStyle = this.gatherStyles(template);
      }
      /** @type {HTMLStyleElement} */


      const style = template._gatheredStyle;
      return style ? this.transformStyle(style, elementName) : null;
    }
    /**
     * @param {!HTMLStyleElement} style
     * @param {string} elementName
     * @return {StyleNode}
     */


    transformStyle(style, elementName = '') {
      let ast = rulesForStyle(style);
      this.transformRules(ast, elementName);
      style.textContent = toCssText(ast);
      return ast;
    }
    /**
     * @param {!HTMLStyleElement} style
     * @return {StyleNode}
     */


    transformCustomStyle(style) {
      let ast = rulesForStyle(style);
      forEachRule(ast, rule => {
        if (rule['selector'] === ':root') {
          rule['selector'] = 'html';
        }

        this.transformRule(rule);
      });
      style.textContent = toCssText(ast);
      return ast;
    }
    /**
     * @param {StyleNode} rules
     * @param {string} elementName
     */


    transformRules(rules, elementName) {
      this._currentElement = elementName;
      forEachRule(rules, r => {
        this.transformRule(r);
      });
      this._currentElement = null;
    }
    /**
     * @param {!StyleNode} rule
     */


    transformRule(rule) {
      rule['cssText'] = this.transformCssText(rule['parsedCssText'], rule); // :root was only used for variable assignment in property shim,
      // but generates invalid selectors with real properties.
      // replace with `:host > *`, which serves the same effect

      if (rule['selector'] === ':root') {
        rule['selector'] = ':host > *';
      }
    }
    /**
     * @param {string} cssText
     * @param {!StyleNode} rule
     * @return {string}
     */


    transformCssText(cssText, rule) {
      // produce variables
      cssText = cssText.replace(VAR_ASSIGN, (matchText, propertyName, valueProperty, valueMixin) => this._produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule)); // consume mixins

      return this._consumeCssProperties(cssText, rule);
    }
    /**
     * @param {string} property
     * @return {string}
     */


    _getInitialValueForProperty(property) {
      if (!this._measureElement) {
        this._measureElement =
        /** @type {HTMLMetaElement} */
        document.createElement('meta');

        this._measureElement.setAttribute('apply-shim-measure', '');

        this._measureElement.style.all = 'initial';
        document.head.appendChild(this._measureElement);
      }

      return window.getComputedStyle(this._measureElement).getPropertyValue(property);
    }
    /**
     * Walk over all rules before this rule to find fallbacks for mixins
     *
     * @param {!StyleNode} startRule
     * @return {!Object}
     */


    _fallbacksFromPreviousRules(startRule) {
      // find the "top" rule
      let topRule = startRule;

      while (topRule['parent']) {
        topRule = topRule['parent'];
      }

      const fallbacks = {};
      let seenStartRule = false;
      forEachRule(topRule, r => {
        // stop when we hit the input rule
        seenStartRule = seenStartRule || r === startRule;

        if (seenStartRule) {
          return;
        } // NOTE: Only matching selectors are "safe" for this fallback processing
        // It would be prohibitive to run `matchesSelector()` on each selector,
        // so we cheat and only check if the same selector string is used, which
        // guarantees things like specificity matching


        if (r['selector'] === startRule['selector']) {
          Object.assign(fallbacks, this._cssTextToMap(r['parsedCssText']));
        }
      });
      return fallbacks;
    }
    /**
     * replace mixin consumption with variable consumption
     * @param {string} text
     * @param {!StyleNode=} rule
     * @return {string}
     */


    _consumeCssProperties(text, rule) {
      /** @type {Array} */
      let m = null; // loop over text until all mixins with defintions have been applied

      while (m = MIXIN_MATCH.exec(text)) {
        let matchText = m[0];
        let mixinName = m[1];
        let idx = m.index; // collect properties before apply to be "defaults" if mixin might override them
        // match includes a "prefix", so find the start and end positions of @apply

        let applyPos = idx + matchText.indexOf('@apply');
        let afterApplyPos = idx + matchText.length; // find props defined before this @apply

        let textBeforeApply = text.slice(0, applyPos);
        let textAfterApply = text.slice(afterApplyPos);
        let defaults = rule ? this._fallbacksFromPreviousRules(rule) : {};
        Object.assign(defaults, this._cssTextToMap(textBeforeApply));

        let replacement = this._atApplyToCssProperties(mixinName, defaults); // use regex match position to replace mixin, keep linear processing time


        text = `${textBeforeApply}${replacement}${textAfterApply}`; // move regex search to _after_ replacement

        MIXIN_MATCH.lastIndex = idx + replacement.length;
      }

      return text;
    }
    /**
     * produce variable consumption at the site of mixin consumption
     * `@apply` --foo; -> for all props (${propname}: var(--foo_-_${propname}, ${fallback[propname]}}))
     * Example:
     *  border: var(--foo_-_border); padding: var(--foo_-_padding, 2px)
     *
     * @param {string} mixinName
     * @param {Object} fallbacks
     * @return {string}
     */


    _atApplyToCssProperties(mixinName, fallbacks) {
      mixinName = mixinName.replace(APPLY_NAME_CLEAN, '');
      let vars = [];

      let mixinEntry = this._map.get(mixinName); // if we depend on a mixin before it is created
      // make a sentinel entry in the map to add this element as a dependency for when it is defined.


      if (!mixinEntry) {
        this._map.set(mixinName, {});

        mixinEntry = this._map.get(mixinName);
      }

      if (mixinEntry) {
        if (this._currentElement) {
          mixinEntry.dependants[this._currentElement] = true;
        }

        let p, parts, f;
        const properties = mixinEntry.properties;

        for (p in properties) {
          f = fallbacks && fallbacks[p];
          parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p];

          if (f) {
            parts.push(',', f.replace(IMPORTANT, ''));
          }

          parts.push(')');

          if (IMPORTANT.test(properties[p])) {
            parts.push(' !important');
          }

          vars.push(parts.join(''));
        }
      }

      return vars.join('; ');
    }
    /**
     * @param {string} property
     * @param {string} value
     * @return {string}
     */


    _replaceInitialOrInherit(property, value) {
      let match = INITIAL_INHERIT.exec(value);

      if (match) {
        if (match[1]) {
          // initial
          // replace `initial` with the concrete initial value for this property
          value = this._getInitialValueForProperty(property);
        } else {
          // inherit
          // with this purposfully illegal value, the variable will be invalid at
          // compute time (https://www.w3.org/TR/css-variables/#invalid-at-computed-value-time)
          // and for inheriting values, will behave similarly
          // we cannot support the same behavior for non inheriting values like 'border'
          value = 'apply-shim-inherit';
        }
      }

      return value;
    }
    /**
     * "parse" a mixin definition into a map of properties and values
     * cssTextToMap('border: 2px solid black') -> ('border', '2px solid black')
     * @param {string} text
     * @param {boolean=} replaceInitialOrInherit
     * @return {!Object<string, string>}
     */


    _cssTextToMap(text, replaceInitialOrInherit = false) {
      let props = text.split(';');
      let property, value;
      let out = {};

      for (let i = 0, p, sp; i < props.length; i++) {
        p = props[i];

        if (p) {
          sp = p.split(':'); // ignore lines that aren't definitions like @media

          if (sp.length > 1) {
            property = sp[0].trim(); // some properties may have ':' in the value, like data urls

            value = sp.slice(1).join(':');

            if (replaceInitialOrInherit) {
              value = this._replaceInitialOrInherit(property, value);
            }

            out[property] = value;
          }
        }
      }

      return out;
    }
    /**
     * @param {MixinMapEntry} mixinEntry
     */


    _invalidateMixinEntry(mixinEntry) {
      if (!invalidCallback) {
        return;
      }

      for (let elementName in mixinEntry.dependants) {
        if (elementName !== this._currentElement) {
          invalidCallback(elementName);
        }
      }
    }
    /**
     * @param {string} matchText
     * @param {string} propertyName
     * @param {?string} valueProperty
     * @param {?string} valueMixin
     * @param {!StyleNode} rule
     * @return {string}
     */


    _produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule) {
      // handle case where property value is a mixin
      if (valueProperty) {
        // form: --mixin2: var(--mixin1), where --mixin1 is in the map
        processVariableAndFallback(valueProperty, (prefix, value) => {
          if (value && this._map.get(value)) {
            valueMixin = `@apply ${value};`;
          }
        });
      }

      if (!valueMixin) {
        return matchText;
      }

      let mixinAsProperties = this._consumeCssProperties('' + valueMixin, rule);

      let prefix = matchText.slice(0, matchText.indexOf('--')); // `initial` and `inherit` as properties in a map should be replaced because
      // these keywords are eagerly evaluated when the mixin becomes CSS Custom Properties,
      // and would set the variable value, rather than carry the keyword to the `var()` usage.

      let mixinValues = this._cssTextToMap(mixinAsProperties, true);

      let combinedProps = mixinValues;

      let mixinEntry = this._map.get(propertyName);

      let oldProps = mixinEntry && mixinEntry.properties;

      if (oldProps) {
        // NOTE: since we use mixin, the map of properties is updated here
        // and this is what we want.
        combinedProps = Object.assign(Object.create(oldProps), mixinValues);
      } else {
        this._map.set(propertyName, combinedProps);
      }

      let out = [];
      let p, v; // set variables defined by current mixin

      let needToInvalidate = false;

      for (p in combinedProps) {
        v = mixinValues[p]; // if property not defined by current mixin, set initial

        if (v === undefined) {
          v = 'initial';
        }

        if (oldProps && !(p in oldProps)) {
          needToInvalidate = true;
        }

        out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`);
      }

      if (needToInvalidate) {
        this._invalidateMixinEntry(mixinEntry);
      }

      if (mixinEntry) {
        mixinEntry.properties = combinedProps;
      } // because the mixinMap is global, the mixin might conflict with
      // a different scope's simple variable definition:
      // Example:
      // some style somewhere:
      // --mixin1:{ ... }
      // --mixin2: var(--mixin1);
      // some other element:
      // --mixin1: 10px solid red;
      // --foo: var(--mixin1);
      // In this case, we leave the original variable definition in place.


      if (valueProperty) {
        prefix = `${matchText};${prefix}`;
      }

      return `${prefix}${out.join('; ')};`;
    }

  }
  /* exports */

  /* eslint-disable no-self-assign */


  ApplyShim.prototype['detectMixin'] = ApplyShim.prototype.detectMixin;
  ApplyShim.prototype['transformStyle'] = ApplyShim.prototype.transformStyle;
  ApplyShim.prototype['transformCustomStyle'] = ApplyShim.prototype.transformCustomStyle;
  ApplyShim.prototype['transformRules'] = ApplyShim.prototype.transformRules;
  ApplyShim.prototype['transformRule'] = ApplyShim.prototype.transformRule;
  ApplyShim.prototype['transformTemplate'] = ApplyShim.prototype.transformTemplate;
  ApplyShim.prototype['_separator'] = MIXIN_VAR_SEP;
  /* eslint-enable no-self-assign */

  Object.defineProperty(ApplyShim.prototype, 'invalidCallback', {
    /** @return {?function(string)} */
    get() {
      return invalidCallback;
    },

    /** @param {?function(string)} cb */
    set(cb) {
      invalidCallback = cb;
    }

  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @const {!Object<string, !HTMLTemplateElement>}
   */

  const templateMap = {};

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  /*
   * Utilities for handling invalidating apply-shim mixins for a given template.
   *
   * The invalidation strategy involves keeping track of the "current" version of a template's mixins, and updating that count when a mixin is invalidated.
   * The template
   */

  /** @const {string} */

  const CURRENT_VERSION = '_applyShimCurrentVersion';
  /** @const {string} */

  const NEXT_VERSION = '_applyShimNextVersion';
  /** @const {string} */

  const VALIDATING_VERSION = '_applyShimValidatingVersion';
  /**
   * @const {Promise<void>}
   */

  const promise = Promise.resolve();
  /**
   * @param {string} elementName
   */

  function invalidate(elementName) {
    let template = templateMap[elementName];

    if (template) {
      invalidateTemplate(template);
    }
  }
  /**
   * This function can be called multiple times to mark a template invalid
   * and signal that the style inside must be regenerated.
   *
   * Use `startValidatingTemplate` to begin an asynchronous validation cycle.
   * During that cycle, call `templateIsValidating` to see if the template must
   * be revalidated
   * @param {HTMLTemplateElement} template
   */

  function invalidateTemplate(template) {
    // default the current version to 0
    template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0; // ensure the "validating for" flag exists

    template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0; // increment the next version

    template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;
  }
  /**
   * @param {HTMLTemplateElement} template
   * @return {boolean}
   */

  function templateIsValid(template) {
    return template[CURRENT_VERSION] === template[NEXT_VERSION];
  }
  /**
   * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.
   * If false, the template must be validated.
   * @param {HTMLTemplateElement} template
   * @return {boolean}
   */

  function templateIsValidating(template) {
    return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];
  }
  /**
   * Begin an asynchronous invalidation cycle.
   * This should be called after every validation of a template
   *
   * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`
   * @param {HTMLTemplateElement} template
   */

  function startValidatingTemplate(template) {
    // remember that the current "next version" is the reason for this validation cycle
    template[VALIDATING_VERSION] = template[NEXT_VERSION]; // however, there only needs to be one async task to clear the counters

    if (!template._validating) {
      template._validating = true;
      promise.then(function () {
        // sync the current version to let future invalidations cause a refresh cycle
        template[CURRENT_VERSION] = template[NEXT_VERSION];
        template._validating = false;
      });
    }
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /** @type {Promise<void>} */

  let readyPromise = null;
  /** @type {?function(?function())} */

  let whenReady = window['HTMLImports'] && window['HTMLImports']['whenReady'] || null;
  /** @type {function()} */

  let resolveFn;
  /**
   * @param {?function()} callback
   */

  function documentWait(callback) {
    requestAnimationFrame(function () {
      if (whenReady) {
        whenReady(callback);
      } else {
        if (!readyPromise) {
          readyPromise = new Promise(resolve => {
            resolveFn = resolve;
          });

          if (document.readyState === 'complete') {
            resolveFn();
          } else {
            document.addEventListener('readystatechange', () => {
              if (document.readyState === 'complete') {
                resolveFn();
              }
            });
          }
        }

        readyPromise.then(function () {
          callback && callback();
        });
      }
    });
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const SEEN_MARKER = '__seenByShadyCSS';
  const CACHED_STYLE = '__shadyCSSCachedStyle';
  /** @type {?function(!HTMLStyleElement)} */

  let transformFn = null;
  /** @type {?function()} */

  let validateFn = null;
  /**
  This interface is provided to add document-level <style> elements to ShadyCSS for processing.
  These styles must be processed by ShadyCSS to simulate ShadowRoot upper-bound encapsulation from outside styles
  In addition, these styles may also need to be processed for @apply rules and CSS Custom Properties

  To add document-level styles to ShadyCSS, one can call `ShadyCSS.addDocumentStyle(styleElement)` or `ShadyCSS.addDocumentStyle({getStyle: () => styleElement})`

  In addition, if the process used to discover document-level styles can be synchronously flushed, one should set `ShadyCSS.documentStyleFlush`.
  This function will be called when calculating styles.

  An example usage of the document-level styling api can be found in `examples/document-style-lib.js`

  @unrestricted
  */

  class CustomStyleInterface {
    constructor() {
      /** @type {!Array<!CustomStyleProvider>} */
      this['customStyles'] = [];
      this['enqueued'] = false; // NOTE(dfreedm): use quotes here to prevent closure inlining to `function(){}`;

      documentWait(() => {
        if (window['ShadyCSS']['flushCustomStyles']) {
          window['ShadyCSS']['flushCustomStyles']();
        }
      });
    }
    /**
     * Queue a validation for new custom styles to batch style recalculations
     */


    enqueueDocumentValidation() {
      if (this['enqueued'] || !validateFn) {
        return;
      }

      this['enqueued'] = true;
      documentWait(validateFn);
    }
    /**
     * @param {!HTMLStyleElement} style
     */


    addCustomStyle(style) {
      if (!style[SEEN_MARKER]) {
        style[SEEN_MARKER] = true;
        this['customStyles'].push(style);
        this.enqueueDocumentValidation();
      }
    }
    /**
     * @param {!CustomStyleProvider} customStyle
     * @return {HTMLStyleElement}
     */


    getStyleForCustomStyle(customStyle) {
      if (customStyle[CACHED_STYLE]) {
        return customStyle[CACHED_STYLE];
      }

      let style;

      if (customStyle['getStyle']) {
        style = customStyle['getStyle']();
      } else {
        style = customStyle;
      }

      return style;
    }
    /**
     * @return {!Array<!CustomStyleProvider>}
     */


    processStyles() {
      const cs = this['customStyles'];

      for (let i = 0; i < cs.length; i++) {
        const customStyle = cs[i];

        if (customStyle[CACHED_STYLE]) {
          continue;
        }

        const style = this.getStyleForCustomStyle(customStyle);

        if (style) {
          // HTMLImports polyfill may have cloned the style into the main document,
          // which is referenced with __appliedElement.
          const styleToTransform =
          /** @type {!HTMLStyleElement} */
          style['__appliedElement'] || style;

          if (transformFn) {
            transformFn(styleToTransform);
          }

          customStyle[CACHED_STYLE] = styleToTransform;
        }
      }

      return cs;
    }

  }
  /* eslint-disable no-self-assign */

  CustomStyleInterface.prototype['addCustomStyle'] = CustomStyleInterface.prototype.addCustomStyle;
  CustomStyleInterface.prototype['getStyleForCustomStyle'] = CustomStyleInterface.prototype.getStyleForCustomStyle;
  CustomStyleInterface.prototype['processStyles'] = CustomStyleInterface.prototype.processStyles;
  /* eslint-enable no-self-assign */

  Object.defineProperties(CustomStyleInterface.prototype, {
    'transformCallback': {
      /** @return {?function(!HTMLStyleElement)} */
      get() {
        return transformFn;
      },

      /** @param {?function(!HTMLStyleElement)} fn */
      set(fn) {
        transformFn = fn;
      }

    },
    'validateCallback': {
      /** @return {?function()} */
      get() {
        return validateFn;
      },

      /**
       * @param {?function()} fn
       * @this {CustomStyleInterface}
       */
      set(fn) {
        let needsEnqueue = false;

        if (!validateFn) {
          needsEnqueue = true;
        }

        validateFn = fn;

        if (needsEnqueue) {
          this.enqueueDocumentValidation();
        }
      }

    }
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /** @const {ApplyShim} */

  const applyShim = new ApplyShim();

  class ApplyShimInterface {
    constructor() {
      /** @type {?CustomStyleInterfaceInterface} */
      this.customStyleInterface = null;
      applyShim['invalidCallback'] = invalidate;
    }

    ensure() {
      if (this.customStyleInterface) {
        return;
      }

      if (window.ShadyCSS.CustomStyleInterface) {
        this.customStyleInterface =
        /** @type {!CustomStyleInterfaceInterface} */
        window.ShadyCSS.CustomStyleInterface;

        this.customStyleInterface['transformCallback'] = style => {
          applyShim.transformCustomStyle(style);
        };

        this.customStyleInterface['validateCallback'] = () => {
          requestAnimationFrame(() => {
            if (this.customStyleInterface['enqueued']) {
              this.flushCustomStyles();
            }
          });
        };
      }
    }
    /**
     * @param {!HTMLTemplateElement} template
     * @param {string} elementName
     */


    prepareTemplate(template, elementName) {
      this.ensure();

      if (elementHasBuiltCss(template)) {
        return;
      }

      templateMap[elementName] = template;
      let ast = applyShim.transformTemplate(template, elementName); // save original style ast to use for revalidating instances

      template['_styleAst'] = ast;
    }

    flushCustomStyles() {
      this.ensure();

      if (!this.customStyleInterface) {
        return;
      }

      let styles = this.customStyleInterface['processStyles']();

      if (!this.customStyleInterface['enqueued']) {
        return;
      }

      for (let i = 0; i < styles.length; i++) {
        let cs = styles[i];
        let style = this.customStyleInterface['getStyleForCustomStyle'](cs);

        if (style) {
          applyShim.transformCustomStyle(style);
        }
      }

      this.customStyleInterface['enqueued'] = false;
    }
    /**
     * @param {HTMLElement} element
     * @param {Object=} properties
     */


    styleSubtree(element, properties) {
      this.ensure();

      if (properties) {
        updateNativeProperties(element, properties);
      }

      if (element.shadowRoot) {
        this.styleElement(element);
        let shadowChildren =
        /** @type {!ParentNode} */
        element.shadowRoot.children || element.shadowRoot.childNodes;

        for (let i = 0; i < shadowChildren.length; i++) {
          this.styleSubtree(
          /** @type {HTMLElement} */
          shadowChildren[i]);
        }
      } else {
        let children = element.children || element.childNodes;

        for (let i = 0; i < children.length; i++) {
          this.styleSubtree(
          /** @type {HTMLElement} */
          children[i]);
        }
      }
    }
    /**
     * @param {HTMLElement} element
     */


    styleElement(element) {
      this.ensure();
      let {
        is
      } = getIsExtends(element);
      let template = templateMap[is];

      if (template && elementHasBuiltCss(template)) {
        return;
      }

      if (template && !templateIsValid(template)) {
        // only revalidate template once
        if (!templateIsValidating(template)) {
          this.prepareTemplate(template, is);
          startValidatingTemplate(template);
        } // update this element instance


        let root = element.shadowRoot;

        if (root) {
          let style =
          /** @type {HTMLStyleElement} */
          root.querySelector('style');

          if (style) {
            // reuse the template's style ast, it has all the original css text
            style['__cssRules'] = template['_styleAst'];
            style.textContent = toCssText(template['_styleAst']);
          }
        }
      }
    }
    /**
     * @param {Object=} properties
     */


    styleDocument(properties) {
      this.ensure();
      this.styleSubtree(document.body, properties);
    }

  }

  if (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) {
    const applyShimInterface = new ApplyShimInterface();
    let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface;
    /** @suppress {duplicate} */

    window.ShadyCSS = {
      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       * @param {string=} elementExtends
       */
      prepareTemplate(template, elementName, elementExtends) {
        // eslint-disable-line no-unused-vars
        applyShimInterface.flushCustomStyles();
        applyShimInterface.prepareTemplate(template, elementName);
      },

      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       * @param {string=} elementExtends
       */
      prepareTemplateStyles(template, elementName, elementExtends) {
        window.ShadyCSS.prepareTemplate(template, elementName, elementExtends);
      },

      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       */
      prepareTemplateDom(template, elementName) {},

      // eslint-disable-line no-unused-vars

      /**
       * @param {!HTMLElement} element
       * @param {Object=} properties
       */
      styleSubtree(element, properties) {
        applyShimInterface.flushCustomStyles();
        applyShimInterface.styleSubtree(element, properties);
      },

      /**
       * @param {!HTMLElement} element
       */
      styleElement(element) {
        applyShimInterface.flushCustomStyles();
        applyShimInterface.styleElement(element);
      },

      /**
       * @param {Object=} properties
       */
      styleDocument(properties) {
        applyShimInterface.flushCustomStyles();
        applyShimInterface.styleDocument(properties);
      },

      /**
       * @param {Element} element
       * @param {string} property
       * @return {string}
       */
      getComputedStyleValue(element, property) {
        return getComputedStyleValue(element, property);
      },

      flushCustomStyles() {
        applyShimInterface.flushCustomStyles();
      },

      nativeCss: nativeCssVariables,
      nativeShadow: nativeShadow,
      cssBuild: cssBuild,
      disableRuntime: disableRuntime
    };

    if (CustomStyleInterface) {
      window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
    }
  }

  window.ShadyCSS.ApplyShim = applyShim;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  /* eslint-disable no-unused-vars */

  /**
   * When using Closure Compiler, JSCompiler_renameProperty(property, object) is replaced by the munged name for object[property]
   * We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling.
   *
   * @param {?} prop Property name
   * @param {*} obj Reference object
   * @return {string} Potentially renamed property name
   */
  window.JSCompiler_renameProperty = function (prop, obj) {
    return prop;
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  let CSS_URL_RX = /(url\()([^)]*)(\))/g;
  let ABS_URL = /(^\/[^\/])|(^#)|(^[\w-\d]*:)/;
  let workingURL;
  let resolveDoc;
  /**
   * Resolves the given URL against the provided `baseUri'.
   *
   * Note that this function performs no resolution for URLs that start
   * with `/` (absolute URLs) or `#` (hash identifiers).  For general purpose
   * URL resolution, use `window.URL`.
   *
   * @param {string} url Input URL to resolve
   * @param {?string=} baseURI Base URI to resolve the URL against
   * @return {string} resolved URL
   */

  function resolveUrl(url, baseURI) {
    if (url && ABS_URL.test(url)) {
      return url;
    }

    if (url === '//') {
      return url;
    } // Lazy feature detection.


    if (workingURL === undefined) {
      workingURL = false;

      try {
        const u = new URL('b', 'http://a');
        u.pathname = 'c%20d';
        workingURL = u.href === 'http://a/c%20d';
      } catch (e) {// silently fail
      }
    }

    if (!baseURI) {
      baseURI = document.baseURI || window.location.href;
    }

    if (workingURL) {
      try {
        return new URL(url, baseURI).href;
      } catch (e) {
        // Bad url or baseURI structure. Do not attempt to resolve.
        return url;
      }
    } // Fallback to creating an anchor into a disconnected document.


    if (!resolveDoc) {
      resolveDoc = document.implementation.createHTMLDocument('temp');
      resolveDoc.base = resolveDoc.createElement('base');
      resolveDoc.head.appendChild(resolveDoc.base);
      resolveDoc.anchor = resolveDoc.createElement('a');
      resolveDoc.body.appendChild(resolveDoc.anchor);
    }

    resolveDoc.base.href = baseURI;
    resolveDoc.anchor.href = url;
    return resolveDoc.anchor.href || url;
  }
  /**
   * Resolves any relative URL's in the given CSS text against the provided
   * `ownerDocument`'s `baseURI`.
   *
   * @param {string} cssText CSS text to process
   * @param {string} baseURI Base URI to resolve the URL against
   * @return {string} Processed CSS text with resolved URL's
   */

  function resolveCss(cssText, baseURI) {
    return cssText.replace(CSS_URL_RX, function (m, pre, url, post) {
      return pre + '\'' + resolveUrl(url.replace(/["']/g, ''), baseURI) + '\'' + post;
    });
  }
  /**
   * Returns a path from a given `url`. The path includes the trailing
   * `/` from the url.
   *
   * @param {string} url Input URL to transform
   * @return {string} resolved path
   */

  function pathFromUrl(url) {
    return url.substring(0, url.lastIndexOf('/') + 1);
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const useShadow = !window.ShadyDOM || !window.ShadyDOM.inUse;
  const useNativeCSSProperties = Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss);
  const supportsAdoptingStyleSheets$1 = useShadow && 'adoptedStyleSheets' in Document.prototype && 'replaceSync' in CSSStyleSheet.prototype && // Since spec may change, feature detect exact API we need
  (() => {
    try {
      const sheet = new CSSStyleSheet();
      sheet.replaceSync('');
      const host = document.createElement('div');
      host.attachShadow({
        mode: 'open'
      });
      host.shadowRoot.adoptedStyleSheets = [sheet];
      return host.shadowRoot.adoptedStyleSheets[0] === sheet;
    } catch (e) {
      return false;
    }
  })();
  /**
   * Globally settable property that is automatically assigned to
   * `ElementMixin` instances, useful for binding in templates to
   * make URL's relative to an application's root.  Defaults to the main
   * document URL, but can be overridden by users.  It may be useful to set
   * `rootPath` to provide a stable application mount path when
   * using client side routing.
   */

  let rootPath = window.Polymer && window.Polymer.rootPath || pathFromUrl(document.baseURI || window.location.href);
  /**
   * A global callback used to sanitize any value before inserting it into the DOM.
   * The callback signature is:
   *
   *  function sanitizeDOMValue(value, name, type, node) { ... }
   *
   * Where:
   *
   * `value` is the value to sanitize.
   * `name` is the name of an attribute or property (for example, href).
   * `type` indicates where the value is being inserted: one of property, attribute, or text.
   * `node` is the node where the value is being inserted.
   *
   * @type {(function(*,string,string,?Node):*)|undefined}
   */

  let sanitizeDOMValue = window.Polymer && window.Polymer.sanitizeDOMValue || undefined;
  /**
   * Globally settable property to make Polymer Gestures use passive TouchEvent listeners when recognizing gestures.
   * When set to `true`, gestures made from touch will not be able to prevent scrolling, allowing for smoother
   * scrolling performance.
   * Defaults to `false` for backwards compatibility.
   */

  let passiveTouchGestures = window.Polymer && window.Polymer.setPassiveTouchGestures || false;
  /**
   * Setting to ensure Polymer template evaluation only occurs based on tempates
   * defined in trusted script.  When true, `<dom-module>` re-registration is
   * disallowed, `<dom-bind>` is disabled, and `<dom-if>`/`<dom-repeat>`
   * templates will only evaluate in the context of a trusted element template.
   */

  let strictTemplatePolicy = window.Polymer && window.Polymer.strictTemplatePolicy || false;
  /**
   * Setting to enable dom-module lookup from Polymer.Element.  By default,
   * templates must be defined in script using the `static get template()`
   * getter and the `html` tag function.  To enable legacy loading of templates
   * via dom-module, set this flag to true.
   */

  let allowTemplateFromDomModule = window.Polymer && window.Polymer.allowTemplateFromDomModule || false;
  /**
   * Setting to skip processing style includes and re-writing urls in css styles.
   * Normally "included" styles are pulled into the element and all urls in styles
   * are re-written to be relative to the containing script url.
   * If no includes or relative urls are used in styles, these steps can be
   * skipped as an optimization.
   */

  let legacyOptimizations = window.Polymer && window.Polymer.legacyOptimizations || false;
  /**
   * Setting to add warnings useful when migrating from Polymer 1.x to 2.x.
   */

  let legacyWarnings = window.Polymer && window.Polymer.legacyWarnings || false;
  /**
   * Setting to perform initial rendering synchronously when running under ShadyDOM.
   * This matches the behavior of Polymer 1.
   */

  let syncInitialRender = window.Polymer && window.Polymer.syncInitialRender || false;
  /**
   * Setting to retain the legacy Polymer 1 behavior for multi-property
   * observers around undefined values. Observers and computed property methods
   * are not called until no argument is undefined.
   */

  let legacyUndefined = window.Polymer && window.Polymer.legacyUndefined || false;
  /**
   * Setting to ensure computed properties are computed in order to ensure
   * re-computation never occurs in a given turn.
   */

  let orderedComputed = window.Polymer && window.Polymer.orderedComputed || false;
  /**
   * Setting to remove nested templates inside `dom-if` and `dom-repeat` as
   * part of element template parsing.  This is a performance optimization that
   * eliminates most of the tax of needing two elements due to the loss of
   * type-extended templates as a result of the V1 specification changes.
   */

  let removeNestedTemplates = window.Polymer && window.Polymer.removeNestedTemplates || false;
  /**
   * Setting to place `dom-if` elements in a performance-optimized mode that takes
   * advantage of lighter-weight host runtime template stamping to eliminate the
   * need for an intermediate Templatizer `TemplateInstance` to mange the nodes
   * stamped by `dom-if`.  Under this setting, any Templatizer-provided API's
   * such as `modelForElement` will not be available for nodes stamped by
   * `dom-if`.
   */

  let fastDomIf = window.Polymer && window.Polymer.fastDomIf || false;
  /**
   * Setting to disable `dom-change` and `rendered-item-count` events from
   * `dom-if` and `dom-repeat`. Users can opt back into `dom-change` events by
   * setting the `notify-dom-change` attribute (`notifyDomChange: true` property)
   * to `dom-if`/`don-repeat` instances.
   */

  let suppressTemplateNotifications = window.Polymer && window.Polymer.suppressTemplateNotifications || false;
  /**
   * Setting to disable use of dynamic attributes. This is an optimization
   * to avoid setting `observedAttributes`. Instead attributes are read
   * once at create time and set/removeAttribute are patched.
   */

  let legacyNoObservedAttributes = window.Polymer && window.Polymer.legacyNoObservedAttributes || false;
  /**
   * Setting to enable use of `adoptedStyleSheets` for sharing style sheets
   * between component instances' shadow roots, if the app uses built Shady CSS
   * styles.
   */

  let useAdoptedStyleSheetsWithBuiltCSS = window.Polymer && window.Polymer.useAdoptedStyleSheetsWithBuiltCSS || false;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  let dedupeId = 0;
  /* eslint-disable valid-jsdoc */

  /**
   * Wraps an ES6 class expression mixin such that the mixin is only applied
   * if it has not already been applied its base argument. Also memoizes mixin
   * applications.
   *
   * @template T
   * @param {T} mixin ES6 class expression mixin to wrap
   * @return {T}
   * @suppress {invalidCasts}
   */

  const dedupingMixin = function (mixin) {
    let mixinApplications =
    /** @type {!MixinFunction} */
    mixin.__mixinApplications;

    if (!mixinApplications) {
      mixinApplications = new WeakMap();
      /** @type {!MixinFunction} */

      mixin.__mixinApplications = mixinApplications;
    } // maintain a unique id for each mixin


    let mixinDedupeId = dedupeId++;

    function dedupingMixin(base) {
      let baseSet =
      /** @type {!MixinFunction} */
      base.__mixinSet;

      if (baseSet && baseSet[mixinDedupeId]) {
        return base;
      }

      let map = mixinApplications;
      let extended = map.get(base);

      if (!extended) {
        extended =
        /** @type {!Function} */
        mixin(base);
        map.set(base, extended); // copy inherited mixin set from the extended class, or the base class
        // NOTE: we avoid use of Set here because some browser (IE11)
        // cannot extend a base Set via the constructor.

        let mixinSet = Object.create(
        /** @type {!MixinFunction} */
        extended.__mixinSet || baseSet || null);
        mixinSet[mixinDedupeId] = true;
        /** @type {!MixinFunction} */

        extended.__mixinSet = mixinSet;
      }

      return extended;
    }

    return dedupingMixin;
  };
  /* eslint-enable valid-jsdoc */

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  let modules = {};
  let lcModules = {};
  /**
   * Sets a dom-module into the global registry by id.
   *
   * @param {string} id dom-module id
   * @param {DomModule} module dom-module instance
   * @return {void}
   */

  function setModule(id, module) {
    // store id separate from lowercased id so that
    // in all cases mixedCase id will stored distinctly
    // and lowercase version is a fallback
    modules[id] = lcModules[id.toLowerCase()] = module;
  }
  /**
   * Retrieves a dom-module from the global registry by id.
   *
   * @param {string} id dom-module id
   * @return {DomModule!} dom-module instance
   */


  function findModule(id) {
    return modules[id] || lcModules[id.toLowerCase()];
  }

  function styleOutsideTemplateCheck(inst) {
    if (inst.querySelector('style')) {
      console.warn('dom-module %s has style outside template', inst.id);
    }
  }
  /**
   * The `dom-module` element registers the dom it contains to the name given
   * by the module's id attribute. It provides a unified database of dom
   * accessible via its static `import` API.
   *
   * A key use case of `dom-module` is for providing custom element `<template>`s
   * via HTML imports that are parsed by the native HTML parser, that can be
   * relocated during a bundling pass and still looked up by `id`.
   *
   * Example:
   *
   *     <dom-module id="foo">
   *       <img src="stuff.png">
   *     </dom-module>
   *
   * Then in code in some other location that cannot access the dom-module above
   *
   *     let img = customElements.get('dom-module').import('foo', 'img');
   *
   * @customElement
   * @extends HTMLElement
   * @summary Custom element that provides a registry of relocatable DOM content
   *   by `id` that is agnostic to bundling.
   * @unrestricted
   */


  class DomModule extends HTMLElement {
    /** @override */
    static get observedAttributes() {
      return ['id'];
    }
    /**
     * Retrieves the element specified by the css `selector` in the module
     * registered by `id`. For example, this.import('foo', 'img');
     * @param {string} id The id of the dom-module in which to search.
     * @param {string=} selector The css selector by which to find the element.
     * @return {Element} Returns the element which matches `selector` in the
     * module registered at the specified `id`.
     *
     * @export
     * @nocollapse Referred to indirectly in style-gather.js
     */


    static import(id, selector) {
      if (id) {
        let m = findModule(id);

        if (m && selector) {
          return m.querySelector(selector);
        }

        return m;
      }

      return null;
    }
    /* eslint-disable no-unused-vars */

    /**
     * @param {string} name Name of attribute.
     * @param {?string} old Old value of attribute.
     * @param {?string} value Current value of attribute.
     * @param {?string} namespace Attribute namespace.
     * @return {void}
     * @override
     */


    attributeChangedCallback(name, old, value, namespace) {
      if (old !== value) {
        this.register();
      }
    }
    /* eslint-enable no-unused-args */

    /**
     * The absolute URL of the original location of this `dom-module`.
     *
     * This value will differ from this element's `ownerDocument` in the
     * following ways:
     * - Takes into account any `assetpath` attribute added during bundling
     *   to indicate the original location relative to the bundled location
     * - Uses the HTMLImports polyfill's `importForElement` API to ensure
     *   the path is relative to the import document's location since
     *   `ownerDocument` is not currently polyfilled
     */


    get assetpath() {
      // Don't override existing assetpath.
      if (!this.__assetpath) {
        // note: assetpath set via an attribute must be relative to this
        // element's location; accomodate polyfilled HTMLImports
        const owner = window.HTMLImports && HTMLImports.importForElement ? HTMLImports.importForElement(this) || document : this.ownerDocument;
        const url = resolveUrl(this.getAttribute('assetpath') || '', owner.baseURI);
        this.__assetpath = pathFromUrl(url);
      }

      return this.__assetpath;
    }
    /**
     * Registers the dom-module at a given id. This method should only be called
     * when a dom-module is imperatively created. For
     * example, `document.createElement('dom-module').register('foo')`.
     * @param {string=} id The id at which to register the dom-module.
     * @return {void}
     */


    register(id) {
      id = id || this.id;

      if (id) {
        // Under strictTemplatePolicy, reject and null out any re-registered
        // dom-module since it is ambiguous whether first-in or last-in is trusted
        if (strictTemplatePolicy && findModule(id) !== undefined) {
          setModule(id, null);
          throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`);
        }

        this.id = id;
        setModule(id, this);
        styleOutsideTemplateCheck(this);
      }
    }

  }
  DomModule.prototype['modules'] = modules;
  customElements.define('dom-module', DomModule);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const MODULE_STYLE_LINK_SELECTOR = 'link[rel=import][type~=css]';
  const INCLUDE_ATTR = 'include';
  const SHADY_UNSCOPED_ATTR = 'shady-unscoped';
  /**
   * @param {string} moduleId .
   * @return {?DomModule} .
   */

  function importModule(moduleId) {
    return (
      /** @type {?DomModule} */
      DomModule.import(moduleId)
    );
  }

  function styleForImport(importDoc) {
    // NOTE: polyfill affordance.
    // under the HTMLImports polyfill, there will be no 'body',
    // but the import pseudo-doc can be used directly.
    let container = importDoc.body ? importDoc.body : importDoc;
    const importCss = resolveCss(container.textContent, importDoc.baseURI);
    const style = document.createElement('style');
    style.textContent = importCss;
    return style;
  }

  /**
   * Returns a list of <style> elements in a space-separated list of `dom-module`s.
   *
   * @function
   * @param {string} moduleIds List of dom-module id's within which to
   * search for css.
   * @return {!Array<!HTMLStyleElement>} Array of contained <style> elements
   */

  function stylesFromModules(moduleIds) {
    const modules = moduleIds.trim().split(/\s+/);
    const styles = [];

    for (let i = 0; i < modules.length; i++) {
      styles.push(...stylesFromModule(modules[i]));
    }

    return styles;
  }
  /**
   * Returns a list of <style> elements in a given `dom-module`.
   * Styles in a `dom-module` can come either from `<style>`s within the
   * first `<template>`, or else from one or more
   * `<link rel="import" type="css">` links outside the template.
   *
   * @param {string} moduleId dom-module id to gather styles from
   * @return {!Array<!HTMLStyleElement>} Array of contained styles.
   */

  function stylesFromModule(moduleId) {
    const m = importModule(moduleId);

    if (!m) {
      console.warn('Could not find style data in module named', moduleId);
      return [];
    }

    if (m._styles === undefined) {
      const styles = []; // module imports: <link rel="import" type="css">

      styles.push(..._stylesFromModuleImports(m)); // include css from the first template in the module

      const template =
      /** @type {?HTMLTemplateElement} */
      m.querySelector('template');

      if (template) {
        styles.push(...stylesFromTemplate(template,
        /** @type {templateWithAssetPath} */
        m.assetpath));
      }

      m._styles = styles;
    }

    return m._styles;
  }
  /**
   * Returns the `<style>` elements within a given template.
   *
   * @param {!HTMLTemplateElement} template Template to gather styles from
   * @param {string=} baseURI baseURI for style content
   * @return {!Array<!HTMLStyleElement>} Array of styles
   */

  function stylesFromTemplate(template, baseURI) {
    if (!template._styles) {
      const styles = []; // if element is a template, get content from its .content

      const e$ = template.content.querySelectorAll('style');

      for (let i = 0; i < e$.length; i++) {
        let e = e$[i]; // support style sharing by allowing styles to "include"
        // other dom-modules that contain styling

        let include = e.getAttribute(INCLUDE_ATTR);

        if (include) {
          styles.push(...stylesFromModules(include).filter(function (item, index, self) {
            return self.indexOf(item) === index;
          }));
        }

        if (baseURI) {
          e.textContent = resolveCss(e.textContent,
          /** @type {string} */
          baseURI);
        }

        styles.push(e);
      }

      template._styles = styles;
    }

    return template._styles;
  }
  /**
   * Returns a list of <style> elements  from stylesheets loaded via `<link rel="import" type="css">` links within the specified `dom-module`.
   *
   * @param {string} moduleId Id of `dom-module` to gather CSS from
   * @return {!Array<!HTMLStyleElement>} Array of contained styles.
   */

  function stylesFromModuleImports(moduleId) {
    let m = importModule(moduleId);
    return m ? _stylesFromModuleImports(m) : [];
  }
  /**
   * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
   * @return {!Array<!HTMLStyleElement>} Array of contained styles
   */

  function _stylesFromModuleImports(module) {
    const styles = [];
    const p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);

    for (let i = 0; i < p$.length; i++) {
      let p = p$[i];

      if (p.import) {
        const importDoc = p.import;
        const unscoped = p.hasAttribute(SHADY_UNSCOPED_ATTR);

        if (unscoped && !importDoc._unscopedStyle) {
          const style = styleForImport(importDoc);
          style.setAttribute(SHADY_UNSCOPED_ATTR, '');
          importDoc._unscopedStyle = style;
        } else if (!importDoc._style) {
          importDoc._style = styleForImport(importDoc);
        }

        styles.push(unscoped ? importDoc._unscopedStyle : importDoc._style);
      }
    }

    return styles;
  }
  /**
   *
   * Returns CSS text of styles in a space-separated list of `dom-module`s.
   * Note: This method is deprecated, use `stylesFromModules` instead.
   *
   * @deprecated
   * @param {string} moduleIds List of dom-module id's within which to
   * search for css.
   * @return {string} Concatenated CSS content from specified `dom-module`s
   */


  function cssFromModules(moduleIds) {
    let modules = moduleIds.trim().split(/\s+/);
    let cssText = '';

    for (let i = 0; i < modules.length; i++) {
      cssText += cssFromModule(modules[i]);
    }

    return cssText;
  }
  /**
   * Returns CSS text of styles in a given `dom-module`.  CSS in a `dom-module`
   * can come either from `<style>`s within the first `<template>`, or else
   * from one or more `<link rel="import" type="css">` links outside the
   * template.
   *
   * Any `<styles>` processed are removed from their original location.
   * Note: This method is deprecated, use `styleFromModule` instead.
   *
   * @deprecated
   * @param {string} moduleId dom-module id to gather styles from
   * @return {string} Concatenated CSS content from specified `dom-module`
   */

  function cssFromModule(moduleId) {
    let m = importModule(moduleId);

    if (m && m._cssText === undefined) {
      // module imports: <link rel="import" type="css">
      let cssText = _cssFromModuleImports(m); // include css from the first template in the module


      let t =
      /** @type {?HTMLTemplateElement} */
      m.querySelector('template');

      if (t) {
        cssText += cssFromTemplate(t,
        /** @type {templateWithAssetPath} */
        m.assetpath);
      }

      m._cssText = cssText || null;
    }

    if (!m) {
      console.warn('Could not find style data in module named', moduleId);
    }

    return m && m._cssText || '';
  }
  /**
   * Returns CSS text of `<styles>` within a given template.
   *
   * Any `<styles>` processed are removed from their original location.
   * Note: This method is deprecated, use `styleFromTemplate` instead.
   *
   * @deprecated
   * @param {!HTMLTemplateElement} template Template to gather styles from
   * @param {string} baseURI Base URI to resolve the URL against
   * @return {string} Concatenated CSS content from specified template
   */

  function cssFromTemplate(template, baseURI) {
    let cssText = '';
    const e$ = stylesFromTemplate(template, baseURI); // if element is a template, get content from its .content

    for (let i = 0; i < e$.length; i++) {
      let e = e$[i];

      if (e.parentNode) {
        e.parentNode.removeChild(e);
      }

      cssText += e.textContent;
    }

    return cssText;
  }
  /**
   * @deprecated
   * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
   * @return {string} Concatenated CSS content from links in the dom-module
   */

  function _cssFromModuleImports(module) {
    let cssText = '';

    let styles = _stylesFromModuleImports(module);

    for (let i = 0; i < styles.length; i++) {
      cssText += styles[i].textContent;
    }

    return cssText;
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  /* eslint-disable valid-jsdoc */

  /**
   * Node wrapper to ensure ShadowDOM safe operation regardless of polyfill
   * presence or mode. Note that with the introduction of `ShadyDOM.noPatch`,
   * a node wrapper must be used to access ShadowDOM API.
   * This is similar to using `Polymer.dom` but relies exclusively
   * on the presence of the ShadyDOM polyfill rather than requiring the loading
   * of legacy (Polymer.dom) API.
   * @type {function(Node):Node}
   */
  const wrap$1 = window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['wrap'] ? window['ShadyDOM']['wrap'] : window['ShadyDOM'] ? n => ShadyDOM['patch'](n) : n => n;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Module with utilities for manipulating structured data path strings.
   *
   * @summary Module with utilities for manipulating structured data path strings.
   */

  /**
   * Returns true if the given string is a structured data path (has dots).
   *
   * Example:
   *
   * ```
   * isPath('foo.bar.baz') // true
   * isPath('foo')         // false
   * ```
   *
   * @param {string} path Path string
   * @return {boolean} True if the string contained one or more dots
   */

  function isPath(path) {
    return path.indexOf('.') >= 0;
  }
  /**
   * Returns the root property name for the given path.
   *
   * Example:
   *
   * ```
   * root('foo.bar.baz') // 'foo'
   * root('foo')         // 'foo'
   * ```
   *
   * @param {string} path Path string
   * @return {string} Root property name
   */

  function root(path) {
    let dotIndex = path.indexOf('.');

    if (dotIndex === -1) {
      return path;
    }

    return path.slice(0, dotIndex);
  }
  /**
   * Given `base` is `foo.bar`, `foo` is an ancestor, `foo.bar` is not
   * Returns true if the given path is an ancestor of the base path.
   *
   * Example:
   *
   * ```
   * isAncestor('foo.bar', 'foo')         // true
   * isAncestor('foo.bar', 'foo.bar')     // false
   * isAncestor('foo.bar', 'foo.bar.baz') // false
   * ```
   *
   * @param {string} base Path string to test against.
   * @param {string} path Path string to test.
   * @return {boolean} True if `path` is an ancestor of `base`.
   */

  function isAncestor(base, path) {
    //     base.startsWith(path + '.');
    return base.indexOf(path + '.') === 0;
  }
  /**
   * Given `base` is `foo.bar`, `foo.bar.baz` is an descendant
   *
   * Example:
   *
   * ```
   * isDescendant('foo.bar', 'foo.bar.baz') // true
   * isDescendant('foo.bar', 'foo.bar')     // false
   * isDescendant('foo.bar', 'foo')         // false
   * ```
   *
   * @param {string} base Path string to test against.
   * @param {string} path Path string to test.
   * @return {boolean} True if `path` is a descendant of `base`.
   */

  function isDescendant(base, path) {
    //     path.startsWith(base + '.');
    return path.indexOf(base + '.') === 0;
  }
  /**
   * Replaces a previous base path with a new base path, preserving the
   * remainder of the path.
   *
   * User must ensure `path` has a prefix of `base`.
   *
   * Example:
   *
   * ```
   * translate('foo.bar', 'zot', 'foo.bar.baz') // 'zot.baz'
   * ```
   *
   * @param {string} base Current base string to remove
   * @param {string} newBase New base string to replace with
   * @param {string} path Path to translate
   * @return {string} Translated string
   */

  function translate(base, newBase, path) {
    return newBase + path.slice(base.length);
  }
  /**
   * @param {string} base Path string to test against
   * @param {string} path Path string to test
   * @return {boolean} True if `path` is equal to `base`
   */

  function matches(base, path) {
    return base === path || isAncestor(base, path) || isDescendant(base, path);
  }
  /**
   * Converts array-based paths to flattened path.  String-based paths
   * are returned as-is.
   *
   * Example:
   *
   * ```
   * normalize(['foo.bar', 0, 'baz'])  // 'foo.bar.0.baz'
   * normalize('foo.bar.0.baz')        // 'foo.bar.0.baz'
   * ```
   *
   * @param {string | !Array<string|number>} path Input path
   * @return {string} Flattened path
   */

  function normalize(path) {
    if (Array.isArray(path)) {
      let parts = [];

      for (let i = 0; i < path.length; i++) {
        let args = path[i].toString().split('.');

        for (let j = 0; j < args.length; j++) {
          parts.push(args[j]);
        }
      }

      return parts.join('.');
    } else {
      return path;
    }
  }
  /**
   * Splits a path into an array of property names. Accepts either arrays
   * of path parts or strings.
   *
   * Example:
   *
   * ```
   * split(['foo.bar', 0, 'baz'])  // ['foo', 'bar', '0', 'baz']
   * split('foo.bar.0.baz')        // ['foo', 'bar', '0', 'baz']
   * ```
   *
   * @param {string | !Array<string|number>} path Input path
   * @return {!Array<string>} Array of path parts
   * @suppress {checkTypes}
   */

  function split(path) {
    if (Array.isArray(path)) {
      return normalize(path).split('.');
    }

    return path.toString().split('.');
  }
  /**
   * Reads a value from a path.  If any sub-property in the path is `undefined`,
   * this method returns `undefined` (will never throw.
   *
   * @param {Object} root Object from which to dereference path from
   * @param {string | !Array<string|number>} path Path to read
   * @param {Object=} info If an object is provided to `info`, the normalized
   *  (flattened) path will be set to `info.path`.
   * @return {*} Value at path, or `undefined` if the path could not be
   *  fully dereferenced.
   */

  function get(root, path, info) {
    let prop = root;
    let parts = split(path); // Loop over path parts[0..n-1] and dereference

    for (let i = 0; i < parts.length; i++) {
      if (!prop) {
        return;
      }

      let part = parts[i];
      prop = prop[part];
    }

    if (info) {
      info.path = parts.join('.');
    }

    return prop;
  }
  /**
   * Sets a value to a path.  If any sub-property in the path is `undefined`,
   * this method will no-op.
   *
   * @param {Object} root Object from which to dereference path from
   * @param {string | !Array<string|number>} path Path to set
   * @param {*} value Value to set to path
   * @return {string | undefined} The normalized version of the input path
   */

  function set(root, path, value) {
    let prop = root;
    let parts = split(path);
    let last = parts[parts.length - 1];

    if (parts.length > 1) {
      // Loop over path parts[0..n-2] and dereference
      for (let i = 0; i < parts.length - 1; i++) {
        let part = parts[i];
        prop = prop[part];

        if (!prop) {
          return;
        }
      } // Set value to object at end of path


      prop[last] = value;
    } else {
      // Simple property set
      prop[path] = value;
    }

    return parts.join('.');
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const caseMap = {};
  const DASH_TO_CAMEL = /-[a-z]/g;
  const CAMEL_TO_DASH = /([A-Z])/g;
  /**
   * @fileoverview Module with utilities for converting between "dash-case" and
   * "camelCase" identifiers.
   */

  /**
   * Converts "dash-case" identifier (e.g. `foo-bar-baz`) to "camelCase"
   * (e.g. `fooBarBaz`).
   *
   * @param {string} dash Dash-case identifier
   * @return {string} Camel-case representation of the identifier
   */

  function dashToCamelCase(dash) {
    return caseMap[dash] || (caseMap[dash] = dash.indexOf('-') < 0 ? dash : dash.replace(DASH_TO_CAMEL, m => m[1].toUpperCase()));
  }
  /**
   * Converts "camelCase" identifier (e.g. `fooBarBaz`) to "dash-case"
   * (e.g. `foo-bar-baz`).
   *
   * @param {string} camel Camel-case identifier
   * @return {string} Dash-case representation of the identifier
   */

  function camelToDashCase(camel) {
    return caseMap[camel] || (caseMap[camel] = camel.replace(CAMEL_TO_DASH, '-$1').toLowerCase());
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  let microtaskCurrHandle = 0;
  let microtaskLastHandle = 0;
  let microtaskCallbacks = [];
  let microtaskNodeContent = 0;
  let microtaskScheduled = false;
  let microtaskNode = document.createTextNode('');
  new window.MutationObserver(microtaskFlush).observe(microtaskNode, {
    characterData: true
  });

  function microtaskFlush() {
    microtaskScheduled = false;
    const len = microtaskCallbacks.length;

    for (let i = 0; i < len; i++) {
      let cb = microtaskCallbacks[i];

      if (cb) {
        try {
          cb();
        } catch (e) {
          setTimeout(() => {
            throw e;
          });
        }
      }
    }

    microtaskCallbacks.splice(0, len);
    microtaskLastHandle += len;
  }
  /**
   * Async interface wrapper around `setTimeout`.
   *
   * @namespace
   * @summary Async interface wrapper around `setTimeout`.
   */


  const timeOut = {
    /**
     * Returns a sub-module with the async interface providing the provided
     * delay.
     *
     * @memberof timeOut
     * @param {number=} delay Time to wait before calling callbacks in ms
     * @return {!AsyncInterface} An async timeout interface
     */
    after(delay) {
      return {
        run(fn) {
          return window.setTimeout(fn, delay);
        },

        cancel(handle) {
          window.clearTimeout(handle);
        }

      };
    },

    /**
     * Enqueues a function called in the next task.
     *
     * @memberof timeOut
     * @param {!Function} fn Callback to run
     * @param {number=} delay Delay in milliseconds
     * @return {number} Handle used for canceling task
     */
    run(fn, delay) {
      return window.setTimeout(fn, delay);
    },

    /**
     * Cancels a previously enqueued `timeOut` callback.
     *
     * @memberof timeOut
     * @param {number} handle Handle returned from `run` of callback to cancel
     * @return {void}
     */
    cancel(handle) {
      window.clearTimeout(handle);
    }

  };
  /**
   * Async interface wrapper around `requestAnimationFrame`.
   *
   * @namespace
   * @summary Async interface wrapper around `requestAnimationFrame`.
   */

  const animationFrame = {
    /**
     * Enqueues a function called at `requestAnimationFrame` timing.
     *
     * @memberof animationFrame
     * @param {function(number):void} fn Callback to run
     * @return {number} Handle used for canceling task
     */
    run(fn) {
      return window.requestAnimationFrame(fn);
    },

    /**
     * Cancels a previously enqueued `animationFrame` callback.
     *
     * @memberof animationFrame
     * @param {number} handle Handle returned from `run` of callback to cancel
     * @return {void}
     */
    cancel(handle) {
      window.cancelAnimationFrame(handle);
    }

  };
  /**
   * Async interface for enqueuing callbacks that run at microtask timing.
   *
   * Note that microtask timing is achieved via a single `MutationObserver`,
   * and thus callbacks enqueued with this API will all run in a single
   * batch, and not interleaved with other microtasks such as promises.
   * Promises are avoided as an implementation choice for the time being
   * due to Safari bugs that cause Promises to lack microtask guarantees.
   *
   * @namespace
   * @summary Async interface for enqueuing callbacks that run at microtask
   *   timing.
   */

  const microTask = {
    /**
     * Enqueues a function called at microtask timing.
     *
     * @memberof microTask
     * @param {!Function=} callback Callback to run
     * @return {number} Handle used for canceling task
     */
    run(callback) {
      if (!microtaskScheduled) {
        microtaskScheduled = true;
        microtaskNode.textContent = microtaskNodeContent++;
      }

      microtaskCallbacks.push(callback);
      return microtaskCurrHandle++;
    },

    /**
     * Cancels a previously enqueued `microTask` callback.
     *
     * @memberof microTask
     * @param {number} handle Handle returned from `run` of callback to cancel
     * @return {void}
     */
    cancel(handle) {
      const idx = handle - microtaskLastHandle;

      if (idx >= 0) {
        if (!microtaskCallbacks[idx]) {
          throw new Error('invalid async handle: ' + handle);
        }

        microtaskCallbacks[idx] = null;
      }
    }

  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /** @const {!AsyncInterface} */

  const microtask = microTask;
  /**
   * Element class mixin that provides basic meta-programming for creating one
   * or more property accessors (getter/setter pair) that enqueue an async
   * (batched) `_propertiesChanged` callback.
   *
   * For basic usage of this mixin, call `MyClass.createProperties(props)`
   * once at class definition time to create property accessors for properties
   * named in props, implement `_propertiesChanged` to react as desired to
   * property changes, and implement `static get observedAttributes()` and
   * include lowercase versions of any property names that should be set from
   * attributes. Last, call `this._enableProperties()` in the element's
   * `connectedCallback` to enable the accessors.
   *
   * @mixinFunction
   * @polymer
   * @summary Element class mixin for reacting to property changes from
   *   generated property accessors.
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */

  const PropertiesChanged = dedupingMixin(
  /**
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */
  superClass => {
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_PropertiesChanged}
     * @unrestricted
     */
    class PropertiesChanged extends superClass {
      /**
       * Creates property accessors for the given property names.
       * @param {!Object} props Object whose keys are names of accessors.
       * @return {void}
       * @protected
       * @nocollapse
       */
      static createProperties(props) {
        const proto = this.prototype;

        for (let prop in props) {
          // don't stomp an existing accessor
          if (!(prop in proto)) {
            proto._createPropertyAccessor(prop);
          }
        }
      }
      /**
       * Returns an attribute name that corresponds to the given property.
       * The attribute name is the lowercased property name. Override to
       * customize this mapping.
       * @param {string} property Property to convert
       * @return {string} Attribute name corresponding to the given property.
       *
       * @protected
       * @nocollapse
       */


      static attributeNameForProperty(property) {
        return property.toLowerCase();
      }
      /**
       * Override point to provide a type to which to deserialize a value to
       * a given property.
       * @param {string} name Name of property
       *
       * @protected
       * @nocollapse
       */


      static typeForProperty(name) {} //eslint-disable-line no-unused-vars

      /**
       * Creates a setter/getter pair for the named property with its own
       * local storage.  The getter returns the value in the local storage,
       * and the setter calls `_setProperty`, which updates the local storage
       * for the property and enqueues a `_propertiesChanged` callback.
       *
       * This method may be called on a prototype or an instance.  Calling
       * this method may overwrite a property value that already exists on
       * the prototype/instance by creating the accessor.
       *
       * @param {string} property Name of the property
       * @param {boolean=} readOnly When true, no setter is created; the
       *   protected `_setProperty` function must be used to set the property
       * @return {void}
       * @protected
       * @override
       */


      _createPropertyAccessor(property, readOnly) {
        this._addPropertyToAttributeMap(property);

        if (!this.hasOwnProperty(JSCompiler_renameProperty('__dataHasAccessor', this))) {
          this.__dataHasAccessor = Object.assign({}, this.__dataHasAccessor);
        }

        if (!this.__dataHasAccessor[property]) {
          this.__dataHasAccessor[property] = true;

          this._definePropertyAccessor(property, readOnly);
        }
      }
      /**
       * Adds the given `property` to a map matching attribute names
       * to property names, using `attributeNameForProperty`. This map is
       * used when deserializing attribute values to properties.
       *
       * @param {string} property Name of the property
       * @override
       */


      _addPropertyToAttributeMap(property) {
        if (!this.hasOwnProperty(JSCompiler_renameProperty('__dataAttributes', this))) {
          this.__dataAttributes = Object.assign({}, this.__dataAttributes);
        } // This check is technically not correct; it's an optimization that
        // assumes that if a _property_ name is already in the map (note this is
        // an attr->property map), the property mapped directly to the attribute
        // and it has already been mapped.  This would fail if
        // `attributeNameForProperty` were overridden such that this was not the
        // case.


        let attr = this.__dataAttributes[property];

        if (!attr) {
          attr = this.constructor.attributeNameForProperty(property);
          this.__dataAttributes[attr] = property;
        }

        return attr;
      }
      /**
       * Defines a property accessor for the given property.
       * @param {string} property Name of the property
       * @param {boolean=} readOnly When true, no setter is created
       * @return {void}
       * @override
       */


      _definePropertyAccessor(property, readOnly) {
        Object.defineProperty(this, property, {
          /* eslint-disable valid-jsdoc */

          /** @this {PropertiesChanged} */
          get() {
            // Inline for perf instead of using `_getProperty`
            return this.__data[property];
          },

          /** @this {PropertiesChanged} */
          set: readOnly ? function () {} : function (value) {
            // Inline for perf instead of using `_setProperty`
            if (this._setPendingProperty(property, value, true)) {
              this._invalidateProperties();
            }
          }
          /* eslint-enable */

        });
      }

      constructor() {
        super();
        /** @type {boolean} */

        this.__dataEnabled = false;
        this.__dataReady = false;
        this.__dataInvalid = false;
        this.__data = {};
        this.__dataPending = null;
        this.__dataOld = null;
        this.__dataInstanceProps = null;
        /** @type {number} */
        // NOTE: used to track re-entrant calls to `_flushProperties`

        this.__dataCounter = 0;
        this.__serializing = false;

        this._initializeProperties();
      }
      /**
       * Lifecycle callback called when properties are enabled via
       * `_enableProperties`.
       *
       * Users may override this function to implement behavior that is
       * dependent on the element having its property data initialized, e.g.
       * from defaults (initialized from `constructor`, `_initializeProperties`),
       * `attributeChangedCallback`, or values propagated from host e.g. via
       * bindings.  `super.ready()` must be called to ensure the data system
       * becomes enabled.
       *
       * @return {void}
       * @public
       * @override
       */


      ready() {
        this.__dataReady = true;

        this._flushProperties();
      }
      /**
       * Initializes the local storage for property accessors.
       *
       * Provided as an override point for performing any setup work prior
       * to initializing the property accessor system.
       *
       * @return {void}
       * @protected
       * @override
       */


      _initializeProperties() {
        // Capture instance properties; these will be set into accessors
        // during first flush. Don't set them here, since we want
        // these to overwrite defaults/constructor assignments
        for (let p in this.__dataHasAccessor) {
          if (this.hasOwnProperty(p)) {
            this.__dataInstanceProps = this.__dataInstanceProps || {};
            this.__dataInstanceProps[p] = this[p];
            delete this[p];
          }
        }
      }
      /**
       * Called at ready time with bag of instance properties that overwrote
       * accessors when the element upgraded.
       *
       * The default implementation sets these properties back into the
       * setter at ready time.  This method is provided as an override
       * point for customizing or providing more efficient initialization.
       *
       * @param {Object} props Bag of property values that were overwritten
       *   when creating property accessors.
       * @return {void}
       * @protected
       * @override
       */


      _initializeInstanceProperties(props) {
        Object.assign(this, props);
      }
      /**
       * Updates the local storage for a property (via `_setPendingProperty`)
       * and enqueues a `_proeprtiesChanged` callback.
       *
       * @param {string} property Name of the property
       * @param {*} value Value to set
       * @return {void}
       * @protected
       * @override
       */


      _setProperty(property, value) {
        if (this._setPendingProperty(property, value)) {
          this._invalidateProperties();
        }
      }
      /**
       * Returns the value for the given property.
       * @param {string} property Name of property
       * @return {*} Value for the given property
       * @protected
       * @override
       */


      _getProperty(property) {
        return this.__data[property];
      }
      /* eslint-disable no-unused-vars */

      /**
       * Updates the local storage for a property, records the previous value,
       * and adds it to the set of "pending changes" that will be passed to the
       * `_propertiesChanged` callback.  This method does not enqueue the
       * `_propertiesChanged` callback.
       *
       * @param {string} property Name of the property
       * @param {*} value Value to set
       * @param {boolean=} ext Not used here; affordance for closure
       * @return {boolean} Returns true if the property changed
       * @protected
       * @override
       */


      _setPendingProperty(property, value, ext) {
        let old = this.__data[property];

        let changed = this._shouldPropertyChange(property, value, old);

        if (changed) {
          if (!this.__dataPending) {
            this.__dataPending = {};
            this.__dataOld = {};
          } // Ensure old is captured from the last turn


          if (this.__dataOld && !(property in this.__dataOld)) {
            this.__dataOld[property] = old;
          }

          this.__data[property] = value;
          this.__dataPending[property] = value;
        }

        return changed;
      }
      /* eslint-enable */

      /**
       * @param {string} property Name of the property
       * @return {boolean} Returns true if the property is pending.
       */


      _isPropertyPending(property) {
        return !!(this.__dataPending && this.__dataPending.hasOwnProperty(property));
      }
      /**
       * Marks the properties as invalid, and enqueues an async
       * `_propertiesChanged` callback.
       *
       * @return {void}
       * @protected
       * @override
       */


      _invalidateProperties() {
        if (!this.__dataInvalid && this.__dataReady) {
          this.__dataInvalid = true;
          microtask.run(() => {
            if (this.__dataInvalid) {
              this.__dataInvalid = false;

              this._flushProperties();
            }
          });
        }
      }
      /**
       * Call to enable property accessor processing. Before this method is
       * called accessor values will be set but side effects are
       * queued. When called, any pending side effects occur immediately.
       * For elements, generally `connectedCallback` is a normal spot to do so.
       * It is safe to call this method multiple times as it only turns on
       * property accessors once.
       *
       * @return {void}
       * @protected
       * @override
       */


      _enableProperties() {
        if (!this.__dataEnabled) {
          this.__dataEnabled = true;

          if (this.__dataInstanceProps) {
            this._initializeInstanceProperties(this.__dataInstanceProps);

            this.__dataInstanceProps = null;
          }

          this.ready();
        }
      }
      /**
       * Calls the `_propertiesChanged` callback with the current set of
       * pending changes (and old values recorded when pending changes were
       * set), and resets the pending set of changes. Generally, this method
       * should not be called in user code.
       *
       * @return {void}
       * @protected
       * @override
       */


      _flushProperties() {
        this.__dataCounter++;
        const props = this.__data;
        const changedProps = this.__dataPending;
        const old = this.__dataOld;

        if (this._shouldPropertiesChange(props, changedProps, old)) {
          this.__dataPending = null;
          this.__dataOld = null;

          this._propertiesChanged(props, changedProps, old);
        }

        this.__dataCounter--;
      }
      /**
       * Called in `_flushProperties` to determine if `_propertiesChanged`
       * should be called. The default implementation returns true if
       * properties are pending. Override to customize when
       * `_propertiesChanged` is called.
       * @param {!Object} currentProps Bag of all current accessor values
       * @param {?Object} changedProps Bag of properties changed since the last
       *   call to `_propertiesChanged`
       * @param {?Object} oldProps Bag of previous values for each property
       *   in `changedProps`
       * @return {boolean} true if changedProps is truthy
       * @override
       */


      _shouldPropertiesChange(currentProps, changedProps, oldProps) {
        // eslint-disable-line no-unused-vars
        return Boolean(changedProps);
      }
      /**
       * Callback called when any properties with accessors created via
       * `_createPropertyAccessor` have been set.
       *
       * @param {!Object} currentProps Bag of all current accessor values
       * @param {?Object} changedProps Bag of properties changed since the last
       *   call to `_propertiesChanged`
       * @param {?Object} oldProps Bag of previous values for each property
       *   in `changedProps`
       * @return {void}
       * @protected
       * @override
       */


      _propertiesChanged(currentProps, changedProps, oldProps) {// eslint-disable-line no-unused-vars
      }
      /**
       * Method called to determine whether a property value should be
       * considered as a change and cause the `_propertiesChanged` callback
       * to be enqueued.
       *
       * The default implementation returns `true` if a strict equality
       * check fails. The method always returns false for `NaN`.
       *
       * Override this method to e.g. provide stricter checking for
       * Objects/Arrays when using immutable patterns.
       *
       * @param {string} property Property name
       * @param {*} value New property value
       * @param {*} old Previous property value
       * @return {boolean} Whether the property should be considered a change
       *   and enqueue a `_proeprtiesChanged` callback
       * @protected
       * @override
       */


      _shouldPropertyChange(property, value, old) {
        return (// Strict equality check
          old !== value && ( // This ensures (old==NaN, value==NaN) always returns false
          old === old || value === value)
        );
      }
      /**
       * Implements native Custom Elements `attributeChangedCallback` to
       * set an attribute value to a property via `_attributeToProperty`.
       *
       * @param {string} name Name of attribute that changed
       * @param {?string} old Old attribute value
       * @param {?string} value New attribute value
       * @param {?string} namespace Attribute namespace.
       * @return {void}
       * @suppress {missingProperties} Super may or may not implement the callback
       * @override
       */


      attributeChangedCallback(name, old, value, namespace) {
        if (old !== value) {
          this._attributeToProperty(name, value);
        }

        if (super.attributeChangedCallback) {
          super.attributeChangedCallback(name, old, value, namespace);
        }
      }
      /**
       * Deserializes an attribute to its associated property.
       *
       * This method calls the `_deserializeValue` method to convert the string to
       * a typed value.
       *
       * @param {string} attribute Name of attribute to deserialize.
       * @param {?string} value of the attribute.
       * @param {*=} type type to deserialize to, defaults to the value
       * returned from `typeForProperty`
       * @return {void}
       * @override
       */


      _attributeToProperty(attribute, value, type) {
        if (!this.__serializing) {
          const map = this.__dataAttributes;
          const property = map && map[attribute] || attribute;
          this[property] = this._deserializeValue(value, type || this.constructor.typeForProperty(property));
        }
      }
      /**
       * Serializes a property to its associated attribute.
       *
       * @suppress {invalidCasts} Closure can't figure out `this` is an element.
       *
       * @param {string} property Property name to reflect.
       * @param {string=} attribute Attribute name to reflect to.
       * @param {*=} value Property value to refect.
       * @return {void}
       * @override
       */


      _propertyToAttribute(property, attribute, value) {
        this.__serializing = true;
        value = arguments.length < 3 ? this[property] : value;

        this._valueToNodeAttribute(
        /** @type {!HTMLElement} */
        this, value, attribute || this.constructor.attributeNameForProperty(property));

        this.__serializing = false;
      }
      /**
       * Sets a typed value to an HTML attribute on a node.
       *
       * This method calls the `_serializeValue` method to convert the typed
       * value to a string.  If the `_serializeValue` method returns `undefined`,
       * the attribute will be removed (this is the default for boolean
       * type `false`).
       *
       * @param {Element} node Element to set attribute to.
       * @param {*} value Value to serialize.
       * @param {string} attribute Attribute name to serialize to.
       * @return {void}
       * @override
       */


      _valueToNodeAttribute(node, value, attribute) {
        const str = this._serializeValue(value);

        if (attribute === 'class' || attribute === 'name' || attribute === 'slot') {
          node =
          /** @type {?Element} */
          wrap$1(node);
        }

        if (str === undefined) {
          node.removeAttribute(attribute);
        } else {
          node.setAttribute(attribute, str);
        }
      }
      /**
       * Converts a typed JavaScript value to a string.
       *
       * This method is called when setting JS property values to
       * HTML attributes.  Users may override this method to provide
       * serialization for custom types.
       *
       * @param {*} value Property value to serialize.
       * @return {string | undefined} String serialized from the provided
       * property  value.
       * @override
       */


      _serializeValue(value) {
        switch (typeof value) {
          case 'boolean':
            return value ? '' : undefined;

          default:
            return value != null ? value.toString() : undefined;
        }
      }
      /**
       * Converts a string to a typed JavaScript value.
       *
       * This method is called when reading HTML attribute values to
       * JS properties.  Users may override this method to provide
       * deserialization for custom `type`s. Types for `Boolean`, `String`,
       * and `Number` convert attributes to the expected types.
       *
       * @param {?string} value Value to deserialize.
       * @param {*=} type Type to deserialize the string to.
       * @return {*} Typed value deserialized from the provided string.
       * @override
       */


      _deserializeValue(value, type) {
        switch (type) {
          case Boolean:
            return value !== null;

          case Number:
            return Number(value);

          default:
            return value;
        }
      }

    }

    return PropertiesChanged;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  // that won't have their values "saved" by `saveAccessorValue`, since
  // reading from an HTMLElement accessor from the context of a prototype throws

  const nativeProperties = {};
  let proto = HTMLElement.prototype;

  while (proto) {
    let props = Object.getOwnPropertyNames(proto);

    for (let i = 0; i < props.length; i++) {
      nativeProperties[props[i]] = true;
    }

    proto = Object.getPrototypeOf(proto);
  }
  /**
   * Used to save the value of a property that will be overridden with
   * an accessor. If the `model` is a prototype, the values will be saved
   * in `__dataProto`, and it's up to the user (or downstream mixin) to
   * decide how/when to set these values back into the accessors.
   * If `model` is already an instance (it has a `__data` property), then
   * the value will be set as a pending property, meaning the user should
   * call `_invalidateProperties` or `_flushProperties` to take effect
   *
   * @param {Object} model Prototype or instance
   * @param {string} property Name of property
   * @return {void}
   * @private
   */


  function saveAccessorValue(model, property) {
    // Don't read/store value for any native properties since they could throw
    if (!nativeProperties[property]) {
      let value = model[property];

      if (value !== undefined) {
        if (model.__data) {
          // Adding accessor to instance; update the property
          // It is the user's responsibility to call _flushProperties
          model._setPendingProperty(property, value);
        } else {
          // Adding accessor to proto; save proto's value for instance-time use
          if (!model.__dataProto) {
            model.__dataProto = {};
          } else if (!model.hasOwnProperty(JSCompiler_renameProperty('__dataProto', model))) {
            model.__dataProto = Object.create(model.__dataProto);
          }

          model.__dataProto[property] = value;
        }
      }
    }
  }
  /**
   * Element class mixin that provides basic meta-programming for creating one
   * or more property accessors (getter/setter pair) that enqueue an async
   * (batched) `_propertiesChanged` callback.
   *
   * For basic usage of this mixin:
   *
   * -   Declare attributes to observe via the standard `static get
   *     observedAttributes()`. Use `dash-case` attribute names to represent
   *     `camelCase` property names.
   * -   Implement the `_propertiesChanged` callback on the class.
   * -   Call `MyClass.createPropertiesForAttributes()` **once** on the class to
   *     generate property accessors for each observed attribute. This must be
   *     called before the first instance is created, for example, by calling it
   *     before calling `customElements.define`. It can also be called lazily from
   *     the element's `constructor`, as long as it's guarded so that the call is
   *     only made once, when the first instance is created.
   * -   Call `this._enableProperties()` in the element's `connectedCallback` to
   *     enable the accessors.
   *
   * Any `observedAttributes` will automatically be
   * deserialized via `attributeChangedCallback` and set to the associated
   * property using `dash-case`-to-`camelCase` convention.
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin PropertiesChanged
   * @summary Element class mixin for reacting to property changes from
   *   generated property accessors.
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */


  const PropertyAccessors = dedupingMixin(superClass => {
    /**
     * @constructor
     * @implements {Polymer_PropertiesChanged}
     * @unrestricted
     * @private
     */
    const base = PropertiesChanged(superClass);
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_PropertyAccessors}
     * @extends {base}
     * @unrestricted
     */

    class PropertyAccessors extends base {
      /**
       * Generates property accessors for all attributes in the standard
       * static `observedAttributes` array.
       *
       * Attribute names are mapped to property names using the `dash-case` to
       * `camelCase` convention
       *
       * @return {void}
       * @nocollapse
       */
      static createPropertiesForAttributes() {
        let a$ =
        /** @type {?} */
        this.observedAttributes;

        for (let i = 0; i < a$.length; i++) {
          this.prototype._createPropertyAccessor(dashToCamelCase(a$[i]));
        }
      }
      /**
       * Returns an attribute name that corresponds to the given property.
       * By default, converts camel to dash case, e.g. `fooBar` to `foo-bar`.
       * @param {string} property Property to convert
       * @return {string} Attribute name corresponding to the given property.
       *
       * @protected
       * @nocollapse
       */


      static attributeNameForProperty(property) {
        return camelToDashCase(property);
      }
      /**
       * Overrides PropertiesChanged implementation to initialize values for
       * accessors created for values that already existed on the element
       * prototype.
       *
       * @return {void}
       * @protected
       * @override
       */


      _initializeProperties() {
        if (this.__dataProto) {
          this._initializeProtoProperties(this.__dataProto);

          this.__dataProto = null;
        }

        super._initializeProperties();
      }
      /**
       * Called at instance time with bag of properties that were overwritten
       * by accessors on the prototype when accessors were created.
       *
       * The default implementation sets these properties back into the
       * setter at instance time.  This method is provided as an override
       * point for customizing or providing more efficient initialization.
       *
       * @param {Object} props Bag of property values that were overwritten
       *   when creating property accessors.
       * @return {void}
       * @protected
       * @override
       */


      _initializeProtoProperties(props) {
        for (let p in props) {
          this._setProperty(p, props[p]);
        }
      }
      /**
       * Ensures the element has the given attribute. If it does not,
       * assigns the given value to the attribute.
       *
       * @suppress {invalidCasts} Closure can't figure out `this` is infact an
       *     element
       *
       * @param {string} attribute Name of attribute to ensure is set.
       * @param {string} value of the attribute.
       * @return {void}
       * @override
       */


      _ensureAttribute(attribute, value) {
        const el =
        /** @type {!HTMLElement} */
        this;

        if (!el.hasAttribute(attribute)) {
          this._valueToNodeAttribute(el, value, attribute);
        }
      }
      /**
       * Overrides PropertiesChanged implemention to serialize objects as JSON.
       *
       * @param {*} value Property value to serialize.
       * @return {string | undefined} String serialized from the provided property
       *     value.
       * @override
       */


      _serializeValue(value) {
        /* eslint-disable no-fallthrough */
        switch (typeof value) {
          case 'object':
            if (value instanceof Date) {
              return value.toString();
            } else if (value) {
              try {
                return JSON.stringify(value);
              } catch (x) {
                return '';
              }
            }

          default:
            return super._serializeValue(value);
        }
      }
      /**
       * Converts a string to a typed JavaScript value.
       *
       * This method is called by Polymer when reading HTML attribute values to
       * JS properties.  Users may override this method on Polymer element
       * prototypes to provide deserialization for custom `type`s.  Note,
       * the `type` argument is the value of the `type` field provided in the
       * `properties` configuration object for a given property, and is
       * by convention the constructor for the type to deserialize.
       *
       *
       * @param {?string} value Attribute value to deserialize.
       * @param {*=} type Type to deserialize the string to.
       * @return {*} Typed value deserialized from the provided string.
       * @override
       */


      _deserializeValue(value, type) {
        /**
         * @type {*}
         */
        let outValue;

        switch (type) {
          case Object:
            try {
              outValue = JSON.parse(
              /** @type {string} */
              value);
            } catch (x) {
              // allow non-JSON literals like Strings and Numbers
              outValue = value;
            }

            break;

          case Array:
            try {
              outValue = JSON.parse(
              /** @type {string} */
              value);
            } catch (x) {
              outValue = null;
              console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`);
            }

            break;

          case Date:
            outValue = isNaN(value) ? String(value) : Number(value);
            outValue = new Date(outValue);
            break;

          default:
            outValue = super._deserializeValue(value, type);
            break;
        }

        return outValue;
      }
      /* eslint-enable no-fallthrough */

      /**
       * Overrides PropertiesChanged implementation to save existing prototype
       * property value so that it can be reset.
       * @param {string} property Name of the property
       * @param {boolean=} readOnly When true, no setter is created
       *
       * When calling on a prototype, any overwritten values are saved in
       * `__dataProto`, and it is up to the subclasser to decide how/when
       * to set those properties back into the accessor.  When calling on an
       * instance, the overwritten value is set via `_setPendingProperty`,
       * and the user should call `_invalidateProperties` or `_flushProperties`
       * for the values to take effect.
       * @protected
       * @return {void}
       * @override
       */


      _definePropertyAccessor(property, readOnly) {
        saveAccessorValue(this, property);

        super._definePropertyAccessor(property, readOnly);
      }
      /**
       * Returns true if this library created an accessor for the given property.
       *
       * @param {string} property Property name
       * @return {boolean} True if an accessor was created
       * @override
       */


      _hasAccessor(property) {
        return this.__dataHasAccessor && this.__dataHasAccessor[property];
      }
      /**
       * Returns true if the specified property has a pending change.
       *
       * @param {string} prop Property name
       * @return {boolean} True if property has a pending change
       * @protected
       * @override
       */


      _isPropertyPending(prop) {
        return Boolean(this.__dataPending && prop in this.__dataPending);
      }

    }

    return PropertyAccessors;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  // This is a clear layering violation and gives favored-nation status to
  // dom-if and dom-repeat templates.  This is a conceit we're choosing to keep
  // a.) to ease 1.x backwards-compatibility due to loss of `is`, and
  // b.) to maintain if/repeat capability in parser-constrained elements
  //     (e.g. table, select) in lieu of native CE type extensions without
  //     massive new invention in this space (e.g. directive system)

  const templateExtensions = {
    'dom-if': true,
    'dom-repeat': true
  };
  let placeholderBugDetect = false;
  let placeholderBug = false;

  function hasPlaceholderBug() {
    if (!placeholderBugDetect) {
      placeholderBugDetect = true;
      const t = document.createElement('textarea');
      t.placeholder = 'a';
      placeholderBug = t.placeholder === t.textContent;
    }

    return placeholderBug;
  }
  /**
   * Some browsers have a bug with textarea, where placeholder text is copied as
   * a textnode child of the textarea.
   *
   * If the placeholder is a binding, this can break template stamping in two
   * ways.
   *
   * One issue is that when the `placeholder` attribute is removed when the
   * binding is processed, the textnode child of the textarea is deleted, and the
   * template info tries to bind into that node.
   *
   * With `legacyOptimizations` in use, when the template is stamped and the
   * `textarea.textContent` binding is processed, no corresponding node is found
   * because it was removed during parsing. An exception is generated when this
   * binding is updated.
   *
   * With `legacyOptimizations` not in use, the template is cloned before
   * processing and this changes the above behavior. The cloned template also has
   * a value property set to the placeholder and textContent. This prevents the
   * removal of the textContent when the placeholder attribute is removed.
   * Therefore the exception does not occur. However, there is an extra
   * unnecessary binding.
   *
   * @param {!Node} node Check node for placeholder bug
   * @return {void}
   */


  function fixPlaceholder(node) {
    if (hasPlaceholderBug() && node.localName === 'textarea' && node.placeholder && node.placeholder === node.textContent) {
      node.textContent = null;
    }
  }

  function wrapTemplateExtension(node) {
    let is = node.getAttribute('is');

    if (is && templateExtensions[is]) {
      let t = node;
      t.removeAttribute('is');
      node = t.ownerDocument.createElement(is);
      t.parentNode.replaceChild(node, t);
      node.appendChild(t);

      while (t.attributes.length) {
        node.setAttribute(t.attributes[0].name, t.attributes[0].value);
        t.removeAttribute(t.attributes[0].name);
      }
    }

    return node;
  }

  function findTemplateNode(root, nodeInfo) {
    // recursively ascend tree until we hit root
    let parent = nodeInfo.parentInfo && findTemplateNode(root, nodeInfo.parentInfo); // unwind the stack, returning the indexed node at each level

    if (parent) {
      // note: marginally faster than indexing via childNodes
      // (http://jsperf.com/childnodes-lookup)
      for (let n = parent.firstChild, i = 0; n; n = n.nextSibling) {
        if (nodeInfo.parentIndex === i++) {
          return n;
        }
      }
    } else {
      return root;
    }
  } // construct `$` map (from id annotations)


  function applyIdToMap(inst, map, node, nodeInfo) {
    if (nodeInfo.id) {
      map[nodeInfo.id] = node;
    }
  } // install event listeners (from event annotations)


  function applyEventListener(inst, node, nodeInfo) {
    if (nodeInfo.events && nodeInfo.events.length) {
      for (let j = 0, e$ = nodeInfo.events, e; j < e$.length && (e = e$[j]); j++) {
        inst._addMethodEventListenerToNode(node, e.name, e.value, inst);
      }
    }
  } // push configuration references at configure time


  function applyTemplateInfo(inst, node, nodeInfo, parentTemplateInfo) {
    if (nodeInfo.templateInfo) {
      // Give the node an instance of this templateInfo and set its parent
      node._templateInfo = nodeInfo.templateInfo;
      node._parentTemplateInfo = parentTemplateInfo;
    }
  }

  function createNodeEventHandler(context, eventName, methodName) {
    // Instances can optionally have a _methodHost which allows redirecting where
    // to find methods. Currently used by `templatize`.
    context = context._methodHost || context;

    let handler = function (e) {
      if (context[methodName]) {
        context[methodName](e, e.detail);
      } else {
        console.warn('listener method `' + methodName + '` not defined');
      }
    };

    return handler;
  }
  /**
   * Element mixin that provides basic template parsing and stamping, including
   * the following template-related features for stamped templates:
   *
   * - Declarative event listeners (`on-eventname="listener"`)
   * - Map of node id's to stamped node instances (`this.$.id`)
   * - Nested template content caching/removal and re-installation (performance
   *   optimization)
   *
   * @mixinFunction
   * @polymer
   * @summary Element class mixin that provides basic template parsing and stamping
   */


  const TemplateStamp = dedupingMixin(
  /**
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */
  superClass => {
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_TemplateStamp}
     */
    class TemplateStamp extends superClass {
      /**
       * Scans a template to produce template metadata.
       *
       * Template-specific metadata are stored in the object returned, and node-
       * specific metadata are stored in objects in its flattened `nodeInfoList`
       * array.  Only nodes in the template that were parsed as nodes of
       * interest contain an object in `nodeInfoList`.  Each `nodeInfo` object
       * contains an `index` (`childNodes` index in parent) and optionally
       * `parent`, which points to node info of its parent (including its index).
       *
       * The template metadata object returned from this method has the following
       * structure (many fields optional):
       *
       * ```js
       *   {
       *     // Flattened list of node metadata (for nodes that generated metadata)
       *     nodeInfoList: [
       *       {
       *         // `id` attribute for any nodes with id's for generating `$` map
       *         id: {string},
       *         // `on-event="handler"` metadata
       *         events: [
       *           {
       *             name: {string},   // event name
       *             value: {string},  // handler method name
       *           }, ...
       *         ],
       *         // Notes when the template contained a `<slot>` for shady DOM
       *         // optimization purposes
       *         hasInsertionPoint: {boolean},
       *         // For nested `<template>`` nodes, nested template metadata
       *         templateInfo: {object}, // nested template metadata
       *         // Metadata to allow efficient retrieval of instanced node
       *         // corresponding to this metadata
       *         parentInfo: {number},   // reference to parent nodeInfo>
       *         parentIndex: {number},  // index in parent's `childNodes` collection
       *         infoIndex: {number},    // index of this `nodeInfo` in `templateInfo.nodeInfoList`
       *       },
       *       ...
       *     ],
       *     // When true, the template had the `strip-whitespace` attribute
       *     // or was nested in a template with that setting
       *     stripWhitespace: {boolean},
       *     // For nested templates, nested template content is moved into
       *     // a document fragment stored here; this is an optimization to
       *     // avoid the cost of nested template cloning
       *     content: {DocumentFragment}
       *   }
       * ```
       *
       * This method kicks off a recursive treewalk as follows:
       *
       * ```
       *    _parseTemplate <---------------------+
       *      _parseTemplateContent              |
       *        _parseTemplateNode  <------------|--+
       *          _parseTemplateNestedTemplate --+  |
       *          _parseTemplateChildNodes ---------+
       *          _parseTemplateNodeAttributes
       *            _parseTemplateNodeAttribute
       *
       * ```
       *
       * These methods may be overridden to add custom metadata about templates
       * to either `templateInfo` or `nodeInfo`.
       *
       * Note that this method may be destructive to the template, in that
       * e.g. event annotations may be removed after being noted in the
       * template metadata.
       *
       * @param {!HTMLTemplateElement} template Template to parse
       * @param {TemplateInfo=} outerTemplateInfo Template metadata from the outer
       *   template, for parsing nested templates
       * @return {!TemplateInfo} Parsed template metadata
       * @nocollapse
       */
      static _parseTemplate(template, outerTemplateInfo) {
        // since a template may be re-used, memo-ize metadata
        if (!template._templateInfo) {
          // TODO(rictic): fix typing
          let
          /** ? */
          templateInfo = template._templateInfo = {};
          templateInfo.nodeInfoList = [];
          templateInfo.nestedTemplate = Boolean(outerTemplateInfo);
          templateInfo.stripWhiteSpace = outerTemplateInfo && outerTemplateInfo.stripWhiteSpace || template.hasAttribute('strip-whitespace'); // TODO(rictic): fix typing

          this._parseTemplateContent(template, templateInfo,
          /** @type {?} */
          {
            parent: null
          });
        }

        return template._templateInfo;
      }
      /**
       * See docs for _parseTemplateNode.
       *
       * @param {!HTMLTemplateElement} template .
       * @param {!TemplateInfo} templateInfo .
       * @param {!NodeInfo} nodeInfo .
       * @return {boolean} .
       * @nocollapse
       */


      static _parseTemplateContent(template, templateInfo, nodeInfo) {
        return this._parseTemplateNode(template.content, templateInfo, nodeInfo);
      }
      /**
       * Parses template node and adds template and node metadata based on
       * the current node, and its `childNodes` and `attributes`.
       *
       * This method may be overridden to add custom node or template specific
       * metadata based on this node.
       *
       * @param {Node} node Node to parse
       * @param {!TemplateInfo} templateInfo Template metadata for current template
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @nocollapse
       */


      static _parseTemplateNode(node, templateInfo, nodeInfo) {
        let noted = false;
        let element =
        /** @type {!HTMLTemplateElement} */
        node;

        if (element.localName == 'template' && !element.hasAttribute('preserve-content')) {
          noted = this._parseTemplateNestedTemplate(element, templateInfo, nodeInfo) || noted;
        } else if (element.localName === 'slot') {
          // For ShadyDom optimization, indicating there is an insertion point
          templateInfo.hasInsertionPoint = true;
        }

        fixPlaceholder(element);

        if (element.firstChild) {
          this._parseTemplateChildNodes(element, templateInfo, nodeInfo);
        }

        if (element.hasAttributes && element.hasAttributes()) {
          noted = this._parseTemplateNodeAttributes(element, templateInfo, nodeInfo) || noted;
        } // Checking `nodeInfo.noted` allows a child node of this node (who gets
        // access to `parentInfo`) to cause the parent to be noted, which
        // otherwise has no return path via `_parseTemplateChildNodes` (used by
        // some optimizations)


        return noted || nodeInfo.noted;
      }
      /**
       * Parses template child nodes for the given root node.
       *
       * This method also wraps whitelisted legacy template extensions
       * (`is="dom-if"` and `is="dom-repeat"`) with their equivalent element
       * wrappers, collapses text nodes, and strips whitespace from the template
       * if the `templateInfo.stripWhitespace` setting was provided.
       *
       * @param {Node} root Root node whose `childNodes` will be parsed
       * @param {!TemplateInfo} templateInfo Template metadata for current template
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @return {void}
       */


      static _parseTemplateChildNodes(root, templateInfo, nodeInfo) {
        if (root.localName === 'script' || root.localName === 'style') {
          return;
        }

        for (let node = root.firstChild, parentIndex = 0, next; node; node = next) {
          // Wrap templates
          if (node.localName == 'template') {
            node = wrapTemplateExtension(node);
          } // collapse adjacent textNodes: fixes an IE issue that can cause
          // text nodes to be inexplicably split =(
          // note that root.normalize() should work but does not so we do this
          // manually.


          next = node.nextSibling;

          if (node.nodeType === Node.TEXT_NODE) {
            let
            /** Node */
            n = next;

            while (n && n.nodeType === Node.TEXT_NODE) {
              node.textContent += n.textContent;
              next = n.nextSibling;
              root.removeChild(n);
              n = next;
            } // optionally strip whitespace


            if (templateInfo.stripWhiteSpace && !node.textContent.trim()) {
              root.removeChild(node);
              continue;
            }
          }

          let childInfo =
          /** @type {!NodeInfo} */
          {
            parentIndex,
            parentInfo: nodeInfo
          };

          if (this._parseTemplateNode(node, templateInfo, childInfo)) {
            childInfo.infoIndex = templateInfo.nodeInfoList.push(childInfo) - 1;
          } // Increment if not removed


          if (node.parentNode) {
            parentIndex++;
          }
        }
      }
      /**
       * Parses template content for the given nested `<template>`.
       *
       * Nested template info is stored as `templateInfo` in the current node's
       * `nodeInfo`. `template.content` is removed and stored in `templateInfo`.
       * It will then be the responsibility of the host to set it back to the
       * template and for users stamping nested templates to use the
       * `_contentForTemplate` method to retrieve the content for this template
       * (an optimization to avoid the cost of cloning nested template content).
       *
       * @param {HTMLTemplateElement} node Node to parse (a <template>)
       * @param {TemplateInfo} outerTemplateInfo Template metadata for current template
       *   that includes the template `node`
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @nocollapse
       */


      static _parseTemplateNestedTemplate(node, outerTemplateInfo, nodeInfo) {
        // TODO(rictic): the type of node should be non-null
        let element =
        /** @type {!HTMLTemplateElement} */
        node;

        let templateInfo = this._parseTemplate(element, outerTemplateInfo);

        let content = templateInfo.content = element.content.ownerDocument.createDocumentFragment();
        content.appendChild(element.content);
        nodeInfo.templateInfo = templateInfo;
        return true;
      }
      /**
       * Parses template node attributes and adds node metadata to `nodeInfo`
       * for nodes of interest.
       *
       * @param {Element} node Node to parse
       * @param {!TemplateInfo} templateInfo Template metadata for current
       *     template
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @nocollapse
       */


      static _parseTemplateNodeAttributes(node, templateInfo, nodeInfo) {
        // Make copy of original attribute list, since the order may change
        // as attributes are added and removed
        let noted = false;
        let attrs = Array.from(node.attributes);

        for (let i = attrs.length - 1, a; a = attrs[i]; i--) {
          noted = this._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, a.name, a.value) || noted;
        }

        return noted;
      }
      /**
       * Parses a single template node attribute and adds node metadata to
       * `nodeInfo` for attributes of interest.
       *
       * This implementation adds metadata for `on-event="handler"` attributes
       * and `id` attributes.
       *
       * @param {Element} node Node to parse
       * @param {!TemplateInfo} templateInfo Template metadata for current template
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @param {string} name Attribute name
       * @param {string} value Attribute value
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @nocollapse
       */


      static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) {
        // events (on-*)
        if (name.slice(0, 3) === 'on-') {
          node.removeAttribute(name);
          nodeInfo.events = nodeInfo.events || [];
          nodeInfo.events.push({
            name: name.slice(3),
            value
          });
          return true;
        } // static id
        else if (name === 'id') {
            nodeInfo.id = value;
            return true;
          }

        return false;
      }
      /**
       * Returns the `content` document fragment for a given template.
       *
       * For nested templates, Polymer performs an optimization to cache nested
       * template content to avoid the cost of cloning deeply nested templates.
       * This method retrieves the cached content for a given template.
       *
       * @param {HTMLTemplateElement} template Template to retrieve `content` for
       * @return {DocumentFragment} Content fragment
       * @nocollapse
       */


      static _contentForTemplate(template) {
        let templateInfo =
        /** @type {HTMLTemplateElementWithInfo} */
        template._templateInfo;
        return templateInfo && templateInfo.content || template.content;
      }
      /**
       * Clones the provided template content and returns a document fragment
       * containing the cloned dom.
       *
       * The template is parsed (once and memoized) using this library's
       * template parsing features, and provides the following value-added
       * features:
       * * Adds declarative event listeners for `on-event="handler"` attributes
       * * Generates an "id map" for all nodes with id's under `$` on returned
       *   document fragment
       * * Passes template info including `content` back to templates as
       *   `_templateInfo` (a performance optimization to avoid deep template
       *   cloning)
       *
       * Note that the memoized template parsing process is destructive to the
       * template: attributes for bindings and declarative event listeners are
       * removed after being noted in notes, and any nested `<template>.content`
       * is removed and stored in notes as well.
       *
       * @param {!HTMLTemplateElement} template Template to stamp
       * @param {TemplateInfo=} templateInfo Optional template info associated
       *   with the template to be stamped; if omitted the template will be
       *   automatically parsed.
       * @return {!StampedTemplate} Cloned template content
       * @override
       */


      _stampTemplate(template, templateInfo) {
        // Polyfill support: bootstrap the template if it has not already been
        if (template && !template.content && window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
          HTMLTemplateElement.decorate(template);
        } // Accepting the `templateInfo` via an argument allows for creating
        // instances of the `templateInfo` by the caller, useful for adding
        // instance-time information to the prototypical data


        templateInfo = templateInfo || this.constructor._parseTemplate(template);
        let nodeInfo = templateInfo.nodeInfoList;
        let content = templateInfo.content || template.content;
        let dom =
        /** @type {DocumentFragment} */
        document.importNode(content, true); // NOTE: ShadyDom optimization indicating there is an insertion point

        dom.__noInsertionPoint = !templateInfo.hasInsertionPoint;
        let nodes = dom.nodeList = new Array(nodeInfo.length);
        dom.$ = {};

        for (let i = 0, l = nodeInfo.length, info; i < l && (info = nodeInfo[i]); i++) {
          let node = nodes[i] = findTemplateNode(dom, info);
          applyIdToMap(this, dom.$, node, info);
          applyTemplateInfo(this, node, info, templateInfo);
          applyEventListener(this, node, info);
        }

        dom =
        /** @type {!StampedTemplate} */
        dom; // eslint-disable-line no-self-assign

        return dom;
      }
      /**
       * Adds an event listener by method name for the event provided.
       *
       * This method generates a handler function that looks up the method
       * name at handling time.
       *
       * @param {!EventTarget} node Node to add listener on
       * @param {string} eventName Name of event
       * @param {string} methodName Name of method
       * @param {*=} context Context the method will be called on (defaults
       *   to `node`)
       * @return {Function} Generated handler function
       * @override
       */


      _addMethodEventListenerToNode(node, eventName, methodName, context) {
        context = context || node;
        let handler = createNodeEventHandler(context, eventName, methodName);

        this._addEventListenerToNode(node, eventName, handler);

        return handler;
      }
      /**
       * Override point for adding custom or simulated event handling.
       *
       * @param {!EventTarget} node Node to add event listener to
       * @param {string} eventName Name of event
       * @param {function(!Event):void} handler Listener function to add
       * @return {void}
       * @override
       */


      _addEventListenerToNode(node, eventName, handler) {
        node.addEventListener(eventName, handler);
      }
      /**
       * Override point for adding custom or simulated event handling.
       *
       * @param {!EventTarget} node Node to remove event listener from
       * @param {string} eventName Name of event
       * @param {function(!Event):void} handler Listener function to remove
       * @return {void}
       * @override
       */


      _removeEventListenerFromNode(node, eventName, handler) {
        node.removeEventListener(eventName, handler);
      }

    }

    return TemplateStamp;
  });

  /**
   * @fileoverview
   * @suppress {checkPrototypalTypes}
   * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
   * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
   * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
   * Google as part of the polymer project is also subject to an additional IP
   * rights grant found at http://polymer.github.io/PATENTS.txt
   */
  // from multiple properties in the same turn

  let dedupeId$1 = 0;
  const NOOP = [];
  /**
   * Property effect types; effects are stored on the prototype using these keys
   * @enum {string}
   */

  const TYPES = {
    COMPUTE: '__computeEffects',
    REFLECT: '__reflectEffects',
    NOTIFY: '__notifyEffects',
    PROPAGATE: '__propagateEffects',
    OBSERVE: '__observeEffects',
    READ_ONLY: '__readOnly'
  };
  const COMPUTE_INFO = '__computeInfo';
  /** @const {!RegExp} */

  const capitalAttributeRegex = /[A-Z]/;

  /**
   * Ensures that the model has an own-property map of effects for the given type.
   * The model may be a prototype or an instance.
   *
   * Property effects are stored as arrays of effects by property in a map,
   * by named type on the model. e.g.
   *
   *   __computeEffects: {
   *     foo: [ ... ],
   *     bar: [ ... ]
   *   }
   *
   * If the model does not yet have an effect map for the type, one is created
   * and returned.  If it does, but it is not an own property (i.e. the
   * prototype had effects), the the map is deeply cloned and the copy is
   * set on the model and returned, ready for new effects to be added.
   *
   * @param {Object} model Prototype or instance
   * @param {string} type Property effect type
   * @param {boolean=} cloneArrays Clone any arrays assigned to the map when
   *   extending a superclass map onto this subclass
   * @return {Object} The own-property map of effects for the given type
   * @private
   */

  function ensureOwnEffectMap(model, type, cloneArrays) {
    let effects = model[type];

    if (!effects) {
      effects = model[type] = {};
    } else if (!model.hasOwnProperty(type)) {
      effects = model[type] = Object.create(model[type]);

      if (cloneArrays) {
        for (let p in effects) {
          let protoFx = effects[p]; // Perf optimization over Array.slice

          let instFx = effects[p] = Array(protoFx.length);

          for (let i = 0; i < protoFx.length; i++) {
            instFx[i] = protoFx[i];
          }
        }
      }
    }

    return effects;
  } // -- effects ----------------------------------------------

  /**
   * Runs all effects of a given type for the given set of property changes
   * on an instance.
   *
   * @param {!Polymer_PropertyEffects} inst The instance with effects to run
   * @param {?Object} effects Object map of property-to-Array of effects
   * @param {?Object} props Bag of current property changes
   * @param {?Object=} oldProps Bag of previous values for changed properties
   * @param {boolean=} hasPaths True with `props` contains one or more paths
   * @param {*=} extraArgs Additional metadata to pass to effect function
   * @return {boolean} True if an effect ran for this property
   * @private
   */


  function runEffects(inst, effects, props, oldProps, hasPaths, extraArgs) {
    if (effects) {
      let ran = false;
      const id = dedupeId$1++;

      for (let prop in props) {
        // Inline `runEffectsForProperty` for perf.
        let rootProperty = hasPaths ? root(prop) : prop;
        let fxs = effects[rootProperty];

        if (fxs) {
          for (let i = 0, l = fxs.length, fx; i < l && (fx = fxs[i]); i++) {
            if ((!fx.info || fx.info.lastRun !== id) && (!hasPaths || pathMatchesTrigger(prop, fx.trigger))) {
              if (fx.info) {
                fx.info.lastRun = id;
              }

              fx.fn(inst, prop, props, oldProps, fx.info, hasPaths, extraArgs);
              ran = true;
            }
          }
        }
      }

      return ran;
    }

    return false;
  }
  /**
   * Runs a list of effects for a given property.
   *
   * @param {!Polymer_PropertyEffects} inst The instance with effects to run
   * @param {!Object} effects Object map of property-to-Array of effects
   * @param {number} dedupeId Counter used for de-duping effects
   * @param {string} prop Name of changed property
   * @param {*} props Changed properties
   * @param {*} oldProps Old properties
   * @param {boolean=} hasPaths True with `props` contains one or more paths
   * @param {*=} extraArgs Additional metadata to pass to effect function
   * @return {boolean} True if an effect ran for this property
   * @private
   */


  function runEffectsForProperty(inst, effects, dedupeId, prop, props, oldProps, hasPaths, extraArgs) {
    let ran = false;
    let rootProperty = hasPaths ? root(prop) : prop;
    let fxs = effects[rootProperty];

    if (fxs) {
      for (let i = 0, l = fxs.length, fx; i < l && (fx = fxs[i]); i++) {
        if ((!fx.info || fx.info.lastRun !== dedupeId) && (!hasPaths || pathMatchesTrigger(prop, fx.trigger))) {
          if (fx.info) {
            fx.info.lastRun = dedupeId;
          }

          fx.fn(inst, prop, props, oldProps, fx.info, hasPaths, extraArgs);
          ran = true;
        }
      }
    }

    return ran;
  }
  /**
   * Determines whether a property/path that has changed matches the trigger
   * criteria for an effect.  A trigger is a descriptor with the following
   * structure, which matches the descriptors returned from `parseArg`.
   * e.g. for `foo.bar.*`:
   * ```
   * trigger: {
   *   name: 'a.b',
   *   structured: true,
   *   wildcard: true
   * }
   * ```
   * If no trigger is given, the path is deemed to match.
   *
   * @param {string} path Path or property that changed
   * @param {?DataTrigger} trigger Descriptor
   * @return {boolean} Whether the path matched the trigger
   */


  function pathMatchesTrigger(path, trigger) {
    if (trigger) {
      let triggerPath =
      /** @type {string} */
      trigger.name;
      return triggerPath == path || !!(trigger.structured && isAncestor(triggerPath, path)) || !!(trigger.wildcard && isDescendant(triggerPath, path));
    } else {
      return true;
    }
  }
  /**
   * Implements the "observer" effect.
   *
   * Calls the method with `info.methodName` on the instance, passing the
   * new and old values.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} property Name of property
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @return {void}
   * @private
   */


  function runObserverEffect(inst, property, props, oldProps, info) {
    let fn = typeof info.method === "string" ? inst[info.method] : info.method;
    let changedProp = info.property;

    if (fn) {
      fn.call(inst, inst.__data[changedProp], oldProps[changedProp]);
    } else if (!info.dynamicFn) {
      console.warn('observer method `' + info.method + '` not defined');
    }
  }
  /**
   * Runs "notify" effects for a set of changed properties.
   *
   * This method differs from the generic `runEffects` method in that it
   * will dispatch path notification events in the case that the property
   * changed was a path and the root property for that path didn't have a
   * "notify" effect.  This is to maintain 1.0 behavior that did not require
   * `notify: true` to ensure object sub-property notifications were
   * sent.
   *
   * @param {!Polymer_PropertyEffects} inst The instance with effects to run
   * @param {Object} notifyProps Bag of properties to notify
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {boolean} hasPaths True with `props` contains one or more paths
   * @return {void}
   * @private
   */


  function runNotifyEffects(inst, notifyProps, props, oldProps, hasPaths) {
    // Notify
    let fxs = inst[TYPES.NOTIFY];
    let notified;
    let id = dedupeId$1++; // Try normal notify effects; if none, fall back to try path notification

    for (let prop in notifyProps) {
      if (notifyProps[prop]) {
        if (fxs && runEffectsForProperty(inst, fxs, id, prop, props, oldProps, hasPaths)) {
          notified = true;
        } else if (hasPaths && notifyPath(inst, prop, props)) {
          notified = true;
        }
      }
    } // Flush host if we actually notified and host was batching
    // And the host has already initialized clients; this prevents
    // an issue with a host observing data changes before clients are ready.


    let host;

    if (notified && (host = inst.__dataHost) && host._invalidateProperties) {
      host._invalidateProperties();
    }
  }
  /**
   * Dispatches {property}-changed events with path information in the detail
   * object to indicate a sub-path of the property was changed.
   *
   * @param {!Polymer_PropertyEffects} inst The element from which to fire the
   *     event
   * @param {string} path The path that was changed
   * @param {Object} props Bag of current property changes
   * @return {boolean} Returns true if the path was notified
   * @private
   */


  function notifyPath(inst, path, props) {
    let rootProperty = root(path);

    if (rootProperty !== path) {
      let eventName = camelToDashCase(rootProperty) + '-changed';
      dispatchNotifyEvent(inst, eventName, props[path], path);
      return true;
    }

    return false;
  }
  /**
   * Dispatches {property}-changed events to indicate a property (or path)
   * changed.
   *
   * @param {!Polymer_PropertyEffects} inst The element from which to fire the
   *     event
   * @param {string} eventName The name of the event to send
   *     ('{property}-changed')
   * @param {*} value The value of the changed property
   * @param {string | null | undefined} path If a sub-path of this property
   *     changed, the path that changed (optional).
   * @return {void}
   * @private
   * @suppress {invalidCasts}
   */


  function dispatchNotifyEvent(inst, eventName, value, path) {
    let detail = {
      value: value,
      queueProperty: true
    };

    if (path) {
      detail.path = path;
    } // As a performance optimization, we could elide the wrap here since notifying
    // events are non-bubbling and shouldn't need retargeting. However, a very
    // small number of internal tests failed in obscure ways, which may indicate
    // user code relied on timing differences resulting from ShadyDOM flushing
    // as a result of the wrapped `dispatchEvent`.


    wrap$1(
    /** @type {!HTMLElement} */
    inst).dispatchEvent(new CustomEvent(eventName, {
      detail
    }));
  }
  /**
   * Implements the "notify" effect.
   *
   * Dispatches a non-bubbling event named `info.eventName` on the instance
   * with a detail object containing the new `value`.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} property Name of property
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @param {boolean} hasPaths True with `props` contains one or more paths
   * @return {void}
   * @private
   */


  function runNotifyEffect(inst, property, props, oldProps, info, hasPaths) {
    let rootProperty = hasPaths ? root(property) : property;
    let path = rootProperty != property ? property : null;
    let value = path ? get(inst, path) : inst.__data[property];

    if (path && value === undefined) {
      value = props[property]; // specifically for .splices
    }

    dispatchNotifyEvent(inst, info.eventName, value, path);
  }
  /**
   * Handler function for 2-way notification events. Receives context
   * information captured in the `addNotifyListener` closure from the
   * `__notifyListeners` metadata.
   *
   * Sets the value of the notified property to the host property or path.  If
   * the event contained path information, translate that path to the host
   * scope's name for that path first.
   *
   * @param {CustomEvent} event Notification event (e.g. '<property>-changed')
   * @param {!Polymer_PropertyEffects} inst Host element instance handling the
   *     notification event
   * @param {string} fromProp Child element property that was bound
   * @param {string} toPath Host property/path that was bound
   * @param {boolean} negate Whether the binding was negated
   * @return {void}
   * @private
   */


  function handleNotification(event, inst, fromProp, toPath, negate) {
    let value;
    let detail =
    /** @type {Object} */
    event.detail;
    let fromPath = detail && detail.path;

    if (fromPath) {
      toPath = translate(fromProp, toPath, fromPath);
      value = detail && detail.value;
    } else {
      value = event.currentTarget[fromProp];
    }

    value = negate ? !value : value;

    if (!inst[TYPES.READ_ONLY] || !inst[TYPES.READ_ONLY][toPath]) {
      if (inst._setPendingPropertyOrPath(toPath, value, true, Boolean(fromPath)) && (!detail || !detail.queueProperty)) {
        inst._invalidateProperties();
      }
    }
  }
  /**
   * Implements the "reflect" effect.
   *
   * Sets the attribute named `info.attrName` to the given property value.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} property Name of property
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @return {void}
   * @private
   */


  function runReflectEffect(inst, property, props, oldProps, info) {
    let value = inst.__data[property];

    if (sanitizeDOMValue) {
      value = sanitizeDOMValue(value, info.attrName, 'attribute',
      /** @type {Node} */
      inst);
    }

    inst._propertyToAttribute(property, info.attrName, value);
  }
  /**
   * Runs "computed" effects for a set of changed properties.
   *
   * This method differs from the generic `runEffects` method in that it
   * continues to run computed effects based on the output of each pass until
   * there are no more newly computed properties.  This ensures that all
   * properties that will be computed by the initial set of changes are
   * computed before other effects (binding propagation, observers, and notify)
   * run.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {?Object} changedProps Bag of changed properties
   * @param {?Object} oldProps Bag of previous values for changed properties
   * @param {boolean} hasPaths True with `props` contains one or more paths
   * @return {void}
   * @private
   */


  function runComputedEffects(inst, changedProps, oldProps, hasPaths) {
    let computeEffects = inst[TYPES.COMPUTE];

    if (computeEffects) {
      if (orderedComputed) {
        // Runs computed effects in efficient order by keeping a topologically-
        // sorted queue of compute effects to run, and inserting subsequently
        // invalidated effects as they are run
        dedupeId$1++;
        const order = getComputedOrder(inst);
        const queue = [];

        for (let p in changedProps) {
          enqueueEffectsFor(p, computeEffects, queue, order, hasPaths);
        }

        let info;

        while (info = queue.shift()) {
          if (runComputedEffect(inst, '', changedProps, oldProps, info)) {
            enqueueEffectsFor(info.methodInfo, computeEffects, queue, order, hasPaths);
          }
        }

        Object.assign(
        /** @type {!Object} */
        oldProps, inst.__dataOld);
        Object.assign(
        /** @type {!Object} */
        changedProps, inst.__dataPending);
        inst.__dataPending = null;
      } else {
        // Original Polymer 2.x computed effects order, which continues running
        // effects until no further computed properties have been invalidated
        let inputProps = changedProps;

        while (runEffects(inst, computeEffects, inputProps, oldProps, hasPaths)) {
          Object.assign(
          /** @type {!Object} */
          oldProps, inst.__dataOld);
          Object.assign(
          /** @type {!Object} */
          changedProps, inst.__dataPending);
          inputProps = inst.__dataPending;
          inst.__dataPending = null;
        }
      }
    }
  }
  /**
   * Inserts a computed effect into a queue, given the specified order. Performs
   * the insert using a binary search.
   *
   * Used by `orderedComputed: true` computed property algorithm.
   *
   * @param {Object} info Property effects metadata
   * @param {Array<Object>} queue Ordered queue of effects
   * @param {Map<string,number>} order Map of computed property name->topological
   *   sort order
   */


  const insertEffect = (info, queue, order) => {
    let start = 0;
    let end = queue.length - 1;
    let idx = -1;

    while (start <= end) {
      const mid = start + end >> 1; // Note `methodInfo` is where the computed property name is stored in
      // the effect metadata

      const cmp = order.get(queue[mid].methodInfo) - order.get(info.methodInfo);

      if (cmp < 0) {
        start = mid + 1;
      } else if (cmp > 0) {
        end = mid - 1;
      } else {
        idx = mid;
        break;
      }
    }

    if (idx < 0) {
      idx = end + 1;
    }

    queue.splice(idx, 0, info);
  };
  /**
   * Inserts all downstream computed effects invalidated by the specified property
   * into the topologically-sorted queue of effects to be run.
   *
   * Used by `orderedComputed: true` computed property algorithm.
   *
   * @param {string} prop Property name
   * @param {Object} computeEffects Computed effects for this element
   * @param {Array<Object>} queue Topologically-sorted queue of computed effects
   *   to be run
   * @param {Map<string,number>} order Map of computed property name->topological
   *   sort order
   * @param {boolean} hasPaths True with `changedProps` contains one or more paths
   */


  const enqueueEffectsFor = (prop, computeEffects, queue, order, hasPaths) => {
    const rootProperty = hasPaths ? root(prop) : prop;
    const fxs = computeEffects[rootProperty];

    if (fxs) {
      for (let i = 0; i < fxs.length; i++) {
        const fx = fxs[i];

        if (fx.info.lastRun !== dedupeId$1 && (!hasPaths || pathMatchesTrigger(prop, fx.trigger))) {
          fx.info.lastRun = dedupeId$1;
          insertEffect(fx.info, queue, order);
        }
      }
    }
  };
  /**
   * Generates and retrieves a memoized map of computed property name to its
   * topologically-sorted order.
   *
   * The map is generated by first assigning a "dependency count" to each property
   * (defined as number properties it depends on, including its method for
   * "dynamic functions"). Any properties that have no dependencies are added to
   * the `ready` queue, which are properties whose order can be added to the final
   * order map. Properties are popped off the `ready` queue one by one and a.) added as
   * the next property in the order map, and b.) each property that it is a
   * dependency for has its dep count decremented (and if that property's dep
   * count goes to zero, it is added to the `ready` queue), until all properties
   * have been visited and ordered.
   *
   * Used by `orderedComputed: true` computed property algorithm.
   *
   * @param {!Polymer_PropertyEffects} inst The instance to retrieve the computed
   *   effect order for.
   * @return {Map<string,number>} Map of computed property name->topological sort
   *   order
   */


  function getComputedOrder(inst) {
    let ordered = inst.constructor.__orderedComputedDeps;

    if (!ordered) {
      ordered = new Map();
      const effects = inst[TYPES.COMPUTE];
      let {
        counts,
        ready,
        total
      } = dependencyCounts(inst);
      let curr;

      while (curr = ready.shift()) {
        ordered.set(curr, ordered.size);
        const computedByCurr = effects[curr];

        if (computedByCurr) {
          computedByCurr.forEach(fx => {
            // Note `methodInfo` is where the computed property name is stored
            const computedProp = fx.info.methodInfo;
            --total;

            if (--counts[computedProp] === 0) {
              ready.push(computedProp);
            }
          });
        }
      }

      if (total !== 0) {
        const el =
        /** @type {HTMLElement} */
        inst;
        console.warn(`Computed graph for ${el.localName} incomplete; circular?`);
      }

      inst.constructor.__orderedComputedDeps = ordered;
    }

    return ordered;
  }
  /**
   * Generates a map of property-to-dependency count (`counts`, where "dependency
   * count" is the number of dependencies a given property has assuming it is a
   * computed property, otherwise 0).  It also returns a pre-populated list of
   * `ready` properties that have no dependencies and a `total` count, which is
   * used for error-checking the graph.
   *
   * Used by `orderedComputed: true` computed property algorithm.
   *
   * @param {!Polymer_PropertyEffects} inst The instance to generate dependency
   *   counts for.
   * @return {!Object} Object containing `counts` map (property-to-dependency
   *   count) and pre-populated `ready` array of properties that had zero
   *   dependencies.
   */


  function dependencyCounts(inst) {
    const infoForComputed = inst[COMPUTE_INFO];
    const counts = {};
    const computedDeps = inst[TYPES.COMPUTE];
    const ready = [];
    let total = 0; // Count dependencies for each computed property

    for (let p in infoForComputed) {
      const info = infoForComputed[p]; // Be sure to add the method name itself in case of "dynamic functions"

      total += counts[p] = info.args.filter(a => !a.literal).length + (info.dynamicFn ? 1 : 0);
    } // Build list of ready properties (that aren't themselves computed)


    for (let p in computedDeps) {
      if (!infoForComputed[p]) {
        ready.push(p);
      }
    }

    return {
      counts,
      ready,
      total
    };
  }
  /**
   * Implements the "computed property" effect by running the method with the
   * values of the arguments specified in the `info` object and setting the
   * return value to the computed property specified.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} property Name of property
   * @param {?Object} changedProps Bag of current property changes
   * @param {?Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @return {boolean} True when the property being computed changed
   * @private
   */


  function runComputedEffect(inst, property, changedProps, oldProps, info) {
    // Dirty check dependencies and run if any invalid
    let result = runMethodEffect(inst, property, changedProps, oldProps, info); // Abort if method returns a no-op result

    if (result === NOOP) {
      return false;
    }

    let computedProp = info.methodInfo;

    if (inst.__dataHasAccessor && inst.__dataHasAccessor[computedProp]) {
      return inst._setPendingProperty(computedProp, result, true);
    } else {
      inst[computedProp] = result;
      return false;
    }
  }
  /**
   * Computes path changes based on path links set up using the `linkPaths`
   * API.
   *
   * @param {!Polymer_PropertyEffects} inst The instance whose props are changing
   * @param {string} path Path that has changed
   * @param {*} value Value of changed path
   * @return {void}
   * @private
   */


  function computeLinkedPaths(inst, path, value) {
    let links = inst.__dataLinkedPaths;

    if (links) {
      let link;

      for (let a in links) {
        let b = links[a];

        if (isDescendant(a, path)) {
          link = translate(a, b, path);

          inst._setPendingPropertyOrPath(link, value, true, true);
        } else if (isDescendant(b, path)) {
          link = translate(b, a, path);

          inst._setPendingPropertyOrPath(link, value, true, true);
        }
      }
    }
  } // -- bindings ----------------------------------------------

  /**
   * Adds binding metadata to the current `nodeInfo`, and binding effects
   * for all part dependencies to `templateInfo`.
   *
   * @param {Function} constructor Class that `_parseTemplate` is currently
   *   running on
   * @param {TemplateInfo} templateInfo Template metadata for current template
   * @param {NodeInfo} nodeInfo Node metadata for current template node
   * @param {string} kind Binding kind, either 'property', 'attribute', or 'text'
   * @param {string} target Target property name
   * @param {!Array<!BindingPart>} parts Array of binding part metadata
   * @param {string=} literal Literal text surrounding binding parts (specified
   *   only for 'property' bindings, since these must be initialized as part
   *   of boot-up)
   * @return {void}
   * @private
   */


  function addBinding(constructor, templateInfo, nodeInfo, kind, target, parts, literal) {
    // Create binding metadata and add to nodeInfo
    nodeInfo.bindings = nodeInfo.bindings || [];
    let
    /** Binding */
    binding = {
      kind,
      target,
      parts,
      literal,
      isCompound: parts.length !== 1
    };
    nodeInfo.bindings.push(binding); // Add listener info to binding metadata

    if (shouldAddListener(binding)) {
      let {
        event,
        negate
      } = binding.parts[0];
      binding.listenerEvent = event || camelToDashCase(target) + '-changed';
      binding.listenerNegate = negate;
    } // Add "propagate" property effects to templateInfo


    let index = templateInfo.nodeInfoList.length;

    for (let i = 0; i < binding.parts.length; i++) {
      let part = binding.parts[i];
      part.compoundIndex = i;
      addEffectForBindingPart(constructor, templateInfo, binding, part, index);
    }
  }
  /**
   * Adds property effects to the given `templateInfo` for the given binding
   * part.
   *
   * @param {Function} constructor Class that `_parseTemplate` is currently
   *   running on
   * @param {TemplateInfo} templateInfo Template metadata for current template
   * @param {!Binding} binding Binding metadata
   * @param {!BindingPart} part Binding part metadata
   * @param {number} index Index into `nodeInfoList` for this node
   * @return {void}
   */


  function addEffectForBindingPart(constructor, templateInfo, binding, part, index) {
    if (!part.literal) {
      if (binding.kind === 'attribute' && binding.target[0] === '-') {
        console.warn('Cannot set attribute ' + binding.target + ' because "-" is not a valid attribute starting character');
      } else {
        let dependencies = part.dependencies;
        let info = {
          index,
          binding,
          part,
          evaluator: constructor
        };

        for (let j = 0; j < dependencies.length; j++) {
          let trigger = dependencies[j];

          if (typeof trigger == 'string') {
            trigger = parseArg(trigger);
            trigger.wildcard = true;
          }

          constructor._addTemplatePropertyEffect(templateInfo, trigger.rootProperty, {
            fn: runBindingEffect,
            info,
            trigger
          });
        }
      }
    }
  }
  /**
   * Implements the "binding" (property/path binding) effect.
   *
   * Note that binding syntax is overridable via `_parseBindings` and
   * `_evaluateBinding`.  This method will call `_evaluateBinding` for any
   * non-literal parts returned from `_parseBindings`.  However,
   * there is no support for _path_ bindings via custom binding parts,
   * as this is specific to Polymer's path binding syntax.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} path Name of property
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @param {boolean} hasPaths True with `props` contains one or more paths
   * @param {Array} nodeList List of nodes associated with `nodeInfoList` template
   *   metadata
   * @return {void}
   * @private
   */


  function runBindingEffect(inst, path, props, oldProps, info, hasPaths, nodeList) {
    let node = nodeList[info.index];
    let binding = info.binding;
    let part = info.part; // Subpath notification: transform path and set to client
    // e.g.: foo="{{obj.sub}}", path: 'obj.sub.prop', set 'foo.prop'=obj.sub.prop

    if (hasPaths && part.source && path.length > part.source.length && binding.kind == 'property' && !binding.isCompound && node.__isPropertyEffectsClient && node.__dataHasAccessor && node.__dataHasAccessor[binding.target]) {
      let value = props[path];
      path = translate(part.source, binding.target, path);

      if (node._setPendingPropertyOrPath(path, value, false, true)) {
        inst._enqueueClient(node);
      }
    } else {
      let value = info.evaluator._evaluateBinding(inst, part, path, props, oldProps, hasPaths); // Propagate value to child
      // Abort if value is a no-op result


      if (value !== NOOP) {
        applyBindingValue(inst, node, binding, part, value);
      }
    }
  }
  /**
   * Sets the value for an "binding" (binding) effect to a node,
   * either as a property or attribute.
   *
   * @param {!Polymer_PropertyEffects} inst The instance owning the binding effect
   * @param {Node} node Target node for binding
   * @param {!Binding} binding Binding metadata
   * @param {!BindingPart} part Binding part metadata
   * @param {*} value Value to set
   * @return {void}
   * @private
   */


  function applyBindingValue(inst, node, binding, part, value) {
    value = computeBindingValue(node, value, binding, part);

    if (sanitizeDOMValue) {
      value = sanitizeDOMValue(value, binding.target, binding.kind, node);
    }

    if (binding.kind == 'attribute') {
      // Attribute binding
      inst._valueToNodeAttribute(
      /** @type {Element} */
      node, value, binding.target);
    } else {
      // Property binding
      let prop = binding.target;

      if (node.__isPropertyEffectsClient && node.__dataHasAccessor && node.__dataHasAccessor[prop]) {
        if (!node[TYPES.READ_ONLY] || !node[TYPES.READ_ONLY][prop]) {
          if (node._setPendingProperty(prop, value)) {
            inst._enqueueClient(node);
          }
        }
      } else {
        // In legacy no-batching mode, bindings applied before dataReady are
        // equivalent to the "apply config" phase, which only set managed props
        inst._setUnmanagedPropertyToNode(node, prop, value);
      }
    }
  }
  /**
   * Transforms an "binding" effect value based on compound & negation
   * effect metadata, as well as handling for special-case properties
   *
   * @param {Node} node Node the value will be set to
   * @param {*} value Value to set
   * @param {!Binding} binding Binding metadata
   * @param {!BindingPart} part Binding part metadata
   * @return {*} Transformed value to set
   * @private
   */


  function computeBindingValue(node, value, binding, part) {
    if (binding.isCompound) {
      let storage = node.__dataCompoundStorage[binding.target];
      storage[part.compoundIndex] = value;
      value = storage.join('');
    }

    if (binding.kind !== 'attribute') {
      // Some browsers serialize `undefined` to `"undefined"`
      if (binding.target === 'textContent' || binding.target === 'value' && (node.localName === 'input' || node.localName === 'textarea')) {
        value = value == undefined ? '' : value;
      }
    }

    return value;
  }
  /**
   * Returns true if a binding's metadata meets all the requirements to allow
   * 2-way binding, and therefore a `<property>-changed` event listener should be
   * added:
   * - used curly braces
   * - is a property (not attribute) binding
   * - is not a textContent binding
   * - is not compound
   *
   * @param {!Binding} binding Binding metadata
   * @return {boolean} True if 2-way listener should be added
   * @private
   */


  function shouldAddListener(binding) {
    return Boolean(binding.target) && binding.kind != 'attribute' && binding.kind != 'text' && !binding.isCompound && binding.parts[0].mode === '{';
  }
  /**
   * Setup compound binding storage structures, notify listeners, and dataHost
   * references onto the bound nodeList.
   *
   * @param {!Polymer_PropertyEffects} inst Instance that bas been previously
   *     bound
   * @param {TemplateInfo} templateInfo Template metadata
   * @return {void}
   * @private
   */


  function setupBindings(inst, templateInfo) {
    // Setup compound storage, dataHost, and notify listeners
    let {
      nodeList,
      nodeInfoList
    } = templateInfo;

    if (nodeInfoList.length) {
      for (let i = 0; i < nodeInfoList.length; i++) {
        let info = nodeInfoList[i];
        let node = nodeList[i];
        let bindings = info.bindings;

        if (bindings) {
          for (let i = 0; i < bindings.length; i++) {
            let binding = bindings[i];
            setupCompoundStorage(node, binding);
            addNotifyListener(node, inst, binding);
          }
        } // This ensures all bound elements have a host set, regardless
        // of whether they upgrade synchronous to creation


        node.__dataHost = inst;
      }
    }
  }
  /**
   * Initializes `__dataCompoundStorage` local storage on a bound node with
   * initial literal data for compound bindings, and sets the joined
   * literal parts to the bound property.
   *
   * When changes to compound parts occur, they are first set into the compound
   * storage array for that property, and then the array is joined to result in
   * the final value set to the property/attribute.
   *
   * @param {Node} node Bound node to initialize
   * @param {Binding} binding Binding metadata
   * @return {void}
   * @private
   */


  function setupCompoundStorage(node, binding) {
    if (binding.isCompound) {
      // Create compound storage map
      let storage = node.__dataCompoundStorage || (node.__dataCompoundStorage = {});
      let parts = binding.parts; // Copy literals from parts into storage for this binding

      let literals = new Array(parts.length);

      for (let j = 0; j < parts.length; j++) {
        literals[j] = parts[j].literal;
      }

      let target = binding.target;
      storage[target] = literals; // Configure properties with their literal parts

      if (binding.literal && binding.kind == 'property') {
        // Note, className needs style scoping so this needs wrapping.
        // We may also want to consider doing this for `textContent` and
        // `innerHTML`.
        if (target === 'className') {
          node = wrap$1(node);
        }

        node[target] = binding.literal;
      }
    }
  }
  /**
   * Adds a 2-way binding notification event listener to the node specified
   *
   * @param {Object} node Child element to add listener to
   * @param {!Polymer_PropertyEffects} inst Host element instance to handle
   *     notification event
   * @param {Binding} binding Binding metadata
   * @return {void}
   * @private
   */


  function addNotifyListener(node, inst, binding) {
    if (binding.listenerEvent) {
      let part = binding.parts[0];
      node.addEventListener(binding.listenerEvent, function (e) {
        handleNotification(e, inst, binding.target, part.source, part.negate);
      });
    }
  } // -- for method-based effects (complexObserver & computed) --------------

  /**
   * Adds property effects for each argument in the method signature (and
   * optionally, for the method name if `dynamic` is true) that calls the
   * provided effect function.
   *
   * @param {Element | Object} model Prototype or instance
   * @param {!MethodSignature} sig Method signature metadata
   * @param {string} type Type of property effect to add
   * @param {Function} effectFn Function to run when arguments change
   * @param {*=} methodInfo Effect-specific information to be included in
   *   method effect metadata
   * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether
   *   method names should be included as a dependency to the effect. Note,
   *   defaults to true if the signature is static (sig.static is true).
   * @return {!Object} Effect metadata for this method effect
   * @private
   */


  function createMethodEffect(model, sig, type, effectFn, methodInfo, dynamicFn) {
    dynamicFn = sig.static || dynamicFn && (typeof dynamicFn !== 'object' || dynamicFn[sig.methodName]);
    let info = {
      methodName: sig.methodName,
      args: sig.args,
      methodInfo,
      dynamicFn
    };

    for (let i = 0, arg; i < sig.args.length && (arg = sig.args[i]); i++) {
      if (!arg.literal) {
        model._addPropertyEffect(arg.rootProperty, type, {
          fn: effectFn,
          info: info,
          trigger: arg
        });
      }
    }

    if (dynamicFn) {
      model._addPropertyEffect(sig.methodName, type, {
        fn: effectFn,
        info: info
      });
    }

    return info;
  }
  /**
   * Calls a method with arguments marshaled from properties on the instance
   * based on the method signature contained in the effect metadata.
   *
   * Multi-property observers, computed properties, and inline computing
   * functions call this function to invoke the method, then use the return
   * value accordingly.
   *
   * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on
   * @param {string} property Name of property
   * @param {Object} props Bag of current property changes
   * @param {Object} oldProps Bag of previous values for changed properties
   * @param {?} info Effect metadata
   * @return {*} Returns the return value from the method invocation
   * @private
   */


  function runMethodEffect(inst, property, props, oldProps, info) {
    // Instances can optionally have a _methodHost which allows redirecting where
    // to find methods. Currently used by `templatize`.
    let context = inst._methodHost || inst;
    let fn = context[info.methodName];

    if (fn) {
      let args = inst._marshalArgs(info.args, property, props);

      return args === NOOP ? NOOP : fn.apply(context, args);
    } else if (!info.dynamicFn) {
      console.warn('method `' + info.methodName + '` not defined');
    }
  }

  const emptyArray = []; // Regular expressions used for binding

  const IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')';
  const NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')';
  const SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')';
  const DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')';
  const STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')';
  const ARGUMENT = '(?:(' + IDENT + '|' + NUMBER + '|' + STRING + ')\\s*' + ')';
  const ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')';
  const ARGUMENT_LIST = '(?:' + '\\(\\s*' + '(?:' + ARGUMENTS + '?' + ')' + '\\)\\s*' + ')';
  const BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3

  const OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
  const CLOSE_BRACKET = '(?:]]|}})';
  const NEGATE = '(?:(!)\\s*)?'; // Group 2

  const EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
  const bindingRegex = new RegExp(EXPRESSION, "g");
  /**
   * Create a string from binding parts of all the literal parts
   *
   * @param {!Array<BindingPart>} parts All parts to stringify
   * @return {string} String made from the literal parts
   */

  function literalFromParts(parts) {
    let s = '';

    for (let i = 0; i < parts.length; i++) {
      let literal = parts[i].literal;
      s += literal || '';
    }

    return s;
  }
  /**
   * Parses an expression string for a method signature, and returns a metadata
   * describing the method in terms of `methodName`, `static` (whether all the
   * arguments are literals), and an array of `args`
   *
   * @param {string} expression The expression to parse
   * @return {?MethodSignature} The method metadata object if a method expression was
   *   found, otherwise `undefined`
   * @private
   */


  function parseMethod(expression) {
    // tries to match valid javascript property names
    let m = expression.match(/([^\s]+?)\(([\s\S]*)\)/);

    if (m) {
      let methodName = m[1];
      let sig = {
        methodName,
        static: true,
        args: emptyArray
      };

      if (m[2].trim()) {
        // replace escaped commas with comma entity, split on un-escaped commas
        let args = m[2].replace(/\\,/g, '&comma;').split(',');
        return parseArgs(args, sig);
      } else {
        return sig;
      }
    }

    return null;
  }
  /**
   * Parses an array of arguments and sets the `args` property of the supplied
   * signature metadata object. Sets the `static` property to false if any
   * argument is a non-literal.
   *
   * @param {!Array<string>} argList Array of argument names
   * @param {!MethodSignature} sig Method signature metadata object
   * @return {!MethodSignature} The updated signature metadata object
   * @private
   */


  function parseArgs(argList, sig) {
    sig.args = argList.map(function (rawArg) {
      let arg = parseArg(rawArg);

      if (!arg.literal) {
        sig.static = false;
      }

      return arg;
    }, this);
    return sig;
  }
  /**
   * Parses an individual argument, and returns an argument metadata object
   * with the following fields:
   *
   *   {
   *     value: 'prop',        // property/path or literal value
   *     literal: false,       // whether argument is a literal
   *     structured: false,    // whether the property is a path
   *     rootProperty: 'prop', // the root property of the path
   *     wildcard: false       // whether the argument was a wildcard '.*' path
   *   }
   *
   * @param {string} rawArg The string value of the argument
   * @return {!MethodArg} Argument metadata object
   * @private
   */


  function parseArg(rawArg) {
    // clean up whitespace
    let arg = rawArg.trim() // replace comma entity with comma
    .replace(/&comma;/g, ',') // repair extra escape sequences; note only commas strictly need
    // escaping, but we allow any other char to be escaped since its
    // likely users will do this
    .replace(/\\(.)/g, '\$1'); // basic argument descriptor

    let a = {
      name: arg,
      value: '',
      literal: false
    }; // detect literal value (must be String or Number)

    let fc = arg[0];

    if (fc === '-') {
      fc = arg[1];
    }

    if (fc >= '0' && fc <= '9') {
      fc = '#';
    }

    switch (fc) {
      case "'":
      case '"':
        a.value = arg.slice(1, -1);
        a.literal = true;
        break;

      case '#':
        a.value = Number(arg);
        a.literal = true;
        break;
    } // if not literal, look for structured path


    if (!a.literal) {
      a.rootProperty = root(arg); // detect structured path (has dots)

      a.structured = isPath(arg);

      if (a.structured) {
        a.wildcard = arg.slice(-2) == '.*';

        if (a.wildcard) {
          a.name = arg.slice(0, -2);
        }
      }
    }

    return a;
  }

  function getArgValue(data, props, path) {
    let value = get(data, path); // when data is not stored e.g. `splices`, get the value from changedProps
    // TODO(kschaaf): Note, this can cause a rare issue where the wildcard
    // info.value could pull a stale value out of changedProps during a reentrant
    // change that sets the value back to undefined.
    // https://github.com/Polymer/polymer/issues/5479

    if (value === undefined) {
      value = props[path];
    }

    return value;
  } // data api

  /**
   * Sends array splice notifications (`.splices` and `.length`)
   *
   * Note: this implementation only accepts normalized paths
   *
   * @param {!Polymer_PropertyEffects} inst Instance to send notifications to
   * @param {Array} array The array the mutations occurred on
   * @param {string} path The path to the array that was mutated
   * @param {Array} splices Array of splice records
   * @return {void}
   * @private
   */


  function notifySplices(inst, array, path, splices) {
    const splicesData = {
      indexSplices: splices
    }; // Legacy behavior stored splices in `__data__` so it was *not* ephemeral.
    // To match this behavior, we store splices directly on the array.

    if (legacyUndefined && !inst._overrideLegacyUndefined) {
      array.splices = splicesData;
    }

    inst.notifyPath(path + '.splices', splicesData);
    inst.notifyPath(path + '.length', array.length); // Clear splice data only when it's stored on the array.

    if (legacyUndefined && !inst._overrideLegacyUndefined) {
      splicesData.indexSplices = [];
    }
  }
  /**
   * Creates a splice record and sends an array splice notification for
   * the described mutation
   *
   * Note: this implementation only accepts normalized paths
   *
   * @param {!Polymer_PropertyEffects} inst Instance to send notifications to
   * @param {Array} array The array the mutations occurred on
   * @param {string} path The path to the array that was mutated
   * @param {number} index Index at which the array mutation occurred
   * @param {number} addedCount Number of added items
   * @param {Array} removed Array of removed items
   * @return {void}
   * @private
   */


  function notifySplice(inst, array, path, index, addedCount, removed) {
    notifySplices(inst, array, path, [{
      index: index,
      addedCount: addedCount,
      removed: removed,
      object: array,
      type: 'splice'
    }]);
  }
  /**
   * Returns an upper-cased version of the string.
   *
   * @param {string} name String to uppercase
   * @return {string} Uppercased string
   * @private
   */


  function upper(name) {
    return name[0].toUpperCase() + name.substring(1);
  }
  /**
   * Element class mixin that provides meta-programming for Polymer's template
   * binding and data observation (collectively, "property effects") system.
   *
   * This mixin uses provides the following key static methods for adding
   * property effects to an element class:
   * - `addPropertyEffect`
   * - `createPropertyObserver`
   * - `createMethodObserver`
   * - `createNotifyingProperty`
   * - `createReadOnlyProperty`
   * - `createReflectedProperty`
   * - `createComputedProperty`
   * - `bindTemplate`
   *
   * Each method creates one or more property accessors, along with metadata
   * used by this mixin's implementation of `_propertiesChanged` to perform
   * the property effects.
   *
   * Underscored versions of the above methods also exist on the element
   * prototype for adding property effects on instances at runtime.
   *
   * Note that this mixin overrides several `PropertyAccessors` methods, in
   * many cases to maintain guarantees provided by the Polymer 1.x features;
   * notably it changes property accessors to be synchronous by default
   * whereas the default when using `PropertyAccessors` standalone is to be
   * async by default.
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin TemplateStamp
   * @appliesMixin PropertyAccessors
   * @summary Element class mixin that provides meta-programming for Polymer's
   * template binding and data observation system.
   */


  const PropertyEffects = dedupingMixin(superClass => {
    /**
     * @constructor
     * @implements {Polymer_PropertyAccessors}
     * @implements {Polymer_TemplateStamp}
     * @unrestricted
     * @private
     */
    const propertyEffectsBase = TemplateStamp(PropertyAccessors(superClass));
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_PropertyEffects}
     * @extends {propertyEffectsBase}
     * @unrestricted
     */

    class PropertyEffects extends propertyEffectsBase {
      constructor() {
        super();
        /** @type {boolean} */
        // Used to identify users of this mixin, ala instanceof

        this.__isPropertyEffectsClient = true;
        /** @type {boolean} */

        this.__dataClientsReady;
        /** @type {Array} */

        this.__dataPendingClients;
        /** @type {Object} */

        this.__dataToNotify;
        /** @type {Object} */

        this.__dataLinkedPaths;
        /** @type {boolean} */

        this.__dataHasPaths;
        /** @type {Object} */

        this.__dataCompoundStorage;
        /** @type {Polymer_PropertyEffects} */

        this.__dataHost;
        /** @type {!Object} */

        this.__dataTemp;
        /** @type {boolean} */

        this.__dataClientsInitialized;
        /** @type {!Object} */

        this.__data;
        /** @type {!Object|null} */

        this.__dataPending;
        /** @type {!Object} */

        this.__dataOld;
        /** @type {Object} */

        this.__computeEffects;
        /** @type {Object} */

        this.__computeInfo;
        /** @type {Object} */

        this.__reflectEffects;
        /** @type {Object} */

        this.__notifyEffects;
        /** @type {Object} */

        this.__propagateEffects;
        /** @type {Object} */

        this.__observeEffects;
        /** @type {Object} */

        this.__readOnly;
        /** @type {!TemplateInfo} */

        this.__templateInfo;
        /** @type {boolean} */

        this._overrideLegacyUndefined;
      }

      get PROPERTY_EFFECT_TYPES() {
        return TYPES;
      }
      /**
       * @override
       * @return {void}
       */


      _initializeProperties() {
        super._initializeProperties();

        this._registerHost();

        this.__dataClientsReady = false;
        this.__dataPendingClients = null;
        this.__dataToNotify = null;
        this.__dataLinkedPaths = null;
        this.__dataHasPaths = false; // May be set on instance prior to upgrade

        this.__dataCompoundStorage = this.__dataCompoundStorage || null;
        this.__dataHost = this.__dataHost || null;
        this.__dataTemp = {};
        this.__dataClientsInitialized = false;
      }

      _registerHost() {
        if (hostStack.length) {
          let host = hostStack[hostStack.length - 1];

          host._enqueueClient(this); // This ensures even non-bound elements have a host set, as
          // long as they upgrade synchronously


          this.__dataHost = host;
        }
      }
      /**
       * Overrides `PropertyAccessors` implementation to provide a
       * more efficient implementation of initializing properties from
       * the prototype on the instance.
       *
       * @override
       * @param {Object} props Properties to initialize on the prototype
       * @return {void}
       */


      _initializeProtoProperties(props) {
        this.__data = Object.create(props);
        this.__dataPending = Object.create(props);
        this.__dataOld = {};
      }
      /**
       * Overrides `PropertyAccessors` implementation to avoid setting
       * `_setProperty`'s `shouldNotify: true`.
       *
       * @override
       * @param {Object} props Properties to initialize on the instance
       * @return {void}
       */


      _initializeInstanceProperties(props) {
        let readOnly = this[TYPES.READ_ONLY];

        for (let prop in props) {
          if (!readOnly || !readOnly[prop]) {
            this.__dataPending = this.__dataPending || {};
            this.__dataOld = this.__dataOld || {};
            this.__data[prop] = this.__dataPending[prop] = props[prop];
          }
        }
      } // Prototype setup ----------------------------------------

      /**
       * Equivalent to static `addPropertyEffect` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Property that should trigger the effect
       * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
       * @param {Object=} effect Effect metadata object
       * @return {void}
       * @protected
       */


      _addPropertyEffect(property, type, effect) {
        this._createPropertyAccessor(property, type == TYPES.READ_ONLY); // effects are accumulated into arrays per property based on type


        let effects = ensureOwnEffectMap(this, type, true)[property];

        if (!effects) {
          effects = this[type][property] = [];
        }

        effects.push(effect);
      }
      /**
       * Removes the given property effect.
       *
       * @override
       * @param {string} property Property the effect was associated with
       * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
       * @param {Object=} effect Effect metadata object to remove
       * @return {void}
       */


      _removePropertyEffect(property, type, effect) {
        let effects = ensureOwnEffectMap(this, type, true)[property];
        let idx = effects.indexOf(effect);

        if (idx >= 0) {
          effects.splice(idx, 1);
        }
      }
      /**
       * Returns whether the current prototype/instance has a property effect
       * of a certain type.
       *
       * @override
       * @param {string} property Property name
       * @param {string=} type Effect type, from this.PROPERTY_EFFECT_TYPES
       * @return {boolean} True if the prototype/instance has an effect of this
       *     type
       * @protected
       */


      _hasPropertyEffect(property, type) {
        let effects = this[type];
        return Boolean(effects && effects[property]);
      }
      /**
       * Returns whether the current prototype/instance has a "read only"
       * accessor for the given property.
       *
       * @override
       * @param {string} property Property name
       * @return {boolean} True if the prototype/instance has an effect of this
       *     type
       * @protected
       */


      _hasReadOnlyEffect(property) {
        return this._hasPropertyEffect(property, TYPES.READ_ONLY);
      }
      /**
       * Returns whether the current prototype/instance has a "notify"
       * property effect for the given property.
       *
       * @override
       * @param {string} property Property name
       * @return {boolean} True if the prototype/instance has an effect of this
       *     type
       * @protected
       */


      _hasNotifyEffect(property) {
        return this._hasPropertyEffect(property, TYPES.NOTIFY);
      }
      /**
       * Returns whether the current prototype/instance has a "reflect to
       * attribute" property effect for the given property.
       *
       * @override
       * @param {string} property Property name
       * @return {boolean} True if the prototype/instance has an effect of this
       *     type
       * @protected
       */


      _hasReflectEffect(property) {
        return this._hasPropertyEffect(property, TYPES.REFLECT);
      }
      /**
       * Returns whether the current prototype/instance has a "computed"
       * property effect for the given property.
       *
       * @override
       * @param {string} property Property name
       * @return {boolean} True if the prototype/instance has an effect of this
       *     type
       * @protected
       */


      _hasComputedEffect(property) {
        return this._hasPropertyEffect(property, TYPES.COMPUTE);
      } // Runtime ----------------------------------------

      /**
       * Sets a pending property or path.  If the root property of the path in
       * question had no accessor, the path is set, otherwise it is enqueued
       * via `_setPendingProperty`.
       *
       * This function isolates relatively expensive functionality necessary
       * for the public API (`set`, `setProperties`, `notifyPath`, and property
       * change listeners via {{...}} bindings), such that it is only done
       * when paths enter the system, and not at every propagation step.  It
       * also sets a `__dataHasPaths` flag on the instance which is used to
       * fast-path slower path-matching code in the property effects host paths.
       *
       * `path` can be a path string or array of path parts as accepted by the
       * public API.
       *
       * @override
       * @param {string | !Array<number|string>} path Path to set
       * @param {*} value Value to set
       * @param {boolean=} shouldNotify Set to true if this change should
       *  cause a property notification event dispatch
       * @param {boolean=} isPathNotification If the path being set is a path
       *   notification of an already changed value, as opposed to a request
       *   to set and notify the change.  In the latter `false` case, a dirty
       *   check is performed and then the value is set to the path before
       *   enqueuing the pending property change.
       * @return {boolean} Returns true if the property/path was enqueued in
       *   the pending changes bag.
       * @protected
       */


      _setPendingPropertyOrPath(path, value, shouldNotify, isPathNotification) {
        if (isPathNotification || root(Array.isArray(path) ? path[0] : path) !== path) {
          // Dirty check changes being set to a path against the actual object,
          // since this is the entry point for paths into the system; from here
          // the only dirty checks are against the `__dataTemp` cache to prevent
          // duplicate work in the same turn only. Note, if this was a notification
          // of a change already set to a path (isPathNotification: true),
          // we always let the change through and skip the `set` since it was
          // already dirty checked at the point of entry and the underlying
          // object has already been updated
          if (!isPathNotification) {
            let old = get(this, path);
            path =
            /** @type {string} */
            set(this, path, value); // Use property-accessor's simpler dirty check

            if (!path || !super._shouldPropertyChange(path, value, old)) {
              return false;
            }
          }

          this.__dataHasPaths = true;

          if (this._setPendingProperty(
          /**@type{string}*/
          path, value, shouldNotify)) {
            computeLinkedPaths(this,
            /**@type{string}*/
            path, value);
            return true;
          }
        } else {
          if (this.__dataHasAccessor && this.__dataHasAccessor[path]) {
            return this._setPendingProperty(
            /**@type{string}*/
            path, value, shouldNotify);
          } else {
            this[path] = value;
          }
        }

        return false;
      }
      /**
       * Applies a value to a non-Polymer element/node's property.
       *
       * The implementation makes a best-effort at binding interop:
       * Some native element properties have side-effects when
       * re-setting the same value (e.g. setting `<input>.value` resets the
       * cursor position), so we do a dirty-check before setting the value.
       * However, for better interop with non-Polymer custom elements that
       * accept objects, we explicitly re-set object changes coming from the
       * Polymer world (which may include deep object changes without the
       * top reference changing), erring on the side of providing more
       * information.
       *
       * Users may override this method to provide alternate approaches.
       *
       * @override
       * @param {!Node} node The node to set a property on
       * @param {string} prop The property to set
       * @param {*} value The value to set
       * @return {void}
       * @protected
       */


      _setUnmanagedPropertyToNode(node, prop, value) {
        // It is a judgment call that resetting primitives is
        // "bad" and resettings objects is also "good"; alternatively we could
        // implement a whitelist of tag & property values that should never
        // be reset (e.g. <input>.value && <select>.value)
        if (value !== node[prop] || typeof value == 'object') {
          // Note, className needs style scoping so this needs wrapping.
          if (prop === 'className') {
            node =
            /** @type {!Node} */
            wrap$1(node);
          }

          node[prop] = value;
        }
      }
      /**
       * Overrides the `PropertiesChanged` implementation to introduce special
       * dirty check logic depending on the property & value being set:
       *
       * 1. Any value set to a path (e.g. 'obj.prop': 42 or 'obj.prop': {...})
       *    Stored in `__dataTemp`, dirty checked against `__dataTemp`
       * 2. Object set to simple property (e.g. 'prop': {...})
       *    Stored in `__dataTemp` and `__data`, dirty checked against
       *    `__dataTemp` by default implementation of `_shouldPropertyChange`
       * 3. Primitive value set to simple property (e.g. 'prop': 42)
       *    Stored in `__data`, dirty checked against `__data`
       *
       * The dirty-check is important to prevent cycles due to two-way
       * notification, but paths and objects are only dirty checked against any
       * previous value set during this turn via a "temporary cache" that is
       * cleared when the last `_propertiesChanged` exits. This is so:
       * a. any cached array paths (e.g. 'array.3.prop') may be invalidated
       *    due to array mutations like shift/unshift/splice; this is fine
       *    since path changes are dirty-checked at user entry points like `set`
       * b. dirty-checking for objects only lasts one turn to allow the user
       *    to mutate the object in-place and re-set it with the same identity
       *    and have all sub-properties re-propagated in a subsequent turn.
       *
       * The temp cache is not necessarily sufficient to prevent invalid array
       * paths, since a splice can happen during the same turn (with pathological
       * user code); we could introduce a "fixup" for temporarily cached array
       * paths if needed: https://github.com/Polymer/polymer/issues/4227
       *
       * @override
       * @param {string} property Name of the property
       * @param {*} value Value to set
       * @param {boolean=} shouldNotify True if property should fire notification
       *   event (applies only for `notify: true` properties)
       * @return {boolean} Returns true if the property changed
       */


      _setPendingProperty(property, value, shouldNotify) {
        let propIsPath = this.__dataHasPaths && isPath(property);
        let prevProps = propIsPath ? this.__dataTemp : this.__data;

        if (this._shouldPropertyChange(property, value, prevProps[property])) {
          if (!this.__dataPending) {
            this.__dataPending = {};
            this.__dataOld = {};
          } // Ensure old is captured from the last turn


          if (!(property in this.__dataOld)) {
            this.__dataOld[property] = this.__data[property];
          } // Paths are stored in temporary cache (cleared at end of turn),
          // which is used for dirty-checking, all others stored in __data


          if (propIsPath) {
            this.__dataTemp[property] = value;
          } else {
            this.__data[property] = value;
          } // All changes go into pending property bag, passed to _propertiesChanged


          this.__dataPending[property] = value; // Track properties that should notify separately

          if (propIsPath || this[TYPES.NOTIFY] && this[TYPES.NOTIFY][property]) {
            this.__dataToNotify = this.__dataToNotify || {};
            this.__dataToNotify[property] = shouldNotify;
          }

          return true;
        }

        return false;
      }
      /**
       * Overrides base implementation to ensure all accessors set `shouldNotify`
       * to true, for per-property notification tracking.
       *
       * @override
       * @param {string} property Name of the property
       * @param {*} value Value to set
       * @return {void}
       */


      _setProperty(property, value) {
        if (this._setPendingProperty(property, value, true)) {
          this._invalidateProperties();
        }
      }
      /**
       * Overrides `PropertyAccessor`'s default async queuing of
       * `_propertiesChanged`: if `__dataReady` is false (has not yet been
       * manually flushed), the function no-ops; otherwise flushes
       * `_propertiesChanged` synchronously.
       *
       * @override
       * @return {void}
       */


      _invalidateProperties() {
        if (this.__dataReady) {
          this._flushProperties();
        }
      }
      /**
       * Enqueues the given client on a list of pending clients, whose
       * pending property changes can later be flushed via a call to
       * `_flushClients`.
       *
       * @override
       * @param {Object} client PropertyEffects client to enqueue
       * @return {void}
       * @protected
       */


      _enqueueClient(client) {
        this.__dataPendingClients = this.__dataPendingClients || [];

        if (client !== this) {
          this.__dataPendingClients.push(client);
        }
      }
      /**
       * Flushes any clients previously enqueued via `_enqueueClient`, causing
       * their `_flushProperties` method to run.
       *
       * @override
       * @return {void}
       * @protected
       */


      _flushClients() {
        if (!this.__dataClientsReady) {
          this.__dataClientsReady = true;

          this._readyClients(); // Override point where accessors are turned on; importantly,
          // this is after clients have fully readied, providing a guarantee
          // that any property effects occur only after all clients are ready.


          this.__dataReady = true;
        } else {
          this.__enableOrFlushClients();
        }
      } // NOTE: We ensure clients either enable or flush as appropriate. This
      // handles two corner cases:
      // (1) clients flush properly when connected/enabled before the host
      // enables; e.g.
      //   (a) Templatize stamps with no properties and does not flush and
      //   (b) the instance is inserted into dom and
      //   (c) then the instance flushes.
      // (2) clients enable properly when not connected/enabled when the host
      // flushes; e.g.
      //   (a) a template is runtime stamped and not yet connected/enabled
      //   (b) a host sets a property, causing stamped dom to flush
      //   (c) the stamped dom enables.


      __enableOrFlushClients() {
        let clients = this.__dataPendingClients;

        if (clients) {
          this.__dataPendingClients = null;

          for (let i = 0; i < clients.length; i++) {
            let client = clients[i];

            if (!client.__dataEnabled) {
              client._enableProperties();
            } else if (client.__dataPending) {
              client._flushProperties();
            }
          }
        }
      }
      /**
       * Perform any initial setup on client dom. Called before the first
       * `_flushProperties` call on client dom and before any element
       * observers are called.
       *
       * @override
       * @return {void}
       * @protected
       */


      _readyClients() {
        this.__enableOrFlushClients();
      }
      /**
       * Sets a bag of property changes to this instance, and
       * synchronously processes all effects of the properties as a batch.
       *
       * Property names must be simple properties, not paths.  Batched
       * path propagation is not supported.
       *
       * @override
       * @param {Object} props Bag of one or more key-value pairs whose key is
       *   a property and value is the new value to set for that property.
       * @param {boolean=} setReadOnly When true, any private values set in
       *   `props` will be set. By default, `setProperties` will not set
       *   `readOnly: true` root properties.
       * @return {void}
       * @public
       */


      setProperties(props, setReadOnly) {
        for (let path in props) {
          if (setReadOnly || !this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][path]) {
            //TODO(kschaaf): explicitly disallow paths in setProperty?
            // wildcard observers currently only pass the first changed path
            // in the `info` object, and you could do some odd things batching
            // paths, e.g. {'foo.bar': {...}, 'foo': null}
            this._setPendingPropertyOrPath(path, props[path], true);
          }
        }

        this._invalidateProperties();
      }
      /**
       * Overrides `PropertyAccessors` so that property accessor
       * side effects are not enabled until after client dom is fully ready.
       * Also calls `_flushClients` callback to ensure client dom is enabled
       * that was not enabled as a result of flushing properties.
       *
       * @override
       * @return {void}
       */


      ready() {
        // It is important that `super.ready()` is not called here as it
        // immediately turns on accessors. Instead, we wait until `readyClients`
        // to enable accessors to provide a guarantee that clients are ready
        // before processing any accessors side effects.
        this._flushProperties(); // If no data was pending, `_flushProperties` will not `flushClients`
        // so ensure this is done.


        if (!this.__dataClientsReady) {
          this._flushClients();
        } // Before ready, client notifications do not trigger _flushProperties.
        // Therefore a flush is necessary here if data has been set.


        if (this.__dataPending) {
          this._flushProperties();
        }
      }
      /**
       * Implements `PropertyAccessors`'s properties changed callback.
       *
       * Runs each class of effects for the batch of changed properties in
       * a specific order (compute, propagate, reflect, observe, notify).
       *
       * @override
       * @param {!Object} currentProps Bag of all current accessor values
       * @param {?Object} changedProps Bag of properties changed since the last
       *   call to `_propertiesChanged`
       * @param {?Object} oldProps Bag of previous values for each property
       *   in `changedProps`
       * @return {void}
       */


      _propertiesChanged(currentProps, changedProps, oldProps) {
        // ----------------------------
        // let c = Object.getOwnPropertyNames(changedProps || {});
        // window.debug && console.group(this.localName + '#' + this.id + ': ' + c);
        // if (window.debug) { debugger; }
        // ----------------------------
        let hasPaths = this.__dataHasPaths;
        this.__dataHasPaths = false;
        let notifyProps; // Compute properties

        runComputedEffects(this, changedProps, oldProps, hasPaths); // Clear notify properties prior to possible reentry (propagate, observe),
        // but after computing effects have a chance to add to them

        notifyProps = this.__dataToNotify;
        this.__dataToNotify = null; // Propagate properties to clients

        this._propagatePropertyChanges(changedProps, oldProps, hasPaths); // Flush clients


        this._flushClients(); // Reflect properties


        runEffects(this, this[TYPES.REFLECT], changedProps, oldProps, hasPaths); // Observe properties

        runEffects(this, this[TYPES.OBSERVE], changedProps, oldProps, hasPaths); // Notify properties to host

        if (notifyProps) {
          runNotifyEffects(this, notifyProps, changedProps, oldProps, hasPaths);
        } // Clear temporary cache at end of turn


        if (this.__dataCounter == 1) {
          this.__dataTemp = {};
        } // ----------------------------
        // window.debug && console.groupEnd(this.localName + '#' + this.id + ': ' + c);
        // ----------------------------

      }
      /**
       * Called to propagate any property changes to stamped template nodes
       * managed by this element.
       *
       * @override
       * @param {Object} changedProps Bag of changed properties
       * @param {Object} oldProps Bag of previous values for changed properties
       * @param {boolean} hasPaths True with `props` contains one or more paths
       * @return {void}
       * @protected
       */


      _propagatePropertyChanges(changedProps, oldProps, hasPaths) {
        if (this[TYPES.PROPAGATE]) {
          runEffects(this, this[TYPES.PROPAGATE], changedProps, oldProps, hasPaths);
        }

        if (this.__templateInfo) {
          this._runEffectsForTemplate(this.__templateInfo, changedProps, oldProps, hasPaths);
        }
      }

      _runEffectsForTemplate(templateInfo, changedProps, oldProps, hasPaths) {
        const baseRunEffects = (changedProps, hasPaths) => {
          runEffects(this, templateInfo.propertyEffects, changedProps, oldProps, hasPaths, templateInfo.nodeList);

          for (let info = templateInfo.firstChild; info; info = info.nextSibling) {
            this._runEffectsForTemplate(info, changedProps, oldProps, hasPaths);
          }
        };

        if (templateInfo.runEffects) {
          templateInfo.runEffects(baseRunEffects, changedProps, hasPaths);
        } else {
          baseRunEffects(changedProps, hasPaths);
        }
      }
      /**
       * Aliases one data path as another, such that path notifications from one
       * are routed to the other.
       *
       * @override
       * @param {string | !Array<string|number>} to Target path to link.
       * @param {string | !Array<string|number>} from Source path to link.
       * @return {void}
       * @public
       */


      linkPaths(to, from) {
        to = normalize(to);
        from = normalize(from);
        this.__dataLinkedPaths = this.__dataLinkedPaths || {};
        this.__dataLinkedPaths[to] = from;
      }
      /**
       * Removes a data path alias previously established with `_linkPaths`.
       *
       * Note, the path to unlink should be the target (`to`) used when
       * linking the paths.
       *
       * @override
       * @param {string | !Array<string|number>} path Target path to unlink.
       * @return {void}
       * @public
       */


      unlinkPaths(path) {
        path = normalize(path);

        if (this.__dataLinkedPaths) {
          delete this.__dataLinkedPaths[path];
        }
      }
      /**
       * Notify that an array has changed.
       *
       * Example:
       *
       *     this.items = [ {name: 'Jim'}, {name: 'Todd'}, {name: 'Bill'} ];
       *     ...
       *     this.items.splice(1, 1, {name: 'Sam'});
       *     this.items.push({name: 'Bob'});
       *     this.notifySplices('items', [
       *       { index: 1, removed: [{name: 'Todd'}], addedCount: 1,
       *         object: this.items, type: 'splice' },
       *       { index: 3, removed: [], addedCount: 1,
       *         object: this.items, type: 'splice'}
       *     ]);
       *
       * @param {string} path Path that should be notified.
       * @param {Array} splices Array of splice records indicating ordered
       *   changes that occurred to the array. Each record should have the
       *   following fields:
       *    * index: index at which the change occurred
       *    * removed: array of items that were removed from this index
       *    * addedCount: number of new items added at this index
       *    * object: a reference to the array in question
       *    * type: the string literal 'splice'
       *
       *   Note that splice records _must_ be normalized such that they are
       *   reported in index order (raw results from `Object.observe` are not
       *   ordered and must be normalized/merged before notifying).
       *
       * @override
       * @return {void}
       * @public
       */


      notifySplices(path, splices) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array} */
        get(this, path, info);
        notifySplices(this, array, info.path, splices);
      }
      /**
       * Convenience method for reading a value from a path.
       *
       * Note, if any part in the path is undefined, this method returns
       * `undefined` (this method does not throw when dereferencing undefined
       * paths).
       *
       * @override
       * @param {(string|!Array<(string|number)>)} path Path to the value
       *   to read.  The path may be specified as a string (e.g. `foo.bar.baz`)
       *   or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
       *   bracketed expressions are not supported; string-based path parts
       *   *must* be separated by dots.  Note that when dereferencing array
       *   indices, the index may be used as a dotted part directly
       *   (e.g. `users.12.name` or `['users', 12, 'name']`).
       * @param {Object=} root Root object from which the path is evaluated.
       * @return {*} Value at the path, or `undefined` if any part of the path
       *   is undefined.
       * @public
       */


      get(path, root) {
        return get(root || this, path);
      }
      /**
       * Convenience method for setting a value to a path and notifying any
       * elements bound to the same path.
       *
       * Note, if any part in the path except for the last is undefined,
       * this method does nothing (this method does not throw when
       * dereferencing undefined paths).
       *
       * @override
       * @param {(string|!Array<(string|number)>)} path Path to the value
       *   to write.  The path may be specified as a string (e.g. `'foo.bar.baz'`)
       *   or an array of path parts (e.g. `['foo.bar', 'baz']`).  Note that
       *   bracketed expressions are not supported; string-based path parts
       *   *must* be separated by dots.  Note that when dereferencing array
       *   indices, the index may be used as a dotted part directly
       *   (e.g. `'users.12.name'` or `['users', 12, 'name']`).
       * @param {*} value Value to set at the specified path.
       * @param {Object=} root Root object from which the path is evaluated.
       *   When specified, no notification will occur.
       * @return {void}
       * @public
       */


      set(path, value, root) {
        if (root) {
          set(root, path, value);
        } else {
          if (!this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][
          /** @type {string} */
          path]) {
            if (this._setPendingPropertyOrPath(path, value, true)) {
              this._invalidateProperties();
            }
          }
        }
      }
      /**
       * Adds items onto the end of the array at the path specified.
       *
       * The arguments after `path` and return value match that of
       * `Array.prototype.push`.
       *
       * This method notifies other paths to the same array that a
       * splice occurred to the array.
       *
       * @override
       * @param {string | !Array<string|number>} path Path to array.
       * @param {...*} items Items to push onto array
       * @return {number} New length of the array.
       * @public
       */


      push(path, ...items) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array}*/
        get(this, path, info);
        let len = array.length;
        let ret = array.push(...items);

        if (items.length) {
          notifySplice(this, array, info.path, len, items.length, []);
        }

        return ret;
      }
      /**
       * Removes an item from the end of array at the path specified.
       *
       * The arguments after `path` and return value match that of
       * `Array.prototype.pop`.
       *
       * This method notifies other paths to the same array that a
       * splice occurred to the array.
       *
       * @override
       * @param {string | !Array<string|number>} path Path to array.
       * @return {*} Item that was removed.
       * @public
       */


      pop(path) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array} */
        get(this, path, info);
        let hadLength = Boolean(array.length);
        let ret = array.pop();

        if (hadLength) {
          notifySplice(this, array, info.path, array.length, 0, [ret]);
        }

        return ret;
      }
      /**
       * Starting from the start index specified, removes 0 or more items
       * from the array and inserts 0 or more new items in their place.
       *
       * The arguments after `path` and return value match that of
       * `Array.prototype.splice`.
       *
       * This method notifies other paths to the same array that a
       * splice occurred to the array.
       *
       * @override
       * @param {string | !Array<string|number>} path Path to array.
       * @param {number} start Index from which to start removing/inserting.
       * @param {number=} deleteCount Number of items to remove.
       * @param {...*} items Items to insert into array.
       * @return {!Array} Array of removed items.
       * @public
       */


      splice(path, start, deleteCount, ...items) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array} */
        get(this, path, info); // Normalize fancy native splice handling of crazy start values

        if (start < 0) {
          start = array.length - Math.floor(-start);
        } else if (start) {
          start = Math.floor(start);
        } // array.splice does different things based on the number of arguments
        // you pass in. Therefore, array.splice(0) and array.splice(0, undefined)
        // do different things. In the former, the whole array is cleared. In the
        // latter, no items are removed.
        // This means that we need to detect whether 1. one of the arguments
        // is actually passed in and then 2. determine how many arguments
        // we should pass on to the native array.splice
        //


        let ret; // Omit any additional arguments if they were not passed in

        if (arguments.length === 2) {
          ret = array.splice(start); // Either start was undefined and the others were defined, but in this
          // case we can safely pass on all arguments
          //
          // Note: this includes the case where none of the arguments were passed in,
          // e.g. this.splice('array'). However, if both start and deleteCount
          // are undefined, array.splice will not modify the array (as expected)
        } else {
          ret = array.splice(start, deleteCount, ...items);
        } // At the end, check whether any items were passed in (e.g. insertions)
        // or if the return array contains items (e.g. deletions).
        // Only notify if items were added or deleted.


        if (items.length || ret.length) {
          notifySplice(this, array, info.path, start, items.length, ret);
        }

        return ret;
      }
      /**
       * Removes an item from the beginning of array at the path specified.
       *
       * The arguments after `path` and return value match that of
       * `Array.prototype.pop`.
       *
       * This method notifies other paths to the same array that a
       * splice occurred to the array.
       *
       * @override
       * @param {string | !Array<string|number>} path Path to array.
       * @return {*} Item that was removed.
       * @public
       */


      shift(path) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array} */
        get(this, path, info);
        let hadLength = Boolean(array.length);
        let ret = array.shift();

        if (hadLength) {
          notifySplice(this, array, info.path, 0, 0, [ret]);
        }

        return ret;
      }
      /**
       * Adds items onto the beginning of the array at the path specified.
       *
       * The arguments after `path` and return value match that of
       * `Array.prototype.push`.
       *
       * This method notifies other paths to the same array that a
       * splice occurred to the array.
       *
       * @override
       * @param {string | !Array<string|number>} path Path to array.
       * @param {...*} items Items to insert info array
       * @return {number} New length of the array.
       * @public
       */


      unshift(path, ...items) {
        let info = {
          path: ''
        };
        let array =
        /** @type {Array} */
        get(this, path, info);
        let ret = array.unshift(...items);

        if (items.length) {
          notifySplice(this, array, info.path, 0, items.length, []);
        }

        return ret;
      }
      /**
       * Notify that a path has changed.
       *
       * Example:
       *
       *     this.item.user.name = 'Bob';
       *     this.notifyPath('item.user.name');
       *
       * @override
       * @param {string} path Path that should be notified.
       * @param {*=} value Value at the path (optional).
       * @return {void}
       * @public
       */


      notifyPath(path, value) {
        /** @type {string} */
        let propPath;

        if (arguments.length == 1) {
          // Get value if not supplied
          let info = {
            path: ''
          };
          value = get(this, path, info);
          propPath = info.path;
        } else if (Array.isArray(path)) {
          // Normalize path if needed
          propPath = normalize(path);
        } else {
          propPath =
          /** @type{string} */
          path;
        }

        if (this._setPendingPropertyOrPath(propPath, value, true, true)) {
          this._invalidateProperties();
        }
      }
      /**
       * Equivalent to static `createReadOnlyProperty` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Property name
       * @param {boolean=} protectedSetter Creates a custom protected setter
       *   when `true`.
       * @return {void}
       * @protected
       */


      _createReadOnlyProperty(property, protectedSetter) {
        this._addPropertyEffect(property, TYPES.READ_ONLY);

        if (protectedSetter) {
          this['_set' + upper(property)] =
          /** @this {PropertyEffects} */
          function (value) {
            this._setProperty(property, value);
          };
        }
      }
      /**
       * Equivalent to static `createPropertyObserver` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Property name
       * @param {string|function(*,*)} method Function or name of observer method
       *     to call
       * @param {boolean=} dynamicFn Whether the method name should be included as
       *   a dependency to the effect.
       * @return {void}
       * @protected
       */


      _createPropertyObserver(property, method, dynamicFn) {
        let info = {
          property,
          method,
          dynamicFn: Boolean(dynamicFn)
        };

        this._addPropertyEffect(property, TYPES.OBSERVE, {
          fn: runObserverEffect,
          info,
          trigger: {
            name: property
          }
        });

        if (dynamicFn) {
          this._addPropertyEffect(
          /** @type {string} */
          method, TYPES.OBSERVE, {
            fn: runObserverEffect,
            info,
            trigger: {
              name: method
            }
          });
        }
      }
      /**
       * Equivalent to static `createMethodObserver` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} expression Method expression
       * @param {boolean|Object=} dynamicFn Boolean or object map indicating
       *   whether method names should be included as a dependency to the effect.
       * @return {void}
       * @protected
       */


      _createMethodObserver(expression, dynamicFn) {
        let sig = parseMethod(expression);

        if (!sig) {
          throw new Error("Malformed observer expression '" + expression + "'");
        }

        createMethodEffect(this, sig, TYPES.OBSERVE, runMethodEffect, null, dynamicFn);
      }
      /**
       * Equivalent to static `createNotifyingProperty` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Property name
       * @return {void}
       * @protected
       */


      _createNotifyingProperty(property) {
        this._addPropertyEffect(property, TYPES.NOTIFY, {
          fn: runNotifyEffect,
          info: {
            eventName: camelToDashCase(property) + '-changed',
            property: property
          }
        });
      }
      /**
       * Equivalent to static `createReflectedProperty` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Property name
       * @return {void}
       * @protected
       * @suppress {missingProperties} go/missingfnprops
       */


      _createReflectedProperty(property) {
        let attr = this.constructor.attributeNameForProperty(property);

        if (attr[0] === '-') {
          console.warn('Property ' + property + ' cannot be reflected to attribute ' + attr + ' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.');
        } else {
          this._addPropertyEffect(property, TYPES.REFLECT, {
            fn: runReflectEffect,
            info: {
              attrName: attr
            }
          });
        }
      }
      /**
       * Equivalent to static `createComputedProperty` API but can be called on
       * an instance to add effects at runtime.  See that method for
       * full API docs.
       *
       * @override
       * @param {string} property Name of computed property to set
       * @param {string} expression Method expression
       * @param {boolean|Object=} dynamicFn Boolean or object map indicating
       *   whether method names should be included as a dependency to the effect.
       * @return {void}
       * @protected
       */


      _createComputedProperty(property, expression, dynamicFn) {
        let sig = parseMethod(expression);

        if (!sig) {
          throw new Error("Malformed computed expression '" + expression + "'");
        }

        const info = createMethodEffect(this, sig, TYPES.COMPUTE, runComputedEffect, property, dynamicFn); // Effects are normally stored as map of dependency->effect, but for
        // ordered computation, we also need tree of computedProp->dependencies

        ensureOwnEffectMap(this, COMPUTE_INFO)[property] = info;
      }
      /**
       * Gather the argument values for a method specified in the provided array
       * of argument metadata.
       *
       * The `path` and `value` arguments are used to fill in wildcard descriptor
       * when the method is being called as a result of a path notification.
       *
       * @param {!Array<!MethodArg>} args Array of argument metadata
       * @param {string} path Property/path name that triggered the method effect
       * @param {Object} props Bag of current property changes
       * @return {!Array<*>} Array of argument values
       * @private
       */


      _marshalArgs(args, path, props) {
        const data = this.__data;
        const values = [];

        for (let i = 0, l = args.length; i < l; i++) {
          let {
            name,
            structured,
            wildcard,
            value,
            literal
          } = args[i];

          if (!literal) {
            if (wildcard) {
              const matches = isDescendant(name, path);
              const pathValue = getArgValue(data, props, matches ? path : name);
              value = {
                path: matches ? path : name,
                value: pathValue,
                base: matches ? get(data, name) : pathValue
              };
            } else {
              value = structured ? getArgValue(data, props, name) : data[name];
            }
          } // When the `legacyUndefined` flag is enabled, pass a no-op value
          // so that the observer, computed property, or compound binding is aborted.


          if (legacyUndefined && !this._overrideLegacyUndefined && value === undefined && args.length > 1) {
            return NOOP;
          }

          values[i] = value;
        }

        return values;
      } // -- static class methods ------------

      /**
       * Ensures an accessor exists for the specified property, and adds
       * to a list of "property effects" that will run when the accessor for
       * the specified property is set.  Effects are grouped by "type", which
       * roughly corresponds to a phase in effect processing.  The effect
       * metadata should be in the following form:
       *
       *     {
       *       fn: effectFunction, // Reference to function to call to perform effect
       *       info: { ... }       // Effect metadata passed to function
       *       trigger: {          // Optional triggering metadata; if not provided
       *         name: string      // the property is treated as a wildcard
       *         structured: boolean
       *         wildcard: boolean
       *       }
       *     }
       *
       * Effects are called from `_propertiesChanged` in the following order by
       * type:
       *
       * 1. COMPUTE
       * 2. PROPAGATE
       * 3. REFLECT
       * 4. OBSERVE
       * 5. NOTIFY
       *
       * Effect functions are called with the following signature:
       *
       *     effectFunction(inst, path, props, oldProps, info, hasPaths)
       *
       * @param {string} property Property that should trigger the effect
       * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
       * @param {Object=} effect Effect metadata object
       * @return {void}
       * @protected
       * @nocollapse
       */


      static addPropertyEffect(property, type, effect) {
        this.prototype._addPropertyEffect(property, type, effect);
      }
      /**
       * Creates a single-property observer for the given property.
       *
       * @param {string} property Property name
       * @param {string|function(*,*)} method Function or name of observer method to call
       * @param {boolean=} dynamicFn Whether the method name should be included as
       *   a dependency to the effect.
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createPropertyObserver(property, method, dynamicFn) {
        this.prototype._createPropertyObserver(property, method, dynamicFn);
      }
      /**
       * Creates a multi-property "method observer" based on the provided
       * expression, which should be a string in the form of a normal JavaScript
       * function signature: `'methodName(arg1, [..., argn])'`.  Each argument
       * should correspond to a property or path in the context of this
       * prototype (or instance), or may be a literal string or number.
       *
       * @param {string} expression Method expression
       * @param {boolean|Object=} dynamicFn Boolean or object map indicating
       * @return {void}
       *   whether method names should be included as a dependency to the effect.
       * @protected
       * @nocollapse
       */


      static createMethodObserver(expression, dynamicFn) {
        this.prototype._createMethodObserver(expression, dynamicFn);
      }
      /**
       * Causes the setter for the given property to dispatch `<property>-changed`
       * events to notify of changes to the property.
       *
       * @param {string} property Property name
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createNotifyingProperty(property) {
        this.prototype._createNotifyingProperty(property);
      }
      /**
       * Creates a read-only accessor for the given property.
       *
       * To set the property, use the protected `_setProperty` API.
       * To create a custom protected setter (e.g. `_setMyProp()` for
       * property `myProp`), pass `true` for `protectedSetter`.
       *
       * Note, if the property will have other property effects, this method
       * should be called first, before adding other effects.
       *
       * @param {string} property Property name
       * @param {boolean=} protectedSetter Creates a custom protected setter
       *   when `true`.
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createReadOnlyProperty(property, protectedSetter) {
        this.prototype._createReadOnlyProperty(property, protectedSetter);
      }
      /**
       * Causes the setter for the given property to reflect the property value
       * to a (dash-cased) attribute of the same name.
       *
       * @param {string} property Property name
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createReflectedProperty(property) {
        this.prototype._createReflectedProperty(property);
      }
      /**
       * Creates a computed property whose value is set to the result of the
       * method described by the given `expression` each time one or more
       * arguments to the method changes.  The expression should be a string
       * in the form of a normal JavaScript function signature:
       * `'methodName(arg1, [..., argn])'`
       *
       * @param {string} property Name of computed property to set
       * @param {string} expression Method expression
       * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether
       *   method names should be included as a dependency to the effect.
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createComputedProperty(property, expression, dynamicFn) {
        this.prototype._createComputedProperty(property, expression, dynamicFn);
      }
      /**
       * Parses the provided template to ensure binding effects are created
       * for them, and then ensures property accessors are created for any
       * dependent properties in the template.  Binding effects for bound
       * templates are stored in a linked list on the instance so that
       * templates can be efficiently stamped and unstamped.
       *
       * @param {!HTMLTemplateElement} template Template containing binding
       *   bindings
       * @return {!TemplateInfo} Template metadata object
       * @protected
       * @nocollapse
       */


      static bindTemplate(template) {
        return this.prototype._bindTemplate(template);
      } // -- binding ----------------------------------------------

      /*
       * Overview of binding flow:
       *
       * During finalization (`instanceBinding==false`, `wasPreBound==false`):
       *  `_bindTemplate(t, false)` called directly during finalization - parses
       *  the template (for the first time), and then assigns that _prototypical_
       *  template info to `__preboundTemplateInfo` _on the prototype_; note in
       *  this case `wasPreBound` is false; this is the first time we're binding
       *  it, thus we create accessors.
       *
       * During first stamping (`instanceBinding==true`, `wasPreBound==true`):
       *   `_stampTemplate` calls `_bindTemplate(t, true)`: the `templateInfo`
       *   returned matches the prebound one, and so this is `wasPreBound == true`
       *   state; thus we _skip_ creating accessors, but _do_ create an instance
       *   of the template info to serve as the start of our linked list (needs to
       *   be an instance, not the prototypical one, so that we can add `nodeList`
       *   to it to contain the `nodeInfo`-ordered list of instance nodes for
       *   bindings, and so we can chain runtime-stamped template infos off of
       *   it). At this point, the call to `_stampTemplate` calls
       *   `applyTemplateInfo` for each nested `<template>` found during parsing
       *   to hand prototypical `_templateInfo` to them; we also pass the _parent_
       *   `templateInfo` to the `<template>` so that we have the instance-time
       *   parent to link the `templateInfo` under in the case it was
       *   runtime-stamped.
       *
       * During subsequent runtime stamping (`instanceBinding==true`,
       *   `wasPreBound==false`): `_stampTemplate` calls `_bindTemplate(t, true)`
       *   - here `templateInfo` is guaranteed to _not_ match the prebound one,
       *   because it was either a different template altogether, or even if it
       *   was the same template, the step above created a instance of the info;
       *   in this case `wasPreBound == false`, so we _do_ create accessors, _and_
       *   link a instance into the linked list.
       */

      /**
       * Equivalent to static `bindTemplate` API but can be called on an instance
       * to add effects at runtime.  See that method for full API docs.
       *
       * This method may be called on the prototype (for prototypical template
       * binding, to avoid creating accessors every instance) once per prototype,
       * and will be called with `runtimeBinding: true` by `_stampTemplate` to
       * create and link an instance of the template metadata associated with a
       * particular stamping.
       *
       * @override
       * @param {!HTMLTemplateElement} template Template containing binding
       * bindings
       * @param {boolean=} instanceBinding When false (default), performs
       * "prototypical" binding of the template and overwrites any previously
       * bound template for the class. When true (as passed from
       * `_stampTemplate`), the template info is instanced and linked into the
       * list of bound templates.
       * @return {!TemplateInfo} Template metadata object; for `runtimeBinding`,
       * this is an instance of the prototypical template info
       * @protected
       * @suppress {missingProperties} go/missingfnprops
       */


      _bindTemplate(template, instanceBinding) {
        let templateInfo = this.constructor._parseTemplate(template);

        let wasPreBound = this.__preBoundTemplateInfo == templateInfo; // Optimization: since this is called twice for proto-bound templates,
        // don't attempt to recreate accessors if this template was pre-bound

        if (!wasPreBound) {
          for (let prop in templateInfo.propertyEffects) {
            this._createPropertyAccessor(prop);
          }
        }

        if (instanceBinding) {
          // For instance-time binding, create instance of template metadata
          // and link into tree of templates if necessary
          templateInfo =
          /** @type {!TemplateInfo} */
          Object.create(templateInfo);
          templateInfo.wasPreBound = wasPreBound;

          if (!this.__templateInfo) {
            // Set the info to the root of the tree
            this.__templateInfo = templateInfo;
          } else {
            // Append this template info onto the end of its parent template's
            // list, which will determine the tree structure via which property
            // effects are run; if this template was not nested in another
            // template, use the root template (the first stamped one) as the
            // parent. Note, `parent` is the `templateInfo` instance for this
            // template's parent (containing) template, which was set up in
            // `applyTemplateInfo`.  While a given template's `parent` is set
            // apriori, it is only added to the parent's child list at the point
            // that it is being bound, since a template may or may not ever be
            // stamped, and may be stamped more than once (in which case instances
            // of the template info will be in the tree under its parent more than
            // once).
            const parent = template._parentTemplateInfo || this.__templateInfo;
            const previous = parent.lastChild;
            templateInfo.parent = parent;
            parent.lastChild = templateInfo;
            templateInfo.previousSibling = previous;

            if (previous) {
              previous.nextSibling = templateInfo;
            } else {
              parent.firstChild = templateInfo;
            }
          }
        } else {
          this.__preBoundTemplateInfo = templateInfo;
        }

        return templateInfo;
      }
      /**
       * Adds a property effect to the given template metadata, which is run
       * at the "propagate" stage of `_propertiesChanged` when the template
       * has been bound to the element via `_bindTemplate`.
       *
       * The `effect` object should match the format in `_addPropertyEffect`.
       *
       * @param {Object} templateInfo Template metadata to add effect to
       * @param {string} prop Property that should trigger the effect
       * @param {Object=} effect Effect metadata object
       * @return {void}
       * @protected
       * @nocollapse
       */


      static _addTemplatePropertyEffect(templateInfo, prop, effect) {
        let hostProps = templateInfo.hostProps = templateInfo.hostProps || {};
        hostProps[prop] = true;
        let effects = templateInfo.propertyEffects = templateInfo.propertyEffects || {};
        let propEffects = effects[prop] = effects[prop] || [];
        propEffects.push(effect);
      }
      /**
       * Stamps the provided template and performs instance-time setup for
       * Polymer template features, including data bindings, declarative event
       * listeners, and the `this.$` map of `id`'s to nodes.  A document fragment
       * is returned containing the stamped DOM, ready for insertion into the
       * DOM.
       *
       * This method may be called more than once; however note that due to
       * `shadycss` polyfill limitations, only styles from templates prepared
       * using `ShadyCSS.prepareTemplate` will be correctly polyfilled (scoped
       * to the shadow root and support CSS custom properties), and note that
       * `ShadyCSS.prepareTemplate` may only be called once per element. As such,
       * any styles required by in runtime-stamped templates must be included
       * in the main element template.
       *
       * @param {!HTMLTemplateElement} template Template to stamp
       * @param {TemplateInfo=} templateInfo Optional bound template info associated
       *   with the template to be stamped; if omitted the template will be
       *   automatically bound.
       * @return {!StampedTemplate} Cloned template content
       * @override
       * @protected
       */


      _stampTemplate(template, templateInfo) {
        templateInfo = templateInfo ||
        /** @type {!TemplateInfo} */
        this._bindTemplate(template, true); // Ensures that created dom is `_enqueueClient`'d to this element so
        // that it can be flushed on next call to `_flushProperties`

        hostStack.push(this);

        let dom = super._stampTemplate(template, templateInfo);

        hostStack.pop(); // Add template-instance-specific data to instanced templateInfo

        templateInfo.nodeList = dom.nodeList; // Capture child nodes to allow unstamping of non-prototypical templates

        if (!templateInfo.wasPreBound) {
          let nodes = templateInfo.childNodes = [];

          for (let n = dom.firstChild; n; n = n.nextSibling) {
            nodes.push(n);
          }
        }

        dom.templateInfo = templateInfo; // Setup compound storage, 2-way listeners, and dataHost for bindings

        setupBindings(this, templateInfo); // Flush properties into template nodes; the check on `__dataClientsReady`
        // ensures we don't needlessly run effects for an element's initial
        // prototypical template stamping since they will happen as a part of the
        // first call to `_propertiesChanged`. This flag is set to true
        // after running the initial propagate effects, and immediately before
        // flushing clients. Since downstream clients could cause stamping on
        // this host (e.g. a fastDomIf `dom-if` being forced to render
        // synchronously), this flag ensures effects for runtime-stamped templates
        // are run at this point during the initial element boot-up.

        if (this.__dataClientsReady) {
          this._runEffectsForTemplate(templateInfo, this.__data, null, false);

          this._flushClients();
        }

        return dom;
      }
      /**
       * Removes and unbinds the nodes previously contained in the provided
       * DocumentFragment returned from `_stampTemplate`.
       *
       * @override
       * @param {!StampedTemplate} dom DocumentFragment previously returned
       *   from `_stampTemplate` associated with the nodes to be removed
       * @return {void}
       * @protected
       */


      _removeBoundDom(dom) {
        // Unlink template info; Note that while the child is unlinked from its
        // parent list, a template's `parent` reference is never removed, since
        // this is is determined by the tree structure and applied at
        // `applyTemplateInfo` time.
        const templateInfo = dom.templateInfo;
        const {
          previousSibling,
          nextSibling,
          parent
        } = templateInfo;

        if (previousSibling) {
          previousSibling.nextSibling = nextSibling;
        } else if (parent) {
          parent.firstChild = nextSibling;
        }

        if (nextSibling) {
          nextSibling.previousSibling = previousSibling;
        } else if (parent) {
          parent.lastChild = previousSibling;
        }

        templateInfo.nextSibling = templateInfo.previousSibling = null; // Remove stamped nodes

        let nodes = templateInfo.childNodes;

        for (let i = 0; i < nodes.length; i++) {
          let node = nodes[i];
          wrap$1(wrap$1(node).parentNode).removeChild(node);
        }
      }
      /**
       * Overrides default `TemplateStamp` implementation to add support for
       * parsing bindings from `TextNode`'s' `textContent`.  A `bindings`
       * array is added to `nodeInfo` and populated with binding metadata
       * with information capturing the binding target, and a `parts` array
       * with one or more metadata objects capturing the source(s) of the
       * binding.
       *
       * @param {Node} node Node to parse
       * @param {TemplateInfo} templateInfo Template metadata for current template
       * @param {NodeInfo} nodeInfo Node metadata for current template node
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @protected
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _parseTemplateNode(node, templateInfo, nodeInfo) {
        // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()
        let noted = propertyEffectsBase._parseTemplateNode.call(this, node, templateInfo, nodeInfo);

        if (node.nodeType === Node.TEXT_NODE) {
          let parts = this._parseBindings(node.textContent, templateInfo);

          if (parts) {
            // Initialize the textContent with any literal parts
            // NOTE: default to a space here so the textNode remains; some browsers
            // (IE) omit an empty textNode following cloneNode/importNode.
            node.textContent = literalFromParts(parts) || ' ';
            addBinding(this, templateInfo, nodeInfo, 'text', 'textContent', parts);
            noted = true;
          }
        }

        return noted;
      }
      /**
       * Overrides default `TemplateStamp` implementation to add support for
       * parsing bindings from attributes.  A `bindings`
       * array is added to `nodeInfo` and populated with binding metadata
       * with information capturing the binding target, and a `parts` array
       * with one or more metadata objects capturing the source(s) of the
       * binding.
       *
       * @param {Element} node Node to parse
       * @param {TemplateInfo} templateInfo Template metadata for current template
       * @param {NodeInfo} nodeInfo Node metadata for current template node
       * @param {string} name Attribute name
       * @param {string} value Attribute value
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @protected
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) {
        let parts = this._parseBindings(value, templateInfo);

        if (parts) {
          // Attribute or property
          let origName = name;
          let kind = 'property'; // The only way we see a capital letter here is if the attr has
          // a capital letter in it per spec. In this case, to make sure
          // this binding works, we go ahead and make the binding to the attribute.

          if (capitalAttributeRegex.test(name)) {
            kind = 'attribute';
          } else if (name[name.length - 1] == '$') {
            name = name.slice(0, -1);
            kind = 'attribute';
          } // Initialize attribute bindings with any literal parts


          let literal = literalFromParts(parts);

          if (literal && kind == 'attribute') {
            // Ensure a ShadyCSS template scoped style is not removed
            // when a class$ binding's initial literal value is set.
            if (name == 'class' && node.hasAttribute('class')) {
              literal += ' ' + node.getAttribute(name);
            }

            node.setAttribute(name, literal);
          } // support disable-upgrade


          if (kind == 'attribute' && origName == 'disable-upgrade$') {
            node.setAttribute(name, '');
          } // Clear attribute before removing, since IE won't allow removing
          // `value` attribute if it previously had a value (can't
          // unconditionally set '' before removing since attributes with `$`
          // can't be set using setAttribute)


          if (node.localName === 'input' && origName === 'value') {
            node.setAttribute(origName, '');
          } // Remove annotation


          node.removeAttribute(origName); // Case hackery: attributes are lower-case, but bind targets
          // (properties) are case sensitive. Gambit is to map dash-case to
          // camel-case: `foo-bar` becomes `fooBar`.
          // Attribute bindings are excepted.

          if (kind === 'property') {
            name = dashToCamelCase(name);
          }

          addBinding(this, templateInfo, nodeInfo, kind, name, parts, literal);
          return true;
        } else {
          // TODO(https://github.com/google/closure-compiler/issues/3240):
          //     Change back to just super.methodCall()
          return propertyEffectsBase._parseTemplateNodeAttribute.call(this, node, templateInfo, nodeInfo, name, value);
        }
      }
      /**
       * Overrides default `TemplateStamp` implementation to add support for
       * binding the properties that a nested template depends on to the template
       * as `_host_<property>`.
       *
       * @param {Node} node Node to parse
       * @param {TemplateInfo} templateInfo Template metadata for current template
       * @param {NodeInfo} nodeInfo Node metadata for current template node
       * @return {boolean} `true` if the visited node added node-specific
       *   metadata to `nodeInfo`
       * @protected
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _parseTemplateNestedTemplate(node, templateInfo, nodeInfo) {
        // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()
        let noted = propertyEffectsBase._parseTemplateNestedTemplate.call(this, node, templateInfo, nodeInfo);

        const parent = node.parentNode;
        const nestedTemplateInfo = nodeInfo.templateInfo;
        const isDomIf = parent.localName === 'dom-if';
        const isDomRepeat = parent.localName === 'dom-repeat'; // Remove nested template and redirect its host bindings & templateInfo
        // onto the parent (dom-if/repeat element)'s nodeInfo

        if (removeNestedTemplates && (isDomIf || isDomRepeat)) {
          parent.removeChild(node); // Use the parent's nodeInfo (for the dom-if/repeat) to record the
          // templateInfo, and use that for any host property bindings below

          nodeInfo = nodeInfo.parentInfo;
          nodeInfo.templateInfo = nestedTemplateInfo; // Ensure the parent dom-if/repeat is noted since it now may have host
          // bindings; it may not have been if it did not have its own bindings

          nodeInfo.noted = true;
          noted = false;
        } // Merge host props into outer template and add bindings


        let hostProps = nestedTemplateInfo.hostProps;

        if (fastDomIf && isDomIf) {
          // `fastDomIf` mode uses runtime-template stamping to add accessors/
          // effects to properties used in its template; as such we don't need to
          // tax the host element with `_host_` bindings for the `dom-if`.
          // However, in the event it is nested in a `dom-repeat`, it is still
          // important that its host properties are added to the
          // TemplateInstance's `hostProps` so that they are forwarded to the
          // TemplateInstance.
          if (hostProps) {
            templateInfo.hostProps = Object.assign(templateInfo.hostProps || {}, hostProps); // Ensure the dom-if is noted so that it has a __dataHost, since
            // `fastDomIf` uses the host for runtime template stamping; note this
            // was already ensured above in the `removeNestedTemplates` case

            if (!removeNestedTemplates) {
              nodeInfo.parentInfo.noted = true;
            }
          }
        } else {
          let mode = '{';

          for (let source in hostProps) {
            let parts = [{
              mode,
              source,
              dependencies: [source],
              hostProp: true
            }];
            addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
          }
        }

        return noted;
      }
      /**
       * Called to parse text in a template (either attribute values or
       * textContent) into binding metadata.
       *
       * Any overrides of this method should return an array of binding part
       * metadata  representing one or more bindings found in the provided text
       * and any "literal" text in between.  Any non-literal parts will be passed
       * to `_evaluateBinding` when any dependencies change.  The only required
       * fields of each "part" in the returned array are as follows:
       *
       * - `dependencies` - Array containing trigger metadata for each property
       *   that should trigger the binding to update
       * - `literal` - String containing text if the part represents a literal;
       *   in this case no `dependencies` are needed
       *
       * Additional metadata for use by `_evaluateBinding` may be provided in
       * each part object as needed.
       *
       * The default implementation handles the following types of bindings
       * (one or more may be intermixed with literal strings):
       * - Property binding: `[[prop]]`
       * - Path binding: `[[object.prop]]`
       * - Negated property or path bindings: `[[!prop]]` or `[[!object.prop]]`
       * - Two-way property or path bindings (supports negation):
       *   `{{prop}}`, `{{object.prop}}`, `{{!prop}}` or `{{!object.prop}}`
       * - Inline computed method (supports negation):
       *   `[[compute(a, 'literal', b)]]`, `[[!compute(a, 'literal', b)]]`
       *
       * The default implementation uses a regular expression for best
       * performance. However, the regular expression uses a white-list of
       * allowed characters in a data-binding, which causes problems for
       * data-bindings that do use characters not in this white-list.
       *
       * Instead of updating the white-list with all allowed characters,
       * there is a StrictBindingParser (see lib/mixins/strict-binding-parser)
       * that uses a state machine instead. This state machine is able to handle
       * all characters. However, it is slightly less performant, therefore we
       * extracted it into a separate optional mixin.
       *
       * @param {string} text Text to parse from attribute or textContent
       * @param {Object} templateInfo Current template metadata
       * @return {Array<!BindingPart>} Array of binding part metadata
       * @protected
       * @nocollapse
       */


      static _parseBindings(text, templateInfo) {
        let parts = [];
        let lastIndex = 0;
        let m; // Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final"
        // Regex matches:
        //        Iteration 1:  Iteration 2:
        // m[1]: '{{'          '[['
        // m[2]: ''            '!'
        // m[3]: 'prop'        'compute(foo,bar)'

        while ((m = bindingRegex.exec(text)) !== null) {
          // Add literal part
          if (m.index > lastIndex) {
            parts.push({
              literal: text.slice(lastIndex, m.index)
            });
          } // Add binding part


          let mode = m[1][0];
          let negate = Boolean(m[2]);
          let source = m[3].trim();
          let customEvent = false,
              notifyEvent = '',
              colon = -1;

          if (mode == '{' && (colon = source.indexOf('::')) > 0) {
            notifyEvent = source.substring(colon + 2);
            source = source.substring(0, colon);
            customEvent = true;
          }

          let signature = parseMethod(source);
          let dependencies = [];

          if (signature) {
            // Inline computed function
            let {
              args,
              methodName
            } = signature;

            for (let i = 0; i < args.length; i++) {
              let arg = args[i];

              if (!arg.literal) {
                dependencies.push(arg);
              }
            }

            let dynamicFns = templateInfo.dynamicFns;

            if (dynamicFns && dynamicFns[methodName] || signature.static) {
              dependencies.push(methodName);
              signature.dynamicFn = true;
            }
          } else {
            // Property or path
            dependencies.push(source);
          }

          parts.push({
            source,
            mode,
            negate,
            customEvent,
            signature,
            dependencies,
            event: notifyEvent
          });
          lastIndex = bindingRegex.lastIndex;
        } // Add a final literal part


        if (lastIndex && lastIndex < text.length) {
          let literal = text.substring(lastIndex);

          if (literal) {
            parts.push({
              literal: literal
            });
          }
        }

        if (parts.length) {
          return parts;
        } else {
          return null;
        }
      }
      /**
       * Called to evaluate a previously parsed binding part based on a set of
       * one or more changed dependencies.
       *
       * @param {!Polymer_PropertyEffects} inst Element that should be used as
       *     scope for binding dependencies
       * @param {BindingPart} part Binding part metadata
       * @param {string} path Property/path that triggered this effect
       * @param {Object} props Bag of current property changes
       * @param {Object} oldProps Bag of previous values for changed properties
       * @param {boolean} hasPaths True with `props` contains one or more paths
       * @return {*} Value the binding part evaluated to
       * @protected
       * @nocollapse
       */


      static _evaluateBinding(inst, part, path, props, oldProps, hasPaths) {
        let value;

        if (part.signature) {
          value = runMethodEffect(inst, path, props, oldProps, part.signature);
        } else if (path != part.source) {
          value = get(inst, part.source);
        } else {
          if (hasPaths && isPath(path)) {
            value = get(inst, path);
          } else {
            value = inst.__data[path];
          }
        }

        if (part.negate) {
          value = !value;
        }

        return value;
      }

    }

    return PropertyEffects;
  });
  /**
   * Stack for enqueuing client dom created by a host element.
   *
   * By default elements are flushed via `_flushProperties` when
   * `connectedCallback` is called. Elements attach their client dom to
   * themselves at `ready` time which results from this first flush.
   * This provides an ordering guarantee that the client dom an element
   * creates is flushed before the element itself (i.e. client `ready`
   * fires before host `ready`).
   *
   * However, if `_flushProperties` is called *before* an element is connected,
   * as for example `Templatize` does, this ordering guarantee cannot be
   * satisfied because no elements are connected. (Note: Bound elements that
   * receive data do become enqueued clients and are properly ordered but
   * unbound elements are not.)
   *
   * To maintain the desired "client before host" ordering guarantee for this
   * case we rely on the "host stack. Client nodes registers themselves with
   * the creating host element when created. This ensures that all client dom
   * is readied in the proper order, maintaining the desired guarantee.
   *
   * @private
   */

  const hostStack = [];

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Registers a class prototype for telemetry purposes.
   * @param {!PolymerElementConstructor} prototype Element prototype to register
   * @protected
   */


  function register(prototype) {
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Creates a copy of `props` with each property normalized such that
   * upgraded it is an object with at least a type property { type: Type}.
   *
   * @param {!Object} props Properties to normalize
   * @return {!Object} Copy of input `props` with normalized properties that
   * are in the form {type: Type}
   * @private
   */

  function normalizeProperties(props) {
    const output = {};

    for (let p in props) {
      const o = props[p];
      output[p] = typeof o === 'function' ? {
        type: o
      } : o;
    }

    return output;
  }
  /**
   * Mixin that provides a minimal starting point to using the PropertiesChanged
   * mixin by providing a mechanism to declare properties in a static
   * getter (e.g. static get properties() { return { foo: String } }). Changes
   * are reported via the `_propertiesChanged` method.
   *
   * This mixin provides no specific support for rendering. Users are expected
   * to create a ShadowRoot and put content into it and update it in whatever
   * way makes sense. This can be done in reaction to properties changing by
   * implementing `_propertiesChanged`.
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin PropertiesChanged
   * @summary Mixin that provides a minimal starting point for using
   * the PropertiesChanged mixin by providing a declarative `properties` object.
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */


  const PropertiesMixin = dedupingMixin(superClass => {
    /**
     * @constructor
     * @implements {Polymer_PropertiesChanged}
     * @private
     */
    const base = PropertiesChanged(superClass);
    /**
     * Returns the super class constructor for the given class, if it is an
     * instance of the PropertiesMixin.
     *
     * @param {!PropertiesMixinConstructor} constructor PropertiesMixin constructor
     * @return {?PropertiesMixinConstructor} Super class constructor
     */

    function superPropertiesClass(constructor) {
      const superCtor = Object.getPrototypeOf(constructor); // Note, the `PropertiesMixin` class below only refers to the class
      // generated by this call to the mixin; the instanceof test only works
      // because the mixin is deduped and guaranteed only to apply once, hence
      // all constructors in a proto chain will see the same `PropertiesMixin`

      return superCtor.prototype instanceof PropertiesMixin ?
      /** @type {!PropertiesMixinConstructor} */
      superCtor : null;
    }
    /**
     * Returns a memoized version of the `properties` object for the
     * given class. Properties not in object format are converted to at
     * least {type}.
     *
     * @param {PropertiesMixinConstructor} constructor PropertiesMixin constructor
     * @return {Object} Memoized properties object
     */


    function ownProperties(constructor) {
      if (!constructor.hasOwnProperty(JSCompiler_renameProperty('__ownProperties', constructor))) {
        let props = null;

        if (constructor.hasOwnProperty(JSCompiler_renameProperty('properties', constructor))) {
          const properties = constructor.properties;

          if (properties) {
            props = normalizeProperties(properties);
          }
        }

        constructor.__ownProperties = props;
      }

      return constructor.__ownProperties;
    }
    /**
     * @polymer
     * @mixinClass
     * @extends {base}
     * @implements {Polymer_PropertiesMixin}
     * @unrestricted
     */


    class PropertiesMixin extends base {
      /**
       * Implements standard custom elements getter to observes the attributes
       * listed in `properties`.
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */
      static get observedAttributes() {
        if (!this.hasOwnProperty(JSCompiler_renameProperty('__observedAttributes', this))) {
          register(this.prototype);
          const props = this._properties;
          this.__observedAttributes = props ? Object.keys(props).map(p => this.prototype._addPropertyToAttributeMap(p)) : [];
        }

        return this.__observedAttributes;
      }
      /**
       * Finalizes an element definition, including ensuring any super classes
       * are also finalized. This includes ensuring property
       * accessors exist on the element prototype. This method calls
       * `_finalizeClass` to finalize each constructor in the prototype chain.
       * @return {void}
       * @nocollapse
       */


      static finalize() {
        if (!this.hasOwnProperty(JSCompiler_renameProperty('__finalized', this))) {
          const superCtor = superPropertiesClass(
          /** @type {!PropertiesMixinConstructor} */
          this);

          if (superCtor) {
            superCtor.finalize();
          }

          this.__finalized = true;

          this._finalizeClass();
        }
      }
      /**
       * Finalize an element class. This includes ensuring property
       * accessors exist on the element prototype. This method is called by
       * `finalize` and finalizes the class constructor.
       *
       * @protected
       * @nocollapse
       */


      static _finalizeClass() {
        const props = ownProperties(
        /** @type {!PropertiesMixinConstructor} */
        this);

        if (props) {
          /** @type {?} */
          this.createProperties(props);
        }
      }
      /**
       * Returns a memoized version of all properties, including those inherited
       * from super classes. Properties not in object format are converted to
       * at least {type}.
       *
       * @return {Object} Object containing properties for this class
       * @protected
       * @nocollapse
       */


      static get _properties() {
        if (!this.hasOwnProperty(JSCompiler_renameProperty('__properties', this))) {
          const superCtor = superPropertiesClass(
          /** @type {!PropertiesMixinConstructor} */
          this);
          this.__properties = Object.assign({}, superCtor && superCtor._properties, ownProperties(
          /** @type {PropertiesMixinConstructor} */
          this));
        }

        return this.__properties;
      }
      /**
       * Overrides `PropertiesChanged` method to return type specified in the
       * static `properties` object for the given property.
       * @param {string} name Name of property
       * @return {*} Type to which to deserialize attribute
       *
       * @protected
       * @nocollapse
       */


      static typeForProperty(name) {
        const info = this._properties[name];
        return info && info.type;
      }
      /**
       * Overrides `PropertiesChanged` method and adds a call to
       * `finalize` which lazily configures the element's property accessors.
       * @override
       * @return {void}
       */


      _initializeProperties() {
        this.constructor.finalize();

        super._initializeProperties();
      }
      /**
       * Called when the element is added to a document.
       * Calls `_enableProperties` to turn on property system from
       * `PropertiesChanged`.
       * @suppress {missingProperties} Super may or may not implement the callback
       * @return {void}
       * @override
       */


      connectedCallback() {
        if (super.connectedCallback) {
          super.connectedCallback();
        }

        this._enableProperties();
      }
      /**
       * Called when the element is removed from a document
       * @suppress {missingProperties} Super may or may not implement the callback
       * @return {void}
       * @override
       */


      disconnectedCallback() {
        if (super.disconnectedCallback) {
          super.disconnectedCallback();
        }
      }

    }

    return PropertiesMixin;
  });

  /**
   * @fileoverview
   * @suppress {checkPrototypalTypes}
   * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
   * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
   * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
   * Google as part of the polymer project is also subject to an additional IP
   * rights grant found at http://polymer.github.io/PATENTS.txt
   */
  /**
   * Current Polymer version in Semver notation.
   * @type {string} Semver notation of the current version of Polymer.
   */

  const version = '3.4.1';
  const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild'];
  /**
   * Element class mixin that provides the core API for Polymer's meta-programming
   * features including template stamping, data-binding, attribute deserialization,
   * and property change observation.
   *
   * Subclassers may provide the following static getters to return metadata
   * used to configure Polymer's features for the class:
   *
   * - `static get is()`: When the template is provided via a `dom-module`,
   *   users should return the `dom-module` id from a static `is` getter.  If
   *   no template is needed or the template is provided directly via the
   *   `template` getter, there is no need to define `is` for the element.
   *
   * - `static get template()`: Users may provide the template directly (as
   *   opposed to via `dom-module`) by implementing a static `template` getter.
   *   The getter must return an `HTMLTemplateElement`.
   *
   * - `static get properties()`: Should return an object describing
   *   property-related metadata used by Polymer features (key: property name
   *   value: object containing property metadata). Valid keys in per-property
   *   metadata include:
   *   - `type` (String|Number|Object|Array|...): Used by
   *     `attributeChangedCallback` to determine how string-based attributes
   *     are deserialized to JavaScript property values.
   *   - `notify` (boolean): Causes a change in the property to fire a
   *     non-bubbling event called `<property>-changed`. Elements that have
   *     enabled two-way binding to the property use this event to observe changes.
   *   - `readOnly` (boolean): Creates a getter for the property, but no setter.
   *     To set a read-only property, use the private setter method
   *     `_setProperty(property, value)`.
   *   - `observer` (string): Observer method name that will be called when
   *     the property changes. The arguments of the method are
   *     `(value, previousValue)`.
   *   - `computed` (string): String describing method and dependent properties
   *     for computing the value of this property (e.g. `'computeFoo(bar, zot)'`).
   *     Computed properties are read-only by default and can only be changed
   *     via the return value of the computing method.
   *
   * - `static get observers()`: Array of strings describing multi-property
   *   observer methods and their dependent properties (e.g.
   *   `'observeABC(a, b, c)'`).
   *
   * The base class provides default implementations for the following standard
   * custom element lifecycle callbacks; users may override these, but should
   * call the super method to ensure
   * - `constructor`: Run when the element is created or upgraded
   * - `connectedCallback`: Run each time the element is connected to the
   *   document
   * - `disconnectedCallback`: Run each time the element is disconnected from
   *   the document
   * - `attributeChangedCallback`: Run each time an attribute in
   *   `observedAttributes` is set or removed (note: this element's default
   *   `observedAttributes` implementation will automatically return an array
   *   of dash-cased attributes based on `properties`)
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin PropertyEffects
   * @appliesMixin PropertiesMixin
   * @property rootPath {string} Set to the value of `rootPath`,
   *   which defaults to the main document path
   * @property importPath {string} Set to the value of the class's static
   *   `importPath` property, which defaults to the path of this element's
   *   `dom-module` (when `is` is used), but can be overridden for other
   *   import strategies.
   * @summary Element class mixin that provides the core API for Polymer's
   * meta-programming features.
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */

  const ElementMixin = dedupingMixin(base => {
    /**
     * @constructor
     * @implements {Polymer_PropertyEffects}
     * @implements {Polymer_PropertiesMixin}
     * @extends {HTMLElement}
     * @private
     */
    const polymerElementBase = PropertiesMixin(PropertyEffects(base));
    /**
     * Returns a list of properties with default values.
     * This list is created as an optimization since it is a subset of
     * the list returned from `_properties`.
     * This list is used in `_initializeProperties` to set property defaults.
     *
     * @param {PolymerElementConstructor} constructor Element class
     * @return {PolymerElementProperties} Flattened properties for this class
     *   that have default values
     * @private
     */

    function propertyDefaults(constructor) {
      if (!constructor.hasOwnProperty(JSCompiler_renameProperty('__propertyDefaults', constructor))) {
        constructor.__propertyDefaults = null;
        let props = constructor._properties;

        for (let p in props) {
          let info = props[p];

          if ('value' in info) {
            constructor.__propertyDefaults = constructor.__propertyDefaults || {};
            constructor.__propertyDefaults[p] = info;
          }
        }
      }

      return constructor.__propertyDefaults;
    }
    /**
     * Returns a memoized version of the `observers` array.
     * @param {PolymerElementConstructor} constructor Element class
     * @return {Array} Array containing own observers for the given class
     * @protected
     */


    function ownObservers(constructor) {
      if (!constructor.hasOwnProperty(JSCompiler_renameProperty('__ownObservers', constructor))) {
        constructor.__ownObservers = constructor.hasOwnProperty(JSCompiler_renameProperty('observers', constructor)) ?
        /** @type {PolymerElementConstructor} */
        constructor.observers : null;
      }

      return constructor.__ownObservers;
    }
    /**
     * Creates effects for a property.
     *
     * Note, once a property has been set to
     * `readOnly`, `computed`, `reflectToAttribute`, or `notify`
     * these values may not be changed. For example, a subclass cannot
     * alter these settings. However, additional `observers` may be added
     * by subclasses.
     *
     * The info object should contain property metadata as follows:
     *
     * * `type`: {function} type to which an attribute matching the property
     * is deserialized. Note the property is camel-cased from a dash-cased
     * attribute. For example, 'foo-bar' attribute is deserialized to a
     * property named 'fooBar'.
     *
     * * `readOnly`: {boolean} creates a readOnly property and
     * makes a private setter for the private of the form '_setFoo' for a
     * property 'foo',
     *
     * * `computed`: {string} creates a computed property. A computed property
     * is also automatically set to `readOnly: true`. The value is calculated
     * by running a method and arguments parsed from the given string. For
     * example 'compute(foo)' will compute a given property when the
     * 'foo' property changes by executing the 'compute' method. This method
     * must return the computed value.
     *
     * * `reflectToAttribute`: {boolean} If true, the property value is reflected
     * to an attribute of the same name. Note, the attribute is dash-cased
     * so a property named 'fooBar' is reflected as 'foo-bar'.
     *
     * * `notify`: {boolean} sends a non-bubbling notification event when
     * the property changes. For example, a property named 'foo' sends an
     * event named 'foo-changed' with `event.detail` set to the value of
     * the property.
     *
     * * observer: {string} name of a method that runs when the property
     * changes. The arguments of the method are (value, previousValue).
     *
     * Note: Users may want control over modifying property
     * effects via subclassing. For example, a user might want to make a
     * reflectToAttribute property not do so in a subclass. We've chosen to
     * disable this because it leads to additional complication.
     * For example, a readOnly effect generates a special setter. If a subclass
     * disables the effect, the setter would fail unexpectedly.
     * Based on feedback, we may want to try to make effects more malleable
     * and/or provide an advanced api for manipulating them.
     *
     * @param {!PolymerElement} proto Element class prototype to add accessors
     *   and effects to
     * @param {string} name Name of the property.
     * @param {Object} info Info object from which to create property effects.
     * Supported keys:
     * @param {Object} allProps Flattened map of all properties defined in this
     *   element (including inherited properties)
     * @return {void}
     * @private
     */


    function createPropertyFromConfig(proto, name, info, allProps) {
      // computed forces readOnly...
      if (info.computed) {
        info.readOnly = true;
      } // Note, since all computed properties are readOnly, this prevents
      // adding additional computed property effects (which leads to a confusing
      // setup where multiple triggers for setting a property)
      // While we do have `hasComputedEffect` this is set on the property's
      // dependencies rather than itself.


      if (info.computed) {
        if (proto._hasReadOnlyEffect(name)) {
          console.warn(`Cannot redefine computed property '${name}'.`);
        } else {
          proto._createComputedProperty(name, info.computed, allProps);
        }
      }

      if (info.readOnly && !proto._hasReadOnlyEffect(name)) {
        proto._createReadOnlyProperty(name, !info.computed);
      } else if (info.readOnly === false && proto._hasReadOnlyEffect(name)) {
        console.warn(`Cannot make readOnly property '${name}' non-readOnly.`);
      }

      if (info.reflectToAttribute && !proto._hasReflectEffect(name)) {
        proto._createReflectedProperty(name);
      } else if (info.reflectToAttribute === false && proto._hasReflectEffect(name)) {
        console.warn(`Cannot make reflected property '${name}' non-reflected.`);
      }

      if (info.notify && !proto._hasNotifyEffect(name)) {
        proto._createNotifyingProperty(name);
      } else if (info.notify === false && proto._hasNotifyEffect(name)) {
        console.warn(`Cannot make notify property '${name}' non-notify.`);
      } // always add observer


      if (info.observer) {
        proto._createPropertyObserver(name, info.observer, allProps[info.observer]);
      } // always create the mapping from attribute back to property for deserialization.


      proto._addPropertyToAttributeMap(name);
    }
    /**
     * Process all style elements in the element template. Styles with the
     * `include` attribute are processed such that any styles in
     * the associated "style modules" are included in the element template.
     * @param {PolymerElementConstructor} klass Element class
     * @param {!HTMLTemplateElement} template Template to process
     * @param {string} is Name of element
     * @param {string} baseURI Base URI for element
     * @private
     */


    function processElementStyles(klass, template, is, baseURI) {
      if (!builtCSS) {
        const templateStyles = template.content.querySelectorAll('style');
        const stylesWithImports = stylesFromTemplate(template); // insert styles from <link rel="import" type="css"> at the top of the template

        const linkedStyles = stylesFromModuleImports(is);
        const firstTemplateChild = template.content.firstElementChild;

        for (let idx = 0; idx < linkedStyles.length; idx++) {
          let s = linkedStyles[idx];
          s.textContent = klass._processStyleText(s.textContent, baseURI);
          template.content.insertBefore(s, firstTemplateChild);
        } // keep track of the last "concrete" style in the template we have encountered


        let templateStyleIndex = 0; // ensure all gathered styles are actually in this template.

        for (let i = 0; i < stylesWithImports.length; i++) {
          let s = stylesWithImports[i];
          let templateStyle = templateStyles[templateStyleIndex]; // if the style is not in this template, it's been "included" and
          // we put a clone of it in the template before the style that included it

          if (templateStyle !== s) {
            s = s.cloneNode(true);
            templateStyle.parentNode.insertBefore(s, templateStyle);
          } else {
            templateStyleIndex++;
          }

          s.textContent = klass._processStyleText(s.textContent, baseURI);
        }
      }

      if (window.ShadyCSS) {
        window.ShadyCSS.prepareTemplate(template, is);
      } // Support for `adoptedStylesheets` relies on using native Shadow DOM
      // and built CSS. Built CSS is required because runtime transformation of
      // `@apply` is not supported. This is because ShadyCSS relies on being able
      // to update a `style` element in the element template and this is
      // removed when using `adoptedStyleSheets`.
      // Note, it would be more efficient to allow style includes to become
      // separate stylesheets; however, because of `@apply` these are
      // potentially not shareable and sharing the ones that could be shared
      // would require some coordination. To keep it simple, all the includes
      // and styles are collapsed into a single shareable stylesheet.


      if (useAdoptedStyleSheetsWithBuiltCSS && builtCSS && supportsAdoptingStyleSheets$1) {
        // Remove styles in template and make a shareable stylesheet
        const styles = template.content.querySelectorAll('style');

        if (styles) {
          let css = '';
          Array.from(styles).forEach(s => {
            css += s.textContent;
            s.parentNode.removeChild(s);
          });
          klass._styleSheet = new CSSStyleSheet();

          klass._styleSheet.replaceSync(css);
        }
      }
    }
    /**
     * Look up template from dom-module for element
     *
     * @param {string} is Element name to look up
     * @return {?HTMLTemplateElement|undefined} Template found in dom module, or
     *   undefined if not found
     * @protected
     */


    function getTemplateFromDomModule(is) {
      let template = null; // Under strictTemplatePolicy in 3.x+, dom-module lookup is only allowed
      // when opted-in via allowTemplateFromDomModule

      if (is && (!strictTemplatePolicy || allowTemplateFromDomModule)) {
        template =
        /** @type {?HTMLTemplateElement} */
        DomModule.import(is, 'template'); // Under strictTemplatePolicy, require any element with an `is`
        // specified to have a dom-module

        if (strictTemplatePolicy && !template) {
          throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`);
        }
      }

      return template;
    }
    /**
     * @polymer
     * @mixinClass
     * @unrestricted
     * @implements {Polymer_ElementMixin}
     * @extends {polymerElementBase}
     */


    class PolymerElement extends polymerElementBase {
      /**
       * Current Polymer version in Semver notation.
       * @type {string} Semver notation of the current version of Polymer.
       * @nocollapse
       */
      static get polymerElementVersion() {
        return version;
      }
      /**
       * Override of PropertiesMixin _finalizeClass to create observers and
       * find the template.
       * @return {void}
       * @protected
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _finalizeClass() {
        // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()
        polymerElementBase._finalizeClass.call(this);

        const observers = ownObservers(this);

        if (observers) {
          this.createObservers(observers, this._properties);
        }

        this._prepareTemplate();
      }
      /** @nocollapse */


      static _prepareTemplate() {
        // note: create "working" template that is finalized at instance time
        let template =
        /** @type {PolymerElementConstructor} */
        this.template;

        if (template) {
          if (typeof template === 'string') {
            console.error('template getter must return HTMLTemplateElement');
            template = null;
          } else if (!legacyOptimizations) {
            template = template.cloneNode(true);
          }
        }
        /** @override */


        this.prototype._template = template;
      }
      /**
       * Override of PropertiesChanged createProperties to create accessors
       * and property effects for all of the properties.
       * @param {!Object} props .
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createProperties(props) {
        for (let p in props) {
          createPropertyFromConfig(
          /** @type {?} */
          this.prototype, p, props[p], props);
        }
      }
      /**
       * Creates observers for the given `observers` array.
       * Leverages `PropertyEffects` to create observers.
       * @param {Object} observers Array of observer descriptors for
       *   this class
       * @param {Object} dynamicFns Object containing keys for any properties
       *   that are functions and should trigger the effect when the function
       *   reference is changed
       * @return {void}
       * @protected
       * @nocollapse
       */


      static createObservers(observers, dynamicFns) {
        const proto = this.prototype;

        for (let i = 0; i < observers.length; i++) {
          proto._createMethodObserver(observers[i], dynamicFns);
        }
      }
      /**
       * Returns the template that will be stamped into this element's shadow root.
       *
       * If a `static get is()` getter is defined, the default implementation will
       * return the first `<template>` in a `dom-module` whose `id` matches this
       * element's `is` (note that a `_template` property on the class prototype
       * takes precedence over the `dom-module` template, to maintain legacy
       * element semantics; a subclass will subsequently fall back to its super
       * class template if neither a `prototype._template` or a `dom-module` for
       * the class's `is` was found).
       *
       * Users may override this getter to return an arbitrary template
       * (in which case the `is` getter is unnecessary). The template returned
       * must be an `HTMLTemplateElement`.
       *
       * Note that when subclassing, if the super class overrode the default
       * implementation and the subclass would like to provide an alternate
       * template via a `dom-module`, it should override this getter and
       * return `DomModule.import(this.is, 'template')`.
       *
       * If a subclass would like to modify the super class template, it should
       * clone it rather than modify it in place.  If the getter does expensive
       * work such as cloning/modifying a template, it should memoize the
       * template for maximum performance:
       *
       *   let memoizedTemplate;
       *   class MySubClass extends MySuperClass {
       *     static get template() {
       *       if (!memoizedTemplate) {
       *         memoizedTemplate = super.template.cloneNode(true);
       *         let subContent = document.createElement('div');
       *         subContent.textContent = 'This came from MySubClass';
       *         memoizedTemplate.content.appendChild(subContent);
       *       }
       *       return memoizedTemplate;
       *     }
       *   }
       *
       * @return {!HTMLTemplateElement|string} Template to be stamped
       * @nocollapse
       */


      static get template() {
        // Explanation of template-related properties:
        // - constructor.template (this getter): the template for the class.
        //     This can come from the prototype (for legacy elements), from a
        //     dom-module, or from the super class's template (or can be overridden
        //     altogether by the user)
        // - constructor._template: memoized version of constructor.template
        // - prototype._template: working template for the element, which will be
        //     parsed and modified in place. It is a cloned version of
        //     constructor.template, saved in _finalizeClass(). Note that before
        //     this getter is called, for legacy elements this could be from a
        //     _template field on the info object passed to Polymer(), a behavior,
        //     or set in registered(); once the static getter runs, a clone of it
        //     will overwrite it on the prototype as the working template.
        if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) {
          const protoTemplate = this.prototype.hasOwnProperty(JSCompiler_renameProperty('_template', this.prototype)) ? this.prototype._template : undefined;
          this._template = // If user has put template on prototype (e.g. in legacy via registered
          // callback or info object), prefer that first. Note that `null` is
          // used as a sentinel to indicate "no template" and can be used to
          // override a super template, whereas `undefined` is used as a
          // sentinel to mean "fall-back to default template lookup" via
          // dom-module and/or super.template.
          protoTemplate !== undefined ? protoTemplate : // Look in dom-module associated with this element's is
          this.hasOwnProperty(JSCompiler_renameProperty('is', this)) && getTemplateFromDomModule(
          /** @type {PolymerElementConstructor}*/
          this.is) || // Next look for superclass template (call the super impl this
          // way so that `this` points to the superclass)
          Object.getPrototypeOf(
          /** @type {PolymerElementConstructor}*/
          this.prototype).constructor.template;
        }

        return this._template;
      }
      /**
       * Set the template.
       *
       * @param {!HTMLTemplateElement|string} value Template to set.
       * @nocollapse
       */


      static set template(value) {
        this._template = value;
      }
      /**
       * Path matching the url from which the element was imported.
       *
       * This path is used to resolve url's in template style cssText.
       * The `importPath` property is also set on element instances and can be
       * used to create bindings relative to the import path.
       *
       * For elements defined in ES modules, users should implement
       * `static get importMeta() { return import.meta; }`, and the default
       * implementation of `importPath` will  return `import.meta.url`'s path.
       * For elements defined in HTML imports, this getter will return the path
       * to the document containing a `dom-module` element matching this
       * element's static `is` property.
       *
       * Note, this path should contain a trailing `/`.
       *
       * @return {string} The import path for this element class
       * @suppress {missingProperties}
       * @nocollapse
       */


      static get importPath() {
        if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))) {
          const meta = this.importMeta;

          if (meta) {
            this._importPath = pathFromUrl(meta.url);
          } else {
            const module = DomModule.import(
            /** @type {PolymerElementConstructor} */
            this.is);
            this._importPath = module && module.assetpath || Object.getPrototypeOf(
            /** @type {PolymerElementConstructor}*/
            this.prototype).constructor.importPath;
          }
        }

        return this._importPath;
      }

      constructor() {
        super();
        /** @type {HTMLTemplateElement} */

        this._template;
        /** @type {string} */

        this._importPath;
        /** @type {string} */

        this.rootPath;
        /** @type {string} */

        this.importPath;
        /** @type {StampedTemplate | HTMLElement | ShadowRoot} */

        this.root;
        /** @type {!Object<string, !Element>} */

        this.$;
      }
      /**
       * Overrides the default `PropertyAccessors` to ensure class
       * metaprogramming related to property accessors and effects has
       * completed (calls `finalize`).
       *
       * It also initializes any property defaults provided via `value` in
       * `properties` metadata.
       *
       * @return {void}
       * @override
       * @suppress {invalidCasts,missingProperties} go/missingfnprops
       */


      _initializeProperties() {
        this.constructor.finalize(); // note: finalize template when we have access to `localName` to
        // avoid dependence on `is` for polyfilling styling.

        this.constructor._finalizeTemplate(
        /** @type {!HTMLElement} */
        this.localName);

        super._initializeProperties(); // set path defaults


        this.rootPath = rootPath;
        this.importPath = this.constructor.importPath; // apply property defaults...

        let p$ = propertyDefaults(this.constructor);

        if (!p$) {
          return;
        }

        for (let p in p$) {
          let info = p$[p];

          if (this._canApplyPropertyDefault(p)) {
            let value = typeof info.value == 'function' ? info.value.call(this) : info.value; // Set via `_setProperty` if there is an accessor, to enable
            // initializing readOnly property defaults

            if (this._hasAccessor(p)) {
              this._setPendingProperty(p, value, true);
            } else {
              this[p] = value;
            }
          }
        }
      }
      /**
       * Determines if a property dfeault can be applied. For example, this
       * prevents a default from being applied when a property that has no
       * accessor is overridden by its host before upgrade (e.g. via a binding).
       * @override
       * @param {string} property Name of the property
       * @return {boolean} Returns true if the property default can be applied.
       */


      _canApplyPropertyDefault(property) {
        return !this.hasOwnProperty(property);
      }
      /**
       * Gather style text for a style element in the template.
       *
       * @param {string} cssText Text containing styling to process
       * @param {string} baseURI Base URI to rebase CSS paths against
       * @return {string} The processed CSS text
       * @protected
       * @nocollapse
       */


      static _processStyleText(cssText, baseURI) {
        return resolveCss(cssText, baseURI);
      }
      /**
      * Configures an element `proto` to function with a given `template`.
      * The element name `is` and extends `ext` must be specified for ShadyCSS
      * style scoping.
      *
      * @param {string} is Tag name (or type extension name) for this element
      * @return {void}
      * @protected
      * @nocollapse
      */


      static _finalizeTemplate(is) {
        /** @const {HTMLTemplateElement} */
        const template = this.prototype._template;

        if (template && !template.__polymerFinalized) {
          template.__polymerFinalized = true;
          const importPath = this.importPath;
          const baseURI = importPath ? resolveUrl(importPath) : ''; // e.g. support `include="module-name"`, and ShadyCSS

          processElementStyles(this, template, is, baseURI);

          this.prototype._bindTemplate(template);
        }
      }
      /**
       * Provides a default implementation of the standard Custom Elements
       * `connectedCallback`.
       *
       * The default implementation enables the property effects system and
       * flushes any pending properties, and updates shimmed CSS properties
       * when using the ShadyCSS scoping/custom properties polyfill.
       *
       * @override
       * @suppress {missingProperties, invalidCasts} Super may or may not
       *     implement the callback
       * @return {void}
       */


      connectedCallback() {
        if (window.ShadyCSS && this._template) {
          window.ShadyCSS.styleElement(
          /** @type {!HTMLElement} */
          this);
        }

        super.connectedCallback();
      }
      /**
       * Stamps the element template.
       *
       * @return {void}
       * @override
       */


      ready() {
        if (this._template) {
          this.root = this._stampTemplate(this._template);
          this.$ = this.root.$;
        }

        super.ready();
      }
      /**
       * Implements `PropertyEffects`'s `_readyClients` call. Attaches
       * element dom by calling `_attachDom` with the dom stamped from the
       * element's template via `_stampTemplate`. Note that this allows
       * client dom to be attached to the element prior to any observers
       * running.
       *
       * @return {void}
       * @override
       */


      _readyClients() {
        if (this._template) {
          this.root = this._attachDom(
          /** @type {StampedTemplate} */
          this.root);
        } // The super._readyClients here sets the clients initialized flag.
        // We must wait to do this until after client dom is created/attached
        // so that this flag can be checked to prevent notifications fired
        // during this process from being handled before clients are ready.


        super._readyClients();
      }
      /**
       * Attaches an element's stamped dom to itself. By default,
       * this method creates a `shadowRoot` and adds the dom to it.
       * However, this method may be overridden to allow an element
       * to put its dom in another location.
       *
       * @override
       * @throws {Error}
       * @suppress {missingReturn}
       * @param {StampedTemplate} dom to attach to the element.
       * @return {ShadowRoot} node to which the dom has been attached.
       */


      _attachDom(dom) {
        const n = wrap$1(this);

        if (n.attachShadow) {
          if (dom) {
            if (!n.shadowRoot) {
              n.attachShadow({
                mode: 'open',
                shadyUpgradeFragment: dom
              });
              n.shadowRoot.appendChild(dom); // When `adoptedStyleSheets` is supported a stylesheet is made
              // available on the element constructor.

              if (this.constructor._styleSheet) {
                n.shadowRoot.adoptedStyleSheets = [this.constructor._styleSheet];
              }
            }

            if (syncInitialRender && window.ShadyDOM) {
              window.ShadyDOM.flushInitial(n.shadowRoot);
            }

            return n.shadowRoot;
          }

          return null;
        } else {
          throw new Error('ShadowDOM not available. ' + // TODO(sorvell): move to compile-time conditional when supported
          'PolymerElement can create dom as children instead of in ' + 'ShadowDOM by setting `this.root = this;\` before \`ready\`.');
        }
      }
      /**
       * When using the ShadyCSS scoping and custom property shim, causes all
       * shimmed styles in this element (and its subtree) to be updated
       * based on current custom property values.
       *
       * The optional parameter overrides inline custom property styles with an
       * object of properties where the keys are CSS properties, and the values
       * are strings.
       *
       * Example: `this.updateStyles({'--color': 'blue'})`
       *
       * These properties are retained unless a value of `null` is set.
       *
       * Note: This function does not support updating CSS mixins.
       * You can not dynamically change the value of an `@apply`.
       *
       * @override
       * @param {Object=} properties Bag of custom property key/values to
       *   apply to this element.
       * @return {void}
       * @suppress {invalidCasts}
       */


      updateStyles(properties) {
        if (window.ShadyCSS) {
          window.ShadyCSS.styleSubtree(
          /** @type {!HTMLElement} */
          this, properties);
        }
      }
      /**
       * Rewrites a given URL relative to a base URL. The base URL defaults to
       * the original location of the document containing the `dom-module` for
       * this element. This method will return the same URL before and after
       * bundling.
       *
       * Note that this function performs no resolution for URLs that start
       * with `/` (absolute URLs) or `#` (hash identifiers).  For general purpose
       * URL resolution, use `window.URL`.
       *
       * @override
       * @param {string} url URL to resolve.
       * @param {string=} base Optional base URL to resolve against, defaults
       * to the element's `importPath`
       * @return {string} Rewritten URL relative to base
       */


      resolveUrl(url, base) {
        if (!base && this.importPath) {
          base = resolveUrl(this.importPath);
        }

        return resolveUrl(url, base);
      }
      /**
       * Overrides `PropertyEffects` to add map of dynamic functions on
       * template info, for consumption by `PropertyEffects` template binding
       * code. This map determines which method templates should have accessors
       * created for them.
       *
       * @param {!HTMLTemplateElement} template Template
       * @param {!TemplateInfo} templateInfo Template metadata for current template
       * @param {!NodeInfo} nodeInfo Node metadata for current template.
       * @return {boolean} .
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _parseTemplateContent(template, templateInfo, nodeInfo) {
        templateInfo.dynamicFns = templateInfo.dynamicFns || this._properties; // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()

        return polymerElementBase._parseTemplateContent.call(this, template, templateInfo, nodeInfo);
      }
      /**
       * Overrides `PropertyEffects` to warn on use of undeclared properties in
       * template.
       *
       * @param {Object} templateInfo Template metadata to add effect to
       * @param {string} prop Property that should trigger the effect
       * @param {Object=} effect Effect metadata object
       * @return {void}
       * @protected
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */


      static _addTemplatePropertyEffect(templateInfo, prop, effect) {
        // Warn if properties are used in template without being declared.
        // Properties must be listed in `properties` to be included in
        // `observedAttributes` since CE V1 reads that at registration time, and
        // since we want to keep template parsing lazy, we can't automatically
        // add undeclared properties used in templates to `observedAttributes`.
        // The warning is only enabled in `legacyOptimizations` mode, since
        // we don't want to spam existing users who might have adopted the
        // shorthand when attribute deserialization is not important.
        if (legacyWarnings && !(prop in this._properties) && // Methods used in templates with no dependencies (or only literal
        // dependencies) become accessors with template effects; ignore these
        !(effect.info.part.signature && effect.info.part.signature.static) && // Warnings for bindings added to nested templates are handled by
        // templatizer so ignore both the host-to-template bindings
        // (`hostProp`) and TemplateInstance-to-child bindings
        // (`nestedTemplate`)
        !effect.info.part.hostProp && !templateInfo.nestedTemplate) {
          console.warn(`Property '${prop}' used in template but not declared in 'properties'; ` + `attribute will not be observed.`);
        } // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()


        return polymerElementBase._addTemplatePropertyEffect.call(this, templateInfo, prop, effect);
      }

    }

    return PolymerElement;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @summary Collapse multiple callbacks into one invocation after a timer.
   */

  class Debouncer {
    constructor() {
      this._asyncModule = null;
      this._callback = null;
      this._timer = null;
    }
    /**
     * Sets the scheduler; that is, a module with the Async interface,
     * a callback and optional arguments to be passed to the run function
     * from the async module.
     *
     * @param {!AsyncInterface} asyncModule Object with Async interface.
     * @param {function()} callback Callback to run.
     * @return {void}
     */


    setConfig(asyncModule, callback) {
      this._asyncModule = asyncModule;
      this._callback = callback;
      this._timer = this._asyncModule.run(() => {
        this._timer = null;
        debouncerQueue.delete(this);

        this._callback();
      });
    }
    /**
     * Cancels an active debouncer and returns a reference to itself.
     *
     * @return {void}
     */


    cancel() {
      if (this.isActive()) {
        this._cancelAsync(); // Canceling a debouncer removes its spot from the flush queue,
        // so if a debouncer is manually canceled and re-debounced, it
        // will reset its flush order (this is a very minor difference from 1.x)
        // Re-debouncing via the `debounce` API retains the 1.x FIFO flush order


        debouncerQueue.delete(this);
      }
    }
    /**
     * Cancels a debouncer's async callback.
     *
     * @return {void}
     */


    _cancelAsync() {
      if (this.isActive()) {
        this._asyncModule.cancel(
        /** @type {number} */
        this._timer);

        this._timer = null;
      }
    }
    /**
     * Flushes an active debouncer and returns a reference to itself.
     *
     * @return {void}
     */


    flush() {
      if (this.isActive()) {
        this.cancel();

        this._callback();
      }
    }
    /**
     * Returns true if the debouncer is active.
     *
     * @return {boolean} True if active.
     */


    isActive() {
      return this._timer != null;
    }
    /**
     * Creates a debouncer if no debouncer is passed as a parameter
     * or it cancels an active debouncer otherwise. The following
     * example shows how a debouncer can be called multiple times within a
     * microtask and "debounced" such that the provided callback function is
     * called once. Add this method to a custom element:
     *
     * ```js
     * import {microTask} from '@polymer/polymer/lib/utils/async.js';
     * import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js';
     * // ...
     *
     * _debounceWork() {
     *   this._debounceJob = Debouncer.debounce(this._debounceJob,
     *       microTask, () => this._doWork());
     * }
     * ```
     *
     * If the `_debounceWork` method is called multiple times within the same
     * microtask, the `_doWork` function will be called only once at the next
     * microtask checkpoint.
     *
     * Note: In testing it is often convenient to avoid asynchrony. To accomplish
     * this with a debouncer, you can use `enqueueDebouncer` and
     * `flush`. For example, extend the above example by adding
     * `enqueueDebouncer(this._debounceJob)` at the end of the
     * `_debounceWork` method. Then in a test, call `flush` to ensure
     * the debouncer has completed.
     *
     * @param {Debouncer?} debouncer Debouncer object.
     * @param {!AsyncInterface} asyncModule Object with Async interface
     * @param {function()} callback Callback to run.
     * @return {!Debouncer} Returns a debouncer object.
     */


    static debounce(debouncer, asyncModule, callback) {
      if (debouncer instanceof Debouncer) {
        // Cancel the async callback, but leave in debouncerQueue if it was
        // enqueued, to maintain 1.x flush order
        debouncer._cancelAsync();
      } else {
        debouncer = new Debouncer();
      }

      debouncer.setConfig(asyncModule, callback);
      return debouncer;
    }

  }
  let debouncerQueue = new Set();
  /**
   * Adds a `Debouncer` to a list of globally flushable tasks.
   *
   * @param {!Debouncer} debouncer Debouncer to enqueue
   * @return {void}
   */

  const enqueueDebouncer = function (debouncer) {
    debouncerQueue.add(debouncer);
  };
  /**
   * Flushes any enqueued debouncers
   *
   * @return {boolean} Returns whether any debouncers were flushed
   */

  const flushDebouncers = function () {
    const didFlush = Boolean(debouncerQueue.size); // If new debouncers are added while flushing, Set.forEach will ensure
    // newly added ones are also flushed

    debouncerQueue.forEach(debouncer => {
      try {
        debouncer.flush();
      } catch (e) {
        setTimeout(() => {
          throw e;
        });
      }
    });
    return didFlush;
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  let HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
  let GESTURE_KEY = '__polymerGestures';
  let HANDLED_OBJ = '__polymerGesturesHandled';
  let TOUCH_ACTION = '__polymerGesturesTouchAction'; // radius for tap and track

  let TAP_DISTANCE = 25;
  let TRACK_DISTANCE = 5; // number of last N track positions to keep

  let TRACK_LENGTH = 2; // Disabling "mouse" handlers for 2500ms is enough

  let MOUSE_TIMEOUT = 2500;
  let MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click']; // an array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons

  let MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];

  let MOUSE_HAS_BUTTONS = function () {
    try {
      return new MouseEvent('test', {
        buttons: 1
      }).buttons === 1;
    } catch (e) {
      return false;
    }
  }();
  /**
   * @param {string} name Possible mouse event name
   * @return {boolean} true if mouse event, false if not
   */


  function isMouseEvent(name) {
    return MOUSE_EVENTS.indexOf(name) > -1;
  }
  /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
  // check for passive event listeners


  let supportsPassive = false;

  (function () {
    try {
      let opts = Object.defineProperty({}, 'passive', {
        get() {
          supportsPassive = true;
        }

      });
      window.addEventListener('test', null, opts);
      window.removeEventListener('test', null, opts);
    } catch (e) {}
  })();
  /**
   * Generate settings for event listeners, dependant on `passiveTouchGestures`
   *
   * @param {string} eventName Event name to determine if `{passive}` option is
   *   needed
   * @return {{passive: boolean} | undefined} Options to use for addEventListener
   *   and removeEventListener
   */


  function PASSIVE_TOUCH(eventName) {
    if (isMouseEvent(eventName) || eventName === 'touchend') {
      return;
    }

    if (HAS_NATIVE_TA && supportsPassive && passiveTouchGestures) {
      return {
        passive: true
      };
    } else {
      return;
    }
  } // Check for touch-only devices


  let IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/); // keep track of any labels hit by the mouseCanceller

  /** @type {!Array<!HTMLLabelElement>} */

  const clickedLabels = [];
  /** @type {!Object<boolean>} */

  const labellable = {
    'button': true,
    'input': true,
    'keygen': true,
    'meter': true,
    'output': true,
    'textarea': true,
    'progress': true,
    'select': true
  }; // Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute

  /** @type {!Object<boolean>} */

  const canBeDisabled = {
    'button': true,
    'command': true,
    'fieldset': true,
    'input': true,
    'keygen': true,
    'optgroup': true,
    'option': true,
    'select': true,
    'textarea': true
  };
  /**
   * @param {HTMLElement} el Element to check labelling status
   * @return {boolean} element can have labels
   */

  function canBeLabelled(el) {
    return labellable[el.localName] || false;
  }
  /**
   * @param {HTMLElement} el Element that may be labelled.
   * @return {!Array<!HTMLLabelElement>} Relevant label for `el`
   */


  function matchingLabels(el) {
    let labels = Array.prototype.slice.call(
    /** @type {HTMLInputElement} */
    el.labels || []); // IE doesn't have `labels` and Safari doesn't populate `labels`
    // if element is in a shadowroot.
    // In this instance, finding the non-ancestor labels is enough,
    // as the mouseCancellor code will handle ancstor labels

    if (!labels.length) {
      labels = [];
      let root = el.getRootNode(); // if there is an id on `el`, check for all labels with a matching `for` attribute

      if (el.id) {
        let matching = root.querySelectorAll(`label[for = ${el.id}]`);

        for (let i = 0; i < matching.length; i++) {
          labels.push(
          /** @type {!HTMLLabelElement} */
          matching[i]);
        }
      }
    }

    return labels;
  } // touch will make synthetic mouse events
  // `preventDefault` on touchend will cancel them,
  // but this breaks `<input>` focus and link clicks
  // disable mouse handlers for MOUSE_TIMEOUT ms after
  // a touchend to ignore synthetic mouse events


  let mouseCanceller = function (mouseEvent) {
    // Check for sourceCapabilities, used to distinguish synthetic events
    // if mouseEvent did not come from a device that fires touch events,
    // it was made by a real mouse and should be counted
    // http://wicg.github.io/InputDeviceCapabilities/#dom-inputdevicecapabilities-firestouchevents
    let sc = mouseEvent.sourceCapabilities;

    if (sc && !sc.firesTouchEvents) {
      return;
    } // skip synthetic mouse events


    mouseEvent[HANDLED_OBJ] = {
      skip: true
    }; // disable "ghost clicks"

    if (mouseEvent.type === 'click') {
      let clickFromLabel = false;
      let path = getComposedPath(mouseEvent);

      for (let i = 0; i < path.length; i++) {
        if (path[i].nodeType === Node.ELEMENT_NODE) {
          if (path[i].localName === 'label') {
            clickedLabels.push(
            /** @type {!HTMLLabelElement} */
            path[i]);
          } else if (canBeLabelled(
          /** @type {!HTMLElement} */
          path[i])) {
            let ownerLabels = matchingLabels(
            /** @type {!HTMLElement} */
            path[i]); // check if one of the clicked labels is labelling this element

            for (let j = 0; j < ownerLabels.length; j++) {
              clickFromLabel = clickFromLabel || clickedLabels.indexOf(ownerLabels[j]) > -1;
            }
          }
        }

        if (path[i] === POINTERSTATE.mouse.target) {
          return;
        }
      } // if one of the clicked labels was labelling the target element,
      // this is not a ghost click


      if (clickFromLabel) {
        return;
      }

      mouseEvent.preventDefault();
      mouseEvent.stopPropagation();
    }
  };
  /**
   * @param {boolean=} setup True to add, false to remove.
   * @return {void}
   */


  function setupTeardownMouseCanceller(setup) {
    let events = IS_TOUCH_ONLY ? ['click'] : MOUSE_EVENTS;

    for (let i = 0, en; i < events.length; i++) {
      en = events[i];

      if (setup) {
        // reset clickLabels array
        clickedLabels.length = 0;
        document.addEventListener(en, mouseCanceller, true);
      } else {
        document.removeEventListener(en, mouseCanceller, true);
      }
    }
  }

  function ignoreMouse(e) {

    if (!POINTERSTATE.mouse.mouseIgnoreJob) {
      setupTeardownMouseCanceller(true);
    }

    let unset = function () {
      setupTeardownMouseCanceller();
      POINTERSTATE.mouse.target = null;
      POINTERSTATE.mouse.mouseIgnoreJob = null;
    };

    POINTERSTATE.mouse.target = getComposedPath(e)[0];
    POINTERSTATE.mouse.mouseIgnoreJob = Debouncer.debounce(POINTERSTATE.mouse.mouseIgnoreJob, timeOut.after(MOUSE_TIMEOUT), unset);
  }
  /**
   * @param {MouseEvent} ev event to test for left mouse button down
   * @return {boolean} has left mouse button down
   */


  function hasLeftMouseButton(ev) {
    let type = ev.type; // exit early if the event is not a mouse event

    if (!isMouseEvent(type)) {
      return false;
    } // ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons)
    // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button)


    if (type === 'mousemove') {
      // allow undefined for testing events
      let buttons = ev.buttons === undefined ? 1 : ev.buttons;

      if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) {
        buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
      } // buttons is a bitmask, check that the left button bit is set (1)


      return Boolean(buttons & 1);
    } else {
      // allow undefined for testing events
      let button = ev.button === undefined ? 0 : ev.button; // ev.button is 0 in mousedown/mouseup/click for left button activation

      return button === 0;
    }
  }

  function isSyntheticClick(ev) {
    if (ev.type === 'click') {
      // ev.detail is 0 for HTMLElement.click in most browsers
      if (ev.detail === 0) {
        return true;
      } // in the worst case, check that the x/y position of the click is within
      // the bounding box of the target of the event
      // Thanks IE 10 >:(


      let t = _findOriginalTarget(ev); // make sure the target of the event is an element so we can use getBoundingClientRect,
      // if not, just assume it is a synthetic click


      if (!t.nodeType ||
      /** @type {Element} */
      t.nodeType !== Node.ELEMENT_NODE) {
        return true;
      }

      let bcr =
      /** @type {Element} */
      t.getBoundingClientRect(); // use page x/y to account for scrolling

      let x = ev.pageX,
          y = ev.pageY; // ev is a synthetic click if the position is outside the bounding box of the target

      return !(x >= bcr.left && x <= bcr.right && y >= bcr.top && y <= bcr.bottom);
    }

    return false;
  }

  let POINTERSTATE = {
    mouse: {
      target: null,
      mouseIgnoreJob: null
    },
    touch: {
      x: 0,
      y: 0,
      id: -1,
      scrollDecided: false
    }
  };

  function firstTouchAction(ev) {
    let ta = 'auto';
    let path = getComposedPath(ev);

    for (let i = 0, n; i < path.length; i++) {
      n = path[i];

      if (n[TOUCH_ACTION]) {
        ta = n[TOUCH_ACTION];
        break;
      }
    }

    return ta;
  }

  function trackDocument(stateObj, movefn, upfn) {
    stateObj.movefn = movefn;
    stateObj.upfn = upfn;
    document.addEventListener('mousemove', movefn);
    document.addEventListener('mouseup', upfn);
  }

  function untrackDocument(stateObj) {
    document.removeEventListener('mousemove', stateObj.movefn);
    document.removeEventListener('mouseup', stateObj.upfn);
    stateObj.movefn = null;
    stateObj.upfn = null;
  }

  {
    // use a document-wide touchend listener to start the ghost-click prevention mechanism
    // Use passive event listeners, if supported, to not affect scrolling performance
    document.addEventListener('touchend', ignoreMouse, supportsPassive ? {
      passive: true
    } : false);
  }
  /**
   * Returns the composedPath for the given event.
   * @param {Event} event to process
   * @return {!Array<!EventTarget>} Path of the event
   */


  const getComposedPath = window.ShadyDOM && window.ShadyDOM.noPatch ? window.ShadyDOM.composedPath : event => event.composedPath && event.composedPath() || [];
  /** @type {!Object<string, !GestureRecognizer>} */

  const gestures = {};
  /** @type {!Array<!GestureRecognizer>} */

  const recognizers = [];
  /**
   * Finds the element rendered on the screen at the provided coordinates.
   *
   * Similar to `document.elementFromPoint`, but pierces through
   * shadow roots.
   *
   * @param {number} x Horizontal pixel coordinate
   * @param {number} y Vertical pixel coordinate
   * @return {Element} Returns the deepest shadowRoot inclusive element
   * found at the screen position given.
   */

  function deepTargetFind(x, y) {
    let node = document.elementFromPoint(x, y);
    let next = node; // this code path is only taken when native ShadowDOM is used
    // if there is a shadowroot, it may have a node at x/y
    // if there is not a shadowroot, exit the loop

    while (next && next.shadowRoot && !window.ShadyDOM) {
      // if there is a node at x/y in the shadowroot, look deeper
      let oldNext = next;
      next = next.shadowRoot.elementFromPoint(x, y); // on Safari, elementFromPoint may return the shadowRoot host

      if (oldNext === next) {
        break;
      }

      if (next) {
        node = next;
      }
    }

    return node;
  }
  /**
   * a cheaper check than ev.composedPath()[0];
   *
   * @private
   * @param {Event|Touch} ev Event.
   * @return {EventTarget} Returns the event target.
   */

  function _findOriginalTarget(ev) {
    const path = getComposedPath(
    /** @type {?Event} */
    ev); // It shouldn't be, but sometimes path is empty (window on Safari).

    return path.length > 0 ? path[0] : ev.target;
  }
  /**
   * @private
   * @param {Event} ev Event.
   * @return {void}
   */


  function _handleNative(ev) {
    let handled;
    let type = ev.type;
    let node = ev.currentTarget;
    let gobj = node[GESTURE_KEY];

    if (!gobj) {
      return;
    }

    let gs = gobj[type];

    if (!gs) {
      return;
    }

    if (!ev[HANDLED_OBJ]) {
      ev[HANDLED_OBJ] = {};

      if (type.slice(0, 5) === 'touch') {
        ev =
        /** @type {TouchEvent} */
        ev; // eslint-disable-line no-self-assign

        let t = ev.changedTouches[0];

        if (type === 'touchstart') {
          // only handle the first finger
          if (ev.touches.length === 1) {
            POINTERSTATE.touch.id = t.identifier;
          }
        }

        if (POINTERSTATE.touch.id !== t.identifier) {
          return;
        }

        if (!HAS_NATIVE_TA) {
          if (type === 'touchstart' || type === 'touchmove') {
            _handleTouchAction(ev);
          }
        }
      }
    }

    handled = ev[HANDLED_OBJ]; // used to ignore synthetic mouse events

    if (handled.skip) {
      return;
    } // reset recognizer state


    for (let i = 0, r; i < recognizers.length; i++) {
      r = recognizers[i];

      if (gs[r.name] && !handled[r.name]) {
        if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) {
          r.reset();
        }
      }
    } // enforce gesture recognizer order


    for (let i = 0, r; i < recognizers.length; i++) {
      r = recognizers[i];

      if (gs[r.name] && !handled[r.name]) {
        handled[r.name] = true;
        r[type](ev);
      }
    }
  }
  /**
   * @private
   * @param {TouchEvent} ev Event.
   * @return {void}
   */


  function _handleTouchAction(ev) {
    let t = ev.changedTouches[0];
    let type = ev.type;

    if (type === 'touchstart') {
      POINTERSTATE.touch.x = t.clientX;
      POINTERSTATE.touch.y = t.clientY;
      POINTERSTATE.touch.scrollDecided = false;
    } else if (type === 'touchmove') {
      if (POINTERSTATE.touch.scrollDecided) {
        return;
      }

      POINTERSTATE.touch.scrollDecided = true;
      let ta = firstTouchAction(ev);
      let shouldPrevent = false;
      let dx = Math.abs(POINTERSTATE.touch.x - t.clientX);
      let dy = Math.abs(POINTERSTATE.touch.y - t.clientY);

      if (!ev.cancelable) ; else if (ta === 'none') {
        shouldPrevent = true;
      } else if (ta === 'pan-x') {
        shouldPrevent = dy > dx;
      } else if (ta === 'pan-y') {
        shouldPrevent = dx > dy;
      }

      if (shouldPrevent) {
        ev.preventDefault();
      } else {
        prevent('track');
      }
    }
  }
  /**
   * Adds an event listener to a node for the given gesture type.
   *
   * @param {!EventTarget} node Node to add listener on
   * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
   * @param {!function(!Event):void} handler Event listener function to call
   * @return {boolean} Returns true if a gesture event listener was added.
   */


  function addListener(node, evType, handler) {
    if (gestures[evType]) {
      _add(node, evType, handler);

      return true;
    }

    return false;
  }
  /**
   * Removes an event listener from a node for the given gesture type.
   *
   * @param {!EventTarget} node Node to remove listener from
   * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
   * @param {!function(!Event):void} handler Event listener function previously passed to
   *  `addListener`.
   * @return {boolean} Returns true if a gesture event listener was removed.
   */

  function removeListener(node, evType, handler) {
    if (gestures[evType]) {
      _remove(node, evType, handler);

      return true;
    }

    return false;
  }
  /**
   * automate the event listeners for the native events
   *
   * @private
   * @param {!EventTarget} node Node on which to add the event.
   * @param {string} evType Event type to add.
   * @param {function(!Event)} handler Event handler function.
   * @return {void}
   */

  function _add(node, evType, handler) {
    let recognizer = gestures[evType];
    let deps = recognizer.deps;
    let name = recognizer.name;
    let gobj = node[GESTURE_KEY];

    if (!gobj) {
      node[GESTURE_KEY] = gobj = {};
    }

    for (let i = 0, dep, gd; i < deps.length; i++) {
      dep = deps[i]; // don't add mouse handlers on iOS because they cause gray selection overlays

      if (IS_TOUCH_ONLY && isMouseEvent(dep) && dep !== 'click') {
        continue;
      }

      gd = gobj[dep];

      if (!gd) {
        gobj[dep] = gd = {
          _count: 0
        };
      }

      if (gd._count === 0) {
        node.addEventListener(dep, _handleNative, PASSIVE_TOUCH(dep));
      }

      gd[name] = (gd[name] || 0) + 1;
      gd._count = (gd._count || 0) + 1;
    }

    node.addEventListener(evType, handler);

    if (recognizer.touchAction) {
      setTouchAction(node, recognizer.touchAction);
    }
  }
  /**
   * automate event listener removal for native events
   *
   * @private
   * @param {!EventTarget} node Node on which to remove the event.
   * @param {string} evType Event type to remove.
   * @param {function(!Event): void} handler Event handler function.
   * @return {void}
   */


  function _remove(node, evType, handler) {
    let recognizer = gestures[evType];
    let deps = recognizer.deps;
    let name = recognizer.name;
    let gobj = node[GESTURE_KEY];

    if (gobj) {
      for (let i = 0, dep, gd; i < deps.length; i++) {
        dep = deps[i];
        gd = gobj[dep];

        if (gd && gd[name]) {
          gd[name] = (gd[name] || 1) - 1;
          gd._count = (gd._count || 1) - 1;

          if (gd._count === 0) {
            node.removeEventListener(dep, _handleNative, PASSIVE_TOUCH(dep));
          }
        }
      }
    }

    node.removeEventListener(evType, handler);
  }
  /**
   * Registers a new gesture event recognizer for adding new custom
   * gesture event types.
   *
   * @param {!GestureRecognizer} recog Gesture recognizer descriptor
   * @return {void}
   */


  function register$1(recog) {
    recognizers.push(recog);

    for (let i = 0; i < recog.emits.length; i++) {
      gestures[recog.emits[i]] = recog;
    }
  }
  /**
   * @private
   * @param {string} evName Event name.
   * @return {Object} Returns the gesture for the given event name.
   */

  function _findRecognizerByEvent(evName) {
    for (let i = 0, r; i < recognizers.length; i++) {
      r = recognizers[i];

      for (let j = 0, n; j < r.emits.length; j++) {
        n = r.emits[j];

        if (n === evName) {
          return r;
        }
      }
    }

    return null;
  }
  /**
   * Sets scrolling direction on node.
   *
   * This value is checked on first move, thus it should be called prior to
   * adding event listeners.
   *
   * @param {!EventTarget} node Node to set touch action setting on
   * @param {string} value Touch action value
   * @return {void}
   */


  function setTouchAction(node, value) {
    if (HAS_NATIVE_TA && node instanceof HTMLElement) {
      // NOTE: add touchAction async so that events can be added in
      // custom element constructors. Otherwise we run afoul of custom
      // elements restriction against settings attributes (style) in the
      // constructor.
      microTask.run(() => {
        node.style.touchAction = value;
      });
    }

    node[TOUCH_ACTION] = value;
  }
  /**
   * Dispatches an event on the `target` element of `type` with the given
   * `detail`.
   * @private
   * @param {!EventTarget} target The element on which to fire an event.
   * @param {string} type The type of event to fire.
   * @param {!Object=} detail The detail object to populate on the event.
   * @return {void}
   */

  function _fire(target, type, detail) {
    let ev = new Event(type, {
      bubbles: true,
      cancelable: true,
      composed: true
    });
    ev.detail = detail;
    wrap$1(
    /** @type {!Node} */
    target).dispatchEvent(ev); // forward `preventDefault` in a clean way

    if (ev.defaultPrevented) {
      let preventer = detail.preventer || detail.sourceEvent;

      if (preventer && preventer.preventDefault) {
        preventer.preventDefault();
      }
    }
  }
  /**
   * Prevents the dispatch and default action of the given event name.
   *
   * @param {string} evName Event name.
   * @return {void}
   */


  function prevent(evName) {
    let recognizer = _findRecognizerByEvent(evName);

    if (recognizer.info) {
      recognizer.info.prevent = true;
    }
  }
  /* eslint-disable valid-jsdoc */

  register$1({
    name: 'downup',
    deps: ['mousedown', 'touchstart', 'touchend'],
    flow: {
      start: ['mousedown', 'touchstart'],
      end: ['mouseup', 'touchend']
    },
    emits: ['down', 'up'],
    info: {
      movefn: null,
      upfn: null
    },

    /**
     * @this {GestureRecognizer}
     * @return {void}
     */
    reset: function () {
      untrackDocument(this.info);
    },

    /**
     * @this {GestureRecognizer}
     * @param {MouseEvent} e
     * @return {void}
     */
    mousedown: function (e) {
      if (!hasLeftMouseButton(e)) {
        return;
      }

      let t = _findOriginalTarget(e);

      let self = this;

      let movefn = function movefn(e) {
        if (!hasLeftMouseButton(e)) {
          downupFire('up', t, e);
          untrackDocument(self.info);
        }
      };

      let upfn = function upfn(e) {
        if (hasLeftMouseButton(e)) {
          downupFire('up', t, e);
        }

        untrackDocument(self.info);
      };

      trackDocument(this.info, movefn, upfn);
      downupFire('down', t, e);
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchstart: function (e) {
      downupFire('down', _findOriginalTarget(e), e.changedTouches[0], e);
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchend: function (e) {
      downupFire('up', _findOriginalTarget(e), e.changedTouches[0], e);
    }
  });
  /**
   * @param {string} type
   * @param {EventTarget} target
   * @param {Event|Touch} event
   * @param {Event=} preventer
   * @return {void}
   */

  function downupFire(type, target, event, preventer) {
    if (!target) {
      return;
    }

    _fire(target, type, {
      x: event.clientX,
      y: event.clientY,
      sourceEvent: event,
      preventer: preventer,
      prevent: function (e) {
        return prevent(e);
      }
    });
  }

  register$1({
    name: 'track',
    touchAction: 'none',
    deps: ['mousedown', 'touchstart', 'touchmove', 'touchend'],
    flow: {
      start: ['mousedown', 'touchstart'],
      end: ['mouseup', 'touchend']
    },
    emits: ['track'],
    info: {
      x: 0,
      y: 0,
      state: 'start',
      started: false,
      moves: [],

      /** @this {GestureInfo} */
      addMove: function (move) {
        if (this.moves.length > TRACK_LENGTH) {
          this.moves.shift();
        }

        this.moves.push(move);
      },
      movefn: null,
      upfn: null,
      prevent: false
    },

    /**
     * @this {GestureRecognizer}
     * @return {void}
     */
    reset: function () {
      this.info.state = 'start';
      this.info.started = false;
      this.info.moves = [];
      this.info.x = 0;
      this.info.y = 0;
      this.info.prevent = false;
      untrackDocument(this.info);
    },

    /**
     * @this {GestureRecognizer}
     * @param {MouseEvent} e
     * @return {void}
     */
    mousedown: function (e) {
      if (!hasLeftMouseButton(e)) {
        return;
      }

      let t = _findOriginalTarget(e);

      let self = this;

      let movefn = function movefn(e) {
        let x = e.clientX,
            y = e.clientY;

        if (trackHasMovedEnough(self.info, x, y)) {
          // first move is 'start', subsequent moves are 'move', mouseup is 'end'
          self.info.state = self.info.started ? e.type === 'mouseup' ? 'end' : 'track' : 'start';

          if (self.info.state === 'start') {
            // if and only if tracking, always prevent tap
            prevent('tap');
          }

          self.info.addMove({
            x: x,
            y: y
          });

          if (!hasLeftMouseButton(e)) {
            // always fire "end"
            self.info.state = 'end';
            untrackDocument(self.info);
          }

          if (t) {
            trackFire(self.info, t, e);
          }

          self.info.started = true;
        }
      };

      let upfn = function upfn(e) {
        if (self.info.started) {
          movefn(e);
        } // remove the temporary listeners


        untrackDocument(self.info);
      }; // add temporary document listeners as mouse retargets


      trackDocument(this.info, movefn, upfn);
      this.info.x = e.clientX;
      this.info.y = e.clientY;
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchstart: function (e) {
      let ct = e.changedTouches[0];
      this.info.x = ct.clientX;
      this.info.y = ct.clientY;
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchmove: function (e) {
      let t = _findOriginalTarget(e);

      let ct = e.changedTouches[0];
      let x = ct.clientX,
          y = ct.clientY;

      if (trackHasMovedEnough(this.info, x, y)) {
        if (this.info.state === 'start') {
          // if and only if tracking, always prevent tap
          prevent('tap');
        }

        this.info.addMove({
          x: x,
          y: y
        });
        trackFire(this.info, t, ct);
        this.info.state = 'track';
        this.info.started = true;
      }
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchend: function (e) {
      let t = _findOriginalTarget(e);

      let ct = e.changedTouches[0]; // only trackend if track was started and not aborted

      if (this.info.started) {
        // reset started state on up
        this.info.state = 'end';
        this.info.addMove({
          x: ct.clientX,
          y: ct.clientY
        });
        trackFire(this.info, t, ct);
      }
    }
  });
  /**
   * @param {!GestureInfo} info
   * @param {number} x
   * @param {number} y
   * @return {boolean}
   */

  function trackHasMovedEnough(info, x, y) {
    if (info.prevent) {
      return false;
    }

    if (info.started) {
      return true;
    }

    let dx = Math.abs(info.x - x);
    let dy = Math.abs(info.y - y);
    return dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE;
  }
  /**
   * @param {!GestureInfo} info
   * @param {?EventTarget} target
   * @param {Touch} touch
   * @return {void}
   */


  function trackFire(info, target, touch) {
    if (!target) {
      return;
    }

    let secondlast = info.moves[info.moves.length - 2];
    let lastmove = info.moves[info.moves.length - 1];
    let dx = lastmove.x - info.x;
    let dy = lastmove.y - info.y;
    let ddx,
        ddy = 0;

    if (secondlast) {
      ddx = lastmove.x - secondlast.x;
      ddy = lastmove.y - secondlast.y;
    }

    _fire(target, 'track', {
      state: info.state,
      x: touch.clientX,
      y: touch.clientY,
      dx: dx,
      dy: dy,
      ddx: ddx,
      ddy: ddy,
      sourceEvent: touch,
      hover: function () {
        return deepTargetFind(touch.clientX, touch.clientY);
      }
    });
  }

  register$1({
    name: 'tap',
    deps: ['mousedown', 'click', 'touchstart', 'touchend'],
    flow: {
      start: ['mousedown', 'touchstart'],
      end: ['click', 'touchend']
    },
    emits: ['tap'],
    info: {
      x: NaN,
      y: NaN,
      prevent: false
    },

    /**
     * @this {GestureRecognizer}
     * @return {void}
     */
    reset: function () {
      this.info.x = NaN;
      this.info.y = NaN;
      this.info.prevent = false;
    },

    /**
     * @this {GestureRecognizer}
     * @param {MouseEvent} e
     * @return {void}
     */
    mousedown: function (e) {
      if (hasLeftMouseButton(e)) {
        this.info.x = e.clientX;
        this.info.y = e.clientY;
      }
    },

    /**
     * @this {GestureRecognizer}
     * @param {MouseEvent} e
     * @return {void}
     */
    click: function (e) {
      if (hasLeftMouseButton(e)) {
        trackForward(this.info, e);
      }
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchstart: function (e) {
      const touch = e.changedTouches[0];
      this.info.x = touch.clientX;
      this.info.y = touch.clientY;
    },

    /**
     * @this {GestureRecognizer}
     * @param {TouchEvent} e
     * @return {void}
     */
    touchend: function (e) {
      trackForward(this.info, e.changedTouches[0], e);
    }
  });
  /**
   * @param {!GestureInfo} info
   * @param {Event | Touch} e
   * @param {Event=} preventer
   * @return {void}
   */

  function trackForward(info, e, preventer) {
    let dx = Math.abs(e.clientX - info.x);
    let dy = Math.abs(e.clientY - info.y); // find original target from `preventer` for TouchEvents, or `e` for MouseEvents

    let t = _findOriginalTarget(preventer || e);

    if (!t || canBeDisabled[
    /** @type {!HTMLElement} */
    t.localName] && t.hasAttribute('disabled')) {
      return;
    } // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`


    if (isNaN(dx) || isNaN(dy) || dx <= TAP_DISTANCE && dy <= TAP_DISTANCE || isSyntheticClick(e)) {
      // prevent taps from being generated if an event has canceled them
      if (!info.prevent) {
        _fire(t, 'tap', {
          x: e.clientX,
          y: e.clientY,
          sourceEvent: e,
          preventer: preventer
        });
      }
    }
  }
  /* eslint-enable valid-jsdoc */

  /** @deprecated */


  const findOriginalTarget = _findOriginalTarget;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Element class mixin that provides API for adding Polymer's cross-platform
   * gesture events to nodes.
   *
   * The API is designed to be compatible with override points implemented
   * in `TemplateStamp` such that declarative event listeners in
   * templates will support gesture events when this mixin is applied along with
   * `TemplateStamp`.
   *
   * @mixinFunction
   * @polymer
   * @summary Element class mixin that provides API for adding Polymer's
   *   cross-platform gesture events to nodes
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */

  const GestureEventListeners = dedupingMixin(superClass => {
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_GestureEventListeners}
     */
    class GestureEventListeners extends superClass {
      /**
       * Add the event listener to the node if it is a gestures event.
       *
       * @param {!EventTarget} node Node to add event listener to
       * @param {string} eventName Name of event
       * @param {function(!Event):void} handler Listener function to add
       * @return {void}
       * @override
       */
      _addEventListenerToNode(node, eventName, handler) {
        if (!addListener(node, eventName, handler)) {
          super._addEventListenerToNode(node, eventName, handler);
        }
      }
      /**
       * Remove the event listener to the node if it is a gestures event.
       *
       * @param {!EventTarget} node Node to remove event listener from
       * @param {string} eventName Name of event
       * @param {function(!Event):void} handler Listener function to remove
       * @return {void}
       * @override
       */


      _removeEventListenerFromNode(node, eventName, handler) {
        if (!removeListener(node, eventName, handler)) {
          super._removeEventListenerFromNode(node, eventName, handler);
        }
      }

    }

    return GestureEventListeners;
  });

  /**
   * @fileoverview
   * @suppress {checkPrototypalTypes}
   * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
   * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
   * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
   * Google as part of the polymer project is also subject to an additional IP
   * rights grant found at http://polymer.github.io/PATENTS.txt
   */
  const HOST_DIR = /:host\(:dir\((ltr|rtl)\)\)/g;
  const HOST_DIR_REPLACMENT = ':host([dir="$1"])';
  const EL_DIR = /([\s\w-#\.\[\]\*]*):dir\((ltr|rtl)\)/g;
  const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1';
  const DIR_CHECK = /:dir\((?:ltr|rtl)\)/;
  const SHIM_SHADOW = Boolean(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
  /**
   * @type {!Array<!Polymer_DirMixin>}
   */

  const DIR_INSTANCES = [];
  /** @type {?MutationObserver} */

  let observer = null;
  let documentDir = '';

  function getRTL() {
    documentDir = document.documentElement.getAttribute('dir');
  }
  /**
   * @param {!Polymer_DirMixin} instance Instance to set RTL status on
   */


  function setRTL(instance) {
    if (!instance.__autoDirOptOut) {
      const el =
      /** @type {!HTMLElement} */
      instance;
      el.setAttribute('dir', documentDir);
    }
  }

  function updateDirection() {
    getRTL();
    documentDir = document.documentElement.getAttribute('dir');

    for (let i = 0; i < DIR_INSTANCES.length; i++) {
      setRTL(DIR_INSTANCES[i]);
    }
  }

  function takeRecords() {
    if (observer && observer.takeRecords().length) {
      updateDirection();
    }
  }
  /**
   * Element class mixin that allows elements to use the `:dir` CSS Selector to
   * have text direction specific styling.
   *
   * With this mixin, any stylesheet provided in the template will transform
   * `:dir` into `:host([dir])` and sync direction with the page via the
   * element's `dir` attribute.
   *
   * Elements can opt out of the global page text direction by setting the `dir`
   * attribute directly in `ready()` or in HTML.
   *
   * Caveats:
   * - Applications must set `<html dir="ltr">` or `<html dir="rtl">` to sync
   *   direction
   * - Automatic left-to-right or right-to-left styling is sync'd with the
   *   `<html>` element only.
   * - Changing `dir` at runtime is supported.
   * - Opting out of the global direction styling is permanent
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin PropertyAccessors
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */


  const DirMixin = dedupingMixin(base => {
    if (!SHIM_SHADOW) {
      if (!observer) {
        getRTL();
        observer = new MutationObserver(updateDirection);
        observer.observe(document.documentElement, {
          attributes: true,
          attributeFilter: ['dir']
        });
      }
    }
    /**
     * @constructor
     * @implements {Polymer_PropertyAccessors}
     * @private
     */


    const elementBase = PropertyAccessors(base);
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_DirMixin}
     */

    class Dir extends elementBase {
      /**
       * @param {string} cssText .
       * @param {string} baseURI .
       * @return {string} .
       * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
       * @nocollapse
       */
      static _processStyleText(cssText, baseURI) {
        // TODO(https://github.com/google/closure-compiler/issues/3240):
        //     Change back to just super.methodCall()
        cssText = elementBase._processStyleText.call(this, cssText, baseURI);

        if (!SHIM_SHADOW && DIR_CHECK.test(cssText)) {
          cssText = this._replaceDirInCssText(cssText);
          this.__activateDir = true;
        }

        return cssText;
      }
      /**
       * Replace `:dir` in the given CSS text
       *
       * @param {string} text CSS text to replace DIR
       * @return {string} Modified CSS
       * @nocollapse
       */


      static _replaceDirInCssText(text) {
        let replacedText = text;
        replacedText = replacedText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
        replacedText = replacedText.replace(EL_DIR, EL_DIR_REPLACMENT);
        return replacedText;
      }

      constructor() {
        super();
        /** @type {boolean} */

        this.__autoDirOptOut = false;
      }
      /**
       * @override
       * @suppress {invalidCasts} Closure doesn't understand that `this` is an
       *     HTMLElement
       * @return {void}
       */


      ready() {
        super.ready();
        this.__autoDirOptOut =
        /** @type {!HTMLElement} */
        this.hasAttribute('dir');
      }
      /**
       * @override
       * @suppress {missingProperties} If it exists on elementBase, it can be
       *   super'd
       * @return {void}
       */


      connectedCallback() {
        if (elementBase.prototype.connectedCallback) {
          super.connectedCallback();
        }

        if (this.constructor.__activateDir) {
          takeRecords();
          DIR_INSTANCES.push(this);
          setRTL(this);
        }
      }
      /**
       * @override
       * @suppress {missingProperties} If it exists on elementBase, it can be
       *   super'd
       * @return {void}
       */


      disconnectedCallback() {
        if (elementBase.prototype.disconnectedCallback) {
          super.disconnectedCallback();
        }

        if (this.constructor.__activateDir) {
          const idx = DIR_INSTANCES.indexOf(this);

          if (idx > -1) {
            DIR_INSTANCES.splice(idx, 1);
          }
        }
      }

    }

    Dir.__activateDir = false;
    return Dir;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  function resolve() {
    document.body.removeAttribute('unresolved');
  }

  if (document.readyState === 'interactive' || document.readyState === 'complete') {
    resolve();
  } else {
    window.addEventListener('DOMContentLoaded', resolve);
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  function newSplice(index, removed, addedCount) {
    return {
      index: index,
      removed: removed,
      addedCount: addedCount
    };
  }

  const EDIT_LEAVE = 0;
  const EDIT_UPDATE = 1;
  const EDIT_ADD = 2;
  const EDIT_DELETE = 3; // Note: This function is *based* on the computation of the Levenshtein
  // "edit" distance. The one change is that "updates" are treated as two
  // edits - not one. With Array splices, an update is really a delete
  // followed by an add. By retaining this, we optimize for "keeping" the
  // maximum array items in the original array. For example:
  //
  //   'xxxx123' -> '123yyyy'
  //
  // With 1-edit updates, the shortest path would be just to update all seven
  // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
  // leaves the substring '123' intact.

  function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
    // "Deletion" columns
    let rowCount = oldEnd - oldStart + 1;
    let columnCount = currentEnd - currentStart + 1;
    let distances = new Array(rowCount); // "Addition" rows. Initialize null column.

    for (let i = 0; i < rowCount; i++) {
      distances[i] = new Array(columnCount);
      distances[i][0] = i;
    } // Initialize null row


    for (let j = 0; j < columnCount; j++) distances[0][j] = j;

    for (let i = 1; i < rowCount; i++) {
      for (let j = 1; j < columnCount; j++) {
        if (equals(current[currentStart + j - 1], old[oldStart + i - 1])) distances[i][j] = distances[i - 1][j - 1];else {
          let north = distances[i - 1][j] + 1;
          let west = distances[i][j - 1] + 1;
          distances[i][j] = north < west ? north : west;
        }
      }
    }

    return distances;
  } // This starts at the final weight, and walks "backward" by finding
  // the minimum previous weight recursively until the origin of the weight
  // matrix.


  function spliceOperationsFromEditDistances(distances) {
    let i = distances.length - 1;
    let j = distances[0].length - 1;
    let current = distances[i][j];
    let edits = [];

    while (i > 0 || j > 0) {
      if (i == 0) {
        edits.push(EDIT_ADD);
        j--;
        continue;
      }

      if (j == 0) {
        edits.push(EDIT_DELETE);
        i--;
        continue;
      }

      let northWest = distances[i - 1][j - 1];
      let west = distances[i - 1][j];
      let north = distances[i][j - 1];
      let min;
      if (west < north) min = west < northWest ? west : northWest;else min = north < northWest ? north : northWest;

      if (min == northWest) {
        if (northWest == current) {
          edits.push(EDIT_LEAVE);
        } else {
          edits.push(EDIT_UPDATE);
          current = northWest;
        }

        i--;
        j--;
      } else if (min == west) {
        edits.push(EDIT_DELETE);
        i--;
        current = west;
      } else {
        edits.push(EDIT_ADD);
        j--;
        current = north;
      }
    }

    edits.reverse();
    return edits;
  }
  /**
   * Splice Projection functions:
   *
   * A splice map is a representation of how a previous array of items
   * was transformed into a new array of items. Conceptually it is a list of
   * tuples of
   *
   *   <index, removed, addedCount>
   *
   * which are kept in ascending index order of. The tuple represents that at
   * the |index|, |removed| sequence of items were removed, and counting forward
   * from |index|, |addedCount| items were added.
   */

  /**
   * Lacking individual splice mutation information, the minimal set of
   * splices can be synthesized given the previous state and final state of an
   * array. The basic approach is to calculate the edit distance matrix and
   * choose the shortest path through it.
   *
   * Complexity: O(l * p)
   *   l: The length of the current array
   *   p: The length of the old array
   *
   * @param {!Array} current The current "changed" array for which to
   * calculate splices.
   * @param {number} currentStart Starting index in the `current` array for
   * which splices are calculated.
   * @param {number} currentEnd Ending index in the `current` array for
   * which splices are calculated.
   * @param {!Array} old The original "unchanged" array to compare `current`
   * against to determine splices.
   * @param {number} oldStart Starting index in the `old` array for
   * which splices are calculated.
   * @param {number} oldEnd Ending index in the `old` array for
   * which splices are calculated.
   * @return {!Array} Returns an array of splice record objects. Each of these
   * contains: `index` the location where the splice occurred; `removed`
   * the array of removed items from this location; `addedCount` the number
   * of items added at this location.
   */


  function calcSplices(current, currentStart, currentEnd, old, oldStart, oldEnd) {
    let prefixCount = 0;
    let suffixCount = 0;
    let splice;
    let minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
    if (currentStart == 0 && oldStart == 0) prefixCount = sharedPrefix(current, old, minLength);
    if (currentEnd == current.length && oldEnd == old.length) suffixCount = sharedSuffix(current, old, minLength - prefixCount);
    currentStart += prefixCount;
    oldStart += prefixCount;
    currentEnd -= suffixCount;
    oldEnd -= suffixCount;
    if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) return [];

    if (currentStart == currentEnd) {
      splice = newSplice(currentStart, [], 0);

      while (oldStart < oldEnd) splice.removed.push(old[oldStart++]);

      return [splice];
    } else if (oldStart == oldEnd) return [newSplice(currentStart, [], currentEnd - currentStart)];

    let ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
    splice = undefined;
    let splices = [];
    let index = currentStart;
    let oldIndex = oldStart;

    for (let i = 0; i < ops.length; i++) {
      switch (ops[i]) {
        case EDIT_LEAVE:
          if (splice) {
            splices.push(splice);
            splice = undefined;
          }

          index++;
          oldIndex++;
          break;

        case EDIT_UPDATE:
          if (!splice) splice = newSplice(index, [], 0);
          splice.addedCount++;
          index++;
          splice.removed.push(old[oldIndex]);
          oldIndex++;
          break;

        case EDIT_ADD:
          if (!splice) splice = newSplice(index, [], 0);
          splice.addedCount++;
          index++;
          break;

        case EDIT_DELETE:
          if (!splice) splice = newSplice(index, [], 0);
          splice.removed.push(old[oldIndex]);
          oldIndex++;
          break;
      }
    }

    if (splice) {
      splices.push(splice);
    }

    return splices;
  }

  function sharedPrefix(current, old, searchLength) {
    for (let i = 0; i < searchLength; i++) if (!equals(current[i], old[i])) return i;

    return searchLength;
  }

  function sharedSuffix(current, old, searchLength) {
    let index1 = current.length;
    let index2 = old.length;
    let count = 0;

    while (count < searchLength && equals(current[--index1], old[--index2])) count++;

    return count;
  }
  /**
   * Returns an array of splice records indicating the minimum edits required
   * to transform the `previous` array into the `current` array.
   *
   * Splice records are ordered by index and contain the following fields:
   * - `index`: index where edit started
   * - `removed`: array of removed items from this index
   * - `addedCount`: number of items added at this index
   *
   * This function is based on the Levenshtein "minimum edit distance"
   * algorithm. Note that updates are treated as removal followed by addition.
   *
   * The worst-case time complexity of this algorithm is `O(l * p)`
   *   l: The length of the current array
   *   p: The length of the previous array
   *
   * However, the worst-case complexity is reduced by an `O(n)` optimization
   * to detect any shared prefix & suffix between the two arrays and only
   * perform the more expensive minimum edit distance calculation over the
   * non-shared portions of the arrays.
   *
   * @function
   * @param {!Array} current The "changed" array for which splices will be
   * calculated.
   * @param {!Array} previous The "unchanged" original array to compare
   * `current` against to determine the splices.
   * @return {!Array} Returns an array of splice record objects. Each of these
   * contains: `index` the location where the splice occurred; `removed`
   * the array of removed items from this location; `addedCount` the number
   * of items added at this location.
   */


  function calculateSplices(current, previous) {
    return calcSplices(current, 0, current.length, previous, 0, previous.length);
  }

  function equals(currentValue, previousValue) {
    return currentValue === previousValue;
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Returns true if `node` is a slot element
   * @param {!Node} node Node to test.
   * @return {boolean} Returns true if the given `node` is a slot
   * @private
   */

  function isSlot(node) {
    return node.localName === 'slot';
  }
  /**
   * Class that listens for changes (additions or removals) to
   * "flattened nodes" on a given `node`. The list of flattened nodes consists
   * of a node's children and, for any children that are `<slot>` elements,
   * the expanded flattened list of `assignedNodes`.
   * For example, if the observed node has children `<a></a><slot></slot><b></b>`
   * and the `<slot>` has one `<div>` assigned to it, then the flattened
   * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
   * `<slot>` elements assigned to it, these are flattened as well.
   *
   * The provided `callback` is called whenever any change to this list
   * of flattened nodes occurs, where an addition or removal of a node is
   * considered a change. The `callback` is called with one argument, an object
   * containing an array of any `addedNodes` and `removedNodes`.
   *
   * Note: the callback is called asynchronous to any changes
   * at a microtask checkpoint. This is because observation is performed using
   * `MutationObserver` and the `<slot>` element's `slotchange` event which
   * are asynchronous.
   *
   * An example:
   * ```js
   * class TestSelfObserve extends PolymerElement {
   *   static get is() { return 'test-self-observe';}
   *   connectedCallback() {
   *     super.connectedCallback();
   *     this._observer = new FlattenedNodesObserver(this, (info) => {
   *       this.info = info;
   *     });
   *   }
   *   disconnectedCallback() {
   *     super.disconnectedCallback();
   *     this._observer.disconnect();
   *   }
   * }
   * customElements.define(TestSelfObserve.is, TestSelfObserve);
   * ```
   *
   * @summary Class that listens for changes (additions or removals) to
   * "flattened nodes" on a given `node`.
   * @implements {PolymerDomApi.ObserveHandle}
   */


  let FlattenedNodesObserver = class {
    /**
     * Returns the list of flattened nodes for the given `node`.
     * This list consists of a node's children and, for any children
     * that are `<slot>` elements, the expanded flattened list of `assignedNodes`.
     * For example, if the observed node has children `<a></a><slot></slot><b></b>`
     * and the `<slot>` has one `<div>` assigned to it, then the flattened
     * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other
     * `<slot>` elements assigned to it, these are flattened as well.
     *
     * @param {!HTMLElement|!HTMLSlotElement} node The node for which to
     *      return the list of flattened nodes.
     * @return {!Array<!Node>} The list of flattened nodes for the given `node`.
     * @nocollapse See https://github.com/google/closure-compiler/issues/2763
     */
    // eslint-disable-next-line
    static getFlattenedNodes(node) {
      const wrapped = wrap$1(node);

      if (isSlot(node)) {
        node =
        /** @type {!HTMLSlotElement} */
        node; // eslint-disable-line no-self-assign

        return wrapped.assignedNodes({
          flatten: true
        });
      } else {
        return Array.from(wrapped.childNodes).map(node => {
          if (isSlot(node)) {
            node =
            /** @type {!HTMLSlotElement} */
            node; // eslint-disable-line no-self-assign

            return wrap$1(node).assignedNodes({
              flatten: true
            });
          } else {
            return [node];
          }
        }).reduce((a, b) => a.concat(b), []);
      }
    }
    /**
     * @param {!HTMLElement} target Node on which to listen for changes.
     * @param {?function(this: Element, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Function called when there are additions
     * or removals from the target's list of flattened nodes.
     */
    // eslint-disable-next-line


    constructor(target, callback) {
      /**
       * @type {MutationObserver}
       * @private
       */
      this._shadyChildrenObserver = null;
      /**
       * @type {MutationObserver}
       * @private
       */

      this._nativeChildrenObserver = null;
      this._connected = false;
      /**
       * @type {!HTMLElement}
       * @private
       */

      this._target = target;
      this.callback = callback;
      this._effectiveNodes = [];
      this._observer = null;
      this._scheduled = false;
      /**
       * @type {function()}
       * @private
       */

      this._boundSchedule = () => {
        this._schedule();
      };

      this.connect();

      this._schedule();
    }
    /**
     * Activates an observer. This method is automatically called when
     * a `FlattenedNodesObserver` is created. It should only be called to
     * re-activate an observer that has been deactivated via the `disconnect` method.
     *
     * @return {void}
     */


    connect() {
      if (isSlot(this._target)) {
        this._listenSlots([this._target]);
      } else if (wrap$1(this._target).children) {
        this._listenSlots(
        /** @type {!NodeList<!Node>} */
        wrap$1(this._target).children);

        if (window.ShadyDOM) {
          this._shadyChildrenObserver = window.ShadyDOM.observeChildren(this._target, mutations => {
            this._processMutations(mutations);
          });
        } else {
          this._nativeChildrenObserver = new MutationObserver(mutations => {
            this._processMutations(mutations);
          });

          this._nativeChildrenObserver.observe(this._target, {
            childList: true
          });
        }
      }

      this._connected = true;
    }
    /**
     * Deactivates the flattened nodes observer. After calling this method
     * the observer callback will not be called when changes to flattened nodes
     * occur. The `connect` method may be subsequently called to reactivate
     * the observer.
     *
     * @return {void}
     * @override
     */


    disconnect() {
      if (isSlot(this._target)) {
        this._unlistenSlots([this._target]);
      } else if (wrap$1(this._target).children) {
        this._unlistenSlots(
        /** @type {!NodeList<!Node>} */
        wrap$1(this._target).children);

        if (window.ShadyDOM && this._shadyChildrenObserver) {
          window.ShadyDOM.unobserveChildren(this._shadyChildrenObserver);
          this._shadyChildrenObserver = null;
        } else if (this._nativeChildrenObserver) {
          this._nativeChildrenObserver.disconnect();

          this._nativeChildrenObserver = null;
        }
      }

      this._connected = false;
    }
    /**
     * @return {void}
     * @private
     */


    _schedule() {
      if (!this._scheduled) {
        this._scheduled = true;
        microTask.run(() => this.flush());
      }
    }
    /**
     * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer
     * @return {void}
     * @private
     */


    _processMutations(mutations) {
      this._processSlotMutations(mutations);

      this.flush();
    }
    /**
     * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer
     * @return {void}
     * @private
     */


    _processSlotMutations(mutations) {
      if (mutations) {
        for (let i = 0; i < mutations.length; i++) {
          let mutation = mutations[i];

          if (mutation.addedNodes) {
            this._listenSlots(mutation.addedNodes);
          }

          if (mutation.removedNodes) {
            this._unlistenSlots(mutation.removedNodes);
          }
        }
      }
    }
    /**
     * Flushes the observer causing any pending changes to be immediately
     * delivered the observer callback. By default these changes are delivered
     * asynchronously at the next microtask checkpoint.
     *
     * @return {boolean} Returns true if any pending changes caused the observer
     * callback to run.
     */


    flush() {
      if (!this._connected) {
        return false;
      }

      if (window.ShadyDOM) {
        ShadyDOM.flush();
      }

      if (this._nativeChildrenObserver) {
        this._processSlotMutations(this._nativeChildrenObserver.takeRecords());
      } else if (this._shadyChildrenObserver) {
        this._processSlotMutations(this._shadyChildrenObserver.takeRecords());
      }

      this._scheduled = false;
      let info = {
        target: this._target,
        addedNodes: [],
        removedNodes: []
      };
      let newNodes = this.constructor.getFlattenedNodes(this._target);
      let splices = calculateSplices(newNodes, this._effectiveNodes); // process removals

      for (let i = 0, s; i < splices.length && (s = splices[i]); i++) {
        for (let j = 0, n; j < s.removed.length && (n = s.removed[j]); j++) {
          info.removedNodes.push(n);
        }
      } // process adds


      for (let i = 0, s; i < splices.length && (s = splices[i]); i++) {
        for (let j = s.index; j < s.index + s.addedCount; j++) {
          info.addedNodes.push(newNodes[j]);
        }
      } // update cache


      this._effectiveNodes = newNodes;
      let didFlush = false;

      if (info.addedNodes.length || info.removedNodes.length) {
        didFlush = true;
        this.callback.call(this._target, info);
      }

      return didFlush;
    }
    /**
     * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change
     * @return {void}
     * @private
     */


    _listenSlots(nodeList) {
      for (let i = 0; i < nodeList.length; i++) {
        let n = nodeList[i];

        if (isSlot(n)) {
          n.addEventListener('slotchange', this._boundSchedule);
        }
      }
    }
    /**
     * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change
     * @return {void}
     * @private
     */


    _unlistenSlots(nodeList) {
      for (let i = 0; i < nodeList.length; i++) {
        let n = nodeList[i];

        if (isSlot(n)) {
          n.removeEventListener('slotchange', this._boundSchedule);
        }
      }
    }

  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Forces several classes of asynchronously queued tasks to flush:
   * - Debouncers added via `enqueueDebouncer`
   * - ShadyDOM distribution
   *
   * @return {void}
   */

  const flush = function () {
    let shadyDOM, debouncers;

    do {
      shadyDOM = window.ShadyDOM && ShadyDOM.flush();

      if (window.ShadyCSS && window.ShadyCSS.ScopingShim) {
        window.ShadyCSS.ScopingShim.flush();
      }

      debouncers = flushDebouncers();
    } while (shadyDOM || debouncers);
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  /* eslint-enable no-unused-vars */

  const p = Element.prototype;
  /**
   * @const {function(this:Node, string): boolean}
   */

  const normalizedMatchesSelector = p.matches || p.matchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector || p.webkitMatchesSelector;
  /**
   * Cross-platform `element.matches` shim.
   *
   * @function matchesSelector
   * @param {!Node} node Node to check selector against
   * @param {string} selector Selector to match
   * @return {boolean} True if node matched selector
   */

  const matchesSelector = function (node, selector) {
    return normalizedMatchesSelector.call(node, selector);
  };
  /**
   * Node API wrapper class returned from `Polymer.dom.(target)` when
   * `target` is a `Node`.
   * @implements {PolymerDomApi}
   * @unrestricted
   */

  class DomApiNative {
    /**
     * @param {!Node} node Node for which to create a Polymer.dom helper object.
     */
    constructor(node) {
      if (window['ShadyDOM'] && window['ShadyDOM']['inUse']) {
        window['ShadyDOM']['patch'](node);
      }

      this.node = node;
    }
    /**
     * Returns an instance of `FlattenedNodesObserver` that
     * listens for node changes on this element.
     *
     * @param {function(this:HTMLElement, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Called when direct or distributed children
     *   of this element changes
     * @return {!PolymerDomApi.ObserveHandle} Observer instance
     * @override
     */


    observeNodes(callback) {
      return new FlattenedNodesObserver(
      /** @type {!HTMLElement} */
      this.node, callback);
    }
    /**
     * Disconnects an observer previously created via `observeNodes`
     *
     * @param {!PolymerDomApi.ObserveHandle} observerHandle Observer instance
     *   to disconnect.
     * @return {void}
     * @override
     */


    unobserveNodes(observerHandle) {
      observerHandle.disconnect();
    }
    /**
     * Provided as a backwards-compatible API only.  This method does nothing.
     * @return {void}
     */


    notifyObserver() {}
    /**
     * Returns true if the provided node is contained with this element's
     * light-DOM children or shadow root, including any nested shadow roots
     * of children therein.
     *
     * @param {Node} node Node to test
     * @return {boolean} Returns true if the given `node` is contained within
     *   this element's light or shadow DOM.
     * @override
     */


    deepContains(node) {
      if (wrap$1(this.node).contains(node)) {
        return true;
      }

      let n = node;
      let doc = node.ownerDocument; // walk from node to `this` or `document`

      while (n && n !== doc && n !== this.node) {
        // use logical parentnode, or native ShadowRoot host
        n = wrap$1(n).parentNode || wrap$1(n).host;
      }

      return n === this.node;
    }
    /**
     * Returns the root node of this node.  Equivalent to `getRootNode()`.
     *
     * @return {!Node} Top most element in the dom tree in which the node
     * exists. If the node is connected to a document this is either a
     * shadowRoot or the document; otherwise, it may be the node
     * itself or a node or document fragment containing it.
     * @override
     */


    getOwnerRoot() {
      return wrap$1(this.node).getRootNode();
    }
    /**
     * For slot elements, returns the nodes assigned to the slot; otherwise
     * an empty array. It is equivalent to `<slot>.addignedNodes({flatten:true})`.
     *
     * @return {!Array<!Node>} Array of assigned nodes
     * @override
     */


    getDistributedNodes() {
      return this.node.localName === 'slot' ? wrap$1(this.node).assignedNodes({
        flatten: true
      }) : [];
    }
    /**
     * Returns an array of all slots this element was distributed to.
     *
     * @return {!Array<!HTMLSlotElement>} Description
     * @override
     */


    getDestinationInsertionPoints() {
      let ip$ = [];
      let n = wrap$1(this.node).assignedSlot;

      while (n) {
        ip$.push(n);
        n = wrap$1(n).assignedSlot;
      }

      return ip$;
    }
    /**
     * Calls `importNode` on the `ownerDocument` for this node.
     *
     * @param {!Node} node Node to import
     * @param {boolean} deep True if the node should be cloned deeply during
     *   import
     * @return {Node} Clone of given node imported to this owner document
     */


    importNode(node, deep) {
      let doc = this.node instanceof Document ? this.node : this.node.ownerDocument;
      return wrap$1(doc).importNode(node, deep);
    }
    /**
     * @return {!Array<!Node>} Returns a flattened list of all child nodes and
     * nodes assigned to child slots.
     * @override
     */


    getEffectiveChildNodes() {
      return FlattenedNodesObserver.getFlattenedNodes(
      /** @type {!HTMLElement} */
      this.node);
    }
    /**
     * Returns a filtered list of flattened child elements for this element based
     * on the given selector.
     *
     * @param {string} selector Selector to filter nodes against
     * @return {!Array<!HTMLElement>} List of flattened child elements
     * @override
     */


    queryDistributedElements(selector) {
      let c$ = this.getEffectiveChildNodes();
      let list = [];

      for (let i = 0, l = c$.length, c; i < l && (c = c$[i]); i++) {
        if (c.nodeType === Node.ELEMENT_NODE && matchesSelector(c, selector)) {
          list.push(c);
        }
      }

      return list;
    }
    /**
     * For shadow roots, returns the currently focused element within this
     * shadow root.
     *
     * return {Node|undefined} Currently focused element
     * @override
     */


    get activeElement() {
      let node = this.node;
      return node._activeElement !== undefined ? node._activeElement : node.activeElement;
    }

  }

  function forwardMethods(proto, methods) {
    for (let i = 0; i < methods.length; i++) {
      let method = methods[i];
      /* eslint-disable valid-jsdoc */

      proto[method] =
      /** @this {DomApiNative} */
      function () {
        return this.node[method].apply(this.node, arguments);
      };
      /* eslint-enable */

    }
  }

  function forwardReadOnlyProperties(proto, properties) {
    for (let i = 0; i < properties.length; i++) {
      let name = properties[i];
      Object.defineProperty(proto, name, {
        get: function () {
          const domApi =
          /** @type {DomApiNative} */
          this;
          return domApi.node[name];
        },
        configurable: true
      });
    }
  }

  function forwardProperties(proto, properties) {
    for (let i = 0; i < properties.length; i++) {
      let name = properties[i];
      Object.defineProperty(proto, name, {
        /**
         * @this {DomApiNative}
         * @return {*} .
         */
        get: function () {
          return this.node[name];
        },

        /**
         * @this {DomApiNative}
         * @param {*} value .
         */
        set: function (value) {
          this.node[name] = value;
        },
        configurable: true
      });
    }
  }
  /**
   * Event API wrapper class returned from `dom.(target)` when
   * `target` is an `Event`.
   */


  class EventApi {
    constructor(event) {
      this.event = event;
    }
    /**
     * Returns the first node on the `composedPath` of this event.
     *
     * @return {!EventTarget} The node this event was dispatched to
     */


    get rootTarget() {
      return this.path[0];
    }
    /**
     * Returns the local (re-targeted) target for this event.
     *
     * @return {!EventTarget} The local (re-targeted) target for this event.
     */


    get localTarget() {
      return this.event.target;
    }
    /**
     * Returns the `composedPath` for this event.
     * @return {!Array<!EventTarget>} The nodes this event propagated through
     */


    get path() {
      return this.event.composedPath();
    }

  }
  let DomApiImpl = DomApiNative;

  if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['Wrapper']) {
    /**
     * @private
     * @extends {HTMLElement}
     */
    class Wrapper extends window['ShadyDOM']['Wrapper'] {} // copy bespoke API onto wrapper


    Object.getOwnPropertyNames(DomApiNative.prototype).forEach(prop => {
      if (prop != 'activeElement') {
        Wrapper.prototype[prop] = DomApiNative.prototype[prop];
      }
    }); // Note, `classList` is here only for legacy compatibility since it does not
    // trigger distribution in v1 Shadow DOM.

    forwardReadOnlyProperties(Wrapper.prototype, ['classList']);
    DomApiImpl = Wrapper;
    Object.defineProperties(EventApi.prototype, {
      // Returns the "lowest" node in the same root as the event's currentTarget.
      // When in `noPatch` mode, this must be calculated by walking the event's
      // path.
      localTarget: {
        get() {
          const current = this.event.currentTarget;
          const currentRoot = current && dom(current).getOwnerRoot();
          const p$ = this.path;

          for (let i = 0; i < p$.length; i++) {
            const e = p$[i];

            if (dom(e).getOwnerRoot() === currentRoot) {
              return e;
            }
          }
        },

        configurable: true
      },
      path: {
        get() {
          return window['ShadyDOM']['composedPath'](this.event);
        },

        configurable: true
      }
    });
  } else {
    // Methods that can provoke distribution or must return the logical, not
    // composed tree.
    forwardMethods(DomApiNative.prototype, ['cloneNode', 'appendChild', 'insertBefore', 'removeChild', 'replaceChild', 'setAttribute', 'removeAttribute', 'querySelector', 'querySelectorAll']); // Properties that should return the logical, not composed tree. Note, `classList`
    // is here only for legacy compatibility since it does not trigger distribution
    // in v1 Shadow DOM.

    forwardReadOnlyProperties(DomApiNative.prototype, ['parentNode', 'firstChild', 'lastChild', 'nextSibling', 'previousSibling', 'firstElementChild', 'lastElementChild', 'nextElementSibling', 'previousElementSibling', 'childNodes', 'children', 'classList']);
    forwardProperties(DomApiNative.prototype, ['textContent', 'innerHTML', 'className']);
  }
  /**
   * Legacy DOM and Event manipulation API wrapper factory used to abstract
   * differences between native Shadow DOM and "Shady DOM" when polyfilling on
   * older browsers.
   *
   * Note that in Polymer 2.x use of `Polymer.dom` is no longer required and
   * in the majority of cases simply facades directly to the standard native
   * API.
   *
   * @summary Legacy DOM and Event manipulation API wrapper factory used to
   * abstract differences between native Shadow DOM and "Shady DOM."
   * @param {(Node|Event|DomApiNative|EventApi)=} obj Node or event to operate on
   * @return {!DomApiNative|!EventApi} Wrapper providing either node API or event API
   */

  const dom = function (obj) {
    obj = obj || document;

    if (obj instanceof DomApiImpl) {
      return (
        /** @type {!DomApi} */
        obj
      );
    }

    if (obj instanceof EventApi) {
      return (
        /** @type {!EventApi} */
        obj
      );
    }

    let helper = obj['__domApi'];

    if (!helper) {
      if (obj instanceof Event) {
        helper = new EventApi(obj);
      } else {
        helper = new DomApiImpl(
        /** @type {Node} */
        obj);
      }

      obj['__domApi'] = helper;
    }

    return helper;
  };

  /**
  @license
  Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const ShadyDOM$1 = window.ShadyDOM;
  const ShadyCSS = window.ShadyCSS;
  /**
   * Return true if node scope is correct.
   *
   * @param {!Element} node Node to check scope
   * @param {!Node} scope Scope reference
   * @return {boolean} True if node is in scope
   */

  function sameScope(node, scope) {
    return wrap$1(node).getRootNode() === scope;
  }
  /**
   * Ensure that elements in a ShadowDOM container are scoped correctly.
   * This function is only needed when ShadyDOM is used and unpatched DOM APIs are used in third party code.
   * This can happen in noPatch mode or when specialized APIs like ranges or tables are used to mutate DOM.
   *
   * @param  {!Element} container Container element to scope
   * @param  {boolean=} shouldObserve if true, start a mutation observer for added nodes to the container
   * @return {?MutationObserver} Returns a new MutationObserver on `container` if `shouldObserve` is true.
   */


  function scopeSubtree(container, shouldObserve = false) {
    // If using native ShadowDOM, abort
    if (!ShadyDOM$1 || !ShadyCSS) {
      return null;
    } // ShadyCSS handles DOM mutations when ShadyDOM does not handle scoping itself


    if (!ShadyDOM$1['handlesDynamicScoping']) {
      return null;
    }

    const ScopingShim = ShadyCSS['ScopingShim']; // if ScopingShim is not available, abort

    if (!ScopingShim) {
      return null;
    } // capture correct scope for container


    const containerScope = ScopingShim['scopeForNode'](container);
    const root = wrap$1(container).getRootNode();

    const scopify = node => {
      if (!sameScope(node, root)) {
        return;
      } // NOTE: native qSA does not honor scoped DOM, but it is faster, and the same behavior as Polymer v1


      const elements = Array.from(ShadyDOM$1['nativeMethods']['querySelectorAll'].call(node, '*'));
      elements.push(node);

      for (let i = 0; i < elements.length; i++) {
        const el = elements[i];

        if (!sameScope(el, root)) {
          continue;
        }

        const currentScope = ScopingShim['currentScopeForNode'](el);

        if (currentScope !== containerScope) {
          if (currentScope !== '') {
            ScopingShim['unscopeNode'](el, currentScope);
          }

          ScopingShim['scopeNode'](el, containerScope);
        }
      }
    }; // scope everything in container


    scopify(container);

    if (shouldObserve) {
      const mo = new MutationObserver(mxns => {
        for (let i = 0; i < mxns.length; i++) {
          const mxn = mxns[i];

          for (let j = 0; j < mxn.addedNodes.length; j++) {
            const addedNode = mxn.addedNodes[j];

            if (addedNode.nodeType === Node.ELEMENT_NODE) {
              scopify(addedNode);
            }
          }
        }
      });
      mo.observe(container, {
        childList: true,
        subtree: true
      });
      return mo;
    } else {
      return null;
    }
  }

  /**
   * @fileoverview
   * @suppress {checkPrototypalTypes}
   * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
   * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
   * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
   * Google as part of the polymer project is also subject to an additional IP
   * rights grant found at http://polymer.github.io/PATENTS.txt
   */
  const DISABLED_ATTR = 'disable-upgrade';
  const findObservedAttributesGetter = ctor => {
    while (ctor) {
      const desc = Object.getOwnPropertyDescriptor(ctor, 'observedAttributes');

      if (desc) {
        return desc.get;
      }

      ctor = Object.getPrototypeOf(ctor.prototype).constructor;
    }

    return () => [];
  };
  /**
   * Element class mixin that allows the element to boot up in a non-enabled
   * state when the `disable-upgrade` attribute is present. This mixin is
   * designed to be used with element classes like PolymerElement that perform
   * initial startup work when they are first connected. When the
   * `disable-upgrade` attribute is removed, if the element is connected, it
   * boots up and "enables" as it otherwise would; if it is not connected, the
   * element boots up when it is next connected.
   *
   * Using `disable-upgrade` with PolymerElement prevents any data propagation
   * to the element, any element DOM from stamping, or any work done in
   * connected/disconnctedCallback from occuring, but it does not prevent work
   * done in the element constructor.
   *
   * Note, this mixin must be applied on top of any element class that
   * itself implements a `connectedCallback` so that it can control the work
   * done in `connectedCallback`. For example,
   *
   *     MyClass = DisableUpgradeMixin(class extends BaseClass {...});
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin ElementMixin
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */

  const DisableUpgradeMixin = dedupingMixin(base => {
    /**
     * @constructor
     * @implements {Polymer_ElementMixin}
     * @extends {HTMLElement}
     * @private
     */
    const superClass = ElementMixin(base); // Work around for closure bug #126934458. Using `super` in a property
    // getter does not work so instead we search the Base prototype for an
    // implementation of observedAttributes so that we can override and call
    // the `super` getter. Note, this is done one time ever because we assume
    // that `Base` is always comes from `Polymer.LegacyElementMixn`.

    let observedAttributesGetter = findObservedAttributesGetter(superClass);
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_DisableUpgradeMixin}
     */

    class DisableUpgradeClass extends superClass {
      constructor() {
        super();
        /** @type {boolean|undefined} */

        this.__isUpgradeDisabled;
      }

      static get observedAttributes() {
        return observedAttributesGetter.call(this).concat(DISABLED_ATTR);
      } // Prevent element from initializing properties when it's upgrade disabled.

      /** @override */


      _initializeProperties() {
        if (this.hasAttribute(DISABLED_ATTR)) {
          this.__isUpgradeDisabled = true;
        } else {
          super._initializeProperties();
        }
      } // Prevent element from enabling properties when it's upgrade disabled.
      // Normally overriding connectedCallback would be enough, but dom-* elements

      /** @override */


      _enableProperties() {
        if (!this.__isUpgradeDisabled) {
          super._enableProperties();
        }
      } // If the element starts upgrade-disabled and a property is set for
      // which an accessor exists, the default should not be applied.
      // This additional check is needed because defaults are applied via
      // `_initializeProperties` which is called after initial properties
      // have been set when the element starts upgrade-disabled.

      /** @override */


      _canApplyPropertyDefault(property) {
        return super._canApplyPropertyDefault(property) && !(this.__isUpgradeDisabled && this._isPropertyPending(property));
      }
      /**
       * @override
       * @param {string} name Attribute name.
       * @param {?string} old The previous value for the attribute.
       * @param {?string} value The new value for the attribute.
       * @param {?string} namespace The XML namespace for the attribute.
       * @return {void}
       */


      attributeChangedCallback(name, old, value, namespace) {
        if (name == DISABLED_ATTR) {
          // When disable-upgrade is removed, intialize properties and
          // provoke connectedCallback if the element is already connected.
          if (this.__isUpgradeDisabled && value == null) {
            super._initializeProperties();

            this.__isUpgradeDisabled = false;

            if (wrap$1(this).isConnected) {
              super.connectedCallback();
            }
          }
        } else {
          super.attributeChangedCallback(name, old, value,
          /** @type {null|string} */
          namespace);
        }
      } // Prevent element from connecting when it's upgrade disabled.
      // This prevents user code in `attached` from being called.

      /** @override */


      connectedCallback() {
        if (!this.__isUpgradeDisabled) {
          super.connectedCallback();
        }
      } // Prevent element from disconnecting when it's upgrade disabled.
      // This avoids allowing user code `detached` from being called without a
      // paired call to `attached`.

      /** @override */


      disconnectedCallback() {
        if (!this.__isUpgradeDisabled) {
          super.disconnectedCallback();
        }
      }

    }

    return DisableUpgradeClass;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const DISABLED_ATTR$1 = 'disable-upgrade';
  let styleInterface = window.ShadyCSS;
  /**
   * Element class mixin that provides Polymer's "legacy" API intended to be
   * backward-compatible to the greatest extent possible with the API
   * found on the Polymer 1.x `Polymer.Base` prototype applied to all elements
   * defined using the `Polymer({...})` function.
   *
   * @mixinFunction
   * @polymer
   * @appliesMixin ElementMixin
   * @appliesMixin GestureEventListeners
   * @appliesMixin DirMixin
   * @property isAttached {boolean} Set to `true` in this element's
   *   `connectedCallback` and `false` in `disconnectedCallback`
   * @summary Element class mixin that provides Polymer's "legacy" API
   */

  const LegacyElementMixin = dedupingMixin(base => {
    // TODO(kschaaf): Note, the `@implements {Polymer_DirMixin}` is required here
    // (rather than on legacyElementBase) for unknown reasons.

    /**
     * @constructor
     * @implements {Polymer_ElementMixin}
     * @implements {Polymer_GestureEventListeners}
     * @implements {Polymer_DirMixin}
     * @extends {HTMLElement}
     * @private
     */
    const GesturesElement = GestureEventListeners(ElementMixin(base)); // Note, the DirMixin does nothing if css is built so avoid including it
    // in that case.

    /**
     * @constructor
     * @extends {GesturesElement}
     * @private
     */

    const legacyElementBase = builtCSS ? GesturesElement : DirMixin(GesturesElement);
    const observedAttributesGetter = findObservedAttributesGetter(legacyElementBase);
    /**
     * Map of simple names to touch action names
     * @dict
     */

    const DIRECTION_MAP = {
      'x': 'pan-x',
      'y': 'pan-y',
      'none': 'none',
      'all': 'auto'
    };
    /**
     * @polymer
     * @mixinClass
     * @extends {legacyElementBase}
     * @implements {Polymer_LegacyElementMixin}
     * @unrestricted
     */

    class LegacyElement extends legacyElementBase {
      constructor() {
        super();
        /** @type {boolean} */

        this.isAttached;
        /** @type {?WeakMap<!Element, !Object<string, !Function>>} */

        this.__boundListeners;
        /** @type {?Object<string, ?Function>} */

        this._debouncers; // NOTE: Inlined for perf from version of DisableUpgradeMixin.

        /** @type {boolean|undefined} */

        this.__isUpgradeDisabled;
        /** @type {boolean|undefined} */

        this.__needsAttributesAtConnected;
        /** @type {boolean|undefined} */

        this._legacyForceObservedAttributes;
      }
      /**
       * Forwards `importMeta` from the prototype (i.e. from the info object
       * passed to `Polymer({...})`) to the static API.
       *
       * @return {!Object} The `import.meta` object set on the prototype
       * @suppress {missingProperties} `this` is always in the instance in
       *  closure for some reason even in a static method, rather than the class
       * @nocollapse
       */


      static get importMeta() {
        return this.prototype.importMeta;
      }
      /**
       * Legacy callback called during the `constructor`, for overriding
       * by the user.
       * @override
       * @return {void}
       */


      created() {}
      /**
       * Processes an attribute reaction when the `legacyNoObservedAttributes`
       * setting is in use.
       * @param {string} name Name of attribute that changed
       * @param {?string} old Old attribute value
       * @param {?string} value New attribute value
       * @return {void}
       */


      __attributeReaction(name, old, value) {
        if (this.__dataAttributes && this.__dataAttributes[name] || name === DISABLED_ATTR$1) {
          this.attributeChangedCallback(name, old, value, null);
        }
      }
      /**
       * Sets the value of an attribute.
       * @override
       * @param {string} name The name of the attribute to change.
       * @param {string|number|boolean|!TrustedHTML|!TrustedScriptURL|!TrustedURL} value The new attribute value.
       */


      setAttribute(name, value) {
        if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) {
          const oldValue = this.getAttribute(name);
          super.setAttribute(name, value); // value coerced to String for closure's benefit

          this.__attributeReaction(name, oldValue, String(value));
        } else {
          super.setAttribute(name, value);
        }
      }
      /**
       * Removes an attribute.
       * @override
       * @param {string} name The name of the attribute to remove.
       */


      removeAttribute(name) {
        if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) {
          const oldValue = this.getAttribute(name);
          super.removeAttribute(name);

          this.__attributeReaction(name, oldValue, null);
        } else {
          super.removeAttribute(name);
        }
      } // NOTE: Inlined for perf from version of DisableUpgradeMixin.


      static get observedAttributes() {
        if (legacyNoObservedAttributes && !this.prototype._legacyForceObservedAttributes) {
          // Ensure this element is property registered with the telemetry system.
          if (!this.hasOwnProperty(JSCompiler_renameProperty('__observedAttributes', this))) {
            this.__observedAttributes = [];
            register(this.prototype);
          }

          return this.__observedAttributes;
        } else {
          return observedAttributesGetter.call(this).concat(DISABLED_ATTR$1);
        }
      } // NOTE: Inlined for perf from version of DisableUpgradeMixin.
      // Prevent element from enabling properties when it's upgrade disabled.
      // Normally overriding connectedCallback would be enough, but dom-* elements

      /** @override */


      _enableProperties() {
        if (!this.__isUpgradeDisabled) {
          super._enableProperties();
        }
      } // NOTE: Inlined for perf from version of DisableUpgradeMixin.
      // If the element starts upgrade-disabled and a property is set for
      // which an accessor exists, the default should not be applied.
      // This additional check is needed because defaults are applied via
      // `_initializeProperties` which is called after initial properties
      // have been set when the element starts upgrade-disabled.

      /** @override */


      _canApplyPropertyDefault(property) {
        return super._canApplyPropertyDefault(property) && !(this.__isUpgradeDisabled && this._isPropertyPending(property));
      }
      /**
       * Provides an implementation of `connectedCallback`
       * which adds Polymer legacy API's `attached` method.
       * @return {void}
       * @override
       */


      connectedCallback() {
        if (this.__needsAttributesAtConnected) {
          this._takeAttributes();
        } // NOTE: Inlined for perf from version of DisableUpgradeMixin.


        if (!this.__isUpgradeDisabled) {
          super.connectedCallback();
          this.isAttached = true;
          this.attached();
        }
      }
      /**
       * Legacy callback called during `connectedCallback`, for overriding
       * by the user.
       * @override
       * @return {void}
       */


      attached() {}
      /**
       * Provides an implementation of `disconnectedCallback`
       * which adds Polymer legacy API's `detached` method.
       * @return {void}
       * @override
       */


      disconnectedCallback() {
        // NOTE: Inlined for perf from version of DisableUpgradeMixin.
        if (!this.__isUpgradeDisabled) {
          super.disconnectedCallback();
          this.isAttached = false;
          this.detached();
        }
      }
      /**
       * Legacy callback called during `disconnectedCallback`, for overriding
       * by the user.
       * @override
       * @return {void}
       */


      detached() {}
      /**
       * Provides an override implementation of `attributeChangedCallback`
       * which adds the Polymer legacy API's `attributeChanged` method.
       * @param {string} name Name of attribute.
       * @param {?string} old Old value of attribute.
       * @param {?string} value Current value of attribute.
       * @param {?string} namespace Attribute namespace.
       * @return {void}
       * @override
       */


      attributeChangedCallback(name, old, value, namespace) {
        if (old !== value) {
          // NOTE: Inlined for perf from version of DisableUpgradeMixin.
          if (name == DISABLED_ATTR$1) {
            // When disable-upgrade is removed, intialize properties and
            // provoke connectedCallback if the element is already connected.
            if (this.__isUpgradeDisabled && value == null) {
              this._initializeProperties();

              this.__isUpgradeDisabled = false;

              if (wrap$1(this).isConnected) {
                this.connectedCallback();
              }
            }
          } else {
            super.attributeChangedCallback(name, old, value, namespace);
            this.attributeChanged(name, old, value);
          }
        }
      }
      /**
       * Legacy callback called during `attributeChangedChallback`, for overriding
       * by the user.
       * @param {string} name Name of attribute.
       * @param {?string} old Old value of attribute.
       * @param {?string} value Current value of attribute.
       * @return {void}
       * @override
       */


      attributeChanged(name, old, value) {} // eslint-disable-line no-unused-vars

      /**
       * Overrides the default `Polymer.PropertyEffects` implementation to
       * add support for class initialization via the `_registered` callback.
       * This is called only when the first instance of the element is created.
       *
       * @return {void}
       * @override
       * @suppress {invalidCasts}
       */


      _initializeProperties() {
        // NOTE: Inlined for perf from version of DisableUpgradeMixin.
        // Only auto-use disable-upgrade if legacyOptimizations is set.
        if (legacyOptimizations && this.hasAttribute(DISABLED_ATTR$1)) {
          this.__isUpgradeDisabled = true;
        } else {
          let proto = Object.getPrototypeOf(this);

          if (!proto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', proto))) {
            this._registered(); // backstop in case the `_registered` implementation does not set this


            proto.__hasRegisterFinished = true;
          }

          super._initializeProperties();

          this.root =
          /** @type {HTMLElement} */
          this;
          this.created(); // Pull all attribute values 1x if `legacyNoObservedAttributes` is set.

          if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) {
            if (this.hasAttributes()) {
              this._takeAttributes(); // Element created from scratch or parser generated

            } else if (!this.parentNode) {
              this.__needsAttributesAtConnected = true;
            }
          } // Ensure listeners are applied immediately so that they are
          // added before declarative event listeners. This allows an element to
          // decorate itself via an event prior to any declarative listeners
          // seeing the event. Note, this ensures compatibility with 1.x ordering.


          this._applyListeners();
        }
      }

      _takeAttributes() {
        const a = this.attributes;

        for (let i = 0, l = a.length; i < l; i++) {
          const attr = a[i];

          this.__attributeReaction(attr.name, null, attr.value);
        }
      }
      /**
       * Called automatically when an element is initializing.
       * Users may override this method to perform class registration time
       * work. The implementation should ensure the work is performed
       * only once for the class.
       * @protected
       * @return {void}
       * @override
       */


      _registered() {}
      /**
       * Overrides the default `Polymer.PropertyEffects` implementation to
       * add support for installing `hostAttributes` and `listeners`.
       *
       * @return {void}
       * @override
       */


      ready() {
        this._ensureAttributes();

        super.ready();
      }
      /**
       * Ensures an element has required attributes. Called when the element
       * is being readied via `ready`. Users should override to set the
       * element's required attributes. The implementation should be sure
       * to check and not override existing attributes added by
       * the user of the element. Typically, setting attributes should be left
       * to the element user and not done here; reasonable exceptions include
       * setting aria roles and focusability.
       * @protected
       * @return {void}
       * @override
       */


      _ensureAttributes() {}
      /**
       * Adds element event listeners. Called when the element
       * is being readied via `ready`. Users should override to
       * add any required element event listeners.
       * In performance critical elements, the work done here should be kept
       * to a minimum since it is done before the element is rendered. In
       * these elements, consider adding listeners asynchronously so as not to
       * block render.
       * @protected
       * @return {void}
       * @override
       */


      _applyListeners() {}
      /**
       * Converts a typed JavaScript value to a string.
       *
       * Note this method is provided as backward-compatible legacy API
       * only.  It is not directly called by any Polymer features. To customize
       * how properties are serialized to attributes for attribute bindings and
       * `reflectToAttribute: true` properties as well as this method, override
       * the `_serializeValue` method provided by `Polymer.PropertyAccessors`.
       *
       * @param {*} value Value to deserialize
       * @return {string | undefined} Serialized value
       * @override
       */


      serialize(value) {
        return this._serializeValue(value);
      }
      /**
       * Converts a string to a typed JavaScript value.
       *
       * Note this method is provided as backward-compatible legacy API
       * only.  It is not directly called by any Polymer features.  To customize
       * how attributes are deserialized to properties for in
       * `attributeChangedCallback`, override `_deserializeValue` method
       * provided by `Polymer.PropertyAccessors`.
       *
       * @param {string} value String to deserialize
       * @param {*} type Type to deserialize the string to
       * @return {*} Returns the deserialized value in the `type` given.
       * @override
       */


      deserialize(value, type) {
        return this._deserializeValue(value, type);
      }
      /**
       * Serializes a property to its associated attribute.
       *
       * Note this method is provided as backward-compatible legacy API
       * only.  It is not directly called by any Polymer features.
       *
       * @param {string} property Property name to reflect.
       * @param {string=} attribute Attribute name to reflect.
       * @param {*=} value Property value to reflect.
       * @return {void}
       * @override
       */


      reflectPropertyToAttribute(property, attribute, value) {
        this._propertyToAttribute(property, attribute, value);
      }
      /**
       * Sets a typed value to an HTML attribute on a node.
       *
       * Note this method is provided as backward-compatible legacy API
       * only.  It is not directly called by any Polymer features.
       *
       * @param {*} value Value to serialize.
       * @param {string} attribute Attribute name to serialize to.
       * @param {Element} node Element to set attribute to.
       * @return {void}
       * @override
       */


      serializeValueToAttribute(value, attribute, node) {
        this._valueToNodeAttribute(
        /** @type {Element} */
        node || this, value, attribute);
      }
      /**
       * Copies own properties (including accessor descriptors) from a source
       * object to a target object.
       *
       * @param {Object} prototype Target object to copy properties to.
       * @param {Object} api Source object to copy properties from.
       * @return {Object} prototype object that was passed as first argument.
       * @override
       */


      extend(prototype, api) {
        if (!(prototype && api)) {
          return prototype || api;
        }

        let n$ = Object.getOwnPropertyNames(api);

        for (let i = 0, n; i < n$.length && (n = n$[i]); i++) {
          let pd = Object.getOwnPropertyDescriptor(api, n);

          if (pd) {
            Object.defineProperty(prototype, n, pd);
          }
        }

        return prototype;
      }
      /**
       * Copies props from a source object to a target object.
       *
       * Note, this method uses a simple `for...in` strategy for enumerating
       * properties.  To ensure only `ownProperties` are copied from source
       * to target and that accessor implementations are copied, use `extend`.
       *
       * @param {!Object} target Target object to copy properties to.
       * @param {!Object} source Source object to copy properties from.
       * @return {!Object} Target object that was passed as first argument.
       * @override
       */


      mixin(target, source) {
        for (let i in source) {
          target[i] = source[i];
        }

        return target;
      }
      /**
       * Sets the prototype of an object.
       *
       * Note this method is provided as backward-compatible legacy API
       * only.  It is not directly called by any Polymer features.
       * @param {Object} object The object on which to set the prototype.
       * @param {Object} prototype The prototype that will be set on the given
       * `object`.
       * @return {Object} Returns the given `object` with its prototype set
       * to the given `prototype` object.
       * @override
       */


      chainObject(object, prototype) {
        if (object && prototype && object !== prototype) {
          object.__proto__ = prototype;
        }

        return object;
      }
      /* **** Begin Template **** */

      /**
       * Calls `importNode` on the `content` of the `template` specified and
       * returns a document fragment containing the imported content.
       *
       * @param {HTMLTemplateElement} template HTML template element to instance.
       * @return {!DocumentFragment} Document fragment containing the imported
       *   template content.
       * @override
       * @suppress {missingProperties} go/missingfnprops
       */


      instanceTemplate(template) {
        let content = this.constructor._contentForTemplate(template);

        let dom =
        /** @type {!DocumentFragment} */
        document.importNode(content, true);
        return dom;
      }
      /* **** Begin Events **** */

      /**
       * Dispatches a custom event with an optional detail value.
       *
       * @param {string} type Name of event type.
       * @param {*=} detail Detail value containing event-specific
       *   payload.
       * @param {{ bubbles: (boolean|undefined), cancelable: (boolean|undefined),
       *     composed: (boolean|undefined) }=}
       *  options Object specifying options.  These may include:
       *  `bubbles` (boolean, defaults to `true`),
       *  `cancelable` (boolean, defaults to false), and
       *  `node` on which to fire the event (HTMLElement, defaults to `this`).
       * @return {!Event} The new event that was fired.
       * @override
       */


      fire(type, detail, options) {
        options = options || {};
        detail = detail === null || detail === undefined ? {} : detail;
        let event = new Event(type, {
          bubbles: options.bubbles === undefined ? true : options.bubbles,
          cancelable: Boolean(options.cancelable),
          composed: options.composed === undefined ? true : options.composed
        });
        event.detail = detail;
        let node = options.node || this;
        wrap$1(node).dispatchEvent(event);
        return event;
      }
      /**
       * Convenience method to add an event listener on a given element,
       * late bound to a named method on this element.
       *
       * @param {?EventTarget} node Element to add event listener to.
       * @param {string} eventName Name of event to listen for.
       * @param {string} methodName Name of handler method on `this` to call.
       * @return {void}
       * @override
       */


      listen(node, eventName, methodName) {
        node =
        /** @type {!EventTarget} */
        node || this;
        let hbl = this.__boundListeners || (this.__boundListeners = new WeakMap());
        let bl = hbl.get(node);

        if (!bl) {
          bl = {};
          hbl.set(node, bl);
        }

        let key = eventName + methodName;

        if (!bl[key]) {
          bl[key] = this._addMethodEventListenerToNode(
          /** @type {!Node} */
          node, eventName, methodName, this);
        }
      }
      /**
       * Convenience method to remove an event listener from a given element,
       * late bound to a named method on this element.
       *
       * @param {?EventTarget} node Element to remove event listener from.
       * @param {string} eventName Name of event to stop listening to.
       * @param {string} methodName Name of handler method on `this` to not call
       anymore.
       * @return {void}
       * @override
       */


      unlisten(node, eventName, methodName) {
        node =
        /** @type {!EventTarget} */
        node || this;

        let bl = this.__boundListeners && this.__boundListeners.get(
        /** @type {!Element} */
        node);

        let key = eventName + methodName;
        let handler = bl && bl[key];

        if (handler) {
          this._removeEventListenerFromNode(
          /** @type {!Node} */
          node, eventName, handler);

          bl[key] =
          /** @type {?} */
          null;
        }
      }
      /**
       * Override scrolling behavior to all direction, one direction, or none.
       *
       * Valid scroll directions:
       *   - 'all': scroll in any direction
       *   - 'x': scroll only in the 'x' direction
       *   - 'y': scroll only in the 'y' direction
       *   - 'none': disable scrolling for this node
       *
       * @param {string=} direction Direction to allow scrolling
       * Defaults to `all`.
       * @param {Element=} node Element to apply scroll direction setting.
       * Defaults to `this`.
       * @return {void}
       * @override
       */


      setScrollDirection(direction, node) {
        setTouchAction(
        /** @type {!Element} */
        node || this, DIRECTION_MAP[direction] || 'auto');
      }
      /* **** End Events **** */

      /**
       * Convenience method to run `querySelector` on this local DOM scope.
       *
       * This function calls `Polymer.dom(this.root).querySelector(slctr)`.
       *
       * @param {string} slctr Selector to run on this local DOM scope
       * @return {Element} Element found by the selector, or null if not found.
       * @override
       */


      $$(slctr) {
        // Note, no need to `wrap` this because root is always patched
        return this.root.querySelector(slctr);
      }
      /**
       * Return the element whose local dom within which this element
       * is contained. This is a shorthand for
       * `this.getRootNode().host`.
       * @this {Element}
       * @return {?Node} The element whose local dom within which this element is
       * contained.
       * @override
       */


      get domHost() {
        let root = wrap$1(this).getRootNode();
        return root instanceof DocumentFragment ?
        /** @type {ShadowRoot} */
        root.host : root;
      }
      /**
       * Force this element to distribute its children to its local dom.
       * This should not be necessary as of Polymer 2.0.2 and is provided only
       * for backwards compatibility.
       * @return {void}
       * @override
       */


      distributeContent() {
        const thisEl =
        /** @type {Element} */
        this;
        const domApi =
        /** @type {PolymerDomApi} */
        dom(thisEl);

        if (window.ShadyDOM && domApi.shadowRoot) {
          ShadyDOM.flush();
        }
      }
      /**
       * Returns a list of nodes that are the effective childNodes. The effective
       * childNodes list is the same as the element's childNodes except that
       * any `<content>` elements are replaced with the list of nodes distributed
       * to the `<content>`, the result of its `getDistributedNodes` method.
       * @return {!Array<!Node>} List of effective child nodes.
       * @suppress {invalidCasts} LegacyElementMixin must be applied to an
       *     HTMLElement
       * @override
       */


      getEffectiveChildNodes() {
        const thisEl =
        /** @type {Element} */
        this;
        const domApi =
        /** @type {PolymerDomApi} */
        dom(thisEl);
        return domApi.getEffectiveChildNodes();
      }
      /**
       * Returns a list of nodes distributed within this element that match
       * `selector`. These can be dom children or elements distributed to
       * children that are insertion points.
       * @param {string} selector Selector to run.
       * @return {!Array<!Node>} List of distributed elements that match selector.
       * @suppress {invalidCasts} LegacyElementMixin must be applied to an
       * HTMLElement
       * @override
       */


      queryDistributedElements(selector) {
        const thisEl =
        /** @type {Element} */
        this;
        const domApi =
        /** @type {PolymerDomApi} */
        dom(thisEl);
        return domApi.queryDistributedElements(selector);
      }
      /**
       * Returns a list of elements that are the effective children. The effective
       * children list is the same as the element's children except that
       * any `<content>` elements are replaced with the list of elements
       * distributed to the `<content>`.
       *
       * @return {!Array<!Node>} List of effective children.
       * @override
       */


      getEffectiveChildren() {
        let list = this.getEffectiveChildNodes();
        return list.filter(function (
        /** @type {!Node} */
        n) {
          return n.nodeType === Node.ELEMENT_NODE;
        });
      }
      /**
       * Returns a string of text content that is the concatenation of the
       * text content's of the element's effective childNodes (the elements
       * returned by <a href="#getEffectiveChildNodes>getEffectiveChildNodes</a>.
       *
       * @return {string} List of effective children.
       * @override
       */


      getEffectiveTextContent() {
        let cn = this.getEffectiveChildNodes();
        let tc = [];

        for (let i = 0, c; c = cn[i]; i++) {
          if (c.nodeType !== Node.COMMENT_NODE) {
            tc.push(c.textContent);
          }
        }

        return tc.join('');
      }
      /**
       * Returns the first effective childNode within this element that
       * match `selector`. These can be dom child nodes or elements distributed
       * to children that are insertion points.
       * @param {string} selector Selector to run.
       * @return {Node} First effective child node that matches selector.
       * @override
       */


      queryEffectiveChildren(selector) {
        let e$ = this.queryDistributedElements(selector);
        return e$ && e$[0];
      }
      /**
       * Returns a list of effective childNodes within this element that
       * match `selector`. These can be dom child nodes or elements distributed
       * to children that are insertion points.
       * @param {string} selector Selector to run.
       * @return {!Array<!Node>} List of effective child nodes that match
       *     selector.
       * @override
       */


      queryAllEffectiveChildren(selector) {
        return this.queryDistributedElements(selector);
      }
      /**
       * Returns a list of nodes distributed to this element's `<slot>`.
       *
       * If this element contains more than one `<slot>` in its local DOM,
       * an optional selector may be passed to choose the desired content.
       *
       * @param {string=} slctr CSS selector to choose the desired
       *   `<slot>`.  Defaults to `content`.
       * @return {!Array<!Node>} List of distributed nodes for the `<slot>`.
       * @override
       */


      getContentChildNodes(slctr) {
        // Note, no need to `wrap` this because root is always
        let content = this.root.querySelector(slctr || 'slot');
        return content ?
        /** @type {PolymerDomApi} */
        dom(content).getDistributedNodes() : [];
      }
      /**
       * Returns a list of element children distributed to this element's
       * `<slot>`.
       *
       * If this element contains more than one `<slot>` in its
       * local DOM, an optional selector may be passed to choose the desired
       * content.  This method differs from `getContentChildNodes` in that only
       * elements are returned.
       *
       * @param {string=} slctr CSS selector to choose the desired
       *   `<content>`.  Defaults to `content`.
       * @return {!Array<!HTMLElement>} List of distributed nodes for the
       *   `<slot>`.
       * @suppress {invalidCasts}
       * @override
       */


      getContentChildren(slctr) {
        let children =
        /** @type {!Array<!HTMLElement>} */
        this.getContentChildNodes(slctr).filter(function (n) {
          return n.nodeType === Node.ELEMENT_NODE;
        });
        return children;
      }
      /**
       * Checks whether an element is in this element's light DOM tree.
       *
       * @param {?Node} node The element to be checked.
       * @return {boolean} true if node is in this element's light DOM tree.
       * @suppress {invalidCasts} LegacyElementMixin must be applied to an
       * HTMLElement
       * @override
       */


      isLightDescendant(node) {
        const thisNode =
        /** @type {Node} */
        this;
        return thisNode !== node && wrap$1(thisNode).contains(node) && wrap$1(thisNode).getRootNode() === wrap$1(node).getRootNode();
      }
      /**
       * Checks whether an element is in this element's local DOM tree.
       *
       * @param {!Element} node The element to be checked.
       * @return {boolean} true if node is in this element's local DOM tree.
       * @override
       */


      isLocalDescendant(node) {
        return this.root === wrap$1(node).getRootNode();
      }
      /**
       * No-op for backwards compatibility. This should now be handled by
       * ShadyCss library.
       * @param  {!Element} container Container element to scope
       * @param  {boolean=} shouldObserve if true, start a mutation observer for added nodes to the container
       * @return {?MutationObserver} Returns a new MutationObserver on `container` if `shouldObserve` is true.
       * @override
       */


      scopeSubtree(container, shouldObserve = false) {
        return scopeSubtree(container, shouldObserve);
      }
      /**
       * Returns the computed style value for the given property.
       * @param {string} property The css property name.
       * @return {string} Returns the computed css property value for the given
       * `property`.
       * @suppress {invalidCasts} LegacyElementMixin must be applied to an
       *     HTMLElement
       * @override
       */


      getComputedStyleValue(property) {
        return styleInterface.getComputedStyleValue(
        /** @type {!Element} */
        this, property);
      } // debounce

      /**
       * Call `debounce` to collapse multiple requests for a named task into
       * one invocation which is made after the wait time has elapsed with
       * no new request.  If no wait time is given, the callback will be called
       * at microtask timing (guaranteed before paint).
       *
       *     debouncedClickAction(e) {
       *       // will not call `processClick` more than once per 100ms
       *       this.debounce('click', function() {
       *        this.processClick();
       *       } 100);
       *     }
       *
       * @param {string} jobName String to identify the debounce job.
       * @param {function():void} callback Function that is called (with `this`
       *   context) when the wait time elapses.
       * @param {number=} wait Optional wait time in milliseconds (ms) after the
       *   last signal that must elapse before invoking `callback`
       * @return {!Object} Returns a debouncer object on which exists the
       * following methods: `isActive()` returns true if the debouncer is
       * active; `cancel()` cancels the debouncer if it is active;
       * `flush()` immediately invokes the debounced callback if the debouncer
       * is active.
       * @override
       */


      debounce(jobName, callback, wait) {
        this._debouncers = this._debouncers || {};
        return this._debouncers[jobName] = Debouncer.debounce(this._debouncers[jobName], wait > 0 ? timeOut.after(wait) : microTask, callback.bind(this));
      }
      /**
       * Returns whether a named debouncer is active.
       *
       * @param {string} jobName The name of the debouncer started with `debounce`
       * @return {boolean} Whether the debouncer is active (has not yet fired).
       * @override
       */


      isDebouncerActive(jobName) {
        this._debouncers = this._debouncers || {};
        let debouncer = this._debouncers[jobName];
        return !!(debouncer && debouncer.isActive());
      }
      /**
       * Immediately calls the debouncer `callback` and inactivates it.
       *
       * @param {string} jobName The name of the debouncer started with `debounce`
       * @return {void}
       * @override
       */


      flushDebouncer(jobName) {
        this._debouncers = this._debouncers || {};
        let debouncer = this._debouncers[jobName];

        if (debouncer) {
          debouncer.flush();
        }
      }
      /**
       * Cancels an active debouncer.  The `callback` will not be called.
       *
       * @param {string} jobName The name of the debouncer started with `debounce`
       * @return {void}
       * @override
       */


      cancelDebouncer(jobName) {
        this._debouncers = this._debouncers || {};
        let debouncer = this._debouncers[jobName];

        if (debouncer) {
          debouncer.cancel();
        }
      }
      /**
       * Runs a callback function asynchronously.
       *
       * By default (if no waitTime is specified), async callbacks are run at
       * microtask timing, which will occur before paint.
       *
       * @param {!Function} callback The callback function to run, bound to
       *     `this`.
       * @param {number=} waitTime Time to wait before calling the
       *   `callback`.  If unspecified or 0, the callback will be run at microtask
       *   timing (before paint).
       * @return {number} Handle that may be used to cancel the async job.
       * @override
       */


      async(callback, waitTime) {
        return waitTime > 0 ? timeOut.run(callback.bind(this), waitTime) : ~microTask.run(callback.bind(this));
      }
      /**
       * Cancels an async operation started with `async`.
       *
       * @param {number} handle Handle returned from original `async` call to
       *   cancel.
       * @return {void}
       * @override
       */


      cancelAsync(handle) {
        handle < 0 ? microTask.cancel(~handle) : timeOut.cancel(handle);
      } // other

      /**
       * Convenience method for creating an element and configuring it.
       *
       * @param {string} tag HTML element tag to create.
       * @param {Object=} props Object of properties to configure on the
       *    instance.
       * @return {!Element} Newly created and configured element.
       * @override
       */


      create(tag, props) {
        let elt = document.createElement(tag);

        if (props) {
          if (elt.setProperties) {
            elt.setProperties(props);
          } else {
            for (let n in props) {
              elt[n] = props[n];
            }
          }
        }

        return elt;
      }
      /**
       * Polyfill for Element.prototype.matches, which is sometimes still
       * prefixed.
       *
       * @param {string} selector Selector to test.
       * @param {!Element=} node Element to test the selector against.
       * @return {boolean} Whether the element matches the selector.
       * @override
       */


      elementMatches(selector, node) {
        return matchesSelector(node || this, selector);
      }
      /**
       * Toggles an HTML attribute on or off.
       *
       * @param {string} name HTML attribute name
       * @param {boolean=} bool Boolean to force the attribute on or off.
       *    When unspecified, the state of the attribute will be reversed.
       * @return {boolean} true if the attribute now exists
       * @override
       */


      toggleAttribute(name, bool) {
        let node =
        /** @type {Element} */
        this;

        if (arguments.length === 3) {
          node =
          /** @type {Element} */
          arguments[2];
        }

        if (arguments.length == 1) {
          bool = !node.hasAttribute(name);
        }

        if (bool) {
          wrap$1(node).setAttribute(name, '');
          return true;
        } else {
          wrap$1(node).removeAttribute(name);
          return false;
        }
      }
      /**
       * Toggles a CSS class on or off.
       *
       * @param {string} name CSS class name
       * @param {boolean=} bool Boolean to force the class on or off.
       *    When unspecified, the state of the class will be reversed.
       * @param {Element=} node Node to target.  Defaults to `this`.
       * @return {void}
       * @override
       */


      toggleClass(name, bool, node) {
        node =
        /** @type {Element} */
        node || this;

        if (arguments.length == 1) {
          bool = !node.classList.contains(name);
        }

        if (bool) {
          node.classList.add(name);
        } else {
          node.classList.remove(name);
        }
      }
      /**
       * Cross-platform helper for setting an element's CSS `transform` property.
       *
       * @param {string} transformText Transform setting.
       * @param {Element=} node Element to apply the transform to.
       * Defaults to `this`
       * @return {void}
       * @override
       */


      transform(transformText, node) {
        node =
        /** @type {Element} */
        node || this;
        node.style.webkitTransform = transformText;
        node.style.transform = transformText;
      }
      /**
       * Cross-platform helper for setting an element's CSS `translate3d`
       * property.
       *
       * @param {number|string} x X offset.
       * @param {number|string} y Y offset.
       * @param {number|string} z Z offset.
       * @param {Element=} node Element to apply the transform to.
       * Defaults to `this`.
       * @return {void}
       * @override
       */


      translate3d(x, y, z, node) {
        node =
        /** @type {Element} */
        node || this;
        this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node);
      }
      /**
       * Removes an item from an array, if it exists.
       *
       * If the array is specified by path, a change notification is
       * generated, so that observers, data bindings and computed
       * properties watching that path can update.
       *
       * If the array is passed directly, **no change
       * notification is generated**.
       *
       * @param {string | !Array<number|string>} arrayOrPath Path to array from
       *     which to remove the item
       *   (or the array itself).
       * @param {*} item Item to remove.
       * @return {Array} Array containing item removed.
       * @override
       */


      arrayDelete(arrayOrPath, item) {
        let index;

        if (Array.isArray(arrayOrPath)) {
          index = arrayOrPath.indexOf(item);

          if (index >= 0) {
            return arrayOrPath.splice(index, 1);
          }
        } else {
          let arr = get(this, arrayOrPath);
          index = arr.indexOf(item);

          if (index >= 0) {
            return this.splice(arrayOrPath, index, 1);
          }
        }

        return null;
      } // logging

      /**
       * Facades `console.log`/`warn`/`error` as override point.
       *
       * @param {string} level One of 'log', 'warn', 'error'
       * @param {Array} args Array of strings or objects to log
       * @return {void}
       * @override
       */


      _logger(level, args) {
        // accept ['foo', 'bar'] and [['foo', 'bar']]
        if (Array.isArray(args) && args.length === 1 && Array.isArray(args[0])) {
          args = args[0];
        }

        switch (level) {
          case 'log':
          case 'warn':
          case 'error':
            console[level](...args);
        }
      }
      /**
       * Facades `console.log` as an override point.
       *
       * @param {...*} args Array of strings or objects to log
       * @return {void}
       * @override
       */


      _log(...args) {
        this._logger('log', args);
      }
      /**
       * Facades `console.warn` as an override point.
       *
       * @param {...*} args Array of strings or objects to log
       * @return {void}
       * @override
       */


      _warn(...args) {
        this._logger('warn', args);
      }
      /**
       * Facades `console.error` as an override point.
       *
       * @param {...*} args Array of strings or objects to log
       * @return {void}
       * @override
       */


      _error(...args) {
        this._logger('error', args);
      }
      /**
       * Formats a message using the element type an a method name.
       *
       * @param {string} methodName Method name to associate with message
       * @param {...*} args Array of strings or objects to log
       * @return {!Array} Array with formatting information for `console`
       *   logging.
       * @override
       */


      _logf(methodName, ...args) {
        return ['[%s::%s]', this.is, methodName, ...args];
      }

    }

    LegacyElement.prototype.is = '';
    return LegacyElement;
  });

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const lifecycleProps = {
    attached: true,
    detached: true,
    ready: true,
    created: true,
    beforeRegister: true,
    registered: true,
    attributeChanged: true,
    listeners: true,
    hostAttributes: true
  };
  const excludeOnInfo = {
    attached: true,
    detached: true,
    ready: true,
    created: true,
    beforeRegister: true,
    registered: true,
    attributeChanged: true,
    behaviors: true,
    _noAccessors: true
  };
  const excludeOnBehaviors = Object.assign({
    listeners: true,
    hostAttributes: true,
    properties: true,
    observers: true
  }, excludeOnInfo);

  function copyProperties(source, target, excludeProps) {
    const noAccessors = source._noAccessors;
    const propertyNames = Object.getOwnPropertyNames(source);

    for (let i = 0; i < propertyNames.length; i++) {
      let p = propertyNames[i];

      if (p in excludeProps) {
        continue;
      }

      if (noAccessors) {
        target[p] = source[p];
      } else {
        let pd = Object.getOwnPropertyDescriptor(source, p);

        if (pd) {
          // ensure property is configurable so that a later behavior can
          // re-configure it.
          pd.configurable = true;
          Object.defineProperty(target, p, pd);
        }
      }
    }
  }
  // 1.x
  // Behaviors were mixed in *in reverse order* and de-duped on the fly.
  // The rule was that behavior properties were copied onto the element
  // prototype if and only if the property did not already exist.
  // Given: Polymer{ behaviors: [A, B, C, A, B]}, property copy order was:
  // (1), B, (2), A, (3) C. This means prototype properties win over
  // B properties win over A win over C. This mirrors what would happen
  // with inheritance if element extended B extended A extended C.
  //
  // Again given, Polymer{ behaviors: [A, B, C, A, B]}, the resulting
  // `behaviors` array was [C, A, B].
  // Behavior lifecycle methods were called in behavior array order
  // followed by the element, e.g. (1) C.created, (2) A.created,
  // (3) B.created, (4) element.created. There was no support for
  // super, and "super-behavior" methods were callable only by name).
  //
  // 2.x
  // Behaviors are made into proper mixins which live in the
  // element's prototype chain. Behaviors are placed in the element prototype
  // eldest to youngest and de-duped youngest to oldest:
  // So, first [A, B, C, A, B] becomes [C, A, B] then,
  // the element prototype becomes (oldest) (1) PolymerElement, (2) class(C),
  // (3) class(A), (4) class(B), (5) class(Polymer({...})).
  // Result:
  // This means element properties win over B properties win over A win
  // over C. (same as 1.x)
  // If lifecycle is called (super then me), order is
  // (1) C.created, (2) A.created, (3) B.created, (4) element.created
  // (again same as 1.x)

  function applyBehaviors(proto, behaviors, lifecycle) {
    for (let i = 0; i < behaviors.length; i++) {
      applyInfo(proto, behaviors[i], lifecycle, excludeOnBehaviors);
    }
  }

  function applyInfo(proto, info, lifecycle, excludeProps) {
    copyProperties(info, proto, excludeProps);

    for (let p in lifecycleProps) {
      if (info[p]) {
        lifecycle[p] = lifecycle[p] || [];
        lifecycle[p].push(info[p]);
      }
    }
  }
  /**
   * @param {Array} behaviors List of behaviors to flatten.
   * @param {Array=} list Target list to flatten behaviors into.
   * @param {Array=} exclude List of behaviors to exclude from the list.
   * @return {!Array} Returns the list of flattened behaviors.
   */


  function flattenBehaviors(behaviors, list, exclude) {
    list = list || [];

    for (let i = behaviors.length - 1; i >= 0; i--) {
      let b = behaviors[i];

      if (b) {
        if (Array.isArray(b)) {
          flattenBehaviors(b, list);
        } else {
          // dedup
          if (list.indexOf(b) < 0 && (!exclude || exclude.indexOf(b) < 0)) {
            list.unshift(b);
          }
        }
      } else {
        console.warn('behavior is null, check for missing or 404 import');
      }
    }

    return list;
  }
  /**
   * Copies property descriptors from source to target, overwriting all fields
   * of any previous descriptor for a property *except* for `value`, which is
   * merged in from the target if it does not exist on the source.
   *
   * @param {*} target Target properties object
   * @param {*} source Source properties object
   */


  function mergeProperties(target, source) {
    for (const p in source) {
      const targetInfo = target[p];
      const sourceInfo = source[p];

      if (!('value' in sourceInfo) && targetInfo && 'value' in targetInfo) {
        target[p] = Object.assign({
          value: targetInfo.value
        }, sourceInfo);
      } else {
        target[p] = sourceInfo;
      }
    }
  }

  const LegacyElement = LegacyElementMixin(HTMLElement);
  /* Note about construction and extension of legacy classes.
    [Changed in Q4 2018 to optimize performance.]

    When calling `Polymer` or `mixinBehaviors`, the generated class below is
    made. The list of behaviors was previously made into one generated class per
    behavior, but this is no longer the case as behaviors are now called
    manually. Note, there may *still* be multiple generated classes in the
    element's prototype chain if extension is used with `mixinBehaviors`.

    The generated class is directly tied to the info object and behaviors
    used to create it. That list of behaviors is filtered so it's only the
    behaviors not active on the superclass. In order to call through to the
    entire list of lifecycle methods, it's important to call `super`.

    The element's `properties` and `observers` are controlled via the finalization
    mechanism provided by `PropertiesMixin`. `Properties` and `observers` are
    collected by manually traversing the prototype chain and merging.

    To limit changes, the `_registered` method is called via `_initializeProperties`
    and not `_finalizeClass`.

  */

  /**
   * @param {!PolymerInit} info Polymer info object
   * @param {function(new:HTMLElement)} Base base class to extend with info object
   * @param {Object=} behaviors behaviors to copy into the element
   * @return {function(new:HTMLElement)} Generated class
   * @suppress {checkTypes}
   * @private
   */

  function GenerateClassFromInfo(info, Base, behaviors) {
    // manages behavior and lifecycle processing (filled in after class definition)
    let behaviorList;
    const lifecycle = {};
    /** @private */

    class PolymerGenerated extends Base {
      // explicitly not calling super._finalizeClass

      /** @nocollapse */
      static _finalizeClass() {
        // if calling via a subclass that hasn't been generated, pass through to super
        if (!this.hasOwnProperty(JSCompiler_renameProperty('generatedFrom', this))) {
          // TODO(https://github.com/google/closure-compiler/issues/3240):
          //     Change back to just super.methodCall()
          Base._finalizeClass.call(this);
        } else {
          // interleave properties and observers per behavior and `info`
          if (behaviorList) {
            for (let i = 0, b; i < behaviorList.length; i++) {
              b = behaviorList[i];

              if (b.properties) {
                this.createProperties(b.properties);
              }

              if (b.observers) {
                this.createObservers(b.observers, b.properties);
              }
            }
          }

          if (info.properties) {
            this.createProperties(info.properties);
          }

          if (info.observers) {
            this.createObservers(info.observers, info.properties);
          } // make sure to prepare the element template


          this._prepareTemplate();
        }
      }
      /** @nocollapse */


      static get properties() {
        const properties = {};

        if (behaviorList) {
          for (let i = 0; i < behaviorList.length; i++) {
            mergeProperties(properties, behaviorList[i].properties);
          }
        }

        mergeProperties(properties, info.properties);
        return properties;
      }
      /** @nocollapse */


      static get observers() {
        let observers = [];

        if (behaviorList) {
          for (let i = 0, b; i < behaviorList.length; i++) {
            b = behaviorList[i];

            if (b.observers) {
              observers = observers.concat(b.observers);
            }
          }
        }

        if (info.observers) {
          observers = observers.concat(info.observers);
        }

        return observers;
      }
      /**
       * @return {void}
       */


      created() {
        super.created();
        const list = lifecycle.created;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            list[i].call(this);
          }
        }
      }
      /**
       * @return {void}
       */


      _registered() {
        /* NOTE: `beforeRegister` is called here for bc, but the behavior
          is different than in 1.x. In 1.0, the method was called *after*
          mixing prototypes together but *before* processing of meta-objects.
          However, dynamic effects can still be set here and can be done either
          in `beforeRegister` or `registered`. It is no longer possible to set
          `is` in `beforeRegister` as you could in 1.x.
        */
        // only proceed if the generated class' prototype has not been registered.
        const generatedProto = PolymerGenerated.prototype;

        if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
          generatedProto.__hasRegisterFinished = true; // ensure superclass is registered first.

          super._registered(); // copy properties onto the generated class lazily if we're optimizing,


          if (legacyOptimizations) {
            copyPropertiesToProto(generatedProto);
          } // make sure legacy lifecycle is called on the *element*'s prototype
          // and not the generated class prototype; if the element has been
          // extended, these are *not* the same.


          const proto = Object.getPrototypeOf(this);
          let list = lifecycle.beforeRegister;

          if (list) {
            for (let i = 0; i < list.length; i++) {
              list[i].call(proto);
            }
          }

          list = lifecycle.registered;

          if (list) {
            for (let i = 0; i < list.length; i++) {
              list[i].call(proto);
            }
          }
        }
      }
      /**
       * @return {void}
       */


      _applyListeners() {
        super._applyListeners();

        const list = lifecycle.listeners;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            const listeners = list[i];

            if (listeners) {
              for (let l in listeners) {
                this._addMethodEventListenerToNode(this, l, listeners[l]);
              }
            }
          }
        }
      } // note: exception to "super then me" rule;
      // do work before calling super so that super attributes
      // only apply if not already set.

      /**
       * @return {void}
       */


      _ensureAttributes() {
        const list = lifecycle.hostAttributes;

        if (list) {
          for (let i = list.length - 1; i >= 0; i--) {
            const hostAttributes = list[i];

            for (let a in hostAttributes) {
              this._ensureAttribute(a, hostAttributes[a]);
            }
          }
        }

        super._ensureAttributes();
      }
      /**
       * @return {void}
       */


      ready() {
        super.ready();
        let list = lifecycle.ready;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            list[i].call(this);
          }
        }
      }
      /**
       * @return {void}
       */


      attached() {
        super.attached();
        let list = lifecycle.attached;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            list[i].call(this);
          }
        }
      }
      /**
       * @return {void}
       */


      detached() {
        super.detached();
        let list = lifecycle.detached;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            list[i].call(this);
          }
        }
      }
      /**
       * Implements native Custom Elements `attributeChangedCallback` to
       * set an attribute value to a property via `_attributeToProperty`.
       *
       * @param {string} name Name of attribute that changed
       * @param {?string} old Old attribute value
       * @param {?string} value New attribute value
       * @return {void}
       */


      attributeChanged(name, old, value) {
        super.attributeChanged();
        let list = lifecycle.attributeChanged;

        if (list) {
          for (let i = 0; i < list.length; i++) {
            list[i].call(this, name, old, value);
          }
        }
      }

    } // apply behaviors, note actual copying is done lazily at first instance creation


    if (behaviors) {
      // NOTE: ensure the behavior is extending a class with
      // legacy element api. This is necessary since behaviors expect to be able
      // to access 1.x legacy api.
      if (!Array.isArray(behaviors)) {
        behaviors = [behaviors];
      }

      let superBehaviors = Base.prototype.behaviors; // get flattened, deduped list of behaviors *not* already on super class

      behaviorList = flattenBehaviors(behaviors, null, superBehaviors);
      PolymerGenerated.prototype.behaviors = superBehaviors ? superBehaviors.concat(behaviors) : behaviorList;
    }

    const copyPropertiesToProto = proto => {
      if (behaviorList) {
        applyBehaviors(proto, behaviorList, lifecycle);
      }

      applyInfo(proto, info, lifecycle, excludeOnInfo);
    }; // copy properties if we're not optimizing


    if (!legacyOptimizations) {
      copyPropertiesToProto(PolymerGenerated.prototype);
    }

    PolymerGenerated.generatedFrom = info;
    return PolymerGenerated;
  }
  /**
   * Generates a class that extends `LegacyElement` based on the
   * provided info object.  Metadata objects on the `info` object
   * (`properties`, `observers`, `listeners`, `behaviors`, `is`) are used
   * for Polymer's meta-programming systems, and any functions are copied
   * to the generated class.
   *
   * Valid "metadata" values are as follows:
   *
   * `is`: String providing the tag name to register the element under. In
   * addition, if a `dom-module` with the same id exists, the first template
   * in that `dom-module` will be stamped into the shadow root of this element,
   * with support for declarative event listeners (`on-...`), Polymer data
   * bindings (`[[...]]` and `{{...}}`), and id-based node finding into
   * `this.$`.
   *
   * `properties`: Object describing property-related metadata used by Polymer
   * features (key: property names, value: object containing property metadata).
   * Valid keys in per-property metadata include:
   * - `type` (String|Number|Object|Array|...): Used by
   *   `attributeChangedCallback` to determine how string-based attributes
   *   are deserialized to JavaScript property values.
   * - `notify` (boolean): Causes a change in the property to fire a
   *   non-bubbling event called `<property>-changed`. Elements that have
   *   enabled two-way binding to the property use this event to observe changes.
   * - `readOnly` (boolean): Creates a getter for the property, but no setter.
   *   To set a read-only property, use the private setter method
   *   `_setProperty(property, value)`.
   * - `observer` (string): Observer method name that will be called when
   *   the property changes. The arguments of the method are
   *   `(value, previousValue)`.
   * - `computed` (string): String describing method and dependent properties
   *   for computing the value of this property (e.g. `'computeFoo(bar, zot)'`).
   *   Computed properties are read-only by default and can only be changed
   *   via the return value of the computing method.
   *
   * `observers`: Array of strings describing multi-property observer methods
   *  and their dependent properties (e.g. `'observeABC(a, b, c)'`).
   *
   * `listeners`: Object describing event listeners to be added to each
   *  instance of this element (key: event name, value: method name).
   *
   * `behaviors`: Array of additional `info` objects containing metadata
   * and callbacks in the same format as the `info` object here which are
   * merged into this element.
   *
   * `hostAttributes`: Object listing attributes to be applied to the host
   *  once created (key: attribute name, value: attribute value).  Values
   *  are serialized based on the type of the value.  Host attributes should
   *  generally be limited to attributes such as `tabIndex` and `aria-...`.
   *  Attributes in `hostAttributes` are only applied if a user-supplied
   *  attribute is not already present (attributes in markup override
   *  `hostAttributes`).
   *
   * In addition, the following Polymer-specific callbacks may be provided:
   * - `registered`: called after first instance of this element,
   * - `created`: called during `constructor`
   * - `attached`: called during `connectedCallback`
   * - `detached`: called during `disconnectedCallback`
   * - `ready`: called before first `attached`, after all properties of
   *   this element have been propagated to its template and all observers
   *   have run
   *
   * @param {!PolymerInit} info Object containing Polymer metadata and functions
   *   to become class methods.
   * @template T
   * @param {function(T):T} mixin Optional mixin to apply to legacy base class
   *   before extending with Polymer metaprogramming.
   * @return {function(new:HTMLElement)} Generated class
   */


  const Class = function (info, mixin) {
    if (!info) {
      console.warn('Polymer.Class requires `info` argument');
    }

    let klass = mixin ? mixin(LegacyElement) : LegacyElement;
    klass = GenerateClassFromInfo(info, klass, info.behaviors); // decorate klass with registration info

    klass.is = klass.prototype.is = info.is;
    return klass;
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Legacy class factory and registration helper for defining Polymer
   * elements.
   *
   * This method is equivalent to
   *
   *     import {Class} from '@polymer/polymer/lib/legacy/class.js';
   *     customElements.define(info.is, Class(info));
   *
   * See `Class` for details on valid legacy metadata format for `info`.
   *
   * @global
   * @override
   * @function
   * @param {!PolymerInit} info Object containing Polymer metadata and functions
   *   to become class methods.
   * @return {function(new: HTMLElement)} Generated class
   * @suppress {duplicate, invalidCasts, checkTypes}
   */

  const Polymer = function (info) {
    // if input is a `class` (aka a function with a prototype), use the prototype
    // remember that the `constructor` will never be called
    let klass;

    if (typeof info === 'function') {
      klass = info;
    } else {
      klass = Polymer.Class(info);
    } // Copy opt out for `legacyNoObservedAttributes` from info object to class.


    if (info._legacyForceObservedAttributes) {
      klass.prototype._legacyForceObservedAttributes = info._legacyForceObservedAttributes;
    }

    customElements.define(klass.is,
    /** @type {!HTMLElement} */
    klass);
    return klass;
  };

  Polymer.Class = Class;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  function mutablePropertyChange(inst, property, value, old, mutableData) {
    let isObject;

    if (mutableData) {
      isObject = typeof value === 'object' && value !== null; // Pull `old` for Objects from temp cache, but treat `null` as a primitive

      if (isObject) {
        old = inst.__dataTemp[property];
      }
    } // Strict equality check, but return false for NaN===NaN


    let shouldChange = old !== value && (old === old || value === value); // Objects are stored in temporary cache (cleared at end of
    // turn), which is used for dirty-checking

    if (isObject && shouldChange) {
      inst.__dataTemp[property] = value;
    }

    return shouldChange;
  }
  /**
   * Element class mixin to skip strict dirty-checking for objects and arrays
   * (always consider them to be "dirty"), for use on elements utilizing
   * `PropertyEffects`
   *
   * By default, `PropertyEffects` performs strict dirty checking on
   * objects, which means that any deep modifications to an object or array will
   * not be propagated unless "immutable" data patterns are used (i.e. all object
   * references from the root to the mutation were changed).
   *
   * Polymer also provides a proprietary data mutation and path notification API
   * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
   * mutation and notification of deep changes in an object graph to all elements
   * bound to the same object graph.
   *
   * In cases where neither immutable patterns nor the data mutation API can be
   * used, applying this mixin will cause Polymer to skip dirty checking for
   * objects and arrays (always consider them to be "dirty").  This allows a
   * user to make a deep modification to a bound object graph, and then either
   * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
   * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
   * elements that wish to be updated based on deep mutations must apply this
   * mixin or otherwise skip strict dirty checking for objects/arrays.
   * Specifically, any elements in the binding tree between the source of a
   * mutation and the consumption of it must apply this mixin or enable the
   * `OptionalMutableData` mixin.
   *
   * In order to make the dirty check strategy configurable, see
   * `OptionalMutableData`.
   *
   * Note, the performance characteristics of propagating large object graphs
   * will be worse as opposed to using strict dirty checking with immutable
   * patterns or Polymer's path notification API.
   *
   * @mixinFunction
   * @polymer
   * @summary Element class mixin to skip strict dirty-checking for objects
   *   and arrays
   * @template T
   * @param {function(new:T)} superClass Class to apply mixin to.
   * @return {function(new:T)} superClass with mixin applied.
   */


  const MutableData = dedupingMixin(superClass => {
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_MutableData}
     */
    class MutableData extends superClass {
      /**
       * Overrides `PropertyEffects` to provide option for skipping
       * strict equality checking for Objects and Arrays.
       *
       * This method pulls the value to dirty check against from the `__dataTemp`
       * cache (rather than the normal `__data` cache) for Objects.  Since the temp
       * cache is cleared at the end of a turn, this implementation allows
       * side-effects of deep object changes to be processed by re-setting the
       * same object (using the temp cache as an in-turn backstop to prevent
       * cycles due to 2-way notification).
       *
       * @param {string} property Property name
       * @param {*} value New property value
       * @param {*} old Previous property value
       * @return {boolean} Whether the property should be considered a change
       * @protected
       */
      _shouldPropertyChange(property, value, old) {
        return mutablePropertyChange(this, property, value, old, true);
      }

    }

    return MutableData;
  });
  /**
   * Element class mixin to add the optional ability to skip strict
   * dirty-checking for objects and arrays (always consider them to be
   * "dirty") by setting a `mutable-data` attribute on an element instance.
   *
   * By default, `PropertyEffects` performs strict dirty checking on
   * objects, which means that any deep modifications to an object or array will
   * not be propagated unless "immutable" data patterns are used (i.e. all object
   * references from the root to the mutation were changed).
   *
   * Polymer also provides a proprietary data mutation and path notification API
   * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient
   * mutation and notification of deep changes in an object graph to all elements
   * bound to the same object graph.
   *
   * In cases where neither immutable patterns nor the data mutation API can be
   * used, applying this mixin will allow Polymer to skip dirty checking for
   * objects and arrays (always consider them to be "dirty").  This allows a
   * user to make a deep modification to a bound object graph, and then either
   * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath`
   * (e.g. `this.notifyPath('items')`) to update the tree.  Note that all
   * elements that wish to be updated based on deep mutations must apply this
   * mixin or otherwise skip strict dirty checking for objects/arrays.
   * Specifically, any elements in the binding tree between the source of a
   * mutation and the consumption of it must enable this mixin or apply the
   * `MutableData` mixin.
   *
   * While this mixin adds the ability to forgo Object/Array dirty checking,
   * the `mutableData` flag defaults to false and must be set on the instance.
   *
   * Note, the performance characteristics of propagating large object graphs
   * will be worse by relying on `mutableData: true` as opposed to using
   * strict dirty checking with immutable patterns or Polymer's path notification
   * API.
   *
   * @mixinFunction
   * @polymer
   * @summary Element class mixin to optionally skip strict dirty-checking
   *   for objects and arrays
   */

  const OptionalMutableData = dedupingMixin(superClass => {
    /**
     * @mixinClass
     * @polymer
     * @implements {Polymer_OptionalMutableData}
     */
    class OptionalMutableData extends superClass {
      /** @nocollapse */
      static get properties() {
        return {
          /**
           * Instance-level flag for configuring the dirty-checking strategy
           * for this element.  When true, Objects and Arrays will skip dirty
           * checking, otherwise strict equality checking will be used.
           */
          mutableData: Boolean
        };
      }
      /**
       * Overrides `PropertyEffects` to provide option for skipping
       * strict equality checking for Objects and Arrays.
       *
       * When `this.mutableData` is true on this instance, this method
       * pulls the value to dirty check against from the `__dataTemp` cache
       * (rather than the normal `__data` cache) for Objects.  Since the temp
       * cache is cleared at the end of a turn, this implementation allows
       * side-effects of deep object changes to be processed by re-setting the
       * same object (using the temp cache as an in-turn backstop to prevent
       * cycles due to 2-way notification).
       *
       * @param {string} property Property name
       * @param {*} value New property value
       * @param {*} old Previous property value
       * @return {boolean} Whether the property should be considered a change
       * @protected
       */


      _shouldPropertyChange(property, value, old) {
        return mutablePropertyChange(this, property, value, old, this.mutableData);
      }

    }

    return OptionalMutableData;
  }); // Export for use by legacy behavior

  MutableData._mutablePropertyChange = mutablePropertyChange;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  // machinery for propagating host properties to children. This is an ES5
  // class only because Babel (incorrectly) requires super() in the class
  // constructor even though no `this` is used and it returns an instance.

  let newInstance = null;
  /**
   * @constructor
   * @extends {HTMLTemplateElement}
   * @private
   */

  function HTMLTemplateElementExtension() {
    return newInstance;
  }

  HTMLTemplateElementExtension.prototype = Object.create(HTMLTemplateElement.prototype, {
    constructor: {
      value: HTMLTemplateElementExtension,
      writable: true
    }
  });
  /**
   * @constructor
   * @implements {Polymer_PropertyEffects}
   * @extends {HTMLTemplateElementExtension}
   * @private
   */

  const DataTemplate = PropertyEffects(HTMLTemplateElementExtension);
  /**
   * @constructor
   * @implements {Polymer_MutableData}
   * @extends {DataTemplate}
   * @private
   */

  const MutableDataTemplate = MutableData(DataTemplate); // Applies a DataTemplate subclass to a <template> instance

  function upgradeTemplate(template, constructor) {
    newInstance = template;
    Object.setPrototypeOf(template, constructor.prototype);
    new constructor();
    newInstance = null;
  }
  /**
   * Base class for TemplateInstance.
   * @constructor
   * @extends {HTMLElement}
   * @implements {Polymer_PropertyEffects}
   * @private
   */


  const templateInstanceBase = PropertyEffects(class {});
  function showHideChildren(hide, children) {
    for (let i = 0; i < children.length; i++) {
      let n = children[i]; // Ignore non-changes

      if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) {
        // clear and restore text
        if (n.nodeType === Node.TEXT_NODE) {
          if (hide) {
            n.__polymerTextContent__ = n.textContent;
            n.textContent = '';
          } else {
            n.textContent = n.__polymerTextContent__;
          } // remove and replace slot

        } else if (n.localName === 'slot') {
          if (hide) {
            n.__polymerReplaced__ = document.createComment('hidden-slot');
            wrap$1(wrap$1(n).parentNode).replaceChild(n.__polymerReplaced__, n);
          } else {
            const replace = n.__polymerReplaced__;

            if (replace) {
              wrap$1(wrap$1(replace).parentNode).replaceChild(n, replace);
            }
          }
        } // hide and show nodes
        else if (n.style) {
            if (hide) {
              n.__polymerDisplay__ = n.style.display;
              n.style.display = 'none';
            } else {
              n.style.display = n.__polymerDisplay__;
            }
          }
      }

      n.__hideTemplateChildren__ = hide;

      if (n._showHideChildren) {
        n._showHideChildren(hide);
      }
    }
  }
  /**
   * @polymer
   * @customElement
   * @appliesMixin PropertyEffects
   * @unrestricted
   */

  class TemplateInstanceBase extends templateInstanceBase {
    constructor(props) {
      super();

      this._configureProperties(props);
      /** @type {!StampedTemplate} */


      this.root = this._stampTemplate(this.__dataHost); // Save list of stamped children

      let children = [];
      /** @suppress {invalidCasts} */

      this.children =
      /** @type {!NodeList} */
      children; // Polymer 1.x did not use `Polymer.dom` here so not bothering.

      for (let n = this.root.firstChild; n; n = n.nextSibling) {
        children.push(n);
        n.__templatizeInstance = this;
      }

      if (this.__templatizeOwner && this.__templatizeOwner.__hideTemplateChildren__) {
        this._showHideChildren(true);
      } // Flush props only when props are passed if instance props exist
      // or when there isn't instance props.


      let options = this.__templatizeOptions;

      if (props && options.instanceProps || !options.instanceProps) {
        this._enableProperties();
      }
    }
    /**
     * Configure the given `props` by calling `_setPendingProperty`. Also
     * sets any properties stored in `__hostProps`.
     * @private
     * @param {Object} props Object of property name-value pairs to set.
     * @return {void}
     */


    _configureProperties(props) {
      let options = this.__templatizeOptions;

      if (options.forwardHostProp) {
        for (let hprop in this.__hostProps) {
          this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]);
        }
      } // Any instance props passed in the constructor will overwrite host props;
      // normally this would be a user error but we don't specifically filter them


      for (let iprop in props) {
        this._setPendingProperty(iprop, props[iprop]);
      }
    }
    /**
     * Forwards a host property to this instance.  This method should be
     * called on instances from the `options.forwardHostProp` callback
     * to propagate changes of host properties to each instance.
     *
     * Note this method enqueues the change, which are flushed as a batch.
     *
     * @param {string} prop Property or path name
     * @param {*} value Value of the property to forward
     * @return {void}
     */


    forwardHostProp(prop, value) {
      if (this._setPendingPropertyOrPath(prop, value, false, true)) {
        this.__dataHost._enqueueClient(this);
      }
    }
    /**
     * Override point for adding custom or simulated event handling.
     *
     * @override
     * @param {!Node} node Node to add event listener to
     * @param {string} eventName Name of event
     * @param {function(!Event):void} handler Listener function to add
     * @return {void}
     */


    _addEventListenerToNode(node, eventName, handler) {
      if (this._methodHost && this.__templatizeOptions.parentModel) {
        // If this instance should be considered a parent model, decorate
        // events this template instance as `model`
        this._methodHost._addEventListenerToNode(node, eventName, e => {
          e.model = this;
          handler(e);
        });
      } else {
        // Otherwise delegate to the template's host (which could be)
        // another template instance
        let templateHost = this.__dataHost.__dataHost;

        if (templateHost) {
          templateHost._addEventListenerToNode(node, eventName, handler);
        }
      }
    }
    /**
     * Shows or hides the template instance top level child elements. For
     * text nodes, `textContent` is removed while "hidden" and replaced when
     * "shown."
     * @param {boolean} hide Set to true to hide the children;
     * set to false to show them.
     * @return {void}
     * @protected
     */


    _showHideChildren(hide) {
      showHideChildren(hide, this.children);
    }
    /**
     * Overrides default property-effects implementation to intercept
     * textContent bindings while children are "hidden" and cache in
     * private storage for later retrieval.
     *
     * @override
     * @param {!Node} node The node to set a property on
     * @param {string} prop The property to set
     * @param {*} value The value to set
     * @return {void}
     * @protected
     */


    _setUnmanagedPropertyToNode(node, prop, value) {
      if (node.__hideTemplateChildren__ && node.nodeType == Node.TEXT_NODE && prop == 'textContent') {
        node.__polymerTextContent__ = value;
      } else {
        super._setUnmanagedPropertyToNode(node, prop, value);
      }
    }
    /**
     * Find the parent model of this template instance.  The parent model
     * is either another templatize instance that had option `parentModel: true`,
     * or else the host element.
     *
     * @return {!Polymer_PropertyEffects} The parent model of this instance
     */


    get parentModel() {
      let model = this.__parentModel;

      if (!model) {
        let options;
        model = this;

        do {
          // A template instance's `__dataHost` is a <template>
          // `model.__dataHost.__dataHost` is the template's host
          model = model.__dataHost.__dataHost;
        } while ((options = model.__templatizeOptions) && !options.parentModel);

        this.__parentModel = model;
      }

      return model;
    }
    /**
     * Stub of HTMLElement's `dispatchEvent`, so that effects that may
     * dispatch events safely no-op.
     *
     * @param {Event} event Event to dispatch
     * @return {boolean} Always true.
     * @override
     */


    dispatchEvent(event) {
      // eslint-disable-line no-unused-vars
      return true;
    }

  }
  /**
   * @constructor
   * @extends {TemplateInstanceBase}
   * @implements {Polymer_MutableData}
   * @private
   */

  const MutableTemplateInstanceBase = MutableData( // This cast shouldn't be neccessary, but Closure doesn't understand that
  // TemplateInstanceBase is a constructor function.

  /** @type {function(new:TemplateInstanceBase)} */
  TemplateInstanceBase);

  function findMethodHost(template) {
    // Technically this should be the owner of the outermost template.
    // In shadow dom, this is always getRootNode().host, but we can
    // approximate this via cooperation with our dataHost always setting
    // `_methodHost` as long as there were bindings (or id's) on this
    // instance causing it to get a dataHost.
    let templateHost = template.__dataHost;
    return templateHost && templateHost._methodHost || templateHost;
  }
  /* eslint-disable valid-jsdoc */

  /**
   * @suppress {missingProperties} class.prototype is not defined for some reason
   */


  function createTemplatizerClass(template, templateInfo, options) {
    /**
     * @constructor
     * @extends {TemplateInstanceBase}
     */
    let templatizerBase = options.mutableData ? MutableTemplateInstanceBase : TemplateInstanceBase; // Affordance for global mixins onto TemplatizeInstance

    if (templatize.mixin) {
      templatizerBase = templatize.mixin(templatizerBase);
    }
    /**
     * Anonymous class created by the templatize
     * @constructor
     * @private
     */


    let klass = class extends templatizerBase {};
    /** @override */

    klass.prototype.__templatizeOptions = options;

    klass.prototype._bindTemplate(template);

    addNotifyEffects(klass, template, templateInfo, options);
    return klass;
  }
  /**
   * Adds propagate effects from the template to the template instance for
   * properties that the host binds to the template using the `_host_` prefix.
   *
   * @suppress {missingProperties} class.prototype is not defined for some reason
   */


  function addPropagateEffects(target, templateInfo, options, methodHost) {
    let userForwardHostProp = options.forwardHostProp;

    if (userForwardHostProp && templateInfo.hasHostProps) {
      // Under the `removeNestedTemplates` optimization, a custom element like
      // `dom-if` or `dom-repeat` can itself be treated as the "template"; this
      // flag is used to switch between upgrading a `<template>` to be a property
      // effects client vs. adding the effects directly to the custom element
      const isTemplate = target.localName == 'template'; // Provide data API and property effects on memoized template class

      let klass = templateInfo.templatizeTemplateClass;

      if (!klass) {
        if (isTemplate) {
          /**
           * @constructor
           * @extends {DataTemplate}
           */
          let templatizedBase = options.mutableData ? MutableDataTemplate : DataTemplate; // NOTE: due to https://github.com/google/closure-compiler/issues/2928,
          // combining the next two lines into one assignment causes a spurious
          // type error.

          /** @private */

          class TemplatizedTemplate extends templatizedBase {}

          klass = templateInfo.templatizeTemplateClass = TemplatizedTemplate;
        } else {
          /**
           * @constructor
           * @extends {PolymerElement}
           */
          const templatizedBase = target.constructor; // Create a cached subclass of the base custom element class onto which
          // to put the template-specific propagate effects
          // NOTE: due to https://github.com/google/closure-compiler/issues/2928,
          // combining the next two lines into one assignment causes a spurious
          // type error.

          /** @private */

          class TemplatizedTemplateExtension extends templatizedBase {}

          klass = templateInfo.templatizeTemplateClass = TemplatizedTemplateExtension;
        } // Add template - >instances effects
        // and host <- template effects


        let hostProps = templateInfo.hostProps;

        for (let prop in hostProps) {
          klass.prototype._addPropertyEffect('_host_' + prop, klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE, {
            fn: createForwardHostPropEffect(prop, userForwardHostProp)
          });

          klass.prototype._createNotifyingProperty('_host_' + prop);
        }

        if (legacyWarnings && methodHost) {
          warnOnUndeclaredProperties(templateInfo, options, methodHost);
        }
      } // Mix any pre-bound data into __data; no need to flush this to
      // instances since they pull from the template at instance-time


      if (target.__dataProto) {
        // Note, generally `__dataProto` could be chained, but it's guaranteed
        // to not be since this is a vanilla template we just added effects to
        Object.assign(target.__data, target.__dataProto);
      }

      if (isTemplate) {
        upgradeTemplate(target, klass); // Clear any pending data for performance

        target.__dataTemp = {};
        target.__dataPending = null;
        target.__dataOld = null;

        target._enableProperties();
      } else {
        // Swizzle the cached subclass prototype onto the custom element
        Object.setPrototypeOf(target, klass.prototype); // Check for any pre-bound instance host properties, and do the
        // instance property delete/assign dance for those (directly into data;
        // not need to go through accessor since they are pulled at instance time)

        const hostProps = templateInfo.hostProps;

        for (let prop in hostProps) {
          prop = '_host_' + prop;

          if (prop in target) {
            const val = target[prop];
            delete target[prop];
            target.__data[prop] = val;
          }
        }
      }
    }
  }
  /* eslint-enable valid-jsdoc */


  function createForwardHostPropEffect(hostProp, userForwardHostProp) {
    return function forwardHostProp(template, prop, props) {
      userForwardHostProp.call(template.__templatizeOwner, prop.substring('_host_'.length), props[prop]);
    };
  }

  function addNotifyEffects(klass, template, templateInfo, options) {
    let hostProps = templateInfo.hostProps || {};

    for (let iprop in options.instanceProps) {
      delete hostProps[iprop];
      let userNotifyInstanceProp = options.notifyInstanceProp;

      if (userNotifyInstanceProp) {
        klass.prototype._addPropertyEffect(iprop, klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY, {
          fn: createNotifyInstancePropEffect(iprop, userNotifyInstanceProp)
        });
      }
    }

    if (options.forwardHostProp && template.__dataHost) {
      for (let hprop in hostProps) {
        // As we're iterating hostProps in this function, note whether
        // there were any, for an optimization in addPropagateEffects
        if (!templateInfo.hasHostProps) {
          templateInfo.hasHostProps = true;
        }

        klass.prototype._addPropertyEffect(hprop, klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY, {
          fn: createNotifyHostPropEffect()
        });
      }
    }
  }

  function createNotifyInstancePropEffect(instProp, userNotifyInstanceProp) {
    return function notifyInstanceProp(inst, prop, props) {
      userNotifyInstanceProp.call(inst.__templatizeOwner, inst, prop, props[prop]);
    };
  }

  function createNotifyHostPropEffect() {
    return function notifyHostProp(inst, prop, props) {
      inst.__dataHost._setPendingPropertyOrPath('_host_' + prop, props[prop], true, true);
    };
  }
  /**
   * Returns an anonymous `PropertyEffects` class bound to the
   * `<template>` provided.  Instancing the class will result in the
   * template being stamped into a document fragment stored as the instance's
   * `root` property, after which it can be appended to the DOM.
   *
   * Templates may utilize all Polymer data-binding features as well as
   * declarative event listeners.  Event listeners and inline computing
   * functions in the template will be called on the host of the template.
   *
   * The constructor returned takes a single argument dictionary of initial
   * property values to propagate into template bindings.  Additionally
   * host properties can be forwarded in, and instance properties can be
   * notified out by providing optional callbacks in the `options` dictionary.
   *
   * Valid configuration in `options` are as follows:
   *
   * - `forwardHostProp(property, value)`: Called when a property referenced
   *   in the template changed on the template's host. As this library does
   *   not retain references to templates instanced by the user, it is the
   *   templatize owner's responsibility to forward host property changes into
   *   user-stamped instances.  The `instance.forwardHostProp(property, value)`
   *    method on the generated class should be called to forward host
   *   properties into the template to prevent unnecessary property-changed
   *   notifications. Any properties referenced in the template that are not
   *   defined in `instanceProps` will be notified up to the template's host
   *   automatically.
   * - `instanceProps`: Dictionary of property names that will be added
   *   to the instance by the templatize owner.  These properties shadow any
   *   host properties, and changes within the template to these properties
   *   will result in `notifyInstanceProp` being called.
   * - `mutableData`: When `true`, the generated class will skip strict
   *   dirty-checking for objects and arrays (always consider them to be
   *   "dirty").
   * - `notifyInstanceProp(instance, property, value)`: Called when
   *   an instance property changes.  Users may choose to call `notifyPath`
   *   on e.g. the owner to notify the change.
   * - `parentModel`: When `true`, events handled by declarative event listeners
   *   (`on-event="handler"`) will be decorated with a `model` property pointing
   *   to the template instance that stamped it.  It will also be returned
   *   from `instance.parentModel` in cases where template instance nesting
   *   causes an inner model to shadow an outer model.
   *
   * All callbacks are called bound to the `owner`. Any context
   * needed for the callbacks (such as references to `instances` stamped)
   * should be stored on the `owner` such that they can be retrieved via
   * `this`.
   *
   * When `options.forwardHostProp` is declared as an option, any properties
   * referenced in the template will be automatically forwarded from the host of
   * the `<template>` to instances, with the exception of any properties listed in
   * the `options.instanceProps` object.  `instanceProps` are assumed to be
   * managed by the owner of the instances, either passed into the constructor
   * or set after the fact.  Note, any properties passed into the constructor will
   * always be set to the instance (regardless of whether they would normally
   * be forwarded from the host).
   *
   * Note that `templatize()` can be run only once for a given `<template>`.
   * Further calls will result in an error. Also, there is a special
   * behavior if the template was duplicated through a mechanism such as
   * `<dom-repeat>` or `<test-fixture>`. In this case, all calls to
   * `templatize()` return the same class for all duplicates of a template.
   * The class returned from `templatize()` is generated only once using
   * the `options` from the first call. This means that any `options`
   * provided to subsequent calls will be ignored. Therefore, it is very
   * important not to close over any variables inside the callbacks. Also,
   * arrow functions must be avoided because they bind the outer `this`.
   * Inside the callbacks, any contextual information can be accessed
   * through `this`, which points to the `owner`.
   *
   * @param {!HTMLTemplateElement} template Template to templatize
   * @param {Polymer_PropertyEffects=} owner Owner of the template instances;
   *   any optional callbacks will be bound to this owner.
   * @param {Object=} options Options dictionary (see summary for details)
   * @return {function(new:TemplateInstanceBase, Object=)} Generated class bound
   *   to the template provided
   * @suppress {invalidCasts}
   */


  function templatize(template, owner, options) {
    // Under strictTemplatePolicy, the templatized element must be owned
    // by a (trusted) Polymer element, indicated by existence of _methodHost;
    // e.g. for dom-if & dom-repeat in main document, _methodHost is null
    if (strictTemplatePolicy && !findMethodHost(template)) {
      throw new Error('strictTemplatePolicy: template owner not trusted');
    }

    options =
    /** @type {!TemplatizeOptions} */
    options || {};

    if (template.__templatizeOwner) {
      throw new Error('A <template> can only be templatized once');
    }

    template.__templatizeOwner = owner;
    const ctor = owner ? owner.constructor : TemplateInstanceBase;

    let templateInfo = ctor._parseTemplate(template); // Get memoized base class for the prototypical template, which
    // includes property effects for binding template & forwarding

    /**
     * @constructor
     * @extends {TemplateInstanceBase}
     */


    let baseClass = templateInfo.templatizeInstanceClass;

    if (!baseClass) {
      baseClass = createTemplatizerClass(template, templateInfo, options);
      templateInfo.templatizeInstanceClass = baseClass;
    }

    const methodHost = findMethodHost(template); // Host property forwarding must be installed onto template instance

    addPropagateEffects(template, templateInfo, options, methodHost); // Subclass base class and add reference for this specific template

    /** @private */

    let klass = class TemplateInstance extends baseClass {};
    /** @override */

    klass.prototype._methodHost = methodHost;
    /** @override */

    klass.prototype.__dataHost =
    /** @type {!DataTemplate} */
    template;
    /** @override */

    klass.prototype.__templatizeOwner =
    /** @type {!Object} */
    owner;
    /** @override */

    klass.prototype.__hostProps = templateInfo.hostProps;
    klass =
    /** @type {function(new:TemplateInstanceBase)} */
    klass; //eslint-disable-line no-self-assign

    return klass;
  }

  function warnOnUndeclaredProperties(templateInfo, options, methodHost) {
    const declaredProps = methodHost.constructor._properties;
    const {
      propertyEffects
    } = templateInfo;
    const {
      instanceProps
    } = options;

    for (let prop in propertyEffects) {
      // Ensure properties with template effects are declared on the outermost
      // host (`methodHost`), unless they are instance props or static functions
      if (!declaredProps[prop] && !(instanceProps && instanceProps[prop])) {
        const effects = propertyEffects[prop];

        for (let i = 0; i < effects.length; i++) {
          const {
            part
          } = effects[i].info;

          if (!(part.signature && part.signature.static)) {
            console.warn(`Property '${prop}' used in template but not ` + `declared in 'properties'; attribute will not be observed.`);
            break;
          }
        }
      }
    }
  }
  /**
   * Returns the template "model" associated with a given element, which
   * serves as the binding scope for the template instance the element is
   * contained in. A template model is an instance of
   * `TemplateInstanceBase`, and should be used to manipulate data
   * associated with this template instance.
   *
   * Example:
   *
   *   let model = modelForElement(el);
   *   if (model.index < 10) {
   *     model.set('item.checked', true);
   *   }
   *
   * @param {HTMLElement} template The model will be returned for
   *   elements stamped from this template (accepts either an HTMLTemplateElement)
   *   or a `<dom-if>`/`<dom-repeat>` element when using `removeNestedTemplates`
   *   optimization.
   * @param {Node=} node Node for which to return a template model.
   * @return {TemplateInstanceBase} Template instance representing the
   *   binding scope for the element
   */


  function modelForElement(template, node) {
    let model;

    while (node) {
      // An element with a __templatizeInstance marks the top boundary
      // of a scope; walk up until we find one, and then ensure that
      // its __dataHost matches `this`, meaning this dom-repeat stamped it
      if (model = node.__dataHost ? node : node.__templatizeInstance) {
        // Found an element stamped by another template; keep walking up
        // from its __dataHost
        if (model.__dataHost != template) {
          node = model.__dataHost;
        } else {
          return model;
        }
      } else {
        // Still in a template scope, keep going up until
        // a __templatizeInstance is found
        node = wrap$1(node).parentNode;
      }
    }

    return null;
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  let elementsHidden = false;
  /**
   * @return {boolean} True if elements will be hidden globally
   */

  function hideElementsGlobally() {
    if (legacyOptimizations && !useShadow) {
      if (!elementsHidden) {
        elementsHidden = true;
        const style = document.createElement('style');
        style.textContent = 'dom-bind,dom-if,dom-repeat{display:none;}';
        document.head.appendChild(style);
      }

      return true;
    }

    return false;
  }

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @constructor
   * @extends {HTMLElement}
   * @implements {Polymer_PropertyEffects}
   * @implements {Polymer_OptionalMutableData}
   * @implements {Polymer_GestureEventListeners}
   * @private
   */

  const domBindBase = GestureEventListeners(OptionalMutableData(PropertyEffects(HTMLElement)));
  /**
   * Custom element to allow using Polymer's template features (data binding,
   * declarative event listeners, etc.) in the main document without defining
   * a new custom element.
   *
   * `<template>` tags utilizing bindings may be wrapped with the `<dom-bind>`
   * element, which will immediately stamp the wrapped template into the main
   * document and bind elements to the `dom-bind` element itself as the
   * binding scope.
   *
   * @polymer
   * @customElement
   * @appliesMixin PropertyEffects
   * @appliesMixin OptionalMutableData
   * @appliesMixin GestureEventListeners
   * @extends {domBindBase}
   * @summary Custom element to allow using Polymer's template features (data
   *   binding, declarative event listeners, etc.) in the main document.
   */

  class DomBind extends domBindBase {
    static get observedAttributes() {
      return ['mutable-data'];
    }

    constructor() {
      super();

      if (strictTemplatePolicy) {
        throw new Error(`strictTemplatePolicy: dom-bind not allowed`);
      }

      this.root = null;
      this.$ = null;
      this.__children = null;
    }
    /* eslint-disable no-unused-vars */

    /**
     * @override
     * @param {string} name Name of attribute that changed
     * @param {?string} old Old attribute value
     * @param {?string} value New attribute value
     * @param {?string} namespace Attribute namespace.
     * @return {void}
     */


    attributeChangedCallback(name, old, value, namespace) {
      // assumes only one observed attribute
      this.mutableData = true;
    }
    /**
     * @override
     * @return {void}
     */


    connectedCallback() {
      if (!hideElementsGlobally()) {
        this.style.display = 'none';
      }

      this.render();
    }
    /**
     * @override
     * @return {void}
     */


    disconnectedCallback() {
      this.__removeChildren();
    }

    __insertChildren() {
      wrap$1(wrap$1(this).parentNode).insertBefore(this.root, this);
    }

    __removeChildren() {
      if (this.__children) {
        for (let i = 0; i < this.__children.length; i++) {
          this.root.appendChild(this.__children[i]);
        }
      }
    }
    /**
     * Forces the element to render its content. This is typically only
     * necessary to call if HTMLImports with the async attribute are used.
     * @return {void}
     */


    render() {
      let template;

      if (!this.__children) {
        template =
        /** @type {?HTMLTemplateElement} */
        template || this.querySelector('template');

        if (!template) {
          // Wait until childList changes and template should be there by then
          let observer = new MutationObserver(() => {
            template =
            /** @type {HTMLTemplateElement} */
            this.querySelector('template');

            if (template) {
              observer.disconnect();
              this.render();
            } else {
              throw new Error('dom-bind requires a <template> child');
            }
          });
          observer.observe(this, {
            childList: true
          });
          return;
        }

        this.root = this._stampTemplate(
        /** @type {!HTMLTemplateElement} */
        template);
        this.$ = this.root.$;
        this.__children = [];

        for (let n = this.root.firstChild; n; n = n.nextSibling) {
          this.__children[this.__children.length] = n;
        }

        this._enableProperties();
      }

      this.__insertChildren();

      this.dispatchEvent(new CustomEvent('dom-change', {
        bubbles: true,
        composed: true
      }));
    }

  }
  customElements.define('dom-bind', DomBind);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Class representing a static string value which can be used to filter
   * strings by asseting that they have been created via this class. The
   * `value` property returns the string passed to the constructor.
   */

  class LiteralString {
    constructor(string) {
      /** @type {string} */
      this.value = string.toString();
    }
    /**
     * @return {string} LiteralString string value
     * @override
     */


    toString() {
      return this.value;
    }

  }
  /**
   * @param {*} value Object to stringify into HTML
   * @return {string} HTML stringified form of `obj`
   */


  function literalValue(value) {
    if (value instanceof LiteralString) {
      return (
        /** @type {!LiteralString} */
        value.value
      );
    } else {
      throw new Error(`non-literal value passed to Polymer's htmlLiteral function: ${value}`);
    }
  }
  /**
   * @param {*} value Object to stringify into HTML
   * @return {string} HTML stringified form of `obj`
   */


  function htmlValue(value) {
    if (value instanceof HTMLTemplateElement) {
      return (
        /** @type {!HTMLTemplateElement } */
        value.innerHTML
      );
    } else if (value instanceof LiteralString) {
      return literalValue(value);
    } else {
      throw new Error(`non-template value passed to Polymer's html function: ${value}`);
    }
  }
  /**
   * A template literal tag that creates an HTML <template> element from the
   * contents of the string.
   *
   * This allows you to write a Polymer Template in JavaScript.
   *
   * Templates can be composed by interpolating `HTMLTemplateElement`s in
   * expressions in the JavaScript template literal. The nested template's
   * `innerHTML` is included in the containing template.  The only other
   * values allowed in expressions are those returned from `htmlLiteral`
   * which ensures only literal values from JS source ever reach the HTML, to
   * guard against XSS risks.
   *
   * All other values are disallowed in expressions to help prevent XSS
   * attacks; however, `htmlLiteral` can be used to compose static
   * string values into templates. This is useful to compose strings into
   * places that do not accept html, like the css text of a `style`
   * element.
   *
   * Example:
   *
   *     static get template() {
   *       return html`
   *         <style>:host{ content:"..." }</style>
   *         <div class="shadowed">${this.partialTemplate}</div>
   *         ${super.template}
   *       `;
   *     }
   *     static get partialTemplate() { return html`<span>Partial!</span>`; }
   *
   * @param {!ITemplateArray} strings Constant parts of tagged template literal
   * @param {...*} values Variable parts of tagged template literal
   * @return {!HTMLTemplateElement} Constructed HTMLTemplateElement
   */


  const html$1 = function html(strings, ...values) {
    const template =
    /** @type {!HTMLTemplateElement} */
    document.createElement('template');
    template.innerHTML = values.reduce((acc, v, idx) => acc + htmlValue(v) + strings[idx + 1], strings[0]);
    return template;
  };

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Base class that provides the core API for Polymer's meta-programming
   * features including template stamping, data-binding, attribute deserialization,
   * and property change observation.
   *
   * @customElement
   * @polymer
   * @constructor
   * @implements {Polymer_ElementMixin}
   * @extends HTMLElement
   * @appliesMixin ElementMixin
   * @summary Custom element base class that provides the core API for Polymer's
   *   key meta-programming features including template stamping, data-binding,
   *   attribute deserialization, and property change observation
   */

  const PolymerElement = ElementMixin(HTMLElement);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @constructor
   * @implements {Polymer_OptionalMutableData}
   * @extends {PolymerElement}
   * @private
   */

  const domRepeatBase = OptionalMutableData(PolymerElement);
  /**
   * The `<dom-repeat>` element will automatically stamp and binds one instance
   * of template content to each object in a user-provided array.
   * `dom-repeat` accepts an `items` property, and one instance of the template
   * is stamped for each item into the DOM at the location of the `dom-repeat`
   * element.  The `item` property will be set on each instance's binding
   * scope, thus templates should bind to sub-properties of `item`.
   *
   * Example:
   *
   * ```html
   * <dom-module id="employee-list">
   *
   *   <template>
   *
   *     <div> Employee list: </div>
   *     <dom-repeat items="{{employees}}">
   *       <template>
   *         <div>First name: <span>{{item.first}}</span></div>
   *         <div>Last name: <span>{{item.last}}</span></div>
   *       </template>
   *     </dom-repeat>
   *
   *   </template>
   *
   * </dom-module>
   * ```
   *
   * With the following custom element definition:
   *
   * ```js
   * class EmployeeList extends PolymerElement {
   *   static get is() { return 'employee-list'; }
   *   static get properties() {
   *     return {
   *       employees: {
   *         value() {
   *           return [
   *             {first: 'Bob', last: 'Smith'},
   *             {first: 'Sally', last: 'Johnson'},
   *             ...
   *           ];
   *         }
   *       }
   *     };
   *   }
   * }
   * ```
   *
   * Notifications for changes to items sub-properties will be forwarded to template
   * instances, which will update via the normal structured data notification system.
   *
   * Mutations to the `items` array itself should be made using the Array
   * mutation API's on the PropertyEffects mixin (`push`, `pop`, `splice`,
   * `shift`, `unshift`), and template instances will be kept in sync with the
   * data in the array.
   *
   * Events caught by event handlers within the `dom-repeat` template will be
   * decorated with a `model` property, which represents the binding scope for
   * each template instance.  The model should be used to manipulate data on the
   * instance, for example `event.model.set('item.checked', true);`.
   *
   * Alternatively, the model for a template instance for an element stamped by
   * a `dom-repeat` can be obtained using the `modelForElement` API on the
   * `dom-repeat` that stamped it, for example
   * `this.$.domRepeat.modelForElement(event.target).set('item.checked', true);`.
   * This may be useful for manipulating instance data of event targets obtained
   * by event handlers on parents of the `dom-repeat` (event delegation).
   *
   * A view-specific filter/sort may be applied to each `dom-repeat` by supplying a
   * `filter` and/or `sort` property.  This may be a string that names a function on
   * the host, or a function may be assigned to the property directly.  The functions
   * should implemented following the standard `Array` filter/sort API.
   *
   * In order to re-run the filter or sort functions based on changes to sub-fields
   * of `items`, the `observe` property may be set as a space-separated list of
   * `item` sub-fields that should cause a re-filter/sort when modified.  If
   * the filter or sort function depends on properties not contained in `items`,
   * the user should observe changes to those properties and call `render` to update
   * the view based on the dependency change.
   *
   * For example, for an `dom-repeat` with a filter of the following:
   *
   * ```js
   * isEngineer(item) {
   *   return item.type == 'engineer' || item.manager.type == 'engineer';
   * }
   * ```
   *
   * Then the `observe` property should be configured as follows:
   *
   * ```html
   * <dom-repeat items="{{employees}}" filter="isEngineer" observe="type manager.type">
   * ```
   *
   * @customElement
   * @polymer
   * @extends {domRepeatBase}
   * @appliesMixin OptionalMutableData
   * @summary Custom element for stamping instance of a template bound to
   *   items in an array.
   */

  class DomRepeat extends domRepeatBase {
    // Not needed to find template; can be removed once the analyzer
    // can find the tag name from customElements.define call
    static get is() {
      return 'dom-repeat';
    }

    static get template() {
      return null;
    }

    static get properties() {
      /**
       * Fired whenever DOM is added or removed by this template (by
       * default, rendering occurs lazily).  To force immediate rendering, call
       * `render`.
       *
       * @event dom-change
       */
      return {
        /**
         * An array containing items determining how many instances of the template
         * to stamp and that that each template instance should bind to.
         */
        items: {
          type: Array
        },

        /**
         * The name of the variable to add to the binding scope for the array
         * element associated with a given template instance.
         */
        as: {
          type: String,
          value: 'item'
        },

        /**
         * The name of the variable to add to the binding scope with the index
         * of the instance in the sorted and filtered list of rendered items.
         * Note, for the index in the `this.items` array, use the value of the
         * `itemsIndexAs` property.
         */
        indexAs: {
          type: String,
          value: 'index'
        },

        /**
         * The name of the variable to add to the binding scope with the index
         * of the instance in the `this.items` array. Note, for the index of
         * this instance in the sorted and filtered list of rendered items,
         * use the value of the `indexAs` property.
         */
        itemsIndexAs: {
          type: String,
          value: 'itemsIndex'
        },

        /**
         * A function that should determine the sort order of the items.  This
         * property should either be provided as a string, indicating a method
         * name on the element's host, or else be an actual function.  The
         * function should match the sort function passed to `Array.sort`.
         * Using a sort function has no effect on the underlying `items` array.
         */
        sort: {
          type: Function,
          observer: '__sortChanged'
        },

        /**
         * A function that can be used to filter items out of the view.  This
         * property should either be provided as a string, indicating a method
         * name on the element's host, or else be an actual function.  The
         * function should match the sort function passed to `Array.filter`.
         * Using a filter function has no effect on the underlying `items` array.
         */
        filter: {
          type: Function,
          observer: '__filterChanged'
        },

        /**
         * When using a `filter` or `sort` function, the `observe` property
         * should be set to a space-separated list of the names of item
         * sub-fields that should trigger a re-sort or re-filter when changed.
         * These should generally be fields of `item` that the sort or filter
         * function depends on.
         */
        observe: {
          type: String,
          observer: '__observeChanged'
        },

        /**
         * When using a `filter` or `sort` function, the `delay` property
         * determines a debounce time in ms after a change to observed item
         * properties that must pass before the filter or sort is re-run.
         * This is useful in rate-limiting shuffling of the view when
         * item changes may be frequent.
         */
        delay: Number,

        /**
         * Count of currently rendered items after `filter` (if any) has been applied.
         * If "chunking mode" is enabled, `renderedItemCount` is updated each time a
         * set of template instances is rendered.
         *
         */
        renderedItemCount: {
          type: Number,
          notify: !suppressTemplateNotifications,
          readOnly: true
        },

        /**
         * When greater than zero, defines an initial count of template instances
         * to render after setting the `items` array, before the next paint, and
         * puts the `dom-repeat` into "chunking mode".  The remaining items (and
         * any future items as a result of pushing onto the array) will be created
         * and rendered incrementally at each animation frame thereof until all
         * instances have been rendered.
         */
        initialCount: {
          type: Number
        },

        /**
         * When `initialCount` is used, this property defines a frame rate (in
         * fps) to target by throttling the number of instances rendered each
         * frame to not exceed the budget for the target frame rate.  The
         * framerate is effectively the number of `requestAnimationFrame`s that
         * it tries to allow to actually fire in a given second. It does this
         * by measuring the time between `rAF`s and continuously adjusting the
         * number of items created each `rAF` to maintain the target framerate.
         * Setting this to a higher number allows lower latency and higher
         * throughput for event handlers and other tasks, but results in a
         * longer time for the remaining items to complete rendering.
         */
        targetFramerate: {
          type: Number,
          value: 20
        },
        _targetFrameTime: {
          type: Number,
          computed: '__computeFrameTime(targetFramerate)'
        },

        /**
         * When the global `suppressTemplateNotifications` setting is used, setting
         * `notifyDomChange: true` will enable firing `dom-change` events on this
         * element.
         */
        notifyDomChange: {
          type: Boolean
        },

        /**
         * When chunking is enabled via `initialCount` and the `items` array is
         * set to a new array, this flag controls whether the previously rendered
         * instances are reused or not.
         *
         * When `true`, any previously rendered template instances are updated in
         * place to their new item values synchronously in one shot, and then any
         * further items (if any) are chunked out.  When `false`, the list is
         * returned back to its `initialCount` (any instances over the initial
         * count are discarded) and the remainder of the list is chunked back in.
         * Set this to `true` to avoid re-creating the list and losing scroll
         * position, although note that when changing the list to completely
         * different data the render thread will be blocked until all existing
         * instances are updated to their new data.
         */
        reuseChunkedInstances: {
          type: Boolean
        }
      };
    }

    static get observers() {
      return ['__itemsChanged(items.*)'];
    }

    constructor() {
      super();
      this.__instances = [];
      this.__renderDebouncer = null;
      this.__itemsIdxToInstIdx = {};
      this.__chunkCount = null;
      this.__renderStartTime = null;
      this.__itemsArrayChanged = false;
      this.__shouldMeasureChunk = false;
      this.__shouldContinueChunking = false;
      this.__chunkingId = 0;
      this.__sortFn = null;
      this.__filterFn = null;
      this.__observePaths = null;
      /** @type {?function(new:TemplateInstanceBase, Object=)} */

      this.__ctor = null;
      this.__isDetached = true;
      this.template = null;
      /** @type {TemplateInfo} */

      this._templateInfo;
    }
    /**
     * @override
     * @return {void}
     */


    disconnectedCallback() {
      super.disconnectedCallback();
      this.__isDetached = true;

      for (let i = 0; i < this.__instances.length; i++) {
        this.__detachInstance(i);
      }
    }
    /**
     * @override
     * @return {void}
     */


    connectedCallback() {
      super.connectedCallback();

      if (!hideElementsGlobally()) {
        this.style.display = 'none';
      } // only perform attachment if the element was previously detached.


      if (this.__isDetached) {
        this.__isDetached = false;
        let wrappedParent = wrap$1(wrap$1(this).parentNode);

        for (let i = 0; i < this.__instances.length; i++) {
          this.__attachInstance(i, wrappedParent);
        }
      }
    }

    __ensureTemplatized() {
      // Templatizing (generating the instance constructor) needs to wait
      // until ready, since won't have its template content handed back to
      // it until then
      if (!this.__ctor) {
        // When `removeNestedTemplates` is true, the "template" is the element
        // itself, which has been given a `_templateInfo` property
        const thisAsTemplate =
        /** @type {!HTMLTemplateElement} */

        /** @type {!HTMLElement} */
        this;
        let template = this.template = thisAsTemplate._templateInfo ? thisAsTemplate :
        /** @type {!HTMLTemplateElement} */
        this.querySelector('template');

        if (!template) {
          // Wait until childList changes and template should be there by then
          let observer = new MutationObserver(() => {
            if (this.querySelector('template')) {
              observer.disconnect();

              this.__render();
            } else {
              throw new Error('dom-repeat requires a <template> child');
            }
          });
          observer.observe(this, {
            childList: true
          });
          return false;
        } // Template instance props that should be excluded from forwarding


        let instanceProps = {};
        instanceProps[this.as] = true;
        instanceProps[this.indexAs] = true;
        instanceProps[this.itemsIndexAs] = true;
        this.__ctor = templatize(template, this, {
          mutableData: this.mutableData,
          parentModel: true,
          instanceProps: instanceProps,

          /**
           * @this {DomRepeat}
           * @param {string} prop Property to set
           * @param {*} value Value to set property to
           */
          forwardHostProp: function (prop, value) {
            let i$ = this.__instances;

            for (let i = 0, inst; i < i$.length && (inst = i$[i]); i++) {
              inst.forwardHostProp(prop, value);
            }
          },

          /**
           * @this {DomRepeat}
           * @param {Object} inst Instance to notify
           * @param {string} prop Property to notify
           * @param {*} value Value to notify
           */
          notifyInstanceProp: function (inst, prop, value) {
            if (matches(this.as, prop)) {
              let idx = inst[this.itemsIndexAs];

              if (prop == this.as) {
                this.items[idx] = value;
              }

              let path = translate(this.as, `${JSCompiler_renameProperty('items', this)}.${idx}`, prop);
              this.notifyPath(path, value);
            }
          }
        });
      }

      return true;
    }

    __getMethodHost() {
      // Technically this should be the owner of the outermost template.
      // In shadow dom, this is always getRootNode().host, but we can
      // approximate this via cooperation with our dataHost always setting
      // `_methodHost` as long as there were bindings (or id's) on this
      // instance causing it to get a dataHost.
      return this.__dataHost._methodHost || this.__dataHost;
    }

    __functionFromPropertyValue(functionOrMethodName) {
      if (typeof functionOrMethodName === 'string') {
        let methodName = functionOrMethodName;

        let obj = this.__getMethodHost();

        return function () {
          return obj[methodName].apply(obj, arguments);
        };
      }

      return functionOrMethodName;
    }

    __sortChanged(sort) {
      this.__sortFn = this.__functionFromPropertyValue(sort);

      if (this.items) {
        this.__debounceRender(this.__render);
      }
    }

    __filterChanged(filter) {
      this.__filterFn = this.__functionFromPropertyValue(filter);

      if (this.items) {
        this.__debounceRender(this.__render);
      }
    }

    __computeFrameTime(rate) {
      return Math.ceil(1000 / rate);
    }

    __observeChanged() {
      this.__observePaths = this.observe && this.observe.replace('.*', '.').split(' ');
    }

    __handleObservedPaths(path) {
      // Handle cases where path changes should cause a re-sort/filter
      if (this.__sortFn || this.__filterFn) {
        if (!path) {
          // Always re-render if the item itself changed
          this.__debounceRender(this.__render, this.delay);
        } else if (this.__observePaths) {
          // Otherwise, re-render if the path changed matches an observed path
          let paths = this.__observePaths;

          for (let i = 0; i < paths.length; i++) {
            if (path.indexOf(paths[i]) === 0) {
              this.__debounceRender(this.__render, this.delay);
            }
          }
        }
      }
    }

    __itemsChanged(change) {
      if (this.items && !Array.isArray(this.items)) {
        console.warn('dom-repeat expected array for `items`, found', this.items);
      } // If path was to an item (e.g. 'items.3' or 'items.3.foo'), forward the
      // path to that instance synchronously (returns false for non-item paths)


      if (!this.__handleItemPath(change.path, change.value)) {
        // Otherwise, the array was reset ('items') or spliced ('items.splices'),
        // so queue a render.  Restart chunking when the items changed (for
        // backward compatibility), unless `reuseChunkedInstances` option is set
        if (change.path === 'items') {
          this.__itemsArrayChanged = true;
        }

        this.__debounceRender(this.__render);
      }
    }
    /**
     * @param {function(this:DomRepeat)} fn Function to debounce.
     * @param {number=} delay Delay in ms to debounce by.
     */


    __debounceRender(fn, delay = 0) {
      this.__renderDebouncer = Debouncer.debounce(this.__renderDebouncer, delay > 0 ? timeOut.after(delay) : microTask, fn.bind(this));
      enqueueDebouncer(this.__renderDebouncer);
    }
    /**
     * Forces the element to render its content. Normally rendering is
     * asynchronous to a provoking change. This is done for efficiency so
     * that multiple changes trigger only a single render. The render method
     * should be called if, for example, template rendering is required to
     * validate application state.
     * @return {void}
     */


    render() {
      // Queue this repeater, then flush all in order
      this.__debounceRender(this.__render);

      flush();
    }

    __render() {
      if (!this.__ensureTemplatized()) {
        // No template found yet
        return;
      }

      let items = this.items || []; // Sort and filter the items into a mapping array from instance->item

      const isntIdxToItemsIdx = this.__sortAndFilterItems(items); // If we're chunking, increase the limit if there are new instances to
      // create and schedule the next chunk


      const limit = this.__calculateLimit(isntIdxToItemsIdx.length); // Create, update, and/or remove instances


      this.__updateInstances(items, limit, isntIdxToItemsIdx); // If we're chunking, schedule a rAF task to measure/continue chunking.     
      // Do this before any notifying events (renderedItemCount & dom-change)
      // since those could modify items and enqueue a new full render which will
      // pre-empt this measurement.


      if (this.initialCount && (this.__shouldMeasureChunk || this.__shouldContinueChunking)) {
        cancelAnimationFrame(this.__chunkingId);
        this.__chunkingId = requestAnimationFrame(() => this.__continueChunking());
      } // Set rendered item count


      this._setRenderedItemCount(this.__instances.length); // Notify users


      if (!suppressTemplateNotifications || this.notifyDomChange) {
        this.dispatchEvent(new CustomEvent('dom-change', {
          bubbles: true,
          composed: true
        }));
      }
    }

    __sortAndFilterItems(items) {
      // Generate array maping the instance index to the items array index
      let isntIdxToItemsIdx = new Array(items.length);

      for (let i = 0; i < items.length; i++) {
        isntIdxToItemsIdx[i] = i;
      } // Apply user filter


      if (this.__filterFn) {
        isntIdxToItemsIdx = isntIdxToItemsIdx.filter((i, idx, array) => this.__filterFn(items[i], idx, array));
      } // Apply user sort


      if (this.__sortFn) {
        isntIdxToItemsIdx.sort((a, b) => this.__sortFn(items[a], items[b]));
      }

      return isntIdxToItemsIdx;
    }

    __calculateLimit(filteredItemCount) {
      let limit = filteredItemCount;
      const currentCount = this.__instances.length; // When chunking, we increase the limit from the currently rendered count
      // by the chunk count that is re-calculated after each rAF (with special
      // cases for reseting the limit to initialCount after changing items)

      if (this.initialCount) {
        let newCount;

        if (!this.__chunkCount || this.__itemsArrayChanged && !this.reuseChunkedInstances) {
          // Limit next render to the initial count
          limit = Math.min(filteredItemCount, this.initialCount); // Subtract off any existing instances to determine the number of
          // instances that will be created

          newCount = Math.max(limit - currentCount, 0); // Initialize the chunk size with how many items we're creating

          this.__chunkCount = newCount || 1;
        } else {
          // The number of new instances that will be created is based on the
          // existing instances, the new list size, and the chunk size
          newCount = Math.min(Math.max(filteredItemCount - currentCount, 0), this.__chunkCount); // Update the limit based on how many new items we're making, limited
          // buy the total size of the list

          limit = Math.min(currentCount + newCount, filteredItemCount);
        } // Record some state about chunking for use in `__continueChunking`


        this.__shouldMeasureChunk = newCount === this.__chunkCount;
        this.__shouldContinueChunking = limit < filteredItemCount;
        this.__renderStartTime = performance.now();
      }

      this.__itemsArrayChanged = false;
      return limit;
    }

    __continueChunking() {
      // Simple auto chunkSize throttling algorithm based on feedback loop:
      // measure actual time between frames and scale chunk count by ratio of
      // target/actual frame time.  Only modify chunk size if our measurement
      // reflects the cost of a creating a full chunk's worth of instances; this
      // avoids scaling up the chunk size if we e.g. quickly re-rendered instances
      // in place
      if (this.__shouldMeasureChunk) {
        const renderTime = performance.now() - this.__renderStartTime;

        const ratio = this._targetFrameTime / renderTime;
        this.__chunkCount = Math.round(this.__chunkCount * ratio) || 1;
      } // Enqueue a new render if we haven't reached the full size of the list


      if (this.__shouldContinueChunking) {
        this.__debounceRender(this.__render);
      }
    }

    __updateInstances(items, limit, isntIdxToItemsIdx) {
      // items->inst map kept for item path forwarding
      const itemsIdxToInstIdx = this.__itemsIdxToInstIdx = {};
      let instIdx; // Generate instances and assign items

      for (instIdx = 0; instIdx < limit; instIdx++) {
        let inst = this.__instances[instIdx];
        let itemIdx = isntIdxToItemsIdx[instIdx];
        let item = items[itemIdx];
        itemsIdxToInstIdx[itemIdx] = instIdx;

        if (inst) {
          inst._setPendingProperty(this.as, item);

          inst._setPendingProperty(this.indexAs, instIdx);

          inst._setPendingProperty(this.itemsIndexAs, itemIdx);

          inst._flushProperties();
        } else {
          this.__insertInstance(item, instIdx, itemIdx);
        }
      } // Remove any extra instances from previous state


      for (let i = this.__instances.length - 1; i >= instIdx; i--) {
        this.__detachAndRemoveInstance(i);
      }
    }

    __detachInstance(idx) {
      let inst = this.__instances[idx];
      const wrappedRoot = wrap$1(inst.root);

      for (let i = 0; i < inst.children.length; i++) {
        let el = inst.children[i];
        wrappedRoot.appendChild(el);
      }

      return inst;
    }

    __attachInstance(idx, parent) {
      let inst = this.__instances[idx]; // Note, this is pre-wrapped as an optimization

      parent.insertBefore(inst.root, this);
    }

    __detachAndRemoveInstance(idx) {
      this.__detachInstance(idx);

      this.__instances.splice(idx, 1);
    }

    __stampInstance(item, instIdx, itemIdx) {
      let model = {};
      model[this.as] = item;
      model[this.indexAs] = instIdx;
      model[this.itemsIndexAs] = itemIdx;
      return new this.__ctor(model);
    }

    __insertInstance(item, instIdx, itemIdx) {
      const inst = this.__stampInstance(item, instIdx, itemIdx);

      let beforeRow = this.__instances[instIdx + 1];
      let beforeNode = beforeRow ? beforeRow.children[0] : this;
      wrap$1(wrap$1(this).parentNode).insertBefore(inst.root, beforeNode);
      this.__instances[instIdx] = inst;
      return inst;
    } // Implements extension point from Templatize mixin

    /**
     * Shows or hides the template instance top level child elements. For
     * text nodes, `textContent` is removed while "hidden" and replaced when
     * "shown."
     * @param {boolean} hidden Set to true to hide the children;
     * set to false to show them.
     * @return {void}
     * @protected
     */


    _showHideChildren(hidden) {
      for (let i = 0; i < this.__instances.length; i++) {
        this.__instances[i]._showHideChildren(hidden);
      }
    } // Called as a side effect of a host items.<key>.<path> path change,
    // responsible for notifying item.<path> changes to inst for key


    __handleItemPath(path, value) {
      let itemsPath = path.slice(6); // 'items.'.length == 6

      let dot = itemsPath.indexOf('.');
      let itemsIdx = dot < 0 ? itemsPath : itemsPath.substring(0, dot); // If path was index into array...

      if (itemsIdx == parseInt(itemsIdx, 10)) {
        let itemSubPath = dot < 0 ? '' : itemsPath.substring(dot + 1); // If the path is observed, it will trigger a full refresh

        this.__handleObservedPaths(itemSubPath); // Note, even if a rull refresh is triggered, always do the path
        // notification because unless mutableData is used for dom-repeat
        // and all elements in the instance subtree, a full refresh may
        // not trigger the proper update.


        let instIdx = this.__itemsIdxToInstIdx[itemsIdx];
        let inst = this.__instances[instIdx];

        if (inst) {
          let itemPath = this.as + (itemSubPath ? '.' + itemSubPath : ''); // This is effectively `notifyPath`, but avoids some of the overhead
          // of the public API

          inst._setPendingPropertyOrPath(itemPath, value, false, true);

          inst._flushProperties();
        }

        return true;
      }
    }
    /**
     * Returns the item associated with a given element stamped by
     * this `dom-repeat`.
     *
     * Note, to modify sub-properties of the item,
     * `modelForElement(el).set('item.<sub-prop>', value)`
     * should be used.
     *
     * @param {!HTMLElement} el Element for which to return the item.
     * @return {*} Item associated with the element.
     */


    itemForElement(el) {
      let instance = this.modelForElement(el);
      return instance && instance[this.as];
    }
    /**
     * Returns the inst index for a given element stamped by this `dom-repeat`.
     * If `sort` is provided, the index will reflect the sorted order (rather
     * than the original array order).
     *
     * @param {!HTMLElement} el Element for which to return the index.
     * @return {?number} Row index associated with the element (note this may
     *   not correspond to the array index if a user `sort` is applied).
     */


    indexForElement(el) {
      let instance = this.modelForElement(el);
      return instance && instance[this.indexAs];
    }
    /**
     * Returns the template "model" associated with a given element, which
     * serves as the binding scope for the template instance the element is
     * contained in. A template model
     * should be used to manipulate data associated with this template instance.
     *
     * Example:
     *
     *   let model = modelForElement(el);
     *   if (model.index < 10) {
     *     model.set('item.checked', true);
     *   }
     *
     * @param {!HTMLElement} el Element for which to return a template model.
     * @return {TemplateInstanceBase} Model representing the binding scope for
     *   the element.
     */


    modelForElement(el) {
      return modelForElement(this.template, el);
    }

  }
  customElements.define(DomRepeat.is, DomRepeat);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @customElement
   * @polymer
   * @extends PolymerElement
   * @summary Base class for dom-if element; subclassed into concrete
   *   implementation.
   */

  class DomIfBase extends PolymerElement {
    // Not needed to find template; can be removed once the analyzer
    // can find the tag name from customElements.define call
    static get is() {
      return 'dom-if';
    }

    static get template() {
      return null;
    }

    static get properties() {
      return {
        /**
         * Fired whenever DOM is added or removed/hidden by this template (by
         * default, rendering occurs lazily).  To force immediate rendering, call
         * `render`.
         *
         * @event dom-change
         */

        /**
         * A boolean indicating whether this template should stamp.
         */
        if: {
          type: Boolean,
          observer: '__debounceRender'
        },

        /**
         * When true, elements will be removed from DOM and discarded when `if`
         * becomes false and re-created and added back to the DOM when `if`
         * becomes true.  By default, stamped elements will be hidden but left
         * in the DOM when `if` becomes false, which is generally results
         * in better performance.
         */
        restamp: {
          type: Boolean,
          observer: '__debounceRender'
        },

        /**
         * When the global `suppressTemplateNotifications` setting is used, setting
         * `notifyDomChange: true` will enable firing `dom-change` events on this
         * element.
         */
        notifyDomChange: {
          type: Boolean
        }
      };
    }

    constructor() {
      super();
      this.__renderDebouncer = null;
      this._lastIf = false;
      this.__hideTemplateChildren__ = false;
      /** @type {!HTMLTemplateElement|undefined} */

      this.__template;
      /** @type {!TemplateInfo|undefined} */

      this._templateInfo;
    }

    __debounceRender() {
      // Render is async for 2 reasons:
      // 1. To eliminate dom creation trashing if user code thrashes `if` in the
      //    same turn. This was more common in 1.x where a compound computed
      //    property could result in the result changing multiple times, but is
      //    mitigated to a large extent by batched property processing in 2.x.
      // 2. To avoid double object propagation when a bag including values bound
      //    to the `if` property as well as one or more hostProps could enqueue
      //    the <dom-if> to flush before the <template>'s host property
      //    forwarding. In that scenario creating an instance would result in
      //    the host props being set once, and then the enqueued changes on the
      //    template would set properties a second time, potentially causing an
      //    object to be set to an instance more than once.  Creating the
      //    instance async from flushing data ensures this doesn't happen. If
      //    we wanted a sync option in the future, simply having <dom-if> flush
      //    (or clear) its template's pending host properties before creating
      //    the instance would also avoid the problem.
      this.__renderDebouncer = Debouncer.debounce(this.__renderDebouncer, microTask, () => this.__render());
      enqueueDebouncer(this.__renderDebouncer);
    }
    /**
     * @override
     * @return {void}
     */


    disconnectedCallback() {
      super.disconnectedCallback();
      const parent = wrap$1(this).parentNode;

      if (!parent || parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE && !wrap$1(parent).host) {
        this.__teardownInstance();
      }
    }
    /**
     * @override
     * @return {void}
     */


    connectedCallback() {
      super.connectedCallback();

      if (!hideElementsGlobally()) {
        this.style.display = 'none';
      }

      if (this.if) {
        this.__debounceRender();
      }
    }
    /**
     * Ensures a template has been assigned to `this.__template`.  If it has not
     * yet been, it querySelectors for it in its children and if it does not yet
     * exist (e.g. in parser-generated case), opens a mutation observer and
     * waits for it to appear (returns false if it has not yet been found,
     * otherwise true).  In the `removeNestedTemplates` case, the "template" will
     * be the `dom-if` element itself.
     *
     * @return {boolean} True when a template has been found, false otherwise
     */


    __ensureTemplate() {
      if (!this.__template) {
        // When `removeNestedTemplates` is true, the "template" is the element
        // itself, which has been given a `_templateInfo` property
        const thisAsTemplate =
        /** @type {!HTMLTemplateElement} */

        /** @type {!HTMLElement} */
        this;
        let template = thisAsTemplate._templateInfo ? thisAsTemplate :
        /** @type {!HTMLTemplateElement} */
        wrap$1(thisAsTemplate).querySelector('template');

        if (!template) {
          // Wait until childList changes and template should be there by then
          let observer = new MutationObserver(() => {
            if (wrap$1(this).querySelector('template')) {
              observer.disconnect();

              this.__render();
            } else {
              throw new Error('dom-if requires a <template> child');
            }
          });
          observer.observe(this, {
            childList: true
          });
          return false;
        }

        this.__template = template;
      }

      return true;
    }
    /**
     * Ensures a an instance of the template has been created and inserted. This
     * method may return false if the template has not yet been found or if
     * there is no `parentNode` to insert the template into (in either case,
     * connection or the template-finding mutation observer firing will queue
     * another render, causing this method to be called again at a more
     * appropriate time).
     *
     * Subclasses should implement the following methods called here:
     * - `__hasInstance`
     * - `__createAndInsertInstance`
     * - `__getInstanceNodes`
     *
     * @return {boolean} True if the instance was created, false otherwise.
     */


    __ensureInstance() {
      let parentNode = wrap$1(this).parentNode;

      if (!this.__hasInstance()) {
        // Guard against element being detached while render was queued
        if (!parentNode) {
          return false;
        } // Find the template (when false, there was no template yet)


        if (!this.__ensureTemplate()) {
          return false;
        }

        this.__createAndInsertInstance(parentNode);
      } else {
        // Move instance children if necessary
        let children = this.__getInstanceNodes();

        if (children && children.length) {
          // Detect case where dom-if was re-attached in new position
          let lastChild = wrap$1(this).previousSibling;

          if (lastChild !== children[children.length - 1]) {
            for (let i = 0, n; i < children.length && (n = children[i]); i++) {
              wrap$1(parentNode).insertBefore(n, this);
            }
          }
        }
      }

      return true;
    }
    /**
     * Forces the element to render its content. Normally rendering is
     * asynchronous to a provoking change. This is done for efficiency so
     * that multiple changes trigger only a single render. The render method
     * should be called if, for example, template rendering is required to
     * validate application state.
     *
     * @return {void}
     */


    render() {
      flush();
    }
    /**
     * Performs the key rendering steps:
     * 1. Ensure a template instance has been stamped (when true)
     * 2. Remove the template instance (when false and restamp:true)
     * 3. Sync the hidden state of the instance nodes with the if/restamp state
     * 4. Fires the `dom-change` event when necessary
     *
     * @return {void}
     */


    __render() {
      if (this.if) {
        if (!this.__ensureInstance()) {
          // No template found yet
          return;
        }
      } else if (this.restamp) {
        this.__teardownInstance();
      }

      this._showHideChildren();

      if ((!suppressTemplateNotifications || this.notifyDomChange) && this.if != this._lastIf) {
        this.dispatchEvent(new CustomEvent('dom-change', {
          bubbles: true,
          composed: true
        }));
        this._lastIf = this.if;
      }
    } // Ideally these would be annotated as abstract methods in an abstract class,
    // but closure compiler is finnicky

    /* eslint-disable valid-jsdoc */

    /**
     * Abstract API to be implemented by subclass: Returns true if a template
     * instance has been created and inserted.
     *
     * @protected
     * @return {boolean} True when an instance has been created.
     */


    __hasInstance() {}
    /**
     * Abstract API to be implemented by subclass: Returns the child nodes stamped
     * from a template instance.
     *
     * @protected
     * @return {Array<Node>} Array of child nodes stamped from the template
     * instance.
     */


    __getInstanceNodes() {}
    /**
     * Abstract API to be implemented by subclass: Creates an instance of the
     * template and inserts it into the given parent node.
     *
     * @protected
     * @param {Node} parentNode The parent node to insert the instance into
     * @return {void}
     */


    __createAndInsertInstance(parentNode) {} // eslint-disable-line no-unused-vars

    /**
     * Abstract API to be implemented by subclass: Removes nodes created by an
     * instance of a template and any associated cleanup.
     *
     * @protected
     * @return {void}
     */


    __teardownInstance() {}
    /**
     * Abstract API to be implemented by subclass: Shows or hides any template
     * instance childNodes based on the `if` state of the element and its
     * `__hideTemplateChildren__` property.
     *
     * @protected
     * @return {void}
     */


    _showHideChildren() {}
    /* eslint-enable valid-jsdoc */


  }
  /**
   * The version of DomIf used when `fastDomIf` setting is in use, which is
   * optimized for first-render (but adds a tax to all subsequent property updates
   * on the host, whether they were used in a given `dom-if` or not).
   *
   * This implementation avoids use of `Templatizer`, which introduces a new scope
   * (a non-element PropertyEffects instance), which is not strictly necessary
   * since `dom-if` never introduces new properties to its scope (unlike
   * `dom-repeat`). Taking advantage of this fact, the `dom-if` reaches up to its
   * `__dataHost` and stamps the template directly from the host using the host's
   * runtime `_stampTemplate` API, which binds the property effects of the
   * template directly to the host. This both avoids the intermediary
   * `Templatizer` instance, but also avoids the need to bind host properties to
   * the `<template>` element and forward those into the template instance.
   *
   * In this version of `dom-if`, the `this.__instance` method is the
   * `DocumentFragment` returned from `_stampTemplate`, which also serves as the
   * handle for later removing it using the `_removeBoundDom` method.
   */


  class DomIfFast extends DomIfBase {
    constructor() {
      super();
      this.__instance = null;
      this.__syncInfo = null;
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * @override
     * @return {boolean} True when an instance has been created.
     */


    __hasInstance() {
      return Boolean(this.__instance);
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * @override
     * @return {Array<Node>} Array of child nodes stamped from the template
     * instance.
     */


    __getInstanceNodes() {
      return this.__instance.templateInfo.childNodes;
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Stamps the template by calling `_stampTemplate` on the `__dataHost` of this
     * element and then inserts the resulting nodes into the given `parentNode`.
     *
     * @override
     * @param {Node} parentNode The parent node to insert the instance into
     * @return {void}
     */


    __createAndInsertInstance(parentNode) {
      const host = this.__dataHost || this;

      if (strictTemplatePolicy) {
        if (!this.__dataHost) {
          throw new Error('strictTemplatePolicy: template owner not trusted');
        }
      } // Pre-bind and link the template into the effects system


      const templateInfo = host._bindTemplate(
      /** @type {!HTMLTemplateElement} */
      this.__template, true); // Install runEffects hook that prevents running property effects
      // (and any nested template effects) when the `if` is false


      templateInfo.runEffects = (runEffects, changedProps, hasPaths) => {
        let syncInfo = this.__syncInfo;

        if (this.if) {
          // Mix any props that changed while the `if` was false into `changedProps`
          if (syncInfo) {
            // If there were properties received while the `if` was false, it is
            // important to sync the hidden state with the element _first_, so that
            // new bindings to e.g. `textContent` do not get stomped on by
            // pre-hidden values if `_showHideChildren` were to be called later at
            // the next render. Clearing `__invalidProps` here ensures
            // `_showHideChildren`'s call to `__syncHostProperties` no-ops, so
            // that we don't call `runEffects` more often than necessary.
            this.__syncInfo = null;

            this._showHideChildren();

            changedProps = Object.assign(syncInfo.changedProps, changedProps);
          }

          runEffects(changedProps, hasPaths);
        } else {
          // Accumulate any values changed while `if` was false, along with the
          // runEffects method to sync them, so that we can replay them once `if`
          // becomes true
          if (this.__instance) {
            if (!syncInfo) {
              syncInfo = this.__syncInfo = {
                runEffects,
                changedProps: {}
              };
            }

            if (hasPaths) {
              // Store root object of any paths; this will ensure direct bindings
              // like [[obj.foo]] bindings run after a `set('obj.foo', v)`, but
              // note that path notifications like `set('obj.foo.bar', v)` will
              // not propagate. Since batched path notifications are not
              // supported, we cannot simply accumulate path notifications. This
              // is equivalent to the non-fastDomIf case, which stores root(p) in
              // __invalidProps.
              for (const p in changedProps) {
                const rootProp = root(p);
                syncInfo.changedProps[rootProp] = this.__dataHost[rootProp];
              }
            } else {
              Object.assign(syncInfo.changedProps, changedProps);
            }
          }
        }
      }; // Stamp the template, and set its DocumentFragment to the "instance"


      this.__instance = host._stampTemplate(
      /** @type {!HTMLTemplateElement} */
      this.__template, templateInfo);
      wrap$1(parentNode).insertBefore(this.__instance, this);
    }
    /**
     * Run effects for any properties that changed while the `if` was false.
     *
     * @return {void}
     */


    __syncHostProperties() {
      const syncInfo = this.__syncInfo;

      if (syncInfo) {
        this.__syncInfo = null;
        syncInfo.runEffects(syncInfo.changedProps, false);
      }
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Remove the instance and any nodes it created.  Uses the `__dataHost`'s
     * runtime `_removeBoundDom` method.
     *
     * @override
     * @return {void}
     */


    __teardownInstance() {
      const host = this.__dataHost || this;

      if (this.__instance) {
        host._removeBoundDom(this.__instance);

        this.__instance = null;
        this.__syncInfo = null;
      }
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Shows or hides the template instance top level child nodes. For
     * text nodes, `textContent` is removed while "hidden" and replaced when
     * "shown."
     *
     * @override
     * @return {void}
     * @protected
     * @suppress {visibility}
     */


    _showHideChildren() {
      const hidden = this.__hideTemplateChildren__ || !this.if;

      if (this.__instance && Boolean(this.__instance.__hidden) !== hidden) {
        this.__instance.__hidden = hidden;
        showHideChildren(hidden, this.__instance.templateInfo.childNodes);
      }

      if (!hidden) {
        this.__syncHostProperties();
      }
    }

  }
  /**
   * The "legacy" implementation of `dom-if`, implemented using `Templatizer`.
   *
   * In this version, `this.__instance` is the `TemplateInstance` returned
   * from the templatized constructor.
   */


  class DomIfLegacy extends DomIfBase {
    constructor() {
      super();
      this.__ctor = null;
      this.__instance = null;
      this.__invalidProps = null;
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * @override
     * @return {boolean} True when an instance has been created.
     */


    __hasInstance() {
      return Boolean(this.__instance);
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * @override
     * @return {Array<Node>} Array of child nodes stamped from the template
     * instance.
     */


    __getInstanceNodes() {
      return this.__instance.children;
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Stamps the template by creating a new instance of the templatized
     * constructor (which is created lazily if it does not yet exist), and then
     * inserts its resulting `root` doc fragment into the given `parentNode`.
     *
     * @override
     * @param {Node} parentNode The parent node to insert the instance into
     * @return {void}
     */


    __createAndInsertInstance(parentNode) {
      // Ensure we have an instance constructor
      if (!this.__ctor) {
        this.__ctor = templatize(
        /** @type {!HTMLTemplateElement} */
        this.__template, this, {
          // dom-if templatizer instances require `mutable: true`, as
          // `__syncHostProperties` relies on that behavior to sync objects
          mutableData: true,

          /**
           * @param {string} prop Property to forward
           * @param {*} value Value of property
           * @this {DomIfLegacy}
           */
          forwardHostProp: function (prop, value) {
            if (this.__instance) {
              if (this.if) {
                this.__instance.forwardHostProp(prop, value);
              } else {
                // If we have an instance but are squelching host property
                // forwarding due to if being false, note the invalidated
                // properties so `__syncHostProperties` can sync them the next
                // time `if` becomes true
                this.__invalidProps = this.__invalidProps || Object.create(null);
                this.__invalidProps[root(prop)] = true;
              }
            }
          }
        });
      } // Create and insert the instance


      this.__instance = new this.__ctor();
      wrap$1(parentNode).insertBefore(this.__instance.root, this);
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Removes the instance and any nodes it created.
     *
     * @override
     * @return {void}
     */


    __teardownInstance() {
      if (this.__instance) {
        let c$ = this.__instance.children;

        if (c$ && c$.length) {
          // use first child parent, for case when dom-if may have been detached
          let parent = wrap$1(c$[0]).parentNode; // Instance children may be disconnected from parents when dom-if
          // detaches if a tree was innerHTML'ed

          if (parent) {
            parent = wrap$1(parent);

            for (let i = 0, n; i < c$.length && (n = c$[i]); i++) {
              parent.removeChild(n);
            }
          }
        }

        this.__invalidProps = null;
        this.__instance = null;
      }
    }
    /**
     * Forwards any properties that changed while the `if` was false into the
     * template instance and flushes it.
     *
     * @return {void}
     */


    __syncHostProperties() {
      let props = this.__invalidProps;

      if (props) {
        this.__invalidProps = null;

        for (let prop in props) {
          this.__instance._setPendingProperty(prop, this.__dataHost[prop]);
        }

        this.__instance._flushProperties();
      }
    }
    /**
     * Implementation of abstract API needed by DomIfBase.
     *
     * Shows or hides the template instance top level child elements. For
     * text nodes, `textContent` is removed while "hidden" and replaced when
     * "shown."
     *
     * @override
     * @protected
     * @return {void}
     * @suppress {visibility}
     */


    _showHideChildren() {
      const hidden = this.__hideTemplateChildren__ || !this.if;

      if (this.__instance && Boolean(this.__instance.__hidden) !== hidden) {
        this.__instance.__hidden = hidden;

        this.__instance._showHideChildren(hidden);
      }

      if (!hidden) {
        this.__syncHostProperties();
      }
    }

  }
  /**
   * The `<dom-if>` element will stamp a light-dom `<template>` child when
   * the `if` property becomes truthy, and the template can use Polymer
   * data-binding and declarative event features when used in the context of
   * a Polymer element's template.
   *
   * When `if` becomes falsy, the stamped content is hidden but not
   * removed from dom. When `if` subsequently becomes truthy again, the content
   * is simply re-shown. This approach is used due to its favorable performance
   * characteristics: the expense of creating template content is paid only
   * once and lazily.
   *
   * Set the `restamp` property to true to force the stamped content to be
   * created / destroyed when the `if` condition changes.
   *
   * @customElement
   * @polymer
   * @extends DomIfBase
   * @constructor
   * @summary Custom element that conditionally stamps and hides or removes
   *   template content based on a boolean flag.
   */


  const DomIf = fastDomIf ? DomIfFast : DomIfLegacy;
  customElements.define(DomIf.is, DomIf);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Element mixin for recording dynamic associations between item paths in a
   * master `items` array and a `selected` array such that path changes to the
   * master array (at the host) element or elsewhere via data-binding) are
   * correctly propagated to items in the selected array and vice-versa.
   *
   * The `items` property accepts an array of user data, and via the
   * `select(item)` and `deselect(item)` API, updates the `selected` property
   * which may be bound to other parts of the application, and any changes to
   * sub-fields of `selected` item(s) will be kept in sync with items in the
   * `items` array.  When `multi` is false, `selected` is a property
   * representing the last selected item.  When `multi` is true, `selected`
   * is an array of multiply selected items.
   *
   * @polymer
   * @mixinFunction
   * @appliesMixin ElementMixin
   * @summary Element mixin for recording dynamic associations between item paths in a
   * master `items` array and a `selected` array
   */

  let ArraySelectorMixin = dedupingMixin(superClass => {
    /**
     * @constructor
     * @implements {Polymer_ElementMixin}
     * @private
     */
    let elementBase = ElementMixin(superClass);
    /**
     * @polymer
     * @mixinClass
     * @implements {Polymer_ArraySelectorMixin}
     * @unrestricted
     */

    class ArraySelectorMixin extends elementBase {
      static get properties() {
        return {
          /**
           * An array containing items from which selection will be made.
           */
          items: {
            type: Array
          },

          /**
           * When `true`, multiple items may be selected at once (in this case,
           * `selected` is an array of currently selected items).  When `false`,
           * only one item may be selected at a time.
           */
          multi: {
            type: Boolean,
            value: false
          },

          /**
           * When `multi` is true, this is an array that contains any selected.
           * When `multi` is false, this is the currently selected item, or `null`
           * if no item is selected.
           * @type {?Object|?Array<!Object>}
           */
          selected: {
            type: Object,
            notify: true
          },

          /**
           * When `multi` is false, this is the currently selected item, or `null`
           * if no item is selected.
           * @type {?Object}
           */
          selectedItem: {
            type: Object,
            notify: true
          },

          /**
           * When `true`, calling `select` on an item that is already selected
           * will deselect the item.
           */
          toggle: {
            type: Boolean,
            value: false
          }
        };
      }

      static get observers() {
        return ['__updateSelection(multi, items.*)'];
      }

      constructor() {
        super();
        this.__lastItems = null;
        this.__lastMulti = null;
        this.__selectedMap = null;
      }

      __updateSelection(multi, itemsInfo) {
        let path = itemsInfo.path;

        if (path == JSCompiler_renameProperty('items', this)) {
          // Case 1 - items array changed, so diff against previous array and
          // deselect any removed items and adjust selected indices
          let newItems = itemsInfo.base || [];
          let lastItems = this.__lastItems;
          let lastMulti = this.__lastMulti;

          if (multi !== lastMulti) {
            this.clearSelection();
          }

          if (lastItems) {
            let splices = calculateSplices(newItems, lastItems);

            this.__applySplices(splices);
          }

          this.__lastItems = newItems;
          this.__lastMulti = multi;
        } else if (itemsInfo.path == `${JSCompiler_renameProperty('items', this)}.splices`) {
          // Case 2 - got specific splice information describing the array mutation:
          // deselect any removed items and adjust selected indices
          this.__applySplices(itemsInfo.value.indexSplices);
        } else {
          // Case 3 - an array element was changed, so deselect the previous
          // item for that index if it was previously selected
          let part = path.slice(`${JSCompiler_renameProperty('items', this)}.`.length);
          let idx = parseInt(part, 10);

          if (part.indexOf('.') < 0 && part == idx) {
            this.__deselectChangedIdx(idx);
          }
        }
      }

      __applySplices(splices) {
        let selected = this.__selectedMap; // Adjust selected indices and mark removals

        for (let i = 0; i < splices.length; i++) {
          let s = splices[i];
          selected.forEach((idx, item) => {
            if (idx < s.index) ; else if (idx >= s.index + s.removed.length) {
              // adjust index
              selected.set(item, idx + s.addedCount - s.removed.length);
            } else {
              // remove index
              selected.set(item, -1);
            }
          });

          for (let j = 0; j < s.addedCount; j++) {
            let idx = s.index + j;

            if (selected.has(this.items[idx])) {
              selected.set(this.items[idx], idx);
            }
          }
        } // Update linked paths


        this.__updateLinks(); // Remove selected items that were removed from the items array


        let sidx = 0;
        selected.forEach((idx, item) => {
          if (idx < 0) {
            if (this.multi) {
              this.splice(JSCompiler_renameProperty('selected', this), sidx, 1);
            } else {
              this.selected = this.selectedItem = null;
            }

            selected.delete(item);
          } else {
            sidx++;
          }
        });
      }

      __updateLinks() {
        this.__dataLinkedPaths = {};

        if (this.multi) {
          let sidx = 0;

          this.__selectedMap.forEach(idx => {
            if (idx >= 0) {
              this.linkPaths(`${JSCompiler_renameProperty('items', this)}.${idx}`, `${JSCompiler_renameProperty('selected', this)}.${sidx++}`);
            }
          });
        } else {
          this.__selectedMap.forEach(idx => {
            this.linkPaths(JSCompiler_renameProperty('selected', this), `${JSCompiler_renameProperty('items', this)}.${idx}`);
            this.linkPaths(JSCompiler_renameProperty('selectedItem', this), `${JSCompiler_renameProperty('items', this)}.${idx}`);
          });
        }
      }
      /**
       * Clears the selection state.
       * @override
       * @return {void}
       */


      clearSelection() {
        // Unbind previous selection
        this.__dataLinkedPaths = {}; // The selected map stores 3 pieces of information:
        // key: items array object
        // value: items array index
        // order: selected array index

        this.__selectedMap = new Map(); // Initialize selection

        this.selected = this.multi ? [] : null;
        this.selectedItem = null;
      }
      /**
       * Returns whether the item is currently selected.
       *
       * @override
       * @param {*} item Item from `items` array to test
       * @return {boolean} Whether the item is selected
       */


      isSelected(item) {
        return this.__selectedMap.has(item);
      }
      /**
       * Returns whether the item is currently selected.
       *
       * @override
       * @param {number} idx Index from `items` array to test
       * @return {boolean} Whether the item is selected
       */


      isIndexSelected(idx) {
        return this.isSelected(this.items[idx]);
      }

      __deselectChangedIdx(idx) {
        let sidx = this.__selectedIndexForItemIndex(idx);

        if (sidx >= 0) {
          let i = 0;

          this.__selectedMap.forEach((idx, item) => {
            if (sidx == i++) {
              this.deselect(item);
            }
          });
        }
      }

      __selectedIndexForItemIndex(idx) {
        let selected = this.__dataLinkedPaths[`${JSCompiler_renameProperty('items', this)}.${idx}`];

        if (selected) {
          return parseInt(selected.slice(`${JSCompiler_renameProperty('selected', this)}.`.length), 10);
        }
      }
      /**
       * Deselects the given item if it is already selected.
       *
       * @override
       * @param {*} item Item from `items` array to deselect
       * @return {void}
       */


      deselect(item) {
        let idx = this.__selectedMap.get(item);

        if (idx >= 0) {
          this.__selectedMap.delete(item);

          let sidx;

          if (this.multi) {
            sidx = this.__selectedIndexForItemIndex(idx);
          }

          this.__updateLinks();

          if (this.multi) {
            this.splice(JSCompiler_renameProperty('selected', this), sidx, 1);
          } else {
            this.selected = this.selectedItem = null;
          }
        }
      }
      /**
       * Deselects the given index if it is already selected.
       *
       * @override
       * @param {number} idx Index from `items` array to deselect
       * @return {void}
       */


      deselectIndex(idx) {
        this.deselect(this.items[idx]);
      }
      /**
       * Selects the given item.  When `toggle` is true, this will automatically
       * deselect the item if already selected.
       *
       * @override
       * @param {*} item Item from `items` array to select
       * @return {void}
       */


      select(item) {
        this.selectIndex(this.items.indexOf(item));
      }
      /**
       * Selects the given index.  When `toggle` is true, this will automatically
       * deselect the item if already selected.
       *
       * @override
       * @param {number} idx Index from `items` array to select
       * @return {void}
       */


      selectIndex(idx) {
        let item = this.items[idx];

        if (!this.isSelected(item)) {
          if (!this.multi) {
            this.__selectedMap.clear();
          }

          this.__selectedMap.set(item, idx);

          this.__updateLinks();

          if (this.multi) {
            this.push(JSCompiler_renameProperty('selected', this), item);
          } else {
            this.selected = this.selectedItem = item;
          }
        } else if (this.toggle) {
          this.deselectIndex(idx);
        }
      }

    }

    return ArraySelectorMixin;
  }); // export mixin
  /**
   * @constructor
   * @extends {PolymerElement}
   * @implements {Polymer_ArraySelectorMixin}
   * @private
   */

  let baseArraySelector = ArraySelectorMixin(PolymerElement);
  /**
   * Element implementing the `ArraySelector` mixin, which records
   * dynamic associations between item paths in a master `items` array and a
   * `selected` array such that path changes to the master array (at the host)
   * element or elsewhere via data-binding) are correctly propagated to items
   * in the selected array and vice-versa.
   *
   * The `items` property accepts an array of user data, and via the
   * `select(item)` and `deselect(item)` API, updates the `selected` property
   * which may be bound to other parts of the application, and any changes to
   * sub-fields of `selected` item(s) will be kept in sync with items in the
   * `items` array.  When `multi` is false, `selected` is a property
   * representing the last selected item.  When `multi` is true, `selected`
   * is an array of multiply selected items.
   *
   * Example:
   *
   * ```js
   * import {PolymerElement} from '@polymer/polymer';
   * import '@polymer/polymer/lib/elements/array-selector.js';
   *
   * class EmployeeList extends PolymerElement {
   *   static get _template() {
   *     return html`
   *         <div> Employee list: </div>
   *         <dom-repeat id="employeeList" items="{{employees}}">
   *           <template>
   *             <div>First name: <span>{{item.first}}</span></div>
   *               <div>Last name: <span>{{item.last}}</span></div>
   *               <button on-click="toggleSelection">Select</button>
   *           </template>
   *         </dom-repeat>
   *
   *         <array-selector id="selector"
   *                         items="{{employees}}"
   *                         selected="{{selected}}"
   *                         multi toggle></array-selector>
   *
   *         <div> Selected employees: </div>
   *         <dom-repeat items="{{selected}}">
   *           <template>
   *             <div>First name: <span>{{item.first}}</span></div>
   *             <div>Last name: <span>{{item.last}}</span></div>
   *           </template>
   *         </dom-repeat>`;
   *   }
   *   static get is() { return 'employee-list'; }
   *   static get properties() {
   *     return {
   *       employees: {
   *         value() {
   *           return [
   *             {first: 'Bob', last: 'Smith'},
   *             {first: 'Sally', last: 'Johnson'},
   *             ...
   *           ];
   *         }
   *       }
   *     };
   *   }
   *   toggleSelection(e) {
   *     const item = this.$.employeeList.itemForElement(e.target);
   *     this.$.selector.select(item);
   *   }
   * }
   * ```
   *
   * @polymer
   * @customElement
   * @extends {baseArraySelector}
   * @appliesMixin ArraySelectorMixin
   * @summary Custom element that links paths between an input `items` array and
   *   an output `selected` item or array based on calls to its selection API.
   */

  class ArraySelector extends baseArraySelector {
    // Not needed to find template; can be removed once the analyzer
    // can find the tag name from customElements.define call
    static get is() {
      return 'array-selector';
    }

    static get template() {
      return null;
    }

  }

  customElements.define(ArraySelector.is, ArraySelector);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const customStyleInterface = new CustomStyleInterface();

  if (!window.ShadyCSS) {
    window.ShadyCSS = {
      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       * @param {string=} elementExtends
       */
      prepareTemplate(template, elementName, elementExtends) {},

      // eslint-disable-line no-unused-vars

      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       */
      prepareTemplateDom(template, elementName) {},

      // eslint-disable-line no-unused-vars

      /**
       * @param {!HTMLTemplateElement} template
       * @param {string} elementName
       * @param {string=} elementExtends
       */
      prepareTemplateStyles(template, elementName, elementExtends) {},

      // eslint-disable-line no-unused-vars

      /**
       * @param {Element} element
       * @param {Object=} properties
       */
      styleSubtree(element, properties) {
        customStyleInterface.processStyles();
        updateNativeProperties(element, properties);
      },

      /**
       * @param {Element} element
       */
      styleElement(element) {
        // eslint-disable-line no-unused-vars
        customStyleInterface.processStyles();
      },

      /**
       * @param {Object=} properties
       */
      styleDocument(properties) {
        customStyleInterface.processStyles();
        updateNativeProperties(document.body, properties);
      },

      /**
       * @param {Element} element
       * @param {string} property
       * @return {string}
       */
      getComputedStyleValue(element, property) {
        return getComputedStyleValue(element, property);
      },

      flushCustomStyles() {},

      nativeCss: nativeCssVariables,
      nativeShadow: nativeShadow,
      cssBuild: cssBuild,
      disableRuntime: disableRuntime
    };
  }

  window.ShadyCSS.CustomStyleInterface = customStyleInterface;

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */
  const attr = 'include';
  const CustomStyleInterface$1 = window.ShadyCSS.CustomStyleInterface;
  /**
   * Custom element for defining styles in the main document that can take
   * advantage of [shady DOM](https://github.com/webcomponents/shadycss) shims
   * for style encapsulation, custom properties, and custom mixins.
   *
   * - Document styles defined in a `<custom-style>` are shimmed to ensure they
   *   do not leak into local DOM when running on browsers without native
   *   Shadow DOM.
   * - Custom properties can be defined in a `<custom-style>`. Use the `html` selector
   *   to define custom properties that apply to all custom elements.
   * - Custom mixins can be defined in a `<custom-style>`, if you import the optional
   *   [apply shim](https://github.com/webcomponents/shadycss#about-applyshim)
   *   (`shadycss/apply-shim.html`).
   *
   * To use:
   *
   * - Import `custom-style.html`.
   * - Place a `<custom-style>` element in the main document, wrapping an inline `<style>` tag that
   *   contains the CSS rules you want to shim.
   *
   * For example:
   *
   * ```html
   * <!-- import apply shim--only required if using mixins -->
   * <link rel="import" href="bower_components/shadycss/apply-shim.html">
   * <!-- import custom-style element -->
   * <link rel="import" href="bower_components/polymer/lib/elements/custom-style.html">
   *
   * <custom-style>
   *   <style>
   *     html {
   *       --custom-color: blue;
   *       --custom-mixin: {
   *         font-weight: bold;
   *         color: red;
   *       };
   *     }
   *   </style>
   * </custom-style>
   * ```
   *
   * @customElement
   * @extends HTMLElement
   * @summary Custom element for defining styles in the main document that can
   *   take advantage of Polymer's style scoping and custom properties shims.
   */

  class CustomStyle extends HTMLElement {
    constructor() {
      super();
      this._style = null;
      CustomStyleInterface$1.addCustomStyle(this);
    }
    /**
     * Returns the light-DOM `<style>` child this element wraps.  Upon first
     * call any style modules referenced via the `include` attribute will be
     * concatenated to this element's `<style>`.
     *
     * @export
     * @return {HTMLStyleElement} This element's light-DOM `<style>`
     */


    getStyle() {
      if (this._style) {
        return this._style;
      }

      const style =
      /** @type {HTMLStyleElement} */
      this.querySelector('style');

      if (!style) {
        return null;
      }

      this._style = style;
      const include = style.getAttribute(attr);

      if (include) {
        style.removeAttribute(attr);
        /** @suppress {deprecated} */

        style.textContent = cssFromModules(include) + style.textContent;
      }
      /*
      HTML Imports styling the main document are deprecated in Chrome
      https://crbug.com/523952
       If this element is not in the main document, then it must be in an HTML Import document.
      In that case, move the custom style to the main document.
       The ordering of `<custom-style>` should stay the same as when loaded by HTML Imports, but there may be odd
      cases of ordering w.r.t the main document styles.
      */


      if (this.ownerDocument !== window.document) {
        window.document.head.appendChild(this);
      }

      return this._style;
    }

  }
  window.customElements.define('custom-style', CustomStyle);

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
  The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
  The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
  Code distributed by Google as part of the polymer project is also
  subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
  */

  const Base = LegacyElementMixin(HTMLElement).prototype;

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  The `<iron-flex-layout>` component provides simple ways to use
  [CSS flexible box
  layout](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Flexible_boxes),
  also known as flexbox. Note that this is an old element, that was written
  before all modern browsers had non-prefixed flex styles. As such, nowadays you
  don't really need to use this element anymore, and can use CSS flex styles
  directly in your code.

  This component provides two different ways to use flexbox:

  1. [Layout
  classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
  The layout class stylesheet provides a simple set of class-based flexbox rules,
  that let you specify layout properties directly in markup. You must include this
  file in every element that needs to use them.

      Sample use:

      ```
      <custom-element-demo>
        <template>
          <script src="../webcomponentsjs/webcomponents-lite.js"></script>
          <next-code-block></next-code-block>
        </template>
      </custom-element-demo>
      ```

      ```js
      import {html} from '@polymer/polymer/lib/utils/html-tag.js';
      import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';

      const template = html`
        <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
        <style>
          .test { width: 100px; }
        </style>
        <div class="layout horizontal center-center">
          <div class="test">horizontal layout center alignment</div>
        </div>
      `;
      document.body.appendChild(template.content);
      ```

  2. [Custom CSS
  mixins](https://github.com/PolymerElements/iron-flex-layout/blob/master/iron-flex-layout.html).
  The mixin stylesheet includes custom CSS mixins that can be applied inside a CSS
  rule using the `@apply` function.

  Please note that the old [/deep/ layout
  classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/classes)
  are deprecated, and should not be used. To continue using layout properties
  directly in markup, please switch to using the new `dom-module`-based
  [layout
  classes](https://github.com/PolymerElements/iron-flex-layout/tree/master/iron-flex-layout-classes.html).
  Please note that the new version does not use `/deep/`, and therefore requires
  you to import the `dom-modules` in every element that needs to use them.

  @group Iron Elements
  @pseudoElement iron-flex-layout
  @demo demo/index.html
  */

  const template = html$1`
<custom-style>
  <style is="custom-style">
    [hidden] {
      display: none !important;
    }
  </style>
</custom-style>
<custom-style>
  <style is="custom-style">
    html {

      --layout: {
        display: -ms-flexbox;
        display: -webkit-flex;
        display: flex;
      };

      --layout-inline: {
        display: -ms-inline-flexbox;
        display: -webkit-inline-flex;
        display: inline-flex;
      };

      --layout-horizontal: {
        @apply --layout;

        -ms-flex-direction: row;
        -webkit-flex-direction: row;
        flex-direction: row;
      };

      --layout-horizontal-reverse: {
        @apply --layout;

        -ms-flex-direction: row-reverse;
        -webkit-flex-direction: row-reverse;
        flex-direction: row-reverse;
      };

      --layout-vertical: {
        @apply --layout;

        -ms-flex-direction: column;
        -webkit-flex-direction: column;
        flex-direction: column;
      };

      --layout-vertical-reverse: {
        @apply --layout;

        -ms-flex-direction: column-reverse;
        -webkit-flex-direction: column-reverse;
        flex-direction: column-reverse;
      };

      --layout-wrap: {
        -ms-flex-wrap: wrap;
        -webkit-flex-wrap: wrap;
        flex-wrap: wrap;
      };

      --layout-wrap-reverse: {
        -ms-flex-wrap: wrap-reverse;
        -webkit-flex-wrap: wrap-reverse;
        flex-wrap: wrap-reverse;
      };

      --layout-flex-auto: {
        -ms-flex: 1 1 auto;
        -webkit-flex: 1 1 auto;
        flex: 1 1 auto;
      };

      --layout-flex-none: {
        -ms-flex: none;
        -webkit-flex: none;
        flex: none;
      };

      --layout-flex: {
        -ms-flex: 1 1 0.000000001px;
        -webkit-flex: 1;
        flex: 1;
        -webkit-flex-basis: 0.000000001px;
        flex-basis: 0.000000001px;
      };

      --layout-flex-2: {
        -ms-flex: 2;
        -webkit-flex: 2;
        flex: 2;
      };

      --layout-flex-3: {
        -ms-flex: 3;
        -webkit-flex: 3;
        flex: 3;
      };

      --layout-flex-4: {
        -ms-flex: 4;
        -webkit-flex: 4;
        flex: 4;
      };

      --layout-flex-5: {
        -ms-flex: 5;
        -webkit-flex: 5;
        flex: 5;
      };

      --layout-flex-6: {
        -ms-flex: 6;
        -webkit-flex: 6;
        flex: 6;
      };

      --layout-flex-7: {
        -ms-flex: 7;
        -webkit-flex: 7;
        flex: 7;
      };

      --layout-flex-8: {
        -ms-flex: 8;
        -webkit-flex: 8;
        flex: 8;
      };

      --layout-flex-9: {
        -ms-flex: 9;
        -webkit-flex: 9;
        flex: 9;
      };

      --layout-flex-10: {
        -ms-flex: 10;
        -webkit-flex: 10;
        flex: 10;
      };

      --layout-flex-11: {
        -ms-flex: 11;
        -webkit-flex: 11;
        flex: 11;
      };

      --layout-flex-12: {
        -ms-flex: 12;
        -webkit-flex: 12;
        flex: 12;
      };

      /* alignment in cross axis */

      --layout-start: {
        -ms-flex-align: start;
        -webkit-align-items: flex-start;
        align-items: flex-start;
      };

      --layout-center: {
        -ms-flex-align: center;
        -webkit-align-items: center;
        align-items: center;
      };

      --layout-end: {
        -ms-flex-align: end;
        -webkit-align-items: flex-end;
        align-items: flex-end;
      };

      --layout-baseline: {
        -ms-flex-align: baseline;
        -webkit-align-items: baseline;
        align-items: baseline;
      };

      /* alignment in main axis */

      --layout-start-justified: {
        -ms-flex-pack: start;
        -webkit-justify-content: flex-start;
        justify-content: flex-start;
      };

      --layout-center-justified: {
        -ms-flex-pack: center;
        -webkit-justify-content: center;
        justify-content: center;
      };

      --layout-end-justified: {
        -ms-flex-pack: end;
        -webkit-justify-content: flex-end;
        justify-content: flex-end;
      };

      --layout-around-justified: {
        -ms-flex-pack: distribute;
        -webkit-justify-content: space-around;
        justify-content: space-around;
      };

      --layout-justified: {
        -ms-flex-pack: justify;
        -webkit-justify-content: space-between;
        justify-content: space-between;
      };

      --layout-center-center: {
        @apply --layout-center;
        @apply --layout-center-justified;
      };

      /* self alignment */

      --layout-self-start: {
        -ms-align-self: flex-start;
        -webkit-align-self: flex-start;
        align-self: flex-start;
      };

      --layout-self-center: {
        -ms-align-self: center;
        -webkit-align-self: center;
        align-self: center;
      };

      --layout-self-end: {
        -ms-align-self: flex-end;
        -webkit-align-self: flex-end;
        align-self: flex-end;
      };

      --layout-self-stretch: {
        -ms-align-self: stretch;
        -webkit-align-self: stretch;
        align-self: stretch;
      };

      --layout-self-baseline: {
        -ms-align-self: baseline;
        -webkit-align-self: baseline;
        align-self: baseline;
      };

      /* multi-line alignment in main axis */

      --layout-start-aligned: {
        -ms-flex-line-pack: start;  /* IE10 */
        -ms-align-content: flex-start;
        -webkit-align-content: flex-start;
        align-content: flex-start;
      };

      --layout-end-aligned: {
        -ms-flex-line-pack: end;  /* IE10 */
        -ms-align-content: flex-end;
        -webkit-align-content: flex-end;
        align-content: flex-end;
      };

      --layout-center-aligned: {
        -ms-flex-line-pack: center;  /* IE10 */
        -ms-align-content: center;
        -webkit-align-content: center;
        align-content: center;
      };

      --layout-between-aligned: {
        -ms-flex-line-pack: justify;  /* IE10 */
        -ms-align-content: space-between;
        -webkit-align-content: space-between;
        align-content: space-between;
      };

      --layout-around-aligned: {
        -ms-flex-line-pack: distribute;  /* IE10 */
        -ms-align-content: space-around;
        -webkit-align-content: space-around;
        align-content: space-around;
      };

      /*******************************
                Other Layout
      *******************************/

      --layout-block: {
        display: block;
      };

      --layout-invisible: {
        visibility: hidden !important;
      };

      --layout-relative: {
        position: relative;
      };

      --layout-fit: {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
      };

      --layout-scroll: {
        -webkit-overflow-scrolling: touch;
        overflow: auto;
      };

      --layout-fullbleed: {
        margin: 0;
        height: 100vh;
      };

      /* fixed position */

      --layout-fixed-top: {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
      };

      --layout-fixed-right: {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
      };

      --layout-fixed-bottom: {
        position: fixed;
        right: 0;
        bottom: 0;
        left: 0;
      };

      --layout-fixed-left: {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
      };

    }
  </style>
</custom-style>`;
  template.setAttribute('style', 'display: none;');
  document.head.appendChild(template.content);
  var style = document.createElement('style');
  style.textContent = '[hidden] { display: none !important; }';
  document.head.appendChild(style);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const template$1 = html$1`
<custom-style>
  <style is="custom-style">
    html {

      /* Material Design color palette for Google products */

      --google-red-100: #f4c7c3;
      --google-red-300: #e67c73;
      --google-red-500: #db4437;
      --google-red-700: #c53929;

      --google-blue-100: #c6dafc;
      --google-blue-300: #7baaf7;
      --google-blue-500: #4285f4;
      --google-blue-700: #3367d6;

      --google-green-100: #b7e1cd;
      --google-green-300: #57bb8a;
      --google-green-500: #0f9d58;
      --google-green-700: #0b8043;

      --google-yellow-100: #fce8b2;
      --google-yellow-300: #f7cb4d;
      --google-yellow-500: #f4b400;
      --google-yellow-700: #f09300;

      --google-grey-100: #f5f5f5;
      --google-grey-300: #e0e0e0;
      --google-grey-500: #9e9e9e;
      --google-grey-700: #616161;

      /* Material Design color palette from online spec document */

      --paper-red-50: #ffebee;
      --paper-red-100: #ffcdd2;
      --paper-red-200: #ef9a9a;
      --paper-red-300: #e57373;
      --paper-red-400: #ef5350;
      --paper-red-500: #f44336;
      --paper-red-600: #e53935;
      --paper-red-700: #d32f2f;
      --paper-red-800: #c62828;
      --paper-red-900: #b71c1c;
      --paper-red-a100: #ff8a80;
      --paper-red-a200: #ff5252;
      --paper-red-a400: #ff1744;
      --paper-red-a700: #d50000;

      --paper-pink-50: #fce4ec;
      --paper-pink-100: #f8bbd0;
      --paper-pink-200: #f48fb1;
      --paper-pink-300: #f06292;
      --paper-pink-400: #ec407a;
      --paper-pink-500: #e91e63;
      --paper-pink-600: #d81b60;
      --paper-pink-700: #c2185b;
      --paper-pink-800: #ad1457;
      --paper-pink-900: #880e4f;
      --paper-pink-a100: #ff80ab;
      --paper-pink-a200: #ff4081;
      --paper-pink-a400: #f50057;
      --paper-pink-a700: #c51162;

      --paper-purple-50: #f3e5f5;
      --paper-purple-100: #e1bee7;
      --paper-purple-200: #ce93d8;
      --paper-purple-300: #ba68c8;
      --paper-purple-400: #ab47bc;
      --paper-purple-500: #9c27b0;
      --paper-purple-600: #8e24aa;
      --paper-purple-700: #7b1fa2;
      --paper-purple-800: #6a1b9a;
      --paper-purple-900: #4a148c;
      --paper-purple-a100: #ea80fc;
      --paper-purple-a200: #e040fb;
      --paper-purple-a400: #d500f9;
      --paper-purple-a700: #aa00ff;

      --paper-deep-purple-50: #ede7f6;
      --paper-deep-purple-100: #d1c4e9;
      --paper-deep-purple-200: #b39ddb;
      --paper-deep-purple-300: #9575cd;
      --paper-deep-purple-400: #7e57c2;
      --paper-deep-purple-500: #673ab7;
      --paper-deep-purple-600: #5e35b1;
      --paper-deep-purple-700: #512da8;
      --paper-deep-purple-800: #4527a0;
      --paper-deep-purple-900: #311b92;
      --paper-deep-purple-a100: #b388ff;
      --paper-deep-purple-a200: #7c4dff;
      --paper-deep-purple-a400: #651fff;
      --paper-deep-purple-a700: #6200ea;

      --paper-indigo-50: #e8eaf6;
      --paper-indigo-100: #c5cae9;
      --paper-indigo-200: #9fa8da;
      --paper-indigo-300: #7986cb;
      --paper-indigo-400: #5c6bc0;
      --paper-indigo-500: #3f51b5;
      --paper-indigo-600: #3949ab;
      --paper-indigo-700: #303f9f;
      --paper-indigo-800: #283593;
      --paper-indigo-900: #1a237e;
      --paper-indigo-a100: #8c9eff;
      --paper-indigo-a200: #536dfe;
      --paper-indigo-a400: #3d5afe;
      --paper-indigo-a700: #304ffe;

      --paper-blue-50: #e3f2fd;
      --paper-blue-100: #bbdefb;
      --paper-blue-200: #90caf9;
      --paper-blue-300: #64b5f6;
      --paper-blue-400: #42a5f5;
      --paper-blue-500: #2196f3;
      --paper-blue-600: #1e88e5;
      --paper-blue-700: #1976d2;
      --paper-blue-800: #1565c0;
      --paper-blue-900: #0d47a1;
      --paper-blue-a100: #82b1ff;
      --paper-blue-a200: #448aff;
      --paper-blue-a400: #2979ff;
      --paper-blue-a700: #2962ff;

      --paper-light-blue-50: #e1f5fe;
      --paper-light-blue-100: #b3e5fc;
      --paper-light-blue-200: #81d4fa;
      --paper-light-blue-300: #4fc3f7;
      --paper-light-blue-400: #29b6f6;
      --paper-light-blue-500: #03a9f4;
      --paper-light-blue-600: #039be5;
      --paper-light-blue-700: #0288d1;
      --paper-light-blue-800: #0277bd;
      --paper-light-blue-900: #01579b;
      --paper-light-blue-a100: #80d8ff;
      --paper-light-blue-a200: #40c4ff;
      --paper-light-blue-a400: #00b0ff;
      --paper-light-blue-a700: #0091ea;

      --paper-cyan-50: #e0f7fa;
      --paper-cyan-100: #b2ebf2;
      --paper-cyan-200: #80deea;
      --paper-cyan-300: #4dd0e1;
      --paper-cyan-400: #26c6da;
      --paper-cyan-500: #00bcd4;
      --paper-cyan-600: #00acc1;
      --paper-cyan-700: #0097a7;
      --paper-cyan-800: #00838f;
      --paper-cyan-900: #006064;
      --paper-cyan-a100: #84ffff;
      --paper-cyan-a200: #18ffff;
      --paper-cyan-a400: #00e5ff;
      --paper-cyan-a700: #00b8d4;

      --paper-teal-50: #e0f2f1;
      --paper-teal-100: #b2dfdb;
      --paper-teal-200: #80cbc4;
      --paper-teal-300: #4db6ac;
      --paper-teal-400: #26a69a;
      --paper-teal-500: #009688;
      --paper-teal-600: #00897b;
      --paper-teal-700: #00796b;
      --paper-teal-800: #00695c;
      --paper-teal-900: #004d40;
      --paper-teal-a100: #a7ffeb;
      --paper-teal-a200: #64ffda;
      --paper-teal-a400: #1de9b6;
      --paper-teal-a700: #00bfa5;

      --paper-green-50: #e8f5e9;
      --paper-green-100: #c8e6c9;
      --paper-green-200: #a5d6a7;
      --paper-green-300: #81c784;
      --paper-green-400: #66bb6a;
      --paper-green-500: #4caf50;
      --paper-green-600: #43a047;
      --paper-green-700: #388e3c;
      --paper-green-800: #2e7d32;
      --paper-green-900: #1b5e20;
      --paper-green-a100: #b9f6ca;
      --paper-green-a200: #69f0ae;
      --paper-green-a400: #00e676;
      --paper-green-a700: #00c853;

      --paper-light-green-50: #f1f8e9;
      --paper-light-green-100: #dcedc8;
      --paper-light-green-200: #c5e1a5;
      --paper-light-green-300: #aed581;
      --paper-light-green-400: #9ccc65;
      --paper-light-green-500: #8bc34a;
      --paper-light-green-600: #7cb342;
      --paper-light-green-700: #689f38;
      --paper-light-green-800: #558b2f;
      --paper-light-green-900: #33691e;
      --paper-light-green-a100: #ccff90;
      --paper-light-green-a200: #b2ff59;
      --paper-light-green-a400: #76ff03;
      --paper-light-green-a700: #64dd17;

      --paper-lime-50: #f9fbe7;
      --paper-lime-100: #f0f4c3;
      --paper-lime-200: #e6ee9c;
      --paper-lime-300: #dce775;
      --paper-lime-400: #d4e157;
      --paper-lime-500: #cddc39;
      --paper-lime-600: #c0ca33;
      --paper-lime-700: #afb42b;
      --paper-lime-800: #9e9d24;
      --paper-lime-900: #827717;
      --paper-lime-a100: #f4ff81;
      --paper-lime-a200: #eeff41;
      --paper-lime-a400: #c6ff00;
      --paper-lime-a700: #aeea00;

      --paper-yellow-50: #fffde7;
      --paper-yellow-100: #fff9c4;
      --paper-yellow-200: #fff59d;
      --paper-yellow-300: #fff176;
      --paper-yellow-400: #ffee58;
      --paper-yellow-500: #ffeb3b;
      --paper-yellow-600: #fdd835;
      --paper-yellow-700: #fbc02d;
      --paper-yellow-800: #f9a825;
      --paper-yellow-900: #f57f17;
      --paper-yellow-a100: #ffff8d;
      --paper-yellow-a200: #ffff00;
      --paper-yellow-a400: #ffea00;
      --paper-yellow-a700: #ffd600;

      --paper-amber-50: #fff8e1;
      --paper-amber-100: #ffecb3;
      --paper-amber-200: #ffe082;
      --paper-amber-300: #ffd54f;
      --paper-amber-400: #ffca28;
      --paper-amber-500: #ffc107;
      --paper-amber-600: #ffb300;
      --paper-amber-700: #ffa000;
      --paper-amber-800: #ff8f00;
      --paper-amber-900: #ff6f00;
      --paper-amber-a100: #ffe57f;
      --paper-amber-a200: #ffd740;
      --paper-amber-a400: #ffc400;
      --paper-amber-a700: #ffab00;

      --paper-orange-50: #fff3e0;
      --paper-orange-100: #ffe0b2;
      --paper-orange-200: #ffcc80;
      --paper-orange-300: #ffb74d;
      --paper-orange-400: #ffa726;
      --paper-orange-500: #ff9800;
      --paper-orange-600: #fb8c00;
      --paper-orange-700: #f57c00;
      --paper-orange-800: #ef6c00;
      --paper-orange-900: #e65100;
      --paper-orange-a100: #ffd180;
      --paper-orange-a200: #ffab40;
      --paper-orange-a400: #ff9100;
      --paper-orange-a700: #ff6500;

      --paper-deep-orange-50: #fbe9e7;
      --paper-deep-orange-100: #ffccbc;
      --paper-deep-orange-200: #ffab91;
      --paper-deep-orange-300: #ff8a65;
      --paper-deep-orange-400: #ff7043;
      --paper-deep-orange-500: #ff5722;
      --paper-deep-orange-600: #f4511e;
      --paper-deep-orange-700: #e64a19;
      --paper-deep-orange-800: #d84315;
      --paper-deep-orange-900: #bf360c;
      --paper-deep-orange-a100: #ff9e80;
      --paper-deep-orange-a200: #ff6e40;
      --paper-deep-orange-a400: #ff3d00;
      --paper-deep-orange-a700: #dd2c00;

      --paper-brown-50: #efebe9;
      --paper-brown-100: #d7ccc8;
      --paper-brown-200: #bcaaa4;
      --paper-brown-300: #a1887f;
      --paper-brown-400: #8d6e63;
      --paper-brown-500: #795548;
      --paper-brown-600: #6d4c41;
      --paper-brown-700: #5d4037;
      --paper-brown-800: #4e342e;
      --paper-brown-900: #3e2723;

      --paper-grey-50: #fafafa;
      --paper-grey-100: #f5f5f5;
      --paper-grey-200: #eeeeee;
      --paper-grey-300: #e0e0e0;
      --paper-grey-400: #bdbdbd;
      --paper-grey-500: #9e9e9e;
      --paper-grey-600: #757575;
      --paper-grey-700: #616161;
      --paper-grey-800: #424242;
      --paper-grey-900: #212121;

      --paper-blue-grey-50: #eceff1;
      --paper-blue-grey-100: #cfd8dc;
      --paper-blue-grey-200: #b0bec5;
      --paper-blue-grey-300: #90a4ae;
      --paper-blue-grey-400: #78909c;
      --paper-blue-grey-500: #607d8b;
      --paper-blue-grey-600: #546e7a;
      --paper-blue-grey-700: #455a64;
      --paper-blue-grey-800: #37474f;
      --paper-blue-grey-900: #263238;

      /* opacity for dark text on a light background */
      --dark-divider-opacity: 0.12;
      --dark-disabled-opacity: 0.38; /* or hint text or icon */
      --dark-secondary-opacity: 0.54;
      --dark-primary-opacity: 0.87;

      /* opacity for light text on a dark background */
      --light-divider-opacity: 0.12;
      --light-disabled-opacity: 0.3; /* or hint text or icon */
      --light-secondary-opacity: 0.7;
      --light-primary-opacity: 1.0;

    }

  </style>
</custom-style>
`;
  template$1.setAttribute('style', 'display: none;');
  document.head.appendChild(template$1.content);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const template$2 = html$1`
<custom-style>
  <style is="custom-style">
    html {
      /*
       * You can use these generic variables in your elements for easy theming.
       * For example, if all your elements use \`--primary-text-color\` as its main
       * color, then switching from a light to a dark theme is just a matter of
       * changing the value of \`--primary-text-color\` in your application.
       */
      --primary-text-color: var(--light-theme-text-color);
      --primary-background-color: var(--light-theme-background-color);
      --secondary-text-color: var(--light-theme-secondary-color);
      --disabled-text-color: var(--light-theme-disabled-color);
      --divider-color: var(--light-theme-divider-color);
      --error-color: var(--paper-deep-orange-a700);

      /*
       * Primary and accent colors. Also see color.js for more colors.
       */
      --primary-color: var(--paper-indigo-500);
      --light-primary-color: var(--paper-indigo-100);
      --dark-primary-color: var(--paper-indigo-700);

      --accent-color: var(--paper-pink-a200);
      --light-accent-color: var(--paper-pink-a100);
      --dark-accent-color: var(--paper-pink-a400);


      /*
       * Material Design Light background theme
       */
      --light-theme-background-color: #ffffff;
      --light-theme-base-color: #000000;
      --light-theme-text-color: var(--paper-grey-900);
      --light-theme-secondary-color: #737373;  /* for secondary text and icons */
      --light-theme-disabled-color: #9b9b9b;  /* disabled/hint text */
      --light-theme-divider-color: #dbdbdb;

      /*
       * Material Design Dark background theme
       */
      --dark-theme-background-color: var(--paper-grey-900);
      --dark-theme-base-color: #ffffff;
      --dark-theme-text-color: #ffffff;
      --dark-theme-secondary-color: #bcbcbc;  /* for secondary text and icons */
      --dark-theme-disabled-color: #646464;  /* disabled/hint text */
      --dark-theme-divider-color: #3c3c3c;

      /*
       * Deprecated values because of their confusing names.
       */
      --text-primary-color: var(--dark-theme-text-color);
      --default-primary-color: var(--primary-color);
    }
  </style>
</custom-style>`;
  template$2.setAttribute('style', 'display: none;');
  document.head.appendChild(template$2.content);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const template$3 = html$1`
<custom-style>
  <style is="custom-style">
    html {

      --shadow-transition: {
        transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
      };

      --shadow-none: {
        box-shadow: none;
      };

      /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */

      --shadow-elevation-2dp: {
        box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
                    0 1px 5px 0 rgba(0, 0, 0, 0.12),
                    0 3px 1px -2px rgba(0, 0, 0, 0.2);
      };

      --shadow-elevation-3dp: {
        box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14),
                    0 1px 8px 0 rgba(0, 0, 0, 0.12),
                    0 3px 3px -2px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-4dp: {
        box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14),
                    0 1px 10px 0 rgba(0, 0, 0, 0.12),
                    0 2px 4px -1px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-6dp: {
        box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14),
                    0 1px 18px 0 rgba(0, 0, 0, 0.12),
                    0 3px 5px -1px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-8dp: {
        box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
                    0 3px 14px 2px rgba(0, 0, 0, 0.12),
                    0 5px 5px -3px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-12dp: {
        box-shadow: 0 12px 16px 1px rgba(0, 0, 0, 0.14),
                    0 4px 22px 3px rgba(0, 0, 0, 0.12),
                    0 6px 7px -4px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-16dp: {
        box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14),
                    0  6px 30px 5px rgba(0, 0, 0, 0.12),
                    0  8px 10px -5px rgba(0, 0, 0, 0.4);
      };

      --shadow-elevation-24dp: {
        box-shadow: 0 24px 38px 3px rgba(0, 0, 0, 0.14),
                    0 9px 46px 8px rgba(0, 0, 0, 0.12),
                    0 11px 15px -7px rgba(0, 0, 0, 0.4);
      };
    }
  </style>
</custom-style>`;
  template$3.setAttribute('style', 'display: none;');
  document.head.appendChild(template$3.content);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  // Give the user the choice to opt out of font loading.

  if (!window.polymerSkipLoadingFontRoboto) {
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.type = 'text/css';
    link.crossOrigin = 'anonymous';
    link.href = 'https://fonts.googleapis.com/css?family=Roboto+Mono:400,700|Roboto:400,300,300italic,400italic,500,500italic,700,700italic';
    document.head.appendChild(link);
  }

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const template$4 = html$1`<custom-style>
  <style is="custom-style">
    html {

      /* Shared Styles */
      --paper-font-common-base: {
        font-family: 'Roboto', 'Noto', sans-serif;
        -webkit-font-smoothing: antialiased;
      };

      --paper-font-common-code: {
        font-family: 'Roboto Mono', 'Consolas', 'Menlo', monospace;
        -webkit-font-smoothing: antialiased;
      };

      --paper-font-common-expensive-kerning: {
        text-rendering: optimizeLegibility;
      };

      --paper-font-common-nowrap: {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      };

      /* Material Font Styles */

      --paper-font-display4: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 112px;
        font-weight: 300;
        letter-spacing: -.044em;
        line-height: 120px;
      };

      --paper-font-display3: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 56px;
        font-weight: 400;
        letter-spacing: -.026em;
        line-height: 60px;
      };

      --paper-font-display2: {
        @apply --paper-font-common-base;

        font-size: 45px;
        font-weight: 400;
        letter-spacing: -.018em;
        line-height: 48px;
      };

      --paper-font-display1: {
        @apply --paper-font-common-base;

        font-size: 34px;
        font-weight: 400;
        letter-spacing: -.01em;
        line-height: 40px;
      };

      --paper-font-headline: {
        @apply --paper-font-common-base;

        font-size: 24px;
        font-weight: 400;
        letter-spacing: -.012em;
        line-height: 32px;
      };

      --paper-font-title: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 20px;
        font-weight: 500;
        line-height: 28px;
      };

      --paper-font-subhead: {
        @apply --paper-font-common-base;

        font-size: 16px;
        font-weight: 400;
        line-height: 24px;
      };

      --paper-font-body2: {
        @apply --paper-font-common-base;

        font-size: 14px;
        font-weight: 500;
        line-height: 24px;
      };

      --paper-font-body1: {
        @apply --paper-font-common-base;

        font-size: 14px;
        font-weight: 400;
        line-height: 20px;
      };

      --paper-font-caption: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 12px;
        font-weight: 400;
        letter-spacing: 0.011em;
        line-height: 20px;
      };

      --paper-font-menu: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 13px;
        font-weight: 500;
        line-height: 24px;
      };

      --paper-font-button: {
        @apply --paper-font-common-base;
        @apply --paper-font-common-nowrap;

        font-size: 14px;
        font-weight: 500;
        letter-spacing: 0.018em;
        line-height: 24px;
        text-transform: uppercase;
      };

      --paper-font-code2: {
        @apply --paper-font-common-code;

        font-size: 14px;
        font-weight: 700;
        line-height: 20px;
      };

      --paper-font-code1: {
        @apply --paper-font-common-code;

        font-size: 14px;
        font-weight: 500;
        line-height: 20px;
      };

    }

  </style>
</custom-style>`;
  template$4.setAttribute('style', 'display: none;');
  document.head.appendChild(template$4.content);

  const documentContainer = document.createElement("template");
  documentContainer.setAttribute("style", "display: none;");
  documentContainer.innerHTML = `<custom-style>
  <style>
    /*
      Home Assistant default styles.

      In Polymer 2.0, default styles should to be set on the html selector.
      (Setting all default styles only on body breaks shadyCSS polyfill.)
      See: https://github.com/home-assistant/home-assistant-polymer/pull/901
    */
    html {
      font-size: 14px;
      height: 100vh;

      /* text */
      --primary-text-color: #212121;
      --secondary-text-color: #727272;
      --text-primary-color: #ffffff;
      --text-light-primary-color: #212121;
      --disabled-text-color: #bdbdbd;

      /* main interface colors */
      --primary-color: #03a9f4;
      --dark-primary-color: #0288d1;
      --light-primary-color: #b3e5fC;
      --accent-color: #ff9800;
      --divider-color: rgba(0, 0, 0, .12);

      --scrollbar-thumb-color: rgb(194, 194, 194);

      --error-color: #db4437;
      --warning-color: #FF9800;
      --success-color: #0f9d58;
      --info-color: #4285f4;

      /* states and badges */
      --state-icon-color: #44739e;
      --state-icon-active-color: #FDD835;

      /* background and sidebar */
      --card-background-color: #ffffff;
      --primary-background-color: #fafafa;
      --secondary-background-color: #e5e5e5; /* behind the cards on state */

      /* for label-badge */
      --label-badge-red: #DF4C1E;
      --label-badge-blue: #039be5;
      --label-badge-green: #0DA035;
      --label-badge-yellow: #f4b400;

      /*
        Paper-styles color.html dependency is stripped on build.
        When a default paper-style color is used, it needs to be copied
        from paper-styles/color.html to here.
      */

      --paper-grey-50: #fafafa; /* default for: --mwc-switch-unchecked-button-color */
      --paper-grey-200: #eeeeee;  /* for ha-date-picker-style */
      --paper-grey-500: #9e9e9e;  /* --label-badge-grey */

      /* for paper-slider */
      --paper-green-400: #66bb6a;
      --paper-blue-400: #42a5f5;
      --paper-orange-400: #ffa726;

      /* opacity for dark text on a light background */
      --dark-divider-opacity: 0.12;
      --dark-disabled-opacity: 0.38; /* or hint text or icon */
      --dark-secondary-opacity: 0.54;
      --dark-primary-opacity: 0.87;

      /* opacity for light text on a dark background */
      --light-divider-opacity: 0.12;
      --light-disabled-opacity: 0.3; /* or hint text or icon */
      --light-secondary-opacity: 0.7;
      --light-primary-opacity: 1.0;

      /* set our slider style */
      --ha-paper-slider-pin-font-size: 15px;

      /* rgb */
      --rgb-primary-color: 3, 169, 244;
      --rgb-accent-color: 255, 152, 0;
      --rgb-primary-text-color: 33, 33, 33;
      --rgb-secondary-text-color: 114, 114, 114;
      --rgb-text-primary-color: 255, 255, 255;
      --rgb-card-background-color: 255, 255, 255;

      ${Object.entries(derivedStyles).map(([key, value]) => `--${key}: ${value};`).join("")}
    }

    /*
      prevent clipping of positioned elements in a small scrollable
      force smooth scrolling if can scroll
      use non-shady selectors so this only targets iOS 9
      conditional mixin set in ha-style-dialog does not work with shadyCSS
    */
    paper-dialog-scrollable:not(.can-scroll) > .scrollable {
      -webkit-overflow-scrolling: auto !important;
    }

    paper-dialog-scrollable.can-scroll > .scrollable {
      -webkit-overflow-scrolling: touch !important;
    }
  </style>
</custom-style>`;
  document.head.appendChild(documentContainer.content);

  const atLeastVersion = (version, major, minor) => {
    const [haMajor, haMinor] = version.split(".", 2);
    return Number(haMajor) > major || Number(haMajor) === major && Number(haMinor) >= minor;
  };

  const getConfiguration = async hass => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/config"
    });
    return response;
  };
  const getRepositories = async hass => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/repositories"
    });
    return response;
  };
  const getCritical = async hass => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/get_critical"
    });
    return response;
  };
  const getStatus = async hass => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/status"
    });
    return response;
  };
  const getRemovedRepositories = async hass => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/removed"
    });
    return response;
  };
  const repositoryInstall = async (hass, repository) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "install",
      repository: repository
    });
  };
  const repositoryUninstall = async (hass, repository) => {
    return await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "uninstall",
      repository: repository
    });
  };
  const repositoryReleasenotes = async (hass, repository) => {
    const response = await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "release_notes",
      repository: repository
    });
    return response;
  };
  const repositoryToggleBeta = async (hass, repository) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "toggle_beta",
      repository: repository
    });
  };
  const repositoryInstallVersion = async (hass, repository, version) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository/data",
      action: "install",
      repository: repository,
      data: version
    });
  };
  const repositoryAdd = async (hass, repository, category) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository/data",
      action: "add",
      repository: repository,
      data: category
    });
  };
  const repositorySetNotNew = async (hass, repository) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "not_new",
      repository: repository
    });
  };
  const repositoryUpdate = async (hass, repository) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "update",
      repository: repository
    });
  };
  const repositoryDelete = async (hass, repository) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/repository",
      action: "delete",
      repository: repository
    });
  };
  const settingsClearAllNewRepositories = async (hass, categories) => {
    await hass.connection.sendMessagePromise({
      type: "hacs/settings",
      action: "clear_new",
      categories
    });
  };
  const getLovelaceConfiguration = async hass => {
    try {
      const response = await hass.connection.sendMessagePromise({
        type: "lovelace/resources"
      });
      return response;
    } catch (e) {
      return null;
    }
  };
  const fetchResources = hass => hass.connection.sendMessagePromise({
    type: "lovelace/resources"
  });
  const createResource = (hass, values) => hass.callWS({
    type: "lovelace/resources/create",
    ...values
  });
  const updateResource = (hass, values) => hass.callWS({
    type: "lovelace/resources/update",
    ...values
  });
  const deleteResource = (hass, id) => hass.callWS({
    type: "lovelace/resources/delete",
    resource_id: id
  });

  const hacsStyleVariables = css`
  :host {
    --hcv-color-error: var(--hacs-error-color, var(--google-red-500, #f44336));
    --hcv-color-warning: var(--hacs-warning-color, #ff8c00);
    --hcv-color-update: var(--hacs-update-color, #f4b400);
    --hcv-color-new: var(--hacs-new-color, var(--google-blue-500, #2196f3));
    --hcv-color-icon: var(--hacs--default-icon-color, var(--sidebar-icon-color));
    --hcv-color-markdown-background: var(--markdown-code-background-color, #f6f8fa);

    --hcv-text-color-primary: var(--primary-text-color);
    --hcv-text-color-on-background: var(--text-primary-color);
    --hcv-text-color-secondary: var(--secondary-text-color);
    --hcv-text-color-link: var(--link-text-color, var(--accent-color));

    --mdc-dialog-heading-ink-color: var(--hcv-text-color-primary);
    --mdc-dialog-content-ink-color: var(--hcv-text-color-primary);

    /*hacs-fab*/
    --hcv-color-fab: var(--hacs-fab-color, var(--accent-color));
    --hcv-text-color-fab: var(--hacs-fab-text-color, var(--hcv-text-color-on-background));

    /*hacs-chip*/
    --hcv-color-chip: var(--hacs-chip-color, var(--accent-color));
    --hcv-text-color-chip: var(--hacs-chip-text-color, var(--hcv-text-color-on-background));

    /*hacs-link*/
    --hcv-text-decoration-link: var(--hacs-link-text-decoration, none);
  }
`;

  const hacsLinkStyle = css`
  a {
    text-decoration: var(--hcv-text-decoration-link);
    color: var(--hcv-text-color-link);
  }
`;
  const hacsButtonStyle = css`
  mwc-button[raised] {
    border-radius: 10px;
  }
`;
  const hacsIconStyle = css`
  paper-menu-button,
  ha-icon-button,
  ha-icon {
    color: var(--hcv-color-icon);
  }
`;
  const hassTabsSubpage = css`
  hass-tabs-subpage {
    font-family: var(--paper-font-body1_-_font-family);
    -webkit-font-smoothing: var(--paper-font-body1_-_-webkit-font-smoothing);
    font-size: var(--paper-font-body1_-_font-size);
    font-weight: var(--paper-font-body1_-_font-weight);
    line-height: var(--paper-font-body1_-_line-height);
  }
`;
  const fabStyles = css`
  mwc-fab {
    position: fixed;
    bottom: 100px;
    right: 24px;
    z-index: 1;
    margin-bottom: -80px;
    transition: margin-bottom 0.3s;
  }

  mwc-fab[is-wide] {
    bottom: 100px;
    right: 24px;
  }
  mwc-fab[narrow] {
    bottom: 152px;
  }
  mwc-fab[dirty] {
    margin-bottom: 0;
  }

  mwc-fab.rtl {
    right: auto;
    right: 24px;
  }

  mwc-fab[is-wide].rtl {
    bottom: 100px;
    right: auto;
    left: 24px;
  }
`;
  const searchStyles = css`
  search-input.header {
    display: block;
    position: relative;
    left: -22px;
    top: -7px;
    color: var(--secondary-text-color);
    margin-left: 0;
  }
  .search {
    padding: 0 16px;
    background: var(--sidebar-background-color);
    border-bottom: 1px solid var(--divider-color);
  }
  .search search-input {
    position: relative;
    top: 2px;
  }

  search-input {
    --layout-fit_-_right: 100vw;
  }
`;
  const scrollBarStyle = css`
  *::-webkit-scrollbar {
    width: 0.6em;
  }

  *::-webkit-scrollbar-track {
    color: rgba(0, 0, 0, 0.3);
    border-radius: 0.3em;
  }

  *::-webkit-scrollbar-thumb {
    background-color: var(--accent-color);
    border-radius: 0.3em;
  }
`;
  const hacsStyleDialog = [haStyleDialog, css``];

  const hacsCommonClasses = css`
  .warning {
    color: var(--hcv-color-warning);
  }
  .pending_update {
    color: var(--hcv-color-update);
  }
  .pending_restart,
  .error,
  .uninstall {
    color: var(--hcv-color-error);
    --mdc-theme-primary: var(--hcv-color-error);
  }
  .header {
    font-size: var(--paper-font-headline_-_font-size);
    opacity: var(--dark-primary-opacity);
    padding: 8px 0 4px 16px;
  }
`;
  const HacsStyles = [haStyle, hacsIconStyle, hacsButtonStyle, hacsCommonClasses, hacsLinkStyle];

  function areInputsEqual(newInputs, lastInputs) {
    if (newInputs.length !== lastInputs.length) {
      return false;
    }

    for (var i = 0; i < newInputs.length; i++) {
      if (newInputs[i] !== lastInputs[i]) {
        return false;
      }
    }

    return true;
  }

  function memoizeOne(resultFn, isEqual) {
    if (isEqual === void 0) {
      isEqual = areInputsEqual;
    }

    var lastThis;
    var lastArgs = [];
    var lastResult;
    var calledOnce = false;

    function memoized() {
      var newArgs = [];

      for (var _i = 0; _i < arguments.length; _i++) {
        newArgs[_i] = arguments[_i];
      }

      if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) {
        return lastResult;
      }

      lastResult = resultFn.apply(this, newArgs);
      calledOnce = true;
      lastThis = this;
      lastArgs = newArgs;
      return lastResult;
    }

    return memoized;
  }

  // Material Design Icons v5.5.55
  var mdiAccount = "M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z";
  var mdiAlertCircle = "M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z";
  var mdiArrowDownBold = "M9,4H15V12H19.84L12,19.84L4.16,12H9V4Z";
  var mdiArrowLeft = "M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z";
  var mdiArrowRight = "M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z";
  var mdiChevronLeft = "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z";
  var mdiChevronRight = "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z";
  var mdiClose = "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z";
  var mdiCube = "M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15Z";
  var mdiDelete = "M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z";
  var mdiDotsVertical = "M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z";
  var mdiExclamationThick = "M10 3H14V14H10V3M10 21V17H14V21H10Z";
  var mdiGithub = "M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z";
  var mdiHomeAssistant = "M21.8,13H20V21H13V17.67L15.79,14.88L16.5,15C17.66,15 18.6,14.06 18.6,12.9C18.6,11.74 17.66,10.8 16.5,10.8A2.1,2.1 0 0,0 14.4,12.9L14.5,13.61L13,15.13V9.65C13.66,9.29 14.1,8.6 14.1,7.8A2.1,2.1 0 0,0 12,5.7A2.1,2.1 0 0,0 9.9,7.8C9.9,8.6 10.34,9.29 11,9.65V15.13L9.5,13.61L9.6,12.9A2.1,2.1 0 0,0 7.5,10.8A2.1,2.1 0 0,0 5.4,12.9A2.1,2.1 0 0,0 7.5,15L8.21,14.88L11,17.67V21H4V13H2.25C1.83,13 1.42,13 1.42,12.79C1.43,12.57 1.85,12.15 2.28,11.72L11,3C11.33,2.67 11.67,2.33 12,2.33C12.33,2.33 12.67,2.67 13,3L17,7V6H19V9L21.78,11.78C22.18,12.18 22.59,12.59 22.6,12.8C22.6,13 22.2,13 21.8,13M7.5,12A0.9,0.9 0 0,1 8.4,12.9A0.9,0.9 0 0,1 7.5,13.8A0.9,0.9 0 0,1 6.6,12.9A0.9,0.9 0 0,1 7.5,12M16.5,12C17,12 17.4,12.4 17.4,12.9C17.4,13.4 17,13.8 16.5,13.8A0.9,0.9 0 0,1 15.6,12.9A0.9,0.9 0 0,1 16.5,12M12,6.9C12.5,6.9 12.9,7.3 12.9,7.8C12.9,8.3 12.5,8.7 12,8.7C11.5,8.7 11.1,8.3 11.1,7.8C11.1,7.3 11.5,6.9 12,6.9Z";
  var mdiMagnify = "M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z";
  var mdiMenu = "M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z";
  var mdiOpenInNew = "M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z";
  var mdiPalette = "M17.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,9A1.5,1.5 0 0,1 19,10.5A1.5,1.5 0 0,1 17.5,12M14.5,8A1.5,1.5 0 0,1 13,6.5A1.5,1.5 0 0,1 14.5,5A1.5,1.5 0 0,1 16,6.5A1.5,1.5 0 0,1 14.5,8M9.5,8A1.5,1.5 0 0,1 8,6.5A1.5,1.5 0 0,1 9.5,5A1.5,1.5 0 0,1 11,6.5A1.5,1.5 0 0,1 9.5,8M6.5,12A1.5,1.5 0 0,1 5,10.5A1.5,1.5 0 0,1 6.5,9A1.5,1.5 0 0,1 8,10.5A1.5,1.5 0 0,1 6.5,12M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A1.5,1.5 0 0,0 13.5,19.5C13.5,19.11 13.35,18.76 13.11,18.5C12.88,18.23 12.73,17.88 12.73,17.5A1.5,1.5 0 0,1 14.23,16H16A5,5 0 0,0 21,11C21,6.58 16.97,3 12,3Z";
  var mdiPlus = "M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z";
  var mdiPuzzle = "M20.5,11H19V7C19,5.89 18.1,5 17,5H13V3.5A2.5,2.5 0 0,0 10.5,1A2.5,2.5 0 0,0 8,3.5V5H4A2,2 0 0,0 2,7V10.8H3.5C5,10.8 6.2,12 6.2,13.5C6.2,15 5,16.2 3.5,16.2H2V20A2,2 0 0,0 4,22H7.8V20.5C7.8,19 9,17.8 10.5,17.8C12,17.8 13.2,19 13.2,20.5V22H17A2,2 0 0,0 19,20V16H20.5A2.5,2.5 0 0,0 23,13.5A2.5,2.5 0 0,0 20.5,11Z";
  var mdiRobot = "M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z";
  var mdiStar = "M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z";

  const languages = {
    "da": {
      "common": {
        "about": "Om",
        "add": "tilføj",
        "appdaemon_apps": "AppDaemon-apps",
        "appdaemon_plural": "AppDaemon-apps",
        "background_task": "Baggrundsopgave kører. Denne side vil genindlæses automatisk.",
        "check_log_file": "Tjek din logfil for flere detaljer.",
        "continue": "Fortsæt",
        "disabled": "Deaktiveret",
        "documentation": "Dokumentation",
        "element": "element",
        "hacs_is_disabled": "HACS er deaktiveret",
        "install": "Installer",
        "installed": "installeret",
        "integration": "Integration",
        "integration_plural": "Integrationer",
        "integrations": "Integrationer",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-element",
        "lovelace_elements": "Lovelace-elementer",
        "manage": "administrer",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon-apps",
        "netdaemon_plural": "NetDaemon-apps",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace-elementer",
        "plugins": "Lovelace-elementer",
        "python_script": "Python-script",
        "python_script_plural": "Python-scripts",
        "python_scripts": "Python-scripts",
        "repositories": "Repositories",
        "repository": "Repository",
        "settings": "indstillinger",
        "theme": "Tema",
        "theme_plural": "Temaer",
        "themes": "Temaer",
        "uninstall": "Afinstaller",
        "update": "Opdater",
        "version": "Version"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Kun en enkelt konfiguration af HACS er tilladt."
        },
        "error": {
          "auth": "Personlig adgangstoken er ikke korrekt."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver opdagelse & sporing af AppDaemon-apps",
              "netdaemon": "Aktiver opdagelse og sporing af NetDaemon-apps",
              "python_script": "Aktivér opdagelse og sporing af python_scripts",
              "sidepanel_icon": "Sidepanelikon",
              "sidepanel_title": "Sidepanelets titel",
              "theme": "Aktivér opdagelse og sporing af temaer",
              "token": "GitHub personlig adgangstoken"
            },
            "description": "Hvis du har brug for hjælp til konfigurationen, så kig her: https://hacs.xyz/docs/configuration/start"
          }
        }
      },
      "confirm": {
        "add_to_lovelace": "Er du sikker på, at du vil tilføje dette til dine Lovelace-ressourcer?",
        "bg_task": "Handlingen er deaktiveret, mens baggrundsopgaver kører.",
        "cancel": "Annuller",
        "continue": "Er du sikker på, at du vil fortsætte?",
        "delete": "Er du sikker på, at du vil slette '{Item}'?",
        "delete_installed": "'{item}' er installeret. Du skal afinstallere det, før du kan slette det.",
        "exist": "{item} findes allerede",
        "generic": "Er du sikker?",
        "home_assistant_is_restarting": "Vent venligst - Home Assistant genstarter nu.",
        "home_assistant_version_not_correct": "Du kører Home Assistant version '{haversion}', men dette repository kræver som minimum version '{minversion}'.",
        "no": "Nej",
        "no_upgrades": "Der er ingen opdateringer tilgængelig",
        "ok": "OK",
        "overwrite": "Dette vil overskrive den.",
        "reload_data": "Dette genindlæser data fra alle repositories, som HACS kender til. Dette vil tage nogen tid at fuldføre.",
        "restart_home_assistant": "Er du sikker på, at du vil genstarte Home Assistant?",
        "uninstall": "Er du sikker på, at du vil afinstallere '{Item}'?",
        "upgrade_all": "Dette vil opdatere alle repositories. Sørg for at du har læst udgivelsesnoterne for dem alle, inden du fortsætter.",
        "yes": "Ja"
      },
      "dialog_about": {
        "frontend_version": "Frontend-version",
        "installed_repositories": "Installerede repositories",
        "integration_version": "Integrationsversion",
        "useful_links": "Nyttige links"
      },
      "dialog_add_repo": {
        "limit": "Kun de første 100 repositories vises. Brug søgningen til at filtrere, hvad du har brug for",
        "no_match": "Der blev ikke fundet nogen repositories, der matcher dit filter",
        "sort_by": "Sorter efter",
        "title": "Tilføj repository"
      },
      "dialog_custom_repositories": {
        "category": "Kategori",
        "no_category": "Manglende kategori",
        "no_repository": "Manglende repository",
        "title": "Brugerdefinerede repositories",
        "url_placeholder": "Tilføj brugerdefineret repository-webadresse"
      },
      "dialog_info": {
        "author": "Udvikler",
        "downloads": "Downloads",
        "install": "Installer dette repository i HACS",
        "loading": "Indlæser oplysninger...",
        "no_info": "Udvikleren har ikke givet flere oplysninger om dette repository",
        "open_issues": "Åbn issues",
        "open_repo": "Åbn repository",
        "stars": "Stjerner",
        "version_installed": "Installeret version"
      },
      "dialog_install": {
        "restart": "Husk, at du skal genstarte Home Assistant, før ændringer af integrationer (custom_components) træder i kræft.",
        "select_version": "Vælg version",
        "show_beta": "Vis betaversioner",
        "type": "Type",
        "url": "Webadresse"
      },
      "dialog_update": {
        "available_version": "Tilgængelig version",
        "changelog": "Udgivelsesnoter",
        "installed_version": "Installeret version",
        "releasenotes": "Udgivelsesnoter for {release}",
        "title": "Ventende opdatering"
      },
      "entry": {
        "information": "Oplysninger",
        "intro": "Opdateringer og vigtige meddelelser vises her, hvis der er nogen",
        "messages": {
          "disabled": {
            "content": "Tjek din logfil for flere detaljer",
            "title": "HACS er deaktiveret"
          },
          "has_pending_tasks": {
            "content": "Nogle repositories vises muligvis ikke, før dette er fuldført",
            "title": "Baggrundsopgaver venter"
          },
          "resources": {
            "content": "Du har {number} Lovelace-elementer, der ikke er indlæst korrekt i Lovelace.",
            "title": "Ikke indlæst i Lovelace"
          },
          "restart": {
            "content": "Du har {number} integrationer, der kræver en genstart af Home Assistant. Du kan genstarte fra 'Serveradministration'-sektionen under Indstillinger i Home Assistant-brugerfladen.",
            "title": "Afventer genstart"
          },
          "startup": {
            "content": "HACS starter op. Der kan i dette tidsrum mangle nogle oplysninger, eller de kan være ukorekte.",
            "title": "HACS starter op"
          },
          "wrong_frontend_installed": {
            "content": "Du har version {running} af HACS-frontend installeret, men version {expected} var forventet, hvis dette ser du denne besked. Home Assistant kunne ikke installere den nye version. Prøv at genstarte Home Assistant.",
            "title": "Uventet frontend-version"
          },
          "wrong_frontend_loaded": {
            "content": "Du kører version {running} af HACS-frontend, men version {expected} var forventet. Du bør rydde din browser-cache.",
            "title": "Uventet frontend-version"
          }
        },
        "pending_updates": "Ventende opdateringer"
      },
      "menu": {
        "about": "Om HACS",
        "clear": "Ryd alle nye",
        "custom_repositories": "Brugerdefinerede repositories",
        "dismiss": "Afvis alle nye repositories",
        "documentation": "Dokumentation",
        "open_issue": "Opret issue",
        "reload": "Genindlæs vindue"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver opdagelse og sporing af AppDaemon-apps",
              "country": "Filtrer med landekode.",
              "debug": "Aktiver debug.",
              "experimental": "Aktivér eksperimentelle funktioner",
              "netdaemon": "Aktiver opdagelse og sporing af NetDaemon-apps",
              "not_in_use": "Ikke i brug med YAML",
              "release_limit": "Antal udgivelser, der skal vises.",
              "sidepanel_icon": "Sidepanelikon",
              "sidepanel_title": "Sidepanelets titel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Denne integration understøtter config_flow. Det betyder, at du nu kan gå til integrationssektionen i din brugerflade for at konfigurere den.",
        "config_flow_title": "Brugerfladekonfiguration understøttet",
        "integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.",
        "no_restart_required": "Ingen genstart påkrævet",
        "not_loaded": "Ikke indlæst",
        "plugin_not_loaded": "Dette element er ikke føjet til dine Lovelace-ressourcer.",
        "restart": "Du skal genstarte Home Assistant.",
        "restart_pending": "Afventer genstart"
      },
      "repository_card": {
        "dismiss": "Afvis",
        "hide": "Skjul",
        "information": "Oplysninger",
        "new_repository": "Nyt repository",
        "not_loaded": "Ikke indlæst",
        "open_issue": "Opret issue",
        "open_source": "Åbn kilde",
        "pending_restart": "Afventer genstart",
        "pending_update": "Ventende opdatering",
        "reinstall": "Geninstaller",
        "report": "Rapporter til fjernelse",
        "update_information": "Opdater oplysninger"
      },
      "repository": {
        "add_to_lovelace": "Tilføj til Lovelace",
        "authors": "Forfattere",
        "available": "Tilgængelig",
        "back_to": "Tilbage til",
        "changelog": "Udgivelsesnoter",
        "downloads": "Downloads",
        "flag_this": "Marker denne",
        "frontend_version": "Frontend-version",
        "github_stars": "GitHub-stjerner",
        "goto_integrations": "Gå til integrationer",
        "hide": "Skjul",
        "hide_beta": "Skjul beta",
        "install": "Installer",
        "installed": "Installeret",
        "lovelace_copy_example": "Kopiér eksemplet til din Udklipsholder",
        "lovelace_instruction": "Tilføj dette til din lovelace-konfiguration",
        "lovelace_no_js_type": "Kunne ikke afgøre typen af dette element, tjek venligst repository'et.",
        "newest": "nyeste",
        "note_appdaemon": "Du skal stadig føje den til filen 'apps.yaml'",
        "note_installed": "Når det er installeret, vil dette være placeret i",
        "note_integration": "du skal stadig føje den til filen 'configuration.yaml'",
        "note_plugin": "du skal stadig tilføje det til din lovelace-konfiguration ('ui-lovelace.yaml' eller Tekstbaseret redigering)",
        "note_plugin_post_107": "du skal stadig tilføje det til din lovelace-konfiguration ('configuration.yaml' eller ressourceeditoren '/config/lovelace/resources')",
        "open_issue": "Opret issue",
        "open_plugin": "Åbn element",
        "reinstall": "Geninstaller",
        "repository": "Repository",
        "restart_home_assistant": "Genstart Home Assistant",
        "show_beta": "Vis beta",
        "uninstall": "Afinstaller",
        "update_information": "Opdater oplysninger",
        "upgrade": "Opdater"
      },
      "search": {
        "installed": "Søg efter installerede repositories",
        "installed_new": "Søg efter installerede eller nye repositories",
        "placeholder": "Søg efter repository"
      },
      "sections": {
        "about": {
          "description": "Vis information om HACS",
          "title": "Om"
        },
        "automation": {
          "description": "Det er her, du finder python_scripts, AppDaemon-apps og NetDaemon-apps",
          "title": "Automatisering"
        },
        "frontend": {
          "description": "Det er her, du finder temaer, brugerdefinerede kort og andre elementer til lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Det er her, du finder brugerdefinerede integrationer (custom_components)",
          "title": "Integrationer"
        },
        "pending_repository_upgrade": "Du kører version {installed}, version {available} er tilgængelig"
      },
      "settings": {
        "add_custom_repository": "TILFØJ ET BRUGERDEFINERET REPOSITORY",
        "adding_new_repo": "Tilføjer nyt repository '{repo}'",
        "adding_new_repo_category": "Med kategorien '{category}'.",
        "bg_task_custom": "Brugerdefinerede repositories er skjult, mens opgaver i baggrunden kører.",
        "category": "Kategori",
        "compact_mode": "Kompakt tilstand",
        "custom_repositories": "BRUGERDEFINEREDE REPOSITORIES",
        "delete": "Slet",
        "display": "Visning",
        "grid": "Gitter",
        "hacs_repo": "HACS-repo",
        "hidden_repositories": "Skjulte repositories",
        "missing_category": "Du skal vælge en kategori",
        "open_repository": "Åbn repository",
        "reload_data": "Genindlæs data",
        "reload_window": "Genindlæs vindue",
        "repository_configuration": "Konfiguration af repository",
        "save": "Gem",
        "table": "Tabel",
        "table_view": "Tabelvisning",
        "unhide": "Vis",
        "upgrade_all": "Opdater alle"
      },
      "store": {
        "ascending": "stigende",
        "clear_new": "Marker alle som set",
        "descending": "faldende",
        "last_updated": "Sidst opdateret",
        "name": "Navn",
        "new_repositories": "Nye repositories",
        "new_repositories_note": "Du har over 10 nye repositories, der vises her. Hvis du vil rydde dem alle, skal du klikke på de 3 prikker i øverste højre hjørne og afvise dem alle.",
        "no_repositories": "Ingen repositories",
        "no_repositories_desc1": "Det ser ud til, at du ikke har nogen repositories installeret i denne sektion endnu.",
        "no_repositories_desc2": "Klik på + i nederste hjørne for at tilføje dit første!",
        "no_repositories_found_desc1": "Der blev ikke fundet installerede repositories, der matcher \"{searchInput}\" i denne sektion.",
        "no_repositories_found_desc2": "Prøv at søge efter noget andet!",
        "pending_upgrades": "Ventende opdateringer",
        "placeholder_search": "Indtast en søgeterm...",
        "sort": "sorter",
        "stars": "Stjerner",
        "status": "Status"
      },
      "time": {
        "ago": "siden",
        "day": "dag",
        "days": "dage",
        "hour": "time",
        "hours": "timer",
        "minute": "minut",
        "minutes": "minutter",
        "month": "måned",
        "months": "måneder",
        "one": "Et",
        "one_day_ago": "en dag siden",
        "one_hour_ago": "en time siden",
        "one_minute_ago": "et minut siden",
        "one_month_ago": "en måned siden",
        "one_second_ago": "et sekund siden",
        "one_year_ago": "et år siden",
        "second": "sekund",
        "seconds": "sekunder",
        "x_days_ago": "{x} dage siden",
        "x_hours_ago": "{x} timer siden",
        "x_minutes_ago": "{x} minutter siden",
        "x_months_ago": "{x} måneder siden",
        "x_seconds_ago": "{x} sekunder siden",
        "x_years_ago": "{x} år siden",
        "year": "år",
        "years": "år"
      }
    },
    "de": {
      "common": {
        "about": "Über",
        "add": "hinzufügen",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apps",
        "appdaemon_plural": "AppDaemon Apps",
        "background_task": "Hintergrundprozess läuft. Diese Seite lädt neu, sobald dieser fertig ist.",
        "check_log_file": "Überprüfe die Logdatei für weitere Informationen.",
        "continue": "Fortfahren",
        "disabled": "Deaktiviert",
        "documentation": "Dokumentation",
        "element": "Element",
        "hacs_is_disabled": "HACS ist deaktiviert",
        "install": "Installieren",
        "installed": "Installiert",
        "integration": "Integration",
        "integration_plural": "Integrationen",
        "integrations": "Integrationen",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-Element",
        "lovelace_elements": "Lovelace-Elemente",
        "manage": "verwalten",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "netdaemon_plural": "NetDaemon Apps",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace-Elemente",
        "plugins": "Lovelace-Elemente",
        "python_script": "Python Skript",
        "python_script_plural": "Python Skripte",
        "python_scripts": "Python Skripte",
        "repositories": "Repositories",
        "repository": "Repository",
        "settings": "Einstellungen",
        "theme": "Theme",
        "theme_plural": "Themes",
        "themes": "Themes",
        "uninstall": "Deinstallieren",
        "update": "Aktualisieren",
        "version": "Version"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Es ist nur eine einzelne HACS-Instanz erlaubt."
        },
        "error": {
          "auth": "Persönlicher Zugriffstoken ist falsch."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "AppDaemon App-Entdeckung & Nachverfolgung aktivieren",
              "netdaemon": "NetDaemon App-Entdeckung & Nachverfolgung aktivieren",
              "python_script": "Python Script-Entdeckung & Nachverfolgung aktivieren",
              "sidepanel_icon": "Sidepanel Symbol",
              "sidepanel_title": "Sidepanel Titel",
              "theme": "Theme-Entdeckung & Nachverfolgung aktivieren",
              "token": "Persönlicher GitHub Zugriffstoken"
            },
            "description": "Wenn du Hilfe mit den Einstellungen brauchst, kannst du hier nachsehen: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Möchtest du dies wirklich zu deinen Lovelace-Ressourcen hinzufügen?",
        "bg_task": "Die Aktion ist deaktiviert, während Hintergrundprozesse ausgeführt werden.",
        "cancel": "Abbrechen",
        "continue": "Bist du dir sicher, dass du fortfahren möchtest?",
        "delete": "Möchtest du '{item}' wirklich löschen?",
        "delete_installed": "'{item}' ist installiert. Du musst es deinstallieren, bevor du es löschen kannst.",
        "exist": "{item} existiert bereits",
        "generic": "Bist du dir sicher?",
        "home_assistant_is_restarting": "Bitte warte, Home Assistant wird jetzt neu gestartet.",
        "home_assistant_version_not_correct": "Du benutzt die Home Assistant-Version '{haversion}', für dieses Repository muss jedoch die Mindestversion '{minversion}' installiert sein.",
        "no": "Nein",
        "no_upgrades": "Keine Upgrades ausstehend",
        "ok": "OK",
        "overwrite": "Dadurch wird die Datei überschrieben.",
        "reload_data": "Hierdurch werden die Daten aller Repositories die HACS kennt neu geladen. Dies wird einige Zeit in Anspruch nehmen.",
        "restart_home_assistant": "Bist du dir sicher, dass du Home Assistant neu starten möchtest?",
        "uninstall": "Möchtest du '{item}' wirklich deinstallieren?",
        "upgrade_all": "Hierdurch werden all diese Repositories aktualisiert. Stelle sicher, dass du die Versionshinweise vorher gelesen hast.",
        "yes": "Ja"
      },
      "dialog_about": {
        "frontend_version": "Frontend Version",
        "installed_repositories": "Installierte Repositories",
        "integration_version": "Integrations Version",
        "useful_links": "Nützliche Links"
      },
      "dialog_add_repo": {
        "limit": "Es werden nur die ersten 100 Repositories angezeigt. Verwenden Sie die Suche, um zu filtern, was Sie benötigen",
        "no_match": "Es wurden keine Repositories gefunden, die Ihrem Filter entsprechen",
        "sort_by": "Sortiere nach",
        "title": "Repository hinzufügen"
      },
      "dialog_custom_repositories": {
        "category": "Kategorie",
        "no_category": "Fehlende Kategorie",
        "no_repository": "Fehlendes Repository",
        "title": "Benutzerdefinierte Repositories",
        "url_placeholder": "Fügen Sie eine benutzerdefinierte Repository-URL hinzu"
      },
      "dialog_info": {
        "author": "Autor",
        "downloads": "Downloads",
        "install": "Installieren Sie dieses Repository in HACS",
        "loading": "Informationen laden...",
        "no_info": "Der Entwickler hat keine weiteren Informationen für dieses Repository bereitgestellt",
        "open_issues": "Probleme melden",
        "open_repo": "Repository öffnen",
        "stars": "Sterne",
        "version_installed": "Version installiert"
      },
      "dialog_install": {
        "restart": "Denken Sie daran, dass Sie Home Assistant neu starten müssen, bevor Änderungen an Integrationen (custom_components) angewendet werden.",
        "select_version": "Version auswählen",
        "show_beta": "Beta-Versionen anzeigen",
        "type": "Typ",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Verfügbare Version",
        "changelog": "Änderungsprotokoll",
        "installed_version": "Installierte Version",
        "releasenotes": "Releasenotes für {release}",
        "title": "Update ausstehend"
      },
      "entry": {
        "information": "Information",
        "intro": "Aktualisierungen und wichtige Meldungen werden hier angezeigt, falls vorhanden",
        "messages": {
          "disabled": {
            "content": "Überprüfe die Logdatei für weitere Informationen",
            "title": "HACS ist deaktiviert"
          },
          "has_pending_tasks": {
            "content": "Einige Repositorys werden möglicherweise erst angezeigt, wenn dies abgeschlossen ist",
            "title": "Hintergrundaufgaben stehen noch aus"
          },
          "resources": {
            "content": "Sie haben {number} Lovelace-Elemente, die in Lovelace nicht richtig geladen sind.",
            "title": "Nicht in Lovelace geladen"
          },
          "restart": {
            "content": "Sie haben {number} Integrationen, die einen Neustart von Home Assistant erfordern. Dies können Sie im Abschnitt 'Server Controls' im Konfigurationsteil der Home Assistant-Benutzeroberfläche tun.",
            "title": "Ausstehender Neustart"
          },
          "startup": {
            "content": "HACS wird gestartet, während dieser Zeit könnten einige Informationen fehlen oder falsch sein",
            "title": "HACS startet"
          },
          "wrong_frontend_installed": {
            "content": "Sie haben {running} des HACS-Frontends installiert, aber Version {expected} wurde erwartet. Wenn diese Meldung angezeigt wird, dass Home Assistant die neue Version nicht installieren konnte, starten Sie Home Assistant neu.",
            "title": "Unerwartete Frontend-Version"
          },
          "wrong_frontend_loaded": {
            "content": "Sie führen die Version {running} des HACS-Frontends aus, aber es wurde die Version {expected} erwartet. Sie sollten Ihren Browser-Cache leeren.",
            "title": "Unerwartete Frontend-Version"
          }
        },
        "pending_updates": "Ausstehende Updates"
      },
      "menu": {
        "about": "Über HACS",
        "clear": "Alles neue als gesehen markieren",
        "custom_repositories": "Benutzerdefinierte Repositories",
        "dismiss": "Alle neuen Repositories ausblenden",
        "documentation": "Dokumentation",
        "open_issue": "Problem melden",
        "reload": "Fenster neu laden"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "AppDaemon App-Entdeckung & Nachverfolgung aktivieren",
              "country": "Nach Ländercode filtern.",
              "debug": "Debug aktivieren.",
              "experimental": "Experimentelle Funktionen aktivieren",
              "netdaemon": "NetDaemon App-Entdeckung & Nachverfolgung aktivieren",
              "not_in_use": "Nicht in Verwendung mit YAML",
              "release_limit": "Anzahl anzuzeigender Releases.",
              "sidepanel_icon": "Sidepanel Symbol",
              "sidepanel_title": "Sidepanel Titel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Diese Integration unterstützt config_flow. Das bedeutet, dass du jetzt in den Integrationsbereich deiner Benutzeroberfläche wechseln kannst, um es zu konfigurieren.",
        "config_flow_title": "UI-Konfiguration unterstützt",
        "integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.",
        "no_restart_required": "Kein Neustart erforderlich",
        "not_loaded": "Nicht geladen",
        "plugin_not_loaded": "Dieses Element wird nicht zu Ihren Lovelace-Ressourcen hinzugefügt.",
        "restart": "Du musst Home Assistant neu starten.",
        "restart_pending": "Neustart ausstehend"
      },
      "repository_card": {
        "dismiss": "Ausblenden",
        "hide": "Verstecken",
        "information": "Information",
        "new_repository": "Neues Repository",
        "not_loaded": "Nicht geladen",
        "open_issue": "Problem melden",
        "open_source": "Quelldateien öffnen",
        "pending_restart": "Ausstehender Neustart",
        "pending_update": "Ausstehende Aktualisierung",
        "reinstall": "Neu installieren",
        "report": "Melden zur Entfernung des Repositorys",
        "update_information": "Aktualisierungsinformationen"
      },
      "repository": {
        "add_to_lovelace": "Zu Lovelace hinzufügen",
        "authors": "Autoren",
        "available": "Verfügbar",
        "back_to": "Zurück zu",
        "changelog": "Änderungsprotokoll",
        "downloads": "Downloads",
        "flag_this": "Melden",
        "frontend_version": "Frontend Version",
        "github_stars": "GitHub Sterne",
        "goto_integrations": "Gehe zu Integrationen",
        "hide": "Verstecken",
        "hide_beta": "Beta verstecken",
        "install": "Installieren",
        "installed": "Installiert",
        "lovelace_copy_example": "Beispiel in die Zwischenablage kopieren",
        "lovelace_instruction": "Zum Hinzufügen zu deinen Lovelace-Einstellungen, benutze Folgendes",
        "lovelace_no_js_type": "Der Typ dieses Elements konnte nicht ermittelt werden. Überprüfen Sie das Repository.",
        "newest": "neueste",
        "note_appdaemon": "du musst es dann noch in die Datei 'apps.yaml' hinzufügen",
        "note_installed": "Wird installiert nach",
        "note_integration": "du musst es dann noch in die Datei 'configuration.yaml' hinzufügen",
        "note_plugin": "du musst es dann noch in deine Lovelace-Einstellungen ('ui-lovelace.yaml' oder im Raw-Konfigurationseditor) hinzufügen",
        "note_plugin_post_107": "Du musst es noch zu deiner Lovelace-Konfiguration hinzufügen ('configuration.yaml' oder der Ressourceneditor '/config/lovelace/resources')",
        "open_issue": "Problem melden",
        "open_plugin": "Element öffnen",
        "reinstall": "Neu installieren",
        "repository": "Repository",
        "restart_home_assistant": "Home Assistant neu starten",
        "show_beta": "Beta anzeigen",
        "uninstall": "Deinstallieren",
        "update_information": "Aktualisierungsinformationen",
        "upgrade": "Aktualisieren"
      },
      "search": {
        "placeholder": "Suche nach Repository"
      },
      "sections": {
        "about": {
          "description": "Informationen zu HACS anzeigen",
          "title": "Über"
        },
        "automation": {
          "description": "Hier finden Sie python_scripts, AppDaemon-Apps und NetDaemon-Apps",
          "title": "Automatisierung"
        },
        "frontend": {
          "description": "Hier finden Sie Themen, individuelle Karten und andere Elemente für Lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Hier finden Sie benutzerdefinierte Integrationen (custom_components)",
          "title": "Integrationen"
        },
        "pending_repository_upgrade": "Sie verwenden Version {installed}, Version {available} ist verfügbar"
      },
      "settings": {
        "add_custom_repository": "BENUTZERDEFINIERTES REPOSITORY HINZUFÜGEN",
        "adding_new_repo": "Hinzufügen eines neuen Repository '{repo}'",
        "adding_new_repo_category": "Mit der Kategorie '{category}'.",
        "bg_task_custom": "Custom Repositorys werden ausgeblendet, während Hintergrundaufgaben ausgeführt werden.",
        "category": "Kategorie",
        "compact_mode": "Kompakter Modus",
        "custom_repositories": "BENUTZERDEFINIERTE REPOSITORIES",
        "delete": "Löschen",
        "display": "Anzeige",
        "grid": "Gitter",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "versteckte Repositories",
        "missing_category": "Du musst eine Kategorie auswählen.",
        "open_repository": "Repository öffnen",
        "reload_data": "Daten neu laden",
        "reload_window": "Fenster neu laden",
        "repository_configuration": "Repository Konfiguration",
        "save": "Speichern",
        "table": "Tabelle",
        "table_view": "Tabellenansicht",
        "unhide": "einblenden",
        "upgrade_all": "Alles aktualisieren"
      },
      "store": {
        "ascending": "Aufsteigend",
        "clear_new": "Alle neuen Repositories als gesehen markieren",
        "descending": "Absteigend",
        "last_updated": "Zuletzt aktualisiert",
        "name": "Name",
        "new_repositories": "Neue Repositories",
        "new_repositories_note": "Hier werden über 10 neue Repositorys angezeigt. Wenn Sie alle als gelesen markieren möchten, klicken Sie auf die 3 Punkte in der oberen rechten Ecke und blenden sie alle aus.",
        "no_repositories": "Keine Repositories",
        "no_repositories_desc1": "Anscheinend sind in diesem Abschnitt noch keine Repositories installiert.",
        "no_repositories_desc2": "Klicken Sie auf das + in der unteren Ecke, um Ihre ersten hinzuzufügen!",
        "no_repositories_found_desc1": "In diesem Abschnitt wurden keine installierten Repositorys gefunden, die mit \"{searchInput}\" übereinstimmen.",
        "no_repositories_found_desc2": "Versuchen Sie, nach etwas anderem zu suchen!",
        "pending_upgrades": "Ausstehende Upgrades",
        "placeholder_search": "Suchbegriff eingeben…",
        "sort": "Sortieren",
        "stars": "Sterne",
        "status": "Status"
      },
      "time": {
        "ago": "vor",
        "day": "Tag",
        "days": "Tage",
        "hour": "Stunde",
        "hours": "Stunden",
        "minute": "Minute",
        "minutes": "Minuten",
        "month": "Monat",
        "months": "Monate",
        "one": "Eins",
        "one_day_ago": "vor einem Tag",
        "one_hour_ago": "vor einer Stunde",
        "one_minute_ago": "vor einer Minute",
        "one_month_ago": "vor einem Monat",
        "one_second_ago": "vor einer Sekunde",
        "one_year_ago": "vor einem Jahr",
        "second": "Sekunde",
        "seconds": "Sekunden",
        "x_days_ago": "vor {x} Tagen",
        "x_hours_ago": "vor {x} Stunden",
        "x_minutes_ago": "vor {x} Minuten",
        "x_months_ago": "vor {x} Monaten",
        "x_seconds_ago": "vor {x} Sekunden",
        "x_years_ago": "vor {x} Jahren",
        "year": "Jahr",
        "years": "Jahre"
      }
    },
    "el": {
      "common": {
        "about": "Σχετικά με",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apps",
        "background_task": "Τρέχει μια διεργασία στο παρασκήνιο, η σελίδα θα ανανεωθεί μόλις αυτό ολοκληρωθεί.",
        "check_log_file": "Ελέγξτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες.",
        "continue": "Να συνεχίσει",
        "disabled": "Απενεργοποιημένο",
        "documentation": "Τεκμηρίωση",
        "hacs_is_disabled": "Το HACS είναι απενεργοποιημένο",
        "installed": "εγκατεστημένο",
        "integration": "Ενσωμάτωση",
        "integrations": "Ενσωματωμένα",
        "manage": "διαχειρίζονται",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "plugin": "Lovelace",
        "plugins": "Πρόσθετα",
        "python_script": "Πρόγραμμα Python",
        "python_scripts": "Προγράμματα Python",
        "repositories": "Αποθετήρια",
        "settings": "ρυθμίσεις",
        "theme": "Θέμα",
        "themes": "Θέματα",
        "version": "Έκδοση"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Μονάχα μία ρύθμιση των παραμέτρων του HACS επιτρέπεται."
        },
        "error": {
          "auth": "Το διακριτικό πρόσβασης δεν είναι σωστό."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το AppDaemon",
              "netdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το NetDaemon",
              "python_script": "Ενεργοποίηση εύρεσης & παρακολούθησης για τα python_scripts",
              "sidepanel_icon": "Εικονίδιο πλαϊνού πάνελ",
              "sidepanel_title": "Τίτλος πλαϊνού πάνελ",
              "theme": "Ενεργοποίηση εύρεσης & παρακολούθησης για τα θεμάτων",
              "token": "Διακριτικό πρόσβασης του GitHub"
            },
            "description": "Εάν χρειαστείτε βοήθεια με τις ρυθμίσεις ανατρέξτε εδώ: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Είστε βέβαιοι ότι θέλετε να προσθέσετε αυτό στους πόρους του Lovelace;",
        "bg_task": "Η ενέργεια είναι απενεργοποιημένη όσο εκτελούνται εργασίες στο παρασκήνιο",
        "cancel": "Ακύρωση",
        "continue": "Είστε βέβαιοι ότι θέλετε να συνεχίσετε;",
        "delete": "Είστε σίγουροι ότι θέλετε να διαγράψετε το '{item}';",
        "delete_installed": "Το '{item}' είναι εγκατεστημένο, πρέπει να το απεγκαταστήσετε πριν να το διαγράψετε.",
        "exist": "{item} υπάρχει ήδη",
        "generic": "Είστε βέβαιοι;",
        "home_assistant_is_restarting": "Περιμένετε, το Home Assistant επανεκκινείται τώρα.",
        "no": "Οχι",
        "no_upgrades": "Δεν υπάρχουν αναβαθμίσεις σε εκκρεμότητα",
        "ok": "Εντάξει",
        "overwrite": "Αυτό θα το αντικαταστήσει.",
        "restart_home_assistant": "Είστε βέβαιοι ότι θέλετε να κάνετε επανεκκίνηση του Home Assistant;",
        "uninstall": "Είστε βέβαιοι ότι θέλετε να απεγκαταστήσετε το '{item}';",
        "upgrade_all": "Αυτό θα αναβαθμίσει όλα αυτά τα αποθετήρια, βεβαιωθείτε ότι έχετε διαβάσει τις σημειώσεις έκδοσης για όλα πριν προχωρήσετε.",
        "yes": "Ναι"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το AppDaemon",
              "country": "Κριτήριο με βάση τον κωδικό χώρας.",
              "debug": "Ενεργοποίηση εντοπισμού σφαλμάτων.",
              "experimental": "Ενεργοποίση πειραματικών λειτουργιών",
              "netdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το NetDaemon",
              "not_in_use": "Δεν χρησιμοποιείται με το YAML",
              "release_limit": "Αριθμός εκδόσεων που να παραθέτονται.",
              "sidepanel_icon": "Εικονίδιο πλαϊνού πάνελ",
              "sidepanel_title": "Τίτλος πλαϊνού πάνελ"
            }
          }
        }
      },
      "repository_banner": {
        "integration_not_loaded": "Αυτή η ενσωμάτωση δεν φορτώθηκε στο Home Assistant.",
        "no_restart_required": "Δεν απαιτείται επανεκκίνηση",
        "not_loaded": "Δεν έχει φορτωθεί",
        "plugin_not_loaded": "Αυτό το πρόσθετο δεν προστέθηκε στους πόρους του Lovelace.",
        "restart": "Πρέπει να κάνετε επανεκκίνηση του Home Assistant.",
        "restart_pending": "Επανεκκίνηση σε εκκρεμότητα"
      },
      "repository": {
        "add_to_lovelace": "Προσθήκη στο Lovelace",
        "authors": "Συγγραφείς",
        "available": "Διαθέσιμο",
        "back_to": "Πίσω στο",
        "changelog": "Σημειώσεις των αλλαγών",
        "downloads": "Λήψεις",
        "flag_this": "Σημείωσε αυτό",
        "frontend_version": "Έκδοση Frontend",
        "github_stars": "GitHub αστέρια",
        "goto_integrations": "Μετάβαση στις ενσωματώσεις",
        "hide": "Απόκρυψη",
        "hide_beta": "Απόκριση του beta",
        "install": "Εγκατάσταση",
        "installed": "Εγκατεστημένο",
        "lovelace_copy_example": "Αντίγραψε το παράδειγμα στο πρόχειρο",
        "lovelace_instruction": "Όταν το προσθέσετε στις ρυθμίσεις του lovelace χρησιμοποιήστε τούτο",
        "lovelace_no_js_type": "Δεν καταφέραμε να προσδιορίσουμε τον τύπο αυτού του προσθέτου, ελέξτε το αποθετήριο.",
        "newest": "νεότερο",
        "note_appdaemon": "εξακολουθεί να χρειάζεται να το προσθέσετε στο αρχείο 'apps.yaml'",
        "note_installed": "Όταν εγκατασταθεί, θα προστεθεί στο",
        "note_integration": "εξακολουθεί να χρειάζεται να το προσθέσετε στο αρχείο 'configuration.yaml'",
        "note_plugin": "εξακολουθεί να χρειάζετε να το προσθέσετε στις ρυθμίσεις του lovelace ('ui-lovelace.yaml' ή μέσω του γραφικού επεξεργαστή των ρυθμίσεων)",
        "open_issue": "Εκκρεμόν ζήτημα",
        "open_plugin": "Ανοιχτό πρόσθετο",
        "reinstall": "Επανεγκατάσταση",
        "repository": "Αποθετήριο",
        "restart_home_assistant": "Επανεκκίνηση του Home Assistant",
        "show_beta": "Εμφάνιση του beta",
        "uninstall": "Απεγκατάσταση",
        "update_information": "Ενημέρωση πληροφοριών",
        "upgrade": "Ενημέρωση"
      },
      "settings": {
        "add_custom_repository": "ΠΡΟΣΘΕΣΤΕ ΕΝΑ ΕΙΔΙΚΟ ΑΠΟΘΕΤΗΡΙΟ",
        "adding_new_repo": "Προσθήκη νέου αποθετηρίου '{repo}'",
        "adding_new_repo_category": "Με κατηγορία '{category}'.",
        "category": "Κατηγορία",
        "compact_mode": "Συμπαγής λειτουργία",
        "custom_repositories": "ΕΙΔΙΚΑ ΑΠΟΘΕΤΗΡΙΑ",
        "delete": "Διαγραφή",
        "display": "Εμφάνιση",
        "grid": "Πλέγμα",
        "hacs_repo": "Αποθετήριο του HACS",
        "hidden_repositories": "κρυφά αποθετήρια",
        "missing_category": "Πρέπει να επιλέξετε μια κατηγορία",
        "open_repository": "Ανοίξτε το αποθετήριο",
        "reload_data": "Επαναφόρτωση δεδομένων",
        "reload_window": "Επαναφόρτωση του παραθύρου",
        "repository_configuration": "Διαμόρφωση αποθετηρίου",
        "save": "Αποθήκευση",
        "table": "Πίνακας",
        "table_view": "Προβολή πίνακα",
        "unhide": "αποκρύψω",
        "upgrade_all": "Αναβάθμιση όλων"
      },
      "store": {
        "ascending": "αύξουσα",
        "clear_new": "Απαλοιφή όλων των νέων αποθετηρίων",
        "descending": "φθίνουσα",
        "last_updated": "Τελευταία ενημέρωση",
        "name": "Ονομα",
        "new_repositories": "Νέα αποθετήρια",
        "pending_upgrades": "Εκκρεμείς αναβαθμίσεις",
        "placeholder_search": "Παρακαλώ πληκτρολογήστε έναν όρο προς αναζήτηση...",
        "sort": "είδος",
        "stars": "Αστέρια",
        "status": "Κατάσταση"
      },
      "time": {
        "ago": "πριν",
        "day": "ημέρα",
        "days": "ημέρες",
        "hour": "ώρα",
        "hours": "ώρες",
        "minute": "λεπτό",
        "minutes": "λεπτά",
        "month": "μήνας",
        "months": "μήνες",
        "one": "Ένα",
        "one_day_ago": "πριν από μία ημέρα",
        "one_hour_ago": "πριν από μια ώρα",
        "one_minute_ago": "πριν από ένα λεπτό",
        "one_month_ago": "πριν από ένα μήνα",
        "one_second_ago": "πριν από ένα δευτερόλεπτο",
        "one_year_ago": "πριν από ένα χρόνο",
        "second": "δευτερόλεπτο",
        "seconds": "δευτερόλεπτα",
        "x_days_ago": "{x} ημέρες πριν",
        "x_hours_ago": "{x} ώρες πριν",
        "x_minutes_ago": "{x} λεπτά πριν",
        "x_months_ago": "{x} μήνες πριν",
        "x_seconds_ago": "{x} δευτερόλεπτα πριν",
        "x_years_ago": "{x} χρόνια πριν",
        "year": "έτος",
        "years": "χρόνια"
      }
    },
    "en": {
      "common": {
        "about": "About",
        "add": "add",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apps",
        "appdaemon_plural": "AppDaemon Apps",
        "background_task": "Background task running, this page will reload when it's done.",
        "cancel": "Cancel",
        "check_log_file": "Check your log file for more details.",
        "continue": "Continue",
        "disabled": "Disabled",
        "documentation": "Documentation",
        "element": "element",
        "hacs_is_disabled": "HACS is disabled",
        "ignore": "Ignore",
        "install": "Install",
        "installed": "installed",
        "integration": "Integration",
        "integration_plural": "Integrations",
        "integrations": "Integrations",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace element",
        "lovelace_elements": "Lovelace elements",
        "manage": "manage",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "netdaemon_plural": "NetDaemon Apps",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace elements",
        "plugins": "Lovelace elements",
        "python_script": "Python Script",
        "python_script_plural": "Python Scripts",
        "python_scripts": "Python Scripts",
        "reload": "Reload",
        "repositories": "Repositories",
        "repository": "Repository",
        "settings": "settings",
        "theme": "Theme",
        "theme_plural": "Themes",
        "themes": "Themes",
        "uninstall": "Uninstall",
        "update": "Update",
        "version": "Version"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Only a single configuration of HACS is allowed."
        },
        "error": {
          "auth": "Personal Access Token is not correct."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Enable AppDaemon apps discovery & tracking",
              "netdaemon": "Enable NetDaemon apps discovery & tracking",
              "python_script": "Enable python_scripts discovery & tracking",
              "sidepanel_icon": "Side panel icon",
              "sidepanel_title": "Side panel title",
              "theme": "Enable Themes discovery & tracking",
              "token": "GitHub Personal Access Token"
            },
            "description": "If you need help with the configuration have a look here: https://hacs.xyz/docs/configuration/start/",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Are you sure you want to add this to your Lovelace resources?",
        "bg_task": "Action is disabled while background tasks is running.",
        "cancel": "Cancel",
        "continue": "Are you sure you want to continue?",
        "delete": "Are you sure you want to delete \"{item}\"?",
        "delete_installed": "'{item}' is installed, you need to uninstall it before you can delete it.",
        "exist": "{item} already exists",
        "generic": "Are you sure?",
        "home_assistant_is_restarting": "Hold on, Home Assistant is now restarting.",
        "home_assistant_version_not_correct": "You are running Home Assistant version '{haversion}', but this repository requires minimum version '{minversion}' to be installed.",
        "no": "No",
        "no_upgrades": "No upgrades pending",
        "ok": "OK",
        "overwrite": "Doing this will overwrite it.",
        "reload_data": "This reloads the data of all repositories HACS knows about, this will take some time to finish.",
        "restart_home_assistant": "Are you sure you want to restart Home Assistant?",
        "uninstall": "Are you sure you want to uninstall \"{item}\"?",
        "upgrade_all": "This will upgrade all of these repositories, make sure that you have read the release notes for all of them before proceeding.",
        "yes": "Yes"
      },
      "dialog_about": {
        "frontend_version": "Frontend version",
        "installed_repositories": "Installed repositories",
        "integration_version": "Integration version",
        "useful_links": "Useful links"
      },
      "dialog_add_repo": {
        "limit": "Only the first 100 repositories are shown, use the search to filter what you need",
        "no_match": "No repositories found matching your filter",
        "sort_by": "Sort by",
        "title": "Add repository"
      },
      "dialog_custom_repositories": {
        "category": "Category",
        "no_category": "Missing category",
        "no_repository": "Missing repository",
        "title": "Custom repositories",
        "url_placeholder": "Add custom repository URL"
      },
      "dialog_info": {
        "author": "Author",
        "downloads": "Downloads",
        "install": "Install this repository in HACS",
        "loading": "Loading information...",
        "no_info": "The developer has not provided any more information for this repository",
        "open_issues": "Open issues",
        "open_repo": "Open repository",
        "stars": "Stars",
        "version_installed": "Version installed"
      },
      "dialog_install": {
        "restart": "Remember that you need to restart Home Assistant before changes to integrations (custom_components) are applied.",
        "select_version": "Select version",
        "show_beta": "Show beta versions",
        "type": "Type",
        "url": "URL"
      },
      "dialog_removed": {
        "link": "External link to more information",
        "name": "Repository name",
        "reason": "Removal reason",
        "type": "Removal type"
      },
      "dialog_update": {
        "available_version": "Available version",
        "changelog": "Changelog",
        "installed_version": "Installed version",
        "releasenotes": "Releasenotes for {release}",
        "title": "Update pending"
      },
      "dialog": {
        "reload": {
          "confirm": "Do you want to do that now?",
          "description": "You need to clear your browser cache when changing Lovelace resources."
        }
      },
      "entry": {
        "information": "Information",
        "intro": "Updates and important messages will show here if there are any",
        "messages": {
          "disabled": {
            "content": "Check your log file for more details",
            "title": "HACS is disabled"
          },
          "has_pending_tasks": {
            "content": "Some repositories might not show untill this is completed",
            "title": "Background tasks pending"
          },
          "removed": "Removed repository '{repository}'",
          "resources": {
            "content": "You have {number} Lovelace elements that are not loaded properly in Lovelace.",
            "title": "Not loaded in Lovelace"
          },
          "restart": {
            "content": "You have {number} integrations that requires a restart of Home Assistant, you can do that from the 'Server Controls' section under the configuration part of Home Assistant UI.",
            "title": "Pending restart"
          },
          "setup": {
            "content": "HACS is setting up, during this time some information might be missing or incorrect",
            "title": "HACS is setting up"
          },
          "startup": {
            "content": "HACS is starting up, during this time some information might be missing or incorrect",
            "title": "HACS is starting up"
          },
          "waiting": {
            "content": "HACS is waiting for Home Assistant to finish startup before starting startup tasks",
            "title": "HACS is waiting"
          },
          "wrong_frontend_installed": {
            "content": "You have {running} of the HACS frontend installed, but version {expected} was expected, if this you see this message Home Assistant was not able to install the new version, try restarting Home Assistant.",
            "title": "Unexpected frontend version"
          },
          "wrong_frontend_loaded": {
            "content": "You are running version {running} of the HACS frontend, but version {expected} was expected, you should clear your browser cache.",
            "title": "Unexpected frontend version"
          }
        },
        "pending_updates": "Pending updates"
      },
      "menu": {
        "about": "About HACS",
        "clear": "Clear all new",
        "custom_repositories": "Custom repositories",
        "dismiss": "Dismiss all new repositories",
        "documentation": "Documentation",
        "open_issue": "Open issue",
        "reload": "Reload window"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Enable AppDaemon apps discovery & tracking",
              "country": "Filter with country code.",
              "debug": "Enable debug.",
              "experimental": "Enable experimental features",
              "netdaemon": "Enable NetDaemon apps discovery & tracking",
              "not_in_use": "Not in use with YAML",
              "release_limit": "Number of releases to show.",
              "sidepanel_icon": "Side panel icon",
              "sidepanel_title": "Side panel title"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "This integration supports config_flow, that means that you now can go to the integration section of your UI to configure it.",
        "config_flow_title": "UI Configuration supported",
        "integration_not_loaded": "This integration is not loaded in Home Assistant.",
        "no_restart_required": "No restart required",
        "not_loaded": "Not loaded",
        "plugin_not_loaded": "This plugin is not added to your Lovelace resources.",
        "restart": "You need to restart Home Assistant.",
        "restart_pending": "Restart pending"
      },
      "repository_card": {
        "dismiss": "dismiss",
        "hide": "Hide",
        "information": "Information",
        "new_repository": "New repository",
        "not_loaded": "Not loaded",
        "open_issue": "Open issue",
        "open_source": "Open source",
        "pending_restart": "Pending restart",
        "pending_update": "Pending update",
        "reinstall": "Reinstall",
        "report": "Report for removal",
        "update_information": "Update information"
      },
      "repository": {
        "add_to_lovelace": "Add to Lovelace",
        "authors": "Authors",
        "available": "Available",
        "back_to": "Back to",
        "changelog": "Change log",
        "downloads": "Downloads",
        "flag_this": "Flag this",
        "frontend_version": "Frontend version",
        "github_stars": "GitHub stars",
        "goto_integrations": "Go to integrations",
        "hide": "Hide",
        "hide_beta": "Hide beta",
        "install": "Install",
        "installed": "Installed",
        "lovelace_copy_example": "Copy the example to your clipboard",
        "lovelace_instruction": "When you add this to your lovelace configuration use this",
        "lovelace_no_js_type": "Could not determine the type of this plugin, check the repository.",
        "newest": "newest",
        "note_appdaemon": "you still need to add it to your 'apps.yaml' file",
        "note_installed": "When installed, this will be located in",
        "note_integration": "you still need to add it to your 'configuration.yaml' file",
        "note_plugin": "you still need to add it to your lovelace configuration ('ui-lovelace.yaml' or the raw UI config editor)",
        "note_plugin_post_107": "you still need to add it to your lovelace configuration ('configuration.yaml' or the resource editor '/config/lovelace/resources')",
        "open_issue": "Open issue",
        "open_plugin": "Open element",
        "reinstall": "Reinstall",
        "repository": "Repository",
        "restart_home_assistant": "Restart Home Assistant",
        "show_beta": "Show beta",
        "uninstall": "Uninstall",
        "update_information": "Update information",
        "upgrade": "Update"
      },
      "search": {
        "installed": "Search for installed repositories",
        "installed_new": "Search for installed or new repositories",
        "placeholder": "Search for repository"
      },
      "sections": {
        "about": {
          "description": "Show information about HACS",
          "title": "About"
        },
        "addon": {
          "description": "There are no addons in HACS, but you can click here to get to the supervisor",
          "title": "Add-ons"
        },
        "automation": {
          "description": "This is where you find python_scripts, AppDaemon apps and NetDaemon apps",
          "title": "Automation"
        },
        "frontend": {
          "description": "This is where you find themes, custom cards and other elements for lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "This is where you find custom integrations (custom_components)",
          "title": "Integrations"
        },
        "pending_repository_upgrade": "You are running version {installed}, version {available} is available"
      },
      "settings": {
        "add_custom_repository": "ADD CUSTOM REPOSITORY",
        "adding_new_repo": "Adding new repository '{repo}'",
        "adding_new_repo_category": "With category '{category}'.",
        "bg_task_custom": "Custom repositories are hidden while background tasks is running.",
        "category": "Category",
        "compact_mode": "Compact mode",
        "custom_repositories": "CUSTOM REPOSITORIES",
        "delete": "Delete",
        "display": "Display",
        "grid": "Grid",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "hidden repositories",
        "missing_category": "You need to select a category",
        "open_repository": "Open repository",
        "reload_data": "Reload data",
        "reload_window": "Reload window",
        "repository_configuration": "Repository configuration",
        "save": "Save",
        "table": "Table",
        "table_view": "Table view",
        "unhide": "unhide",
        "upgrade_all": "Upgrade all"
      },
      "store": {
        "ascending": "ascending",
        "clear_new": "Clear all new repositories",
        "descending": "descending",
        "last_updated": "Last updated",
        "name": "Name",
        "new_repositories": "New Repositories",
        "new_repositories_note": "You have over 10 new repositories showing here, if you want to clear them all click the 3 dots in the top right corner and dismiss all of them.",
        "no_repositories": "No repositories",
        "no_repositories_desc1": "It seems like you don't have any repositories installed in this section yet.",
        "no_repositories_desc2": "Click on the + in the bottom corner to add your first!",
        "no_repositories_found_desc1": "No installed repositories matching \"{searchInput}\" found in this section.",
        "no_repositories_found_desc2": "Try searching for something else!",
        "pending_upgrades": "Pending upgrades",
        "placeholder_search": "Please enter a search term...",
        "sort": "sort",
        "stars": "Stars",
        "status": "Status"
      },
      "time": {
        "ago": "ago",
        "day": "day",
        "days": "days",
        "hour": "hour",
        "hours": "hours",
        "minute": "minute",
        "minutes": "minutes",
        "month": "month",
        "months": "months",
        "one": "One",
        "one_day_ago": "one day ago",
        "one_hour_ago": "one hour ago",
        "one_minute_ago": "one minute ago",
        "one_month_ago": "one month ago",
        "one_second_ago": "one second ago",
        "one_year_ago": "one year ago",
        "second": "second",
        "seconds": "seconds",
        "x_days_ago": "{x} days ago",
        "x_hours_ago": "{x} hours ago",
        "x_minutes_ago": "{x} minutes ago",
        "x_months_ago": "{x} months ago",
        "x_seconds_ago": "{x} seconds ago",
        "x_years_ago": "{x} years ago",
        "year": "year",
        "years": "years"
      }
    },
    "es": {
      "common": {
        "about": "Acerca de",
        "add": "añadir",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apps",
        "appdaemon_plural": "AppDaemon Apps",
        "background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.",
        "check_log_file": "Compruebe el archivo de registro para obtener más detalles.",
        "continue": "Continuar",
        "disabled": "Deshabilitado",
        "documentation": "Documentación",
        "element": "elemento",
        "hacs_is_disabled": "HACS está deshabilitado",
        "ignore": "Ignorar",
        "install": "Instalar",
        "installed": "instalado",
        "integration": "Integración",
        "integration_plural": "Integraciones",
        "integrations": "Integraciones",
        "lovelace": "Lovelace",
        "lovelace_element": "Elemento de Lovelace",
        "lovelace_elements": "Elementos de Lovelace",
        "manage": "Administrar",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "netdaemon_plural": "Aplicaciones NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Elementos de Lovelace",
        "plugins": "Elementos de Lovelace",
        "python_script": "Python Script",
        "python_script_plural": "Python Scripts",
        "python_scripts": "Python Scripts",
        "repositories": "Repositorios",
        "repository": "Repositorio",
        "settings": "configuraciones",
        "theme": "Tema",
        "theme_plural": "Temas",
        "themes": "Temas",
        "uninstall": "Desinstalar",
        "update": "Actualizar",
        "version": "Versión"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Sólo se permite una única configuración de HACS."
        },
        "error": {
          "auth": "El token de acceso personal no es correcto."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de AppDaemon",
              "netdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de NetDaemon",
              "python_script": "Habilitar el descubrimiento y seguimiento en python_scripts",
              "sidepanel_icon": "Ícono del panel lateral",
              "sidepanel_title": "Título del panel lateral",
              "theme": "Habilitar el descubrimiento y seguimiento de temas",
              "token": "Token de acceso personal de GitHub"
            },
            "description": "Si necesitas ayuda con la configuración, visita la siguiente pagina: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "¿Está seguro de que desea agregar esto a sus recursos de Lovelace?",
        "bg_task": "La acción está deshabilitada mientras se ejecutan tareas en segundo plano.",
        "cancel": "Cancelar",
        "continue": "Estás seguro de que quieres continuar?",
        "delete": "¿Seguro que quieres eliminar '{item}'?",
        "delete_installed": "'{item}' está instalado, debe desinstalarlo antes de poder eliminarlo.",
        "exist": "{item} ya existe",
        "generic": "¿Estás seguro?",
        "home_assistant_is_restarting": "Espera, Home Assistant se está reiniciando.",
        "home_assistant_version_not_correct": "Está ejecutando la versión '{haversion}' de Home Assistant, pero este repositorio requiere la instalación de la versión '{minversion}' mínima.",
        "no": "No",
        "no_upgrades": "No hay actualizaciones pendientes",
        "ok": "OK",
        "overwrite": "Si haces esto, se sobrescribirá.",
        "reload_data": "Esto recarga los datos de todos los repositorios que HACS conoce, esto tardará algún tiempo en finalizar.",
        "restart_home_assistant": "¿Está seguro de que desea reiniciar Home Assistant?",
        "uninstall": "¿Está seguro de que deseas desinstalar '{item}'?",
        "upgrade_all": "Esto actualizará todos estos repositorios, asegúrese de que ha leído las notas de la versión de todos ellos antes de continuar.",
        "yes": "Si"
      },
      "dialog_about": {
        "frontend_version": "Versión del front-end",
        "installed_repositories": "Repositorios instalados",
        "integration_version": "Versión de la integración",
        "useful_links": "Enlaces útiles"
      },
      "dialog_add_repo": {
        "limit": "Sólo se muestran los primeros 100 repositorios, utilice la búsqueda para filtrar lo que necesita",
        "no_match": "No se han encontrado repositorios que coincidan con el filtro",
        "sort_by": "Ordenar por",
        "title": "Añadir repositorio"
      },
      "dialog_custom_repositories": {
        "category": "Categoría",
        "no_category": "Categoría que falta",
        "no_repository": "Falta el repositorio",
        "title": "Repositorios personalizados",
        "url_placeholder": "Agrega la URL del repositorio personalizado que deseas añadir"
      },
      "dialog_info": {
        "author": "Autor",
        "downloads": "Descargas",
        "install": "Instalar este repositorio en HACS",
        "loading": "Cargando información ...",
        "no_info": "El desarrollador no ha proporcionado más información para este repositorio",
        "open_issues": "Abrir incidencias",
        "open_repo": "Abrir repositorio",
        "stars": "Estrellas",
        "version_installed": "Versión instalada"
      },
      "dialog_install": {
        "restart": "Recuerde que debe reiniciar Home Assistant para que se apliquen los cambios en las integraciones (custom_components).",
        "select_version": "Seleccione la versión",
        "show_beta": "Mostrar versiones beta",
        "type": "Tipo",
        "url": "URL"
      },
      "dialog_removed": {
        "link": "Enlace externo para más información",
        "name": "Nombre del repositorio",
        "reason": "Motivo de la eliminación",
        "type": "Tipo de eliminación"
      },
      "dialog_update": {
        "available_version": "Versión disponible",
        "changelog": "Registro de cambios",
        "installed_version": "Versión instalada",
        "releasenotes": "Notas de la versión para {release}",
        "title": "Actualización pendiente"
      },
      "entry": {
        "information": "Información",
        "intro": "Las actualizaciones y los mensajes importantes se mostrarán aquí si hay alguno que mostrar",
        "messages": {
          "disabled": {
            "content": "Compruebe el archivo de registro para obtener más detalles",
            "title": "HACS está deshabilitado"
          },
          "has_pending_tasks": {
            "content": "Es posible que algunos repositorios no se muestren hasta que esto se complete",
            "title": "Tareas en segundo plano pendientes"
          },
          "removed": "Repositorio '{repository}' eliminado",
          "resources": {
            "content": "Tienes {number} elementos de Lovelace que no están cargados correctamente en Lovelace.",
            "title": "No está cargado en Lovelace"
          },
          "restart": {
            "content": "Tienes {number} integraciones que requieren un reinicio de Home Assistant, puedes hacerlo desde la sección 'Controles del Servidor' en la parte de configuración de la UI de Home Assistant.",
            "title": "Reinicio pendiente"
          },
          "startup": {
            "content": "HACS se está iniciando, durante este tiempo alguna información podría faltar o ser incorrecta",
            "title": "HACS se está iniciando"
          },
          "wrong_frontend_installed": {
            "content": "Tienes instalada la versión {running} de la interfaz de HACS, pero se esperaba la versión {expected}, si ves este mensaje, Home Assistant no pudo instalar la nueva versión, intenta reiniciar Home Assistant.",
            "title": "Versión inesperada de la interfaz"
          },
          "wrong_frontend_loaded": {
            "content": "Estás ejecutando la versión {running} de la interfaz HACS, pero se esperaba la versión {expected} , deberías de limpiar la memoria caché del navegador.",
            "title": "Versión inesperada de la interfaz"
          }
        },
        "pending_updates": "Actualizaciones pendientes"
      },
      "menu": {
        "about": "Acerca de HACS",
        "clear": "Borrar todo lo nuevo",
        "custom_repositories": "Repositorios personalizados",
        "dismiss": "Descartar todos los repositorios nuevos",
        "documentation": "Documentación",
        "open_issue": "Abrir issue",
        "reload": "Recargar la ventana"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de AppDaemon",
              "country": "Filtrar por el código de país.",
              "debug": "Habilitar depuración.",
              "experimental": "Habilitar funciones experimentales",
              "netdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de NetDaemon",
              "not_in_use": "No usarse con YAML",
              "release_limit": "Número de versiones a mostrar.",
              "sidepanel_icon": "Icono del panel lateral",
              "sidepanel_title": "Título del panel lateral"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Esta integración soporta config_flow, lo que significa que ahora puede ir a la sección de integración de su UI para configurarlo.",
        "config_flow_title": "Configuración de UI soportada",
        "integration_not_loaded": "Esta integración no se carga en Home Assistant.",
        "no_restart_required": "No es necesario reiniciar",
        "not_loaded": "No está cargado",
        "plugin_not_loaded": "Este plugin aun no se ha añadido a sus recursos de Lovelace.",
        "restart": "Es necesario reiniciar Home Assistant.",
        "restart_pending": "Reinicio pendiente"
      },
      "repository_card": {
        "dismiss": "descartar",
        "hide": "Ocultar",
        "information": "Información",
        "new_repository": "Nuevo repositorio",
        "not_loaded": "Sin cargar",
        "open_issue": "Abrir issue",
        "open_source": "Código abierto",
        "pending_restart": "Pendiente de reinicio",
        "pending_update": "Actualización pendiente",
        "reinstall": "Reinstalar",
        "report": "Informe para la eliminación",
        "update_information": "Actualizar información"
      },
      "repository": {
        "add_to_lovelace": "Añadir a Lovelace",
        "authors": "Autores",
        "available": "Disponible",
        "back_to": "Volver a",
        "changelog": "Registro de cambios",
        "downloads": "Descargas",
        "flag_this": "Marcar esto",
        "frontend_version": "Versión del frontend",
        "github_stars": "Estrellas de GitHub",
        "goto_integrations": "Ir a integraciones",
        "hide": "Ocultar",
        "hide_beta": "Ocultar beta",
        "install": "Instalar",
        "installed": "Instalado",
        "lovelace_copy_example": "Copiar el ejemplo al clipboard",
        "lovelace_instruction": "Agregue lo siguiente en su configuración de lovelace",
        "lovelace_no_js_type": "No se pudo determinar el tipo de elemento, revise el repositorio.",
        "newest": "más nuevo",
        "note_appdaemon": "deberá agregar esto a su archivo 'apps.yaml'",
        "note_installed": "Cuando esté instalado, se ubicará en",
        "note_integration": "deberá agregar esto a su archivo 'configuration.yaml'",
        "note_plugin": "deberá agregar esto a su configuración de lovelace ('ui-lovelace.yaml' o en el editor UI de lovelace)",
        "note_plugin_post_107": "todavía necesita agregarlo a su configuración de lovelace ('configuration.yaml' o al editor de recursos '/config/lovelace/resources')",
        "open_issue": "Abrir issue",
        "open_plugin": "Abrir elemento",
        "reinstall": "Reinstalar",
        "repository": "Repositorio",
        "restart_home_assistant": "Reiniciar Home Assistant",
        "show_beta": "Mostrar beta",
        "uninstall": "Desinstalar",
        "update_information": "Actualizar información",
        "upgrade": "Actualizar"
      },
      "search": {
        "installed": "Buscar repositorios instalados",
        "installed_new": "Buscar repositorios instalados o nuevos",
        "placeholder": "Buscar repositorio"
      },
      "sections": {
        "about": {
          "description": "Mostrar información sobre HACS",
          "title": "Acerca de"
        },
        "automation": {
          "description": "Aquí es donde se encuentran python_scripts, aplicaciones AppDaemon y aplicaciones NetDaemon",
          "title": "Automatización"
        },
        "frontend": {
          "description": "Aquí es donde encontrarás temas, tarjetas personalizadas y otros elementos para lovelace",
          "title": "Interfaz"
        },
        "integrations": {
          "description": "Aquí es donde se encuentran las integraciones personalizadas (custom_components)",
          "title": "Integraciones"
        },
        "pending_repository_upgrade": "Está ejecutando la versión {installed}, la versión {available} está disponible"
      },
      "settings": {
        "add_custom_repository": "AGREGAR REPOSITORIO PERSONALIZADO",
        "adding_new_repo": "Añadiendo un nuevo repositorio '{repo}'.",
        "adding_new_repo_category": "Con la categoría '{category}'.",
        "bg_task_custom": "Los repositorios personalizados están ocultos mientras se ejecutan las tareas en segundo plano.",
        "category": "Categoría",
        "compact_mode": "Modo compacto",
        "custom_repositories": "REPOSITORIOS PERSONALIZADOS",
        "delete": "Eliminar",
        "display": "Mostrar",
        "grid": "Cuadrícula",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "repositorios ocultos",
        "missing_category": "Es necesario seleccionar una categoría",
        "open_repository": "Abrir repositorio",
        "reload_data": "Recargar datos",
        "reload_window": "Recargar ventana",
        "repository_configuration": "Configuración del repositorio",
        "save": "Guardar",
        "table": "Tabla",
        "table_view": "Vista de la tabla",
        "unhide": "mostrar",
        "upgrade_all": "Actualizar todo"
      },
      "store": {
        "ascending": "ascendente",
        "clear_new": "Eliminar la lista los nuevos repositorios",
        "descending": "descendente",
        "last_updated": "Última actualización",
        "name": "Nombre",
        "new_repositories": "Nuevos Repositorios",
        "new_repositories_note": "Tienes más de 10 nuevos repositorios mostrados aquí, si quieres borrarlos todos haz clic en los 3 puntos de la esquina superior derecha y deséchalos todos.",
        "no_repositories": "Sin repositorios",
        "no_repositories_desc1": "Parece que todavía no tiene ningún repositorio instalado en esta sección.",
        "no_repositories_desc2": "Haga clic en el + de la esquina inferior derecha para agregar su primer repositorio!",
        "no_repositories_found_desc1": "No se ha encontrado ningún repositorio instalado que coincida con el valor de \"{searchInput}\" en esta sección.",
        "no_repositories_found_desc2": "¡Intenta buscar otra cosa!",
        "pending_upgrades": "Actualizaciones pendientes",
        "placeholder_search": "Por favor escriba una palabra clave de búsqueda...",
        "sort": "ordenar",
        "stars": "Estrellas",
        "status": "Estado"
      },
      "time": {
        "ago": "hace",
        "day": "dia",
        "days": "dias",
        "hour": "hora",
        "hours": "horas",
        "minute": "minuto",
        "minutes": "minutos",
        "month": "mes",
        "months": "meses",
        "one": "Uno",
        "one_day_ago": "hace un día",
        "one_hour_ago": "hace una hora",
        "one_minute_ago": "hace un minuto",
        "one_month_ago": "hace un mes",
        "one_second_ago": "hace un segundo",
        "one_year_ago": "hace un año",
        "second": "segundo",
        "seconds": "segundos",
        "x_days_ago": "hace {x} dias",
        "x_hours_ago": "hace {x} horas",
        "x_minutes_ago": "hace {x} minutos",
        "x_months_ago": "hace {x} meses",
        "x_seconds_ago": "hace {x} segundos",
        "x_years_ago": "hace {x} años",
        "year": "año",
        "years": "años"
      }
    },
    "fi": {
      "common": {
        "add": "Lisää",
        "appdaemon_plural": "AppDaemon-sovellukset",
        "element": "elementti",
        "hacs_is_disabled": "HACS on poistettu käytöstä",
        "install": "Asenna",
        "integration_plural": "Integraatiot",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-elementti",
        "lovelace_elements": "Lovelace-elementit",
        "manage": "hallinnoi",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon-sovellukset",
        "netdaemon_plural": "NetDaemon-sovellukset",
        "plugin_plural": "Lovelace-elementit",
        "python_script_plural": "Python-skriptit",
        "theme_plural": "Teemat",
        "uninstall": "Poista",
        "update": "Päivitä"
      },
      "config": {
        "step": {
          "user": {
            "data": {
              "netdaemon": "Ota NetDaemon-sovellusten etsintä ja seuranta käyttöön"
            }
          }
        }
      },
      "dialog_about": {
        "useful_links": "Hyödyllisiä linkkejä"
      },
      "dialog_add_repo": {
        "sort_by": "Järjestä",
        "title": "Lisää repo"
      },
      "dialog_custom_repositories": {
        "category": "Kategoria"
      },
      "dialog_info": {
        "downloads": "Lataukset",
        "open_issues": "Avoimet ongelmat",
        "stars": "Tähdet",
        "version_installed": "Asennettu versio"
      },
      "dialog_install": {
        "select_version": "Valitse versio",
        "show_beta": "Näytä beetaversiot",
        "type": "Tyyppi",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Saatavilla oleva versio",
        "changelog": "Muutosloki",
        "installed_version": "Asennettu versio"
      },
      "entry": {
        "messages": {
          "disabled": {
            "content": "Tarkista lokitiedostosi saadaksesi lisätietoja",
            "title": "HACS on poistettu käytöstä"
          },
          "startup": {
            "title": "HACS käynnistyy"
          }
        },
        "pending_updates": "Päivityksiä saatavilla"
      },
      "menu": {
        "about": "Tietoja HACS:stä",
        "clear": "Tyhjennä kaikki uudet",
        "custom_repositories": "Mukautetut repot",
        "dismiss": "Hylkää kaikki uudet repot",
        "documentation": "Dokumentointi",
        "open_issue": "Avoin ongelma",
        "reload": "Lataa ikkuna uudelleen"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "netdaemon": "Ota NetDaemon-sovellusten etsintä ja seuranta käyttöön",
              "not_in_use": "Ei käytössä YAML:n kanssa",
              "sidepanel_icon": "Sivupaneelin kuvake"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow_title": "Käyttöliittymän määrityksiä tuetaan"
      },
      "repository_card": {
        "dismiss": "Hylkää",
        "hide": "Piilota",
        "information": "Tiedot",
        "open_issue": "Avoin ongelma",
        "open_source": "Avoin lähdekoodi",
        "reinstall": "Asenna uudelleen",
        "update_information": "Päivitä tiedot"
      },
      "repository": {
        "github_stars": "GitHub-tähdet"
      },
      "search": {
        "placeholder": "Etsi repoa"
      },
      "sections": {
        "about": {
          "title": "Tietoja"
        },
        "automation": {
          "title": "Automaatio"
        },
        "frontend": {
          "title": "Frontend"
        },
        "integrations": {
          "description": "Täältä löydät mukautetut integraatiot (custom_components)",
          "title": "integraatiot"
        }
      },
      "time": {
        "one_day_ago": "yksi päivä sitten",
        "one_hour_ago": "tunti sitten",
        "one_minute_ago": "minuutti sitten",
        "one_month_ago": "kuukausi sitten",
        "one_second_ago": "sekunti sitten",
        "one_year_ago": "vuosi sitten",
        "x_days_ago": "{x} päivää sitten",
        "x_hours_ago": "{x} tuntia sitten",
        "x_minutes_ago": "{x} minuuttia sitten",
        "x_months_ago": "{x} kuukautta sitten",
        "x_seconds_ago": "{x} sekuntia sitten",
        "x_years_ago": "{x} vuotta sitten"
      }
    },
    "fr": {
      "common": {
        "about": "À propos de",
        "add": "ajouter",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Applications AppDaemon",
        "appdaemon_plural": "AppDaemon Apps",
        "background_task": "Tache de fond en cours, cette page se rechargera une fois terminée",
        "check_log_file": "Consultez votre fichier journal pour plus de détails.",
        "continue": "Continuer",
        "disabled": "Désactivé",
        "documentation": "Documentation",
        "element": "élément",
        "hacs_is_disabled": "HACS est désactivé",
        "install": "Installer",
        "installed": "installés",
        "integration": "Intégration",
        "integration_plural": "Intégrations",
        "integrations": "Intégrations",
        "lovelace": "Lovelace",
        "lovelace_element": "Élément Lovelace",
        "lovelace_elements": "Éléments Lovelace",
        "manage": "gérer",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Applications NetDaemon",
        "netdaemon_plural": "NetDaemon Apps",
        "plugin": "Lovelace",
        "plugin_plural": "Éléments Lovelace",
        "plugins": "Éléments Lovelace",
        "python_script": "Script Python",
        "python_script_plural": "Scripts Python",
        "python_scripts": "Scripts Python",
        "repositories": "Dépôts",
        "repository": "Dépôt",
        "settings": "paramètres",
        "theme": "Thème",
        "theme_plural": "Thèmes",
        "themes": "Thèmes",
        "uninstall": "Désinstaller",
        "update": "Mise à jour",
        "version": "Version"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Une seule configuration de HACS est autorisée."
        },
        "error": {
          "auth": "Le jeton personnel d'accès est invalide."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Activer la découverte et le suivi des applications AppDaemon",
              "netdaemon": "Activer la découverte et le suivi des applications NetDaemon",
              "python_script": "Activer la découverte et le suivi des scripts python",
              "sidepanel_icon": "Icône de la barre latérale",
              "sidepanel_title": "Titre de la barre latérale",
              "theme": "Activer la découverte et le suivi des thèmes",
              "token": "Jeton d'accès personnel GitHub"
            },
            "description": "Si vous avez besoin d'aide pour la configuration, jetez un œil ici: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Êtes-vous sûr de vouloir l'ajouter à vos ressources Lovelace ?",
        "bg_task": "L'action est désactivée pendant l'exécution de tâches en arrière-plan.",
        "cancel": "Annuler",
        "continue": "Es-tu sur de vouloir continuer?",
        "delete": "Êtes-vous sûr de vouloir supprimer '{item}'?",
        "delete_installed": "'{item}' est installé, vous devez le désinstaller avant de pouvoir le supprimer.",
        "exist": "{item} existe déjà",
        "generic": "Êtes-vous sûr?",
        "home_assistant_is_restarting": "Attendez, Home Assistant redémarre maintenant.",
        "home_assistant_version_not_correct": "Vous utilisez la version '{haversion}' de Home Assistant, mais ce référentiel nécessite l'installation de la version minimale '{minversion}'.",
        "no": "Non",
        "no_upgrades": "Aucune mise à niveau en attente",
        "ok": "OK",
        "overwrite": "En faisant cela, cela l'écrasera.",
        "reload_data": "Cela recharge les données de tous les dépôts dont HACS a connaissance, cela prendra un certain temps à terminer.",
        "restart_home_assistant": "Voulez-vous vraiment redémarrer Home Assistant ?",
        "uninstall": "Êtes-vous sûr de vouloir désinstaller '{item}'?",
        "upgrade_all": "Cela mettra à niveau tous ces dépôts, assurez-vous d'avoir lu les notes de publication pour chacun d'entre eux avant de continuer.",
        "yes": "Oui"
      },
      "dialog_about": {
        "frontend_version": "Version frontend",
        "installed_repositories": "Dépôts installés",
        "integration_version": "Version d'intégration",
        "useful_links": "Liens utiles"
      },
      "dialog_add_repo": {
        "limit": "Seuls les 100 premiers dépôts sont affichés, utilisez la recherche pour filtrer ce dont vous avez besoin",
        "no_match": "Aucun dépôt trouvé correspondant à votre filtre",
        "sort_by": "Trier par",
        "title": "Ajouter un dépôt"
      },
      "dialog_custom_repositories": {
        "category": "Catégorie",
        "no_category": "Catégorie manquante",
        "no_repository": "Dépôt manquant",
        "title": "Dépôts personnalisés",
        "url_placeholder": "Ajouter une URL de dépôt personnalisée"
      },
      "dialog_info": {
        "author": "Auteur",
        "downloads": "Téléchargements",
        "install": "Installer ce dépôt dans HACS",
        "loading": "Chargement des informations ...",
        "no_info": "Le développeur n'a pas fourni plus d'informations pour ce dépôt",
        "open_issues": "Open issues",
        "open_repo": "Dépôt ouvert",
        "stars": "Étoiles",
        "version_installed": "Version installée"
      },
      "dialog_install": {
        "restart": "N'oubliez pas que vous devez redémarrer Home Assistant avant que les modifications aux intégrations (custom_components) soient appliquées.",
        "select_version": "Sélectionnez une version",
        "show_beta": "Afficher les versions bêta",
        "type": "Type",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Version disponible",
        "changelog": "Changelog",
        "installed_version": "Version installée",
        "releasenotes": "Notes de version pour {release}",
        "title": "Mise à jour en attente"
      },
      "entry": {
        "information": "Information",
        "intro": "Les mises à jour et les messages importants s'afficheront ici s'il y en a",
        "messages": {
          "disabled": {
            "content": "Vérifiez votre fichier journal pour plus de détails",
            "title": "HACS est désactivé"
          },
          "has_pending_tasks": {
            "content": "Certains dépôts peuvent ne pas apparaître tant que cette opération n'est pas terminée",
            "title": "Tâches d’arrière-plan en attente"
          },
          "resources": {
            "content": "Vous avez {number} éléments Lovelace qui ne sont pas chargés correctement dans Lovelace.",
            "title": "Non chargé dans Lovelace"
          },
          "restart": {
            "content": "Vous disposez de {number} intégrations qui nécessitent un redémarrage de Home Assistant, vous pouvez le faire à partir de la section \"Commandes du serveur\" sous la partie configuration de l'interface utilisateur de Home Assistant.",
            "title": "En attente de redémarrage"
          },
          "startup": {
            "content": "HACS démarre, pendant ce temps, certaines informations peuvent être manquantes ou incorrectes",
            "title": "HACS est en train de démarrer"
          },
          "wrong_frontend_installed": {
            "content": "Vous avez {running} du frontend HACS installé, mais la version {expected} était attendue. Si ce message s'affiche, Home Assistant n'a pas pu installer la nouvelle version, essayez de redémarrer Home Assistant.",
            "title": "Version frontend inattendue"
          },
          "wrong_frontend_loaded": {
            "content": "Vous exécutez la version {running} de l'interface HACS, mais la version {expected} était attendue, vous devez vider le cache de votre navigateur.",
            "title": "Version frontend inattendue"
          }
        },
        "pending_updates": "Mises à jour en attente"
      },
      "menu": {
        "about": "À propos de HACS",
        "clear": "Effacer tous les nouveaux",
        "custom_repositories": "Dépôts personnalisés",
        "dismiss": "Rejeter tous les nouveaux dépôts",
        "documentation": "Documentation",
        "open_issue": "Open issue",
        "reload": "Recharger la fenêtre"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Activer la découverte et le suivi des applications AppDaemon",
              "country": "Filtrer par code pays.",
              "debug": "Activez le débogage.",
              "experimental": "Activer les fonctionnalités expérimentales",
              "netdaemon": "Activer la découverte et le suivi des applications NetDaemon",
              "not_in_use": "Non utilisé avec YAML",
              "release_limit": "Nombre de recensés à afficher.",
              "sidepanel_icon": "Icône de la barre latérale",
              "sidepanel_title": "Titre de la barre latérale"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Cette intégration prend en charge config_flow, ce qui signifie que vous pouvez maintenant accéder à la section d'intégration de votre interface utilisateur pour le configurer.",
        "config_flow_title": "Configuration de l'interface utilisateur prise en charge",
        "integration_not_loaded": "Cette intégration n'est pas chargée dans Home Assistant.",
        "no_restart_required": "Aucun redémarrage requis",
        "not_loaded": "Non chargé",
        "plugin_not_loaded": "Ce plugin n'est pas ajouté à vos ressources Lovelace.",
        "restart": "Vous devez redémarrer Home Assistant.",
        "restart_pending": "Redémarrage en attente"
      },
      "repository_card": {
        "dismiss": "rejeter",
        "hide": "Cacher",
        "information": "Information",
        "new_repository": "Nouveau dépôt",
        "not_loaded": "Non chargé",
        "open_issue": "Ouvrir issue",
        "open_source": "Open source",
        "pending_restart": "Redémarrage en attente",
        "pending_update": "Mise à jour en attente",
        "reinstall": "Réinstaller",
        "report": "Rapport de suppression",
        "update_information": "Mettre à jour les informations"
      },
      "repository": {
        "add_to_lovelace": "Ajouter à Lovelace",
        "authors": "Auteurs",
        "available": "Disponible",
        "back_to": "Retour",
        "changelog": "Change log",
        "downloads": "Téléchargements",
        "flag_this": "Marquer",
        "frontend_version": "Version de l'interface",
        "github_stars": "Étoiles GitHub",
        "goto_integrations": "Aller aux intégrations",
        "hide": "Masquer",
        "hide_beta": "Masquer les bêta",
        "install": "Installer",
        "installed": "Installé",
        "lovelace_copy_example": "Copier cet exemple dans le presse-papier",
        "lovelace_instruction": "Quand vous l'ajoutez à votre configuration lovelace, utilisez",
        "lovelace_no_js_type": "Impossible de déterminer le type de plugin, veuillez vérifier le dépôt",
        "newest": "nouveau",
        "note_appdaemon": "vous devez toujours l'ajouter à votre fichier 'apps.yaml'",
        "note_installed": "Une fois installé, il se trouvera dans",
        "note_integration": "Vous devez toujours l'ajouter à votre fichier 'configuration.yaml'",
        "note_plugin": "Vous devez toujours l'ajouter à votre configuration lovelace ('ui-lovelace.yaml' ou l'éditeur de configuration de l'interface)",
        "note_plugin_post_107": "Vous devez toujours l'ajouter à votre configuration lovelace ('configuration.yaml' ou l'éditeur de configuration de l'interface '/config/lovelace/resources')",
        "open_issue": "Ouvrir un ticket",
        "open_plugin": "Ouvrir l'élément",
        "reinstall": "Réinstaller",
        "repository": "Dépôt",
        "restart_home_assistant": "Redémarrer Home Assistant",
        "show_beta": "Afficher les bêta",
        "uninstall": "Désinstaller",
        "update_information": "Mettre à jour les informations",
        "upgrade": "Mettre à jour"
      },
      "search": {
        "placeholder": "Rechercher un dépôt"
      },
      "sections": {
        "about": {
          "description": "Afficher des informations sur le système HACS",
          "title": "À propos de"
        },
        "automation": {
          "description": "C'est ici que vous trouverez les scripts python, les applications AppDaemon et NetDaemon",
          "title": "Automatisation"
        },
        "frontend": {
          "description": "C'est ici que vous trouverez des thèmes, des cartes personnalisées et d'autres éléments pour lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "C'est ici que vous trouverez les intégrations personnalisées (custom_components)",
          "title": "Intégrations"
        },
        "pending_repository_upgrade": "Vous utilisez la version {installed} , la version {available} est disponible"
      },
      "settings": {
        "add_custom_repository": "AJOUTER UN DÉPÔT PERSONNALISÉ",
        "adding_new_repo": "Ajout d'un nouveau dépôt '{repo}'",
        "adding_new_repo_category": "Avec la catégorie '{category}'.",
        "bg_task_custom": "Les dépôts personnalisés sont masqués pendant l'exécution de tâches en arrière-plan.",
        "category": "Catégorie",
        "compact_mode": "Mode compact",
        "custom_repositories": "DÉPÔTS PERSONNALISÉS",
        "delete": "Supprimer",
        "display": "Affichage",
        "grid": "Grille",
        "hacs_repo": "Dépôt HACS",
        "hidden_repositories": "dépôts cachés",
        "missing_category": "Vous devez sélectionner une catégorie",
        "open_repository": "Ouvrir dépôt",
        "reload_data": "Recharger les données",
        "reload_window": "Recharger la fenêtre",
        "repository_configuration": "Configuration de dépôt",
        "save": "Enregistrer",
        "table": "Tableau",
        "table_view": "Vue table",
        "unhide": "Afficher",
        "upgrade_all": "Tout mettre à jour"
      },
      "store": {
        "ascending": "ascendant",
        "clear_new": "Effacer tous les nouveaux dépôts",
        "descending": "descendant",
        "last_updated": "Dernière mise à jour",
        "name": "Nom",
        "new_repositories": "Nouveaux dépôts",
        "new_repositories_note": "Vous avez plus de 10 nouveaux dépôts qui apparaissent ici. Si vous voulez les effacer tous, cliquez sur les 3 points en haut à droite de l'écran et ignorez-les tous.",
        "no_repositories": "Aucun dépôt",
        "no_repositories_desc1": "Il semble que vous n’avez pas de dépôts installés dans cette section encore.",
        "no_repositories_desc2": "Cliquez sur le + dans le coin inférieur pour ajouter votre premier !",
        "no_repositories_found_desc1": "Aucun référentiel installé correspondant à \" {searchInput} \" n'a été trouvé dans cette section.",
        "no_repositories_found_desc2": "Essayez de chercher autre chose !",
        "pending_upgrades": "Mises à niveau en attente",
        "placeholder_search": "Veuillez entrer un terme de recherche...",
        "sort": "Trier",
        "stars": "Étoiles",
        "status": "Statut"
      },
      "time": {
        "ago": "il y a",
        "day": "jour",
        "days": "jours",
        "hour": "heure",
        "hours": "heures",
        "minute": "minute",
        "minutes": "minutes",
        "month": "mois",
        "months": "mois",
        "one": "Un",
        "one_day_ago": "il y a 1 jour",
        "one_hour_ago": "il y a 1 heure",
        "one_minute_ago": "il y a 1 minute",
        "one_month_ago": "il y a 1 mois",
        "one_second_ago": "il y a 1 seconde",
        "one_year_ago": "il y a 1 an",
        "second": "seconde",
        "seconds": "secondes",
        "x_days_ago": "il y a {x} jours",
        "x_hours_ago": "il y a {x} heures",
        "x_minutes_ago": "il y a {x} minutes",
        "x_months_ago": "il y a {x} mois",
        "x_seconds_ago": "il y a {x} secondes",
        "x_years_ago": "il y a {x} ans",
        "year": "an",
        "years": "ans"
      }
    },
    "hu": {
      "common": {
        "about": "Névjegy",
        "add": "hozzáadás",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Appok",
        "appdaemon_plural": "AppDaemon appok",
        "background_task": "Éppen háttérfeladat fut, ez az oldal frissülni fog, ha kész.",
        "check_log_file": "További részletekért ellenőrizd a naplófájlt.",
        "continue": "Folytatás",
        "disabled": "Tiltva",
        "documentation": "Dokumentáció",
        "element": "bővítmény",
        "hacs_is_disabled": "A HACS le van tiltva",
        "install": "Telepítés",
        "installed": "Telepített",
        "integration": "Integráció",
        "integration_plural": "Integrációk",
        "integrations": "Integrációk",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace bővítmény",
        "lovelace_elements": "Lovelace bővítmények",
        "manage": "kezelés",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Appok",
        "netdaemon_plural": "NetDaemon appok",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace bővítmények",
        "plugins": "Lovelace bővítmények",
        "python_script": "Python Szkript",
        "python_script_plural": "Python szkriptek",
        "python_scripts": "Python Szkriptek",
        "repositories": "Tárolók",
        "repository": "Tároló",
        "settings": "beállítások",
        "theme": "Téma",
        "theme_plural": "Témák",
        "themes": "Témák",
        "uninstall": "Eltávolítás",
        "update": "Frissítés",
        "version": "Verzió"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Csak egyetlen HACS-konfiguráció megengedett."
        },
        "error": {
          "auth": "A Személyes Hozzáférési Token nem megfelelő."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "AppDaemon appok felfedezésének és nyomon követésének engedélyezése",
              "netdaemon": "NetDaemon appok felfedezésének és nyomon követésének engedélyezése",
              "python_script": "Python szkriptek felfedezésének és nyomon követésének engedélyezése",
              "sidepanel_icon": "Oldalsó panel ikon",
              "sidepanel_title": "Oldalsó panel cím",
              "theme": "Témák felfedezésének és nyomon követésének engedélyezése",
              "token": "GitHub Személyes Hozzáférési Token"
            },
            "description": "Ha segítségre van szükséged a konfigurációval kapcsolatban, akkor tekintsd meg ezt az oldalt: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Biztosan hozzá szeretnéd ezt adni a Lovelace erőforrásokhoz?",
        "bg_task": "A művelet le van tiltva, amíg háttérfeladat fut.",
        "cancel": "Mégse",
        "continue": "Biztosan folytatni szeretnéd?",
        "delete": "Biztosan törölni szeretnéd a(z) '{item}'-t?",
        "delete_installed": "A(z) '{item}' telepítve van, törlés előtt el kell távolítanod.",
        "exist": "{item} már létezik",
        "generic": "Biztos vagy benne?",
        "home_assistant_is_restarting": "Várj, a Home Assistant éppen újraindul.",
        "home_assistant_version_not_correct": "A Home Assistant '{haversion}' verzióját használod, de ehhez a tárolóhoz legalább a(z) '{minversion}' verzióra van szükség.",
        "no": "Nem",
        "no_upgrades": "Nincsenek elérhető frissítések",
        "ok": "OK",
        "overwrite": "Ezzel felül fogod írni.",
        "reload_data": "Ez újratölti a HACS által ismert összes tároló adatát, ami némi időbe telhet.",
        "restart_home_assistant": "Biztosan újraindítod a Home Assistant programot?",
        "uninstall": "Biztosan el szeretnéd távolítani a(z) '{item}'-t?",
        "upgrade_all": "Ez frissíteni fogja az összes tárolót. Győzödj meg róla, hogy elolvastad az összes kiadási megjegyzést, mielőtt továbblépnél.",
        "yes": "Igen"
      },
      "dialog_about": {
        "frontend_version": "Frontend verzió",
        "installed_repositories": "Telepített tárolók",
        "integration_version": "Integráció verzió",
        "useful_links": "Hasznos linkek"
      },
      "dialog_add_repo": {
        "limit": "Csak az első 100 tároló jelenik meg, használd a keresőt a találatok szűkítéséhez",
        "no_match": "Nincs a szűrésnek megfelelő tároló",
        "sort_by": "Rendezés",
        "title": "Tároló hozzáadása"
      },
      "dialog_custom_repositories": {
        "category": "Kategória",
        "no_category": "Hiányzó kategória",
        "no_repository": "Hiányzó tároló",
        "title": "Egyéni tárolók",
        "url_placeholder": "Egyéni tároló URL címének hozzáadása"
      },
      "dialog_info": {
        "author": "Szerző",
        "downloads": "Letöltések",
        "install": "Tároló telepítése HACS-ben",
        "loading": "Információ betöltése...",
        "no_info": "A fejlesztő nem adott meg több információt ehhez a tárolóhoz",
        "open_issues": "Jelentett problémák",
        "open_repo": "Tároló megnyitása",
        "stars": "Csillagok",
        "version_installed": "Telepített verzió"
      },
      "dialog_install": {
        "restart": "Ne feledd, hogy az egyéni integrációk (custom_components) módosításainak alkalmazásához újra kell indítanod a Home Assistant alkalmazást.",
        "select_version": "Verzió kiválasztása",
        "show_beta": "Béta verziók megjelenítése",
        "type": "Típus",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Elérhető verzió",
        "changelog": "Változási napló",
        "installed_version": "Telepített verzió",
        "title": "Frissítés érhető el"
      },
      "entry": {
        "information": "Információ",
        "intro": "A frissítések és a fontos üzenetek itt jelennek meg, ha vannak",
        "messages": {
          "disabled": {
            "content": "További részletek a naplófájlban",
            "title": "A HACS le van tiltva"
          },
          "has_pending_tasks": {
            "content": "Előfordulhat, hogy egyes tárolók nem jelennek meg, amíg ez be nem fejeződik",
            "title": "Függőben lévő háttérfeladatok"
          },
          "startup": {
            "content": "A HACS éppen indul, ezidő alatt egyes információk hiányozhatnak vagy helytelenek lehetnek",
            "title": "A HACS éppen indul"
          }
        },
        "pending_updates": "Frissítések érhetők el"
      },
      "menu": {
        "about": "HACS névjegye",
        "clear": "Új jelölések törlése",
        "custom_repositories": "Egyéni tárolók",
        "dismiss": "Minden új tároló elvetése",
        "documentation": "Dokumentáció",
        "open_issue": "Probléma jelentése",
        "reload": "Ablak újratöltése"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "AppDaemon appok felfedezésének és nyomon követésének engedélyezése",
              "country": "Szűrés országkóddal.",
              "debug": "Hibakeresés engedélyezése.",
              "experimental": "Kísérleti funkciók engedélyezése",
              "netdaemon": "NetDaemon appok felfedezésének és nyomon követésének engedélyezése",
              "not_in_use": "YAML-lel nem használható",
              "release_limit": "Megjelenítendő kiadások száma.",
              "sidepanel_icon": "Oldalsó panel ikon",
              "sidepanel_title": "Oldalsó panel cím"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Ez az integráció támogatja a config_flow-t, vagyis a felhasználói felületen az integrációk menüben lehet konfigurálni.",
        "config_flow_title": "A felhasználói felület konfigurációja támogatott",
        "integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.",
        "no_restart_required": "Nincs szükség újraindításra",
        "not_loaded": "Nincs betöltve",
        "plugin_not_loaded": "Ez a bővítmény nincs hozzáadva a Lovelace erőforrásaidhoz.",
        "restart": "Indítsd újra a Home Assistant programot.",
        "restart_pending": "Újraindítás függőben"
      },
      "repository_card": {
        "dismiss": "elvetés",
        "hide": "Elrejtés",
        "information": "Információ",
        "new_repository": "Új tároló",
        "open_issue": "Probléma jelentése",
        "open_source": "Forrás megnyitása",
        "pending_update": "Frissítés érhető el",
        "reinstall": "Újratelepítés",
        "report": "Jelentés eltávolításra",
        "update_information": "Frissítési információ"
      },
      "repository": {
        "add_to_lovelace": "Hozzáadás a Lovelace-hez",
        "authors": "Szerzők",
        "available": "Elérhető",
        "back_to": "Vissza -",
        "changelog": "Változási napló",
        "downloads": "Letöltések",
        "flag_this": "Megjelölés",
        "frontend_version": "Frontend verzió",
        "github_stars": "GitHub csillagok",
        "goto_integrations": "Ugrás az integrációkhoz",
        "hide": "Elrejtés",
        "hide_beta": "Béta elrejtése",
        "install": "Telepítés",
        "installed": "Telepített",
        "lovelace_copy_example": "Példa másolása a vágólapra",
        "lovelace_instruction": "Amikor hozzáadod ezt a lovelace konfigurációdhoz, használd ezt",
        "lovelace_no_js_type": "Nem sikerült meghatározni a bővítmény típusát, ellenőrizd a tárolót.",
        "newest": "legújabb",
        "note_appdaemon": "de még hozzá kell adnod az 'apps.yaml' fájlhoz",
        "note_installed": "Telepítéskor a következő helyre kerül:",
        "note_integration": "de még hozzá kell adnod a 'configuration.yaml' fájlhoz",
        "note_plugin": "de még hozzá kell adnod a lovelace konfigurációhoz (az 'ui-lovelace.yaml' fájlban vagy a Lovelace felületen a konfiguráció szerkesztőben)",
        "note_plugin_post_107": "de még hozzá kell adnod a lovelace konfigurációhoz ('configuration.yaml' vagy az erőforrás szerkesztőben '/config/lovelace/resources')",
        "open_issue": "Probléma jelentése",
        "open_plugin": "Bővítmény megnyitása",
        "reinstall": "Újratelepítés",
        "repository": "Tároló",
        "restart_home_assistant": "Home Assistant újraindítása",
        "show_beta": "Béta megjelenítése",
        "uninstall": "Eltávolítás",
        "update_information": "Frissítési információk",
        "upgrade": "Frissítés"
      },
      "search": {
        "placeholder": "Tároló keresése"
      },
      "sections": {
        "about": {
          "description": "Információk megjelenítése a HACS-ről",
          "title": "Névjegy"
        },
        "automation": {
          "description": "Itt Python szkripteket, AppDaemon és NetDaemon appokat találsz",
          "title": "Automatizálás"
        },
        "frontend": {
          "description": "Itt témákat, egyéni kártyákat és más bővítményeket találsz a Lovelace-hez",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Itt találod az egyéni integrációkat (custom_components)",
          "title": "Integrációk"
        },
        "pending_repository_upgrade": "A(z) {installed} verziót futtatod, a(z) {available} verzió már elérhető"
      },
      "settings": {
        "add_custom_repository": "EGYÉNI TÁROLÓ HOZZÁADÁSA",
        "adding_new_repo": "Új tároló hozzáadása '{repo}'",
        "adding_new_repo_category": "A '{category}' kategóriával.",
        "bg_task_custom": "Az egyéni tárolók rejtve vannak, amíg háttérfeladat fut.",
        "category": "Kategória",
        "compact_mode": "Kompakt mód",
        "custom_repositories": "EGYÉNI TÁROLÓK",
        "delete": "Törlés",
        "display": "Megjelenítés",
        "grid": "Rács",
        "hacs_repo": "HACS tároló",
        "hidden_repositories": "rejtett tárolók",
        "missing_category": "Ki kell választanod egy kategóriát",
        "open_repository": "Tároló megnyitása",
        "reload_data": "Adatok újratöltése",
        "reload_window": "Ablak újratöltése",
        "repository_configuration": "Tároló konfiguráció",
        "save": "Mentés",
        "table": "Táblázat",
        "table_view": "Táblázat nézet",
        "unhide": "felfedés",
        "upgrade_all": "Minden frissítése"
      },
      "store": {
        "ascending": "növekvő",
        "clear_new": "Új tárolók megjelölése látottként",
        "descending": "csökkenő",
        "last_updated": "Utolsó frissítés",
        "name": "Név",
        "new_repositories": "Új tárolók",
        "new_repositories_note": "Több mint 10 új tároló látható. Ha törölni szeretnéd őket, akkor kattints a jobb felső sarokban lévő 3 pontra, és válaszd ki a 'Minden új tároló elvetése' menüpontot.",
        "no_repositories": "Nincsenek tárolók",
        "no_repositories_desc1": "Úgy tűnik, még nincsenek telepítve tárolók ebben a szekcióban.",
        "no_repositories_desc2": "Kattints az alsó sarokban található + jelre az első hozzáadásához!",
        "pending_upgrades": "Frissítések érhetők el",
        "placeholder_search": "Kérlek adj meg egy keresési kifejezést...",
        "sort": "rendezés",
        "stars": "Csillag",
        "status": "Állapot"
      },
      "time": {
        "ago": "ezelőtt",
        "day": "nap",
        "days": "nap",
        "hour": "óra",
        "hours": "óra",
        "minute": "perc",
        "minutes": "perc",
        "month": "hónap",
        "months": "hónap",
        "one": "Egy",
        "one_day_ago": "egy nappal ezelőtt",
        "one_hour_ago": "egy órával ezelőtt",
        "one_minute_ago": "egy perccel ezelőtt",
        "one_month_ago": "egy hónappal ezelőtt",
        "one_second_ago": "egy másodperccel ezelőtt",
        "one_year_ago": "egy évvel ezelőtt",
        "second": "másodperc",
        "seconds": "másodperc",
        "x_days_ago": "{x} nappal ezelőtt",
        "x_hours_ago": "{x} órával ezelőtt",
        "x_minutes_ago": "{x} perccel ezelőtt",
        "x_months_ago": "{x} hónappal ezelőtt",
        "x_seconds_ago": "{x} másodperccel ezelőtt",
        "x_years_ago": "{x} évvel ezelőtt",
        "year": "év",
        "years": "év"
      }
    },
    "it": {
      "common": {
        "about": "Informazioni su",
        "add": "aggiungi",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Applicazioni AppDaemon",
        "appdaemon_plural": "Applicazioni AppDaemon",
        "background_task": "Attività in esecuzione, questa pagina sarà ricaricata al termine.",
        "check_log_file": "Controlla il tuo file di registro per maggiori dettagli.",
        "continue": "Continua",
        "disabled": "Disabilitato",
        "documentation": "Documentazione",
        "element": "elemento",
        "hacs_is_disabled": "HACS è disabilitato",
        "install": "Installa",
        "installed": "Installati",
        "integration": "Integrazione",
        "integration_plural": "Integrazioni",
        "integrations": "Integrazioni",
        "lovelace": "Lovelace",
        "lovelace_element": "Elemento Lovelace",
        "lovelace_elements": "Elementi di Lovelace",
        "manage": "gestione",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Applicazioni NetDaemon",
        "netdaemon_plural": "Applicazioni NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Elementi di Lovelace",
        "plugins": "Elementi di Lovelace",
        "python_script": "Script python",
        "python_script_plural": "Script in Python",
        "python_scripts": "Script python",
        "repositories": "Repository",
        "repository": "Repository",
        "settings": "Impostazioni",
        "theme": "Tema",
        "theme_plural": "Temi",
        "themes": "Temi",
        "uninstall": "Disinstallare",
        "update": "Aggiorna",
        "version": "Versione"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "È consentita una sola configurazione di HACS."
        },
        "error": {
          "auth": "Il token di accesso personale non è corretto."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni AppDaemon",
              "netdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni NetDaemon",
              "python_script": "Abilita il rilevamento e il monitoraggio dei python_scripts",
              "sidepanel_icon": "Icona nel pannello laterale",
              "sidepanel_title": "Titolo nel pannello laterale",
              "theme": "Abilita individuazione e tracciamento dei temi",
              "token": "Token di accesso personale GitHub"
            },
            "description": "Se hai bisogno di aiuto con la configurazione dai un'occhiata qui: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Sei sicuro di voler aggiungerlo alle tue risorse Lovelace?",
        "bg_task": "L'azione è disabilitata mentre sono in esecuzione attività in background.",
        "cancel": "Annulla",
        "continue": "Sei sicuro di voler continuare?",
        "delete": "Sei sicuro di voler disinstallare '{item}'?",
        "delete_installed": "'{item}' è installato, è necessario disinstallarlo prima di poterlo eliminare.",
        "exist": "{item} esiste già",
        "generic": "Sei sicuro?",
        "home_assistant_is_restarting": "Aspetta, Home Assistant si sta riavviando.",
        "home_assistant_version_not_correct": "Stai eseguendo la versione Home Assistant '{haversion}', ma questo repository richiede l'installazione della versione minima '{minversion}'.",
        "no": "No",
        "no_upgrades": "Nessun aggiornamento in sospeso",
        "ok": "OK",
        "overwrite": "In questo modo lo sovrascriverà.",
        "reload_data": "Questo ricarica i dati di tutte le repository di cui HACS è a conoscenza, ci vorrà del tempo per finire.",
        "restart_home_assistant": "Sei sicuro di voler riavviare Home Assistant?",
        "uninstall": "Sei sicuro di voler disinstallare '{item}'?",
        "upgrade_all": "Questa azione aggiornerà tutti i repository, assicurati di aver letto le note di rilascio prima di procedere.",
        "yes": "Sì"
      },
      "dialog_about": {
        "frontend_version": "Versione frontend",
        "installed_repositories": "Repository installati",
        "integration_version": "Versione di integrazione",
        "useful_links": "Link utili"
      },
      "dialog_add_repo": {
        "limit": "Vengono visualizzati solo i primi 100 repository, utilizza la ricerca per filtrare ciò di cui hai bisogno",
        "no_match": "Nessun repository trovato corrispondente al filtro",
        "sort_by": "Ordina per",
        "title": "Aggiungi repository"
      },
      "dialog_custom_repositories": {
        "category": "Categoria",
        "no_category": "Categoria mancante",
        "no_repository": "Repository mancante",
        "title": "Repository personalizzati",
        "url_placeholder": "Aggiungi l'URL del repository personalizzato"
      },
      "dialog_info": {
        "author": "Autore",
        "downloads": "Download",
        "install": "Installa questo repository in HACS",
        "loading": "Caricamento informazioni in corso ...",
        "no_info": "Lo sviluppatore non ha fornito ulteriori informazioni per questo repository",
        "open_issues": "Problemi aperti",
        "open_repo": "Apri il repository",
        "stars": "Stelle",
        "version_installed": "Versione installata"
      },
      "dialog_install": {
        "restart": "Tenere presente che è necessario riavviare Home Assistant prima di applicare le modifiche alle integrazioni (custom_components).",
        "select_version": "Seleziona la versione",
        "show_beta": "Mostra le versioni beta",
        "type": "Tipo",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Versione disponibile",
        "changelog": "Registro dei cambiamenti",
        "installed_version": "Versione installata",
        "releasenotes": "Note di rilascio per {release}",
        "title": "Aggiornamento in sospeso"
      },
      "entry": {
        "information": "Informazioni",
        "intro": "Gli aggiornamenti e i messaggi importanti verranno visualizzati qui se presenti",
        "messages": {
          "disabled": {
            "content": "Controlla il tuo file di registro per maggiori dettagli",
            "title": "HACS è disabilitato"
          },
          "has_pending_tasks": {
            "content": "Alcuni repository potrebbero non essere visualizzati fino al completamento",
            "title": "Attività in background in sospeso"
          },
          "resources": {
            "content": "Hai {number} elementi di Lovelace che non sono stati caricati correttamente in Lovelace.",
            "title": "Non caricato in Lovelace"
          },
          "restart": {
            "content": "Hai {number} integrazioni che richiedono il riavvio di Home Assistant, puoi farlo dalla sezione \"Controlli del server\" nella sezione Impostazioni dell'interfaccia utente di Home Assistant.",
            "title": "In attesa di riavvio"
          },
          "startup": {
            "content": "HACS si sta avviando, durante questo periodo alcune informazioni potrebbero essere mancanti o errate",
            "title": "HACS si sta avviando"
          },
          "wrong_frontend_installed": {
            "content": "Hai installato {running} del frontend HACS, ma era attesa la versione {expected}, se viene visualizzato questo messaggio Home Assistant non è stato in grado di installare la nuova versione, prova a riavviare Home Assistant.",
            "title": "Versione frontend inaspettata"
          },
          "wrong_frontend_loaded": {
            "content": "Stai eseguendo la versione {running} del frontend HACS, ma la versione {expected} era prevista, è necessario svuotare la cache del browser.",
            "title": "Versione frontend inaspettata"
          }
        },
        "pending_updates": "Aggiornamenti in sospeso"
      },
      "menu": {
        "about": "Informazioni su HACS",
        "clear": "Cancella tutte le novità",
        "custom_repositories": "Repository personalizzati",
        "dismiss": "Elimina tutti i nuovi repository",
        "documentation": "Documentazione",
        "open_issue": "Segnala anomalia",
        "reload": "Ricarica la finestra"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni AppDaemon",
              "country": "Filtra con prefisso internazionale.",
              "debug": "Abilita debug.",
              "experimental": "Abilita funzionalità sperimentali",
              "netdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni NetDaemon",
              "not_in_use": "Non in uso con YAML",
              "release_limit": "Numero di versioni da mostrare.",
              "sidepanel_icon": "Icona nel pannello laterale",
              "sidepanel_title": "Titolo nel pannello laterale"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Questa integrazione supporta config_flow, questo significa che è ora possibile passare alla sezione \"IntegrazionI\" dell'interfaccia utente per la configurazione.",
        "config_flow_title": "Configurazione dell'interfaccia utente supportata",
        "integration_not_loaded": "Questa integrazione non è caricata in Home Assistant.",
        "no_restart_required": "Non è necessario riavviare",
        "not_loaded": "Non caricato",
        "plugin_not_loaded": "Questo elemento non è aggiunto alle tue risorse Lovelace.",
        "restart": "È necessario riavviare Home Assistant.",
        "restart_pending": "Riavvio in attesa"
      },
      "repository_card": {
        "dismiss": "elimina",
        "hide": "Nascondi",
        "information": "Informazioni",
        "new_repository": "Nuovo repository",
        "not_loaded": "Non caricato",
        "open_issue": "Segnala anomalia",
        "open_source": "Open source",
        "pending_restart": "In attesa di riavvio",
        "pending_update": "Aggiornamento in sospeso",
        "reinstall": "Reinstallare",
        "report": "Segnala per la rimozione",
        "update_information": "Aggiorna informazioni"
      },
      "repository": {
        "add_to_lovelace": "Aggiungi a Lovelace",
        "authors": "Autori",
        "available": "Disponibile",
        "back_to": "Torna a",
        "changelog": "Change log",
        "downloads": "Downloads",
        "flag_this": "Spunta questo",
        "frontend_version": "Frontend versione",
        "github_stars": "GitHub stelle",
        "goto_integrations": "Vai alle Integrazioni",
        "hide": "Nascondi",
        "hide_beta": "Nascondi beta",
        "install": "Installa",
        "installed": "Installato",
        "lovelace_copy_example": "Copia l'esempio negli appunti",
        "lovelace_instruction": "Quando lo aggiungi nella configurazione di lovelace, usa questo",
        "lovelace_no_js_type": "Impossibile determinare il tipo di questo elemento, controllare il repository.",
        "newest": "Più recente",
        "note_appdaemon": "dovrai aggiungerlo nel file 'apps.yaml'",
        "note_installed": "Una volta installato, si troverà in",
        "note_integration": "dovrai aggiungerlo nel file 'configuration.yaml'",
        "note_plugin": "devi aggiungere la configurazione nel file 'ui-lovelace.yaml' oppure via Editor RAW della UI.",
        "note_plugin_post_107": "devi aggiungere la configurazione nel file 'ui-lovelace.yaml' oppure via Editor RAW della UI.",
        "open_issue": "Segnala anomalia",
        "open_plugin": "Apri elemento",
        "reinstall": "Reinstalla",
        "repository": "Archivio Software (Repository)",
        "restart_home_assistant": "Riavvia Home Assistant",
        "show_beta": "Visualizza beta",
        "uninstall": "Rimuovi",
        "update_information": "Aggiorna informazioni",
        "upgrade": "Aggiorna"
      },
      "search": {
        "installed": "Cerca i repository installati",
        "installed_new": "Cercare repository installati o nuovi",
        "placeholder": "Cerca repository"
      },
      "sections": {
        "about": {
          "description": "Mostra informazioni su HACS",
          "title": "Informazioni su"
        },
        "automation": {
          "description": "Qui trovi python_scripts, app AppDaemon e app NetDaemon",
          "title": "Automazione"
        },
        "frontend": {
          "description": "Qui troverai temi, schede personalizzate e altri elementi per Lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Qui si trovano le integrazioni personalizzate (custom_components)",
          "title": "Integrazioni"
        },
        "pending_repository_upgrade": "Stai eseguendo la versione {installed}, la versione {available}è disponibile"
      },
      "settings": {
        "add_custom_repository": "AGGIUNGI REPOSITORY PERSONALIZZATA",
        "adding_new_repo": "Aggiunta di un nuovo repository '{repo}'",
        "adding_new_repo_category": "Con la categoria '{category}'.",
        "bg_task_custom": "I repository personalizzati sono nascosti mentre sono in esecuzione attività in background.",
        "category": "Categoria",
        "compact_mode": "Modalità compatta",
        "custom_repositories": "REPOSITORY PERSONALIZZATE",
        "delete": "Cancella",
        "display": "Visualizza",
        "grid": "Griglia",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "repository nascosti",
        "missing_category": "Devi selezionare una categoria",
        "open_repository": "Apri il repository",
        "reload_data": "Ricarica i dati",
        "reload_window": "Ricarica la finestra",
        "repository_configuration": "Configurazione del repository",
        "save": "Salva",
        "table": "Tabella",
        "table_view": "Vista tabella",
        "unhide": "Mostra",
        "upgrade_all": "Aggiorna tutto"
      },
      "store": {
        "ascending": "ascendente",
        "clear_new": "Ripulisci i nuovi repository",
        "descending": "discendente",
        "last_updated": "Ultimo aggiornamento",
        "name": "Nome",
        "new_repositories": "Nuovi repository",
        "new_repositories_note": "Hai più di 10 nuovi repository visualizzati qui, se vuoi cancellarli tutti fai clic sui 3 punti nell'angolo in alto a destra e li eliminerai tutti.",
        "no_repositories": "Nessun repository",
        "no_repositories_desc1": "Sembra che non ci siano ancora repository installati in questa sezione.",
        "no_repositories_desc2": "Fai clic sul + nell'angolo in basso per aggiungere il tuo primo!",
        "no_repositories_found_desc1": "Nessun repository installato corrispondente a \"{searchInput}\" trovato in questa sezione.",
        "no_repositories_found_desc2": "Prova a cercare qualcos'altro!",
        "pending_upgrades": "Aggiornamenti in sospeso",
        "placeholder_search": "Inserire un termine di ricerca",
        "sort": "Ordinare",
        "stars": "Stelle",
        "status": "Stato"
      },
      "time": {
        "ago": "fa",
        "day": "giorno",
        "days": "giorni",
        "hour": "ora",
        "hours": "ore",
        "minute": "minuto",
        "minutes": "minuti",
        "month": "mese",
        "months": "mesi",
        "one": "Un",
        "one_day_ago": "un giorno fa",
        "one_hour_ago": "un'ora fa",
        "one_minute_ago": "un minuto fa",
        "one_month_ago": "un mese fa",
        "one_second_ago": "un secondo fa",
        "one_year_ago": "un anno fa",
        "second": "secondo",
        "seconds": "secondi",
        "x_days_ago": "{x} giorni fa",
        "x_hours_ago": "{x} ore fa",
        "x_minutes_ago": "{x} minuti fa",
        "x_months_ago": "{x} mesi fa",
        "x_seconds_ago": "{x} secondi fa",
        "x_years_ago": "{x} anni fa",
        "year": "anno",
        "years": "anni"
      }
    },
    "nb": {
      "common": {
        "about": "Om",
        "add": "legg til",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apper",
        "appdaemon_plural": "AppDaemon-apper",
        "background_task": "Bakgrunnsoppgaven kjører. Denne siden lastes inn på nytt når den er ferdig.",
        "check_log_file": "Sjekk loggfilen din for mer informasjon.",
        "continue": "Fortsett",
        "disabled": "Deaktivert",
        "documentation": "dokumentasjon",
        "element": "element",
        "hacs_is_disabled": "HACS er deaktivert",
        "install": "Installer",
        "installed": "Installert",
        "integration": "Integrasjon",
        "integration_plural": "Integrasjoner",
        "integrations": "Integrasjoner",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-element",
        "lovelace_elements": "Lovelace-elementer",
        "manage": "manage",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apper",
        "netdaemon_plural": "NetDaemon-apper",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace-elementer",
        "plugins": "Lovelace-elementer",
        "python_script": "Python-skript",
        "python_script_plural": "Python-skript",
        "python_scripts": "Python-skript",
        "repositories": "Repositories",
        "repository": "Repository",
        "settings": "Innstillinger",
        "theme": "Tema",
        "theme_plural": "Temaer",
        "themes": "Temaer",
        "uninstall": "Avinstaller",
        "update": "Oppdater",
        "version": "Versjon"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Bare en konfigurasjon av HACS er tillatt."
        },
        "error": {
          "auth": "Personlig tilgangstoken er ikke korrekt."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver oppdagelse og sporing av AppDaemon-apper",
              "netdaemon": "Aktiver oppdagelse og sporing av NetDaemon-apper",
              "python_script": "Aktiver oppdagelse og sporing av python_scripts",
              "sidepanel_icon": "Sidepanel ikon",
              "sidepanel_title": "Sidepanel tittel",
              "theme": "Aktiver oppdagelse og sporing av temaer",
              "token": "GitHub Personal Access Token"
            },
            "description": "Hvis du trenger hjelp med konfigurasjonen, ta en titt her: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Er du sikker på at du vil legge dette til i dine Lovelace resources?",
        "bg_task": "Handlingen er deaktivert mens bakgrunnsoppgaver kjører.",
        "cancel": "Avbryt",
        "continue": "Er du sikker på at du vil fortsette?",
        "delete": "Er du sikker på at du vil fjerne '{item}'?",
        "delete_installed": "'{item}' er installert, du må avinstallere det før du kan slette det.",
        "exist": "{item} eksisterer allerede",
        "generic": "Er du sikker?",
        "home_assistant_is_restarting": "Vent, Home Assistant starter nå på nytt.",
        "home_assistant_version_not_correct": "Du kjører Home Assistant '{haversion}', men denne krever minimum versjon '{minversion}' for å bli installert.",
        "no": "Nei",
        "no_upgrades": "Ingen oppgraderinger tilgjengelig",
        "ok": "OK",
        "overwrite": "Å gjøre dette vil overskrive det.",
        "reload_data": "Dette laster inn dataene til alle repositories HACS vet om, dette vil ta litt tid å fullføre.",
        "restart_home_assistant": "Er du sikker på at du vil starte Home Assistant på nytt?",
        "uninstall": "Er du sikker på at du vil avinstallere '{item}'?",
        "upgrade_all": "Dette vil oppgradere alle disse repositorene, sørg for at du har lest utgivelses notatene for dem alle før du fortsetter.",
        "yes": "Ja"
      },
      "dialog_about": {
        "frontend_version": "Frontend versjon",
        "installed_repositories": "Installerte repositories",
        "integration_version": "Integrasjonsversjon",
        "useful_links": "Nyttige lenker"
      },
      "dialog_add_repo": {
        "limit": "Bare de første 100 elementene vises, bruk søket til å filtrere det du trenger",
        "no_match": "Ingen elementer funnet som samsvarer med filteret ditt",
        "title": "Legg til repository"
      },
      "dialog_custom_repositories": {
        "category": "Kategori",
        "no_category": "Mangler kategori",
        "no_repository": "Mangler repository",
        "title": "Custom repositories",
        "url_placeholder": "Legg til custom repository"
      },
      "dialog_info": {
        "author": "Utgiver",
        "downloads": "Nedlastinger",
        "install": "Installer dette elementet i HACS",
        "loading": "Laster inn informasjon ...",
        "no_info": "Utvikleren har ikke gitt mer informasjon for dette elementet",
        "open_issues": "Åpne problemer",
        "open_repo": "Åpne repository",
        "stars": "Stjerner",
        "version_installed": "Versjon installert"
      },
      "dialog_install": {
        "select_version": "Velg versjon",
        "show_beta": "Vis betaversjoner",
        "type": "Type",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Tilgjengelig versjon",
        "changelog": "Endringslogg",
        "installed_version": "Installert versjon",
        "title": "Oppdatering venter"
      },
      "entry": {
        "information": "Informasjon",
        "intro": "Oppdateringer og viktige meldinger vises her hvis det er noen",
        "messages": {
          "disabled": {
            "content": "Sjekk loggfilen din for mer informasjon",
            "title": "HACS er deaktivert"
          },
          "has_pending_tasks": {
            "content": "Noen elementer vises kanskje ikke før dette er fullført",
            "title": "Venter på bakgrunnsoppgaver"
          },
          "startup": {
            "content": "HACS starter opp, i løpet av denne tiden kan det hende at noe informasjon mangler eller er feil",
            "title": "HACS starter opp"
          }
        },
        "pending_updates": "Oppdateringer er klare"
      },
      "menu": {
        "about": "Om HACS",
        "clear": "Fjern alt nytt",
        "custom_repositories": "Custom repositories",
        "documentation": "Dokumentasjon",
        "open_issue": "Meld et problem",
        "reload": "Last inn vinduet på nytt"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver oppdagelse og sporing av AppDaemon-apper",
              "country": "Filtrer med landskode.",
              "debug": "Aktiver debug",
              "experimental": "Aktiver eksperimentelle funksjoner",
              "netdaemon": "Aktiver oppdagelse og sporing av NetDaemon-apper",
              "not_in_use": "Ikke i bruk med YAML",
              "release_limit": "Antall utgivelser som skal vises.",
              "sidepanel_icon": "Sidepanel ikon",
              "sidepanel_title": "Sidepanel tittel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Denne integrasjonen støtter config_flow, det betyr at du nå kan gå til integrasjoner i brukergrensesnittet for å konfigurere den.",
        "config_flow_title": "UI konfigurasjon støttet",
        "integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.",
        "no_restart_required": "Ingen omstart kreves",
        "not_loaded": "Ikke lastet inn",
        "plugin_not_loaded": "Dette elementet er ikke lagt til i lovelace under \"resource\" delen av konfigurasjonen.",
        "restart": "Du må restart Home Assistant",
        "restart_pending": "Restart er nødvendig"
      },
      "repository_card": {
        "hide": "Skjul",
        "information": "Informasjon",
        "new_repository": "Ny",
        "open_issue": "Meld et problem",
        "open_source": "Åpne kilde",
        "pending_update": "Oppdatering venter",
        "reinstall": "Installer på nytt",
        "report": "Rapporter for fjerning",
        "update_information": "Oppdater informasjon"
      },
      "repository": {
        "add_to_lovelace": "Legg til i Lovelace",
        "authors": "Laget av",
        "available": "Tilgjengelig",
        "back_to": "Tilbake til",
        "changelog": "Endringslogg",
        "downloads": "Nedlastinger",
        "flag_this": "Flag dette",
        "frontend_version": "Frontend versjon",
        "github_stars": "GitHub-stjerner",
        "goto_integrations": "Gå til integrasjoner",
        "hide": "Skjul",
        "hide_beta": "Skjul beta",
        "installed": "Installert",
        "lovelace_copy_example": "Kopier eksemplet til utklippstavlen",
        "lovelace_instruction": "Når du legger til dette i lovelace-konfigurasjonen din, bruk dette",
        "lovelace_no_js_type": "Kunne ikke bestemme typen for dettte elementet, sjekk repository.",
        "newest": "Nyeste",
        "note_appdaemon": "du må fortsatt legge den til i 'apps.yaml' filen",
        "note_installed": "Når det er installert, vil dette ligge i",
        "note_integration": "du må fortsatt legge den til 'configuration.yaml' filen",
        "note_plugin": "du må fortsatt legge den til i lovelace-konfigurasjonen ('ui-lovelace.yaml' eller den rå UI-konfigurasjonsredigereren)",
        "note_plugin_post_107": "du må fortsatt legge den til i lovelace konfigurasjonen ('configuration.yaml' eller via resource behanleren i grensesnittet '/config/lovelace/resources')",
        "open_issue": "Meld et problem",
        "open_plugin": "Åpne kilde",
        "reinstall": "Installer på nytt",
        "repository": "Repository",
        "restart_home_assistant": "Start Home Assistant på nytt",
        "show_beta": "Vis beta",
        "uninstall": "Avinstaller",
        "update_information": "Oppdater informasjon",
        "upgrade": "Oppdater"
      },
      "search": {
        "placeholder": "Søk etter repository"
      },
      "sections": {
        "about": {
          "description": "Vis informasjon om HACS",
          "title": "Om"
        },
        "automation": {
          "description": "Det er her du finner python_scripts, AppDaemon-apper og NetDaemon-apper",
          "title": "Automasjon"
        },
        "frontend": {
          "description": "Det er her du finner temaer, tilpassede kort og andre elementer for lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Det er her du finner tilpassede integrasjoner (custom_components)",
          "title": "Integrasjoner"
        },
        "pending_repository_upgrade": "Du kjører versjon {installed} , versjon {available} er tilgjengelig"
      },
      "settings": {
        "add_custom_repository": "LEGG TIL REPOSITORY",
        "adding_new_repo": "Legger til ny repository '{repo}'",
        "adding_new_repo_category": "Med kategori '{category}'.",
        "bg_task_custom": "Custom repositories er skjult mens bakgrunnsoppgaver kjører.",
        "category": "Kategori",
        "compact_mode": "Kompakt modus",
        "custom_repositories": "TILPASSEDE REPOSITORIER",
        "delete": "Slett",
        "display": "Vise",
        "grid": "Nett",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "Gjemte repositories",
        "missing_category": "Du må velge en kategori",
        "open_repository": "Åpne repository",
        "reload_data": "Last inn data på nytt",
        "reload_window": "Last inn vinduet på nytt",
        "repository_configuration": "Repository konfigurasjon",
        "save": "Lagre",
        "table": "Tabell",
        "table_view": "Tabellvisning",
        "unhide": "Vis igjen",
        "upgrade_all": "Oppgradere alle"
      },
      "store": {
        "ascending": "stigende",
        "clear_new": "Tøm nye repositories",
        "descending": "synkende",
        "last_updated": "Sist oppdatert",
        "name": "Navn",
        "new_repositories": "Nye repositories",
        "no_repositories": "Ingen repositories",
        "no_repositories_desc1": "Det virker som om du ikke har noen elementer installert i denne delen ennå.",
        "no_repositories_desc2": "Klikk på + i nederste hjørne for å legge til din første!",
        "pending_upgrades": "Venter på oppgradering",
        "placeholder_search": "Skriv inn et søkeord ...",
        "sort": "sorter",
        "stars": "Stjerner",
        "status": "Status"
      },
      "time": {
        "ago": "siden",
        "day": "dag",
        "days": "dager",
        "hour": "time",
        "hours": "timer",
        "minute": "minutt",
        "minutes": "minutter",
        "month": "måned",
        "months": "måneder",
        "one": "En",
        "one_day_ago": "for en dag siden",
        "one_hour_ago": "en time siden",
        "one_minute_ago": "ett minutt siden",
        "one_month_ago": "en måned siden",
        "one_second_ago": "ett sekund siden",
        "one_year_ago": "ett år siden",
        "second": "sekund",
        "seconds": "sekunder",
        "x_days_ago": "{x} dager siden",
        "x_hours_ago": "{x} timer siden",
        "x_minutes_ago": "{x} minutter siden",
        "x_months_ago": "{x} måneder siden",
        "x_seconds_ago": "{x} sekunder siden",
        "x_years_ago": "{x} år siden",
        "year": "år",
        "years": "år"
      }
    },
    "nl": {
      "common": {
        "about": "Over",
        "add": "toevoegen",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Apps",
        "appdaemon_plural": "AppDaemon Apps",
        "background_task": "Achtergrond taak is draaiende, de pagina herhaalt zichzelf wanneer dit klaar is.",
        "check_log_file": "Controleer het logbestand voor meer details.",
        "continue": "Doorgaan",
        "disabled": "Uitgeschakeld",
        "documentation": "Documentatie",
        "element": "element",
        "hacs_is_disabled": "HACS is uitgeschakeld",
        "install": "Installeer",
        "installed": "geinstalleerd",
        "integration": "Integratie",
        "integration_plural": "Integraties",
        "integrations": "Integraties",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-element",
        "lovelace_elements": "Lovelace-elementen",
        "manage": "beheer",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "netdaemon_plural": "NetDaemon Apps",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace-elementen",
        "plugins": "Plugins",
        "python_script": "Python Script",
        "python_script_plural": "Python Scripts",
        "python_scripts": "Python Scripts",
        "repositories": "Repositories",
        "repository": "Repository",
        "settings": "instellingen",
        "theme": "Thema",
        "theme_plural": "Themas",
        "themes": "Themas",
        "uninstall": "Verwijderen",
        "update": "Update",
        "version": "Versie"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Je kunt maar een enkele configuratie van HACS tegelijk hebben."
        },
        "error": {
          "auth": "Persoonlijke Toegang Token is niet correct."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Zet AppDaemon apps ontdekken & traceren aan",
              "netdaemon": "Zet NetDaemon apps ontdekken & traceren aan",
              "python_script": "Zet python_scripts ontdekken & traceren aan",
              "sidepanel_icon": "Zijpaneel icoon",
              "sidepanel_title": "Zijpaneel titel",
              "theme": "Zet Themes ontdekken & traceren aan",
              "token": "GitHub Persoonlijke Toegang Token"
            },
            "description": "Als je hulp nodig hebt met de configuratie, kun je hier verder kijken: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Weet u zeker dat u dit wilt toevoegen aan uw Lovelace bronnen?",
        "bg_task": "Actie is geblokkeerd terwijl achtergrondtaken actief zijn.",
        "cancel": "Annuleer",
        "continue": "Weet je zeker dat je wilt doorgaan?",
        "delete": "Weet u zeker dat u '{item}' wilt verwijderen?",
        "delete_installed": "'{item}' is geïnstalleerd, je dient het eerst te deïnstalleren voordat je het kan verwijderen.",
        "exist": "{item} bestaat al.",
        "generic": "Weet je het zeker?",
        "home_assistant_is_restarting": "Een moment alstublieft, Home Assistant is aan het herstarten.",
        "home_assistant_version_not_correct": "Je gebruikt Home Assistant versie '{haversion}', echter deze repository vereist dat minimaal versie '{minversion}' is geïnstalleerd.",
        "no": "Nee",
        "no_upgrades": "Geen upgrades in afwachting.",
        "ok": "Oké",
        "overwrite": "Door dit te doen, wordt het overschreven.",
        "reload_data": "Dit zal alle bekende data herladen van alle repositories van HACS. Dit kan even duren",
        "restart_home_assistant": "Weet u zeker dat u Home Assistant opnieuw wilt starten?",
        "uninstall": "Weet u zeker dat u '{item}' wilt verwijderen?",
        "upgrade_all": "Hiermee worden al deze repositories geüpgraded. Zorg ervoor dat u de release-opmerkingen van allen heeft gelezen voordat u doorgaat.",
        "yes": "Ja"
      },
      "dialog_about": {
        "frontend_version": "Frontend versie",
        "installed_repositories": "Geïnstalleerde repositories",
        "integration_version": "Integratieversie",
        "useful_links": "Nuttige links"
      },
      "dialog_add_repo": {
        "limit": "Alleen de eerste 100 repositories worden getoond, gebruik de zoekopdracht om te filteren wat je nodig hebt",
        "no_match": "Er zijn geen repositories gevonden die overeenkomen met uw filter",
        "sort_by": "Sorteren op",
        "title": "Repository toevoegen"
      },
      "dialog_custom_repositories": {
        "category": "Categorie",
        "no_category": "Ontbrekende categorie",
        "no_repository": "Ontbrekende repository",
        "title": "Aangepaste repositories",
        "url_placeholder": "Voeg een aangepaste repository-URL toevoegen"
      },
      "dialog_info": {
        "author": "Auteur",
        "downloads": "Downloads",
        "install": "Installeer deze repository in HACS",
        "loading": "Informatie laden ...",
        "no_info": "De ontwikkelaar heeft geen verdere informatie verstrekt voor deze repository",
        "open_issues": "Openstaande problemen",
        "open_repo": "Open repository",
        "stars": "Sterren",
        "version_installed": "Versie geïnstalleerd"
      },
      "dialog_install": {
        "restart": "Onthoud dat u Home Assistant opnieuw moet opstarten voordat wijzigingen aan integraties (custom_components) worden toegepast.",
        "select_version": "Selecteer versie",
        "show_beta": "Bètaversies weergeven",
        "type": "Type",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Beschikbare versie",
        "changelog": "Changelog",
        "installed_version": "Geïnstalleerde versie",
        "releasenotes": "Releasenotes voor {release}",
        "title": "Update in behandeling"
      },
      "entry": {
        "information": "Informatie",
        "intro": "Updates en belangrijke berichten worden hier weergegeven als die er zijn",
        "messages": {
          "disabled": {
            "content": "Controleer uw logbestand voor meer details",
            "title": "HACS is uitgeschakeld"
          },
          "has_pending_tasks": {
            "content": "Sommige repositories worden mogelijk pas weergegeven als dit is voltooid",
            "title": "Achtergrondtaken in behandeling"
          },
          "resources": {
            "content": "Je hebt {number} Lovelace-elementen die niet correct zijn geladen in Lovelace.",
            "title": "Niet geladen in Lovelace"
          },
          "restart": {
            "content": "Je hebt {number} integraties waarvoor de Home Assistant opnieuw moet worden opgestart, je kunt dat doen via het gedeelte 'Serverbeheer' onder het configuratiegedeelte van de Home Assistant UI.",
            "title": "In afwachting van herstart"
          },
          "startup": {
            "content": "HACS is aan het opstarten, gedurende deze tijd kan er informatie ontbreken of onjuist zijn",
            "title": "HACS is aan het opstarten"
          },
          "wrong_frontend_installed": {
            "content": "Je hebt {running} van de HACS-frontend geïnstalleerd, maar versie {expected} werd verwacht. Als je dit bericht ziet, kon Home Assistant de nieuwe versie niet installeren, probeer dan Home Assistant opnieuw op te starten.",
            "title": "Onverwachte frontend-versie"
          },
          "wrong_frontend_loaded": {
            "content": "U gebruikt versie {running} van de HACS-frontend, maar versie {expected} werd verwacht, u moet uw browsercache wissen.",
            "title": "Onverwachte frontend-versie"
          }
        },
        "pending_updates": "In afwachting van updates"
      },
      "menu": {
        "about": "Over HACS",
        "clear": "Wis alle nieuwe",
        "custom_repositories": "Aangepaste repositories",
        "dismiss": "Sluit alle nieuwe repositories af",
        "documentation": "Documentatie",
        "open_issue": "Meld probleem",
        "reload": "Herlaad venster"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Zet AppDaemon apps ontdekken & traceren aan",
              "country": "Filter met land code.",
              "debug": "Schakel debug in.",
              "experimental": "Zet experimentele functies aan",
              "netdaemon": "Zet NetDaemon apps ontdekken & traceren aan",
              "not_in_use": "Niet in gebruik met YAML",
              "release_limit": "Aantal releases om te laten zien.",
              "sidepanel_icon": "Zijpaneel icoon",
              "sidepanel_title": "Zijpaneel titel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Deze integratie ondersteunt config_flow, wat betekent dat u via uw \"Instellingen\" naar \"Integraties\" kunt gaan om het te configureren.",
        "config_flow_title": "UI-configuratie ondersteund",
        "integration_not_loaded": "Deze integratie wordt niet geladen in Home Assistant.",
        "no_restart_required": "Geen herstart vereist",
        "not_loaded": "Niet geladen",
        "plugin_not_loaded": "Deze plugin wordt niet toegevoegd aan je Lovelace resources.",
        "restart": "U moet Home Assistant opnieuw starten.",
        "restart_pending": "Wachten op herstart"
      },
      "repository_card": {
        "dismiss": "afwijzen",
        "hide": "Verbergen",
        "information": "Informatie",
        "new_repository": "Nieuwe repository",
        "not_loaded": "Niet geladen",
        "open_issue": "Meld probleem",
        "open_source": "Open source",
        "pending_restart": "In afwachting van herstart",
        "pending_update": "In afwachting van update",
        "reinstall": "Herinstalleer",
        "report": "Rapport voor verwijdering",
        "update_information": "Update informatie"
      },
      "repository": {
        "add_to_lovelace": "Toevoegen aan Lovelace",
        "authors": "Auteurs",
        "available": "Beschikbaar",
        "back_to": "Terug naar",
        "changelog": "Changelog",
        "downloads": "Downloads",
        "flag_this": "Vlag dit",
        "frontend_version": "Frontend versie",
        "github_stars": "GitHub-sterren",
        "goto_integrations": "Ga naar integraties",
        "hide": "Verberg",
        "hide_beta": "Verberg beta",
        "install": "Installeer",
        "installed": "Geinstalleerd",
        "lovelace_copy_example": "Kopier het voorbeeld naar je klembord",
        "lovelace_instruction": "Wanneer je dit gaat toevoegen aan je lovelace configuratie gebruik dit",
        "lovelace_no_js_type": "Kon niet achterhalen welk type plugin dit is, controleer de repository van de plugin.",
        "newest": "nieuwste",
        "note_appdaemon": "je moet het nog steeds toevoegen aan je 'apps.yaml' bestand",
        "note_installed": "Wanneer geïnstalleerd, staat het in",
        "note_integration": "je moet het nog steeds toevoegen aan je 'configuration.yaml' bestand",
        "note_plugin": "je moet het nog steeds toevoegen aan je lovelace configuratie ('ui-lovelace.yaml') of raw UI config editor.",
        "note_plugin_post_107": "je moet het nog steeds toevoegen aan je lovelace configuratie ('configuration.yaml' of de resource editor '/config/lovelace/resources')",
        "open_issue": "Meld probleem",
        "open_plugin": "Open plugin",
        "reinstall": "Herinstalleer",
        "repository": "Repository",
        "restart_home_assistant": "Start Home Assistant opnieuw",
        "show_beta": "Laat beta zien",
        "uninstall": "Verwijder",
        "update_information": "Update informatie",
        "upgrade": "Update"
      },
      "search": {
        "placeholder": "Zoek naar repository"
      },
      "sections": {
        "about": {
          "description": "Toon informatie over HACS",
          "title": "Over"
        },
        "automation": {
          "description": "Hier vind je python_scripts, AppDaemon-apps en NetDaemon-apps",
          "title": "Automatisering"
        },
        "frontend": {
          "description": "Dit is waar je thema's, aangepaste kaarten en andere elementen voor lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Hier vindt u aangepaste integraties (custom_components)",
          "title": "Integraties"
        },
        "pending_repository_upgrade": "U gebruikt versie {installed} , versie {available} is beschikbaar"
      },
      "settings": {
        "add_custom_repository": "VOEG EIGEN REPOSITORY TOE",
        "adding_new_repo": "Nieuwe repository '{repo}' toevoegen",
        "adding_new_repo_category": "Met categorie '{category}'.",
        "bg_task_custom": "Aangepaste repositories zijn verborgen terwijl de achtergrondtaken actief zijn.",
        "category": "Categorie",
        "compact_mode": "Compacte modus",
        "custom_repositories": "EIGEN REPOSITORIES",
        "delete": "Verwijder",
        "display": "Weergave",
        "grid": "Rooster",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "verborgen repositories",
        "missing_category": "Je moet een categorie selecteren.",
        "open_repository": "Open repository",
        "reload_data": "Herlaad data",
        "reload_window": "Herlaad venster",
        "repository_configuration": "Repository configuratie",
        "save": "Opslaan",
        "table": "Tabel",
        "table_view": "Tabelweergave",
        "unhide": "zichtbaar maken",
        "upgrade_all": "Upgrade alles"
      },
      "store": {
        "ascending": "oplopend",
        "clear_new": "Wissen van alle nieuwe repositories",
        "descending": "Aflopend",
        "last_updated": "Laatste update",
        "name": "Naam",
        "new_repositories": "Nieuwe Repositories",
        "new_repositories_note": "Je hebt meer dan 10 nieuwe repositories die hier worden weergegeven, als je ze allemaal wilt wissen, klik dan op de 3 puntjes in de rechterbovenhoek en wijs ze af.",
        "no_repositories": "Geen repositories",
        "no_repositories_desc1": "Het lijkt erop dat je nog geen repositories hebt geïnstalleerd in deze sectie.",
        "no_repositories_desc2": "Klik op de + in de benedenhoek om je eerste toe te voegen!",
        "no_repositories_found_desc1": "Er zijn geen geïnstalleerde repositories die overeenkomen met {searchInput}\" in deze sectie.",
        "no_repositories_found_desc2": "Probeer iets anders te zoeken!",
        "pending_upgrades": "Upgrades in afwachting",
        "placeholder_search": "Typ iets om te zoeken...",
        "sort": "sorteer",
        "stars": "Sterren",
        "status": "Status"
      },
      "time": {
        "ago": "geleden",
        "day": "dag",
        "days": "dagen",
        "hour": "uur",
        "hours": "uren",
        "minute": "minuut",
        "minutes": "minuten",
        "month": "maand",
        "months": "maanden",
        "one": "Eén",
        "one_day_ago": "een dag geleden",
        "one_hour_ago": "een uur geleden",
        "one_minute_ago": "een minuut geleden",
        "one_month_ago": "een maand geleden",
        "one_second_ago": "een seconde geleden",
        "one_year_ago": "een jaar geleden",
        "second": "seconde",
        "seconds": "seconden",
        "x_days_ago": "{x} dagen geleden",
        "x_hours_ago": "{x} uur geleden",
        "x_minutes_ago": "{x} minuten geleden",
        "x_months_ago": "{x} maanden geleden",
        "x_seconds_ago": "{x} seconden geleden",
        "x_years_ago": "{x} jaar geleden",
        "year": "jaar",
        "years": "jaren"
      }
    },
    "nn": {
      "common": {
        "about": "Om",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDeamon-appar",
        "appdaemon_plural": "AppDaemon-appar",
        "background_task": "Bakgrunnsoppgåve køyrer. Denne sida kjem til å laste seg omatt når ho er ferdig.",
        "check_log_file": "Sjå i loggfila di for meir detaljar.",
        "continue": "Hald fram",
        "disabled": "Deaktivert",
        "documentation": "Dokumentasjon",
        "element": "element",
        "hacs_is_disabled": "HACS er deaktivert",
        "installed": "Installert",
        "integration": "Integrasjon",
        "integration_plural": "Integrasjonar",
        "integrations": "Integrasjonar",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace-element",
        "lovelace_elements": "Lovelace-element",
        "manage": "Handtere",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDeamon-appar",
        "netdaemon_plural": "NetDaemon-appar",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace-element",
        "plugins": "Lovelace-element",
        "python_script": "Pythonskript",
        "python_script_plural": "Pythonskript",
        "python_scripts": "Pythonskript",
        "repositories": "Repositories",
        "settings": "innstillingar",
        "theme": "Tema",
        "theme_plural": "Tema",
        "themes": "Tema",
        "version": "Versjon"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Berre éin enkelt konfigurasjon av HACS er tillete."
        },
        "error": {
          "auth": "Personleg tilgangsnøkkel er ikkje korrekt."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver AppDeamon-appar-oppdaging og sporing",
              "netdaemon": "Aktiver NetDeamon-appar-oppdaging og sporing",
              "python_script": "Aktiver pythonscript-oppdaging og sporing",
              "sidepanel_icon": "Sidepanelikon",
              "sidepanel_title": "Sidepaneltittel",
              "theme": "Aktiver tema-oppdaging og sporing",
              "token": "Personleg GitHub tilgangsnøkkel"
            },
            "description": "Dersom du treng hjelp med konfigurasjonen, ta ein kik her: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Er du sikker på at du vil legge til dette i Lovelace-ressursane dine?",
        "bg_task": "Handlinga er deaktivert medan bakgrunnsoppgåveene køyrer.",
        "cancel": "Avbryt",
        "continue": "Er du sikker på at du vil halde fram?",
        "delete": "Er du sikker på at du vil slette '{item}'?",
        "delete_installed": "'{item}' er installert. Du må avinstallere det før du kan slette det.",
        "exist": "{item} eksisterer allereie",
        "generic": "Er du sikker?",
        "home_assistant_is_restarting": "Vent... Home Assistant starter på nytt no.",
        "home_assistant_version_not_correct": "Du køyrer Home Assistant-versjonen '{haversion}', men dette kodedepoet krev minst versjon '{minversion}' for å bli installert.",
        "no": "Nei",
        "no_upgrades": "Ingen ventande oppgradringer",
        "ok": "OK",
        "overwrite": "Ved å gjere dette kjem du til å overskrive.",
        "reload_data": "Dette laster inn dataa til depota HACS veit om, og dette vil ta litt tid å fullføre.",
        "restart_home_assistant": "Er du sikker på at du vil starte Home Assistant på nytt?",
        "uninstall": "Er du sikker på at du vil avinstallere '{item}'?",
        "upgrade_all": "Dette kjem til å oppgradere alle depota. Ver sikker på at du har lest alle versjonsmerknadene før du held fram.",
        "yes": "Ja"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Aktiver AppDeamon-appar-oppdaging og sporing",
              "country": "Filterer med landskode",
              "debug": "Aktiver debug.",
              "experimental": "Aktiver ekspreimentelle funksjonar",
              "netdaemon": "Aktiver NetDeamon-appar-oppdaging og sporing",
              "not_in_use": "Kan ikkje brukast saman med YAML",
              "release_limit": "Talet på utgivingar",
              "sidepanel_icon": "Sidepanelikon",
              "sidepanel_title": "Sidepaneltittel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Denne integrasjonen støttar config_flow, som betyr at du no kan gå til integrasjonssida i brukargrensesnittet for å konfigurere den.",
        "config_flow_title": "UI-konfigurasjon støtta",
        "integration_not_loaded": "Integrasjonen er ikkje lasta inn i Home Assistant.",
        "no_restart_required": "Ingen omstart kravd",
        "not_loaded": "Ikkje lasta",
        "plugin_not_loaded": "Tillegget er ikkje lagt til i Lovelace-ressursane dine.",
        "restart": "Du må starte Home Assistant på nytt",
        "restart_pending": "Omstart venter"
      },
      "repository": {
        "add_to_lovelace": "Legg til i Lovelace",
        "authors": "Forfatter(e)",
        "available": "Tilgjengeleg",
        "back_to": "Tilbake til",
        "changelog": "Endre logg",
        "downloads": "Nedlastinger",
        "flag_this": "Marker dette",
        "frontend_version": "Frontend-versjon",
        "github_stars": "GitHub-stjerner",
        "goto_integrations": "Gå til integrasjonar",
        "hide": "Gøym",
        "hide_beta": "Gøym beta",
        "install": "Installer",
        "installed": "Installert",
        "lovelace_copy_example": "Kopier eksempelet til utklippsbreittet ditt",
        "lovelace_instruction": "Når du legg til dette i Lovelace-konfigurasjonen din, bruk dette",
        "lovelace_no_js_type": "Kunne ikkje slå fast typen til dette tilegget. Sjå i repositoryet.",
        "newest": "nyaste",
        "note_appdaemon": "du må framleis legge dette til i \"apps.yaml\"-fila di",
        "note_installed": "Når dette er installert, kjem den til å vere plassert i",
        "note_integration": "du må framleis legge dette til i \"configuration.yaml\"-fila di",
        "note_plugin": "du må framleis dette til i Lovelace-konfigurasjonen (\"ui-lovelace.yaml\" eller i rå-brukargrensesnittredigeraren",
        "note_plugin_post_107": "du må framleis legge dette til i lovelace-konfigurasjonen ('configuration.yaml' eller i kjelderedigeraren ''/config/lovelace/resources')",
        "open_issue": "Opne problem",
        "open_plugin": "Opne tillegg",
        "reinstall": "Installer på nytt",
        "repository": "Repository",
        "restart_home_assistant": "Start Home Assistant på nytt",
        "show_beta": "Vis beta",
        "uninstall": "Avinstaller",
        "update_information": "Oppdater informasjonen",
        "upgrade": "Oppdater"
      },
      "sections": {
        "about": {
          "description": "Vis informasjon om HACS",
          "title": "Om"
        },
        "automation": {
          "description": "Her finn du python_scripts, AppDaemon-appar og NetDaemon-appar",
          "title": "Automasjon"
        },
        "frontend": {
          "description": "Her finn du tema, eigendefinerte kort og andre element for lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Her finn du eigendefinerte ingtegrasjonar (custom_components)",
          "title": "Integrasjonar"
        },
        "pending_repository_upgrade": "Du køyrer versjon {installed}, og versjon {available} er tilgjengeleg"
      },
      "settings": {
        "add_custom_repository": "LEGG TIL EIN ANNAN REPOSITORY",
        "adding_new_repo": "Legger til ny repository '{repo}'",
        "adding_new_repo_category": "Med kategori '{category}'.",
        "bg_task_custom": "Custom repositories er skjult medan bakgrunnsoppgaver køyrer.",
        "category": "Kategori",
        "compact_mode": "Kompaktmodus",
        "custom_repositories": "VANLEG REPOSITORY",
        "delete": "Slett",
        "display": "Vis",
        "grid": "rutenett",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "gøymde repositories",
        "missing_category": "Du må velje éin kategori",
        "open_repository": "Opne repository",
        "reload_data": "Last om dataa",
        "reload_window": "Last inn vindauget på nytt",
        "repository_configuration": "Repository-konfigurasjon",
        "save": "Lagre",
        "table": "Tavle",
        "table_view": "Tabellvisning",
        "unhide": "vis",
        "upgrade_all": "Oppdater alle"
      },
      "store": {
        "ascending": "stigande",
        "clear_new": "Fjern alle nye repositories",
        "descending": "synkande",
        "last_updated": "Sist oppdatert",
        "name": "Namn",
        "new_repositories": "Ny repository",
        "pending_upgrades": "Ventande oppgraderinger",
        "placeholder_search": "Ver vennleg og skriv inn ei søkefrase",
        "sort": "Sorter",
        "stars": "Stjerner",
        "status": "Status"
      },
      "time": {
        "ago": "sidan",
        "day": "dag",
        "days": "dagar",
        "hour": "time",
        "hours": "timar",
        "minute": "minutt",
        "minutes": "minutt",
        "month": "månad",
        "months": "månadar",
        "one": "Éin",
        "one_day_ago": "for éin dag sidan",
        "one_hour_ago": "éin time sidan",
        "one_minute_ago": "eitt minutt sidan",
        "one_month_ago": "ein månad sidan",
        "one_second_ago": "eitt sekund sidan",
        "one_year_ago": "eitt år sidan",
        "second": "sekund",
        "seconds": "sekund",
        "x_days_ago": "{x} dagar siden",
        "x_hours_ago": "{x} timer sidan",
        "x_minutes_ago": "{x} minutt sidan",
        "x_months_ago": "{x} månadar sidan",
        "x_seconds_ago": "{x} sekund sidan",
        "x_years_ago": "{x} år sidan",
        "year": "år",
        "years": "år"
      }
    },
    "pl": {
      "common": {
        "about": "O",
        "add": "dodaj",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Aplikacje AppDaemon",
        "appdaemon_plural": "Aplikacje AppDaemon",
        "background_task": "Wykonywanie zadania w tle, ta strona zostanie odświeżona, gdy zadanie zostanie ukończone.",
        "check_log_file": "Sprawdź plik dziennika, aby uzyskać więcej informacji.",
        "continue": "Kontynuuj",
        "disabled": "Wyłączony",
        "documentation": "Dokumentacja",
        "element": "element",
        "hacs_is_disabled": "HACS jest wyłączony",
        "ignore": "Ignoruj",
        "install": "Zainstaluj",
        "installed": "zainstalowane",
        "integration": "Integracja",
        "integration_plural": "Integracje",
        "integrations": "Integracje",
        "lovelace": "Lovelace",
        "lovelace_element": "Element Lovelace",
        "lovelace_elements": "Elementy Lovelace",
        "manage": "zarządzaj",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Aplikacje NetDaemon",
        "netdaemon_plural": "Aplikacje NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Elementy Lovelace",
        "plugins": "Elementy Lovelace",
        "python_script": "Skrypt Python",
        "python_script_plural": "Skrypty języka Python",
        "python_scripts": "Skrypty Python",
        "repositories": "Repozytoria",
        "repository": "Repozytorium",
        "settings": "ustawienia",
        "theme": "Motyw",
        "theme_plural": "Motywy",
        "themes": "Motywy",
        "uninstall": "Odinstaluj",
        "update": "Uaktualnij",
        "version": "Wersja"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja HACS."
        },
        "error": {
          "auth": "Osobisty token dostępu jest nieprawidłowy."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Włącz wykrywanie i śledzenie aplikacji AppDaemon",
              "netdaemon": "Włącz wykrywanie i śledzenie aplikacji NetDaemon",
              "python_script": "Włącz wykrywanie i śledzenie skryptów Python",
              "sidepanel_icon": "Ikona w panelu bocznym",
              "sidepanel_title": "Tytuł w panelu bocznym",
              "theme": "Włącz wykrywanie i śledzenie motywów",
              "token": "Osobisty token dostępu GitHub"
            },
            "description": "Jeśli potrzebujesz pomocy w konfiguracji, przejdź na stronę: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Na pewno chcesz dodać to do swoich zasobów Lovelace?",
        "bg_task": "Akcja jest wyłączona podczas wykonywania zadań w tle.",
        "cancel": "Anuluj",
        "continue": "Na pewno chcesz kontynuować?",
        "delete": "Na pewno chcesz usunąć '{item}'?",
        "delete_installed": "'{item}' jest zainstalowany, musisz go odinstalować zanim będziesz mógł go usunąć.",
        "exist": "{item} już istnieje",
        "generic": "Jesteś pewny?",
        "home_assistant_is_restarting": "Poczekaj, Home Assistant jest teraz ponownie uruchamiany.",
        "home_assistant_version_not_correct": "Używasz Home Assistant'a w wersji '{haversion}', a to repozytorium wymaga wersji minimum '{minversion}'.",
        "no": "Nie",
        "no_upgrades": "Brak oczekujących aktualizacji",
        "ok": "Ok",
        "overwrite": "Spowoduje to zastąpienie istniejącej kopii.",
        "reload_data": "To przeładowuje dane wszystkich repozytoriów, o których wie HACS, może to trochę potrwać.",
        "restart_home_assistant": "Na pewno chcesz ponownie uruchomić Home Assistant'a?",
        "uninstall": "Na pewno chcesz odinstalować '{item}'?",
        "upgrade_all": "To uaktualni wszystkie te repozytoria, upewnij się, że przeczytałeś uwagi do wydania dla wszystkich z nich przed kontynuacją.",
        "yes": "Tak"
      },
      "dialog_about": {
        "frontend_version": "Wersja interfejsu użytkownika",
        "installed_repositories": "Zainstalowane repozytoria",
        "integration_version": "Wersja integracji",
        "useful_links": "Przydatne linki"
      },
      "dialog_add_repo": {
        "limit": "Wyświetlanych jest tylko pierwszych 100 repozytoriów, użyj wyszukiwania, aby przefiltrować potrzebne informacje",
        "no_match": "Nie znaleziono repozytoriów pasujących do filtra",
        "sort_by": "Sortuj według",
        "title": "Dodawanie repozytorium"
      },
      "dialog_custom_repositories": {
        "category": "Kategoria",
        "no_category": "Brak kategorii",
        "no_repository": "Brak repozytorium",
        "title": "Niestandardowe repozytoria",
        "url_placeholder": "Adres URL niestandardowego repozytorium"
      },
      "dialog_info": {
        "author": "Autor",
        "downloads": "Ilość pobrań",
        "install": "Zainstaluj to repozytorium w HACS",
        "loading": "Pobieranie informacji...",
        "no_info": "Deweloper nie dostarczył więcej informacji na temat tego repozytorium",
        "open_issues": "Problemy",
        "open_repo": "Otwórz repozytorium",
        "stars": "Gwiazdki",
        "version_installed": "Wersja zainstalowana"
      },
      "dialog_install": {
        "restart": "Pamiętaj, że musisz ponownie uruchomić Home Assistanta by zastosować zmiany w integracjach (custom_components).",
        "select_version": "Wybierz wersję",
        "show_beta": "Wyświetl wydania beta",
        "type": "Typ",
        "url": "URL"
      },
      "dialog_removed": {
        "link": "Link zewnętrzny do dodatkowych informacji",
        "name": "Nazwa repozytorium",
        "reason": "Powód usunięcia",
        "type": "Rodzaj usunięcia"
      },
      "dialog_update": {
        "available_version": "Dostępna wersja",
        "changelog": "Lista zmian",
        "installed_version": "Zainstalowana wersja",
        "releasenotes": "Informacje o {release}",
        "title": "Dostępna aktualizacja"
      },
      "entry": {
        "information": "Informacje",
        "intro": "Aktualizacje i ważne komunikaty będą wyświetlane w tym miejscu",
        "messages": {
          "disabled": {
            "content": "Sprawdź log, aby uzyskać więcej informacji",
            "title": "HACS jest wyłączony"
          },
          "has_pending_tasks": {
            "content": "Dopóki nie zostaną zakończone, niektóre repozytoria mogą nie być wyświetlane",
            "title": "Wykonywane są zadania w tle"
          },
          "removed": "Usunięto repozytorium '{repository}'",
          "resources": {
            "content": "Elementy Lovelace, które nie zostały poprawnie załadowane: {number}",
            "title": "Nie załadowano w Lovelace"
          },
          "restart": {
            "content": "Integracje, które wymagają ponownego uruchomienia Home Assistanta: {number}\nMożesz to zrobić w sekcji 'Kontrola serwera' konfiguracji Home Assistanta.",
            "title": "Oczekiwanie na restart"
          },
          "startup": {
            "content": "HACS uruchamia się, w tym czasie może brakować pewnych informacji lub mogą one być nieprawidłowe.",
            "title": "HACS uruchamia się"
          },
          "wrong_frontend_installed": {
            "content": "Masz zainstalowany interfejs HACS w wersji {running}, a wersja {expected} była oczekiwana. Komunikat ten oznacza, że Home Assistant nie mógł zainstalować nowej wersji interfejsu HACS, spróbuj ponownie uruchomić Home Assistanta.",
            "title": "Nieoczekiwana wersja interfejsu"
          },
          "wrong_frontend_loaded": {
            "content": "Używasz wersji {running} interfejsu HACS, a wersja {expected} była oczekiwana, powinieneś wyczyścić pamięć podręczną przeglądarki.",
            "title": "Nieoczekiwana wersja interfejsu"
          }
        },
        "pending_updates": "Oczekujące aktualizacje"
      },
      "menu": {
        "about": "O HACS",
        "clear": "Wyczyść oznaczenia nowych",
        "custom_repositories": "Niestandardowe repozytoria",
        "dismiss": "Odrzuć wszystkie nowe repozytoria",
        "documentation": "Dokumentacja",
        "open_issue": "Powiadom o problemie",
        "reload": "Załaduj ponownie okno"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Włącz wykrywanie i śledzenie aplikacji AppDaemon",
              "country": "Filtruj według kodu kraju",
              "debug": "Włącz debugowanie.",
              "experimental": "Włącz funkcje eksperymentalne",
              "netdaemon": "Włącz wykrywanie i śledzenie aplikacji NetDaemon",
              "not_in_use": "Nieużywany z YAML",
              "release_limit": "Liczba wydań do wyświetlenia",
              "sidepanel_icon": "Ikona w panelu bocznym",
              "sidepanel_title": "Tytuł w panelu bocznym"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Ta integracja obsługuje config_flow, co oznacza, że możesz teraz przejść do sekcji integracji w interfejsie użytkownika, aby ją skonfigurować.",
        "config_flow_title": "Obsługiwana konfiguracja poprzez interfejs użytkownika",
        "integration_not_loaded": "Ta integracja nie jest załadowana do Home Assistant'a.",
        "no_restart_required": "Ponowne uruchomienie nie jest wymagane",
        "not_loaded": "Nie załadowano",
        "plugin_not_loaded": "Ta wtyczka nie jest dodana do zasobów Lovelace.",
        "restart": "Musisz ponownie uruchomić Home Assistant'a.",
        "restart_pending": "Oczekiwanie na ponowne uruchomienie"
      },
      "repository_card": {
        "dismiss": "odrzuć",
        "hide": "Ukryj",
        "information": "Informacje",
        "new_repository": "Nowe repozytorium",
        "not_loaded": "Nie załadowano",
        "open_issue": "Powiadom o problemie",
        "open_source": "Otwórz kod źródłowy",
        "pending_restart": "Oczekiwanie na restart",
        "pending_update": "Oczekująca aktualizacja",
        "reinstall": "Przeinstaluj",
        "report": "Zgłoś do usunięcia",
        "update_information": "Uaktualnij dane"
      },
      "repository": {
        "add_to_lovelace": "Dodaj do Lovelace",
        "authors": "Autorzy",
        "available": "Dostępna",
        "back_to": "Wróć do",
        "changelog": "Lista zmian",
        "downloads": "Ilość pobrań",
        "flag_this": "Oflaguj",
        "frontend_version": "Wersja frontendu",
        "github_stars": "Gwiazdki GitHub",
        "goto_integrations": "Przejdź do integracji",
        "hide": "Ukryj",
        "hide_beta": "Ukryj wydania beta",
        "install": "Zainstaluj",
        "installed": "Zainstalowano",
        "lovelace_copy_example": "Skopiuj przykład do schowka",
        "lovelace_instruction": "Interfejs użytkownika użyje tej wtyczki po dodaniu konfiguracji",
        "lovelace_no_js_type": "Nie można określić typu tej wtyczki, sprawdź repozytorium.",
        "newest": "najnowsza",
        "note_appdaemon": "musisz jeszcze dodać aplikację do pliku 'apps.yaml'",
        "note_installed": "Po zainstalowaniu dodatek będzie znajdować się w",
        "note_integration": "musisz jeszcze dodać integrację do pliku 'configuration.yaml'",
        "note_plugin": "musisz jeszcze dodać wtyczkę do konfiguracji interfejsu użytkownika (plik 'ui-lovelace.yaml' lub edytor interfejsu użytkownika)",
        "note_plugin_post_107": "nadal musisz dodać go do konfiguracji Lovelace ('configuration.yaml' lub edytora zasobów '/config/lovelace/resources')",
        "open_issue": "Powiadom o problemie",
        "open_plugin": "Otwórz element",
        "reinstall": "Przeinstaluj",
        "repository": "Repozytorium",
        "restart_home_assistant": "Uruchom ponownie Home Assistant'a",
        "show_beta": "Wyświetl wydania beta",
        "uninstall": "Odinstaluj",
        "update_information": "Uaktualnij dane",
        "upgrade": "Uaktualnij"
      },
      "search": {
        "installed": "Wyszukaj zainstalowane repozytoria",
        "installed_new": "Wyszukaj zainstalowane lub nowe repozytoria",
        "placeholder": "Wyszukaj repozytorium"
      },
      "sections": {
        "about": {
          "description": "Informacje o HACS",
          "title": "O HACS"
        },
        "addon": {
          "description": "W HACS nie ma dodatków, ale możesz kliknąć tutaj, aby przejść do Supervisora",
          "title": "Dodatki"
        },
        "automation": {
          "description": "Skrypty Pythona, aplikacje AppDaemon i NetDaemon",
          "title": "Automatyzacje"
        },
        "frontend": {
          "description": "Motywy, niestandardowe karty i inne elementy interfejsu użytkownika",
          "title": "Interfejs użytkownika"
        },
        "integrations": {
          "description": "Niestandardowe integracje (custom_components)",
          "title": "Integracje"
        },
        "pending_repository_upgrade": "Używasz wersji {installed}, wersja {available} jest dostępna"
      },
      "settings": {
        "add_custom_repository": "DODAJ REPOZYTORIUM NIESTANDARDOWE",
        "adding_new_repo": "Dodawanie nowego repozytorium '{repo}'",
        "adding_new_repo_category": "Z kategorią '{category}'.",
        "bg_task_custom": "Niestandardowe repozytoria są ukryte podczas wykonywania zadań w tle.",
        "category": "Kategoria",
        "compact_mode": "Tryb kompaktowy",
        "custom_repositories": "REPOZYTORIA NIESTANDARDOWE",
        "delete": "Usuń",
        "display": "Sposób wyświetlania",
        "grid": "Siatka",
        "hacs_repo": "Repozytorium HACS",
        "hidden_repositories": "ukryte repozytoria",
        "missing_category": "Musisz wybrać kategorię",
        "open_repository": "Otwórz repozytorium",
        "reload_data": "Wczytaj ponownie dane",
        "reload_window": "Załaduj ponownie okno",
        "repository_configuration": "Konfiguracja repozytorium",
        "save": "Zapisz",
        "table": "tabela",
        "table_view": "Widok tabeli",
        "unhide": "pokaż",
        "upgrade_all": "Uaktualnij wszystkie"
      },
      "store": {
        "ascending": "rosnąco",
        "clear_new": "Wyczyść wszystkie nowe repozytoria",
        "descending": "malejąco",
        "last_updated": "Ostatnia aktualizacja",
        "name": "Nazwa",
        "new_repositories": "Nowe repozytoria",
        "new_repositories_note": "Jest ponad 10 nowych repozytoriów, jeśli chcesz je wyczyścić, kliknij menu z trzema kropkami w prawym górnym rogu i odrzuć je wszystkie.",
        "no_repositories": "Brak repozytoriów",
        "no_repositories_desc1": "Wygląda na to, że nie masz jeszcze zainstalowanych repozytoriów w tej sekcji.",
        "no_repositories_desc2": "Kliknij + w dolnym rogu, aby dodać pierwsze!",
        "no_repositories_found_desc1": "W tej sekcji nie znaleziono zainstalowanych repozytoriów pasujących do \"{searchInput}\".",
        "no_repositories_found_desc2": "Spróbuj wyszukać czegoś innego!",
        "pending_upgrades": "Oczekujące aktualizacje",
        "placeholder_search": "Wprowadź wyszukiwane hasło...",
        "sort": "sortowanie",
        "stars": "Gwiazdki",
        "status": "Status"
      },
      "time": {
        "ago": "temu",
        "day": "dzień",
        "days": "dni",
        "hour": "godzina",
        "hours": "godziny",
        "minute": "minuta",
        "minutes": "minuty",
        "month": "miesiąc",
        "months": "miesięcy",
        "one": "Jeden",
        "one_day_ago": "jeden dzień temu",
        "one_hour_ago": "jedna godzina temu",
        "one_minute_ago": "jedna minuta temu",
        "one_month_ago": "jeden miesiąc temu",
        "one_second_ago": "jedna sekunda temu",
        "one_year_ago": "jeden rok temu",
        "second": "sekunda",
        "seconds": "sekundy",
        "x_days_ago": "{x} dni temu",
        "x_hours_ago": "{x} godzin(y) temu",
        "x_minutes_ago": "{x} minut(y) temu",
        "x_months_ago": "{x} miesi(ące/ęcy) temu",
        "x_seconds_ago": "{x} sekund(y) temu",
        "x_years_ago": "{x} lat(a) temu",
        "year": "rok",
        "years": "lata"
      }
    },
    "pt-BR": {
      "common": {
        "about": "Sobre",
        "add": "adicionar",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Aplicativos AppDaemon",
        "appdaemon_plural": "Aplicativos AppDaemon",
        "background_task": "Tarefa em segundo plano em execução, esta página será recarregada quando terminar.",
        "check_log_file": "Verifique seu arquivo de log para obter mais detalhes.",
        "continue": "Continuar",
        "disabled": "Desativado",
        "documentation": "Documentação",
        "element": "elemento",
        "hacs_is_disabled": "HACS está desativado",
        "install": "Instalar",
        "installed": "instalado",
        "integration": "Integração",
        "integration_plural": "Integrações",
        "integrations": "Integrações",
        "lovelace": "Lovelace",
        "lovelace_element": "Elemento do Lovelace",
        "lovelace_elements": "Elementos do lovelace",
        "manage": "gerenciar",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Apps",
        "netdaemon_plural": "Aplicativos NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Elementos do Lovelace",
        "plugins": "Elementos do Lovelace",
        "python_script": "Script Python",
        "python_script_plural": "Scripts python",
        "python_scripts": "Scripts python",
        "repositories": "Repositórios",
        "repository": "Repositório",
        "settings": "configurações",
        "theme": "Tema",
        "theme_plural": "Temas",
        "themes": "Temas",
        "uninstall": "Desinstalar",
        "update": "Atualizar",
        "version": "Versão"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Apenas uma configuração do HACS é permitida."
        },
        "error": {
          "auth": "Token de acesso pessoal incorreto."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Habilitar descoberta & rastreamento de apps AppDaemon",
              "netdaemon": "Habilitar NetDaemon apps descoberta & rastreamento",
              "python_script": "Habilitar descoberta & rastreamento de python_scripts",
              "sidepanel_icon": "Icone do painel lateral",
              "sidepanel_title": "Titulo do painel lateral",
              "theme": "Habilitar descoberta & rastreamento de temas",
              "token": "Token de acesso pessoal do GitHub"
            },
            "description": "Se você preciar de ajuda com a configuração olhe aqui: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Tem certeza de que deseja adicionar isso aos seus recursos do Lovelace?",
        "bg_task": "A ação é desativada enquanto as tarefas de fundo estão sendo executadas.",
        "cancel": "Cancelar",
        "continue": "Tem certeza que quer continuar?",
        "delete": "Tem certeza de que deseja excluir '{item}'?",
        "delete_installed": "'{item}' está instalado, é necessário desinstalá-lo para poder excluí-lo.",
        "exist": "{item} já existe",
        "generic": "Tem certeza?",
        "home_assistant_is_restarting": "Espere, o Home Assistant está agora a reiniciar.",
        "home_assistant_version_not_correct": "Você está executando a versão Home Assistant '{haversion}', mas este repositório requer que a versão mínima '{minversion}' esteja instalada.",
        "no": "Não",
        "no_upgrades": "Não há atualizações pendentes",
        "ok": "OK",
        "overwrite": "Fazer isso irá substituí-lo.",
        "reload_data": "Isso recarrega os dados de todos os repositórios que o HACS conhece e levará algum tempo para concluir.",
        "restart_home_assistant": "Tem certeza de que deseja reiniciar o Home Assistant?",
        "uninstall": "Tem certeza de que deseja desinstalar '{item}'?",
        "upgrade_all": "Isso atualizará todos esses repositórios, verifique se você leu as notas de versão de todos eles antes de continuar.",
        "yes": "Sim"
      },
      "dialog_about": {
        "frontend_version": "Versão do frontend",
        "installed_repositories": "Repositórios instalados",
        "integration_version": "Versão da integração",
        "useful_links": "Links úteis"
      },
      "dialog_add_repo": {
        "limit": "Apenas os 100 primeiros repositórios são mostrados, use a pesquisa para filtrar o que você precisa",
        "no_match": "Nenhum repositório encontrado correspondente ao seu filtro",
        "sort_by": "Ordenar por",
        "title": "Novo repositório"
      },
      "dialog_custom_repositories": {
        "category": "Categoria",
        "no_category": "Categoria ausente",
        "no_repository": "Repositório ausente",
        "title": "Repositórios personalizados",
        "url_placeholder": "Adicionar URL de repositório personalizado"
      },
      "dialog_info": {
        "author": "Autor",
        "downloads": "Downloads",
        "install": "Instalar esse repositório no HACS",
        "loading": "Carregando informações...",
        "no_info": "O desenvolvedor não forneceu mais informações para este repositório",
        "open_issues": "Problemas em aberto",
        "open_repo": "Abrir repositório",
        "stars": "Estrelas",
        "version_installed": "Versão instalada"
      },
      "dialog_install": {
        "restart": "Lembre-se de que você precisa reiniciar o Home Assistant para que as alterações nas integrações (custom_components) sejam aplicadas.",
        "select_version": "Selecionar versão",
        "show_beta": "Mostrar versões beta",
        "type": "Tipo",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Versão disponível",
        "changelog": "Registro de mudanças",
        "installed_version": "Versão instalada",
        "releasenotes": "Notas de lançamento para {release}",
        "title": "Atualização pendente"
      },
      "entry": {
        "information": "Informações",
        "intro": "Atualizações e mensagens importantes serão mostradas aqui se houver",
        "messages": {
          "disabled": {
            "content": "Verifique seu arquivo de log para mais detalhes.",
            "title": "O HACS está desativado"
          },
          "has_pending_tasks": {
            "content": "Alguns repositórios podem não aparecer até que isso seja concluído",
            "title": "Tarefas em segundo plano pendentes"
          },
          "resources": {
            "content": "Tem {número} elementos do Lovelace que não são carregados corretamente no Lovelace.",
            "title": "Não carregado em Lovelace"
          },
          "restart": {
            "content": "Tem {número} integrações que requerem um reinício do Home Assistant. Você pode fazer isso na seção 'Controles do Servidor' na parte de configuração do Home Assistant UI.",
            "title": "Reinicialização pendente"
          },
          "startup": {
            "content": "O HACS está sendo iniciado, durante esse período algumas informações podem estar ausentes ou incorretas",
            "title": "O HACS está iniciando"
          },
          "wrong_frontend_installed": {
            "content": "Você instalou a versão {running} do frontend HACS, mas a versão {expected} era esperada. Se você vir esta mensagem, o Home Assistant não conseguiu instalar a nova versão. Por favor tente reiniciar o Home Assistant.",
            "title": "Versão frontend inesperada"
          },
          "wrong_frontend_loaded": {
            "content": "Você está executando a versão {running} do frontend HACS, mas a versão {expected} era esperada. Por favor limpe o cache do seu navegador.",
            "title": "Versão frontend inesperada"
          }
        },
        "pending_updates": "Atualizações pendentes"
      },
      "menu": {
        "about": "Sobre o HACS",
        "clear": "Limpar todos os novos",
        "custom_repositories": "Repositórios personalizados",
        "dismiss": "Limpar todos os novos repositórios",
        "documentation": "Documentação",
        "open_issue": "Relatar problema",
        "reload": "Recarregar janela"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Habilitar AppDaemon apps descoberta & rastreamento",
              "country": "Filtrar pelo código do país.",
              "debug": "Ative a depuração.",
              "experimental": "Ativar recursos experimentais",
              "netdaemon": "Habilitar NetDaemon apps descoberta & rastreamento",
              "not_in_use": "Não está em uso com o YAML",
              "release_limit": "Número de lançamentos a serem exibidos.",
              "sidepanel_icon": "Icone do painel lateral",
              "sidepanel_title": "Titulo do painel lateral"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Essa integração oferece suporte ao config_flow, o que significa que agora você pode acessar a seção de integração da sua interface do usuário para configurá-lo.",
        "config_flow_title": "Configuração de interface do usuário suportada",
        "integration_not_loaded": "Esta integração não está carregada no Home Assistant.",
        "no_restart_required": "Não é necessário reiniciar",
        "not_loaded": "Não carregado",
        "plugin_not_loaded": "Este elemento não é adicionado aos seus recursos do Lovelace.",
        "restart": "Você precisa reiniciar o Home Assistant.",
        "restart_pending": "Reinicialização pendente"
      },
      "repository_card": {
        "dismiss": "Dispensar",
        "hide": "Esconder",
        "information": "Informações",
        "new_repository": "Novo repositório",
        "not_loaded": "Não carregado",
        "open_issue": "Relatar problema",
        "open_source": "Código aberto",
        "pending_restart": "Reinicialização pendente",
        "pending_update": "Atualização pendente",
        "reinstall": "Reinstalar",
        "report": "Denunciar para remoção",
        "update_information": "Atualizar informações"
      },
      "repository": {
        "add_to_lovelace": "Adicionar a Lovelace",
        "authors": "Autores",
        "available": "Disponível",
        "back_to": "Voltar para",
        "changelog": "Changelog",
        "downloads": "Downloads",
        "flag_this": "Sinalizar isso",
        "frontend_version": "Versão Frontend",
        "github_stars": "Estrelas de GitHub",
        "goto_integrations": "Ir para integrações",
        "hide": "Esconder",
        "hide_beta": "Esconder beta",
        "install": "Instalar",
        "installed": "Instalado",
        "lovelace_copy_example": "Copie este exemplo para seu clipboard",
        "lovelace_instruction": "Quando você adicionar isso à sua configuração do lovelace, use este",
        "lovelace_no_js_type": "Não foi possível determinar o tipo desse elemento, verifique o repositório.",
        "newest": "O mais novo",
        "note_appdaemon": "Você ainda precisa adicioná-lo ao seu arquivo 'apps.yaml'",
        "note_installed": "Quando instalado, ele estará localizado em",
        "note_integration": "Você ainda precisa adicioná-lo ao seu arquivo 'configuration.yaml'",
        "note_plugin": "você ainda precisará adicioná-lo à sua configuração do lovelace ('ui-lovelace.yaml' ou o editor de configuração da interface do usuário)",
        "note_plugin_post_107": "você ainda precisará adicioná-lo à sua configuração do lovelace ('configuration.yaml' ou o editor de recursos '/config /lovelace/resources')",
        "open_issue": "Open issue",
        "open_plugin": "Elemento aberto",
        "reinstall": "Reinstalar",
        "repository": "Repositório",
        "restart_home_assistant": "Reiniciar Home Assistant",
        "show_beta": "Mostrar beta",
        "uninstall": "Desinstalar",
        "update_information": "Atualizar informação",
        "upgrade": "Melhorar"
      },
      "search": {
        "installed": "Procure repositórios instalados",
        "installed_new": "Procure repositórios instalados ou novos",
        "placeholder": "Procurar repositório"
      },
      "sections": {
        "about": {
          "description": "Exibir informações sobre o HACS",
          "title": "Sobre"
        },
        "automation": {
          "description": "É aqui que você encontra python_scripts, aplicativos AppDaemon e aplicativos NetDaemon",
          "title": "Automação"
        },
        "frontend": {
          "description": "É aqui que você encontra temas, cartões personalizados e outros elementos para o lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "É aqui que você encontra integrações personalizadas (custom_components)",
          "title": "Integrações"
        },
        "pending_repository_upgrade": "Você está executando a versão {installed}, a versão {available} está disponível"
      },
      "settings": {
        "add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO",
        "adding_new_repo": "Adicionando novo repositório '{repo}'",
        "adding_new_repo_category": "Com a categoria '{category}'.",
        "bg_task_custom": "Os repositórios personalizados ficam ocultos enquanto as tarefas de fundo estão em execução.",
        "category": "Categoria",
        "compact_mode": "Modo compacto",
        "custom_repositories": "REPOSITÓRIOS PERSONALIZADOS",
        "delete": "Deletar",
        "display": "Display",
        "grid": "Grade",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "repositórios ocultos",
        "missing_category": "Você precisa selecionar uma categoria",
        "open_repository": "Repositório aberto",
        "reload_data": "Recarregar dados",
        "reload_window": "Recarregar janela",
        "repository_configuration": "Configuração do Repositório",
        "save": "Salvar",
        "table": "Tabela",
        "table_view": "Vista de mesa",
        "unhide": "reexibir",
        "upgrade_all": "Atualizar tudo"
      },
      "store": {
        "ascending": "ascendente",
        "clear_new": "Limpar todos os novos repositórios",
        "descending": "descendente",
        "last_updated": "Última atualização",
        "name": "Nome",
        "new_repositories": "Novos Repositórios",
        "new_repositories_note": "Você tem mais de 10 novos repositórios sendo mostrados aqui, se quiser limpar todos eles clique nos 3 pontos no canto superior direito e dispense-os.",
        "no_repositories": "Nenhum repositório",
        "no_repositories_desc1": "Parece que você ainda não tem nenhum repositório instalado nesta seção.",
        "no_repositories_desc2": "Clique no + no canto inferior para adicionar o seu primeiro repositório!",
        "no_repositories_found_desc1": "Nenhum repositório instalado foi encontrado que corresponda a \"{searchInput}\" nesta seção.",
        "no_repositories_found_desc2": "Tente procurar por outra coisa!",
        "pending_upgrades": "Atualizações pendentes",
        "placeholder_search": "Por favor insira um termo de pesquisa...",
        "sort": "ordenar",
        "stars": "Estrelas",
        "status": "Status"
      },
      "time": {
        "ago": "atrás",
        "day": "dia",
        "days": "dias",
        "hour": "hora",
        "hours": "horas",
        "minute": "minuto",
        "minutes": "minutos",
        "month": "mês",
        "months": "meses",
        "one": "Um",
        "one_day_ago": "um dia atrás",
        "one_hour_ago": "uma hora atrás",
        "one_minute_ago": "um minuto atrás",
        "one_month_ago": "um mês atrás",
        "one_second_ago": "um segundo atrás",
        "one_year_ago": "um ano atrás",
        "second": "segundo",
        "seconds": "segundos",
        "x_days_ago": "{x} dias atrás",
        "x_hours_ago": "{x} horas atrás",
        "x_minutes_ago": "{x} minutos atrás",
        "x_months_ago": "{x} meses atrás",
        "x_seconds_ago": "{x} segundos atrás",
        "x_years_ago": "{x} anos atrás",
        "year": "ano",
        "years": "anos"
      }
    },
    "pt": {
      "common": {
        "about": "Sobre",
        "add": "adicionar",
        "appdaemon_apps": "Aplicações AppDaemon",
        "appdaemon_plural": "Aplicações AppDaemon",
        "background_task": "Está a ser executada uma tarefa em segundo plano, esta página será recarregada quando terminar.",
        "check_log_file": "Verifique o seu ficheiro de log para obter mais detalhes.",
        "continue": "Continuar",
        "disabled": "Desativado",
        "documentation": "Documentação",
        "element": "elemento",
        "hacs_is_disabled": "HACS está desativado",
        "ignore": "Ignorar",
        "install": "Instalar",
        "installed": "instalado",
        "integration": "Integração",
        "integration_plural": "Integrações",
        "integrations": "Integrações",
        "lovelace": "Lovelace",
        "lovelace_element": "Elemento Lovelace",
        "lovelace_elements": "Elementos Lovelace",
        "manage": "gerir",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Aplicações NetDaemon",
        "netdaemon_plural": "Aplicações NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Elementos Lovelace",
        "plugins": "Elementos Lovelace",
        "python_script": "Script Python",
        "python_script_plural": "Scripts Python",
        "python_scripts": "Scripts Python",
        "repositories": "Repositórios",
        "repository": "Repositório",
        "settings": "configurações",
        "theme": "Tema",
        "theme_plural": "Temas",
        "themes": "Temas",
        "uninstall": "Desinstalar",
        "update": "Atualizar",
        "version": "Versão"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Apenas é permitida uma única configuração do HACS."
        },
        "error": {
          "auth": "O token de acesso pessoal não está correto."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Ativar a localização e o seguimento de aplicações AppDaemon",
              "netdaemon": "Ativar a localização e o seguimento de aplicações NetDaemon",
              "python_script": "Ativar a localização e o seguimento de python_scripts",
              "sidepanel_icon": "Ícone no painel lateral",
              "sidepanel_title": "Título no painel lateral",
              "theme": "Ativar a localização e o seguimento de temas",
              "token": "Token de acesso pessoal do GitHub"
            },
            "description": "Se precisar de ajuda com a configuração, consulte aqui: https://hacs.xyz/docs/configuration/start"
          }
        }
      },
      "confirm": {
        "add_to_lovelace": "Tem certeza que deseja adicionar aos recursos do Lovelace?",
        "bg_task": "Esta ação é desativada enquanto as tarefas em segundo plano estão a ser executadas.",
        "cancel": "Cancelar",
        "continue": "Tem a certeza que deseja continuar?",
        "delete": "Tem a certeza que deseja apagar '{item}'?",
        "delete_installed": "'{item}' está instalado, é preciso desinstalá-lo antes de o poder apagar.",
        "exist": "{item} já existe",
        "generic": "Tem a certeza?",
        "home_assistant_is_restarting": "Aguarde, o Home Assistant está a reiniciar.",
        "home_assistant_version_not_correct": "Está a executar a versão '{haversion}' do Home Assistant, mas este repositório requer a versão mínima '{minversion}' para ser instalado.",
        "no": "Não",
        "no_upgrades": "Não há atualizações pendentes",
        "ok": "OK",
        "overwrite": "Ao fazer esta ação irá substituir o ficheiro atual.",
        "reload_data": "Isto irá recarregar os dados de todos os repositórios que a HACS conhece, irá demorar algum tempo até terminar.",
        "restart_home_assistant": "Tem a certeza que deseja reiniciar o Home Assistant?",
        "uninstall": "Tem a certeza que deseja desinstalar '{item}'?",
        "upgrade_all": "Isto irá actualizar todos estes repositórios, certifique-se de que leu as notas de lançamento de todos antes de continuar.",
        "yes": "Sim"
      },
      "dialog_about": {
        "frontend_version": "Versão Frontend",
        "installed_repositories": "Repositórios instalados",
        "integration_version": "Versão de integração",
        "useful_links": "Links úteis"
      },
      "dialog_add_repo": {
        "limit": "Apenas os 100 primeiros repositórios serão mostrados, use a pesquisa para filtrar o que precisa",
        "no_match": "Não foram encontrados repositórios que correspondam ao filtro",
        "sort_by": "Ordenar por",
        "title": "Adicionar repositório"
      },
      "dialog_custom_repositories": {
        "category": "Categoria",
        "no_category": "Categoria em falta",
        "no_repository": "Repositório em falta",
        "title": "Repositórios personalizados",
        "url_placeholder": "Adicionar URL do repositório personalizado"
      },
      "dialog_info": {
        "author": "Autor",
        "downloads": "Transferências",
        "install": "Instalar este repositório no HACS",
        "loading": "A carregar informações...",
        "no_info": "O developer não forneceu mais informações sobre este repositório",
        "open_issues": "Questões em aberto",
        "open_repo": "Abrir Repositório",
        "stars": "Estrelas",
        "version_installed": "Versão instalada"
      },
      "dialog_install": {
        "restart": "Lembre-se de que é preciso reiniciar o Home Assistant para que as alterações das integrações (custom_components) sejam aplicadas.",
        "select_version": "Selecionar versão",
        "show_beta": "Mostrar versões beta",
        "type": "Tipo",
        "url": "URL"
      },
      "dialog_removed": {
        "link": "Link externo para mais informações",
        "name": "Nome do repositório",
        "reason": "Motivo de remoção",
        "type": "Tipo de remoção"
      },
      "dialog_update": {
        "available_version": "Versão disponível",
        "changelog": "Changelog",
        "installed_version": "Versão instalada",
        "releasenotes": "Notas de lançamento para {release}",
        "title": "Atualização pendente"
      },
      "entry": {
        "information": "Informações",
        "intro": "Atualizações e mensagens importantes serão mostradas aqui",
        "messages": {
          "disabled": {
            "content": "Verifique o seu ficheiro de log para obter mais detalhes",
            "title": "HACS está desativado"
          },
          "has_pending_tasks": {
            "content": "Alguns repositórios podem não aparecer até que isso seja concluído",
            "title": "Tarefas em segundo plano pendentes"
          },
          "removed": "Repositório '{repository}' removido",
          "resources": {
            "content": "Tem {number} elementos que não são carregados corretamente em Lovelace.",
            "title": "Não carregado em Lovelace"
          },
          "restart": {
            "content": "Tem {number} integrações que exigem uma reinicialização do Home Assistant, pode fazer isso a partir da secção 'Controlo do Servidor' na parte de configuração do Home Assistant.",
            "title": "Reinicialização pendente"
          },
          "startup": {
            "content": "O HACS está a iniciar. Durante este tempo, algumas informações podem estar ausentes ou incorretas",
            "title": "O HACS está a iniciar"
          },
          "wrong_frontend_installed": {
            "content": "Tem a versão {running} do frontend HACS instalado, mas a versão {expected} é a mais atualizada, se está a ver esta mensagem e o Home Assistant não foi capaz de instalar a nova versão, tente reiniciar o Home Assistant.",
            "title": "Versão do frontend inesperada"
          },
          "wrong_frontend_loaded": {
            "content": "Está a executar a versão {running} do frontend HACS, mas a versão {expected} é a mais atualizada, deve limpar a cache do seu browser.",
            "title": "Versão do frontend inesperada"
          }
        },
        "pending_updates": "Atualizações pendentes"
      },
      "menu": {
        "about": "Sobre o HACS",
        "clear": "Limpar todos os recentes",
        "custom_repositories": "Repositórios personalizados",
        "dismiss": "Dispensar todos os novos repositórios",
        "documentation": "Documentação",
        "open_issue": "Questão em aberto",
        "reload": "Recarregar janela"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Ativar a localização e o seguimento de aplicações AppDaemon",
              "country": "Filtrar com o código do país.",
              "debug": "Ativar depuração.",
              "experimental": "Ativar recursos experimentais",
              "netdaemon": "Ativar a localização e o seguimento de aplicações NetDaemon",
              "not_in_use": "Não está a ser usado com YAML",
              "release_limit": "Número de lançamentos a mostrar.",
              "sidepanel_icon": "Ícone no painel lateral",
              "sidepanel_title": "Título no painel lateral"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Esta integração suporta o config_flow, o que significa que agora pode ir para a seção de integração do IU para a configurar.",
        "config_flow_title": "Configuração UI suportada",
        "integration_not_loaded": "Esta integração não foi carregada no Home Assistant.",
        "no_restart_required": "Não é necessário reiniciar",
        "not_loaded": "Não carregado",
        "plugin_not_loaded": "Este elemento não é adicionado aos recursos Lovelace.",
        "restart": "É necessário reiniciar o Home Assistant.",
        "restart_pending": "Reinício pendente"
      },
      "repository_card": {
        "dismiss": "dispensar",
        "hide": "Esconder",
        "information": "Informações",
        "new_repository": "Novo repositório",
        "not_loaded": "Não carregado",
        "open_issue": "Questão em aberto",
        "open_source": "Código aberto",
        "pending_restart": "Reinicialização pendente",
        "pending_update": "Atualização pendente",
        "reinstall": "Reinstalar",
        "report": "Motivo para remover",
        "update_information": "Atualizar informações"
      },
      "repository": {
        "add_to_lovelace": "Adicionar ao Lovelace",
        "authors": "Autores",
        "available": "Disponível",
        "back_to": "Voltar",
        "changelog": "Histórico de alterações",
        "downloads": "Transferências",
        "flag_this": "Marcar isto",
        "frontend_version": "Versão Frontend",
        "github_stars": "Estrelas do GitHub",
        "goto_integrations": "Ir para as integrações",
        "hide": "Ocultar",
        "hide_beta": "Ocultar beta",
        "install": "Instalar",
        "installed": "Instalado",
        "lovelace_copy_example": "Copiar o exemplo para a área de transferência",
        "lovelace_instruction": "Quando for adicionar à sua configuração do lovelace, use este",
        "lovelace_no_js_type": "Não foi possível determinar o tipo do elemento, verifique o repositório.",
        "newest": "mais recente",
        "note_appdaemon": "ainda é preciso adicioná-lo ao ficheiro 'apps.yaml'",
        "note_installed": "Quando estiver instalado, a localização será",
        "note_integration": "ainda é preciso adicioná-lo ao ficheiro 'configuration.yaml'",
        "note_plugin": "ainda é preciso adicioná-lo à configuração do lovelace ('ui-lovelace.yaml' ou o editor de configuração de UI)",
        "note_plugin_post_107": "ainda é preciso adicioná-lo à configuração do lovelace ('configuration.yaml' ou no editor '/config/lovelace/resources')",
        "open_issue": "Questão em aberto",
        "open_plugin": "Abrir elemento",
        "reinstall": "Reinstalar",
        "repository": "Repositório",
        "restart_home_assistant": "Reiniciar o Home Assistant",
        "show_beta": "Mostrar beta",
        "uninstall": "Desinstalar",
        "update_information": "Atualizar informações",
        "upgrade": "Atualizar"
      },
      "search": {
        "installed": "Pesquisar repositórios instalados",
        "installed_new": "Pesquisar novos repositórios ou instalados",
        "placeholder": "Procurar repositório"
      },
      "sections": {
        "about": {
          "description": "Mostrar informações sobre o HACS",
          "title": "Sobre"
        },
        "automation": {
          "description": "É aqui que encontra os python_scripts, aplicações AppDaemon e NetDaemon",
          "title": "Automação"
        },
        "frontend": {
          "description": "É aqui que encontra temas, cartões personalizados e outros elementos para o lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "É aqui que encontra integrações personalizadas (custom_components)",
          "title": "Integrações"
        },
        "pending_repository_upgrade": "Está a executar a versão {installed} , mas a versão {available} já está disponível"
      },
      "settings": {
        "add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO",
        "adding_new_repo": "Adicionando novo repositório '{repo}'",
        "adding_new_repo_category": "Com a categoria '{category}'.",
        "bg_task_custom": "Repositórios personalizados ficam ocultos, enquanto as tarefas em segundo plano estão a ser executadas.",
        "category": "Categoria",
        "compact_mode": "Modo compacto",
        "custom_repositories": "REPOSITÓRIOS PERSONALIZADOS",
        "delete": "Eliminar",
        "display": "Mostrar",
        "grid": "Grade",
        "hacs_repo": "Repositório HACS",
        "hidden_repositories": "repositórios ocultos",
        "missing_category": "É preciso selecionar uma categoria",
        "open_repository": "Abrir Repositório",
        "reload_data": "Recarregar dados",
        "reload_window": "Recarregar janela",
        "repository_configuration": "Configuração do Repositório",
        "save": "Guardar",
        "table": "Tabela",
        "table_view": "Vista de tabela",
        "unhide": "Mostrar",
        "upgrade_all": "Atualizar tudo"
      },
      "store": {
        "ascending": "ascendente",
        "clear_new": "Limpar todos os novos repositórios",
        "descending": "descendente",
        "last_updated": "Última atualização",
        "name": "Nome",
        "new_repositories": "Novos Repositórios",
        "new_repositories_note": "Tem mais de 10 novos repositórios visiveis aqui, se quiser limpar todos clique nos 3 pontos no canto superior direito e dispense todos.",
        "no_repositories": "Sem repositórios",
        "no_repositories_desc1": "Parece que ainda não possui nenhum repositório instalado nesta seção.",
        "no_repositories_desc2": "Clique no \"+\", no canto inferior para adicionar o seu primeiro!",
        "no_repositories_found_desc1": "Nenhum repositório instalado, correspondente a \"{searchInput}\" foi encontrado nesta secção.",
        "no_repositories_found_desc2": "Tente procurar outra coisa!",
        "pending_upgrades": "Atualizações pendentes",
        "placeholder_search": "Por favor, introduza um termo de pesquisa...",
        "sort": "ordenar",
        "stars": "Estrelas",
        "status": "Estado"
      },
      "time": {
        "ago": "atrás",
        "day": "dia",
        "days": "dias",
        "hour": "hora",
        "hours": "horas",
        "minute": "minuto",
        "minutes": "minutos",
        "month": "mês",
        "months": "meses",
        "one": "Um",
        "one_day_ago": "há um dia",
        "one_hour_ago": "há uma hora",
        "one_minute_ago": "há um minuto",
        "one_month_ago": "há um mês",
        "one_second_ago": "há um segundo",
        "one_year_ago": "há um ano",
        "second": "segundo",
        "seconds": "segundos",
        "x_days_ago": "{x} dias atrás",
        "x_hours_ago": "{x} horas atrás",
        "x_minutes_ago": "{x} minutos atrás",
        "x_months_ago": "{x} meses atrás",
        "x_seconds_ago": "{x} segundos atrás",
        "x_years_ago": "{x} anos atrás",
        "year": "ano",
        "years": "anos"
      }
    },
    "ro": {
      "common": {
        "about": "Despre",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Aplicații AppDaemon",
        "background_task": "Activitatea de fundal se execută, această pagină se va reîncărca atunci când este gata.",
        "check_log_file": "Verificați log-ul pentru mai multe detalii.",
        "continue": "Continua",
        "disabled": "Dezactivat",
        "documentation": "Documentație",
        "hacs_is_disabled": "HACS este dezactivat",
        "installed": "instalat",
        "integration": "Integrare",
        "integrations": "Integrări",
        "manage": "administra",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Aplicații NetDaemon",
        "plugin": "Plugin",
        "plugins": "Plugin-uri",
        "python_script": "Script Python",
        "python_scripts": "Scripturi Python",
        "repositories": "Depozite",
        "settings": "setări",
        "theme": "Temă",
        "themes": "Teme",
        "version": "Versiune"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Doar o singură configurație pentru HACS este permisă."
        },
        "error": {
          "auth": "Token-ul de acces personal nu este corect."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Activați descoperirea și urmărirea aplicațiilor AppDaemon",
              "netdaemon": "Activați descoperirea și urmărirea aplicațiilor NetDaemon",
              "python_script": "Activați descoperirea și urmărirea python_scripts",
              "sidepanel_icon": "Pictogramă Panou lateral",
              "sidepanel_title": "Titlu panou lateral",
              "theme": "Activați descoperirea și urmărirea temelor",
              "token": "Token de acces personal GitHub"
            },
            "description": "Dacă aveți nevoie de ajutor pentru configurare, uitați-vă aici: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Ești sigur că vrei să adaugi asta la resursele tale Lovelace?",
        "bg_task": "Acțiunea este dezactivată în timp ce activitățile de fundal se execută.",
        "cancel": "Anulare",
        "continue": "Esti sigur ca vrei sa continui?",
        "delete": "Sigur doriți să ștergeți '{item}'?",
        "exist": "{item} există deja",
        "generic": "Ești sigur?",
        "home_assistant_is_restarting": "Asteptati, Home Assistant repornește.",
        "no": "Nu",
        "no_upgrades": "Nu există actualizări în curs",
        "ok": "OK",
        "overwrite": "Făcând acest lucru, îl va suprascrie.",
        "restart_home_assistant": "Sigur doriți să reporniți Home Assistant?",
        "uninstall": "Sigur doriți să dezinstalați '{item}'?",
        "yes": "Da"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Activați descoperirea și urmărirea aplicațiilor AppDaemon",
              "country": "Filtrează cu codul țării.",
              "debug": "Activează depanarea.",
              "experimental": "Activați funcțiile experimentale",
              "netdaemon": "Activați descoperirea și urmărirea aplicațiilor NetDaemon",
              "not_in_use": "Nu este utilizat cu YAML",
              "release_limit": "Număr de versiuni afișate.",
              "sidepanel_icon": "Pictogramă Panou lateral",
              "sidepanel_title": "Titlu panou lateral"
            }
          }
        }
      },
      "repository_banner": {
        "integration_not_loaded": "Această integrare nu este încărcată în Home Assistant.",
        "no_restart_required": "Nu este necesară repornirea",
        "not_loaded": "Neîncărcat",
        "plugin_not_loaded": "Acest plugin nu este adăugat la resursele Lovelace.",
        "restart": "Trebuie să reporniți Home Assistant.",
        "restart_pending": "Reporniți în așteptare"
      },
      "repository": {
        "add_to_lovelace": "Adăugați la Lovelace",
        "authors": "Autori",
        "available": "Disponibil",
        "back_to": "Înapoi la",
        "changelog": "Jurnal modificari",
        "downloads": "Descărcări",
        "flag_this": "Semnalizează",
        "frontend_version": "Versiune frontend",
        "github_stars": "Stele GitHub",
        "goto_integrations": "Mergi la integrări",
        "hide": "Ascunde",
        "hide_beta": "Ascundere beta",
        "install": "Instalează",
        "installed": "Instalat",
        "lovelace_copy_example": "Copiați exemplul în clipboard",
        "lovelace_instruction": "Când adăugați acest lucru la configurația lovelace, folosiți acest",
        "lovelace_no_js_type": "Nu s-a putut determina tipul acestui plugin, verificați \"repository\".",
        "newest": "cele mai noi",
        "note_appdaemon": "mai trebuie să o adăugați in fișierul „apps.yaml”",
        "note_installed": "Când este instalat, acesta va fi localizat în",
        "note_integration": "mai trebuie să o adăugați in fișierul „configuration.yaml”",
        "note_plugin": "mai trebuie să o adăugați la configurația lovelace ('ui-lovelace.yaml' sau in editorul UI)",
        "open_issue": "Deschidere problemă",
        "open_plugin": "Deschide plugin",
        "reinstall": "Reinstalare",
        "repository": "Depozit",
        "restart_home_assistant": "Reporniți Home Assistant",
        "show_beta": "Afișare beta",
        "uninstall": "Dezinstalare",
        "update_information": "Actualizare informație",
        "upgrade": "Actualizare"
      },
      "settings": {
        "add_custom_repository": "ADĂUGARE DEPOZIT PERSONALIZAT",
        "adding_new_repo": "Adăugarea unui depozit nou '{repo}'",
        "adding_new_repo_category": "Cu categoria '{categoria}'.",
        "category": "Categorie",
        "compact_mode": "Modul compact",
        "custom_repositories": "DEPOZITE PERSONALIZATE",
        "delete": "Șterge",
        "display": "Afişeaza",
        "grid": "Grilă",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "depozite ascunse",
        "missing_category": "Trebuie să selectați o categorie",
        "open_repository": "Deschideți depozitul",
        "reload_data": "Reîncărcați datele",
        "reload_window": "Reîncărcați fereastra",
        "repository_configuration": "Configurația depozitului",
        "save": "Salveaza",
        "table": "Tabel",
        "table_view": "Vizualizare tabel",
        "unhide": "Unhide",
        "upgrade_all": "Actualizați toate"
      },
      "store": {
        "ascending": "ascendent",
        "clear_new": "Ștergeți toate depozitele noi",
        "descending": "descendent",
        "last_updated": "Ultima actualizare",
        "name": "Nume",
        "new_repositories": "Noi depozite",
        "pending_upgrades": "Actualizări în așteptare",
        "placeholder_search": "Vă rugăm să introduceți un termen de căutare ...",
        "sort": "fel",
        "stars": "Stele",
        "status": "Starea"
      },
      "time": {
        "ago": "în urmă",
        "day": "zi",
        "days": "zi",
        "hour": "oră",
        "hours": "ore",
        "minute": "minut",
        "minutes": "minute",
        "month": "lună",
        "months": "luni",
        "one": "Unu",
        "one_day_ago": "acum o zi",
        "one_hour_ago": "acum o oră",
        "one_minute_ago": "acum un minut",
        "one_month_ago": "acum o luna",
        "one_second_ago": "acum o secunda",
        "one_year_ago": "acum un an",
        "second": "al doilea",
        "seconds": "secunde",
        "x_days_ago": "{x} zile în urmă",
        "x_hours_ago": "{x} ore în urmă",
        "x_minutes_ago": "{x} minute în urmă",
        "x_months_ago": "{x} luni în urmă",
        "x_seconds_ago": "{x} secunde în urmă",
        "x_years_ago": "{x} ani în urmă",
        "year": "an",
        "years": "ani"
      }
    },
    "ru": {
      "common": {
        "about": "О проекте",
        "add": "добавить",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Приложения AppDaemon",
        "appdaemon_plural": "Приложения AppDaemon",
        "background_task": "Выполняется фоновая задача, страница перезагрузится по готовности.",
        "check_log_file": "Проверьте логи для получения более подробной информации.",
        "continue": "Продолжить",
        "disabled": "Отключено",
        "documentation": "Документация",
        "element": "элемент",
        "hacs_is_disabled": "HACS отключен",
        "install": "Установить",
        "installed": "установлено",
        "integration": "Интеграция",
        "integration_plural": "Интеграции",
        "integrations": "Интеграции",
        "lovelace": "Lovelace",
        "lovelace_element": "Элемент Lovelace",
        "lovelace_elements": "Элементы Lovelace",
        "manage": "управлять",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Приложения NetDaemon",
        "netdaemon_plural": "Приложения NetDaemon",
        "plugin": "Lovelace",
        "plugin_plural": "Элементы Lovelace",
        "plugins": "Плагины",
        "python_script": "Скрипт Python",
        "python_script_plural": "Скрипты Python",
        "python_scripts": "Скрипты Python",
        "repositories": "Репозитории",
        "repository": "Репозиторий",
        "settings": "настройки",
        "theme": "Тема",
        "theme_plural": "Темы",
        "themes": "Темы",
        "uninstall": "Удалить",
        "update": "Обновить",
        "version": "Версия"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Разрешена только одна настройка HACS."
        },
        "error": {
          "auth": "Неверный токен персонального доступа."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Включить поиск и отслеживание приложений AppDaemon",
              "netdaemon": "Включить поиск и отслеживание приложений NetDaemon",
              "python_script": "Включить поиск и отслеживание скриптов Python",
              "sidepanel_icon": "Иконка в боковом меню",
              "sidepanel_title": "Название в боковом меню",
              "theme": "Включить поиск и отслеживание тем",
              "token": "Персональный токен доступа GitHub"
            },
            "description": "Если вам нужна помощь с настройкой, посмотрите: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Вы уверены, что хотите добавить это в ресурсы Lovelace?",
        "bg_task": "Действие отключено во время выполнения фоновых задач.",
        "cancel": "Отмена",
        "continue": "Вы уверены, что хотите продолжить?",
        "delete": "Вы уверены, что хотите удалить '{item}'?",
        "delete_installed": "'{item}' установлен, вам нужно нажать 'Удалить', чтобы удалить его.",
        "exist": "{item} уже существует.",
        "generic": "Вы уверены?",
        "home_assistant_is_restarting": "Подожди, теперь Home Assistant перезагружается.",
        "home_assistant_version_not_correct": "Вы используете Home Assistant версии '{haversion}', но данный репозиторий требует минимальную установленную версию '{minversion}'.",
        "no": "Нет",
        "no_upgrades": "Нет обновлений",
        "ok": "ОК",
        "overwrite": "После подтверждения файлы будут перезаписаны.",
        "reload_data": "Выполняется перезагрузка данных всех репозиториев в HACS, это займет некоторое время.",
        "restart_home_assistant": "Вы уверены, что хотите перезапустить Home Assistant?",
        "uninstall": "Вы уверены, что хотите удалить '{item}'?",
        "upgrade_all": "Это произведёт обновление всех этих репозиториев, убедитесь, что вы прочитали примечания к выпуску для каждого из них, прежде чем продолжить.",
        "yes": "Да"
      },
      "dialog_about": {
        "frontend_version": "Версия интерфейса",
        "installed_repositories": "Установленные репозитории",
        "integration_version": "Версия интеграции",
        "useful_links": "Полезные ссылки"
      },
      "dialog_add_repo": {
        "limit": "Показаны только первые 100 репозиториев, используйте поиск для фильтрации того, что вам нужно",
        "no_match": "Не найдено репозиторий, соответствующих фильтру",
        "sort_by": "Сортировать по",
        "title": "Новый репозиторий"
      },
      "dialog_custom_repositories": {
        "category": "Категория",
        "no_category": "Категория не указана",
        "no_repository": "Репозиторий не указан",
        "title": "Пользовательские репозитории",
        "url_placeholder": "Добавить пользовательский URL-адрес репозитория"
      },
      "dialog_info": {
        "author": "Автор",
        "downloads": "Загрузки",
        "install": "Установить этот репозиторий в HACS",
        "loading": "Загрузка информации...",
        "no_info": "Разработчик не предоставил никакой дополнительной информации для этого репозитория",
        "open_issues": "Открытые вопросы",
        "open_repo": "Открыть репозиторий",
        "stars": "Звёзды",
        "version_installed": "Установлена версия"
      },
      "dialog_install": {
        "restart": "Помните, что вам нужно перезапустить Home Assistant, прежде чем будут применены изменения в интеграциях (custom_components).",
        "select_version": "Выберите версию",
        "show_beta": "Показывать бета-версии",
        "type": "Тип",
        "url": "Ссылка"
      },
      "dialog_update": {
        "available_version": "Доступная версия",
        "changelog": "Изменения",
        "installed_version": "Установленная версия",
        "releasenotes": "Примечания к выпуску для {release}",
        "title": "Обновление в ожидании"
      },
      "entry": {
        "information": "Информация",
        "intro": "Обновления и важные сообщения будут отображаться здесь, если таковые имеются",
        "messages": {
          "disabled": {
            "content": "Проверьте логи для получения более подробной информации.",
            "title": "HACS отключен"
          },
          "has_pending_tasks": {
            "content": "Некоторые репозитории могут не отображаться до тех пор, пока это не будет завершено",
            "title": "Выполняются фоновые задачи"
          },
          "resources": {
            "content": "У вас есть {number} элементов Lovelace, которые не загружаются должным образом.",
            "title": "Не загружено в Lovelace"
          },
          "restart": {
            "content": "У вас есть {number} интеграций, которые требуют перезагрузки Home Assistant, Вы можете сделать это из раздела 'Сервер' в разделе конфигурации пользовательского интерфейса Home Assistant.",
            "title": "В ожидании перезапуска"
          },
          "startup": {
            "content": "HACS запускается, в течение этого времени некоторые сведения могут отсутствовать или быть неверными",
            "title": "HACS запускается"
          },
          "wrong_frontend_installed": {
            "content": "Вы используете версию {running} интерфейса HACS, однако ожидаемая версия — {expected}; если Вы видите это сообщение, то Home Assistant не смог установить новую версию интерфейса; попробуйте перезапустить Home Assistant",
            "title": "Неожиданная версия интерфейса"
          },
          "wrong_frontend_loaded": {
            "content": "Вы используете версию {running} интерфейса HACS, однако ожидаемая версия — {expected}, попробуйте очистить кеш браузера.",
            "title": "Неожиданная версия интерфейса"
          }
        },
        "pending_updates": "Обновления в ожидании"
      },
      "menu": {
        "about": "О HACS",
        "clear": "Очистить все новые репозитории",
        "custom_repositories": "Пользовательские репозитории",
        "dismiss": "Убрать все новые репозитории",
        "documentation": "Документация",
        "open_issue": "Сообщить о проблеме",
        "reload": "Перезагрузить окно"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Включить поиск и отслеживание приложений AppDaemon",
              "country": "Фильтр по стране.",
              "debug": "Включить отладку.",
              "experimental": "Вкл. экспериментальные функции",
              "netdaemon": "Включить поиск и отслеживание приложений NetDaemon",
              "not_in_use": "Не используется с YAML",
              "release_limit": "Число доступных версий.",
              "sidepanel_icon": "Иконка в боковом меню",
              "sidepanel_title": "Название в боковом меню"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Эта интеграция поддерживает config_flow, это означает, что теперь вы можете перейти в раздел интеграций вашего пользовательского интерфейса, чтобы настроить её.",
        "config_flow_title": "Поддерживается настройка из пользовательского интерфейса",
        "integration_not_loaded": "Эта интеграция не загружена в Home Assistant.",
        "no_restart_required": "Перезагрузка не требуется",
        "not_loaded": "Не загружено",
        "plugin_not_loaded": "Этот плагин не добавлен к ресурсам Lovelace.",
        "restart": "Вам нужно перезапустить Home Assistant.",
        "restart_pending": "Ожидается перезапуск"
      },
      "repository_card": {
        "dismiss": "убрать",
        "hide": "Скрыть",
        "information": "Информация",
        "new_repository": "Новый репозиторий",
        "not_loaded": "Не загружено",
        "open_issue": "Сообщить о проблеме",
        "open_source": "Открыть источник",
        "pending_restart": "В ожидании перезапуска",
        "pending_update": "Ожидается обновление",
        "reinstall": "Переустановить",
        "report": "Сообщить о нарушении",
        "update_information": "Обновить информацию"
      },
      "repository": {
        "add_to_lovelace": "Добавить в Lovelace",
        "authors": "Авторы",
        "available": "Доступно",
        "back_to": "Назад к",
        "changelog": "Изменения",
        "downloads": "Загрузки",
        "flag_this": "Пожаловаться",
        "frontend_version": "Версия",
        "github_stars": "Звезды GitHub",
        "goto_integrations": "Перейти к интеграции",
        "hide": "Скрыть",
        "hide_beta": "Скрыть бета",
        "install": "Установить",
        "installed": "Установлено",
        "lovelace_copy_example": "Скопируйте пример в буфер обмена",
        "lovelace_instruction": "Для добавления в конфигурацию Lovelace, используйте",
        "lovelace_no_js_type": "Не удалось определить тип этого плагина, проверьте репозиторий.",
        "newest": "новейшая",
        "note_appdaemon": "вам всё ещё нужно добавить код для настройки приложения в файл 'apps.yaml'",
        "note_installed": "После установки, файлы будут расположены в",
        "note_integration": "вам всё ещё нужно добавить код для настройки интеграции в файл 'configuration.yaml'",
        "note_plugin": "вам всё ещё нужно добавить код для настройки плагина в конфигурацию Lovelace ('ui-lovelace.yaml')",
        "note_plugin_post_107": "вам всё равно нужно добавить его в конфигурацию Lovelace ('configuration.yaml' или редактор ресурсов по пути '/config/lovelace/resources').",
        "open_issue": "Сообщить о проблеме",
        "open_plugin": "Открыть плагин",
        "reinstall": "Переустановить",
        "repository": "Репозиторий",
        "restart_home_assistant": "Перезагрузка Home Assistant",
        "show_beta": "Показать бета",
        "uninstall": "Удалить",
        "update_information": "Обновить информацию",
        "upgrade": "Обновить"
      },
      "search": {
        "placeholder": "Поиск репозитория"
      },
      "sections": {
        "about": {
          "description": "Показать информацию о HACS",
          "title": "О проекте"
        },
        "automation": {
          "description": "Здесь вы найдете python_scripts, приложения AppDaemon и NetDaemon.",
          "title": "Автоматизация"
        },
        "frontend": {
          "description": "Здесь вы найдете темы, пользовательские карточки и другие элементы для Lovelace",
          "title": "Пользовательский интерфейс"
        },
        "integrations": {
          "description": "Здесь вы найдете пользовательские интеграции (custom_components)",
          "title": "Интеграции"
        },
        "pending_repository_upgrade": "Вы используете версию {installed}, доступна версия {available}"
      },
      "settings": {
        "add_custom_repository": "ДОБАВИТЬ СВОЙ РЕПОЗИТОРИЙ",
        "adding_new_repo": "Добавление нового репозитория '{repo}'",
        "adding_new_repo_category": "С категорией '{category}'.",
        "bg_task_custom": "Свои репозитории скрыты во время выполнения фоновых задач.",
        "category": "Категория",
        "compact_mode": "Компактный режим",
        "custom_repositories": "СВОИ РЕПОЗИТОРИИ",
        "delete": "Удалить",
        "display": "Вид",
        "grid": "Сетка",
        "hacs_repo": "Репозиторий HACS",
        "hidden_repositories": "Скрытые репозитории",
        "missing_category": "Вы должны выбрать категорию",
        "open_repository": "Открыть репозиторий",
        "reload_data": "Перезагрузить",
        "reload_window": "Перезагрузить окно",
        "repository_configuration": "Конфигурация репозитория",
        "save": "Сохранить",
        "table": "Таблица",
        "table_view": "Таблица",
        "unhide": "Показать",
        "upgrade_all": "Обновить всё"
      },
      "store": {
        "ascending": "по возрастанию",
        "clear_new": "Очистить все новые репозитории",
        "descending": "по убыванию",
        "last_updated": "Последнее обновление",
        "name": "Название",
        "new_repositories": "Новые репозитории",
        "new_repositories_note": "У Вас есть более 10 новых репозиториев, показанных здесь; если Вы хотите очистить их все, нажмите 3 точки в верхнем правом углу и уберите их разом.",
        "no_repositories": "Нет репозиториев",
        "no_repositories_desc1": "Похоже, у вас еще нет репозиториев, установленных в этом разделе.",
        "no_repositories_desc2": "Нажмите на + в нижнем углу, чтобы добавить первый!",
        "no_repositories_found_desc1": "В этом разделе не найдено установленных репозиторий, соответствующих \"{searchinput}\".",
        "no_repositories_found_desc2": "Попробуйте искать что-нибудь другое!",
        "pending_upgrades": "Ожидается обновление",
        "placeholder_search": "Пожалуйста, введите условие для поиска...",
        "sort": "Сортировка",
        "stars": "Звезды",
        "status": "Статус"
      },
      "time": {
        "ago": "назад",
        "day": "день",
        "days": "дней",
        "hour": "час",
        "hours": "часов",
        "minute": "минута",
        "minutes": "минут",
        "month": "месяц",
        "months": "месяца",
        "one": "Одна",
        "one_day_ago": "один день назад",
        "one_hour_ago": "час назад",
        "one_minute_ago": "минуту назад",
        "one_month_ago": "месяц назад",
        "one_second_ago": "одну секунду назад",
        "one_year_ago": "один год назад",
        "second": "секунда",
        "seconds": "секунд",
        "x_days_ago": "{x} дней назад",
        "x_hours_ago": "{x} часов назад",
        "x_minutes_ago": "{x} минут назад",
        "x_months_ago": "{x} месяцев назад",
        "x_seconds_ago": "{x} секунд назад",
        "x_years_ago": "{x} лет назад",
        "year": "год",
        "years": "лет"
      }
    },
    "sl": {
      "common": {
        "about": "O tem",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon Aplikacije",
        "background_task": "V ozadju se izvaja naloga, ko se konča, se bo stran osvežila.",
        "check_log_file": "Za več podrobnosti preverite datoteko dnevnika.",
        "continue": "Nadaljuj",
        "disabled": "Onemogočeno",
        "documentation": "Dokumentacija",
        "hacs_is_disabled": "HACS je onemogočen",
        "installed": "Nameščeno",
        "integration": "Integracija",
        "integrations": "Integracije",
        "manage": "upravljanje",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Aplikacije",
        "plugin": "Lovelace",
        "plugins": "Vtičniki",
        "python_script": "Python skripta",
        "python_scripts": "Python skripte",
        "repositories": "Repozitoriji",
        "settings": "nastavitve",
        "theme": "Tema",
        "themes": "Teme",
        "version": "Različica"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Dovoljena je samo ena konfiguracija HACS."
        },
        "error": {
          "auth": "Osebni dostopni žeton ni pravilen."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Omogoči odkrivanje in sledenje aplikacij AppDaemon",
              "netdaemon": "Omogoči odkrivanje in sledenje aplikacij NetDaemon",
              "python_script": "Omogoči odkrivanje in sledenje python_scripts",
              "sidepanel_icon": "Ikona stranske plošče",
              "sidepanel_title": "Naslov stranske plošče",
              "theme": "Omogoči odkrivanje in sledenje tem",
              "token": "GitHub žeton za osebni dostop"
            },
            "description": "Če potrebujete pomoč pri konfiguraciji, poglejte tukaj: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Ali si prepričan, da želiš to dodati med svoje vire Lovelace?",
        "bg_task": "Dejanje je onemogočeno, medtem ko se izvajajo opravila v ozadju.",
        "cancel": "Prekliči",
        "continue": "Ali ste prepričani, da želite nadaljevati?",
        "delete": "Ali ste prepričani, da želite izbrisati '{item}'?",
        "delete_installed": "'{item}' je nameščen, pred odstranitvijo ga morate odstraniti.",
        "exist": "{item} že obstaja",
        "generic": "Ali si prepričan?",
        "home_assistant_is_restarting": "Počakaj, Home Assistant se zdaj znova zaganja.",
        "home_assistant_version_not_correct": "Uporabljate Home Assistant verzije '{haversion}', vendar to skladišče zahteva nameščeno najmanj različico '{minversion}'.",
        "no": "Ne",
        "no_upgrades": "Ni nadgradenj",
        "ok": "V redu",
        "overwrite": "Če to storite, bo to prepisano.",
        "reload_data": "Ponovno se naložijo podatki vseh shramb HA, to bo trajalo nekaj časa.",
        "restart_home_assistant": "Ali ste prepričani, da želite znova zagnati Home Assistant?",
        "uninstall": "Ali ste prepričani, da želite odstraniti '{item}'?",
        "upgrade_all": "To bo nadgradil vse te repozitorije, preden nadaljujete poskrbite, da ste prebrali vse opombe ob izdaji.",
        "yes": "Da"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Omogoči odkrivanje in sledenje aplikacij AppDaemon",
              "country": "Filtrirajte s kodo države.",
              "debug": "Omogoči odpravljanje napak.",
              "experimental": "Omogočite poskusne funkcije",
              "netdaemon": "Omogoči odkrivanje in sledenje aplikacij NetDaemon",
              "not_in_use": "Ni v uporabi z YAML",
              "release_limit": "Število izdaj, ki jih želite prikazati.",
              "sidepanel_icon": "Ikona stranske plošče",
              "sidepanel_title": "Naslov stranske plošče"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Ta integracija podpira config_flow, kar pomeni, da lahko zdaj greste na odsek integracije vašega uporabniškega vmesnika, da ga konfigurirate.",
        "config_flow_title": "Konfiguracija uporabniškega vmesnika je podprta",
        "integration_not_loaded": "Ta integracija ni naložena v programu Home Assistant.",
        "no_restart_required": "Ponovni zagon ni potreben",
        "not_loaded": "Ni naloženo",
        "plugin_not_loaded": "Ta vtičnik ni dodan vašim virom Lovelace.",
        "restart": "Znova morate zagnati Home Assistant.",
        "restart_pending": "Vnovični zagon v čakanju"
      },
      "repository": {
        "add_to_lovelace": "Dodaj v Lovelace",
        "authors": "Avtorji",
        "available": "Na voljo",
        "back_to": "Nazaj na",
        "changelog": "Dnevnik sprememb",
        "downloads": "Prenosi",
        "flag_this": "Označite to",
        "frontend_version": "Frontend različica",
        "github_stars": "GitHub zvezde",
        "goto_integrations": "Pojdite na integracije",
        "hide": "Skrij",
        "hide_beta": "Skrij različico beta",
        "install": "Namestite",
        "installed": "Nameščeno",
        "lovelace_copy_example": "Kopirajte primer v odložišče",
        "lovelace_instruction": "Ko ga boste dodajali v svojo lovelace konfiguracijo uporabite to:",
        "lovelace_no_js_type": "Ni bilo mogoče določiti vrsto tega vtičnika, preverite repozitorij.",
        "newest": "najnovejše",
        "note_appdaemon": "Še vedno ga morate dodati v svojo 'apps.yaml' datoteko",
        "note_installed": "Ko bo nameščen, se bo nahajal v",
        "note_integration": "Še vedno ga morate dodati v svojo 'configuration.yaml' datoteko",
        "note_plugin": "vendar ga še vedno morate dodati v svojo lovelace konfiguracijo ('ui-lovelace.yaml' ali \"raw\" UI config urejevalnik)",
        "note_plugin_post_107": "še vedno ga morate dodati v svojo konfiguracijo lovelace ('config.yaml' ali urejevalnik virov '/config/lovelace/resources')",
        "open_issue": "Odprite težavo",
        "open_plugin": "Odprite vtičnik",
        "reinstall": "Znova namestite",
        "repository": "Repozitorij",
        "restart_home_assistant": "Znova zaženite Home Assistant",
        "show_beta": "Pokaži različico beta",
        "uninstall": "Odstranite",
        "update_information": "Posodobite podatke",
        "upgrade": "Nadgradnja"
      },
      "settings": {
        "add_custom_repository": "DODAJTE SVOJ REPOZITORIJ",
        "adding_new_repo": "Dodajanje novega repozitorija '{repo}'",
        "adding_new_repo_category": "S kategorijo '{category}'.",
        "bg_task_custom": "Med izvajanjem nalog v ozadju so repozitoriji po meri skriti.",
        "category": "Kategorija",
        "compact_mode": "Kompaktni način",
        "custom_repositories": "VAŠI REPOZITORIJI",
        "delete": "Izbriši",
        "display": "Prikaz",
        "grid": "Mreža",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "skriti repozitoriji",
        "missing_category": "Izbrati moraš kategorijo",
        "open_repository": "Odprite skladišče",
        "reload_data": "Ponovno naloži podatke",
        "reload_window": "Ponovno naloži okno",
        "repository_configuration": "Konfiguracija skladišč",
        "save": "Shranite",
        "table": "Tabela",
        "table_view": "Pogled tabele",
        "unhide": "Razkrij",
        "upgrade_all": "Nadgradite vse"
      },
      "store": {
        "ascending": "naraščajoče",
        "clear_new": "Počisti vse nove repozitorije",
        "descending": "padajoče",
        "last_updated": "Nazadnje posodobljeno",
        "name": "Ime",
        "new_repositories": "Novi repozitoriji",
        "pending_upgrades": "Nadgradnje na čakanju",
        "placeholder_search": "Prosim vnesite iskalni izraz ...",
        "sort": "razvrsti",
        "stars": "Zvezd",
        "status": "Stanje"
      },
      "time": {
        "ago": "nazaj",
        "day": "dan",
        "days": "Dni",
        "hour": "ura",
        "hours": "Ur",
        "minute": "minuta",
        "minutes": "minut",
        "month": "mesec",
        "months": "mesecev",
        "one": "Eno",
        "one_day_ago": "pred enim dnevom",
        "one_hour_ago": "pred eno uro",
        "one_minute_ago": "pred eno minuto",
        "one_month_ago": "pred enim mesecem",
        "one_second_ago": "pred sekundo",
        "one_year_ago": "pred enim letom",
        "second": "drugič",
        "seconds": "sekund",
        "x_days_ago": "{x} dni nazaj",
        "x_hours_ago": "{x} ur nazaj",
        "x_minutes_ago": "{x} minut nazaj",
        "x_months_ago": "{x} mesecev nazaj",
        "x_seconds_ago": "{x} sekund nazaj",
        "x_years_ago": "{x} let nazaj",
        "year": "leto",
        "years": "Let"
      }
    },
    "sv": {
      "common": {
        "about": "Om",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "Appdaemon Applikationer",
        "background_task": "Bakgrundsjobb körs, denna sida kommer att laddas igen när det är klart.",
        "check_log_file": "Kontrollera din loggfil för mer information.",
        "continue": "Fortsätta",
        "disabled": "Inaktiverad",
        "documentation": "Dokumentation",
        "hacs_is_disabled": "HACS är inaktiverat",
        "installed": "installerad",
        "integration": "Integration",
        "integrations": "Integrationer",
        "manage": "hantera",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "NetDaemon Applikationer",
        "plugin": "Lovelace",
        "plugins": "Plugins",
        "python_script": "Python skript",
        "python_scripts": "Python skript",
        "repositories": "Repositories",
        "settings": "inställningar",
        "theme": "Tema",
        "themes": "Teman",
        "version": "Version"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Endast en konfiguration kan användas i HACS."
        },
        "error": {
          "auth": "Personal Access Token är inte korrekt."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Upptäck och följ Appdaemon applikationer",
              "netdaemon": "Upptäck och följ NetDaemon applikationer",
              "python_script": "Upptäck och följ python_scripts",
              "sidepanel_icon": "Ikon för sidpanel",
              "sidepanel_title": "Rubrik för sidpanel",
              "theme": "Upptäck och följ teman",
              "token": "GitHub Personal Access Token"
            },
            "description": "Om du behöver hjälp med konfigurationen, se här: https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "Är du säker på att du vill lägga till detta till dina Lovelace-resurser?",
        "bg_task": "Åtgärden är inaktiverad medan aktiviteter i bakgrunden körs",
        "cancel": "Avbryt",
        "continue": "Är du säker på att du vill fortsätta?",
        "delete": "Är du säker på att du vill ta bort '{item}'?",
        "delete_installed": "'{item}' är installerat, du måste avinstallera det innan du kan ta bort det.",
        "exist": "{item} existerar redan",
        "generic": "Är du säker?",
        "home_assistant_is_restarting": "Vänta, Home Assistant startar nu om.",
        "home_assistant_version_not_correct": "Du kör Home Assistant-versionen '{haversion}', men detta repository kräver att lägsta versionen '{minversion}' måste installeras.",
        "no": "Nej",
        "no_upgrades": "Inga uppgraderingar väntar",
        "ok": "OK",
        "overwrite": "Detta kommer att skriva över den.",
        "reload_data": "Detta laddar om data för alla repositories HACS vet om, kommer detta att ta lite tid att slutföra.",
        "restart_home_assistant": "Är du säker på att du vill starta om Home Assistant?",
        "uninstall": "Är du säker på att du vill avinstallera '{item}'?",
        "upgrade_all": "Detta kommer uppgradera alla dessa repositories, säkerhetsställ att du läst release anteckningarna för dem alla innan du fortsätter",
        "yes": "Ja"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Upptäck och följ Appdaemon applikationer",
              "country": "Filtrera på landskod.",
              "debug": "Aktivera felsökning",
              "experimental": "Använd experimentella funktioner",
              "netdaemon": "Upptäck och följ NetDaemon applikationer",
              "not_in_use": "Används inte med YAML",
              "release_limit": "Antalet releaser som visas.",
              "sidepanel_icon": "Ikon för sidpanel",
              "sidepanel_title": "Rubrik för sidpanel"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Den här integreringen stöder config_flow, det innebär att du nu kan gå till integrationsdelen i användargränssnittet för att konfigurera det.",
        "config_flow_title": "UI-konfiguration stöds",
        "integration_not_loaded": "Denna integration inte laddats i Hem Assistent.",
        "no_restart_required": "Ingen omstart krävs",
        "not_loaded": "Ej laddad",
        "plugin_not_loaded": "Detta plugin är inte tillagt till din Lovelaceresurs.",
        "restart": "Du måste starta om Home Assistant.",
        "restart_pending": "Omstart väntar"
      },
      "repository": {
        "add_to_lovelace": "Lägg till i Lovelace",
        "authors": "Författare",
        "available": "Tillgänglig",
        "back_to": "Tillbaka till",
        "changelog": "Ändringslogg",
        "downloads": "Nedladdningar",
        "flag_this": "Flagga denna",
        "frontend_version": "Frontend-version",
        "github_stars": "GitHub stjärnor",
        "goto_integrations": "Gå till integrationer",
        "hide": "Göm",
        "hide_beta": "Göm betaversioner",
        "install": "Installera",
        "installed": "Installerad",
        "lovelace_copy_example": "Kopiera exemplet till urklipp",
        "lovelace_instruction": "När du lägger till denna till din lovelace konfiguration, använd",
        "lovelace_no_js_type": "Kan inte avgöra villken typ av plugin, kontrollera i GIT \nrepository",
        "newest": "nyaste",
        "note_appdaemon": "du behöver fortfarande lägga till den till filen 'apps.yaml'",
        "note_installed": "När den är installerad kommer den finnas i",
        "note_integration": "du behöver fortfarande lägga den till filen 'configuration.yaml'",
        "note_plugin": "du behöver fortfarande lägga till den till din lovelace konfiguration ('ui-lovelace.yaml' eller raw UI config redigerare)",
        "note_plugin_post_107": "du behöver fortfarande lägga till den i din lovelace-konfiguration ('configuration.yaml' eller resursredigeraren /config/lovelace/resources')",
        "open_issue": "Rapportera problem",
        "open_plugin": "Öppna plugin",
        "reinstall": "Ominstallera",
        "repository": "Repository",
        "restart_home_assistant": "Starta om Home Assistant",
        "show_beta": "Visa betaversioner",
        "uninstall": "Avinstallera",
        "update_information": "Uppdatera information",
        "upgrade": "Uppdatera"
      },
      "settings": {
        "add_custom_repository": "LÄGG TILL ETT REPOSITORY",
        "adding_new_repo": "Lägger till nytt repository '{repo}'",
        "adding_new_repo_category": "Med kategori '{kategori}'.",
        "bg_task_custom": "Anpassade repositories är dolda under tiden aktiviteter i bakgrunden körs",
        "category": "Kategori",
        "compact_mode": "Kompakt läge",
        "custom_repositories": "REPOSITORIES",
        "delete": "Ta bort",
        "display": "Visa",
        "grid": "Rutnät",
        "hacs_repo": "HACS repo",
        "hidden_repositories": "dolda förråd",
        "missing_category": "Du behöver välja en kategori",
        "open_repository": "Öppna Repository",
        "reload_data": "Ladda om data",
        "reload_window": "Ladda om fönstret",
        "repository_configuration": "Konfiguration av repository",
        "save": "Spara",
        "table": "Tabell",
        "table_view": "Tabellvy",
        "unhide": "ta fram",
        "upgrade_all": "Uppgradera alla"
      },
      "store": {
        "ascending": "stigande",
        "clear_new": "Rensa alla nya förvar",
        "descending": "fallande",
        "last_updated": "Senast uppdaterad",
        "name": "Namn",
        "new_repositories": "Nya förvar",
        "pending_upgrades": "Väntande uppgraderingar",
        "placeholder_search": "Skriv ett sökord...",
        "sort": "sortera",
        "stars": "Stjärnor",
        "status": "Status"
      },
      "time": {
        "ago": "sedan",
        "day": "dag",
        "days": "dagar",
        "hour": "timme",
        "hours": "timmar",
        "minute": "minut",
        "minutes": "minuter",
        "month": "månad",
        "months": "månader",
        "one": "Ett",
        "one_day_ago": "för en dag sedan",
        "one_hour_ago": "för en timme sedan",
        "one_minute_ago": "en minut sedan",
        "one_month_ago": "en månad sedan",
        "one_second_ago": "för en sekund sedan",
        "one_year_ago": "ett år sedan",
        "second": "andra",
        "seconds": "sekunder",
        "x_days_ago": "{x} dagar sedan",
        "x_hours_ago": "{x} timmar sedan",
        "x_minutes_ago": "{x} minuter sedan",
        "x_months_ago": "{x} månader sedan",
        "x_seconds_ago": "{x} sekunder sedan",
        "x_years_ago": "{x} år sedan",
        "year": "år",
        "years": "år"
      }
    },
    "vi": {
      "common": {
        "about": "Thông tin",
        "add": "thêm",
        "appdaemon_apps": "Ứng dụng AppDaemon",
        "appdaemon_plural": "Ứng dụng AppDaemon",
        "background_task": "Tác vụ nền đang chạy, trang này sẽ được tải lại khi hoàn thành.",
        "check_log_file": "Kiểm tra tệp nhật ký để biết thêm chi tiết.",
        "continue": "Tiếp tục",
        "disabled": "Bị vô hiệu hoá",
        "documentation": "Tài liệu",
        "element": "thành phần",
        "hacs_is_disabled": "HACS đã bị vô hiệu hoá",
        "install": "Cài đặt",
        "installed": "đã cài đặt",
        "integration": "Tích Hợp",
        "integration_plural": "Tích Hợp",
        "integrations": "Tích Hợp",
        "lovelace": "Lovelace",
        "lovelace_element": "Thành phần Lovelace",
        "lovelace_elements": "Các thành phần Lovelace",
        "manage": "quản lý",
        "netdaemon": "NetDaemon",
        "netdaemon_apps": "Ứng dụng NetDaemon",
        "netdaemon_plural": "Ứng dụng NetDaemon",
        "plugin": "Bổ Sung",
        "plugin_plural": "Các thành phần Lovelace",
        "plugins": "Bổ Sung",
        "python_script": "Chương trình Python",
        "python_script_plural": "Chương trình Python",
        "python_scripts": "Chương trình Python",
        "repositories": "Các kho ứng dụng",
        "repository": "Kho lưu trữ",
        "settings": "thiết lập",
        "theme": "Chủ đề",
        "theme_plural": "Chủ đề",
        "themes": "Chủ đề",
        "uninstall": "Gỡ cài đặt",
        "update": "Cập nhật",
        "version": "Phiên bản"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "Chỉ cho phép một cấu hình HACS duy nhất."
        },
        "error": {
          "auth": "Mã Truy Cập Cá Nhân không đúng."
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng AppDaemon",
              "netdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng NetDaemon",
              "python_script": "Kích hoạt phát hiện & theo dõi các Chương trình Python",
              "sidepanel_icon": "Biểu tượng bảng điều khiển cạnh bên",
              "sidepanel_title": "Tiêu đề bảng điều khiển cạnh bên",
              "theme": "Kích hoạt phát hiện & theo dõi các Chủ Đề",
              "token": "Mã Truy Cập Cá Nhân từ GitHub"
            },
            "description": "Nếu bạn cần giúp đỡ về các cấu hình, hãy xem ở đây:\nhttps://hacs.xyz/docs/configuration/start"
          }
        }
      },
      "confirm": {
        "add_to_lovelace": "Bạn có chắc muốn thêm vào tài nguyên Lovelace của mình không?",
        "bg_task": "Hành động bị vô hiệu hoá trong khi các tác vụ nền đang chạy.",
        "cancel": "Huỷ bỏ",
        "continue": "Bạn có chắc chắn muốn tiếp tục?",
        "delete": "Bạn có chắc muốn xoá '{item}'?",
        "delete_installed": "'{item}' đã được cài đặt, bạn cần gỡ cài đặt nó trước khi có thể xoá.",
        "exist": "{item} đang tồn tại",
        "generic": "Bạn có chắc không?",
        "home_assistant_is_restarting": "Khoan đã, Home Assistant đang khởi động lại.",
        "home_assistant_version_not_correct": "Bạn đang chạy phiên bản Home Assistant '{haversion}' nhưng kho ứng dụng này yêu cầu phiên bản thấp nhất '{minversion}' để cài đặt.",
        "no": "Không",
        "no_upgrades": "Không có cập nhật nào đang xử lý",
        "ok": "OK",
        "overwrite": "Tiếp tục sẽ khiến card này bị ghi đè lên.",
        "reload_data": "Điều này tải lại dữ liệu của tất cả các kho ứng dụng trong HACS, sẽ mất một lúc để hoàn tất.",
        "restart_home_assistant": "Bạn có muốn khởi động lại Home Assistant?",
        "uninstall": "Bạn có chắc muốn gỡ cài đặt '{item}'?",
        "upgrade_all": "Điều này sẽ nâng cấp tất cả các kho ứng dụng này, đảm bảo rằng bạn đã đọc tất cả các lưu ý phát hành trước khi tiếp tục.",
        "yes": "Có"
      },
      "dialog_about": {
        "frontend_version": "Phiên bản Frontend",
        "installed_repositories": "Kho lưu trữ đã cài đặt",
        "integration_version": "Phiên bản tích hợp",
        "useful_links": "Liên kết hữu ích"
      },
      "dialog_add_repo": {
        "limit": "Chỉ 100 kho lưu trữ đầu tiên được hiển thị, sử dụng mục tìm kiếm để lọc những gì bạn cần",
        "no_match": "Không tìm thấy kho lưu trữ phù hợp với bộ lọc của bạn",
        "sort_by": "Sắp xếp theo",
        "title": "Thêm kho lưu trữ"
      },
      "dialog_custom_repositories": {
        "category": "Danh mục",
        "no_category": "Thiếu danh mục",
        "no_repository": "Kho lưu trữ bị thiếu",
        "title": "Các kho lưu trữ tuỳ chỉnh",
        "url_placeholder": "Thêm URL của kho lưu trữ tùy chỉnh"
      },
      "dialog_info": {
        "author": "Tác giả",
        "downloads": "Tải xuống",
        "install": "Cài đặt kho lưu trữ này trong HACS",
        "loading": "Đang tải thông tin...",
        "no_info": "Nhà phát triển đã không cung cấp thêm thông tin nào cho kho lưu trữ này",
        "open_issues": "Báo cáo vấn đề",
        "open_repo": "Mở Kho ứng dụng",
        "stars": "Số sao",
        "version_installed": "Phiên bản đã cài đặt"
      },
      "dialog_install": {
        "restart": "Hãy nhớ rằng bạn cần khởi động lại Home Assistant trước khi các thay đổi với tích hợp (custom_components) được áp dụng.",
        "select_version": "Chọn phiên bản",
        "show_beta": "Hiển thị phiên bản beta",
        "type": "Loại",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "Phiên bản hiện có",
        "changelog": "Thay đổi",
        "installed_version": "Phiên bản đã cài đặt",
        "releasenotes": "Thông tin phiên bản {Release}",
        "title": "Cập nhật đang chờ"
      },
      "entry": {
        "information": "Thông tin",
        "intro": "Các cập nhật và thông điệp quan trọng sẽ hiển thị ở đây nếu có",
        "messages": {
          "disabled": {
            "content": "Kiểm tra tệp nhật ký của bạn để biết thêm chi tiết",
            "title": "HACS đã bị vô hiệu hoá"
          },
          "has_pending_tasks": {
            "content": "Một số kho có thể không thấy cho đến khi điều này hoàn tất",
            "title": "Tác vụ nền đang chờ"
          },
          "resources": {
            "content": "Bạn có {number} thành phần Lovelace không được tải chính xác.",
            "title": "Không được tải trong Lovelace"
          },
          "restart": {
            "content": "Bạn có {number} tích hợp yêu cầu khởi động lại Home Assistant, bạn có thể làm điều này từ mục 'Điều khiển máy chủ' bên trong tab Cấu hình trên giao diện Home Assistant.",
            "title": "Đang chờ khởi động lại"
          },
          "startup": {
            "content": "HACS đang khởi động, suốt quá trình này có thể một số thông tin sẽ bị thiếu hoặc không chính xác",
            "title": "HACS đang khởi động"
          },
          "wrong_frontend_installed": {
            "content": "Bạn có HACS frontend phiên bản {running} đã được cài đặt, nhưng phiên bản yêu cầu là {version}, khi bạn nhìn thấy thông điệp này có nghĩa là Home Assistant không thể cài đặt phiên bản mới, thử khởi động lại Home Assistant.",
            "title": "Phiên bản frontend không đúng"
          },
          "wrong_frontend_loaded": {
            "content": "Bạn đang chạy phiên bản {running} của HACS frontend, nhưng phiên bản được yêu cầu là {expected}, bạn nên xoá bộ đệm của trình duyệt web.",
            "title": "Phiên bản frontend không đúng"
          }
        },
        "pending_updates": "Đang chờ cập nhật"
      },
      "menu": {
        "about": "Giới thiệu HACS",
        "clear": "Ẩn thông báo mục mới",
        "custom_repositories": "Các kho ứng dụng tuỳ chỉnh",
        "dismiss": "Bỏ qua tất cả kho chứa mới",
        "documentation": "Tài liệu",
        "open_issue": "Báo cáo vấn đề",
        "reload": "Tải lại cửa sổ"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng AppDaemon",
              "country": "Lọc với mã quốc gia.",
              "debug": "Kích hoạt Trình gỡ lỗi",
              "experimental": "Kích hoạt các tính năng thử nghiệm",
              "netdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng NetDaemon",
              "not_in_use": "Không sử dụng với YAML",
              "release_limit": "Số lượng phiên bản hiển thị.",
              "sidepanel_icon": "Biểu tượng bảng điều khiển cạnh bên",
              "sidepanel_title": "Tiêu đề bảng điều khiển cạnh bên"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "Tích hợp này hỗ trợ config_flow, điều này có nghĩa là bây giờ bạn có thể chuyển đến khu vực Tích hợp trên giao diện để cấu hình nó.",
        "config_flow_title": "Hỗ trợ cấu hình qua giao diện người dùng",
        "integration_not_loaded": "Tích hợp này chưa được tải trong Home Assistant.",
        "no_restart_required": "Không cần khởi động lại",
        "not_loaded": "Chưa được tải",
        "plugin_not_loaded": "Bổ sung này chưa được thêm vào tài nguyên Lovelace của bạn.",
        "restart": "Bạn cần khởi động lại Home Assistant.",
        "restart_pending": "Đang chờ khởi động lại"
      },
      "repository_card": {
        "dismiss": "bỏ qua",
        "hide": "Ẩn",
        "information": "Thông tin",
        "new_repository": "Kho lưu trữ mới",
        "not_loaded": "Không được tải",
        "open_issue": "Báo cáo vấn đề",
        "open_source": "Mã nguồn mở",
        "pending_restart": "Đang chờ khởi động lại",
        "pending_update": "Cập nhật đang chờ",
        "reinstall": "Cài đặt lại",
        "report": "Báo cáo để loại bỏ",
        "update_information": "Cập nhật thông tin"
      },
      "repository": {
        "add_to_lovelace": "Thêm vào Lovelace",
        "authors": "Tác giả",
        "available": "Có sẵn",
        "back_to": "Quay lại",
        "changelog": "Nhật ký thay đổi",
        "downloads": "Tải xuống",
        "flag_this": "Đánh dấu mục này",
        "frontend_version": "Phiên bản Frontend",
        "github_stars": "Số Sao trên GitHub",
        "goto_integrations": "Đi đến Tích hợp",
        "hide": "Ẩn",
        "hide_beta": "Ẩn phiên bản beta",
        "install": "Cài đặt",
        "installed": "Đã cài đặt",
        "lovelace_copy_example": "Sao chép ví dụ vào bộ nhớ tạm",
        "lovelace_instruction": "Khi bạn thêm mục này vào cấu hình lovelace, sử dụng",
        "lovelace_no_js_type": "Không thể xác định loại Bổ sung này, kiểm tra lại kho ứng dụng.",
        "newest": "mới nhất",
        "note_appdaemon": "bạn vẫn cần thêm vào tập tin 'apps.yaml'",
        "note_installed": "Một khi được cài đặt, mục này sẽ nằm ở",
        "note_integration": "bạn vẫn cần thêm vào tập tin 'configuration.yaml'",
        "note_plugin": "bạn vẫn cần thêm vào cấu hình lovelace ('ui-lovelace.yaml' hoặc trình soạn thảo giao diện)",
        "note_plugin_post_107": "bạn vẫn cần phải thêm nó vào cấu hình Lovelace ('configuration.yaml' hoặc trình biên tập tài nguyên '/config/Lovelace/Resources ')",
        "open_issue": "Báo cáo vấn đề",
        "open_plugin": "Mở Bổ sung",
        "reinstall": "Cài đặt lại",
        "repository": "Kho ứng dụng",
        "restart_home_assistant": "Khởi động lại Home Assistant",
        "show_beta": "Hiển thị phiên bản beta",
        "uninstall": "Gỡ cài đặt",
        "update_information": "Cập nhật thông tin",
        "upgrade": "Cập nhật"
      },
      "search": {
        "placeholder": "Tìm kiếm kho lưu trữ"
      },
      "sections": {
        "about": {
          "description": "Hiển thị thông tin về HACS",
          "title": "Thông tin"
        },
        "automation": {
          "description": "Đây là nơi bạn tìm thấy python_scripts, ứng dụng AppDaemon và ứng dụng NetDaemon",
          "title": "Tự động hóa"
        },
        "frontend": {
          "description": "Đây là nơi bạn tìm thấy chủ đề, thẻ tùy chỉnh và các thành phần khác cho lovelace",
          "title": "Frontend"
        },
        "integrations": {
          "description": "Đây là nơi bạn tìm thấy tích hợp tùy chỉnh (custom_components)",
          "title": "Tích Hợp"
        },
        "pending_repository_upgrade": "Bạn đang chạy phiên bản {installed}, phiên bản {available} có sẵn"
      },
      "settings": {
        "add_custom_repository": "THÊM KHO ỨNG DỤNG TUỲ CHỈNH",
        "adding_new_repo": "Thêm kho ứng dụng mới '{repo}'",
        "adding_new_repo_category": "Với danh mục '{category}'",
        "bg_task_custom": "Các kho ứng dụng tuỳ chỉnh bị ẩn khi tác vụ nền đang chạy.",
        "category": "Danh mục",
        "compact_mode": "Chế độ thu gọn",
        "custom_repositories": "KHO ỨNG DỤNG TUỲ CHỈNH",
        "delete": "Xoá",
        "display": "Hiển thị",
        "grid": "Lưới",
        "hacs_repo": "Kho ứng dụng HACS",
        "hidden_repositories": "các kho ứng dụng bị ẩn",
        "missing_category": "Bạn cần chọn một danh mục",
        "open_repository": "Mở Kho ứng dụng",
        "reload_data": "Tải lại dữ liệu",
        "reload_window": "Tải lại cửa sổ",
        "repository_configuration": "Cấu hình Kho ứng dụng",
        "save": "Lưu",
        "table": "Bảng",
        "table_view": "Xem dạng bảng",
        "unhide": "thôi ẩn",
        "upgrade_all": "Nâng cấp tất cả"
      },
      "store": {
        "ascending": "tăng dần",
        "clear_new": "Xoá tất cả kho ứng dụng mới",
        "descending": "giảm dần",
        "last_updated": "Cập nhật lần cuối",
        "name": "Tên",
        "new_repositories": "Kho ứng dụng Mới",
        "new_repositories_note": "Bạn có hơn 10 kho chứa mới được hiển thị, nếu bạn muốn xoá tất cả thông báo này, bấm vào dấu 3 chấm ở góc trên bên phải rồi chọn Bỏ qua tất cả kho chứa mới.",
        "no_repositories": "Không có kho lưu trữ",
        "no_repositories_desc1": "Có vẻ như bạn chưa có bất kỳ kho lưu trữ nào được cài đặt trong phần này.",
        "no_repositories_desc2": "Nhấp vào biểu tượng + ở góc dưới cùng để thêm mục mới đầu tiên của bạn!",
        "no_repositories_found_desc1": "Không tìm thấy kho lưu trữ được cài đặt phù hợp với \"{searchInput}\" trong mục này.",
        "no_repositories_found_desc2": "Thử tìm kiếm với từ khoá khác!",
        "pending_upgrades": "Nâng cấp chờ xử lý",
        "placeholder_search": "Vui lòng nhập cụm từ cần tìm",
        "sort": "sắp xếp",
        "stars": "Sao",
        "status": "Trạng thái"
      },
      "time": {
        "ago": "trước",
        "day": "ngày",
        "days": "ngày",
        "hour": "giờ",
        "hours": "giờ",
        "minute": "phút",
        "minutes": "phút",
        "month": "tháng",
        "months": "tháng",
        "one": "Một",
        "one_day_ago": "một ngày trước",
        "one_hour_ago": "một giờ trước",
        "one_minute_ago": "một phút trước",
        "one_month_ago": "một tháng trước",
        "one_second_ago": "một giây trước",
        "one_year_ago": "một năm trước",
        "second": "giây",
        "seconds": "giây",
        "x_days_ago": "{x} ngày trước",
        "x_hours_ago": "{x} giờ trước",
        "x_minutes_ago": "{x} phút trước",
        "x_months_ago": "{x} tháng trước",
        "x_seconds_ago": "{x} giây trước",
        "x_years_ago": "{x} năm trước",
        "year": "năm",
        "years": "năm"
      }
    },
    "zh-Hans": {
      "common": {
        "about": "关于",
        "add": "添加",
        "appdaemon": "AppDaemon",
        "appdaemon_apps": "AppDaemon应用",
        "appdaemon_plural": "AppDaemon应用",
        "background_task": "后台任务正在运行，完成后将重新加载此页面。",
        "check_log_file": "请查看日志文件以了解更多信息。",
        "continue": "继续",
        "disabled": "禁用",
        "documentation": "文档",
        "element": "元素",
        "hacs_is_disabled": "HACS已禁用",
        "install": "安装",
        "installed": "已安装",
        "integration": "集成",
        "integration_plural": "集成",
        "integrations": "集成",
        "lovelace": "Lovelace",
        "lovelace_element": "Lovelace元素",
        "lovelace_elements": "Lovelace元素",
        "manage": "管理",
        "netdaemon": "NetDaemon应用",
        "netdaemon_apps": "NetDaemon应用",
        "netdaemon_plural": "NetDaemon应用",
        "plugin": "Lovelace",
        "plugin_plural": "Lovelace元素",
        "plugins": "Lovelace元素",
        "python_script": "Python脚本",
        "python_script_plural": "Python脚本",
        "python_scripts": "Python脚本",
        "repositories": "储存库数量",
        "repository": "存储库",
        "settings": "设置",
        "theme": "主题",
        "theme_plural": "主题",
        "themes": "主题",
        "uninstall": "卸载",
        "update": "升级",
        "version": "版本"
      },
      "config": {
        "abort": {
          "single_instance_allowed": "仅允许单个HACS配置。"
        },
        "error": {
          "auth": "个人访问令牌不正确。"
        },
        "step": {
          "user": {
            "data": {
              "appdaemon": "启用AppDaemon应用发现和跟踪",
              "netdaemon": "启用NetDaemon应用发现和跟踪",
              "python_script": "启用python_scripts发现和跟踪",
              "sidepanel_icon": "侧面板图标",
              "sidepanel_title": "侧面板标题",
              "theme": "启用主题发现和跟踪",
              "token": "GitHub个人访问令牌"
            },
            "description": "如果您需要有关配置的帮助，请在此处查看：https://hacs.xyz/docs/configuration/start",
            "title": "HACS (Home Assistant Community Store)"
          }
        },
        "title": "HACS (Home Assistant Community Store)"
      },
      "confirm": {
        "add_to_lovelace": "您确定要将此添加到Lovelace资源中吗？",
        "bg_task": "后台任务正在运行时，操作被禁用。",
        "cancel": "取消",
        "continue": "你确定你要继续吗？",
        "delete": "是否确实要删除\"{item}\"？",
        "delete_installed": "已安装 '{item}'，需要先将其卸载，然后才能将其删除。",
        "exist": "{item}已经存在",
        "generic": "你确定吗？",
        "home_assistant_is_restarting": "请等待，Home Assistant现在正在重新启动。",
        "home_assistant_version_not_correct": "您正在运行Home Assistant的版本为'{haversion}'，但是这个需要安装最低版本是'{minversion}'。",
        "no": "取消",
        "no_upgrades": "暂无升级",
        "ok": "确定",
        "overwrite": "这样做会覆盖它。",
        "reload_data": "这将重新加载HACS知道的所有存储库的数据，这需要一些时间才能完成。",
        "restart_home_assistant": "您确定要重新启动Home Assistant吗？",
        "uninstall": "您确定要卸载 '{item}' 吗？",
        "upgrade_all": "这将升级所有这些存储库，请确保在继续之前已阅读所有存储库的发行说明。",
        "yes": "确定"
      },
      "dialog_about": {
        "frontend_version": "前端版本",
        "installed_repositories": "已安装的存储库",
        "integration_version": "集成版本",
        "useful_links": "有用的链接"
      },
      "dialog_add_repo": {
        "limit": "仅显示前100个存储库，使用搜索过滤所需的内容",
        "no_match": "找不到与您的过滤器匹配的存储库",
        "sort_by": "排序方式",
        "title": "添加存储库"
      },
      "dialog_custom_repositories": {
        "category": "类别",
        "no_category": "没有选择类别",
        "no_repository": "存储库地址不能为空",
        "title": "自定义存储库",
        "url_placeholder": "添加自定义存储库URL"
      },
      "dialog_info": {
        "author": "作者",
        "downloads": "下载",
        "install": "在HACS中安装此存储库",
        "loading": "正在加载详细信息...",
        "no_info": "开发人员尚未为此存储库提供任何更多信息",
        "open_issues": "提交问题",
        "open_repo": "打开存储库",
        "stars": "星级",
        "version_installed": "已安装版本"
      },
      "dialog_install": {
        "restart": "请记住，在应用对集成（custom_components）所做的更改之前，您需要重新启动Home Assistant。",
        "select_version": "选择版本",
        "show_beta": "显示测试版",
        "type": "Type",
        "url": "URL"
      },
      "dialog_update": {
        "available_version": "可用版本",
        "changelog": "更改日志",
        "installed_version": "已安装版本",
        "releasenotes": "更新日志{release}",
        "title": "待更新"
      },
      "entry": {
        "information": "详情",
        "intro": "如果有更新和重要消息，将在此处显示",
        "messages": {
          "disabled": {
            "content": "请查看日志文件以了解更多信息",
            "title": "HACS已禁用"
          },
          "has_pending_tasks": {
            "content": "在完成此操作之前，某些存储库可能不会显示",
            "title": "待处理的后台任务"
          },
          "resources": {
            "content": "您有{number} 个Lovelace元素未正确加载到Lovelace中。",
            "title": "未载入Lovelace"
          },
          "restart": {
            "content": "您有{number}集成，需要重新启动Home Assistant，您可以从Home Assistant 的配置下的“服务器控制”部分执行此操作。",
            "title": "待重启"
          },
          "startup": {
            "content": "HACS正在启动，在此期间某些信息可能丢失或不正确",
            "title": "HACS正在启动"
          },
          "wrong_frontend_installed": {
            "content": "您已经安装了{running}的HACS前端，但是{expected}版本是{expected} ，如果看到此消息，则Home Assistant无法安装新版本，请尝试重新启动Home Assistant。",
            "title": "额外的前端版本"
          },
          "wrong_frontend_loaded": {
            "content": "您正在运行HACS前端的{running}版本，但是{expected}版本，您应该清除浏览器缓存。",
            "title": "额外的前端版本"
          }
        },
        "pending_updates": "待升级"
      },
      "menu": {
        "about": "关于HACS",
        "clear": "清除所有新标记",
        "custom_repositories": "自定义存储库",
        "dismiss": "忽略所有新的存储库",
        "documentation": "文档",
        "open_issue": "提交问题",
        "reload": "重新加载窗口"
      },
      "options": {
        "step": {
          "user": {
            "data": {
              "appdaemon": "启用AppDaemon应用发现和跟踪",
              "country": "用国家代码过滤。",
              "debug": "启用调试。",
              "experimental": "启用实验功能",
              "netdaemon": "启用NetDaemon应用发现和跟踪",
              "not_in_use": "不适用于 YAML",
              "release_limit": "要显示的发行数量。",
              "sidepanel_icon": "侧面板图标",
              "sidepanel_title": "侧面板标题"
            }
          }
        }
      },
      "repository_banner": {
        "config_flow": "此组件支持config_flow，这意味着您现在可以转到UI的“集成”部分进行配置。",
        "config_flow_title": "支持UI配置",
        "integration_not_loaded": "此集成未加载到Home Assistant中。",
        "no_restart_required": "无需重启",
        "not_loaded": "未加载",
        "plugin_not_loaded": "该元件未添加到您的Lovelace资源中。",
        "restart": "您需要重新启动Home Assistant。",
        "restart_pending": "重新启动待处理"
      },
      "repository_card": {
        "dismiss": "忽略",
        "hide": "隐藏",
        "information": "详情",
        "new_repository": "新存储库",
        "not_loaded": "未加载",
        "open_issue": "提交问题",
        "open_source": "开源",
        "pending_restart": "待重启",
        "pending_update": "待升级",
        "reinstall": "重新安装",
        "report": "报告删除",
        "update_information": "更新信息"
      },
      "repository": {
        "add_to_lovelace": "添加到Lovelace",
        "authors": "作者",
        "available": "版本号",
        "back_to": "返回",
        "changelog": "更新日志",
        "downloads": "下载",
        "flag_this": "标记",
        "frontend_version": "前端版本",
        "github_stars": "GitHub星级",
        "goto_integrations": "转到集成",
        "hide": "隐藏",
        "hide_beta": "隐藏测试版",
        "install": "安装",
        "installed": "已安装版本",
        "lovelace_copy_example": "复制样例代码到你的剪贴板",
        "lovelace_instruction": "您仍然需要将下列代码添加到lovelace配置中（“ ui-lovelace.yaml”或原始配置编辑器）:",
        "lovelace_no_js_type": "无法确定此元素的类型，请检查存储库。",
        "newest": "最新",
        "note_appdaemon": "您仍然需要将其添加到“ apps.yaml”文件中",
        "note_installed": "安装后，它将位于",
        "note_integration": "您仍然需要将其添加到“ configuration.yaml”文件中",
        "note_plugin": "您仍然需要将其添加到lovelace配置中（“ ui-lovelace.yaml”或原始配置编辑器）ui-lovelace.yaml",
        "note_plugin_post_107": "您仍然需要将其添加到lovelace配置中（“ configuration.yaml”或资源编辑器“/config/lovelace/resources”）",
        "open_issue": "提交问题",
        "open_plugin": "打开元素",
        "reinstall": "重新安装",
        "repository": "存储库",
        "restart_home_assistant": "重新启动Home Assistant",
        "show_beta": "显示测试版",
        "uninstall": "卸载",
        "update_information": "更新信息",
        "upgrade": "升级"
      },
      "search": {
        "placeholder": "搜索存储库"
      },
      "sections": {
        "about": {
          "description": "显示有关HACS的信息",
          "title": "关于"
        },
        "automation": {
          "description": "在这里可以找到python_scripts，AppDaemon应用程序和NetDaemon应用程序",
          "title": "自动化"
        },
        "frontend": {
          "description": "在这里，您可以找到主题，自定义卡片和其他用于lovelace的元素",
          "title": "前端"
        },
        "integrations": {
          "description": "在这里您可以找到自定义集成（custom_components）",
          "title": "集成"
        },
        "pending_repository_upgrade": "您正在运行版本{installed} ，有新版 ({available}) 可用"
      },
      "settings": {
        "add_custom_repository": "添加自定义存储库",
        "adding_new_repo": "添加新的储存库 '{repo}'",
        "adding_new_repo_category": "类别为 '{category}'。",
        "bg_task_custom": "自定义存储库在后台任务运行时被隐藏。",
        "category": "类别",
        "compact_mode": "紧凑模式",
        "custom_repositories": "自定义存储库",
        "delete": "删除",
        "display": "视图方式",
        "grid": "网格",
        "hacs_repo": "HACS存储库",
        "hidden_repositories": "隐藏的存储库",
        "missing_category": "您需要选择一个类别",
        "open_repository": "打开存储库",
        "reload_data": "重载数据",
        "reload_window": "重新加载窗口",
        "repository_configuration": "存储库配置",
        "save": "保存",
        "table": "列表",
        "table_view": "表视图",
        "unhide": "取消隐藏",
        "upgrade_all": "升级全部"
      },
      "store": {
        "ascending": "升序",
        "clear_new": "清除所有新存储库标记",
        "descending": "降序",
        "last_updated": "最近更新时间",
        "name": "名称",
        "new_repositories": "新存储库",
        "new_repositories_note": "这里有多于10个新存储库，如果要清除它们，请单击右上角的3点按钮，然后将其全部忽略。",
        "no_repositories": "没有储存库",
        "no_repositories_desc1": "在本节中您似乎尚未安装任何存储库。",
        "no_repositories_desc2": "单击右下角的+，添加第一个！",
        "no_repositories_found_desc1": "在这里找不到与“ {searchInput} ”匹配的已安装存储库。",
        "no_repositories_found_desc2": "尝试寻找其他东西！",
        "pending_upgrades": "待升级",
        "placeholder_search": "搜索项目...",
        "sort": "排序",
        "stars": "星级",
        "status": "状态"
      },
      "time": {
        "ago": "过去",
        "day": "天",
        "days": "天",
        "hour": "小时",
        "hours": "小时",
        "minute": "分",
        "minutes": "分",
        "month": "月",
        "months": "月",
        "one": "一",
        "one_day_ago": "一天前",
        "one_hour_ago": "一个小时之前",
        "one_minute_ago": "一分钟前",
        "one_month_ago": "一个月前",
        "one_second_ago": "一秒钟前",
        "one_year_ago": "一年前",
        "second": "秒",
        "seconds": "秒",
        "x_days_ago": "{x}天前",
        "x_hours_ago": "{x}小时前",
        "x_minutes_ago": "{x}分钟前",
        "x_months_ago": "{x}个月前",
        "x_seconds_ago": "{x}秒前",
        "x_years_ago": "{x}年前",
        "year": "年",
        "years": "年"
      }
    }
  };

  function localize(string, search = undefined, replace = undefined) {
    let translated;
    const split = string.split(".");
    const lang = (localStorage.getItem("selectedLanguage") || "en").replace(/['"]+/g, "").replace("-", "_");

    try {
      translated = languages[lang];
      split.forEach(section => {
        translated = translated[section];
      });
    } catch (e) {
      translated = languages["en"];
      split.forEach(section => {
        translated = translated[section];
      });
    }

    if (translated === undefined) {
      translated = languages["en"];
      split.forEach(section => {
        translated = translated[section];
      });
    }

    if (search !== undefined && replace !== undefined) {
      translated = translated.replace(search, replace);
    }

    return translated;
  }

  const sections = {
    updates: [],
    messages: [],
    subsections: {
      main: [{
        categories: ["integration"],
        iconPath: mdiPuzzle,
        id: "integrations",
        info: localize("sections.integrations.description"),
        name: localize("sections.integrations.title"),
        path: "/hacs/integrations",
        core: true
      }, {
        categories: ["plugin", "theme"],
        iconPath: mdiPalette,
        id: "frontend",
        info: localize("sections.frontend.description"),
        name: localize("sections.frontend.title"),
        path: "/hacs/frontend",
        core: true
      }, {
        categories: ["python_script", "appdaemon", "netdaemon"],
        iconPath: mdiRobot,
        id: "automation",
        info: localize("sections.automation.description"),
        name: localize("sections.automation.title"),
        path: "/hacs/automation",
        core: true
      }]
    }
  };
  const sectionsEnabled = memoizeOne(config => {
    return sections.subsections.main.filter(section => {
      const categories = section.categories;
      return (categories === null || categories === void 0 ? void 0 : categories.filter(category => {
        var _config$categories;

        return config === null || config === void 0 ? void 0 : (_config$categories = config.categories) === null || _config$categories === void 0 ? void 0 : _config$categories.includes(category);
      }).length) !== 0;
    });
  });
  const activePanel = route => {
    const section = route.path.replace("/", "");
    return sections.subsections.main.find(panel => panel.id == section);
  };

  const addedToLovelace = (hacs, repository) => {
    var _hacs$status, _hacs$resources;

    if (((_hacs$status = hacs.status) === null || _hacs$status === void 0 ? void 0 : _hacs$status.lovelace_mode) !== "storage") {
      return true;
    }

    if (!repository.installed) {
      return true;
    }

    if (repository.category !== "plugin") {
      return true;
    }

    const url = `/hacsfiles/${repository === null || repository === void 0 ? void 0 : repository.full_name.split("/")[1]}/${repository === null || repository === void 0 ? void 0 : repository.file_name}`;
    return (_hacs$resources = hacs.resources) === null || _hacs$resources === void 0 ? void 0 : _hacs$resources.map(resource => resource.url).includes(url);
  };

  class HacsLogger {
    constructor() {
      _defineProperty(this, "configuration", void 0);

      _defineProperty(this, "prefix", "HACS");
    }

    info(log, prefix = "") {
      this.log(log, prefix);
    }

    log(log, prefix = "") {
      const _prefix = prefix ? `[${this.prefix}:${prefix}]` : `[${this.prefix}]`;

      console.log(_prefix, log);
    }

    debug(log, prefix = "") {
      const _prefix = prefix ? `[${this.prefix}:${prefix}]` : `[${this.prefix}]`;

      console.debug(_prefix, log);
    }

  }

  let HacsElement = _decorate(null, function (_initialize, _LitElement) {
    class HacsElement extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsElement,
      d: [{
        kind: "field",
        decorators: [property({
          type: Object
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "method",
        key: "connectedCallback",
        value: async function connectedCallback() {
          _get(_getPrototypeOf(HacsElement.prototype), "connectedCallback", this).call(this);

          this.addEventListener("update-hacs", e => this._updateHacs(e.detail));

          if (this.hacs === undefined) {
            const log = new HacsLogger();
            this.hacs = {
              messages: [],
              updates: [],
              resources: [],
              removed: [],
              sections: [],
              configuration: {},
              status: {},
              localize,
              addedToLovelace,
              log
            };
          }
        }
      }, {
        kind: "method",
        key: "_updateHacs",
        value: function _updateHacs(obj) {
          let shouldUpdate = false;
          Object.keys(obj).forEach(key => {
            if (JSON.stringify(this.hacs[key]) !== JSON.stringify(obj[key])) {
              shouldUpdate = true;
            }
          });

          if (shouldUpdate) {
            this.hacs = { ...this.hacs,
              ...obj
            };
          }
        }
      }, {
        kind: "method",
        key: "updated",
        value: function updated() {
          this.hacs.sections = sectionsEnabled(this.hacs.configuration);
        }
      }]
    };
  }, LitElement);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$1(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$1 = css`:host{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}`;

  /** @soyCompatible */

  let Icon = class Icon extends LitElement {
    /** @soyCompatible */
    render() {
      return html`<slot></slot>`;
    }

  };
  Icon.styles = style$1;
  Icon = __decorate$1([customElement('mwc-icon')], Icon);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$2(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /**
   * @license
   * Copyright 2018 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  /**
   * @fileoverview A "ponyfill" is a polyfill that doesn't modify the global prototype chain.
   * This makes ponyfills safer than traditional polyfills, especially for libraries like MDC.
   */
  function closest(element, selector) {
    if (element.closest) {
      return element.closest(selector);
    }

    var el = element;

    while (el) {
      if (matches$1(el, selector)) {
        return el;
      }

      el = el.parentElement;
    }

    return null;
  }
  function matches$1(element, selector) {
    var nativeMatches = element.matches || element.webkitMatchesSelector || element.msMatchesSelector;
    return nativeMatches.call(element, selector);
  }

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  /**
   * Determines whether a node is an element.
   *
   * @param node Node to check
   */

  const isNodeElement = node => {
    return node.nodeType === Node.ELEMENT_NODE;
  };
  function findAssignedElement(slot, selector) {
    for (const node of slot.assignedNodes({
      flatten: true
    })) {
      if (isNodeElement(node)) {
        const el = node;

        if (matches$1(el, selector)) {
          return el;
        }
      }
    }

    return null;
  }
  function addHasRemoveClass(element) {
    return {
      addClass: className => {
        element.classList.add(className);
      },
      removeClass: className => {
        element.classList.remove(className);
      },
      hasClass: className => element.classList.contains(className)
    };
  }

  const fn = () => {};

  const optionsBlock = {
    get passive() {
      return false;
    }

  };
  document.addEventListener('x', fn, optionsBlock);
  document.removeEventListener('x', fn);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  /** @soyCompatible */

  class BaseElement extends LitElement {
    /**
     * Create and attach the MDC Foundation to the instance
     */
    createFoundation() {
      if (this.mdcFoundation !== undefined) {
        this.mdcFoundation.destroy();
      }

      if (this.mdcFoundationClass) {
        this.mdcFoundation = new this.mdcFoundationClass(this.createAdapter());
        this.mdcFoundation.init();
      }
    }

    firstUpdated() {
      this.createFoundation();
    }

  }

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */

  /* global Reflect, Promise */
  var extendStatics = function (d, b) {
    extendStatics = Object.setPrototypeOf || {
      __proto__: []
    } instanceof Array && function (d, b) {
      d.__proto__ = b;
    } || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    };

    return extendStatics(d, b);
  };

  function __extends(d, b) {
    extendStatics(d, b);

    function __() {
      this.constructor = d;
    }

    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  }
  var __assign = function () {
    __assign = Object.assign || function __assign(t) {
      for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];

        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
      }

      return t;
    };

    return __assign.apply(this, arguments);
  };

  /**
   * @license
   * Copyright 2016 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  var MDCFoundation =
  /** @class */
  function () {
    function MDCFoundation(adapter) {
      if (adapter === void 0) {
        adapter = {};
      }

      this.adapter = adapter;
    }

    Object.defineProperty(MDCFoundation, "cssClasses", {
      get: function () {
        // Classes extending MDCFoundation should implement this method to return an object which exports every
        // CSS class the foundation class needs as a property. e.g. {ACTIVE: 'mdc-component--active'}
        return {};
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCFoundation, "strings", {
      get: function () {
        // Classes extending MDCFoundation should implement this method to return an object which exports all
        // semantic strings as constants. e.g. {ARIA_ROLE: 'tablist'}
        return {};
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCFoundation, "numbers", {
      get: function () {
        // Classes extending MDCFoundation should implement this method to return an object which exports all
        // of its semantic numbers as constants. e.g. {ANIMATION_DELAY_MS: 350}
        return {};
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCFoundation, "defaultAdapter", {
      get: function () {
        // Classes extending MDCFoundation may choose to implement this getter in order to provide a convenient
        // way of viewing the necessary methods of an adapter. In the future, this could also be used for adapter
        // validation.
        return {};
      },
      enumerable: true,
      configurable: true
    });

    MDCFoundation.prototype.init = function () {// Subclasses should override this method to perform initialization routines (registering events, etc.)
    };

    MDCFoundation.prototype.destroy = function () {// Subclasses should override this method to perform de-initialization routines (de-registering events, etc.)
    };

    return MDCFoundation;
  }();

  /**
   * @license
   * Copyright 2016 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  var cssClasses = {
    // Ripple is a special case where the "root" component is really a "mixin" of sorts,
    // given that it's an 'upgrade' to an existing component. That being said it is the root
    // CSS class that all other CSS classes derive from.
    BG_FOCUSED: 'mdc-ripple-upgraded--background-focused',
    FG_ACTIVATION: 'mdc-ripple-upgraded--foreground-activation',
    FG_DEACTIVATION: 'mdc-ripple-upgraded--foreground-deactivation',
    ROOT: 'mdc-ripple-upgraded',
    UNBOUNDED: 'mdc-ripple-upgraded--unbounded'
  };
  var strings = {
    VAR_FG_SCALE: '--mdc-ripple-fg-scale',
    VAR_FG_SIZE: '--mdc-ripple-fg-size',
    VAR_FG_TRANSLATE_END: '--mdc-ripple-fg-translate-end',
    VAR_FG_TRANSLATE_START: '--mdc-ripple-fg-translate-start',
    VAR_LEFT: '--mdc-ripple-left',
    VAR_TOP: '--mdc-ripple-top'
  };
  var numbers = {
    DEACTIVATION_TIMEOUT_MS: 225,
    FG_DEACTIVATION_MS: 150,
    INITIAL_ORIGIN_SCALE: 0.6,
    PADDING: 10,
    TAP_DELAY_MS: 300
  };

  /**
   * Stores result from supportsCssVariables to avoid redundant processing to
   * detect CSS custom variable support.
   */
  function getNormalizedEventCoords(evt, pageOffset, clientRect) {
    if (!evt) {
      return {
        x: 0,
        y: 0
      };
    }

    var x = pageOffset.x,
        y = pageOffset.y;
    var documentX = x + clientRect.left;
    var documentY = y + clientRect.top;
    var normalizedX;
    var normalizedY; // Determine touch point relative to the ripple container.

    if (evt.type === 'touchstart') {
      var touchEvent = evt;
      normalizedX = touchEvent.changedTouches[0].pageX - documentX;
      normalizedY = touchEvent.changedTouches[0].pageY - documentY;
    } else {
      var mouseEvent = evt;
      normalizedX = mouseEvent.pageX - documentX;
      normalizedY = mouseEvent.pageY - documentY;
    }

    return {
      x: normalizedX,
      y: normalizedY
    };
  }

  /**
   * @license
   * Copyright 2016 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  var ACTIVATION_EVENT_TYPES = ['touchstart', 'pointerdown', 'mousedown', 'keydown']; // Deactivation events registered on documentElement when a pointer-related down event occurs

  var POINTER_DEACTIVATION_EVENT_TYPES = ['touchend', 'pointerup', 'mouseup', 'contextmenu']; // simultaneous nested activations

  var activatedTargets = [];

  var MDCRippleFoundation =
  /** @class */
  function (_super) {
    __extends(MDCRippleFoundation, _super);

    function MDCRippleFoundation(adapter) {
      var _this = _super.call(this, __assign(__assign({}, MDCRippleFoundation.defaultAdapter), adapter)) || this;

      _this.activationAnimationHasEnded_ = false;
      _this.activationTimer_ = 0;
      _this.fgDeactivationRemovalTimer_ = 0;
      _this.fgScale_ = '0';
      _this.frame_ = {
        width: 0,
        height: 0
      };
      _this.initialSize_ = 0;
      _this.layoutFrame_ = 0;
      _this.maxRadius_ = 0;
      _this.unboundedCoords_ = {
        left: 0,
        top: 0
      };
      _this.activationState_ = _this.defaultActivationState_();

      _this.activationTimerCallback_ = function () {
        _this.activationAnimationHasEnded_ = true;

        _this.runDeactivationUXLogicIfReady_();
      };

      _this.activateHandler_ = function (e) {
        return _this.activate_(e);
      };

      _this.deactivateHandler_ = function () {
        return _this.deactivate_();
      };

      _this.focusHandler_ = function () {
        return _this.handleFocus();
      };

      _this.blurHandler_ = function () {
        return _this.handleBlur();
      };

      _this.resizeHandler_ = function () {
        return _this.layout();
      };

      return _this;
    }

    Object.defineProperty(MDCRippleFoundation, "cssClasses", {
      get: function () {
        return cssClasses;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCRippleFoundation, "strings", {
      get: function () {
        return strings;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCRippleFoundation, "numbers", {
      get: function () {
        return numbers;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCRippleFoundation, "defaultAdapter", {
      get: function () {
        return {
          addClass: function () {
            return undefined;
          },
          browserSupportsCssVars: function () {
            return true;
          },
          computeBoundingRect: function () {
            return {
              top: 0,
              right: 0,
              bottom: 0,
              left: 0,
              width: 0,
              height: 0
            };
          },
          containsEventTarget: function () {
            return true;
          },
          deregisterDocumentInteractionHandler: function () {
            return undefined;
          },
          deregisterInteractionHandler: function () {
            return undefined;
          },
          deregisterResizeHandler: function () {
            return undefined;
          },
          getWindowPageOffset: function () {
            return {
              x: 0,
              y: 0
            };
          },
          isSurfaceActive: function () {
            return true;
          },
          isSurfaceDisabled: function () {
            return true;
          },
          isUnbounded: function () {
            return true;
          },
          registerDocumentInteractionHandler: function () {
            return undefined;
          },
          registerInteractionHandler: function () {
            return undefined;
          },
          registerResizeHandler: function () {
            return undefined;
          },
          removeClass: function () {
            return undefined;
          },
          updateCssVariable: function () {
            return undefined;
          }
        };
      },
      enumerable: true,
      configurable: true
    });

    MDCRippleFoundation.prototype.init = function () {
      var _this = this;

      var supportsPressRipple = this.supportsPressRipple_();
      this.registerRootHandlers_(supportsPressRipple);

      if (supportsPressRipple) {
        var _a = MDCRippleFoundation.cssClasses,
            ROOT_1 = _a.ROOT,
            UNBOUNDED_1 = _a.UNBOUNDED;
        requestAnimationFrame(function () {
          _this.adapter.addClass(ROOT_1);

          if (_this.adapter.isUnbounded()) {
            _this.adapter.addClass(UNBOUNDED_1); // Unbounded ripples need layout logic applied immediately to set coordinates for both shade and ripple


            _this.layoutInternal_();
          }
        });
      }
    };

    MDCRippleFoundation.prototype.destroy = function () {
      var _this = this;

      if (this.supportsPressRipple_()) {
        if (this.activationTimer_) {
          clearTimeout(this.activationTimer_);
          this.activationTimer_ = 0;
          this.adapter.removeClass(MDCRippleFoundation.cssClasses.FG_ACTIVATION);
        }

        if (this.fgDeactivationRemovalTimer_) {
          clearTimeout(this.fgDeactivationRemovalTimer_);
          this.fgDeactivationRemovalTimer_ = 0;
          this.adapter.removeClass(MDCRippleFoundation.cssClasses.FG_DEACTIVATION);
        }

        var _a = MDCRippleFoundation.cssClasses,
            ROOT_2 = _a.ROOT,
            UNBOUNDED_2 = _a.UNBOUNDED;
        requestAnimationFrame(function () {
          _this.adapter.removeClass(ROOT_2);

          _this.adapter.removeClass(UNBOUNDED_2);

          _this.removeCssVars_();
        });
      }

      this.deregisterRootHandlers_();
      this.deregisterDeactivationHandlers_();
    };
    /**
     * @param evt Optional event containing position information.
     */


    MDCRippleFoundation.prototype.activate = function (evt) {
      this.activate_(evt);
    };

    MDCRippleFoundation.prototype.deactivate = function () {
      this.deactivate_();
    };

    MDCRippleFoundation.prototype.layout = function () {
      var _this = this;

      if (this.layoutFrame_) {
        cancelAnimationFrame(this.layoutFrame_);
      }

      this.layoutFrame_ = requestAnimationFrame(function () {
        _this.layoutInternal_();

        _this.layoutFrame_ = 0;
      });
    };

    MDCRippleFoundation.prototype.setUnbounded = function (unbounded) {
      var UNBOUNDED = MDCRippleFoundation.cssClasses.UNBOUNDED;

      if (unbounded) {
        this.adapter.addClass(UNBOUNDED);
      } else {
        this.adapter.removeClass(UNBOUNDED);
      }
    };

    MDCRippleFoundation.prototype.handleFocus = function () {
      var _this = this;

      requestAnimationFrame(function () {
        return _this.adapter.addClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);
      });
    };

    MDCRippleFoundation.prototype.handleBlur = function () {
      var _this = this;

      requestAnimationFrame(function () {
        return _this.adapter.removeClass(MDCRippleFoundation.cssClasses.BG_FOCUSED);
      });
    };
    /**
     * We compute this property so that we are not querying information about the client
     * until the point in time where the foundation requests it. This prevents scenarios where
     * client-side feature-detection may happen too early, such as when components are rendered on the server
     * and then initialized at mount time on the client.
     */


    MDCRippleFoundation.prototype.supportsPressRipple_ = function () {
      return this.adapter.browserSupportsCssVars();
    };

    MDCRippleFoundation.prototype.defaultActivationState_ = function () {
      return {
        activationEvent: undefined,
        hasDeactivationUXRun: false,
        isActivated: false,
        isProgrammatic: false,
        wasActivatedByPointer: false,
        wasElementMadeActive: false
      };
    };
    /**
     * supportsPressRipple Passed from init to save a redundant function call
     */


    MDCRippleFoundation.prototype.registerRootHandlers_ = function (supportsPressRipple) {
      var _this = this;

      if (supportsPressRipple) {
        ACTIVATION_EVENT_TYPES.forEach(function (evtType) {
          _this.adapter.registerInteractionHandler(evtType, _this.activateHandler_);
        });

        if (this.adapter.isUnbounded()) {
          this.adapter.registerResizeHandler(this.resizeHandler_);
        }
      }

      this.adapter.registerInteractionHandler('focus', this.focusHandler_);
      this.adapter.registerInteractionHandler('blur', this.blurHandler_);
    };

    MDCRippleFoundation.prototype.registerDeactivationHandlers_ = function (evt) {
      var _this = this;

      if (evt.type === 'keydown') {
        this.adapter.registerInteractionHandler('keyup', this.deactivateHandler_);
      } else {
        POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {
          _this.adapter.registerDocumentInteractionHandler(evtType, _this.deactivateHandler_);
        });
      }
    };

    MDCRippleFoundation.prototype.deregisterRootHandlers_ = function () {
      var _this = this;

      ACTIVATION_EVENT_TYPES.forEach(function (evtType) {
        _this.adapter.deregisterInteractionHandler(evtType, _this.activateHandler_);
      });
      this.adapter.deregisterInteractionHandler('focus', this.focusHandler_);
      this.adapter.deregisterInteractionHandler('blur', this.blurHandler_);

      if (this.adapter.isUnbounded()) {
        this.adapter.deregisterResizeHandler(this.resizeHandler_);
      }
    };

    MDCRippleFoundation.prototype.deregisterDeactivationHandlers_ = function () {
      var _this = this;

      this.adapter.deregisterInteractionHandler('keyup', this.deactivateHandler_);
      POINTER_DEACTIVATION_EVENT_TYPES.forEach(function (evtType) {
        _this.adapter.deregisterDocumentInteractionHandler(evtType, _this.deactivateHandler_);
      });
    };

    MDCRippleFoundation.prototype.removeCssVars_ = function () {
      var _this = this;

      var rippleStrings = MDCRippleFoundation.strings;
      var keys = Object.keys(rippleStrings);
      keys.forEach(function (key) {
        if (key.indexOf('VAR_') === 0) {
          _this.adapter.updateCssVariable(rippleStrings[key], null);
        }
      });
    };

    MDCRippleFoundation.prototype.activate_ = function (evt) {
      var _this = this;

      if (this.adapter.isSurfaceDisabled()) {
        return;
      }

      var activationState = this.activationState_;

      if (activationState.isActivated) {
        return;
      } // Avoid reacting to follow-on events fired by touch device after an already-processed user interaction


      var previousActivationEvent = this.previousActivationEvent_;
      var isSameInteraction = previousActivationEvent && evt !== undefined && previousActivationEvent.type !== evt.type;

      if (isSameInteraction) {
        return;
      }

      activationState.isActivated = true;
      activationState.isProgrammatic = evt === undefined;
      activationState.activationEvent = evt;
      activationState.wasActivatedByPointer = activationState.isProgrammatic ? false : evt !== undefined && (evt.type === 'mousedown' || evt.type === 'touchstart' || evt.type === 'pointerdown');
      var hasActivatedChild = evt !== undefined && activatedTargets.length > 0 && activatedTargets.some(function (target) {
        return _this.adapter.containsEventTarget(target);
      });

      if (hasActivatedChild) {
        // Immediately reset activation state, while preserving logic that prevents touch follow-on events
        this.resetActivationState_();
        return;
      }

      if (evt !== undefined) {
        activatedTargets.push(evt.target);
        this.registerDeactivationHandlers_(evt);
      }

      activationState.wasElementMadeActive = this.checkElementMadeActive_(evt);

      if (activationState.wasElementMadeActive) {
        this.animateActivation_();
      }

      requestAnimationFrame(function () {
        // Reset array on next frame after the current event has had a chance to bubble to prevent ancestor ripples
        activatedTargets = [];

        if (!activationState.wasElementMadeActive && evt !== undefined && (evt.key === ' ' || evt.keyCode === 32)) {
          // If space was pressed, try again within an rAF call to detect :active, because different UAs report
          // active states inconsistently when they're called within event handling code:
          // - https://bugs.chromium.org/p/chromium/issues/detail?id=635971
          // - https://bugzilla.mozilla.org/show_bug.cgi?id=1293741
          // We try first outside rAF to support Edge, which does not exhibit this problem, but will crash if a CSS
          // variable is set within a rAF callback for a submit button interaction (#2241).
          activationState.wasElementMadeActive = _this.checkElementMadeActive_(evt);

          if (activationState.wasElementMadeActive) {
            _this.animateActivation_();
          }
        }

        if (!activationState.wasElementMadeActive) {
          // Reset activation state immediately if element was not made active.
          _this.activationState_ = _this.defaultActivationState_();
        }
      });
    };

    MDCRippleFoundation.prototype.checkElementMadeActive_ = function (evt) {
      return evt !== undefined && evt.type === 'keydown' ? this.adapter.isSurfaceActive() : true;
    };

    MDCRippleFoundation.prototype.animateActivation_ = function () {
      var _this = this;

      var _a = MDCRippleFoundation.strings,
          VAR_FG_TRANSLATE_START = _a.VAR_FG_TRANSLATE_START,
          VAR_FG_TRANSLATE_END = _a.VAR_FG_TRANSLATE_END;
      var _b = MDCRippleFoundation.cssClasses,
          FG_DEACTIVATION = _b.FG_DEACTIVATION,
          FG_ACTIVATION = _b.FG_ACTIVATION;
      var DEACTIVATION_TIMEOUT_MS = MDCRippleFoundation.numbers.DEACTIVATION_TIMEOUT_MS;
      this.layoutInternal_();
      var translateStart = '';
      var translateEnd = '';

      if (!this.adapter.isUnbounded()) {
        var _c = this.getFgTranslationCoordinates_(),
            startPoint = _c.startPoint,
            endPoint = _c.endPoint;

        translateStart = startPoint.x + "px, " + startPoint.y + "px";
        translateEnd = endPoint.x + "px, " + endPoint.y + "px";
      }

      this.adapter.updateCssVariable(VAR_FG_TRANSLATE_START, translateStart);
      this.adapter.updateCssVariable(VAR_FG_TRANSLATE_END, translateEnd); // Cancel any ongoing activation/deactivation animations

      clearTimeout(this.activationTimer_);
      clearTimeout(this.fgDeactivationRemovalTimer_);
      this.rmBoundedActivationClasses_();
      this.adapter.removeClass(FG_DEACTIVATION); // Force layout in order to re-trigger the animation.

      this.adapter.computeBoundingRect();
      this.adapter.addClass(FG_ACTIVATION);
      this.activationTimer_ = setTimeout(function () {
        return _this.activationTimerCallback_();
      }, DEACTIVATION_TIMEOUT_MS);
    };

    MDCRippleFoundation.prototype.getFgTranslationCoordinates_ = function () {
      var _a = this.activationState_,
          activationEvent = _a.activationEvent,
          wasActivatedByPointer = _a.wasActivatedByPointer;
      var startPoint;

      if (wasActivatedByPointer) {
        startPoint = getNormalizedEventCoords(activationEvent, this.adapter.getWindowPageOffset(), this.adapter.computeBoundingRect());
      } else {
        startPoint = {
          x: this.frame_.width / 2,
          y: this.frame_.height / 2
        };
      } // Center the element around the start point.


      startPoint = {
        x: startPoint.x - this.initialSize_ / 2,
        y: startPoint.y - this.initialSize_ / 2
      };
      var endPoint = {
        x: this.frame_.width / 2 - this.initialSize_ / 2,
        y: this.frame_.height / 2 - this.initialSize_ / 2
      };
      return {
        startPoint: startPoint,
        endPoint: endPoint
      };
    };

    MDCRippleFoundation.prototype.runDeactivationUXLogicIfReady_ = function () {
      var _this = this; // This method is called both when a pointing device is released, and when the activation animation ends.
      // The deactivation animation should only run after both of those occur.


      var FG_DEACTIVATION = MDCRippleFoundation.cssClasses.FG_DEACTIVATION;
      var _a = this.activationState_,
          hasDeactivationUXRun = _a.hasDeactivationUXRun,
          isActivated = _a.isActivated;
      var activationHasEnded = hasDeactivationUXRun || !isActivated;

      if (activationHasEnded && this.activationAnimationHasEnded_) {
        this.rmBoundedActivationClasses_();
        this.adapter.addClass(FG_DEACTIVATION);
        this.fgDeactivationRemovalTimer_ = setTimeout(function () {
          _this.adapter.removeClass(FG_DEACTIVATION);
        }, numbers.FG_DEACTIVATION_MS);
      }
    };

    MDCRippleFoundation.prototype.rmBoundedActivationClasses_ = function () {
      var FG_ACTIVATION = MDCRippleFoundation.cssClasses.FG_ACTIVATION;
      this.adapter.removeClass(FG_ACTIVATION);
      this.activationAnimationHasEnded_ = false;
      this.adapter.computeBoundingRect();
    };

    MDCRippleFoundation.prototype.resetActivationState_ = function () {
      var _this = this;

      this.previousActivationEvent_ = this.activationState_.activationEvent;
      this.activationState_ = this.defaultActivationState_(); // Touch devices may fire additional events for the same interaction within a short time.
      // Store the previous event until it's safe to assume that subsequent events are for new interactions.

      setTimeout(function () {
        return _this.previousActivationEvent_ = undefined;
      }, MDCRippleFoundation.numbers.TAP_DELAY_MS);
    };

    MDCRippleFoundation.prototype.deactivate_ = function () {
      var _this = this;

      var activationState = this.activationState_; // This can happen in scenarios such as when you have a keyup event that blurs the element.

      if (!activationState.isActivated) {
        return;
      }

      var state = __assign({}, activationState);

      if (activationState.isProgrammatic) {
        requestAnimationFrame(function () {
          return _this.animateDeactivation_(state);
        });
        this.resetActivationState_();
      } else {
        this.deregisterDeactivationHandlers_();
        requestAnimationFrame(function () {
          _this.activationState_.hasDeactivationUXRun = true;

          _this.animateDeactivation_(state);

          _this.resetActivationState_();
        });
      }
    };

    MDCRippleFoundation.prototype.animateDeactivation_ = function (_a) {
      var wasActivatedByPointer = _a.wasActivatedByPointer,
          wasElementMadeActive = _a.wasElementMadeActive;

      if (wasActivatedByPointer || wasElementMadeActive) {
        this.runDeactivationUXLogicIfReady_();
      }
    };

    MDCRippleFoundation.prototype.layoutInternal_ = function () {
      var _this = this;

      this.frame_ = this.adapter.computeBoundingRect();
      var maxDim = Math.max(this.frame_.height, this.frame_.width); // Surface diameter is treated differently for unbounded vs. bounded ripples.
      // Unbounded ripple diameter is calculated smaller since the surface is expected to already be padded appropriately
      // to extend the hitbox, and the ripple is expected to meet the edges of the padded hitbox (which is typically
      // square). Bounded ripples, on the other hand, are fully expected to expand beyond the surface's longest diameter
      // (calculated based on the diagonal plus a constant padding), and are clipped at the surface's border via
      // `overflow: hidden`.

      var getBoundedRadius = function () {
        var hypotenuse = Math.sqrt(Math.pow(_this.frame_.width, 2) + Math.pow(_this.frame_.height, 2));
        return hypotenuse + MDCRippleFoundation.numbers.PADDING;
      };

      this.maxRadius_ = this.adapter.isUnbounded() ? maxDim : getBoundedRadius(); // Ripple is sized as a fraction of the largest dimension of the surface, then scales up using a CSS scale transform

      var initialSize = Math.floor(maxDim * MDCRippleFoundation.numbers.INITIAL_ORIGIN_SCALE); // Unbounded ripple size should always be even number to equally center align.

      if (this.adapter.isUnbounded() && initialSize % 2 !== 0) {
        this.initialSize_ = initialSize - 1;
      } else {
        this.initialSize_ = initialSize;
      }

      this.fgScale_ = "" + this.maxRadius_ / this.initialSize_;
      this.updateLayoutCssVars_();
    };

    MDCRippleFoundation.prototype.updateLayoutCssVars_ = function () {
      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;
      this.adapter.updateCssVariable(VAR_FG_SIZE, this.initialSize_ + "px");
      this.adapter.updateCssVariable(VAR_FG_SCALE, this.fgScale_);

      if (this.adapter.isUnbounded()) {
        this.unboundedCoords_ = {
          left: Math.round(this.frame_.width / 2 - this.initialSize_ / 2),
          top: Math.round(this.frame_.height / 2 - this.initialSize_ / 2)
        };
        this.adapter.updateCssVariable(VAR_LEFT, this.unboundedCoords_.left + "px");
        this.adapter.updateCssVariable(VAR_TOP, this.unboundedCoords_.top + "px");
      }
    };

    return MDCRippleFoundation;
  }(MDCFoundation);

  /**
   * @license
   * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */

  class ClassList {
    constructor(element) {
      this.classes = new Set();
      this.changed = false;
      this.element = element;
      const classList = (element.getAttribute('class') || '').split(/\s+/);

      for (const cls of classList) {
        this.classes.add(cls);
      }
    }

    add(cls) {
      this.classes.add(cls);
      this.changed = true;
    }

    remove(cls) {
      this.classes.delete(cls);
      this.changed = true;
    }

    commit() {
      if (this.changed) {
        let classString = '';
        this.classes.forEach(cls => classString += cls + ' ');
        this.element.setAttribute('class', classString);
      }
    }

  }
  /**
   * Stores the ClassInfo object applied to a given AttributePart.
   * Used to unset existing values when a new ClassInfo object is applied.
   */


  const previousClassesCache = new WeakMap();
  /**
   * A directive that applies CSS classes. This must be used in the `class`
   * attribute and must be the only part used in the attribute. It takes each
   * property in the `classInfo` argument and adds the property name to the
   * element's `class` if the property value is truthy; if the property value is
   * falsey, the property name is removed from the element's `class`. For example
   * `{foo: bar}` applies the class `foo` if the value of `bar` is truthy.
   * @param classInfo {ClassInfo}
   */

  const classMap = directive(classInfo => part => {
    if (!(part instanceof AttributePart) || part instanceof PropertyPart || part.committer.name !== 'class' || part.committer.parts.length > 1) {
      throw new Error('The `classMap` directive must be used in the `class` attribute ' + 'and must be the only part in the attribute.');
    }

    const {
      committer
    } = part;
    const {
      element
    } = committer;
    let previousClasses = previousClassesCache.get(part);

    if (previousClasses === undefined) {
      // Write static classes once
      // Use setAttribute() because className isn't a string on SVG elements
      element.setAttribute('class', committer.strings.join(' '));
      previousClassesCache.set(part, previousClasses = new Set());
    }

    const classList = element.classList || new ClassList(element); // Remove old classes that no longer apply
    // We use forEach() instead of for-of so that re don't require down-level
    // iteration.

    previousClasses.forEach(name => {
      if (!(name in classInfo)) {
        classList.remove(name);
        previousClasses.delete(name);
      }
    }); // Add or remove classes based on their classMap value

    for (const name in classInfo) {
      const value = classInfo[name];

      if (value != previousClasses.has(name)) {
        // We explicitly want a loose truthy check of `value` because it seems
        // more convenient that '' and 0 are skipped.
        if (value) {
          classList.add(name);
          previousClasses.add(name);
        } else {
          classList.remove(name);
          previousClasses.delete(name);
        }
      }
    }

    if (typeof classList.commit === 'function') {
      classList.commit();
    }
  });

  /**
   * @license
   * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  /**
   * Stores the StyleInfo object applied to a given AttributePart.
   * Used to unset existing values when a new StyleInfo object is applied.
   */

  const previousStylePropertyCache = new WeakMap();
  /**
   * A directive that applies CSS properties to an element.
   *
   * `styleMap` can only be used in the `style` attribute and must be the only
   * expression in the attribute. It takes the property names in the `styleInfo`
   * object and adds the property values as CSS properties. Property names with
   * dashes (`-`) are assumed to be valid CSS property names and set on the
   * element's style object using `setProperty()`. Names without dashes are
   * assumed to be camelCased JavaScript property names and set on the element's
   * style object using property assignment, allowing the style object to
   * translate JavaScript-style names to CSS property names.
   *
   * For example `styleMap({backgroundColor: 'red', 'border-top': '5px', '--size':
   * '0'})` sets the `background-color`, `border-top` and `--size` properties.
   *
   * @param styleInfo {StyleInfo}
   */

  const styleMap = directive(styleInfo => part => {
    if (!(part instanceof AttributePart) || part instanceof PropertyPart || part.committer.name !== 'style' || part.committer.parts.length > 1) {
      throw new Error('The `styleMap` directive must be used in the style attribute ' + 'and must be the only part in the attribute.');
    }

    const {
      committer
    } = part;
    const {
      style
    } = committer.element;
    let previousStyleProperties = previousStylePropertyCache.get(part);

    if (previousStyleProperties === undefined) {
      // Write static styles once
      style.cssText = committer.strings.join(' ');
      previousStylePropertyCache.set(part, previousStyleProperties = new Set());
    } // Remove old properties that no longer exist in styleInfo
    // We use forEach() instead of for-of so that re don't require down-level
    // iteration.


    previousStyleProperties.forEach(name => {
      if (!(name in styleInfo)) {
        previousStyleProperties.delete(name);

        if (name.indexOf('-') === -1) {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          style[name] = null;
        } else {
          style.removeProperty(name);
        }
      }
    }); // Add or update properties

    for (const name in styleInfo) {
      previousStyleProperties.add(name);

      if (name.indexOf('-') === -1) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        style[name] = styleInfo[name];
      } else {
        style.setProperty(name, styleInfo[name]);
      }
    }
  });

  /** @soyCompatible */

  class RippleBase extends BaseElement {
    constructor() {
      super(...arguments);
      this.primary = false;
      this.accent = false;
      this.unbounded = false;
      this.disabled = false;
      this.activated = false;
      this.selected = false;
      this.hovering = false;
      this.bgFocused = false;
      this.fgActivation = false;
      this.fgDeactivation = false;
      this.fgScale = '';
      this.fgSize = '';
      this.translateStart = '';
      this.translateEnd = '';
      this.leftPos = '';
      this.topPos = '';
      this.mdcFoundationClass = MDCRippleFoundation;
    }

    get isActive() {
      return (this.parentElement || this).matches(':active');
    }

    createAdapter() {
      return {
        browserSupportsCssVars: () => true,
        isUnbounded: () => this.unbounded,
        isSurfaceActive: () => this.isActive,
        isSurfaceDisabled: () => this.disabled,
        addClass: className => {
          switch (className) {
            case 'mdc-ripple-upgraded--background-focused':
              this.bgFocused = true;
              break;

            case 'mdc-ripple-upgraded--foreground-activation':
              this.fgActivation = true;
              break;

            case 'mdc-ripple-upgraded--foreground-deactivation':
              this.fgDeactivation = true;
              break;
          }
        },
        removeClass: className => {
          switch (className) {
            case 'mdc-ripple-upgraded--background-focused':
              this.bgFocused = false;
              break;

            case 'mdc-ripple-upgraded--foreground-activation':
              this.fgActivation = false;
              break;

            case 'mdc-ripple-upgraded--foreground-deactivation':
              this.fgDeactivation = false;
              break;
          }
        },
        containsEventTarget: () => true,
        registerInteractionHandler: () => undefined,
        deregisterInteractionHandler: () => undefined,
        registerDocumentInteractionHandler: () => undefined,
        deregisterDocumentInteractionHandler: () => undefined,
        registerResizeHandler: () => undefined,
        deregisterResizeHandler: () => undefined,
        updateCssVariable: (varName, value) => {
          switch (varName) {
            case '--mdc-ripple-fg-scale':
              this.fgScale = value;
              break;

            case '--mdc-ripple-fg-size':
              this.fgSize = value;
              break;

            case '--mdc-ripple-fg-translate-end':
              this.translateEnd = value;
              break;

            case '--mdc-ripple-fg-translate-start':
              this.translateStart = value;
              break;

            case '--mdc-ripple-left':
              this.leftPos = value;
              break;

            case '--mdc-ripple-top':
              this.topPos = value;
              break;
          }
        },
        computeBoundingRect: () => (this.parentElement || this).getBoundingClientRect(),
        getWindowPageOffset: () => ({
          x: window.pageXOffset,
          y: window.pageYOffset
        })
      };
    }

    startPress(ev) {
      this.waitForFoundation(() => {
        this.mdcFoundation.activate(ev);
      });
    }

    endPress() {
      this.waitForFoundation(() => {
        this.mdcFoundation.deactivate();
      });
    }

    startFocus() {
      this.waitForFoundation(() => {
        this.mdcFoundation.handleFocus();
      });
    }

    endFocus() {
      this.waitForFoundation(() => {
        this.mdcFoundation.handleBlur();
      });
    }

    startHover() {
      this.hovering = true;
    }

    endHover() {
      this.hovering = false;
    }
    /**
     * Wait for the MDCFoundation to be created by `firstUpdated`
     */


    waitForFoundation(fn) {
      if (this.mdcFoundation) {
        fn();
      } else {
        this.updateComplete.then(fn);
      }
    }
    /** @soyCompatible */


    render() {
      /** @classMap */
      const classes = {
        'mdc-ripple-upgraded--unbounded': this.unbounded,
        'mdc-ripple-upgraded--background-focused': this.bgFocused,
        'mdc-ripple-upgraded--foreground-activation': this.fgActivation,
        'mdc-ripple-upgraded--foreground-deactivation': this.fgDeactivation,
        'hover': this.hovering,
        'primary': this.primary,
        'accent': this.accent,
        'disabled': this.disabled,
        'activated': this.activated,
        'selected': this.selected
      };
      return html`
        <div class="mdc-ripple-surface mdc-ripple-upgraded ${classMap(classes)}"
          style="${styleMap({
      '--mdc-ripple-fg-scale': this.fgScale,
      '--mdc-ripple-fg-size': this.fgSize,
      '--mdc-ripple-fg-translate-end': this.translateEnd,
      '--mdc-ripple-fg-translate-start': this.translateStart,
      '--mdc-ripple-left': this.leftPos,
      '--mdc-ripple-top': this.topPos
    })}"></div>`;
    }

  }

  __decorate$2([query('.mdc-ripple-surface')], RippleBase.prototype, "mdcRoot", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "primary", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "accent", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "unbounded", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "disabled", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "activated", void 0);

  __decorate$2([property({
    type: Boolean
  })], RippleBase.prototype, "selected", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "hovering", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "bgFocused", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "fgActivation", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "fgDeactivation", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "fgScale", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "fgSize", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "translateStart", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "translateEnd", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "leftPos", void 0);

  __decorate$2([internalProperty()], RippleBase.prototype, "topPos", void 0);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$2 = css`:host{display:block;position:absolute;top:0;bottom:0;left:0;right:0;width:100%;height:100%;pointer-events:none}@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}}:host .primary{--mdc-ripple-color: var(--mdc-theme-primary, #6200ee)}:host .accent{--mdc-ripple-color: var( --mdc-theme-secondary, #018786 )}:host .mdc-ripple-surface{top:0;bottom:0;left:0;right:0;width:100%;height:100%;pointer-events:none;--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity;position:relative;outline:none;overflow:hidden;--mdc-ripple-fg-opacity: var( --mdc-ripple-press-opacity, 0.12 )}:host .mdc-ripple-surface::before,:host .mdc-ripple-surface::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}:host .mdc-ripple-surface::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1}:host .mdc-ripple-surface.mdc-ripple-upgraded::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}:host .mdc-ripple-surface.mdc-ripple-upgraded::after{top:0;left:0;transform:scale(0);transform-origin:center center}:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}:host .mdc-ripple-surface.mdc-ripple-upgraded--foreground-activation::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}:host .mdc-ripple-surface.mdc-ripple-upgraded--foreground-deactivation::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}:host .mdc-ripple-surface::before,:host .mdc-ripple-surface::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}:host .mdc-ripple-surface.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded{overflow:visible}:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded::before,:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded::after{top:calc(50% - 50%);left:calc(50% - 50%);width:100%;height:100%}:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::before,:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::after{top:var(--mdc-ripple-top, calc(50% - 50%));left:var(--mdc-ripple-left, calc(50% - 50%));width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}:host .mdc-ripple-surface.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}:host .mdc-ripple-surface.hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}:host .mdc-ripple-surface.mdc-ripple-upgraded--background-focused::before{opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}:host .mdc-ripple-surface::before,:host .mdc-ripple-surface::after{background-color:#000;background-color:var(--mdc-ripple-color, #000)}:host .mdc-ripple-surface.activated{--mdc-ripple-press-opacity: calc( var(--mdc-ripple-press-opacity, 0.12) + 0.12 )}:host .mdc-ripple-surface.activated::before{opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12)}:host .mdc-ripple-surface.activated.hover::before{opacity:.16;opacity:calc( var(--mdc-ripple-hover-opacity, 0.04) + var(--mdc-ripple-activated-opacity, 0.12) )}:host .mdc-ripple-surface.activated.mdc-ripple-upgraded--background-focused::before{opacity:.24;opacity:calc( var(--mdc-ripple-focus-opacity, 0.12) + var(--mdc-ripple-activated-opacity, 0.12) )}:host .mdc-ripple-surface.selected{--mdc-ripple-press-opacity: calc( var(--mdc-ripple-press-opacity, 0.12) + 0.08 )}:host .mdc-ripple-surface.selected::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}:host .mdc-ripple-surface.selected.hover::before{opacity:.12;opacity:calc( var(--mdc-ripple-hover-opacity, 0.04) + var(--mdc-ripple-selected-opacity, 0.08) )}:host .mdc-ripple-surface.selected.mdc-ripple-upgraded--background-focused::before{opacity:.2;opacity:calc( var(--mdc-ripple-focus-opacity, 0.12) + var(--mdc-ripple-selected-opacity, 0.08) )}:host .mdc-ripple-surface.disabled{--mdc-ripple-color: transparent}:host .mdc-ripple-surface::before{z-index:1;z-index:var(--m-ripple-z-index, 1)}:host .mdc-ripple-surface::after{z-index:0;z-index:var(--m-ripple-z-index, 0)}`;

  /** @soyCompatible */

  let Ripple = class Ripple extends RippleBase {};
  Ripple.styles = style$2;
  Ripple = __decorate$2([customElement('mwc-ripple')], Ripple);

  /**
  @license
  Copyright 2020 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */

  /**
   * Class that encapsulates the events handlers for `mwc-ripple`
   *
   *
   * Example:
   * ```
   * class XFoo extends LitElement {
   *   async getRipple() {
   *     this.renderRipple = true;
   *     await this.updateComplete;
   *     return this.renderRoot.querySelector('mwc-ripple');
   *   }
   *   rippleHandlers = new RippleHandlers(() => this.getRipple());
   *
   *   render() {
   *     return html`
   *       <div @mousedown=${this.rippleHandlers.startPress}></div>
   *       ${this.renderRipple ? html`<mwc-ripple></mwc-ripple>` : ''}
   *     `;
   *   }
   * }
   * ```
   */
  class RippleHandlers {
    constructor(
    /** Function that returns a `mwc-ripple` */
    rippleFn) {
      this.startPress = ev => {
        rippleFn().then(r => {
          r && r.startPress(ev);
        });
      };

      this.endPress = () => {
        rippleFn().then(r => {
          r && r.endPress();
        });
      };

      this.startFocus = () => {
        rippleFn().then(r => {
          r && r.startFocus();
        });
      };

      this.endFocus = () => {
        rippleFn().then(r => {
          r && r.endFocus();
        });
      };

      this.startHover = () => {
        rippleFn().then(r => {
          r && r.startHover();
        });
      };

      this.endHover = () => {
        rippleFn().then(r => {
          r && r.endHover();
        });
      };
    }

  }

  /** @soyCompatible */

  class ButtonBase extends LitElement {
    constructor() {
      super(...arguments);
      this.raised = false;
      this.unelevated = false;
      this.outlined = false;
      this.dense = false;
      this.disabled = false;
      this.trailingIcon = false;
      this.fullwidth = false;
      this.icon = '';
      this.label = '';
      this.shouldRenderRipple = false;
      this.rippleHandlers = new RippleHandlers(() => {
        this.shouldRenderRipple = true;
        return this.ripple;
      });
    }
    /** @soyCompatible */


    renderRipple() {
      const filled = this.raised || this.unelevated;
      return html`${this.shouldRenderRipple ? html`<mwc-ripple .primary="${!filled}" .disabled="${this.disabled}"></mwc-ripple>` : ''}`;
    }

    createRenderRoot() {
      return this.attachShadow({
        mode: 'open',
        delegatesFocus: true
      });
    }

    focus() {
      const buttonElement = this.buttonElement;

      if (buttonElement) {
        this.rippleHandlers.startFocus();
        buttonElement.focus();
      }
    }

    blur() {
      const buttonElement = this.buttonElement;

      if (buttonElement) {
        this.rippleHandlers.endFocus();
        buttonElement.blur();
      }
    }
    /** @soyCompatible */


    render() {
      /** @classMap */
      const classes = {
        'mdc-button--raised': this.raised,
        'mdc-button--unelevated': this.unelevated,
        'mdc-button--outlined': this.outlined,
        'mdc-button--dense': this.dense
      };
      return html`
      <button
          id="button"
          class="mdc-button ${classMap(classes)}"
          ?disabled="${this.disabled}"
          aria-label="${this.label || this.icon}"
          @focus="${this.handleRippleFocus}"
          @blur="${this.handleRippleBlur}"
          @mousedown="${this.handleRippleActivate}"
          @mouseenter="${this.handleRippleMouseEnter}"
          @mouseleave="${this.handleRippleMouseLeave}"
          @touchstart="${this.handleRippleActivate}"
          @touchend="${this.handleRippleDeactivate}"
          @touchcancel="${this.handleRippleDeactivate}">
        ${this.renderRipple()}
        <span class="leading-icon">
          <slot name="icon">
            ${this.icon && !this.trailingIcon ? this.renderIcon() : ''}
          </slot>
        </span>
        <span class="mdc-button__label">${this.label}</span>
        <slot></slot>
        <span class="trailing-icon">
          <slot name="trailingIcon">
            ${this.icon && this.trailingIcon ? this.renderIcon() : ''}
          </slot>
        </span>
      </button>`;
    }
    /** @soyCompatible */


    renderIcon() {
      return html`
    <mwc-icon class="mdc-button__icon">
      ${this.icon}
    </mwc-icon>`;
    }

    handleRippleActivate(evt) {
      const onUp = () => {
        window.removeEventListener('mouseup', onUp);
        this.handleRippleDeactivate();
      };

      window.addEventListener('mouseup', onUp);
      this.rippleHandlers.startPress(evt);
    }

    handleRippleDeactivate() {
      this.rippleHandlers.endPress();
    }

    handleRippleMouseEnter() {
      this.rippleHandlers.startHover();
    }

    handleRippleMouseLeave() {
      this.rippleHandlers.endHover();
    }

    handleRippleFocus() {
      this.rippleHandlers.startFocus();
    }

    handleRippleBlur() {
      this.rippleHandlers.endFocus();
    }

  }

  __decorate([property({
    type: Boolean
  })], ButtonBase.prototype, "raised", void 0);

  __decorate([property({
    type: Boolean
  })], ButtonBase.prototype, "unelevated", void 0);

  __decorate([property({
    type: Boolean
  })], ButtonBase.prototype, "outlined", void 0);

  __decorate([property({
    type: Boolean
  })], ButtonBase.prototype, "dense", void 0);

  __decorate([property({
    type: Boolean,
    reflect: true
  })], ButtonBase.prototype, "disabled", void 0);

  __decorate([property({
    type: Boolean,
    attribute: 'trailingicon'
  })], ButtonBase.prototype, "trailingIcon", void 0);

  __decorate([property({
    type: Boolean,
    reflect: true
  })], ButtonBase.prototype, "fullwidth", void 0);

  __decorate([property({
    type: String
  })], ButtonBase.prototype, "icon", void 0);

  __decorate([property({
    type: String
  })], ButtonBase.prototype, "label", void 0);

  __decorate([query('#button')], ButtonBase.prototype, "buttonElement", void 0);

  __decorate([queryAsync('mwc-ripple')], ButtonBase.prototype, "ripple", void 0);

  __decorate([internalProperty()], ButtonBase.prototype, "shouldRenderRipple", void 0);

  __decorate([eventOptions({
    passive: true
  })], ButtonBase.prototype, "handleRippleActivate", null);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$3 = css`.mdc-touch-target-wrapper{display:inline}.mdc-elevation-overlay{position:absolute;border-radius:inherit;pointer-events:none;opacity:0;opacity:var(--mdc-elevation-overlay-opacity, 0);transition:opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);background-color:#fff;background-color:var(--mdc-elevation-overlay-color, #fff)}.mdc-button{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-button-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-button-font-size, 0.875rem);line-height:2.25rem;line-height:var(--mdc-typography-button-line-height, 2.25rem);font-weight:500;font-weight:var(--mdc-typography-button-font-weight, 500);letter-spacing:0.0892857143em;letter-spacing:var(--mdc-typography-button-letter-spacing, 0.0892857143em);text-decoration:none;text-decoration:var(--mdc-typography-button-text-decoration, none);text-transform:uppercase;text-transform:var(--mdc-typography-button-text-transform, uppercase);padding:0 8px 0 8px;position:relative;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:64px;border:none;outline:none;line-height:inherit;user-select:none;-webkit-appearance:none;overflow:visible;vertical-align:middle;border-radius:4px;border-radius:var(--mdc-shape-small, 4px);height:36px}.mdc-button .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}.mdc-button::-moz-focus-inner{padding:0;border:0}.mdc-button:active{outline:none}.mdc-button:hover{cursor:pointer}.mdc-button:disabled{cursor:default;pointer-events:none}.mdc-button .mdc-button__ripple{border-radius:4px;border-radius:var(--mdc-shape-small, 4px)}.mdc-button:not(:disabled){background-color:transparent}.mdc-button:disabled{background-color:transparent}.mdc-button .mdc-button__icon{margin-left:0;margin-right:8px;display:inline-block;width:18px;height:18px;font-size:18px;vertical-align:top}[dir=rtl] .mdc-button .mdc-button__icon,.mdc-button .mdc-button__icon[dir=rtl]{margin-left:8px;margin-right:0}.mdc-button .mdc-button__touch{position:absolute;top:50%;right:0;height:48px;left:0;transform:translateY(-50%)}.mdc-button:not(:disabled){color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}.mdc-button:disabled{color:rgba(0,0,0,.38)}.mdc-button__label+.mdc-button__icon{margin-left:8px;margin-right:0}[dir=rtl] .mdc-button__label+.mdc-button__icon,.mdc-button__label+.mdc-button__icon[dir=rtl]{margin-left:0;margin-right:8px}svg.mdc-button__icon{fill:currentColor}.mdc-button--raised .mdc-button__icon,.mdc-button--unelevated .mdc-button__icon,.mdc-button--outlined .mdc-button__icon{margin-left:-4px;margin-right:8px}[dir=rtl] .mdc-button--raised .mdc-button__icon,.mdc-button--raised .mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--unelevated .mdc-button__icon,.mdc-button--unelevated .mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--outlined .mdc-button__icon,.mdc-button--outlined .mdc-button__icon[dir=rtl]{margin-left:8px;margin-right:-4px}.mdc-button--raised .mdc-button__label+.mdc-button__icon,.mdc-button--unelevated .mdc-button__label+.mdc-button__icon,.mdc-button--outlined .mdc-button__label+.mdc-button__icon{margin-left:8px;margin-right:-4px}[dir=rtl] .mdc-button--raised .mdc-button__label+.mdc-button__icon,.mdc-button--raised .mdc-button__label+.mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--unelevated .mdc-button__label+.mdc-button__icon,.mdc-button--unelevated .mdc-button__label+.mdc-button__icon[dir=rtl],[dir=rtl] .mdc-button--outlined .mdc-button__label+.mdc-button__icon,.mdc-button--outlined .mdc-button__label+.mdc-button__icon[dir=rtl]{margin-left:-4px;margin-right:8px}.mdc-button--raised,.mdc-button--unelevated{padding:0 16px 0 16px}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){background-color:#6200ee;background-color:var(--mdc-theme-primary, #6200ee)}.mdc-button--raised:not(:disabled),.mdc-button--unelevated:not(:disabled){color:#fff;color:var(--mdc-theme-on-primary, #fff)}.mdc-button--raised:disabled,.mdc-button--unelevated:disabled{background-color:rgba(0,0,0,.12)}.mdc-button--raised:disabled,.mdc-button--unelevated:disabled{color:rgba(0,0,0,.38)}.mdc-button--raised{box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-button--raised:hover,.mdc-button--raised:focus{box-shadow:0px 2px 4px -1px rgba(0, 0, 0, 0.2),0px 4px 5px 0px rgba(0, 0, 0, 0.14),0px 1px 10px 0px rgba(0,0,0,.12)}.mdc-button--raised:active{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12)}.mdc-button--raised:disabled{box-shadow:0px 0px 0px 0px rgba(0, 0, 0, 0.2),0px 0px 0px 0px rgba(0, 0, 0, 0.14),0px 0px 0px 0px rgba(0,0,0,.12)}.mdc-button--outlined{padding:0 15px 0 15px;border-width:1px;border-style:solid}.mdc-button--outlined .mdc-button__ripple{top:-1px;left:-1px;border:1px solid transparent}.mdc-button--outlined .mdc-button__touch{left:-1px;width:calc(100% + 2 * 1px)}.mdc-button--outlined:not(:disabled){border-color:rgba(0,0,0,.12)}.mdc-button--outlined:disabled{border-color:rgba(0,0,0,.12)}.mdc-button--touch{margin-top:6px;margin-bottom:6px}.trailing-icon ::slotted(*),.trailing-icon .mdc-button__icon,.leading-icon ::slotted(*),.leading-icon .mdc-button__icon{margin-left:0;margin-right:8px;display:inline-block;width:18px;height:18px;font-size:18px;vertical-align:top}[dir=rtl] .trailing-icon ::slotted(*),.trailing-icon ::slotted(*)[dir=rtl],[dir=rtl] .trailing-icon .mdc-button__icon,.trailing-icon .mdc-button__icon[dir=rtl],[dir=rtl] .leading-icon ::slotted(*),.leading-icon ::slotted(*)[dir=rtl],[dir=rtl] .leading-icon .mdc-button__icon,.leading-icon .mdc-button__icon[dir=rtl]{margin-left:8px;margin-right:0}.trailing-icon ::slotted(*),.trailing-icon .mdc-button__icon{margin-left:8px;margin-right:0}[dir=rtl] .trailing-icon ::slotted(*),.trailing-icon ::slotted(*)[dir=rtl],[dir=rtl] .trailing-icon .mdc-button__icon,.trailing-icon .mdc-button__icon[dir=rtl]{margin-left:0;margin-right:8px}.mdc-button--raised{box-shadow:var(--mdc-button-raised-box-shadow, 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12))}.mdc-button--raised:hover,.mdc-button--raised:focus{box-shadow:var(--mdc-button-raised-box-shadow-hover, 0px 2px 4px -1px rgba(0, 0, 0, 0.2), 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12))}.mdc-button--raised:active{box-shadow:var(--mdc-button-raised-box-shadow-active, 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12))}.mdc-button--raised:disabled{box-shadow:var(--mdc-button-raised-box-shadow-disabled, 0px 0px 0px 0px rgba(0, 0, 0, 0.2), 0px 0px 0px 0px rgba(0, 0, 0, 0.14), 0px 0px 0px 0px rgba(0, 0, 0, 0.12))}:host{display:inline-flex;outline:none;vertical-align:top}:host([disabled]){pointer-events:none}:host([fullwidth]){width:100%}.mdc-button{flex:auto;overflow:hidden;padding:0 var(--mdc-button-horizontal-padding, 8px) 0 var(--mdc-button-horizontal-padding, 8px)}.mdc-button.mdc-button--raised,.mdc-button.mdc-button--unelevated{padding:0 var(--mdc-button-horizontal-padding, 16px) 0 var(--mdc-button-horizontal-padding, 16px)}.mdc-button.mdc-button--raised mwc-ripple,.mdc-button.mdc-button--unelevated mwc-ripple{--mdc-ripple-color: var(--mdc-theme-on-primary, #fff);--mdc-ripple-hover-opacity: .08;--mdc-ripple-press-opacity: .24;--mdc-ripple-focus-opacity: .24}.mdc-button.mdc-button--outlined{padding:0 calc(var(--mdc-button-horizontal-padding, 16px) - var(--mdc-button-outline-width, 1px)) 0 calc(var(--mdc-button-horizontal-padding, 16px) - var(--mdc-button-outline-width, 1px));border-width:var(--mdc-button-outline-width, 1px);border-color:var(--mdc-button-outline-color, var(--mdc-theme-primary, #6200ee))}.mdc-button.mdc-button--outlined mwc-ripple{top:-1px;left:-1px;border:1px solid transparent}.mdc-button.mdc-button--dense{height:28px;margin-top:0;margin-bottom:0}.mdc-button.mdc-button--dense .mdc-button__touch{display:none}:host([disabled]) .mdc-button.mdc-button--raised,:host([disabled]) .mdc-button.mdc-button--unelevated{background-color:var(--mdc-button-disabled-fill-color, rgba(0, 0, 0, 0.12));color:var(--mdc-button-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-button:not(.mdc-button--raised):not(.mdc-button--unelevated){color:var(--mdc-button-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-button.mdc-button--outlined{border-color:var(--mdc-button-disabled-ink-color, rgba(0, 0, 0, 0.38));border-color:var(--mdc-button-disabled-outline-color, var(--mdc-button-disabled-ink-color, rgba(0, 0, 0, 0.38)))}`;

  /** @soyCompatible */

  let Button = class Button extends ButtonBase {};
  Button.styles = style$3;
  Button = __decorate([customElement('mwc-button')], Button);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$3(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /** @soyCompatible */

  class IconButtonBase extends LitElement {
    constructor() {
      super(...arguments);
      this.disabled = false;
      this.icon = '';
      this.label = '';
      this.shouldRenderRipple = false;
      this.rippleHandlers = new RippleHandlers(() => {
        this.shouldRenderRipple = true;
        return this.ripple;
      });
    }
    /** @soyCompatible */


    renderRipple() {
      return html`${this.shouldRenderRipple ? html`
            <mwc-ripple
                .disabled="${this.disabled}"
                unbounded>
            </mwc-ripple>` : ''}`;
    }

    focus() {
      const buttonElement = this.buttonElement;

      if (buttonElement) {
        this.rippleHandlers.startFocus();
        buttonElement.focus();
      }
    }

    blur() {
      const buttonElement = this.buttonElement;

      if (buttonElement) {
        this.rippleHandlers.endFocus();
        buttonElement.blur();
      }
    }
    /** @soyCompatible */


    render() {
      return html`<button
        class="mdc-icon-button"
        aria-label="${this.label || this.icon}"
        ?disabled="${this.disabled}"
        @focus="${this.handleRippleFocus}"
        @blur="${this.handleRippleBlur}"
        @mousedown="${this.handleRippleMouseDown}"
        @mouseenter="${this.handleRippleMouseEnter}"
        @mouseleave="${this.handleRippleMouseLeave}"
        @touchstart="${this.handleRippleTouchStart}"
        @touchend="${this.handleRippleDeactivate}"
        @touchcancel="${this.handleRippleDeactivate}">
      ${this.renderRipple()}
    <i class="material-icons">${this.icon}</i>
    <slot></slot>
  </button>`;
    }

    handleRippleMouseDown(event) {
      const onUp = () => {
        window.removeEventListener('mouseup', onUp);
        this.handleRippleDeactivate();
      };

      window.addEventListener('mouseup', onUp);
      this.rippleHandlers.startPress(event);
    }

    handleRippleTouchStart(event) {
      this.rippleHandlers.startPress(event);
    }

    handleRippleDeactivate() {
      this.rippleHandlers.endPress();
    }

    handleRippleMouseEnter() {
      this.rippleHandlers.startHover();
    }

    handleRippleMouseLeave() {
      this.rippleHandlers.endHover();
    }

    handleRippleFocus() {
      this.rippleHandlers.startFocus();
    }

    handleRippleBlur() {
      this.rippleHandlers.endFocus();
    }

  }

  __decorate$3([property({
    type: Boolean,
    reflect: true
  })], IconButtonBase.prototype, "disabled", void 0);

  __decorate$3([property({
    type: String
  })], IconButtonBase.prototype, "icon", void 0);

  __decorate$3([property({
    type: String
  })], IconButtonBase.prototype, "label", void 0);

  __decorate$3([query('button')], IconButtonBase.prototype, "buttonElement", void 0);

  __decorate$3([queryAsync('mwc-ripple')], IconButtonBase.prototype, "ripple", void 0);

  __decorate$3([internalProperty()], IconButtonBase.prototype, "shouldRenderRipple", void 0);

  __decorate$3([eventOptions({
    passive: true
  })], IconButtonBase.prototype, "handleRippleMouseDown", null);

  __decorate$3([eventOptions({
    passive: true
  })], IconButtonBase.prototype, "handleRippleTouchStart", null);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$4 = css`.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}.mdc-icon-button{display:inline-block;position:relative;box-sizing:border-box;border:none;outline:none;background-color:transparent;fill:currentColor;color:inherit;font-size:24px;text-decoration:none;cursor:pointer;user-select:none;width:48px;height:48px;padding:12px}.mdc-icon-button svg,.mdc-icon-button img{width:24px;height:24px}.mdc-icon-button:disabled{color:rgba(0,0,0,.38);color:var(--mdc-theme-text-disabled-on-light, rgba(0, 0, 0, 0.38))}.mdc-icon-button:disabled{cursor:default;pointer-events:none}.mdc-icon-button__icon{display:inline-block}.mdc-icon-button__icon.mdc-icon-button__icon--on{display:none}.mdc-icon-button--on .mdc-icon-button__icon{display:none}.mdc-icon-button--on .mdc-icon-button__icon.mdc-icon-button__icon--on{display:inline-block}:host{display:inline-block;outline:none;--mdc-ripple-color: currentcolor}:host([disabled]){pointer-events:none}:host,.mdc-icon-button{vertical-align:top}.mdc-icon-button{width:var(--mdc-icon-button-size, 48px);height:var(--mdc-icon-button-size, 48px);padding:calc((var(--mdc-icon-button-size, 48px) - var(--mdc-icon-size, 24px)) / 2)}.mdc-icon-button>i{position:absolute;top:0;padding-top:inherit}.mdc-icon-button i,.mdc-icon-button svg,.mdc-icon-button img,.mdc-icon-button ::slotted(*){display:block;width:var(--mdc-icon-size, 24px);height:var(--mdc-icon-size, 24px)}`;

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  /** @soyCompatible */

  let IconButton = class IconButton extends IconButtonBase {};
  IconButton.styles = style$4;
  IconButton = __decorate$3([customElement('mwc-icon-button')], IconButton);

  const computeDomain = entityId => {
    return entityId.substr(0, entityId.indexOf("."));
  };

  const createStore = state => {
    let listeners = [];

    function unsubscribe(listener) {
      let out = [];

      for (let i = 0; i < listeners.length; i++) {
        if (listeners[i] === listener) {
          listener = null;
        } else {
          out.push(listeners[i]);
        }
      }

      listeners = out;
    }

    function setState(update, overwrite) {
      state = overwrite ? update : Object.assign(Object.assign({}, state), update);
      let currentListeners = listeners;

      for (let i = 0; i < currentListeners.length; i++) {
        currentListeners[i](state);
      }
    }
    /**
     * An observable state container, returned from {@link createStore}
     * @name store
     */


    return {
      get state() {
        return state;
      },

      /**
       * Create a bound copy of the given action function.
       * The bound returned function invokes action() and persists the result back to the store.
       * If the return value of `action` is a Promise, the resolved value will be used as state.
       * @param {Function} action	An action of the form `action(state, ...args) -> stateUpdate`
       * @returns {Function} boundAction()
       */
      action(action) {
        function apply(result) {
          setState(result, false);
        } // Note: perf tests verifying this implementation: https://esbench.com/bench/5a295e6299634800a0349500


        return function () {
          let args = [state];

          for (let i = 0; i < arguments.length; i++) args.push(arguments[i]); // @ts-ignore


          let ret = action.apply(this, args);

          if (ret != null) {
            return ret instanceof Promise ? ret.then(apply) : apply(ret);
          }
        };
      },

      /**
       * Apply a partial state object to the current state, invoking registered listeners.
       * @param {Object} update				An object with properties to be merged into state
       * @param {Boolean} [overwrite=false]	If `true`, update will replace state instead of being merged into it
       */
      setState,

      /**
       * Register a listener function to be called whenever state is changed. Returns an `unsubscribe()` function.
       * @param {Function} listener	A function to call when state changes. Gets passed the new state.
       * @returns {Function} unsubscribe()
       */
      subscribe(listener) {
        listeners.push(listener);
        return () => {
          unsubscribe(listener);
        };
      } // /**
      //  * Remove a previously-registered listener function.
      //  * @param {Function} listener	The callback previously passed to `subscribe()` that should be removed.
      //  * @function
      //  */
      // unsubscribe,


    };
  };

  const getCollection = (conn, key, fetchCollection, subscribeUpdates) => {
    if (conn[key]) {
      return conn[key];
    }

    let active = 0;
    let unsubProm;
    let store = createStore();

    const refresh = () => fetchCollection(conn).then(state => store.setState(state, true));

    const refreshSwallow = () => refresh().catch(err => {
      // Swallow errors if socket is connecting, closing or closed.
      // We will automatically call refresh again when we re-establish the connection.
      if (conn.connected) {
        throw err;
      }
    });

    conn[key] = {
      get state() {
        return store.state;
      },

      refresh,

      subscribe(subscriber) {
        active++; // If this was the first subscriber, attach collection

        if (active === 1) {
          if (subscribeUpdates) {
            unsubProm = subscribeUpdates(conn, store);
          } // Fetch when connection re-established.


          conn.addEventListener("ready", refreshSwallow);
          refreshSwallow();
        }

        const unsub = store.subscribe(subscriber);

        if (store.state !== undefined) {
          // Don't call it right away so that caller has time
          // to initialize all the things.
          setTimeout(() => subscriber(store.state), 0);
        }

        return () => {
          unsub();
          active--;

          if (!active) {
            // Unsubscribe from changes
            if (unsubProm) unsubProm.then(unsub => {
              unsub();
            });
            conn.removeEventListener("ready", refresh);
          }
        };
      }

    };
    return conn[key];
  }; // Legacy name. It gets a collection and subscribes.

  const createCollection = (key, fetchCollection, subscribeUpdates, conn, onChange) => getCollection(conn, key, fetchCollection, subscribeUpdates).subscribe(onChange);

  const fetchNotifications = conn => conn.sendMessagePromise({
    type: "persistent_notification/get"
  });

  const subscribeUpdates = (conn, store) => conn.subscribeEvents(() => fetchNotifications(conn).then(ntf => store.setState(ntf, true)), "persistent_notifications_updated");

  const subscribeNotifications = (conn, onChange) => createCollection("_ntf", fetchNotifications, subscribeUpdates, conn, onChange);

  let HaSvgIcon = _decorate([customElement("ha-svg-icon")], function (_initialize, _LitElement) {
    class HaSvgIcon extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaSvgIcon,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "path",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "viewBox",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return svg`
    <svg 
      viewBox=${this.viewBox || "0 0 24 24"}
      preserveAspectRatio="xMidYMid meet"
      focusable="false">
      <g>
      ${this.path ? svg`<path d=${this.path}></path>` : ""}
      </g>
    </svg>`;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: var(--ha-icon-display, inline-flex);
        align-items: center;
        justify-content: center;
        position: relative;
        vertical-align: middle;
        fill: currentcolor;
        width: var(--mdc-icon-size, 24px);
        height: var(--mdc-icon-size, 24px);
      }
      svg {
        width: 100%;
        height: 100%;
        pointer-events: none;
        display: block;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HaMenuButton = _decorate([customElement("ha-menu-button")], function (_initialize, _LitElement) {
    class HaMenuButton extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaMenuButton,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "hassio",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_hasNotifications",

        value() {
          return false;
        }

      }, {
        kind: "field",
        key: "_alwaysVisible",

        value() {
          return false;
        }

      }, {
        kind: "field",
        key: "_attachNotifOnConnect",

        value() {
          return false;
        }

      }, {
        kind: "field",
        key: "_unsubNotifications",
        value: void 0
      }, {
        kind: "method",
        key: "connectedCallback",
        value: function connectedCallback() {
          _get(_getPrototypeOf(HaMenuButton.prototype), "connectedCallback", this).call(this);

          if (this._attachNotifOnConnect) {
            this._attachNotifOnConnect = false;

            this._subscribeNotifications();
          }
        }
      }, {
        kind: "method",
        key: "disconnectedCallback",
        value: function disconnectedCallback() {
          _get(_getPrototypeOf(HaMenuButton.prototype), "disconnectedCallback", this).call(this);

          if (this._unsubNotifications) {
            this._attachNotifOnConnect = true;

            this._unsubNotifications();

            this._unsubNotifications = undefined;
          }
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          const hasNotifications = (this.narrow || this.hass.dockedSidebar === "always_hidden") && (this._hasNotifications || Object.keys(this.hass.states).some(entityId => computeDomain(entityId) === "configurator"));
          return html`
      <mwc-icon-button
        aria-label=${this.hass.localize("ui.sidebar.sidebar_toggle")}
        @click=${this._toggleMenu}
      >
        <ha-svg-icon path=${mdiMenu}></ha-svg-icon>
      </mwc-icon-button>
      ${hasNotifications ? html` <div class="dot"></div> ` : ""}
    `;
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: function firstUpdated(changedProps) {
          _get(_getPrototypeOf(HaMenuButton.prototype), "firstUpdated", this).call(this, changedProps);

          if (!this.hassio) {
            return;
          } // This component is used on Hass.io too, but Hass.io might run the UI
          // on older frontends too, that don't have an always visible menu button
          // in the sidebar.


          this._alwaysVisible = (Number(window.parent.frontendVersion) || 0) < 20190710;
        }
      }, {
        kind: "method",
        key: "updated",
        value: function updated(changedProps) {
          _get(_getPrototypeOf(HaMenuButton.prototype), "updated", this).call(this, changedProps);

          if (!changedProps.has("narrow") && !changedProps.has("hass")) {
            return;
          }

          const oldHass = changedProps.get("hass");
          const oldNarrow = changedProps.get("narrow") || oldHass && oldHass.dockedSidebar === "always_hidden";
          const newNarrow = this.narrow || this.hass.dockedSidebar === "always_hidden";

          if (oldNarrow === newNarrow) {
            return;
          }

          this.style.visibility = newNarrow || this._alwaysVisible ? "initial" : "hidden";

          if (!newNarrow) {
            this._hasNotifications = false;

            if (this._unsubNotifications) {
              this._unsubNotifications();

              this._unsubNotifications = undefined;
            }

            return;
          }

          this._subscribeNotifications();
        }
      }, {
        kind: "method",
        key: "_subscribeNotifications",
        value: function _subscribeNotifications() {
          this._unsubNotifications = subscribeNotifications(this.hass.connection, notifications => {
            this._hasNotifications = notifications.length > 0;
          });
        }
      }, {
        kind: "method",
        key: "_toggleMenu",
        value: function _toggleMenu() {
          fireEvent(this, "hass-toggle-menu");
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        position: relative;
      }
      .dot {
        pointer-events: none;
        position: absolute;
        background-color: var(--accent-color);
        width: 12px;
        height: 12px;
        top: 9px;
        right: 7px;
        border-radius: 50%;
        border: 2px solid var(--app-header-background-color);
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HaIconButtonArrowPrev = _decorate([customElement("ha-icon-button-arrow-prev")], function (_initialize, _LitElement) {
    class HaIconButtonArrowPrev extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaIconButtonArrowPrev,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "disabled",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_icon",

        value() {
          return mdiArrowLeft;
        }

      }, {
        kind: "method",
        key: "connectedCallback",
        value: function connectedCallback() {
          _get(_getPrototypeOf(HaIconButtonArrowPrev.prototype), "connectedCallback", this).call(this); // wait to check for direction since otherwise direction is wrong even though top level is RTL


          setTimeout(() => {
            this._icon = window.getComputedStyle(this).direction === "ltr" ? mdiArrowLeft : mdiArrowRight;
          }, 100);
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`<mwc-icon-button .disabled=${this.disabled}>
      <ha-svg-icon .path=${this._icon}></ha-svg-icon>
    </mwc-icon-button> `;
        }
      }]
    };
  }, LitElement);

  const restoreScroll = selector => {
    return element => ({
      kind: "method",
      placement: "prototype",
      key: element.key,
      descriptor: {
        set(value) {
          this[`__${String(element.key)}`] = value;
        },

        get() {
          return this[`__${String(element.key)}`];
        },

        enumerable: true,
        configurable: true
      },

      finisher(cls) {
        const connectedCallback = cls.prototype.connectedCallback;

        cls.prototype.connectedCallback = function () {
          connectedCallback.call(this);

          if (this[element.key]) {
            const target = this.renderRoot.querySelector(selector);

            if (!target) {
              return;
            }

            target.scrollTop = this[element.key];
          }
        };
      }

    });
  };

  let HassSubpage = _decorate([customElement("hass-subpage")], function (_initialize, _LitElement) {
    class HassSubpage extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HassSubpage,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "header",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "showBackButton",

        value() {
          return true;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "hassio",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [restoreScroll(".content")],
        key: "_savedScrollPos",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: // @ts-ignore
        function render() {
          return html`
      <div class="toolbar">
        <ha-icon-button-arrow-prev
          aria-label="Back"
          @click=${this._backTapped}
          class=${classMap({
          hidden: !this.showBackButton
        })}
        ></ha-icon-button-arrow-prev>

        <div class="main-title">${this.header}</div>
        <slot name="toolbar-icon"></slot>
      </div>
      <div class="content" @scroll=${this._saveScrollPos}><slot></slot></div>
    `;
        }
      }, {
        kind: "method",
        decorators: [eventOptions({
          passive: true
        })],
        key: "_saveScrollPos",
        value: function _saveScrollPos(e) {
          this._savedScrollPos = e.target.scrollTop;
        }
      }, {
        kind: "method",
        key: "_backTapped",
        value: function _backTapped() {
          history.back();
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: block;
        height: 100%;
        background-color: var(--primary-background-color);
      }

      :host([narrow]) {
        width: 100%;
        position: fixed;
      }

      .toolbar {
        display: flex;
        align-items: center;
        font-size: 20px;
        height: 65px;
        padding: 0 16px;
        pointer-events: none;
        background-color: var(--app-header-background-color);
        font-weight: 400;
        color: var(--app-header-text-color, white);
        border-bottom: var(--app-header-border-bottom, none);
        box-sizing: border-box;
      }

      ha-icon-button-arrow-prev,
      ::slotted([slot="toolbar-icon"]) {
        pointer-events: auto;
      }

      ha-icon-button-arrow-prev.hidden {
        visibility: hidden;
      }

      .main-title {
        margin: 0 0 0 24px;
        line-height: 20px;
        flex-grow: 1;
      }

      .content {
        position: relative;
        width: 100%;
        height: calc(100% - 65px);
        overflow-y: auto;
        overflow: auto;
        -webkit-overflow-scrolling: touch;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HassErrorScreen = _decorate([customElement("hass-error-screen")], function (_initialize, _LitElement) {
    class HassErrorScreen extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HassErrorScreen,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "toolbar",

        value() {
          return true;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "error",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      ${this.toolbar ? html`<div class="toolbar">
            <ha-icon-button-arrow-prev
              @click=${this._handleBack}
            ></ha-icon-button-arrow-prev>
          </div>` : ""}
      <div class="content">
        <h3>${this.error}</h3>
        <slot>
          <mwc-button @click=${this._handleBack}>go back</mwc-button>
        </slot>
      </div>
    `;
        }
      }, {
        kind: "method",
        key: "_handleBack",
        value: function _handleBack() {
          history.back();
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [css`
        :host {
          display: block;
          height: 100%;
          background-color: var(--primary-background-color);
        }
        .toolbar {
          display: flex;
          align-items: center;
          font-size: 20px;
          height: 65px;
          padding: 0 16px;
          pointer-events: none;
          background-color: var(--app-header-background-color);
          font-weight: 400;
          color: var(--app-header-text-color, white);
          border-bottom: var(--app-header-border-bottom, none);
          box-sizing: border-box;
        }
        ha-icon-button-arrow-prev {
          pointer-events: auto;
        }
        .content {
          color: var(--primary-text-color);
          height: calc(100% - 64px);
          display: flex;
          align-items: center;
          justify-content: center;
          flex-direction: column;
        }
      `];
        }
      }]
    };
  }, LitElement);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  app-toolbar is a horizontal toolbar containing items that can be used for
  label, navigation, search and actions.

  ### Example

  Add a title to the toolbar.

  ```html
  <app-toolbar>
    <div main-title>App name</div>
  </app-toolbar>
  ```

  Add a button to the left and right side of the toolbar.

  ```html
  <app-toolbar>
    <paper-icon-button icon="menu"></paper-icon-button>
    <div main-title>App name</div>
    <paper-icon-button icon="search"></paper-icon-button>
  </app-toolbar>
  ```

  You can use the attributes `top-item` or `bottom-item` to completely fit an
  element to the top or bottom of the toolbar respectively.

  ### Content attributes

  Attribute            | Description
  ---------------------|---------------------------------------------------------
  `main-title`         | The main title element.
  `condensed-title`    | The title element if used inside a condensed app-header.
  `spacer`             | Adds a left margin of `64px`.
  `bottom-item`        | Sticks the element to the bottom of the toolbar.
  `top-item`           | Sticks the element to the top of the toolbar.

  ### Styling

  Custom property              | Description                  | Default
  -----------------------------|------------------------------|-----------------------
  `--app-toolbar-font-size`    | Toolbar font size            | 20px

  @element app-toolbar
  @demo app-toolbar/demo/index.html
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>

      :host {
        @apply --layout-horizontal;
        @apply --layout-center;
        position: relative;
        height: 64px;
        padding: 0 16px;
        pointer-events: none;
        font-size: var(--app-toolbar-font-size, 20px);
      }

      :host ::slotted(*) {
        pointer-events: auto;
      }

      :host ::slotted(paper-icon-button) {
        /* paper-icon-button/issues/33 */
        font-size: 0;
      }

      :host ::slotted([main-title]),
      :host ::slotted([condensed-title]) {
        pointer-events: none;
        @apply --layout-flex;
      }

      :host ::slotted([bottom-item]) {
        position: absolute;
        right: 0;
        bottom: 0;
        left: 0;
      }

      :host ::slotted([top-item]) {
        position: absolute;
        top: 0;
        right: 0;
        left: 0;
      }

      :host ::slotted([spacer]) {
        margin-left: 64px;
      }
    </style>

    <slot></slot>
`,
    is: 'app-toolbar'
  });

  var progressStyles = "/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/material-components/material-components-web/blob/master/LICENSE\n */\n.mdc-circular-progress__determinate-circle,.mdc-circular-progress__indeterminate-circle-graphic{stroke:#6200ee;stroke:var(--mdc-theme-primary, #6200ee)}.mdc-circular-progress__determinate-track{stroke:transparent}@-webkit-keyframes mdc-circular-progress-container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes mdc-circular-progress-container-rotate{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes mdc-circular-progress-spinner-layer-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes mdc-circular-progress-spinner-layer-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes mdc-circular-progress-color-1-fade-in-out{from{opacity:.99}25%{opacity:.99}26%{opacity:0}89%{opacity:0}90%{opacity:.99}to{opacity:.99}}@keyframes mdc-circular-progress-color-1-fade-in-out{from{opacity:.99}25%{opacity:.99}26%{opacity:0}89%{opacity:0}90%{opacity:.99}to{opacity:.99}}@-webkit-keyframes mdc-circular-progress-color-2-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:.99}50%{opacity:.99}51%{opacity:0}to{opacity:0}}@keyframes mdc-circular-progress-color-2-fade-in-out{from{opacity:0}15%{opacity:0}25%{opacity:.99}50%{opacity:.99}51%{opacity:0}to{opacity:0}}@-webkit-keyframes mdc-circular-progress-color-3-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:.99}75%{opacity:.99}76%{opacity:0}to{opacity:0}}@keyframes mdc-circular-progress-color-3-fade-in-out{from{opacity:0}40%{opacity:0}50%{opacity:.99}75%{opacity:.99}76%{opacity:0}to{opacity:0}}@-webkit-keyframes mdc-circular-progress-color-4-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:.99}90%{opacity:.99}to{opacity:0}}@keyframes mdc-circular-progress-color-4-fade-in-out{from{opacity:0}65%{opacity:0}75%{opacity:.99}90%{opacity:.99}to{opacity:0}}@-webkit-keyframes mdc-circular-progress-left-spin{from{-webkit-transform:rotate(265deg);transform:rotate(265deg)}50%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}to{-webkit-transform:rotate(265deg);transform:rotate(265deg)}}@keyframes mdc-circular-progress-left-spin{from{-webkit-transform:rotate(265deg);transform:rotate(265deg)}50%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}to{-webkit-transform:rotate(265deg);transform:rotate(265deg)}}@-webkit-keyframes mdc-circular-progress-right-spin{from{-webkit-transform:rotate(-265deg);transform:rotate(-265deg)}50%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}to{-webkit-transform:rotate(-265deg);transform:rotate(-265deg)}}@keyframes mdc-circular-progress-right-spin{from{-webkit-transform:rotate(-265deg);transform:rotate(-265deg)}50%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}to{-webkit-transform:rotate(-265deg);transform:rotate(-265deg)}}.mdc-circular-progress{display:inline-flex;position:relative;direction:ltr;transition:opacity 250ms 0ms cubic-bezier(0.4, 0, 0.6, 1)}.mdc-circular-progress__determinate-container,.mdc-circular-progress__indeterminate-circle-graphic,.mdc-circular-progress__indeterminate-container,.mdc-circular-progress__spinner-layer{position:absolute;width:100%;height:100%}.mdc-circular-progress__determinate-container{-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.mdc-circular-progress__indeterminate-container{opacity:0}.mdc-circular-progress__determinate-circle-graphic,.mdc-circular-progress__indeterminate-circle-graphic{fill:transparent}.mdc-circular-progress__determinate-circle{transition:stroke-dashoffset 500ms 0ms cubic-bezier(0, 0, 0.2, 1)}.mdc-circular-progress__gap-patch{position:absolute;top:0;left:47.5%;box-sizing:border-box;width:5%;height:100%;overflow:hidden}.mdc-circular-progress__gap-patch .mdc-circular-progress__indeterminate-circle-graphic{left:-900%;width:2000%;-webkit-transform:rotate(180deg);transform:rotate(180deg)}.mdc-circular-progress__circle-clipper{display:inline-flex;position:relative;width:50%;height:100%;overflow:hidden}.mdc-circular-progress__circle-clipper .mdc-circular-progress__indeterminate-circle-graphic{width:200%}.mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic{left:-100%}.mdc-circular-progress--indeterminate .mdc-circular-progress__determinate-container{opacity:0}.mdc-circular-progress--indeterminate .mdc-circular-progress__indeterminate-container{opacity:1}.mdc-circular-progress--indeterminate .mdc-circular-progress__indeterminate-container{-webkit-animation:mdc-circular-progress-container-rotate 1568.2352941176ms linear infinite;animation:mdc-circular-progress-container-rotate 1568.2352941176ms linear infinite}.mdc-circular-progress--indeterminate .mdc-circular-progress__spinner-layer{-webkit-animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__color-1{-webkit-animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-1-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__color-2{-webkit-animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-2-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__color-3{-webkit-animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-3-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__color-4{-webkit-animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-spinner-layer-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both,mdc-circular-progress-color-4-fade-in-out 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-left .mdc-circular-progress__indeterminate-circle-graphic{-webkit-animation:mdc-circular-progress-left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--indeterminate .mdc-circular-progress__circle-right .mdc-circular-progress__indeterminate-circle-graphic{-webkit-animation:mdc-circular-progress-right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both;animation:mdc-circular-progress-right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both}.mdc-circular-progress--closed{opacity:0}\n\n/*# sourceMappingURL=mdc.circular-progress.min.css.map*/";

  let HaCircularProgress = _decorate([customElement("ha-circular-progress")], function (_initialize, _LitElement) {
    class HaCircularProgress extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaCircularProgress,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "active",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "alt",

        value() {
          return "Loading";
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "size",

        value() {
          return "medium";
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          let indeterminatePart;

          if (this.size === "small") {
            indeterminatePart = svg`
        <svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
          <circle cx="12" cy="12" r="8.75" stroke-dasharray="54.978" stroke-dashoffset="27.489"/>
        </svg>`;
          } else if (this.size === "large") {
            indeterminatePart = svg`
        <svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
          <circle cx="24" cy="24" r="18" stroke-dasharray="113.097" stroke-dashoffset="56.549"/>
        </svg>`;
          } else {
            // medium
            indeterminatePart = svg`
        <svg class="mdc-circular-progress__indeterminate-circle-graphic" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
          <circle cx="16" cy="16" r="12.5" stroke-dasharray="78.54" stroke-dashoffset="39.27"/>
        </svg>`;
          } // ignoring prettier as it will introduce unwanted whitespace
          // We have not implemented the determinate support of mdc circular progress.
          // prettier-ignore


          return html`
      <div
        class="mdc-circular-progress ${classMap({
          "mdc-circular-progress--indeterminate": this.active,
          [`mdc-circular-progress--${this.size}`]: true
        })}"
        role="progressbar"
        aria-label=${this.alt}
        aria-valuemin="0"
        aria-valuemax="1"
      >
        <div class="mdc-circular-progress__indeterminate-container">
          <div class="mdc-circular-progress__spinner-layer">
            <div class="mdc-circular-progress__circle-clipper mdc-circular-progress__circle-left">
              ${indeterminatePart}
            </div><div class="mdc-circular-progress__gap-patch">
              ${indeterminatePart}
            </div><div class="mdc-circular-progress__circle-clipper mdc-circular-progress__circle-right">
              ${indeterminatePart}
            </div>
          </div>
        </div>
      </div>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [unsafeCSS(progressStyles), css`
        :host {
          text-align: initial;
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HassLoadingScreen = _decorate([customElement("hass-loading-screen")], function (_initialize, _LitElement) {
    class HassLoadingScreen extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HassLoadingScreen,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean,
          attribute: "no-toolbar"
        })],
        key: "noToolbar",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "rootnav",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "narrow",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      ${this.noToolbar ? "" : html`<div class="toolbar">
            ${this.rootnav ? html`
                  <ha-menu-button
                    .hass=${this.hass}
                    .narrow=${this.narrow}
                  ></ha-menu-button>
                ` : html`
                  <ha-icon-button-arrow-prev
                    @click=${this._handleBack}
                  ></ha-icon-button-arrow-prev>
                `}
          </div>`}
      <div class="content">
        <ha-circular-progress active></ha-circular-progress>
      </div>
    `;
        }
      }, {
        kind: "method",
        key: "_handleBack",
        value: function _handleBack() {
          history.back();
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [haStyle, css`
        :host {
          display: block;
          height: 100%;
          background-color: var(--primary-background-color);
        }
        .toolbar {
          display: flex;
          align-items: center;
          font-size: 20px;
          height: 65px;
          padding: 0 16px;
          pointer-events: none;
          background-color: var(--app-header-background-color);
          font-weight: 400;
          color: var(--app-header-text-color, white);
          border-bottom: var(--app-header-border-bottom, none);
          box-sizing: border-box;
        }
        ha-menu-button,
        ha-icon-button-arrow-prev {
          pointer-events: auto;
        }
        .content {
          height: calc(100% - 64px);
          display: flex;
          align-items: center;
          justify-content: center;
        }
      `];
        }
      }]
    };
  }, LitElement);

  const extractPage = (path, defaultPage) => {
    if (path === "") {
      return defaultPage;
    }

    const subpathStart = path.indexOf("/", 1);
    return subpathStart === -1 ? path.substr(1) : path.substr(1, subpathStart - 1);
  };

  // Time to wait for code to load before we show loading screen.
  const LOADING_SCREEN_THRESHOLD = 400; // ms

  let HassRouterPage = _decorate(null, function (_initialize, _UpdatingElement) {
    class HassRouterPage extends _UpdatingElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HassRouterPage,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        key: "routerOptions",
        value: void 0
      }, {
        kind: "field",
        key: "_currentPage",

        value() {
          return "";
        }

      }, {
        kind: "field",
        key: "_currentLoadProm",
        value: void 0
      }, {
        kind: "field",
        key: "_cache",

        value() {
          return {};
        }

      }, {
        kind: "field",
        key: "_initialLoadDone",

        value() {
          return false;
        }

      }, {
        kind: "field",
        key: "_computeTail",

        value() {
          return memoizeOne(route => {
            const dividerPos = route.path.indexOf("/", 1);
            return dividerPos === -1 ? {
              prefix: route.prefix + route.path,
              path: ""
            } : {
              prefix: route.prefix + route.path.substr(0, dividerPos),
              path: route.path.substr(dividerPos)
            };
          });
        }

      }, {
        kind: "method",
        key: "update",
        value: function update(changedProps) {
          _get(_getPrototypeOf(HassRouterPage.prototype), "update", this).call(this, changedProps);

          const routerOptions = this.routerOptions || {
            routes: {}
          };

          if (routerOptions && routerOptions.initialLoad && !this._initialLoadDone) {
            return;
          }

          if (!changedProps.has("route")) {
            // Do not update if we have a currentLoadProm, because that means
            // that there is still an old panel shown and we're moving to a new one.
            if (this.lastChild && !this._currentLoadProm) {
              this.updatePageEl(this.lastChild, changedProps);
            }

            return;
          }

          const route = this.route;
          const defaultPage = routerOptions.defaultPage;

          if (route && route.path === "" && defaultPage !== undefined) {
            navigate(this, `${route.prefix}/${defaultPage}`, true);
          }

          let newPage = route ? extractPage(route.path, defaultPage || "") : "not_found";
          let routeOptions = routerOptions.routes[newPage]; // Handle redirects

          while (typeof routeOptions === "string") {
            newPage = routeOptions;
            routeOptions = routerOptions.routes[newPage];
          }

          if (routerOptions.beforeRender) {
            const result = routerOptions.beforeRender(newPage);

            if (result !== undefined) {
              newPage = result;
              routeOptions = routerOptions.routes[newPage]; // Handle redirects

              while (typeof routeOptions === "string") {
                newPage = routeOptions;
                routeOptions = routerOptions.routes[newPage];
              } // Update the url if we know where we're mounted.


              if (route) {
                navigate(this, `${route.prefix}/${result}`, true);
              }
            }
          }

          if (this._currentPage === newPage) {
            if (this.lastChild) {
              this.updatePageEl(this.lastChild, changedProps);
            }

            return;
          }

          if (!routeOptions) {
            this._currentPage = "";

            if (this.lastChild) {
              this.removeChild(this.lastChild);
            }

            return;
          }

          this._currentPage = newPage;
          const loadProm = routeOptions.load ? routeOptions.load() : Promise.resolve();
          let showLoadingScreenTimeout; // Check when loading the page source failed.

          loadProm.catch(err => {
            // eslint-disable-next-line
            console.error("Error loading page", newPage, err); // Verify that we're still trying to show the same page.

            if (this._currentPage !== newPage) {
              return;
            } // Removes either loading screen or the panel


            if (this.lastChild) {
              this.removeChild(this.lastChild);
            }

            if (showLoadingScreenTimeout) {
              clearTimeout(showLoadingScreenTimeout);
            } // Show error screen


            this.appendChild(this.createErrorScreen(`Error while loading page ${newPage}.`));
          }); // If we don't show loading screen, just show the panel.
          // It will be automatically upgraded when loading done.

          if (!routerOptions.showLoading) {
            this._createPanel(routerOptions, newPage, routeOptions);

            return;
          } // We are only going to show the loading screen after some time.
          // That way we won't have a double fast flash on fast connections.


          let created = false;
          showLoadingScreenTimeout = window.setTimeout(() => {
            if (created || this._currentPage !== newPage) {
              return;
            } // Show a loading screen.


            if (this.lastChild) {
              this.removeChild(this.lastChild);
            }

            this.appendChild(this.createLoadingScreen());
          }, LOADING_SCREEN_THRESHOLD);
          this._currentLoadProm = loadProm.then(() => {
            this._currentLoadProm = undefined; // Check if we're still trying to show the same page.

            if (this._currentPage !== newPage) {
              return;
            }

            created = true;

            this._createPanel(routerOptions, newPage, // @ts-ignore TS forgot this is not a string.
            routeOptions);
          }, () => {
            this._currentLoadProm = undefined;
          });
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: function firstUpdated(changedProps) {
          _get(_getPrototypeOf(HassRouterPage.prototype), "firstUpdated", this).call(this, changedProps);

          const options = this.routerOptions;

          if (!options) {
            return;
          }

          if (options.preloadAll) {
            Object.values(options.routes).forEach(route => typeof route === "object" && route.load && route.load());
          }

          if (options.initialLoad) {
            setTimeout(() => {
              if (!this._initialLoadDone) {
                this.appendChild(this.createLoadingScreen());
              }
            }, LOADING_SCREEN_THRESHOLD);
            options.initialLoad().then(() => {
              this._initialLoadDone = true;
              this.requestUpdate("route");
            });
          }
        }
      }, {
        kind: "method",
        key: "createLoadingScreen",
        value: function createLoadingScreen() {
          return document.createElement("hass-loading-screen");
        }
      }, {
        kind: "method",
        key: "createErrorScreen",
        value: function createErrorScreen(error) {
          const errorEl = document.createElement("hass-error-screen");
          errorEl.error = error;
          return errorEl;
        }
        /**
         * Rebuild the current panel.
         *
         * Promise will resolve when rebuilding is done and DOM updated.
         */

      }, {
        kind: "method",
        key: "rebuild",
        value: async function rebuild() {
          const oldRoute = this.route;

          if (oldRoute === undefined) {
            return;
          }

          this.route = undefined;
          await this.updateComplete; // Make sure that the parent didn't override this in the meanwhile.

          if (this.route === undefined) {
            this.route = oldRoute;
          }
        }
        /**
         * Promise that resolves when the page has rendered.
         */

      }, {
        kind: "get",
        key: "pageRendered",
        value: function pageRendered() {
          return this.updateComplete.then(() => this._currentLoadProm);
        }
      }, {
        kind: "method",
        key: "createElement",
        value: function createElement(tag) {
          return document.createElement(tag);
        }
      }, {
        kind: "method",
        key: "updatePageEl",
        value: function updatePageEl(_pageEl, _changedProps) {// default we do nothing
        }
      }, {
        kind: "get",
        key: "routeTail",
        value: function routeTail() {
          return this._computeTail(this.route);
        }
      }, {
        kind: "method",
        key: "_createPanel",
        value: function _createPanel(routerOptions, page, routeOptions) {
          if (this.lastChild) {
            this.removeChild(this.lastChild);
          }

          const panelEl = this._cache[page] || this.createElement(routeOptions.tag);
          this.updatePageEl(panelEl);
          this.appendChild(panelEl);

          if (routerOptions.cacheAll || routeOptions.cache) {
            this._cache[page] = panelEl;
          }
        }
      }]
    };
  }, UpdatingElement);

  /**
   * Attach a media query. Listener is called right away and when it matches.
   * @param mediaQuery media query to match.
   * @param listener listener to call when media query changes between match/unmatch
   * @returns function to remove the listener.
   */
  const listenMediaQuery = (mediaQuery, matchesChanged) => {
    const mql = matchMedia(mediaQuery);

    const listener = e => matchesChanged(e.matches);

    mql.addListener(listener);
    matchesChanged(mql.matches);
    return () => mql.removeListener(listener);
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */

  var ORPHANS = new Set();
  /**
   * `IronResizableBehavior` is a behavior that can be used in Polymer elements to
   * coordinate the flow of resize events between "resizers" (elements that
   *control the size or hidden state of their children) and "resizables" (elements
   *that need to be notified when they are resized or un-hidden by their parents
   *in order to take action on their new measurements).
   *
   * Elements that perform measurement should add the `IronResizableBehavior`
   *behavior to their element definition and listen for the `iron-resize` event on
   *themselves. This event will be fired when they become showing after having
   *been hidden, when they are resized explicitly by another resizable, or when
   *the window has been resized.
   *
   * Note, the `iron-resize` event is non-bubbling.
   *
   * @polymerBehavior
   * @demo demo/index.html
   **/

  const IronResizableBehavior = {
    properties: {
      /**
       * The closest ancestor element that implements `IronResizableBehavior`.
       */
      _parentResizable: {
        type: Object,
        observer: '_parentResizableChanged'
      },

      /**
       * True if this element is currently notifying its descendant elements of
       * resize.
       */
      _notifyingDescendant: {
        type: Boolean,
        value: false
      }
    },
    listeners: {
      'iron-request-resize-notifications': '_onIronRequestResizeNotifications'
    },
    created: function () {
      // We don't really need property effects on these, and also we want them
      // to be created before the `_parentResizable` observer fires:
      this._interestedResizables = [];
      this._boundNotifyResize = this.notifyResize.bind(this);
      this._boundOnDescendantIronResize = this._onDescendantIronResize.bind(this);
    },
    attached: function () {
      this._requestResizeNotifications();
    },
    detached: function () {
      if (this._parentResizable) {
        this._parentResizable.stopResizeNotificationsFor(this);
      } else {
        ORPHANS.delete(this);
        window.removeEventListener('resize', this._boundNotifyResize);
      }

      this._parentResizable = null;
    },

    /**
     * Can be called to manually notify a resizable and its descendant
     * resizables of a resize change.
     */
    notifyResize: function () {
      if (!this.isAttached) {
        return;
      }

      this._interestedResizables.forEach(function (resizable) {
        if (this.resizerShouldNotify(resizable)) {
          this._notifyDescendant(resizable);
        }
      }, this);

      this._fireResize();
    },

    /**
     * Used to assign the closest resizable ancestor to this resizable
     * if the ancestor detects a request for notifications.
     */
    assignParentResizable: function (parentResizable) {
      if (this._parentResizable) {
        this._parentResizable.stopResizeNotificationsFor(this);
      }

      this._parentResizable = parentResizable;

      if (parentResizable && parentResizable._interestedResizables.indexOf(this) === -1) {
        parentResizable._interestedResizables.push(this);

        parentResizable._subscribeIronResize(this);
      }
    },

    /**
     * Used to remove a resizable descendant from the list of descendants
     * that should be notified of a resize change.
     */
    stopResizeNotificationsFor: function (target) {
      var index = this._interestedResizables.indexOf(target);

      if (index > -1) {
        this._interestedResizables.splice(index, 1);

        this._unsubscribeIronResize(target);
      }
    },

    /**
     * Subscribe this element to listen to iron-resize events on the given target.
     *
     * Preferred over target.listen because the property renamer does not
     * understand to rename when the target is not specifically "this"
     *
     * @param {!HTMLElement} target Element to listen to for iron-resize events.
     */
    _subscribeIronResize: function (target) {
      target.addEventListener('iron-resize', this._boundOnDescendantIronResize);
    },

    /**
     * Unsubscribe this element from listening to to iron-resize events on the
     * given target.
     *
     * Preferred over target.unlisten because the property renamer does not
     * understand to rename when the target is not specifically "this"
     *
     * @param {!HTMLElement} target Element to listen to for iron-resize events.
     */
    _unsubscribeIronResize: function (target) {
      target.removeEventListener('iron-resize', this._boundOnDescendantIronResize);
    },

    /**
     * This method can be overridden to filter nested elements that should or
     * should not be notified by the current element. Return true if an element
     * should be notified, or false if it should not be notified.
     *
     * @param {HTMLElement} element A candidate descendant element that
     * implements `IronResizableBehavior`.
     * @return {boolean} True if the `element` should be notified of resize.
     */
    resizerShouldNotify: function (element) {
      return true;
    },
    _onDescendantIronResize: function (event) {
      if (this._notifyingDescendant) {
        event.stopPropagation();
        return;
      } // no need to use this during shadow dom because of event retargeting


      if (!useShadow) {
        this._fireResize();
      }
    },
    _fireResize: function () {
      this.fire('iron-resize', null, {
        node: this,
        bubbles: false
      });
    },
    _onIronRequestResizeNotifications: function (event) {
      var target =
      /** @type {!EventTarget} */
      dom(event).rootTarget;

      if (target === this) {
        return;
      }

      target.assignParentResizable(this);

      this._notifyDescendant(target);

      event.stopPropagation();
    },
    _parentResizableChanged: function (parentResizable) {
      if (parentResizable) {
        window.removeEventListener('resize', this._boundNotifyResize);
      }
    },
    _notifyDescendant: function (descendant) {
      // NOTE(cdata): In IE10, attached is fired on children first, so it's
      // important not to notify them if the parent is not attached yet (or
      // else they will get redundantly notified when the parent attaches).
      if (!this.isAttached) {
        return;
      }

      this._notifyingDescendant = true;
      descendant.notifyResize();
      this._notifyingDescendant = false;
    },
    _requestResizeNotifications: function () {
      if (!this.isAttached) {
        return;
      }

      if (document.readyState === 'loading') {
        var _requestResizeNotifications = this._requestResizeNotifications.bind(this);

        document.addEventListener('readystatechange', function readystatechanged() {
          document.removeEventListener('readystatechange', readystatechanged);

          _requestResizeNotifications();
        });
      } else {
        this._findParent();

        if (!this._parentResizable) {
          // If this resizable is an orphan, tell other orphans to try to find
          // their parent again, in case it's this resizable.
          ORPHANS.forEach(function (orphan) {
            if (orphan !== this) {
              orphan._findParent();
            }
          }, this);
          window.addEventListener('resize', this._boundNotifyResize);
          this.notifyResize();
        } else {
          // If this resizable has a parent, tell other child resizables of
          // that parent to try finding their parent again, in case it's this
          // resizable.
          this._parentResizable._interestedResizables.forEach(function (resizable) {
            if (resizable !== this) {
              resizable._findParent();
            }
          }, this);
        }
      }
    },
    _findParent: function () {
      this.assignParentResizable(null);
      this.fire('iron-request-resize-notifications', null, {
        node: this,
        bubbles: true,
        cancelable: true
      });

      if (!this._parentResizable) {
        ORPHANS.add(this);
      } else {
        ORPHANS.delete(this);
      }
    }
  };

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @polymerBehavior
   */

  const AppLayoutBehavior = [IronResizableBehavior, {
    listeners: {
      'app-reset-layout': '_appResetLayoutHandler',
      'iron-resize': 'resetLayout'
    },
    attached: function () {
      this.fire('app-reset-layout');
    },
    _appResetLayoutHandler: function (e) {
      if (dom(e).path[0] === this) {
        return;
      }

      this.resetLayout();
      e.stopPropagation();
    },
    _updateLayoutStates: function () {
      console.error('unimplemented');
    },

    /**
     * Resets the layout. If you changed the size of this element via CSS
     * you can notify the changes by either firing the `iron-resize` event
     * or calling `resetLayout` directly.
     *
     * @method resetLayout
     */
    resetLayout: function () {

      var cb = this._updateLayoutStates.bind(this);

      this._layoutDebouncer = Debouncer.debounce(this._layoutDebouncer, animationFrame, cb);
      enqueueDebouncer(this._layoutDebouncer);

      this._notifyDescendantResize();
    },
    _notifyLayoutChanged: function () {
      var self = this; // TODO: the event `app-reset-layout` can be fired synchronously
      // as long as `_updateLayoutStates` waits for all the microtasks after
      // rAF. E.g. requestAnimationFrame(setTimeOut())

      requestAnimationFrame(function () {
        self.fire('app-reset-layout');
      });
    },
    _notifyDescendantResize: function () {
      if (!this.isAttached) {
        return;
      }

      this._interestedResizables.forEach(function (resizable) {
        if (this.resizerShouldNotify(resizable)) {
          this._notifyDescendant(resizable);
        }
      }, this);
    }
  }];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  app-header-layout is a wrapper element that positions an app-header and other
  content. This element uses the document scroll by default, but it can also
  define its own scrolling region.

  Using the document scroll:

  ```html
  <app-header-layout>
    <app-header slot="header" fixed condenses effects="waterfall">
      <app-toolbar>
        <div main-title>App name</div>
      </app-toolbar>
    </app-header>
    <div>
      main content
    </div>
  </app-header-layout>
  ```

  Using an own scrolling region:

  ```html
  <app-header-layout has-scrolling-region style="width: 300px; height: 400px;">
    <app-header slot="header" fixed condenses effects="waterfall">
      <app-toolbar>
        <div main-title>App name</div>
      </app-toolbar>
    </app-header>
    <div>
      main content
    </div>
  </app-header-layout>
  ```

  Add the `fullbleed` attribute to app-header-layout to make it fit the size of
  its container:

  ```html
  <app-header-layout fullbleed>
   ...
  </app-header-layout>
  ```

  @element app-header-layout
  @demo app-header-layout/demo/simple.html Simple Demo
  @demo app-header-layout/demo/scrolling-region.html Scrolling Region
  @demo app-header-layout/demo/music.html Music Demo
  @demo app-header-layout/demo/footer.html Footer Demo
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: block;
        /**
         * Force app-header-layout to have its own stacking context so that its parent can
         * control the stacking of it relative to other elements (e.g. app-drawer-layout).
         * This could be done using \`isolation: isolate\`, but that's not well supported
         * across browsers.
         */
        position: relative;
        z-index: 0;
      }

      #wrapper ::slotted([slot=header]) {
        @apply --layout-fixed-top;
        z-index: 1;
      }

      #wrapper.initializing ::slotted([slot=header]) {
        position: relative;
      }

      :host([has-scrolling-region]) {
        height: 100%;
      }

      :host([has-scrolling-region]) #wrapper ::slotted([slot=header]) {
        position: absolute;
      }

      :host([has-scrolling-region]) #wrapper.initializing ::slotted([slot=header]) {
        position: relative;
      }

      :host([has-scrolling-region]) #wrapper #contentContainer {
        @apply --layout-fit;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
      }

      :host([has-scrolling-region]) #wrapper.initializing #contentContainer {
        position: relative;
      }

      :host([fullbleed]) {
        @apply --layout-vertical;
        @apply --layout-fit;
      }

      :host([fullbleed]) #wrapper,
      :host([fullbleed]) #wrapper #contentContainer {
        @apply --layout-vertical;
        @apply --layout-flex;
      }

      #contentContainer {
        /* Create a stacking context here so that all children appear below the header. */
        position: relative;
        z-index: 0;
      }

      @media print {
        :host([has-scrolling-region]) #wrapper #contentContainer {
          overflow-y: visible;
        }
      }

    </style>

    <div id="wrapper" class="initializing">
      <slot id="headerSlot" name="header"></slot>

      <div id="contentContainer">
        <slot></slot>
      </div>
    </div>
`,
    is: 'app-header-layout',
    behaviors: [AppLayoutBehavior],
    properties: {
      /**
       * If true, the current element will have its own scrolling region.
       * Otherwise, it will use the document scroll to control the header.
       */
      hasScrollingRegion: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      }
    },
    observers: ['resetLayout(isAttached, hasScrollingRegion)'],

    /**
     * A reference to the app-header element.
     *
     * @property header
     */
    get header() {
      return dom(this.$.headerSlot).getDistributedNodes()[0];
    },

    _updateLayoutStates: function () {
      var header = this.header;

      if (!this.isAttached || !header) {
        return;
      } // Remove the initializing class, which staticly positions the header and
      // the content until the height of the header can be read.


      this.$.wrapper.classList.remove('initializing'); // Update scroll target.

      header.scrollTarget = this.hasScrollingRegion ? this.$.contentContainer : this.ownerDocument.documentElement; // Get header height here so that style reads are batched together before
      // style writes (i.e. getBoundingClientRect() below).

      var headerHeight = header.offsetHeight; // Update the header position.

      if (!this.hasScrollingRegion) {
        requestAnimationFrame(function () {
          var rect = this.getBoundingClientRect();
          var rightOffset = document.documentElement.clientWidth - rect.right;
          header.style.left = rect.left + 'px';
          header.style.right = rightOffset + 'px';
        }.bind(this));
      } else {
        header.style.left = '';
        header.style.right = '';
      } // Update the content container position.


      var containerStyle = this.$.contentContainer.style;

      if (header.fixed && !header.condenses && this.hasScrollingRegion) {
        // If the header size does not change and we're using a scrolling region,
        // exclude the header area from the scrolling region so that the header
        // doesn't overlap the scrollbar.
        containerStyle.marginTop = headerHeight + 'px';
        containerStyle.paddingTop = '';
      } else {
        containerStyle.paddingTop = headerHeight + 'px';
        containerStyle.marginTop = '';
      }
    }
  });

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * `Polymer.IronScrollTargetBehavior` allows an element to respond to scroll
   * events from a designated scroll target.
   *
   * Elements that consume this behavior can override the `_scrollHandler`
   * method to add logic on the scroll event.
   *
   * @demo demo/scrolling-region.html Scrolling Region
   * @demo demo/document.html Document Element
   * @polymerBehavior
   */

  const IronScrollTargetBehavior = {
    properties: {
      /**
       * Specifies the element that will handle the scroll event
       * on the behalf of the current element. This is typically a reference to an
       *element, but there are a few more posibilities:
       *
       * ### Elements id
       *
       *```html
       * <div id="scrollable-element" style="overflow: auto;">
       *  <x-element scroll-target="scrollable-element">
       *    <!-- Content-->
       *  </x-element>
       * </div>
       *```
       * In this case, the `scrollTarget` will point to the outer div element.
       *
       * ### Document scrolling
       *
       * For document scrolling, you can use the reserved word `document`:
       *
       *```html
       * <x-element scroll-target="document">
       *   <!-- Content -->
       * </x-element>
       *```
       *
       * ### Elements reference
       *
       *```js
       * appHeader.scrollTarget = document.querySelector('#scrollable-element');
       *```
       *
       * @type {HTMLElement}
       * @default document
       */
      scrollTarget: {
        type: HTMLElement,
        value: function () {
          return this._defaultScrollTarget;
        }
      }
    },
    observers: ['_scrollTargetChanged(scrollTarget, isAttached)'],

    /**
     * True if the event listener should be installed.
     */
    _shouldHaveListener: true,
    _scrollTargetChanged: function (scrollTarget, isAttached) {

      if (this._oldScrollTarget) {
        this._toggleScrollListener(false, this._oldScrollTarget);

        this._oldScrollTarget = null;
      }

      if (!isAttached) {
        return;
      } // Support element id references


      if (scrollTarget === 'document') {
        this.scrollTarget = this._doc;
      } else if (typeof scrollTarget === 'string') {
        var domHost = this.domHost;
        this.scrollTarget = domHost && domHost.$ ? domHost.$[scrollTarget] : dom(this.ownerDocument).querySelector('#' + scrollTarget);
      } else if (this._isValidScrollTarget()) {
        this._oldScrollTarget = scrollTarget;

        this._toggleScrollListener(this._shouldHaveListener, scrollTarget);
      }
    },

    /**
     * Runs on every scroll event. Consumer of this behavior may override this
     * method.
     *
     * @protected
     */
    _scrollHandler: function scrollHandler() {},

    /**
     * The default scroll target. Consumers of this behavior may want to customize
     * the default scroll target.
     *
     * @type {Element}
     */
    get _defaultScrollTarget() {
      return this._doc;
    },

    /**
     * Shortcut for the document element
     *
     * @type {Element}
     */
    get _doc() {
      return this.ownerDocument.documentElement;
    },

    /**
     * Gets the number of pixels that the content of an element is scrolled
     * upward.
     *
     * @type {number}
     */
    get _scrollTop() {
      if (this._isValidScrollTarget()) {
        return this.scrollTarget === this._doc ? window.pageYOffset : this.scrollTarget.scrollTop;
      }

      return 0;
    },

    /**
     * Gets the number of pixels that the content of an element is scrolled to the
     * left.
     *
     * @type {number}
     */
    get _scrollLeft() {
      if (this._isValidScrollTarget()) {
        return this.scrollTarget === this._doc ? window.pageXOffset : this.scrollTarget.scrollLeft;
      }

      return 0;
    },

    /**
     * Sets the number of pixels that the content of an element is scrolled
     * upward.
     *
     * @type {number}
     */
    set _scrollTop(top) {
      if (this.scrollTarget === this._doc) {
        window.scrollTo(window.pageXOffset, top);
      } else if (this._isValidScrollTarget()) {
        this.scrollTarget.scrollTop = top;
      }
    },

    /**
     * Sets the number of pixels that the content of an element is scrolled to the
     * left.
     *
     * @type {number}
     */
    set _scrollLeft(left) {
      if (this.scrollTarget === this._doc) {
        window.scrollTo(left, window.pageYOffset);
      } else if (this._isValidScrollTarget()) {
        this.scrollTarget.scrollLeft = left;
      }
    },

    /**
     * Scrolls the content to a particular place.
     *
     * @method scroll
     * @param {number|!{left: number, top: number}} leftOrOptions The left position or scroll options
     * @param {number=} top The top position
     * @return {void}
     */
    scroll: function (leftOrOptions, top) {
      var left;

      if (typeof leftOrOptions === 'object') {
        left = leftOrOptions.left;
        top = leftOrOptions.top;
      } else {
        left = leftOrOptions;
      }

      left = left || 0;
      top = top || 0;

      if (this.scrollTarget === this._doc) {
        window.scrollTo(left, top);
      } else if (this._isValidScrollTarget()) {
        this.scrollTarget.scrollLeft = left;
        this.scrollTarget.scrollTop = top;
      }
    },

    /**
     * Gets the width of the scroll target.
     *
     * @type {number}
     */
    get _scrollTargetWidth() {
      if (this._isValidScrollTarget()) {
        return this.scrollTarget === this._doc ? window.innerWidth : this.scrollTarget.offsetWidth;
      }

      return 0;
    },

    /**
     * Gets the height of the scroll target.
     *
     * @type {number}
     */
    get _scrollTargetHeight() {
      if (this._isValidScrollTarget()) {
        return this.scrollTarget === this._doc ? window.innerHeight : this.scrollTarget.offsetHeight;
      }

      return 0;
    },

    /**
     * Returns true if the scroll target is a valid HTMLElement.
     *
     * @return {boolean}
     */
    _isValidScrollTarget: function () {
      return this.scrollTarget instanceof HTMLElement;
    },
    _toggleScrollListener: function (yes, scrollTarget) {
      var eventTarget = scrollTarget === this._doc ? window : scrollTarget;

      if (yes) {
        if (!this._boundScrollHandler) {
          this._boundScrollHandler = this._scrollHandler.bind(this);
          eventTarget.addEventListener('scroll', this._boundScrollHandler);
        }
      } else {
        if (this._boundScrollHandler) {
          eventTarget.removeEventListener('scroll', this._boundScrollHandler);
          this._boundScrollHandler = null;
        }
      }
    },

    /**
     * Enables or disables the scroll event listener.
     *
     * @param {boolean} yes True to add the event, False to remove it.
     */
    toggleScrollListener: function (yes) {
      this._shouldHaveListener = yes;

      this._toggleScrollListener(yes, this.scrollTarget);
    }
  };

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const _scrollEffects = {};

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * `Polymer.AppScrollEffectsBehavior` provides an interface that allows an
   * element to use scrolls effects.
   *
   * ### Importing the app-layout effects
   *
   * app-layout provides a set of scroll effects that can be used by explicitly
   * importing `app-scroll-effects.js`:
   *
   * ```js
   * import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
   * ```
   *
   * The scroll effects can also be used by individually importing
   * `@polymer/app-layout/app-scroll-effects/effects/[effectName].js`. For
   * example:
   *
   * ```js
   * import '@polymer/app-layout/app-scroll-effects/effects/waterfall.js';
   * ```
   *
   * ### Consuming effects
   *
   * Effects can be consumed via the `effects` property. For example:
   *
   * ```html
   * <app-header effects="waterfall"></app-header>
   * ```
   *
   * ### Creating scroll effects
   *
   * You may want to create a custom scroll effect if you need to modify the CSS
   * of an element based on the scroll position.
   *
   * A scroll effect definition is an object with `setUp()`, `tearDown()` and
   * `run()` functions.
   *
   * To register the effect, you can use
   * `Polymer.AppLayout.registerEffect(effectName, effectDef)` For example, let's
   * define an effect that resizes the header's logo:
   *
   * ```js
   * Polymer.AppLayout.registerEffect('resizable-logo', {
   *   setUp: function(config) {
   *     // the effect's config is passed to the setUp.
   *     this._fxResizeLogo = { logo: Polymer.dom(this).querySelector('[logo]') };
   *   },
   *
   *   run: function(progress) {
   *      // the progress of the effect
   *      this.transform('scale3d(' + progress + ', '+ progress +', 1)',
   * this._fxResizeLogo.logo);
   *   },
   *
   *   tearDown: function() {
   *      // clean up and reset of states
   *      delete this._fxResizeLogo;
   *   }
   * });
   * ```
   * Now, you can consume the effect:
   *
   * ```html
   * <app-header id="appHeader" effects="resizable-logo">
   *   <img logo src="logo.svg">
   * </app-header>
   * ```
   *
   * ### Imperative API
   *
   * ```js
   * var logoEffect = appHeader.createEffect('resizable-logo', effectConfig);
   * // run the effect: logoEffect.run(progress);
   * // tear down the effect: logoEffect.tearDown();
   * ```
   *
   * ### Configuring effects
   *
   * For effects installed via the `effects` property, their configuration can be
   * set via the `effectsConfig` property. For example:
   *
   * ```html
   * <app-header effects="waterfall"
   *   effects-config='{"waterfall": {"startsAt": 0, "endsAt": 0.5}}'>
   * </app-header>
   * ```
   *
   * All effects have a `startsAt` and `endsAt` config property. They specify at
   * what point the effect should start and end. This value goes from 0 to 1
   * inclusive.
   *
   * @polymerBehavior
   */

  const AppScrollEffectsBehavior = [IronScrollTargetBehavior, {
    properties: {
      /**
       * A space-separated list of the effects names that will be triggered when
       * the user scrolls. e.g. `waterfall parallax-background` installs the
       * `waterfall` and `parallax-background`.
       */
      effects: {
        type: String
      },

      /**
       * An object that configurates the effects installed via the `effects`
       * property. e.g.
       * ```js
       *  element.effectsConfig = {
       *   "blend-background": {
       *     "startsAt": 0.5
       *   }
       * };
       * ```
       * Every effect has at least two config properties: `startsAt` and
       * `endsAt`. These properties indicate when the event should start and end
       * respectively and relative to the overall element progress. So for
       * example, if `blend-background` starts at `0.5`, the effect will only
       * start once the current element reaches 0.5 of its progress. In this
       * context, the progress is a value in the range of `[0, 1]` that
       * indicates where this element is on the screen relative to the viewport.
       */
      effectsConfig: {
        type: Object,
        value: function () {
          return {};
        }
      },

      /**
       * Disables CSS transitions and scroll effects on the element.
       */
      disabled: {
        type: Boolean,
        reflectToAttribute: true,
        value: false
      },

      /**
       * Allows to set a `scrollTop` threshold. When greater than 0,
       * `thresholdTriggered` is true only when the scroll target's `scrollTop`
       * has reached this value.
       *
       * For example, if `threshold = 100`, `thresholdTriggered` is true when
       * the `scrollTop` is at least `100`.
       */
      threshold: {
        type: Number,
        value: 0
      },

      /**
       * True if the `scrollTop` threshold (set in `scrollTopThreshold`) has
       * been reached.
       */
      thresholdTriggered: {
        type: Boolean,
        notify: true,
        readOnly: true,
        reflectToAttribute: true
      }
    },
    observers: ['_effectsChanged(effects, effectsConfig, isAttached)'],

    /**
     * Updates the scroll state. This method should be overridden
     * by the consumer of this behavior.
     *
     * @method _updateScrollState
     * @param {number} scrollTop
     */
    _updateScrollState: function (scrollTop) {},

    /**
     * Returns true if the current element is on the screen.
     * That is, visible in the current viewport. This method should be
     * overridden by the consumer of this behavior.
     *
     * @method isOnScreen
     * @return {boolean}
     */
    isOnScreen: function () {
      return false;
    },

    /**
     * Returns true if there's content below the current element. This method
     * should be overridden by the consumer of this behavior.
     *
     * @method isContentBelow
     * @return {boolean}
     */
    isContentBelow: function () {
      return false;
    },

    /**
     * List of effects handlers that will take place during scroll.
     *
     * @type {Array<Function>}
     */
    _effectsRunFn: null,

    /**
     * List of the effects definitions installed via the `effects` property.
     *
     * @type {Array<Object>}
     */
    _effects: null,

    /**
     * The clamped value of `_scrollTop`.
     * @type number
     */
    get _clampedScrollTop() {
      return Math.max(0, this._scrollTop);
    },

    attached: function () {
      this._scrollStateChanged();
    },
    detached: function () {
      this._tearDownEffects();
    },

    /**
     * Creates an effect object from an effect's name that can be used to run
     * effects programmatically.
     *
     * @method createEffect
     * @param {string} effectName The effect's name registered via `Polymer.AppLayout.registerEffect`.
     * @param {Object=} effectConfig The effect config object. (Optional)
     * @return {Object} An effect object with the following functions:
     *
     *  * `effect.setUp()`, Sets up the requirements for the effect.
     *       This function is called automatically before the `effect` function
     * returns.
     *  * `effect.run(progress, y)`, Runs the effect given a `progress`.
     *  * `effect.tearDown()`, Cleans up any DOM nodes or element references
     * used by the effect.
     *
     * Example:
     * ```js
     * var parallax = element.createEffect('parallax-background');
     * // runs the effect
     * parallax.run(0.5, 0);
     * ```
     */
    createEffect: function (effectName, effectConfig) {
      var effectDef = _scrollEffects[effectName];

      if (!effectDef) {
        throw new ReferenceError(this._getUndefinedMsg(effectName));
      }

      var prop = this._boundEffect(effectDef, effectConfig || {});

      prop.setUp();
      return prop;
    },

    /**
     * Called when `effects` or `effectsConfig` changes.
     */
    _effectsChanged: function (effects, effectsConfig, isAttached) {
      this._tearDownEffects();

      if (!effects || !isAttached) {
        return;
      }

      effects.split(' ').forEach(function (effectName) {
        var effectDef;

        if (effectName !== '') {
          if (effectDef = _scrollEffects[effectName]) {
            this._effects.push(this._boundEffect(effectDef, effectsConfig[effectName]));
          } else {
            console.warn(this._getUndefinedMsg(effectName));
          }
        }
      }, this);

      this._setUpEffect();
    },

    /**
     * Forces layout
     */
    _layoutIfDirty: function () {
      return this.offsetWidth;
    },

    /**
     * Returns an effect object bound to the current context.
     *
     * @param {Object} effectDef
     * @param {Object=} effectsConfig The effect config object if the effect accepts config values. (Optional)
     */
    _boundEffect: function (effectDef, effectsConfig) {
      effectsConfig = effectsConfig || {};
      var startsAt = parseFloat(effectsConfig.startsAt || 0);
      var endsAt = parseFloat(effectsConfig.endsAt || 1);
      var deltaS = endsAt - startsAt;

      var noop = function () {}; // fast path if possible


      var runFn = startsAt === 0 && endsAt === 1 ? effectDef.run : function (progress, y) {
        effectDef.run.call(this, Math.max(0, (progress - startsAt) / deltaS), y);
      };
      return {
        setUp: effectDef.setUp ? effectDef.setUp.bind(this, effectsConfig) : noop,
        run: effectDef.run ? runFn.bind(this) : noop,
        tearDown: effectDef.tearDown ? effectDef.tearDown.bind(this) : noop
      };
    },

    /**
     * Sets up the effects.
     */
    _setUpEffect: function () {
      if (this.isAttached && this._effects) {
        this._effectsRunFn = [];

        this._effects.forEach(function (effectDef) {
          // install the effect only if no error was reported
          if (effectDef.setUp() !== false) {
            this._effectsRunFn.push(effectDef.run);
          }
        }, this);
      }
    },

    /**
     * Tears down the effects.
     */
    _tearDownEffects: function () {
      if (this._effects) {
        this._effects.forEach(function (effectDef) {
          effectDef.tearDown();
        });
      }

      this._effectsRunFn = [];
      this._effects = [];
    },

    /**
     * Runs the effects.
     *
     * @param {number} p The progress
     * @param {number} y The top position of the current element relative to the viewport.
     */
    _runEffects: function (p, y) {
      if (this._effectsRunFn) {
        this._effectsRunFn.forEach(function (run) {
          run(p, y);
        });
      }
    },

    /**
     * Overrides the `_scrollHandler`.
     */
    _scrollHandler: function () {
      this._scrollStateChanged();
    },
    _scrollStateChanged: function () {
      if (!this.disabled) {
        var scrollTop = this._clampedScrollTop;

        this._updateScrollState(scrollTop);

        if (this.threshold > 0) {
          this._setThresholdTriggered(scrollTop >= this.threshold);
        }
      }
    },

    /**
     * Override this method to return a reference to a node in the local DOM.
     * The node is consumed by a scroll effect.
     *
     * @param {string} id The id for the node.
     */
    _getDOMRef: function (id) {
      console.warn('_getDOMRef', '`' + id + '` is undefined');
    },
    _getUndefinedMsg: function (effectName) {
      return 'Scroll effect `' + effectName + '` is undefined. ' + 'Did you forget to import app-layout/app-scroll-effects/effects/' + effectName + '.html ?';
    }
  }];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  app-header is container element for app-toolbars at the top of the screen that
  can have scroll effects. By default, an app-header moves away from the viewport
  when scrolling down and if using `reveals`, the header slides back when
  scrolling back up. For example:

  ```html
  <app-header reveals>
    <app-toolbar>
      <div main-title>App name</div>
    </app-toolbar>
  </app-header>
  ```

  app-header can also condense when scrolling down. To achieve this behavior, the
  header must have a larger height than the `sticky` element in the light DOM. For
  example:

  ```html
  <app-header style="height: 96px;" condenses fixed>
    <app-toolbar style="height: 64px;">
      <div main-title>App name</div>
    </app-toolbar>
  </app-header>
  ```

  In this case the header is initially `96px` tall, and it shrinks to `64px` when
  scrolling down. That is what is meant by "condensing".

  ### Sticky element

  The element that is positioned fixed to top of the header's `scrollTarget` when
  a threshold is reached, similar to `position: sticky` in CSS. This element
  **must** be an immediate child of app-header. By default, the `sticky` element
  is the first `app-toolbar that is an immediate child of app-header.

  ```html
  <app-header condenses>
    <app-toolbar> Sticky element </app-toolbar>
    <app-toolbar></app-toolbar>
  </app-header>
  ```

  #### Customizing the sticky element

  ```html
  <app-header condenses>
    <app-toolbar></app-toolbar>
    <app-toolbar sticky> Sticky element </app-toolbar>
  </app-header>
  ```

  ### Scroll target

  The app-header's `scrollTarget` property allows to customize the scrollable
  element to which the header responds when the user scrolls. By default,
  app-header uses the document as the scroll target, but you can customize this
  property by setting the id of the element, e.g.

  ```html
  <div id="scrollingRegion" style="overflow-y: auto;">
    <app-header scroll-target="scrollingRegion">
    </app-header>
  </div>
  ```

  In this case, the `scrollTarget` property points to the outer div element.
  Alternatively, you can set this property programmatically:

  ```js
  appHeader.scrollTarget = document.querySelector("#scrollingRegion");
  ```

  ## Backgrounds
  app-header has two background layers that can be used for styling when the
  header is condensed or when the scrollable element is scrolled to the top.

  ## Scroll effects

  Scroll effects are _optional_ visual effects applied in app-header based on
  scroll position. For example, The [Material Design scrolling
  techniques](https://www.google.com/design/spec/patterns/scrolling-techniques.html)
  recommends effects that can be installed via the `effects` property. e.g.

  ```html
  <app-header effects="waterfall">
    <app-toolbar>App name</app-toolbar>
  </app-header>
  ```

  #### Importing the effects

  To use the scroll effects, you must explicitly import them in addition to
  `app-header`:

  ```js
  import '@polymer/app-layout/app-scroll-effects/app-scroll-effects.js';
  ```

  #### List of effects

  * **blend-background**
  Fades in/out two background elements by applying CSS opacity based on scroll
  position. You can use this effect to smoothly change the background color or
  image of the header. For example, using the mixin
  `--app-header-background-rear-layer` lets you assign a different background when
  the header is condensed:

  ```css
  app-header {
    background-color: red;
    --app-header-background-rear-layer: {
      /* The header is blue when condensed *\/
      background-color: blue;
    };
  }
  ```

  * **fade-background**
  Upon scrolling past a threshold, this effect will trigger an opacity transition
  to fade in/out the backgrounds. Compared to the `blend-background` effect, this
  effect doesn't interpolate the opacity based on scroll position.


  * **parallax-background**
  A simple parallax effect that vertically translates the backgrounds based on a
  fraction of the scroll position. For example:

  ```css
  app-header {
    --app-header-background-front-layer: {
      background-image: url(...);
    };
  }
  ```
  ```html
  <app-header style="height: 300px;" effects="parallax-background">
    <app-toolbar>App name</app-toolbar>
  </app-header>
  ```

  The fraction determines how far the background moves relative to the scroll
  position. This value can be assigned via the `scalar` config value and it is
  typically a value between 0 and 1 inclusive. If `scalar=0`, the background
  doesn't move away from the header.

  * **resize-title**
  Progressively interpolates the size of the title from the element with the
  `main-title` attribute to the element with the `condensed-title` attribute as
  the header condenses. For example:

  ```html
  <app-header condenses reveals effects="resize-title">
    <app-toolbar>
        <h4 condensed-title>App name</h4>
    </app-toolbar>
    <app-toolbar>
        <h1 main-title>App name</h1>
    </app-toolbar>
  </app-header>
  ```

  * **resize-snapped-title**
  Upon scrolling past a threshold, this effect fades in/out the titles using
  opacity transitions. Similarly to `resize-title`, the `main-title` and
  `condensed-title` elements must be placed in the light DOM.

  * **waterfall**
  Toggles the shadow property in app-header to create a sense of depth (as
  recommended in the MD spec) between the header and the underneath content. You
  can change the shadow by customizing the `--app-header-shadow` mixin. For
  example:

  ```css
  app-header {
    --app-header-shadow: {
      box-shadow: inset 0px 5px 2px -3px rgba(0, 0, 0, 0.2);
    };
  }
  ```

  ```html
  <app-header condenses reveals effects="waterfall">
    <app-toolbar>
        <h1 main-title>App name</h1>
    </app-toolbar>
  </app-header>
  ```

  * **material**
  Installs the waterfall, resize-title, blend-background and parallax-background
  effects.

  ### Content attributes

  Attribute | Description         | Default
  ----------|---------------------|----------------------------------------
  `sticky` | Element that remains at the top when the header condenses. | The first app-toolbar in the light DOM.


  ## Styling

  Mixin | Description | Default
  ------|-------------|----------
  `--app-header-background-front-layer` | Applies to the front layer of the background. | {}
  `--app-header-background-rear-layer` | Applies to the rear layer of the background. | {}
  `--app-header-shadow` | Applies to the shadow. | {}

  @element app-header
  @demo app-header/demo/blend-background-1.html Blend Background Image
  @demo app-header/demo/blend-background-2.html Blend 2 Background Images
  @demo app-header/demo/blend-background-3.html Blend Background Colors
  @demo app-header/demo/contacts.html Contacts Demo
  @demo app-header/demo/give.html Resize Snapped Title Demo
  @demo app-header/demo/music.html Reveals Demo
  @demo app-header/demo/no-effects.html Condenses and Reveals Demo
  @demo app-header/demo/notes.html Fixed with Dynamic Shadow Demo
  @demo app-header/demo/custom-sticky-element-1.html Custom Sticky Element Demo 1
  @demo app-header/demo/custom-sticky-element-2.html Custom Sticky Element Demo 2

  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        position: relative;
        display: block;
        transition-timing-function: linear;
        transition-property: -webkit-transform;
        transition-property: transform;
      }

      :host::before {
        position: absolute;
        right: 0px;
        bottom: -5px;
        left: 0px;
        width: 100%;
        height: 5px;
        content: "";
        transition: opacity 0.4s;
        pointer-events: none;
        opacity: 0;
        box-shadow: inset 0px 5px 6px -3px rgba(0, 0, 0, 0.4);
        will-change: opacity;
        @apply --app-header-shadow;
      }

      :host([shadow])::before {
        opacity: 1;
      }

      #background {
        @apply --layout-fit;
        overflow: hidden;
      }

      #backgroundFrontLayer,
      #backgroundRearLayer {
        @apply --layout-fit;
        height: 100%;
        pointer-events: none;
        background-size: cover;
      }

      #backgroundFrontLayer {
        @apply --app-header-background-front-layer;
      }

      #backgroundRearLayer {
        opacity: 0;
        @apply --app-header-background-rear-layer;
      }

      #contentContainer {
        position: relative;
        width: 100%;
        height: 100%;
      }

      :host([disabled]),
      :host([disabled])::after,
      :host([disabled]) #backgroundFrontLayer,
      :host([disabled]) #backgroundRearLayer,
      /* Silent scrolling should not run CSS transitions */
      :host([silent-scroll]),
      :host([silent-scroll])::after,
      :host([silent-scroll]) #backgroundFrontLayer,
      :host([silent-scroll]) #backgroundRearLayer {
        transition: none !important;
      }

      :host([disabled]) ::slotted(app-toolbar:first-of-type),
      :host([disabled]) ::slotted([sticky]),
      /* Silent scrolling should not run CSS transitions */
      :host([silent-scroll]) ::slotted(app-toolbar:first-of-type),
      :host([silent-scroll]) ::slotted([sticky]) {
        transition: none !important;
      }

    </style>
    <div id="contentContainer">
      <slot id="slot"></slot>
    </div>
`,
    is: 'app-header',
    behaviors: [AppScrollEffectsBehavior, AppLayoutBehavior],
    properties: {
      /**
       * If true, the header will automatically collapse when scrolling down.
       * That is, the `sticky` element remains visible when the header is fully
       *condensed whereas the rest of the elements will collapse below `sticky`
       *element.
       *
       * By default, the `sticky` element is the first toolbar in the light DOM:
       *
       *```html
       * <app-header condenses>
       *   <app-toolbar>This toolbar remains on top</app-toolbar>
       *   <app-toolbar></app-toolbar>
       *   <app-toolbar></app-toolbar>
       * </app-header>
       * ```
       *
       * Additionally, you can specify which toolbar or element remains visible in
       *condensed mode by adding the `sticky` attribute to that element. For
       *example: if we want the last toolbar to remain visible, we can add the
       *`sticky` attribute to it.
       *
       *```html
       * <app-header condenses>
       *   <app-toolbar></app-toolbar>
       *   <app-toolbar></app-toolbar>
       *   <app-toolbar sticky>This toolbar remains on top</app-toolbar>
       * </app-header>
       * ```
       *
       * Note the `sticky` element must be a direct child of `app-header`.
       */
      condenses: {
        type: Boolean,
        value: false
      },

      /**
       * Mantains the header fixed at the top so it never moves away.
       */
      fixed: {
        type: Boolean,
        value: false
      },

      /**
       * Slides back the header when scrolling back up.
       */
      reveals: {
        type: Boolean,
        value: false
      },

      /**
       * Displays a shadow below the header.
       */
      shadow: {
        type: Boolean,
        reflectToAttribute: true,
        value: false
      }
    },
    observers: ['_configChanged(isAttached, condenses, fixed)'],

    /**
     * A cached offsetHeight of the current element.
     *
     * @type {number}
     */
    _height: 0,

    /**
     * The distance in pixels the header will be translated to when scrolling.
     *
     * @type {number}
     */
    _dHeight: 0,

    /**
     * The offsetTop of `_stickyEl`
     *
     * @type {number}
     */
    _stickyElTop: 0,

    /**
     * A reference to the element that remains visible when the header condenses.
     *
     * @type {HTMLElement}
     */
    _stickyElRef: null,

    /**
     * The header's top value used for the `transformY`
     *
     * @type {number}
     */
    _top: 0,

    /**
     * The current scroll progress.
     *
     * @type {number}
     */
    _progress: 0,
    _wasScrollingDown: false,
    _initScrollTop: 0,
    _initTimestamp: 0,
    _lastTimestamp: 0,
    _lastScrollTop: 0,

    /**
     * The distance the header is allowed to move away.
     *
     * @type {number}
     */
    get _maxHeaderTop() {
      return this.fixed ? this._dHeight : this._height + 5;
    },

    /**
     * Returns a reference to the sticky element.
     *
     * @return {HTMLElement}?
     */
    get _stickyEl() {
      if (this._stickyElRef) {
        return this._stickyElRef;
      }

      var nodes = dom(this.$.slot).getDistributedNodes(); // Get the element with the sticky attribute on it or the first element in
      // the light DOM.

      for (var i = 0, node; node =
      /** @type {!HTMLElement} */
      nodes[i]; i++) {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.hasAttribute('sticky')) {
            this._stickyElRef = node;
            break;
          } else if (!this._stickyElRef) {
            this._stickyElRef = node;
          }
        }
      }

      return this._stickyElRef;
    },

    _configChanged: function () {
      this.resetLayout();

      this._notifyLayoutChanged();
    },
    _updateLayoutStates: function () {
      if (this.offsetWidth === 0 && this.offsetHeight === 0) {
        return;
      }

      var scrollTop = this._clampedScrollTop;
      var firstSetup = this._height === 0 || scrollTop === 0;
      var currentDisabled = this.disabled;
      this._height = this.offsetHeight;
      this._stickyElRef = null;
      this.disabled = true; // prepare for measurement

      if (!firstSetup) {
        this._updateScrollState(0, true);
      }

      if (this._mayMove()) {
        this._dHeight = this._stickyEl ? this._height - this._stickyEl.offsetHeight : 0;
      } else {
        this._dHeight = 0;
      }

      this._stickyElTop = this._stickyEl ? this._stickyEl.offsetTop : 0;

      this._setUpEffect();

      if (firstSetup) {
        this._updateScrollState(scrollTop, true);
      } else {
        this._updateScrollState(this._lastScrollTop, true);

        this._layoutIfDirty();
      } // restore no transition


      this.disabled = currentDisabled;
    },

    /**
     * Updates the scroll state.
     *
     * @param {number} scrollTop
     * @param {boolean=} forceUpdate (default: false)
     */
    _updateScrollState: function (scrollTop, forceUpdate) {
      if (this._height === 0) {
        return;
      }

      var progress = 0;
      var top = 0;
      var lastTop = this._top;
      var lastScrollTop = this._lastScrollTop;
      var maxHeaderTop = this._maxHeaderTop;
      var dScrollTop = scrollTop - this._lastScrollTop;
      var absDScrollTop = Math.abs(dScrollTop);
      var isScrollingDown = scrollTop > this._lastScrollTop;
      var now = performance.now();

      if (this._mayMove()) {
        top = this._clamp(this.reveals ? lastTop + dScrollTop : scrollTop, 0, maxHeaderTop);
      }

      if (scrollTop >= this._dHeight) {
        top = this.condenses && !this.fixed ? Math.max(this._dHeight, top) : top;
        this.style.transitionDuration = '0ms';
      }

      if (this.reveals && !this.disabled && absDScrollTop < 100) {
        // set the initial scroll position
        if (now - this._initTimestamp > 300 || this._wasScrollingDown !== isScrollingDown) {
          this._initScrollTop = scrollTop;
          this._initTimestamp = now;
        }

        if (scrollTop >= maxHeaderTop) {
          // check if the header is allowed to snap
          if (Math.abs(this._initScrollTop - scrollTop) > 30 || absDScrollTop > 10) {
            if (isScrollingDown && scrollTop >= maxHeaderTop) {
              top = maxHeaderTop;
            } else if (!isScrollingDown && scrollTop >= this._dHeight) {
              top = this.condenses && !this.fixed ? this._dHeight : 0;
            }

            var scrollVelocity = dScrollTop / (now - this._lastTimestamp);
            this.style.transitionDuration = this._clamp((top - lastTop) / scrollVelocity, 0, 300) + 'ms';
          } else {
            top = this._top;
          }
        }
      }

      if (this._dHeight === 0) {
        progress = scrollTop > 0 ? 1 : 0;
      } else {
        progress = top / this._dHeight;
      }

      if (!forceUpdate) {
        this._lastScrollTop = scrollTop;
        this._top = top;
        this._wasScrollingDown = isScrollingDown;
        this._lastTimestamp = now;
      }

      if (forceUpdate || progress !== this._progress || lastTop !== top || scrollTop === 0) {
        this._progress = progress;

        this._runEffects(progress, top);

        this._transformHeader(top);
      }
    },

    /**
     * Returns true if the current header is allowed to move as the user scrolls.
     *
     * @return {boolean}
     */
    _mayMove: function () {
      return this.condenses || !this.fixed;
    },

    /**
     * Returns true if the current header will condense based on the size of the
     * header and the `consenses` property.
     *
     * @return {boolean}
     */
    willCondense: function () {
      return this._dHeight > 0 && this.condenses;
    },

    /**
     * Returns true if the current element is on the screen.
     * That is, visible in the current viewport.
     *
     * @method isOnScreen
     * @return {boolean}
     */
    isOnScreen: function () {
      return this._height !== 0 && this._top < this._height;
    },

    /**
     * Returns true if there's content below the current element.
     *
     * @method isContentBelow
     * @return {boolean}
     */
    isContentBelow: function () {
      return this._top === 0 ? this._clampedScrollTop > 0 : this._clampedScrollTop - this._maxHeaderTop >= 0;
    },

    /**
     * Transforms the header.
     *
     * @param {number} y
     */
    _transformHeader: function (y) {
      this.translate3d(0, -y + 'px', 0);

      if (this._stickyEl) {
        this.translate3d(0, this.condenses && y >= this._stickyElTop ? Math.min(y, this._dHeight) - this._stickyElTop + 'px' : 0, 0, this._stickyEl);
      }
    },
    _clamp: function (v, min, max) {
      return Math.min(max, Math.max(min, v));
    },
    _ensureBgContainers: function () {
      if (!this._bgContainer) {
        this._bgContainer = document.createElement('div');
        this._bgContainer.id = 'background';
        this._bgRear = document.createElement('div');
        this._bgRear.id = 'backgroundRearLayer';

        this._bgContainer.appendChild(this._bgRear);

        this._bgFront = document.createElement('div');
        this._bgFront.id = 'backgroundFrontLayer';

        this._bgContainer.appendChild(this._bgFront);

        dom(this.root).insertBefore(this._bgContainer, this.$.contentContainer);
      }
    },
    _getDOMRef: function (id) {
      switch (id) {
        case 'backgroundFrontLayer':
          this._ensureBgContainers();

          return this._bgFront;

        case 'backgroundRearLayer':
          this._ensureBgContainers();

          return this._bgRear;

        case 'background':
          this._ensureBgContainers();

          return this._bgContainer;

        case 'mainTitle':
          return dom(this).querySelector('[main-title]');

        case 'condensedTitle':
          return dom(this).querySelector('[condensed-title]');
      }

      return null;
    },

    /**
     * Returns an object containing the progress value of the scroll effects
     * and the top position of the header.
     *
     * @method getScrollState
     * @return {Object}
     */
    getScrollState: function () {
      return {
        progress: this._progress,
        top: this._top
      };
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const $_documentContainer = document.createElement('template');
  $_documentContainer.setAttribute('style', 'display: none;');
  $_documentContainer.innerHTML = `<dom-module id="paper-item-shared-styles">
  <template>
    <style>
      :host, .paper-item {
        display: block;
        position: relative;
        min-height: var(--paper-item-min-height, 48px);
        padding: 0px 16px;
      }

      .paper-item {
        @apply --paper-font-subhead;
        border:none;
        outline: none;
        background: white;
        width: 100%;
        text-align: left;
      }

      :host([hidden]), .paper-item[hidden] {
        display: none !important;
      }

      :host(.iron-selected), .paper-item.iron-selected {
        font-weight: var(--paper-item-selected-weight, bold);

        @apply --paper-item-selected;
      }

      :host([disabled]), .paper-item[disabled] {
        color: var(--paper-item-disabled-color, var(--disabled-text-color));

        @apply --paper-item-disabled;
      }

      :host(:focus), .paper-item:focus {
        position: relative;
        outline: 0;

        @apply --paper-item-focused;
      }

      :host(:focus):before, .paper-item:focus:before {
        @apply --layout-fit;

        background: currentColor;
        content: '';
        opacity: var(--dark-divider-opacity);
        pointer-events: none;

        @apply --paper-item-focused-before;
      }
    </style>
  </template>
</dom-module>`;
  document.head.appendChild($_documentContainer.content);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @demo demo/index.html
   * @polymerBehavior
   */

  const IronControlState = {
    properties: {
      /**
       * If true, the element currently has focus.
       */
      focused: {
        type: Boolean,
        value: false,
        notify: true,
        readOnly: true,
        reflectToAttribute: true
      },

      /**
       * If true, the user cannot interact with this element.
       */
      disabled: {
        type: Boolean,
        value: false,
        notify: true,
        observer: '_disabledChanged',
        reflectToAttribute: true
      },

      /**
       * Value of the `tabindex` attribute before `disabled` was activated.
       * `null` means the attribute was not present.
       * @type {?string|undefined}
       */
      _oldTabIndex: {
        type: String
      },
      _boundFocusBlurHandler: {
        type: Function,
        value: function () {
          return this._focusBlurHandler.bind(this);
        }
      }
    },
    observers: ['_changedControlState(focused, disabled)'],

    /**
     * @return {void}
     */
    ready: function () {
      this.addEventListener('focus', this._boundFocusBlurHandler, true);
      this.addEventListener('blur', this._boundFocusBlurHandler, true);
    },
    _focusBlurHandler: function (event) {
      // Polymer takes care of retargeting events.
      this._setFocused(event.type === 'focus');

      return;
    },
    _disabledChanged: function (disabled, old) {
      this.setAttribute('aria-disabled', disabled ? 'true' : 'false');
      this.style.pointerEvents = disabled ? 'none' : '';

      if (disabled) {
        // Read the `tabindex` attribute instead of the `tabIndex` property.
        // The property returns `-1` if there is no `tabindex` attribute.
        // This distinction is important when restoring the value because
        // leaving `-1` hides shadow root children from the tab order.
        this._oldTabIndex = this.getAttribute('tabindex');

        this._setFocused(false);

        this.tabIndex = -1;
        this.blur();
      } else if (this._oldTabIndex !== undefined) {
        if (this._oldTabIndex === null) {
          this.removeAttribute('tabindex');
        } else {
          this.setAttribute('tabindex', this._oldTabIndex);
        }
      }
    },
    _changedControlState: function () {
      // _controlStateChanged is abstract, follow-on behaviors may implement it
      if (this._controlStateChanged) {
        this._controlStateChanged();
      }
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Chrome uses an older version of DOM Level 3 Keyboard Events
   *
   * Most keys are labeled as text, but some are Unicode codepoints.
   * Values taken from:
   * http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221/keyset.html#KeySet-Set
   */

  var KEY_IDENTIFIER = {
    'U+0008': 'backspace',
    'U+0009': 'tab',
    'U+001B': 'esc',
    'U+0020': 'space',
    'U+007F': 'del'
  };
  /**
   * Special table for KeyboardEvent.keyCode.
   * KeyboardEvent.keyIdentifier is better, and KeyBoardEvent.key is even better
   * than that.
   *
   * Values from:
   * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode#Value_of_keyCode
   */

  var KEY_CODE = {
    8: 'backspace',
    9: 'tab',
    13: 'enter',
    27: 'esc',
    33: 'pageup',
    34: 'pagedown',
    35: 'end',
    36: 'home',
    32: 'space',
    37: 'left',
    38: 'up',
    39: 'right',
    40: 'down',
    46: 'del',
    106: '*'
  };
  /**
   * MODIFIER_KEYS maps the short name for modifier keys used in a key
   * combo string to the property name that references those same keys
   * in a KeyboardEvent instance.
   */

  var MODIFIER_KEYS = {
    'shift': 'shiftKey',
    'ctrl': 'ctrlKey',
    'alt': 'altKey',
    'meta': 'metaKey'
  };
  /**
   * KeyboardEvent.key is mostly represented by printable character made by
   * the keyboard, with unprintable keys labeled nicely.
   *
   * However, on OS X, Alt+char can make a Unicode character that follows an
   * Apple-specific mapping. In this case, we fall back to .keyCode.
   */

  var KEY_CHAR = /[a-z0-9*]/;
  /**
   * Matches a keyIdentifier string.
   */

  var IDENT_CHAR = /U\+/;
  /**
   * Matches arrow keys in Gecko 27.0+
   */

  var ARROW_KEY = /^arrow/;
  /**
   * Matches space keys everywhere (notably including IE10's exceptional name
   * `spacebar`).
   */

  var SPACE_KEY = /^space(bar)?/;
  /**
   * Matches ESC key.
   *
   * Value from: http://w3c.github.io/uievents-key/#key-Escape
   */

  var ESC_KEY = /^escape$/;
  /**
   * Transforms the key.
   * @param {string} key The KeyBoardEvent.key
   * @param {Boolean} [noSpecialChars] Limits the transformation to
   * alpha-numeric characters.
   */

  function transformKey(key, noSpecialChars) {
    var validKey = '';

    if (key) {
      var lKey = key.toLowerCase();

      if (lKey === ' ' || SPACE_KEY.test(lKey)) {
        validKey = 'space';
      } else if (ESC_KEY.test(lKey)) {
        validKey = 'esc';
      } else if (lKey.length == 1) {
        if (!noSpecialChars || KEY_CHAR.test(lKey)) {
          validKey = lKey;
        }
      } else if (ARROW_KEY.test(lKey)) {
        validKey = lKey.replace('arrow', '');
      } else if (lKey == 'multiply') {
        // numpad '*' can map to Multiply on IE/Windows
        validKey = '*';
      } else {
        validKey = lKey;
      }
    }

    return validKey;
  }

  function transformKeyIdentifier(keyIdent) {
    var validKey = '';

    if (keyIdent) {
      if (keyIdent in KEY_IDENTIFIER) {
        validKey = KEY_IDENTIFIER[keyIdent];
      } else if (IDENT_CHAR.test(keyIdent)) {
        keyIdent = parseInt(keyIdent.replace('U+', '0x'), 16);
        validKey = String.fromCharCode(keyIdent).toLowerCase();
      } else {
        validKey = keyIdent.toLowerCase();
      }
    }

    return validKey;
  }

  function transformKeyCode(keyCode) {
    var validKey = '';

    if (Number(keyCode)) {
      if (keyCode >= 65 && keyCode <= 90) {
        // ascii a-z
        // lowercase is 32 offset from uppercase
        validKey = String.fromCharCode(32 + keyCode);
      } else if (keyCode >= 112 && keyCode <= 123) {
        // function keys f1-f12
        validKey = 'f' + (keyCode - 112 + 1);
      } else if (keyCode >= 48 && keyCode <= 57) {
        // top 0-9 keys
        validKey = String(keyCode - 48);
      } else if (keyCode >= 96 && keyCode <= 105) {
        // num pad 0-9
        validKey = String(keyCode - 96);
      } else {
        validKey = KEY_CODE[keyCode];
      }
    }

    return validKey;
  }
  /**
   * Calculates the normalized key for a KeyboardEvent.
   * @param {KeyboardEvent} keyEvent
   * @param {Boolean} [noSpecialChars] Set to true to limit keyEvent.key
   * transformation to alpha-numeric chars. This is useful with key
   * combinations like shift + 2, which on FF for MacOS produces
   * keyEvent.key = @
   * To get 2 returned, set noSpecialChars = true
   * To get @ returned, set noSpecialChars = false
   */


  function normalizedKeyForEvent(keyEvent, noSpecialChars) {
    // Fall back from .key, to .detail.key for artifical keyboard events,
    // and then to deprecated .keyIdentifier and .keyCode.
    if (keyEvent.key) {
      return transformKey(keyEvent.key, noSpecialChars);
    }

    if (keyEvent.detail && keyEvent.detail.key) {
      return transformKey(keyEvent.detail.key, noSpecialChars);
    }

    return transformKeyIdentifier(keyEvent.keyIdentifier) || transformKeyCode(keyEvent.keyCode) || '';
  }

  function keyComboMatchesEvent(keyCombo, event) {
    // For combos with modifiers we support only alpha-numeric keys
    var keyEvent = normalizedKeyForEvent(event, keyCombo.hasModifiers);
    return keyEvent === keyCombo.key && (!keyCombo.hasModifiers || !!event.shiftKey === !!keyCombo.shiftKey && !!event.ctrlKey === !!keyCombo.ctrlKey && !!event.altKey === !!keyCombo.altKey && !!event.metaKey === !!keyCombo.metaKey);
  }

  function parseKeyComboString(keyComboString) {
    if (keyComboString.length === 1) {
      return {
        combo: keyComboString,
        key: keyComboString,
        event: 'keydown'
      };
    }

    return keyComboString.split('+').reduce(function (parsedKeyCombo, keyComboPart) {
      var eventParts = keyComboPart.split(':');
      var keyName = eventParts[0];
      var event = eventParts[1];

      if (keyName in MODIFIER_KEYS) {
        parsedKeyCombo[MODIFIER_KEYS[keyName]] = true;
        parsedKeyCombo.hasModifiers = true;
      } else {
        parsedKeyCombo.key = keyName;
        parsedKeyCombo.event = event || 'keydown';
      }

      return parsedKeyCombo;
    }, {
      combo: keyComboString.split(':').shift()
    });
  }

  function parseEventString(eventString) {
    return eventString.trim().split(' ').map(function (keyComboString) {
      return parseKeyComboString(keyComboString);
    });
  }
  /**
   * `Polymer.IronA11yKeysBehavior` provides a normalized interface for processing
   * keyboard commands that pertain to [WAI-ARIA best
   * practices](http://www.w3.org/TR/wai-aria-practices/#kbd_general_binding). The
   * element takes care of browser differences with respect to Keyboard events and
   * uses an expressive syntax to filter key presses.
   *
   * Use the `keyBindings` prototype property to express what combination of keys
   * will trigger the callback. A key binding has the format
   * `"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or
   * `"KEY:EVENT": "callback"` are valid as well). Some examples:
   *
   *      keyBindings: {
   *        'space': '_onKeydown', // same as 'space:keydown'
   *        'shift+tab': '_onKeydown',
   *        'enter:keypress': '_onKeypress',
   *        'esc:keyup': '_onKeyup'
   *      }
   *
   * The callback will receive with an event containing the following information
   * in `event.detail`:
   *
   *      _onKeydown: function(event) {
   *        console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab"
   *        console.log(event.detail.key); // KEY only, e.g. "tab"
   *        console.log(event.detail.event); // EVENT, e.g. "keydown"
   *        console.log(event.detail.keyboardEvent); // the original KeyboardEvent
   *      }
   *
   * Use the `keyEventTarget` attribute to set up event handlers on a specific
   * node.
   *
   * See the [demo source
   * code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html)
   * for an example.
   *
   * @demo demo/index.html
   * @polymerBehavior
   */


  const IronA11yKeysBehavior = {
    properties: {
      /**
       * The EventTarget that will be firing relevant KeyboardEvents. Set it to
       * `null` to disable the listeners.
       * @type {?EventTarget}
       */
      keyEventTarget: {
        type: Object,
        value: function () {
          return this;
        }
      },

      /**
       * If true, this property will cause the implementing element to
       * automatically stop propagation on any handled KeyboardEvents.
       */
      stopKeyboardEventPropagation: {
        type: Boolean,
        value: false
      },
      _boundKeyHandlers: {
        type: Array,
        value: function () {
          return [];
        }
      },
      // We use this due to a limitation in IE10 where instances will have
      // own properties of everything on the "prototype".
      _imperativeKeyBindings: {
        type: Object,
        value: function () {
          return {};
        }
      }
    },
    observers: ['_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'],

    /**
     * To be used to express what combination of keys  will trigger the relative
     * callback. e.g. `keyBindings: { 'esc': '_onEscPressed'}`
     * @type {!Object}
     */
    keyBindings: {},
    registered: function () {
      this._prepKeyBindings();
    },
    attached: function () {
      this._listenKeyEventListeners();
    },
    detached: function () {
      this._unlistenKeyEventListeners();
    },

    /**
     * Can be used to imperatively add a key binding to the implementing
     * element. This is the imperative equivalent of declaring a keybinding
     * in the `keyBindings` prototype property.
     *
     * @param {string} eventString
     * @param {string} handlerName
     */
    addOwnKeyBinding: function (eventString, handlerName) {
      this._imperativeKeyBindings[eventString] = handlerName;

      this._prepKeyBindings();

      this._resetKeyEventListeners();
    },

    /**
     * When called, will remove all imperatively-added key bindings.
     */
    removeOwnKeyBindings: function () {
      this._imperativeKeyBindings = {};

      this._prepKeyBindings();

      this._resetKeyEventListeners();
    },

    /**
     * Returns true if a keyboard event matches `eventString`.
     *
     * @param {KeyboardEvent} event
     * @param {string} eventString
     * @return {boolean}
     */
    keyboardEventMatchesKeys: function (event, eventString) {
      var keyCombos = parseEventString(eventString);

      for (var i = 0; i < keyCombos.length; ++i) {
        if (keyComboMatchesEvent(keyCombos[i], event)) {
          return true;
        }
      }

      return false;
    },
    _collectKeyBindings: function () {
      var keyBindings = this.behaviors.map(function (behavior) {
        return behavior.keyBindings;
      });

      if (keyBindings.indexOf(this.keyBindings) === -1) {
        keyBindings.push(this.keyBindings);
      }

      return keyBindings;
    },
    _prepKeyBindings: function () {
      this._keyBindings = {};

      this._collectKeyBindings().forEach(function (keyBindings) {
        for (var eventString in keyBindings) {
          this._addKeyBinding(eventString, keyBindings[eventString]);
        }
      }, this);

      for (var eventString in this._imperativeKeyBindings) {
        this._addKeyBinding(eventString, this._imperativeKeyBindings[eventString]);
      } // Give precedence to combos with modifiers to be checked first.


      for (var eventName in this._keyBindings) {
        this._keyBindings[eventName].sort(function (kb1, kb2) {
          var b1 = kb1[0].hasModifiers;
          var b2 = kb2[0].hasModifiers;
          return b1 === b2 ? 0 : b1 ? -1 : 1;
        });
      }
    },
    _addKeyBinding: function (eventString, handlerName) {
      parseEventString(eventString).forEach(function (keyCombo) {
        this._keyBindings[keyCombo.event] = this._keyBindings[keyCombo.event] || [];

        this._keyBindings[keyCombo.event].push([keyCombo, handlerName]);
      }, this);
    },
    _resetKeyEventListeners: function () {
      this._unlistenKeyEventListeners();

      if (this.isAttached) {
        this._listenKeyEventListeners();
      }
    },
    _listenKeyEventListeners: function () {
      if (!this.keyEventTarget) {
        return;
      }

      Object.keys(this._keyBindings).forEach(function (eventName) {
        var keyBindings = this._keyBindings[eventName];

        var boundKeyHandler = this._onKeyBindingEvent.bind(this, keyBindings);

        this._boundKeyHandlers.push([this.keyEventTarget, eventName, boundKeyHandler]);

        this.keyEventTarget.addEventListener(eventName, boundKeyHandler);
      }, this);
    },
    _unlistenKeyEventListeners: function () {
      var keyHandlerTuple;
      var keyEventTarget;
      var eventName;
      var boundKeyHandler;

      while (this._boundKeyHandlers.length) {
        // My kingdom for block-scope binding and destructuring assignment..
        keyHandlerTuple = this._boundKeyHandlers.pop();
        keyEventTarget = keyHandlerTuple[0];
        eventName = keyHandlerTuple[1];
        boundKeyHandler = keyHandlerTuple[2];
        keyEventTarget.removeEventListener(eventName, boundKeyHandler);
      }
    },
    _onKeyBindingEvent: function (keyBindings, event) {
      if (this.stopKeyboardEventPropagation) {
        event.stopPropagation();
      } // if event has been already prevented, don't do anything


      if (event.defaultPrevented) {
        return;
      }

      for (var i = 0; i < keyBindings.length; i++) {
        var keyCombo = keyBindings[i][0];
        var handlerName = keyBindings[i][1];

        if (keyComboMatchesEvent(keyCombo, event)) {
          this._triggerKeyHandler(keyCombo, handlerName, event); // exit the loop if eventDefault was prevented


          if (event.defaultPrevented) {
            return;
          }
        }
      }
    },
    _triggerKeyHandler: function (keyCombo, handlerName, keyboardEvent) {
      var detail = Object.create(keyCombo);
      detail.keyboardEvent = keyboardEvent;
      var event = new CustomEvent(keyCombo.event, {
        detail: detail,
        cancelable: true
      });
      this[handlerName].call(this, event);

      if (event.defaultPrevented) {
        keyboardEvent.preventDefault();
      }
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @demo demo/index.html
   * @polymerBehavior IronButtonState
   */

  const IronButtonStateImpl = {
    properties: {
      /**
       * If true, the user is currently holding down the button.
       */
      pressed: {
        type: Boolean,
        readOnly: true,
        value: false,
        reflectToAttribute: true,
        observer: '_pressedChanged'
      },

      /**
       * If true, the button toggles the active state with each tap or press
       * of the spacebar.
       */
      toggles: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },

      /**
       * If true, the button is a toggle and is currently in the active state.
       */
      active: {
        type: Boolean,
        value: false,
        notify: true,
        reflectToAttribute: true
      },

      /**
       * True if the element is currently being pressed by a "pointer," which
       * is loosely defined as mouse or touch input (but specifically excluding
       * keyboard input).
       */
      pointerDown: {
        type: Boolean,
        readOnly: true,
        value: false
      },

      /**
       * True if the input device that caused the element to receive focus
       * was a keyboard.
       */
      receivedFocusFromKeyboard: {
        type: Boolean,
        readOnly: true
      },

      /**
       * The aria attribute to be set if the button is a toggle and in the
       * active state.
       */
      ariaActiveAttribute: {
        type: String,
        value: 'aria-pressed',
        observer: '_ariaActiveAttributeChanged'
      }
    },
    listeners: {
      down: '_downHandler',
      up: '_upHandler',
      tap: '_tapHandler'
    },
    observers: ['_focusChanged(focused)', '_activeChanged(active, ariaActiveAttribute)'],

    /**
     * @type {!Object}
     */
    keyBindings: {
      'enter:keydown': '_asyncClick',
      'space:keydown': '_spaceKeyDownHandler',
      'space:keyup': '_spaceKeyUpHandler'
    },
    _mouseEventRe: /^mouse/,
    _tapHandler: function () {
      if (this.toggles) {
        // a tap is needed to toggle the active state
        this._userActivate(!this.active);
      } else {
        this.active = false;
      }
    },
    _focusChanged: function (focused) {
      this._detectKeyboardFocus(focused);

      if (!focused) {
        this._setPressed(false);
      }
    },
    _detectKeyboardFocus: function (focused) {
      this._setReceivedFocusFromKeyboard(!this.pointerDown && focused);
    },
    // to emulate native checkbox, (de-)activations from a user interaction fire
    // 'change' events
    _userActivate: function (active) {
      if (this.active !== active) {
        this.active = active;
        this.fire('change');
      }
    },
    _downHandler: function (event) {
      this._setPointerDown(true);

      this._setPressed(true);

      this._setReceivedFocusFromKeyboard(false);
    },
    _upHandler: function () {
      this._setPointerDown(false);

      this._setPressed(false);
    },

    /**
     * @param {!KeyboardEvent} event .
     */
    _spaceKeyDownHandler: function (event) {
      var keyboardEvent = event.detail.keyboardEvent;
      var target = dom(keyboardEvent).localTarget; // Ignore the event if this is coming from a focused light child, since that
      // element will deal with it.

      if (this.isLightDescendant(
      /** @type {Node} */
      target)) return;
      keyboardEvent.preventDefault();
      keyboardEvent.stopImmediatePropagation();

      this._setPressed(true);
    },

    /**
     * @param {!KeyboardEvent} event .
     */
    _spaceKeyUpHandler: function (event) {
      var keyboardEvent = event.detail.keyboardEvent;
      var target = dom(keyboardEvent).localTarget; // Ignore the event if this is coming from a focused light child, since that
      // element will deal with it.

      if (this.isLightDescendant(
      /** @type {Node} */
      target)) return;

      if (this.pressed) {
        this._asyncClick();
      }

      this._setPressed(false);
    },
    // trigger click asynchronously, the asynchrony is useful to allow one
    // event handler to unwind before triggering another event
    _asyncClick: function () {
      this.async(function () {
        this.click();
      }, 1);
    },
    // any of these changes are considered a change to button state
    _pressedChanged: function (pressed) {
      this._changedButtonState();
    },
    _ariaActiveAttributeChanged: function (value, oldValue) {
      if (oldValue && oldValue != value && this.hasAttribute(oldValue)) {
        this.removeAttribute(oldValue);
      }
    },
    _activeChanged: function (active, ariaActiveAttribute) {
      if (this.toggles) {
        this.setAttribute(this.ariaActiveAttribute, active ? 'true' : 'false');
      } else {
        this.removeAttribute(this.ariaActiveAttribute);
      }

      this._changedButtonState();
    },
    _controlStateChanged: function () {
      if (this.disabled) {
        this._setPressed(false);
      } else {
        this._changedButtonState();
      }
    },
    // provide hook for follow-on behaviors to react to button-state
    _changedButtonState: function () {
      if (this._buttonStateChanged) {
        this._buttonStateChanged(); // abstract

      }
    }
  };
  /** @polymerBehavior */

  const IronButtonState = [IronA11yKeysBehavior, IronButtonStateImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `PaperItemBehavior` is a convenience behavior shared by <paper-item> and
  <paper-icon-item> that manages the shared control states and attributes of
  the items.
  */

  /** @polymerBehavior PaperItemBehavior */

  const PaperItemBehaviorImpl = {
    hostAttributes: {
      role: 'option',
      tabindex: '0'
    }
  };
  /** @polymerBehavior */

  const PaperItemBehavior = [IronButtonState, IronControlState, PaperItemBehaviorImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `<paper-icon-item>` is a convenience element to make an item with icon. It is an
  interactive list item with a fixed-width icon area, according to Material
  Design. This is useful if the icons are of varying widths, but you want the item
  bodies to line up. Use this like a `<paper-item>`. The child node with the slot
  name `item-icon` is placed in the icon area.

      <paper-icon-item>
        <iron-icon icon="favorite" slot="item-icon"></iron-icon>
        Favorite
      </paper-icon-item>
      <paper-icon-item>
        <div class="avatar" slot="item-icon"></div>
        Avatar
      </paper-icon-item>

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-item-icon-width` | Width of the icon area | `56px`
  `--paper-item-icon` | Mixin applied to the icon area | `{}`
  `--paper-icon-item` | Mixin applied to the item | `{}`
  `--paper-item-selected-weight` | The font weight of a selected item | `bold`
  `--paper-item-selected` | Mixin applied to selected paper-items | `{}`
  `--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color`
  `--paper-item-disabled` | Mixin applied to disabled paper-items | `{}`
  `--paper-item-focused` | Mixin applied to focused paper-items | `{}`
  `--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}`

  */

  Polymer({
    _template: html$1`
    <style include="paper-item-shared-styles"></style>
    <style>
      :host {
        @apply --layout-horizontal;
        @apply --layout-center;
        @apply --paper-font-subhead;

        @apply --paper-item;
        @apply --paper-icon-item;
      }

      .content-icon {
        @apply --layout-horizontal;
        @apply --layout-center;

        width: var(--paper-item-icon-width, 56px);
        @apply --paper-item-icon;
      }
    </style>

    <div id="contentIcon" class="content-icon">
      <slot name="item-icon"></slot>
    </div>
    <slot></slot>
`,
    is: 'paper-icon-item',
    behaviors: [PaperItemBehavior]
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  Use `<paper-item-body>` in a `<paper-item>` or `<paper-icon-item>` to make two-
  or three- line items. It is a flex item that is a vertical flexbox.

      <paper-item>
        <paper-item-body two-line>
          <div>Show your status</div>
          <div secondary>Your status is visible to everyone</div>
        </paper-item-body>
      </paper-item>

  The child elements with the `secondary` attribute is given secondary text
  styling.

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-item-body-two-line-min-height` | Minimum height of a two-line item | `72px`
  `--paper-item-body-three-line-min-height` | Minimum height of a three-line item | `88px`
  `--paper-item-body-secondary-color` | Foreground color for the `secondary` area | `--secondary-text-color`
  `--paper-item-body-secondary` | Mixin applied to the `secondary` area | `{}`

  */

  Polymer({
    _template: html$1`
    <style>
      :host {
        overflow: hidden; /* needed for text-overflow: ellipsis to work on ff */
        @apply --layout-vertical;
        @apply --layout-center-justified;
        @apply --layout-flex;
      }

      :host([two-line]) {
        min-height: var(--paper-item-body-two-line-min-height, 72px);
      }

      :host([three-line]) {
        min-height: var(--paper-item-body-three-line-min-height, 88px);
      }

      :host > ::slotted(*) {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      :host > ::slotted([secondary]) {
        @apply --paper-font-body1;

        color: var(--paper-item-body-secondary-color, var(--secondary-text-color));

        @apply --paper-item-body-secondary;
      }
    </style>

    <slot></slot>
`,
    is: 'paper-item-body'
  });

  let HaCard = _decorate([customElement("ha-card")], function (_initialize, _LitElement) {
    class HaCard extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaCard,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "header",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "outlined",

        value() {
          return false;
        }

      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        background: var(
          --ha-card-background,
          var(--card-background-color, white)
        );
        border-radius: var(--ha-card-border-radius, 4px);
        box-shadow: var(
          --ha-card-box-shadow,
          0px 2px 1px -1px rgba(0, 0, 0, 0.2),
          0px 1px 1px 0px rgba(0, 0, 0, 0.14),
          0px 1px 3px 0px rgba(0, 0, 0, 0.12)
        );
        color: var(--primary-text-color);
        display: block;
        transition: all 0.3s ease-out;
        position: relative;
      }

      :host([outlined]) {
        box-shadow: none;
        border-width: 1px;
        border-style: solid;
        border-color: var(
          --ha-card-border-color,
          var(--divider-color, #e0e0e0)
        );
      }

      .card-header,
      :host ::slotted(.card-header) {
        color: var(--ha-card-header-color, --primary-text-color);
        font-family: var(--ha-card-header-font-family, inherit);
        font-size: var(--ha-card-header-font-size, 24px);
        letter-spacing: -0.012em;
        line-height: 32px;
        padding: 24px 16px 16px;
        display: block;
      }

      :host ::slotted(.card-content:not(:first-child)),
      slot:not(:first-child)::slotted(.card-content) {
        padding-top: 0px;
        margin-top: -8px;
      }

      :host ::slotted(.card-content) {
        padding: 16px;
      }

      :host ::slotted(.card-actions) {
        border-top: 1px solid var(--divider-color, #e8e8e8);
        padding: 5px 16px;
      }
    `;
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      ${this.header ? html` <div class="card-header">${this.header}</div> ` : html``}
      <slot></slot>
    `;
        }
      }]
    };
  }, LitElement);

  let HaConfigSection = _decorate([customElement("ha-config-section")], function (_initialize, _LitElement) {
    class HaConfigSection extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaConfigSection,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "isWide",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <div
        class="content ${classMap({
          narrow: !this.isWide
        })}"
      >
        <div class="header"><slot name="header"></slot></div>
        <div
          class="together layout ${classMap({
          narrow: !this.isWide,
          vertical: !this.isWide,
          horizontal: this.isWide
        })}"
        >
          <div class="intro"><slot name="introduction"></slot></div>
          <div class="panel flex-auto"><slot></slot></div>
        </div>
      </div>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: block;
      }
      .content {
        padding: 28px 20px 0;
        max-width: 1040px;
        margin: 0 auto;
      }

      .layout {
        display: flex;
      }

      .horizontal {
        flex-direction: row;
      }

      .vertical {
        flex-direction: column;
      }

      .flex-auto {
        flex: 1 1 auto;
      }

      .header {
        font-family: var(--paper-font-headline_-_font-family);
        -webkit-font-smoothing: var(
          --paper-font-headline_-_-webkit-font-smoothing
        );
        font-size: var(--paper-font-headline_-_font-size);
        font-weight: var(--paper-font-headline_-_font-weight);
        letter-spacing: var(--paper-font-headline_-_letter-spacing);
        line-height: var(--paper-font-headline_-_line-height);
        opacity: var(--dark-primary-opacity);
      }

      .together {
        margin-top: 32px;
      }

      .intro {
        font-family: var(--paper-font-subhead_-_font-family);
        -webkit-font-smoothing: var(
          --paper-font-subhead_-_-webkit-font-smoothing
        );
        font-weight: var(--paper-font-subhead_-_font-weight);
        line-height: var(--paper-font-subhead_-_line-height);
        width: 100%;
        max-width: 400px;
        margin-right: 40px;
        opacity: var(--dark-primary-opacity);
        font-size: 14px;
        padding-bottom: 20px;
      }

      .panel {
        margin-top: -24px;
      }

      .panel ::slotted(*) {
        margin-top: 24px;
        display: block;
      }

      .narrow.content {
        max-width: 640px;
      }
      .narrow .together {
        margin-top: 20px;
      }
      .narrow .intro {
        padding-bottom: 20px;
        margin-right: 0;
        max-width: 500px;
      }
    `;
        }
      }]
    };
  }, LitElement);

  const sortRepositoriesByName = repositories => {
    return repositories === null || repositories === void 0 ? void 0 : repositories.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
  };

  const version$1 = "202009132200";

  const getMessages = memoizeOne((hacs, repositories) => {
    var _hacs$status, _hacs$status2, _hacs$status3;

    const messages = [];
    const repositoriesNotAddedToLovelace = [];
    const repositoriesRestartPending = [];
    repositories === null || repositories === void 0 ? void 0 : repositories.forEach(repo => {
      if (repo.status === "pending-restart") {
        repositoriesRestartPending.push(repo);
      }

      if (repo.installed && repo.category === "plugin" && !addedToLovelace(hacs, repo)) {
        repositoriesNotAddedToLovelace.push(repo);
      }

      if (repo.installed && hacs.removed.map(r => r.repository).includes(repo.full_name)) {
        const removedrepo = hacs.removed.find(r => r.repository === repo.full_name);
        messages.push({
          name: hacs.localize("entry.messages.removed").replace("{repository}", removedrepo.repository),
          info: removedrepo.reason,
          severity: "error",
          dialog: "removed",
          repository: repo
        });
      }
    });

    if (hacs.configuration.frontend_expected !== hacs.configuration.frontend_running) {
      messages.push({
        name: hacs.localize("entry.messages.wrong_frontend_installed.title"),
        info: hacs.localize("entry.messages.wrong_frontend_installed.content").replace("{running}", hacs.configuration.frontend_running).replace("{expected}", hacs.configuration.frontend_expected),
        severity: "error"
      });
    } else if (hacs.configuration.frontend_expected !== version$1 && hacs.configuration.frontend_expected !== undefined) {
      messages.push({
        name: hacs.localize("entry.messages.wrong_frontend_loaded.title"),
        info: hacs.localize("entry.messages.wrong_frontend_loaded.content").replace("{running}", version$1).replace("{expected}", hacs.configuration.frontend_expected),
        severity: "error"
      });
    }

    if (((_hacs$status = hacs.status) === null || _hacs$status === void 0 ? void 0 : _hacs$status.startup) && ["setup", "waiting", "startup"].includes(hacs.status.stage)) {
      messages.push({
        name: hacs.localize(`entry.messages.${hacs.status.stage}.title`),
        info: hacs.localize(`entry.messages.${hacs.status.stage}.content`),
        severity: "information"
      });
    }

    if ((_hacs$status2 = hacs.status) === null || _hacs$status2 === void 0 ? void 0 : _hacs$status2.has_pending_tasks) {
      messages.push({
        name: hacs.localize("entry.messages.has_pending_tasks.title"),
        info: hacs.localize("entry.messages.has_pending_tasks.content"),
        severity: "warning"
      });
    }

    if ((_hacs$status3 = hacs.status) === null || _hacs$status3 === void 0 ? void 0 : _hacs$status3.disabled) {
      messages.push({
        name: hacs.localize("entry.messages.disabled.title"),
        info: hacs.localize("entry.messages.disabled.content"),
        severity: "error"
      });
    }

    if (repositoriesNotAddedToLovelace.length > 0) {
      messages.push({
        name: hacs.localize("entry.messages.resources.title"),
        info: hacs.localize("entry.messages.resources.content").replace("{number}", String(repositoriesNotAddedToLovelace.length)),
        severity: "error",
        path: "/hacs/frontend"
      });
    }

    if (repositoriesRestartPending.length > 0) {
      messages.push({
        name: hacs.localize("entry.messages.restart.title"),
        info: hacs.localize("entry.messages.restart.content").replace("{number}", String(repositoriesRestartPending.length)),
        severity: "error",
        path: "/config/server_control"
      });
    }

    return messages;
  });

  class HaIconNext extends HaSvgIcon {
    connectedCallback() {
      super.connectedCallback(); // wait to check for direction since otherwise direction is wrong even though top level is RTL

      setTimeout(() => {
        this.path = window.getComputedStyle(this).direction === "ltr" ? mdiChevronRight : mdiChevronLeft;
      }, 100);
    }

  }
  customElements.define("ha-icon-next", HaIconNext);

  let HacsLink = _decorate([customElement("hacs-link")], function (_initialize, _LitElement) {
    class HacsLink extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsLink,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "newtab",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "parent",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "title",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "url",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`<span title=${this.title || this.url} @tap=${this._open}><slot></slot></span>`;
        }
      }, {
        kind: "method",
        key: "_open",
        value: function _open() {
          var _this$url;

          if (this.url.startsWith("/")) {
            navigate(this, this.url, true);
            return;
          }

          const external = (_this$url = this.url) === null || _this$url === void 0 ? void 0 : _this$url.includes("http");
          let features = "";
          let target = "_blank";

          if (external) {
            features = "noreferrer=true";
          }

          if (!external && !this.newtab) {
            target = "_top";
          }

          if (!external && !this.parent) {
            target = "_parent";
          }

          window.open(this.url, target, features);
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      span {
        cursor: pointer;
        color: var(--hcv-text-color-link);
        text-decoration: var(--hcv-text-decoration-link);
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HacsSectionNavigation = _decorate([customElement("hacs-section-navigation")], function (_initialize, _LitElement) {
    class HacsSectionNavigation extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsSectionNavigation,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "pages",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "header",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          attribute: "no-next"
        })],
        key: "noNext",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <ha-card>
        ${this.header ? html`<div class="card-header">${this.header}</div>` : ""}
        ${this.pages.map(page => html`
              <hacs-link .url=${page.path}>
                <paper-icon-item
                  @tap=${() => {
          this._openDialog(page);
        }}
                >
                  <ha-svg-icon
                    .path=${page.iconPath}
                    slot="item-icon"
                    class="${page.class || ""}"
                  ></ha-svg-icon>
                  <paper-item-body two-line>
                    ${page.name}
                    <div secondary>
                      ${page.info ? page.info : ""}
                    </div>
                  </paper-item-body>
                  ${!this.noNext ? html`<ha-icon-next></ha-icon-next>` : ""}
                </paper-icon-item>
              </hacs-link>
            `)}
      </ha-card>
    `;
        }
      }, {
        kind: "method",
        key: "_openDialog",
        value: function _openDialog(page) {
          if (!page.dialog) {
            return;
          }

          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: page.dialog,
              repository: page.repository
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      hacs-link {
        text-decoration: none;
        --hcv-text-color-link: var(--primary-text-color);
        position: relative;
        display: block;
        outline: 0;
      }
      ha-svg-icon,
      ha-icon-next {
        color: var(--secondary-text-color);
      }
      .iron-selected paper-item::before,
      a:not(.iron-selected):focus::before {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        pointer-events: none;
        content: "";
        transition: opacity 15ms linear;
        will-change: opacity;
      }
      a:not(.iron-selected):focus::before {
        background-color: currentColor;
        opacity: var(--dark-divider-opacity);
      }
      .iron-selected paper-item:focus::before,
      .iron-selected:focus paper-item::before {
        opacity: 0.2;
      }
      div[secondary] {
        white-space: normal;
      }
      ha-svg-icon.warning {
        color: var(--hcv-color-warning);
      }
      ha-svg-icon.error {
        color: var(--hcv-color-error);
      }

      .card-header {
        color: var(--ha-card-header-color, --primary-text-color);
        font-family: var(--ha-card-header-font-family, inherit);
        font-size: var(--ha-card-header-font-size, 24px);
        letter-spacing: -0.012em;
        line-height: 32px;
        display: block;
        padding: 8px 16px 0;
      }
    `;
        }
      }]
    };
  }, LitElement);

  /** Return if a component is loaded. */
  const isComponentLoaded = (hass, component) => hass && hass.config.components.indexOf(component) !== -1;

  let HacsEntryPanel = _decorate([customElement("hacs-entry-panel")], function (_initialize, _LitElement) {
    class HacsEntryPanel extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsEntryPanel,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "configuration",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "lovelace",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "removed",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "isWide",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$repositories, _this$hacs$messages, _this$hacs$messages2, _this$hacs$updates;

          const updates = [];
          const messages = [];
          const allMessages = getMessages(this.hacs, this.repositories);
          (_this$repositories = this.repositories) === null || _this$repositories === void 0 ? void 0 : _this$repositories.forEach(repo => {
            if (repo.pending_upgrade) {
              updates.push(repo);
            }
          });
          allMessages === null || allMessages === void 0 ? void 0 : allMessages.forEach(message => {
            messages.push({
              iconPath: mdiAlertCircle,
              name: message.name,
              info: message.info,
              path: message.path || "",
              class: message.severity,
              dialog: message.dialog,
              repository: message.repository
            });
          });
          this.dispatchEvent(new CustomEvent("update-hacs", {
            detail: {
              messages,
              updates
            },
            bubbles: true,
            composed: true
          }));
          return html`
      <app-header-layout has-scrolling-region>
        <app-header fixed slot="header">
          <app-toolbar>
            <ha-menu-button .hass=${this.hass} .narrow=${this.narrow}></ha-menu-button>
          </app-toolbar>
        </app-header>
        <ha-config-section .narrow=${this.narrow} .isWide=${this.isWide}>
          <div slot="header">
            ${this.narrow ? "HACS" : "Home Assistant Community Store"}
          </div>

          <div slot="introduction">
            ${this.isWide || this.hacs.updates.length === 0 && ((_this$hacs$messages = this.hacs.messages) === null || _this$hacs$messages === void 0 ? void 0 : _this$hacs$messages.length) === 0 ? localize("entry.intro") : ""}
          </div>

          ${((_this$hacs$messages2 = this.hacs.messages) === null || _this$hacs$messages2 === void 0 ? void 0 : _this$hacs$messages2.length) !== 0 ? html`
                <hacs-section-navigation
                  .hass=${this.hass}
                  .header=${localize("entry.information")}
                  .pages=${this.hacs.messages}
                  no-next
                ></hacs-section-navigation>
              ` : ""}
          ${((_this$hacs$updates = this.hacs.updates) === null || _this$hacs$updates === void 0 ? void 0 : _this$hacs$updates.length) !== 0 ? html` <ha-card>
                <div class="header">${localize("entry.pending_updates")}</div>
                ${sortRepositoriesByName(this.hacs.updates).map(repository => html`
                      <paper-icon-item @click="${() => this._openUpdateDialog(repository)}">
                        <ha-svg-icon
                          class="pending_update"
                          .path=${mdiAlertCircle}
                          slot="item-icon"
                        ></ha-svg-icon>
                        <paper-item-body two-line>
                          ${repository.name}
                          <div secondary>
                            ${localize("sections.pending_repository_upgrade").replace("{installed}", repository.installed_version).replace("{available}", repository.available_version)}
                          </div>
                        </paper-item-body>
                      </paper-icon-item>
                    `)}
              </ha-card>` : ""}
          <hacs-section-navigation .pages=${this.hacs.sections}></hacs-section-navigation>

          <ha-card>
            ${isComponentLoaded(this.hass, "hassio") ? html`
                  <paper-icon-item @click=${this._openSupervisorDialog}>
                    <ha-svg-icon .path=${mdiHomeAssistant} slot="item-icon"></ha-svg-icon>
                    <paper-item-body two-line>
                      ${localize(`sections.addon.title`)}
                      <div secondary>
                        ${localize(`sections.addon.description`)}
                      </div>
                    </paper-item-body>
                    <ha-svg-icon right .path=${mdiOpenInNew}></ha-svg-icon>
                  </paper-icon-item>
                ` : ""}
          </ha-card>

          <ha-card>
            <paper-icon-item @click=${this._openAboutDialog}>
              <ha-svg-icon .path=${mdiAlertCircle} slot="item-icon"></ha-svg-icon>
              <paper-item-body two-line>
                ${localize(`sections.about.title`)}
                <div secondary>
                  ${localize(`sections.about.description`)}
                </div>
              </paper-item-body>
            </paper-icon-item>
          </ha-card>
        </ha-config-section>
      </app-header-layout>
    `;
        }
      }, {
        kind: "method",
        key: "_openUpdateDialog",
        value: function _openUpdateDialog(repository) {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "update",
              repository: repository.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_openAboutDialog",
        value: async function _openAboutDialog() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "about",
              configuration: this.configuration,
              repositories: this.repositories
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_openSupervisorDialog",
        value: async function _openSupervisorDialog() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "navigate",
              path: "/hassio"
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [haStyle, HacsStyles, css`
        paper-icon-item {
          cursor: pointer;
        }
        paper-icon-item[information] {
          cursor: normal;
        }

        app-header-layout {
          display: contents;
        }
        app-header,
        app-toolbar,
        ha-menu-button {
          color: var(--secondary-text-color);
          background-color: var(--primary-background-color);
          --app-header-background-color: var(--primary-background-color);
        }

        ha-svg-icon {
          color: var(--secondary-text-color);
        }

        ha-config-section {
          color: var(--primary-text-color);
          padding-bottom: 24px;
          margin-top: -24px;
        }

        paper-item-body {
          width: 100%;
          min-height: var(--paper-item-body-two-line-min-height, 72px);
          display: var(--layout-vertical_-_display);
          flex-direction: var(--layout-vertical_-_flex-direction);
          justify-content: var(--layout-center-justified_-_justify-content);
        }
        paper-item-body {
          color: var(--hcv-text-color-primary);
        }
        paper-item-body div {
          font-size: 14px;
          color: var(--hcv-text-color-secondary);
        }
        div[secondary] {
          white-space: normal;
        }
      `];
        }
      }]
    };
  }, LitElement);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$4(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /**
   * Fab Base class logic and template definition
   * @soyCompatible
   */

  class FabBase extends LitElement {
    constructor() {
      super(...arguments);
      this.mini = false;
      this.exited = false;
      this.disabled = false;
      this.extended = false;
      this.showIconAtEnd = false;
      this.icon = '';
      this.label = '';
      this.shouldRenderRipple = false;
      this.rippleHandlers = new RippleHandlers(() => {
        this.shouldRenderRipple = true;
        return this.ripple;
      });
    }

    createRenderRoot() {
      return this.attachShadow({
        mode: 'open',
        delegatesFocus: true
      });
    }
    /** @soyCompatible */


    render() {
      /** @classMap */
      const classes = {
        'mdc-fab--mini': this.mini,
        'mdc-fab--exited': this.exited,
        'mdc-fab--extended': this.extended,
        'icon-end': this.showIconAtEnd
      };
      return html`
      <button
          class="mdc-fab ${classMap(classes)}"
          ?disabled="${this.disabled}"
          aria-label="${this.label || this.icon}"
          @mouseenter=${this.handleRippleMouseEnter}
          @mouseleave=${this.handleRippleMouseLeave}
          @focus=${this.handleRippleFocus}
          @blur=${this.handleRippleBlur}
          @mousedown=${this.handleRippleActivate}
          @touchstart=${this.handleRippleStartPress}
          @touchend=${this.handleRippleDeactivate}
          @touchcancel=${this.handleRippleDeactivate}>
        ${this.renderBeforeRipple()}
        ${this.renderRipple()}
        ${this.showIconAtEnd ? this.renderLabel() : ''}
        <slot name="icon">
          ${this.renderIcon()}
        </slot>
        ${!this.showIconAtEnd ? this.renderLabel() : ''}
      </button>`;
    }
    /** @soyCompatible */


    renderIcon() {
      return html`${this.icon ? html`
          <span class="material-icons mdc-fab__icon">${this.icon}</span>` : ''}`;
    }
    /** @soyCompatible */


    renderLabel() {
      const showLabel = this.label !== '' && this.extended;
      return html`${showLabel ? html`<span class="mdc-fab__label">${this.label}</span>` : ''}`;
    }
    /** @soyCompatible */


    renderBeforeRipple() {
      return html``;
    }
    /** @soyCompatible */


    renderRipple() {
      return html`${this.shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ''}`;
    }

    handleRippleActivate(event) {
      const onUp = () => {
        window.removeEventListener('mouseup', onUp);
        this.handleRippleDeactivate();
      };

      window.addEventListener('mouseup', onUp);
      this.handleRippleStartPress(event);
    }

    handleRippleStartPress(event) {
      this.rippleHandlers.startPress(event);
    }

    handleRippleDeactivate() {
      this.rippleHandlers.endPress();
    }

    handleRippleMouseEnter() {
      this.rippleHandlers.startHover();
    }

    handleRippleMouseLeave() {
      this.rippleHandlers.endHover();
    }

    handleRippleFocus() {
      this.rippleHandlers.startFocus();
    }

    handleRippleBlur() {
      this.rippleHandlers.endFocus();
    }

  }

  __decorate$4([queryAsync('mwc-ripple')], FabBase.prototype, "ripple", void 0);

  __decorate$4([property({
    type: Boolean
  })], FabBase.prototype, "mini", void 0);

  __decorate$4([property({
    type: Boolean
  })], FabBase.prototype, "exited", void 0);

  __decorate$4([property({
    type: Boolean
  })], FabBase.prototype, "disabled", void 0);

  __decorate$4([property({
    type: Boolean
  })], FabBase.prototype, "extended", void 0);

  __decorate$4([property({
    type: Boolean
  })], FabBase.prototype, "showIconAtEnd", void 0);

  __decorate$4([property()], FabBase.prototype, "icon", void 0);

  __decorate$4([property()], FabBase.prototype, "label", void 0);

  __decorate$4([internalProperty()], FabBase.prototype, "shouldRenderRipple", void 0);

  __decorate$4([eventOptions({
    passive: true
  })], FabBase.prototype, "handleRippleStartPress", null);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$5 = css`:host .mdc-fab .material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{outline:none;--mdc-ripple-color: currentcolor;user-select:none;-webkit-tap-highlight-color:transparent}:host .mdc-touch-target-wrapper{display:inline}:host .mdc-elevation-overlay{position:absolute;border-radius:inherit;pointer-events:none;opacity:0;opacity:var(--mdc-elevation-overlay-opacity, 0);transition:opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);background-color:#fff;background-color:var(--mdc-elevation-overlay-color, #fff)}:host .mdc-fab{position:relative;box-shadow:0px 3px 5px -1px rgba(0, 0, 0, 0.2),0px 6px 10px 0px rgba(0, 0, 0, 0.14),0px 1px 18px 0px rgba(0,0,0,.12);display:inline-flex;position:relative;align-items:center;justify-content:center;box-sizing:border-box;width:56px;height:56px;padding:0;border:none;fill:currentColor;text-decoration:none;cursor:pointer;user-select:none;-moz-appearance:none;-webkit-appearance:none;overflow:visible;transition:box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1),opacity 15ms linear 30ms,transform 270ms 0ms cubic-bezier(0, 0, 0.2, 1);background-color:#018786;background-color:var(--mdc-theme-secondary, #018786);color:#fff;color:var(--mdc-theme-on-secondary, #fff)}:host .mdc-fab .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}:host .mdc-fab:not(.mdc-fab--extended){border-radius:50%}:host .mdc-fab:not(.mdc-fab--extended) .mdc-fab__ripple{border-radius:50%}:host .mdc-fab::-moz-focus-inner{padding:0;border:0}:host .mdc-fab:hover,:host .mdc-fab:focus{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12)}:host .mdc-fab:active{box-shadow:0px 7px 8px -4px rgba(0, 0, 0, 0.2),0px 12px 17px 2px rgba(0, 0, 0, 0.14),0px 5px 22px 4px rgba(0,0,0,.12)}:host .mdc-fab:active,:host .mdc-fab:focus{outline:none}:host .mdc-fab:hover{cursor:pointer}:host .mdc-fab>svg{width:100%}:host .mdc-fab .mdc-fab__icon{width:24px;height:24px;font-size:24px}:host .mdc-fab--mini{width:40px;height:40px}:host .mdc-fab--extended{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-button-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-button-font-size, 0.875rem);line-height:2.25rem;line-height:var(--mdc-typography-button-line-height, 2.25rem);font-weight:500;font-weight:var(--mdc-typography-button-font-weight, 500);letter-spacing:0.0892857143em;letter-spacing:var(--mdc-typography-button-letter-spacing, 0.0892857143em);text-decoration:none;text-decoration:var(--mdc-typography-button-text-decoration, none);text-transform:uppercase;text-transform:var(--mdc-typography-button-text-transform, uppercase);border-radius:24px;padding-left:20px;padding-right:20px;width:auto;max-width:100%;height:48px;line-height:normal}:host .mdc-fab--extended .mdc-fab__ripple{border-radius:24px}:host .mdc-fab--extended .mdc-fab__icon{margin-left:-8px;margin-right:12px}[dir=rtl] :host .mdc-fab--extended .mdc-fab__icon,:host .mdc-fab--extended .mdc-fab__icon[dir=rtl]{margin-right:-8px}[dir=rtl] :host .mdc-fab--extended .mdc-fab__icon,:host .mdc-fab--extended .mdc-fab__icon[dir=rtl]{margin-left:12px}:host .mdc-fab--extended .mdc-fab__label+.mdc-fab__icon{margin-right:-8px;margin-left:12px}[dir=rtl] :host .mdc-fab--extended .mdc-fab__label+.mdc-fab__icon,:host .mdc-fab--extended .mdc-fab__label+.mdc-fab__icon[dir=rtl]{margin-left:-8px}[dir=rtl] :host .mdc-fab--extended .mdc-fab__label+.mdc-fab__icon,:host .mdc-fab--extended .mdc-fab__label+.mdc-fab__icon[dir=rtl]{margin-right:12px}:host .mdc-fab--touch{margin-top:4px;margin-bottom:4px;margin-right:4px;margin-left:4px}:host .mdc-fab--touch .mdc-fab__touch{position:absolute;top:50%;right:0;height:48px;left:50%;width:48px;transform:translate(-50%, -50%)}:host .mdc-fab::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:""}:host .mdc-fab__label{justify-content:flex-start;text-overflow:ellipsis;white-space:nowrap;overflow-x:hidden;overflow-y:visible}:host .mdc-fab__icon{transition:transform 180ms 90ms cubic-bezier(0, 0, 0.2, 1);fill:currentColor;will-change:transform}:host .mdc-fab .mdc-fab__icon{display:inline-flex;align-items:center;justify-content:center}:host .mdc-fab--exited{transform:scale(0);opacity:0;transition:opacity 15ms linear 150ms,transform 180ms 0ms cubic-bezier(0.4, 0, 1, 1)}:host .mdc-fab--exited .mdc-fab__icon{transform:scale(0);transition:transform 135ms 0ms cubic-bezier(0.4, 0, 1, 1)}:host .mdc-fab{box-shadow:0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12);box-shadow:var(--mdc-fab-box-shadow, 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12))}:host .mdc-fab:hover,:host .mdc-fab:focus{box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12);box-shadow:var(--mdc-fab-box-shadow, 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12))}:host .mdc-fab:active{box-shadow:0px 7px 8px -4px rgba(0, 0, 0, 0.2), 0px 12px 17px 2px rgba(0, 0, 0, 0.14), 0px 5px 22px 4px rgba(0, 0, 0, 0.12);box-shadow:var(--mdc-fab-box-shadow, 0px 7px 8px -4px rgba(0, 0, 0, 0.2), 0px 12px 17px 2px rgba(0, 0, 0, 0.14), 0px 5px 22px 4px rgba(0, 0, 0, 0.12))}:host .mdc-fab mwc-ripple{overflow:hidden}:host .mdc-fab .mdc-fab__label{z-index:0}:host .mdc-fab:not(.mdc-fab--extended) mwc-ripple{border-radius:50%}:host .mdc-fab.mdc-fab--extended mwc-ripple{border-radius:24px}:host .mdc-fab .mdc-fab__icon,:host .mdc-fab ::slotted([slot=icon]){width:24px;width:var(--mdc-icon-size, 24px);height:24px;height:var(--mdc-icon-size, 24px);font-size:24px;font-size:var(--mdc-icon-size, 24px);transition:transform 180ms 90ms cubic-bezier(0, 0, 0.2, 1);fill:currentColor;will-change:transform;display:inline-flex;align-items:center;justify-content:center}:host .mdc-fab.mdc-fab--extended{padding-left:20px;padding-left:var(--mdc-fab-extended-label-padding, 20px);padding-right:20px;padding-right:var(--mdc-fab-extended-label-padding, 20px)}:host .mdc-fab.mdc-fab--extended .mdc-fab__icon{margin-left:-8px;margin-left:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px));margin-right:12px;margin-right:var(--mdc-fab-extended-icon-padding, 12px)}[dir=rtl] :host .mdc-fab.mdc-fab--extended .mdc-fab__icon,:host .mdc-fab.mdc-fab--extended .mdc-fab__icon[dir=rtl]{margin-right:-8px;margin-right:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px))}[dir=rtl] :host .mdc-fab.mdc-fab--extended .mdc-fab__icon,:host .mdc-fab.mdc-fab--extended .mdc-fab__icon[dir=rtl]{margin-left:12px;margin-left:var(--mdc-fab-extended-icon-padding, 12px)}:host .mdc-fab.mdc-fab--extended .mdc-fab__label+.mdc-fab__icon{margin-right:-8px;margin-right:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px));margin-left:12px;margin-left:var(--mdc-fab-extended-icon-padding, 12px)}[dir=rtl] :host .mdc-fab.mdc-fab--extended .mdc-fab__label+.mdc-fab__icon,:host .mdc-fab.mdc-fab--extended .mdc-fab__label+.mdc-fab__icon[dir=rtl]{margin-left:-8px;margin-left:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px))}[dir=rtl] :host .mdc-fab.mdc-fab--extended .mdc-fab__label+.mdc-fab__icon,:host .mdc-fab.mdc-fab--extended .mdc-fab__label+.mdc-fab__icon[dir=rtl]{margin-right:12px;margin-right:var(--mdc-fab-extended-icon-padding, 12px)}:host .mdc-fab.mdc-fab--extended ::slotted([slot=icon]){margin-left:-8px;margin-left:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px));margin-right:12px;margin-right:var(--mdc-fab-extended-icon-padding, 12px)}[dir=rtl] :host .mdc-fab.mdc-fab--extended ::slotted([slot=icon]),:host .mdc-fab.mdc-fab--extended ::slotted([slot=icon])[dir=rtl]{margin-right:-8px;margin-right:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px))}[dir=rtl] :host .mdc-fab.mdc-fab--extended ::slotted([slot=icon]),:host .mdc-fab.mdc-fab--extended ::slotted([slot=icon])[dir=rtl]{margin-left:12px;margin-left:var(--mdc-fab-extended-icon-padding, 12px)}:host .mdc-fab.mdc-fab--extended.icon-end .mdc-fab__icon{margin-right:-8px;margin-right:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px));margin-left:12px;margin-left:var(--mdc-fab-extended-icon-padding, 12px)}[dir=rtl] :host .mdc-fab.mdc-fab--extended.icon-end .mdc-fab__icon,:host .mdc-fab.mdc-fab--extended.icon-end .mdc-fab__icon[dir=rtl]{margin-left:-8px;margin-left:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px))}[dir=rtl] :host .mdc-fab.mdc-fab--extended.icon-end .mdc-fab__icon,:host .mdc-fab.mdc-fab--extended.icon-end .mdc-fab__icon[dir=rtl]{margin-right:12px;margin-right:var(--mdc-fab-extended-icon-padding, 12px)}:host .mdc-fab.mdc-fab--extended.icon-end ::slotted([slot=icon]){margin-right:-8px;margin-right:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px));margin-left:12px;margin-left:var(--mdc-fab-extended-icon-padding, 12px)}[dir=rtl] :host .mdc-fab.mdc-fab--extended.icon-end ::slotted([slot=icon]),:host .mdc-fab.mdc-fab--extended.icon-end ::slotted([slot=icon])[dir=rtl]{margin-left:-8px;margin-left:calc(var(--mdc-fab-extended-icon-padding, 12px) - var(--mdc-fab-extended-label-padding, 20px))}[dir=rtl] :host .mdc-fab.mdc-fab--extended.icon-end ::slotted([slot=icon]),:host .mdc-fab.mdc-fab--extended.icon-end ::slotted([slot=icon])[dir=rtl]{margin-right:12px;margin-right:var(--mdc-fab-extended-icon-padding, 12px)}:host .mdc-fab.mdc-fab--exited ::slotted([slot=icon]){transform:scale(0);transition:transform 135ms 0ms cubic-bezier(0.4, 0, 1, 1)}`;

  /** @soyCompatible */

  let Fab = class Fab extends FabBase {};
  Fab.styles = style$5;
  Fab = __decorate$4([customElement('mwc-fab')], Fab);

  function computeRTL(hass) {
    const lang = hass.language || "en";

    if (hass.translationMetadata.translations[lang]) {
      return hass.translationMetadata.translations[lang].isRTL || false;
    }

    return false;
  }
  function computeRTLDirection(hass) {
    return emitRTLDirection(computeRTL(hass));
  }
  function emitRTLDirection(rtl) {
    return rtl ? "rtl" : "ltr";
  }

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  class IronMeta {
    /**
     * @param {{
     *   type: (string|null|undefined),
     *   key: (string|null|undefined),
     *   value: *,
     * }=} options
     */
    constructor(options) {
      IronMeta[' '](options);
      /** @type {string} */

      this.type = options && options.type || 'default';
      /** @type {string|null|undefined} */

      this.key = options && options.key;

      if (options && 'value' in options) {
        /** @type {*} */
        this.value = options.value;
      }
    }
    /** @return {*} */


    get value() {
      var type = this.type;
      var key = this.key;

      if (type && key) {
        return IronMeta.types[type] && IronMeta.types[type][key];
      }
    }
    /** @param {*} value */


    set value(value) {
      var type = this.type;
      var key = this.key;

      if (type && key) {
        type = IronMeta.types[type] = IronMeta.types[type] || {};

        if (value == null) {
          delete type[key];
        } else {
          type[key] = value;
        }
      }
    }
    /** @return {!Array<*>} */


    get list() {
      var type = this.type;

      if (type) {
        var items = IronMeta.types[this.type];

        if (!items) {
          return [];
        }

        return Object.keys(items).map(function (key) {
          return metaDatas[this.type][key];
        }, this);
      }
    }
    /**
     * @param {string} key
     * @return {*}
     */


    byKey(key) {
      this.key = key;
      return this.value;
    }

  }
  // for instances that are not held anywhere. For example, when
  // `new IronMeta({...})` is used only for the side effect of adding a value.

  IronMeta[' '] = function () {};

  IronMeta.types = {};
  var metaDatas = IronMeta.types;
  /**
  `iron-meta` is a generic element you can use for sharing information across the
  DOM tree. It uses [monostate pattern](http://c2.com/cgi/wiki?MonostatePattern)
  such that any instance of iron-meta has access to the shared information. You
  can use `iron-meta` to share whatever you want (or create an extension [like
  x-meta] for enhancements).

  The `iron-meta` instances containing your actual data can be loaded in an
  import, or constructed in any way you see fit. The only requirement is that you
  create them before you try to access them.

  Examples:

  If I create an instance like this:

      <iron-meta key="info" value="foo/bar"></iron-meta>

  Note that value="foo/bar" is the metadata I've defined. I could define more
  attributes or use child nodes to define additional metadata.

  Now I can access that element (and it's metadata) from any iron-meta instance
  via the byKey method, e.g.

      meta.byKey('info');

  Pure imperative form would be like:

      document.createElement('iron-meta').byKey('info');

  Or, in a Polymer element, you can include a meta in your template:

      <iron-meta id="meta"></iron-meta>
      ...
      this.$.meta.byKey('info');

  @group Iron Elements
  @demo demo/index.html
  @element iron-meta
  */

  Polymer({
    is: 'iron-meta',
    properties: {
      /**
       * The type of meta-data.  All meta-data of the same type is stored
       * together.
       * @type {string}
       */
      type: {
        type: String,
        value: 'default'
      },

      /**
       * The key used to store `value` under the `type` namespace.
       * @type {?string}
       */
      key: {
        type: String
      },

      /**
       * The meta-data to store or retrieve.
       * @type {*}
       */
      value: {
        type: String,
        notify: true
      },

      /**
       * If true, `value` is set to the iron-meta instance itself.
       */
      self: {
        type: Boolean,
        observer: '_selfChanged'
      },
      __meta: {
        type: Boolean,
        computed: '__computeMeta(type, key, value)'
      }
    },
    hostAttributes: {
      hidden: true
    },
    __computeMeta: function (type, key, value) {
      var meta = new IronMeta({
        type: type,
        key: key
      });

      if (value !== undefined && value !== meta.value) {
        meta.value = value;
      } else if (this.value !== meta.value) {
        this.value = meta.value;
      }

      return meta;
    },

    get list() {
      return this.__meta && this.__meta.list;
    },

    _selfChanged: function (self) {
      if (self) {
        this.value = this;
      }
    },

    /**
     * Retrieves meta data value by key.
     *
     * @method byKey
     * @param {string} key The key of the meta-data to be returned.
     * @return {*}
     */
    byKey: function (key) {
      return new IronMeta({
        type: this.type,
        key: key
      }).value;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**

  The `iron-icon` element displays an icon. By default an icon renders as a 24px
  square.

  Example using src:

      <iron-icon src="star.png"></iron-icon>

  Example setting size to 32px x 32px:

      <iron-icon class="big" src="big_star.png"></iron-icon>

      <style is="custom-style">
        .big {
          --iron-icon-height: 32px;
          --iron-icon-width: 32px;
        }
      </style>

  The iron elements include several sets of icons. To use the default set of
  icons, import `iron-icons.js` and use the `icon` attribute to specify an icon:

      <script type="module">
        import "@polymer/iron-icons/iron-icons.js";
      </script>

      <iron-icon icon="menu"></iron-icon>

  To use a different built-in set of icons, import the specific
  `iron-icons/<iconset>-icons.js`, and specify the icon as `<iconset>:<icon>`.
  For example, to use a communication icon, you would use:

      <script type="module">
        import "@polymer/iron-icons/communication-icons.js";
      </script>

      <iron-icon icon="communication:email"></iron-icon>

  You can also create custom icon sets of bitmap or SVG icons.

  Example of using an icon named `cherry` from a custom iconset with the ID
  `fruit`:

      <iron-icon icon="fruit:cherry"></iron-icon>

  See `<iron-iconset>` and `<iron-iconset-svg>` for more information about how to
  create a custom iconset.

  See the `iron-icons` demo to see the icons available in the various iconsets.

  ### Styling

  The following custom properties are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--iron-icon` | Mixin applied to the icon | {}
  `--iron-icon-width` | Width of the icon | `24px`
  `--iron-icon-height` | Height of the icon | `24px`
  `--iron-icon-fill-color` | Fill color of the svg icon | `currentcolor`
  `--iron-icon-stroke-color` | Stroke color of the svg icon | none

  @group Iron Elements
  @element iron-icon
  @demo demo/index.html
  @hero hero.svg
  @homepage polymer.github.io
  */

  Polymer({
    _template: html$1`
    <style>
      :host {
        @apply --layout-inline;
        @apply --layout-center-center;
        position: relative;

        vertical-align: middle;

        fill: var(--iron-icon-fill-color, currentcolor);
        stroke: var(--iron-icon-stroke-color, none);

        width: var(--iron-icon-width, 24px);
        height: var(--iron-icon-height, 24px);
        @apply --iron-icon;
      }

      :host([hidden]) {
        display: none;
      }
    </style>
`,
    is: 'iron-icon',
    properties: {
      /**
       * The name of the icon to use. The name should be of the form:
       * `iconset_name:icon_name`.
       */
      icon: {
        type: String
      },

      /**
       * The name of the theme to used, if one is specified by the
       * iconset.
       */
      theme: {
        type: String
      },

      /**
       * If using iron-icon without an iconset, you can set the src to be
       * the URL of an individual icon image file. Note that this will take
       * precedence over a given icon attribute.
       */
      src: {
        type: String
      },

      /**
       * @type {!IronMeta}
       */
      _meta: {
        value: Base.create('iron-meta', {
          type: 'iconset'
        })
      }
    },
    observers: ['_updateIcon(_meta, isAttached)', '_updateIcon(theme, isAttached)', '_srcChanged(src, isAttached)', '_iconChanged(icon, isAttached)'],
    _DEFAULT_ICONSET: 'icons',
    _iconChanged: function (icon) {
      var parts = (icon || '').split(':');
      this._iconName = parts.pop();
      this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;

      this._updateIcon();
    },
    _srcChanged: function (src) {
      this._updateIcon();
    },
    _usesIconset: function () {
      return this.icon || !this.src;
    },

    /** @suppress {visibility} */
    _updateIcon: function () {
      if (this._usesIconset()) {
        if (this._img && this._img.parentNode) {
          dom(this.root).removeChild(this._img);
        }

        if (this._iconName === '') {
          if (this._iconset) {
            this._iconset.removeIcon(this);
          }
        } else if (this._iconsetName && this._meta) {
          this._iconset =
          /** @type {?Polymer.Iconset} */
          this._meta.byKey(this._iconsetName);

          if (this._iconset) {
            this._iconset.applyIcon(this, this._iconName, this.theme);

            this.unlisten(window, 'iron-iconset-added', '_updateIcon');
          } else {
            this.listen(window, 'iron-iconset-added', '_updateIcon');
          }
        }
      } else {
        if (this._iconset) {
          this._iconset.removeIcon(this);
        }

        if (!this._img) {
          this._img = document.createElement('img');
          this._img.style.width = '100%';
          this._img.style.height = '100%';
          this._img.draggable = false;
        }

        this._img.src = this.src;
        dom(this.root).appendChild(this._img);
      }
    }
  });

  const customIconsetsWindow = window;

  if (!("customIconsets" in customIconsetsWindow)) {
    customIconsetsWindow.customIconsets = {};
  }

  const customIconsets = customIconsetsWindow.customIconsets;

  const version$2="0";const parts$1=[];var iconMetadata = {version:version$2,parts:parts$1};

  const iconMetadata$1 = iconMetadata;

  class Store {
      constructor(dbName = 'keyval-store', storeName = 'keyval') {
          this.storeName = storeName;
          this._dbp = new Promise((resolve, reject) => {
              const openreq = indexedDB.open(dbName, 1);
              openreq.onerror = () => reject(openreq.error);
              openreq.onsuccess = () => resolve(openreq.result);
              // First time setup: create an empty object store
              openreq.onupgradeneeded = () => {
                  openreq.result.createObjectStore(storeName);
              };
          });
      }
      _withIDBStore(type, callback) {
          return this._dbp.then(db => new Promise((resolve, reject) => {
              const transaction = db.transaction(this.storeName, type);
              transaction.oncomplete = () => resolve();
              transaction.onabort = transaction.onerror = () => reject(transaction.error);
              callback(transaction.objectStore(this.storeName));
          }));
      }
  }
  let store;
  function getDefaultStore() {
      if (!store)
          store = new Store();
      return store;
  }
  function get$1(key, store = getDefaultStore()) {
      let req;
      return store._withIDBStore('readonly', store => {
          req = store.get(key);
      }).then(() => req.result);
  }
  function set$1(key, value, store = getDefaultStore()) {
      return store._withIDBStore('readwrite', store => {
          store.put(value, key);
      });
  }
  function clear(store = getDefaultStore()) {
      return store._withIDBStore('readwrite', store => {
          store.clear();
      });
  }

  const iconStore = new Store("hass-icon-db", "mdi-icon-store");
  const MDI_PREFIXES = ["mdi", "hass", "hassio", "hademo"];
  let toRead = []; // Queue up as many icon fetches in 1 transaction

  const getIcon = iconName => new Promise((resolve, reject) => {
    toRead.push([iconName, resolve, reject]);

    if (toRead.length > 1) {
      return;
    }

    const results = [];

    iconStore._withIDBStore("readonly", store => {
      for (const [iconName_, resolve_] of toRead) {
        results.push([resolve_, store.get(iconName_)]);
      }

      toRead = [];
    }).then(() => {
      for (const [resolve_, request] of results) {
        resolve_(request.result);
      }
    }).catch(() => {
      // Firefox in private mode doesn't support IDB
      for (const [,, reject_] of toRead) {
        reject_();
      }

      toRead = [];
    });
  });
  const findIconChunk = icon => {
    let lastChunk;

    for (const chunk of iconMetadata$1.parts) {
      if (chunk.start !== undefined && icon < chunk.start) {
        break;
      }

      lastChunk = chunk;
    }

    return lastChunk.file;
  };
  const writeCache = async chunks => {
    const keys = Object.keys(chunks);
    const iconsSets = await Promise.all(Object.values(chunks)); // We do a batch opening the store just once, for (considerable) performance

    iconStore._withIDBStore("readwrite", store => {
      iconsSets.forEach((icons, idx) => {
        Object.entries(icons).forEach(([name, path]) => {
          store.put(path, name);
        });
        delete chunks[keys[idx]];
      });
    });
  };
  const checkCacheVersion = () => {
    get$1("_version", iconStore).then(version => {
      if (!version) {
        set$1("_version", iconMetadata$1.version, iconStore);
      } else if (version !== iconMetadata$1.version) {
        clear(iconStore).then(() => set$1("_version", iconMetadata$1.version, iconStore));
      }
    });
  };

  // From: https://davidwalsh.name/javascript-debounce-function
  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds. If `immediate` is passed, trigger the function on the
  // leading edge, instead of the trailing.
  // eslint-disable-next-line: ban-types
  const debounce = (func, wait, immediate = false) => {
    let timeout; // @ts-ignore

    return function (...args) {
      // @ts-ignore
      // eslint-disable-next-line @typescript-eslint/no-this-alias
      const context = this;

      const later = () => {
        timeout = null;

        if (!immediate) {
          func.apply(context, args);
        }
      };

      const callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);

      if (callNow) {
        func.apply(context, args);
      }
    };
  };

  const mdiRenameMapping = {
    "account-badge": "badge-account",
    "account-badge-alert": "badge-account-alert",
    "account-badge-alert-outline": "badge-account-alert-outline",
    "account-badge-horizontal": "badge-account-horizontal",
    "account-badge-horizontal-outline": "badge-account-horizontal-outline",
    "account-badge-outline": "badge-account-outline",
    "account-card-details": "card-account-details",
    "account-card-details-outline": "card-account-details-outline",
    airplay: "apple-airplay",
    artist: "account-music",
    "artist-outline": "account-music-outline",
    audiobook: "book-music",
    azure: "microsoft-azure",
    "azure-devops": "microsoft-azure-devops",
    bible: "book-cross",
    bowl: "bowl-mix",
    "calendar-repeat": "calendar-sync",
    "calendar-repeat-outline": "calendar-sync-outline",
    "camcorder-box": "video-box",
    "camcorder-box-off": "video-box-off",
    "cellphone-settings-variant": "cellphone-cog",
    "chart-snakey": "chart-sankey",
    "chart-snakey-variant": "chart-sankey-variant",
    coin: "currency-usd-circle",
    "coin-outline": "currency-usd-circle-outline",
    "coins-outline": "circle-multiple-outline",
    "contact-mail": "card-account-mail",
    "contact-mail-outline": "card-account-mail-outline",
    "contact-phone": "card-account-phone",
    "contact-phone-outline": "card-account-phone-outline",
    cowboy: "account-cowboy-hat",
    "database-refresh": "database-sync",
    dictionary: "book-alphabet",
    edge: "microsoft-edge",
    "edge-legacy": "microsoft-edge-legacy",
    "file-document-box": "text-box",
    "file-document-box-check-outline": "text-box-check-outline",
    "file-document-box-minus": "text-box-minus",
    "file-document-box-minus-outline": "text-box-minus-outline",
    "file-document-box-multiple": "text-box-multiple",
    "file-document-box-multiple-outline": "text-box-multiple-outline",
    "file-document-box-outline": "text-box-outline",
    "file-document-box-plus": "text-box-plus",
    "file-document-box-plus-outline": "text-box-plus-outline",
    "file-document-box-remove": "text-box-remove",
    "file-document-box-remove-outline": "text-box-remove-outline",
    "file-document-box-search": "text-box-search",
    "file-document-box-search-outline": "text-box-search-outline",
    "file-settings-variant": "file-cog",
    "file-settings-variant-outline": "file-cog-outline",
    "folder-settings-variant": "folder-cog",
    "folder-settings-variant-outline": "folder-cog-outline",
    "github-circle": "github",
    "google-adwords": "google-ads",
    hackernews: "y-combinator",
    hotel: "bed",
    "image-filter": "image-multiple-outline",
    "internet-explorer": "microsoft-internet-explorer",
    json: "code-json",
    kotlin: "language-kotlin",
    "library-books": "filmstrip-box",
    "library-movie": "filmstrip-box-multiple",
    "library-music": "music-box-multiple",
    "library-music-outline": "music-box-multiple-outline",
    "library-video": "play-box-multiple",
    markdown: "language-markdown",
    "markdown-outline": "language-markdown-outline",
    "message-settings-variant": "message-cog",
    "message-settings-variant-outline": "message-cog-outline",
    "microsoft-dynamics": "microsoft-dynamics-365",
    "network-router": "router-network",
    office: "microsoft-office",
    onedrive: "microsoft-onedrive",
    onenote: "microsoft-onenote",
    outlook: "microsoft-outlook",
    playstation: "sony-playstation",
    "periodic-table-co": "molecule-co",
    "periodic-table-co2": "molecule-co2",
    pot: "pot-steam",
    ruby: "language-ruby",
    sailing: "sail-boat",
    scooter: "human-scooter",
    settings: "cog",
    "settings-box": "cog-box",
    "settings-outline": "cog-outline",
    "settings-transfer": "cog-transfer",
    "settings-transfer-outline": "cog-transfer-outline",
    "shield-refresh": "shield-sync",
    "shield-refresh-outline": "shield-sync-outline",
    "sort-alphabetical": "sort-alphabetical-variant",
    "sort-alphabetical-ascending": "sort-alphabetical-ascending-variant",
    "sort-alphabetical-descending": "sort-alphabetical-descending-variant",
    "sort-numeric": "sort-numeric-variant",
    "star-half": "star-half-full",
    storefront: "storefront-outline",
    timer: "timer-outline",
    "timer-off": "timer-off-outline",
    towing: "tow-truck",
    voice: "account-voice",
    "wall-sconce-variant": "wall-sconce-round-variant",
    wii: "nintendo-wii",
    wiiu: "nintendo-wiiu",
    windows: "microsoft-windows",
    "windows-classic": "microsoft-windows-classic",
    worker: "account-hard-hat",
    xbox: "microsoft-xbox",
    "xbox-controller": "microsoft-xbox-controller",
    "xbox-controller-battery-alert": "microsoft-xbox-controller-battery-alert",
    "xbox-controller-battery-charging": "microsoft-xbox-controller-battery-charging",
    "xbox-controller-battery-empty": "microsoft-xbox-controller-battery-empty",
    "xbox-controller-battery-full": "microsoft-xbox-controller-battery-full",
    "xbox-controller-battery-low": "microsoft-xbox-controller-battery-low",
    "xbox-controller-battery-medium": "microsoft-xbox-controller-battery-medium",
    "xbox-controller-battery-unknown": "microsoft-xbox-controller-battery-unknown",
    "xbox-controller-menu": "microsoft-xbox-controller-menu",
    "xbox-controller-off": "microsoft-xbox-controller-off",
    "xbox-controller-view": "microsoft-xbox-controller-view",
    yammer: "microsoft-yammer",
    "youtube-creator-studio": "youtube-studio",
    "selection-mutliple": "selection-multiple",
    textarea: "form-textarea",
    textbox: "form-textbox",
    "textbox-lock": "form-textbox-lock",
    "textbox-password": "form-textbox-password",
    "syllabary-katakana-half-width": "syllabary-katakana-halfwidth",
    "visual-studio-code": "microsoft-visual-studio-code",
    "visual-studio": "microsoft-visual-studio"
  };
  const mdiRemovedIcons = new Set(["accusoft", "amazon-drive", "android-head", "basecamp", "beats", "behance", "blackberry", "cisco-webex", "disqus-outline", "dribbble", "dribbble-box", "etsy", "eventbrite", "facebook-box", "flattr", "flickr", "foursquare", "github-box", "github-face", "glassdoor", "google-adwords", "google-pages", "google-physical-web", "google-plus-box", "houzz", "houzz-box", "instapaper", "itunes", "language-python-text", "lastfm", "linkedin-box", "lyft", "mail-ru", "mastodon-variant", "medium", "meetup", "mixcloud", "mixer", "nfc-off", "npm-variant", "npm-variant-outline", "paypal", "periscope", "pinterest-box", "pocket", "quicktime", "shopify", "slackware", "square-inc", "square-inc-cash", "steam-box", "strava", "tor", "tumblr", "tumblr-box", "tumblr-reblog", "twitter-box", "twitter-circle", "uber", "venmo", "vk-box", "vk-circle", "wunderlist", "xda", "xing-box", "xing-circle", "yelp"]);
  const chunks = {};
  checkCacheVersion();
  const debouncedWriteCache = debounce(() => writeCache(chunks), 2000);
  const cachedIcons = {};
  let HaIcon = _decorate([customElement("ha-icon")], function (_initialize, _LitElement) {
    class HaIcon extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaIcon,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "icon",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_path",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_viewBox",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_legacy",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "updated",
        value: function updated(changedProps) {
          if (changedProps.has("icon")) {
            this._path = undefined;
            this._viewBox = undefined;

            this._loadIcon();
          }
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.icon) {
            return html``;
          }

          if (this._legacy) {
            return html`<iron-icon .icon=${this.icon}></iron-icon>`;
          }

          return html`<ha-svg-icon
      .path=${this._path}
      .viewBox=${this._viewBox}
    ></ha-svg-icon>`;
        }
      }, {
        kind: "method",
        key: "_loadIcon",
        value: async function _loadIcon() {
          if (!this.icon) {
            return;
          }

          const [iconPrefix, origIconName] = this.icon.split(":", 2);
          let iconName = origIconName;

          if (!iconPrefix || !iconName) {
            return;
          }

          if (!MDI_PREFIXES.includes(iconPrefix)) {
            if (iconPrefix in customIconsets) {
              const customIconset = customIconsets[iconPrefix];

              if (customIconset) {
                this._setCustomPath(customIconset(iconName));
              }

              return;
            }

            this._legacy = true;
            return;
          }

          this._legacy = false;

          if (iconName in mdiRenameMapping) {
            iconName = mdiRenameMapping[iconName];
            const message = `Icon ${iconPrefix}:${origIconName} was renamed to ${iconPrefix}:${iconName}, please change your config, it will be removed in version 0.115.`; // eslint-disable-next-line no-console

            console.warn(message);
            fireEvent(this, "write_log", {
              level: "warning",
              message
            });
          } else if (mdiRemovedIcons.has(iconName)) {
            const message = `Icon ${this.icon} was removed from MDI, please replace this icon with an other icon in your config, it will be removed in version 0.115.`; // eslint-disable-next-line no-console

            console.warn(message);
            fireEvent(this, "write_log", {
              level: "warning",
              message
            });
          }

          if (iconName in cachedIcons) {
            this._path = cachedIcons[iconName];
            return;
          }

          let databaseIcon;

          try {
            databaseIcon = await getIcon(iconName);
          } catch (_err) {
            // Firefox in private mode doesn't support IDB
            databaseIcon = undefined;
          }

          if (databaseIcon) {
            this._path = databaseIcon;
            cachedIcons[iconName] = databaseIcon;
            return;
          }

          const chunk = findIconChunk(iconName);

          if (chunk in chunks) {
            this._setPath(chunks[chunk], iconName);

            return;
          }

          const iconPromise = fetch(`/static/mdi/${chunk}.json`).then(response => response.json());
          chunks[chunk] = iconPromise;

          this._setPath(iconPromise, iconName);

          debouncedWriteCache();
        }
      }, {
        kind: "method",
        key: "_setCustomPath",
        value: async function _setCustomPath(promise) {
          const icon = await promise;
          this._path = icon.path;
          this._viewBox = icon.viewBox;
        }
      }, {
        kind: "method",
        key: "_setPath",
        value: async function _setPath(promise, iconName) {
          const iconPack = await promise;
          this._path = iconPack[iconName];
          cachedIcons[iconName] = iconPack[iconName];
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        fill: currentcolor;
      }
    `;
        }
      }]
    };
  }, LitElement);

  /**
   * @license
   * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
   * This code may only be used under the BSD style license found at
   * http://polymer.github.io/LICENSE.txt
   * The complete set of authors may be found at
   * http://polymer.github.io/AUTHORS.txt
   * The complete set of contributors may be found at
   * http://polymer.github.io/CONTRIBUTORS.txt
   * Code distributed by Google as part of the polymer project is also
   * subject to an additional IP rights grant found at
   * http://polymer.github.io/PATENTS.txt
   */
  const previousValues = new WeakMap();
  /**
   * For AttributeParts, sets the attribute if the value is defined and removes
   * the attribute if the value is undefined.
   *
   * For other part types, this directive is a no-op.
   */

  const ifDefined = directive(value => part => {
    const previousValue = previousValues.get(part);

    if (value === undefined && part instanceof AttributePart) {
      // If the value is undefined, remove the attribute, but only if the value
      // was previously defined.
      if (previousValue !== undefined || !previousValues.has(part)) {
        const name = part.committer.name;
        part.committer.element.removeAttribute(name);
      }
    } else if (value !== previousValue) {
      part.setValue(value);
    }

    previousValues.set(part, value);
  });

  let HaTab = _decorate([customElement("ha-tab")], function (_initialize, _LitElement) {
    class HaTab extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaTab,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "active",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "narrow",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "name",
        value: void 0
      }, {
        kind: "field",
        decorators: [queryAsync("mwc-ripple")],
        key: "_ripple",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_shouldRenderRipple",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <div
        tabindex="0"
        role="tab"
        aria-selected=${this.active}
        aria-label=${ifDefined(this.name)}
        @focus=${this.handleRippleFocus}
        @blur=${this.handleRippleBlur}
        @mousedown=${this.handleRippleActivate}
        @mouseup=${this.handleRippleDeactivate}
        @mouseenter=${this.handleRippleMouseEnter}
        @mouseleave=${this.handleRippleMouseLeave}
        @touchstart=${this.handleRippleActivate}
        @touchend=${this.handleRippleDeactivate}
        @touchcancel=${this.handleRippleDeactivate}
        @keydown=${this._handleKeyDown}
      >
        ${this.narrow ? html`<slot name="icon"></slot>` : ""}
        ${!this.narrow || this.active ? html`<span class="name">${this.name}</span>` : ""}
        ${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
      </div>
    `;
        }
      }, {
        kind: "field",
        key: "_rippleHandlers",

        value() {
          return new RippleHandlers(() => {
            this._shouldRenderRipple = true;
            return this._ripple;
          });
        }

      }, {
        kind: "method",
        key: "_handleKeyDown",
        value: function _handleKeyDown(ev) {
          if (ev.keyCode === 13) {
            ev.target.click();
          }
        }
      }, {
        kind: "method",
        decorators: [eventOptions({
          passive: true
        })],
        key: "handleRippleActivate",
        value: function handleRippleActivate(evt) {
          this._rippleHandlers.startPress(evt);
        }
      }, {
        kind: "method",
        key: "handleRippleDeactivate",
        value: function handleRippleDeactivate() {
          this._rippleHandlers.endPress();
        }
      }, {
        kind: "method",
        key: "handleRippleMouseEnter",
        value: function handleRippleMouseEnter() {
          this._rippleHandlers.startHover();
        }
      }, {
        kind: "method",
        key: "handleRippleMouseLeave",
        value: function handleRippleMouseLeave() {
          this._rippleHandlers.endHover();
        }
      }, {
        kind: "method",
        key: "handleRippleFocus",
        value: function handleRippleFocus() {
          this._rippleHandlers.startFocus();
        }
      }, {
        kind: "method",
        key: "handleRippleBlur",
        value: function handleRippleBlur() {
          this._rippleHandlers.endFocus();
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      div {
        padding: 0 32px;
        display: flex;
        flex-direction: column;
        text-align: center;
        align-items: center;
        justify-content: center;
        height: 64px;
        cursor: pointer;
        position: relative;
        outline: none;
        box-sizing: border-box;
      }

      .name {
        white-space: nowrap;
      }

      :host([active]) {
        color: var(--primary-color);
      }

      :host(:not([narrow])[active]) div {
        border-bottom: 2px solid var(--primary-color);
      }

      :host([narrow]) {
        padding: 0 16px;
        width: 20%;
        min-width: 0;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HassTabsSubpage = _decorate([customElement("hass-tabs-subpage")], function (_initialize, _LitElement) {
    class HassTabsSubpage extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HassTabsSubpage,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "hassio",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: String,
          attribute: "back-path"
        })],
        key: "backPath",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "backCallback",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          attribute: "main-page"
        })],
        key: "mainPage",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "tabs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "narrow",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true,
          attribute: "is-wide"
        })],
        key: "isWide",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "rtl",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_activeTab",
        value: void 0
      }, {
        kind: "field",
        decorators: [restoreScroll(".content")],
        key: "_savedScrollPos",
        value: void 0
      }, {
        kind: "field",
        key: "_getTabs",

        value() {
          return memoizeOne((tabs, activeTab, showAdvanced, _components, _language, _narrow) => {
            const shownTabs = tabs.filter(page => (!page.component || page.core || isComponentLoaded(this.hass, page.component)) && (!page.advancedOnly || showAdvanced));
            return shownTabs.map(page => html`
            <ha-tab
              .hass=${this.hass}
              @click=${this._tabTapped}
              .path=${page.path}
              .active=${page === activeTab}
              .narrow=${this.narrow}
              .name=${page.translationKey ? this.hass.localize(page.translationKey) : page.name}
            >
              ${page.iconPath ? html`<ha-svg-icon
                    slot="icon"
                    .path=${page.iconPath}
                  ></ha-svg-icon>` : html`<ha-icon slot="icon" .icon=${page.icon}></ha-icon>`}
            </ha-tab>
          `);
          });
        }

      }, {
        kind: "method",
        key: "updated",
        value: // @ts-ignore
        function updated(changedProperties) {
          _get(_getPrototypeOf(HassTabsSubpage.prototype), "updated", this).call(this, changedProperties);

          if (changedProperties.has("route")) {
            this._activeTab = this.tabs.find(tab => `${this.route.prefix}${this.route.path}`.includes(tab.path));
          }

          if (changedProperties.has("hass")) {
            const oldHass = changedProperties.get("hass");

            if (!oldHass || oldHass.language !== this.hass.language) {
              this.rtl = computeRTL(this.hass);
            }
          }
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$hass$userData;

          const tabs = this._getTabs(this.tabs, this._activeTab, (_this$hass$userData = this.hass.userData) === null || _this$hass$userData === void 0 ? void 0 : _this$hass$userData.showAdvanced, this.hass.config.components, this.hass.language, this.narrow);

          const showTabs = tabs.length > 1 || !this.narrow;
          return html`
      <div class="toolbar">
        ${this.mainPage ? html`
              <ha-menu-button
                .hassio=${this.hassio}
                .hass=${this.hass}
                .narrow=${this.narrow}
              ></ha-menu-button>
            ` : html`
              <ha-icon-button-arrow-prev
                aria-label="Back"
                @click=${this._backTapped}
              ></ha-icon-button-arrow-prev>
            `}
        ${this.narrow ? html` <div class="main-title"><slot name="header"></slot></div> ` : ""}
        ${showTabs ? html`
              <div id="tabbar" class=${classMap({
          "bottom-bar": this.narrow
        })}>
                ${tabs}
              </div>
            ` : ""}
        <div id="toolbar-icon">
          <slot name="toolbar-icon"></slot>
        </div>
      </div>
      <div
        class="content ${classMap({
          tabs: showTabs
        })}"
        @scroll=${this._saveScrollPos}
      >
        <slot></slot>
      </div>
      <div id="fab" class="${classMap({
          tabs: showTabs
        })}">
        <slot name="fab"></slot>
      </div>
    `;
        }
      }, {
        kind: "method",
        decorators: [eventOptions({
          passive: true
        })],
        key: "_saveScrollPos",
        value: function _saveScrollPos(e) {
          this._savedScrollPos = e.target.scrollTop;
        }
      }, {
        kind: "method",
        key: "_tabTapped",
        value: function _tabTapped(ev) {
          navigate(this, ev.currentTarget.path, true);
        }
      }, {
        kind: "method",
        key: "_backTapped",
        value: function _backTapped() {
          if (this.backPath) {
            navigate(this, this.backPath);
            return;
          }

          if (this.backCallback) {
            this.backCallback();
            return;
          }

          history.back();
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: block;
        height: 100%;
        background-color: var(--primary-background-color);
      }

      :host([narrow]) {
        width: 100%;
        position: fixed;
      }

      ha-menu-button {
        margin-right: 24px;
      }

      .toolbar {
        display: flex;
        align-items: center;
        font-size: 20px;
        height: 65px;
        background-color: var(--sidebar-background-color);
        font-weight: 400;
        color: var(--sidebar-text-color);
        border-bottom: 1px solid var(--divider-color);
        padding: 0 16px;
        box-sizing: border-box;
      }

      #tabbar {
        display: flex;
        font-size: 14px;
      }

      #tabbar.bottom-bar {
        position: absolute;
        bottom: 0;
        left: 0;
        padding: 0 16px;
        box-sizing: border-box;
        background-color: var(--sidebar-background-color);
        border-top: 1px solid var(--divider-color);
        justify-content: space-between;
        z-index: 2;
        font-size: 12px;
        width: 100%;
        padding-bottom: env(safe-area-inset-bottom);
      }

      #tabbar:not(.bottom-bar) {
        flex: 1;
        justify-content: center;
      }

      :host(:not([narrow])) #toolbar-icon {
        min-width: 40px;
      }

      ha-menu-button,
      ha-icon-button-arrow-prev,
      ::slotted([slot="toolbar-icon"]) {
        flex-shrink: 0;
        pointer-events: auto;
        color: var(--sidebar-icon-color);
      }

      .main-title {
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        max-height: 58px;
        line-height: 20px;
      }

      .content {
        position: relative;
        width: calc(
          100% - env(safe-area-inset-left) - env(safe-area-inset-right)
        );
        margin-left: env(safe-area-inset-left);
        margin-right: env(safe-area-inset-right);
        height: calc(100% - 65px);
        height: calc(100% - 65px - env(safe-area-inset-bottom));
        overflow-y: auto;
        overflow: auto;
        -webkit-overflow-scrolling: touch;
      }

      :host([narrow]) .content.tabs {
        height: calc(100% - 128px);
        height: calc(100% - 128px - env(safe-area-inset-bottom));
      }

      #fab {
        position: fixed;
        right: calc(16px + env(safe-area-inset-right));
        bottom: calc(16px + env(safe-area-inset-bottom));
        z-index: 1;
      }
      :host([narrow]) #fab.tabs {
        bottom: calc(84px + env(safe-area-inset-bottom));
      }
      #fab[is-wide] {
        bottom: 24px;
        right: 24px;
      }
      :host([rtl]) #fab {
        right: auto;
        left: calc(16px + env(safe-area-inset-left));
      }
      :host([rtl][is-wide]) #fab {
        bottom: 24px;
        left: 24px;
        right: auto;
      }
    `;
        }
      }]
    };
  }, LitElement);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  `iron-a11y-announcer` is a singleton element that is intended to add a11y
  to features that require on-demand announcement from screen readers. In
  order to make use of the announcer, it is best to request its availability
  in the announcing element.

  Example:

      Polymer({

        is: 'x-chatty',

        attached: function() {
          // This will create the singleton element if it has not
          // been created yet:
          Polymer.IronA11yAnnouncer.requestAvailability();
        }
      });

  After the `iron-a11y-announcer` has been made available, elements can
  make announces by firing bubbling `iron-announce` events.

  Example:

      this.fire('iron-announce', {
        text: 'This is an announcement!'
      }, { bubbles: true });

  Note: announcements are only audible if you have a screen reader enabled.

  @demo demo/index.html
  */

  const IronA11yAnnouncer = Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: inline-block;
        position: fixed;
        clip: rect(0px,0px,0px,0px);
      }
    </style>
    <div aria-live$="[[mode]]">[[_text]]</div>
`,
    is: 'iron-a11y-announcer',
    properties: {
      /**
       * The value of mode is used to set the `aria-live` attribute
       * for the element that will be announced. Valid values are: `off`,
       * `polite` and `assertive`.
       */
      mode: {
        type: String,
        value: 'polite'
      },

      /**
       * The timeout on refreshing the announcement text. Larger timeouts are
       * needed for certain screen readers to re-announce the same message.
       */
      timeout: {
        type: Number,
        value: 150
      },
      _text: {
        type: String,
        value: ''
      }
    },

    /** @override */
    created: function () {
      if (!IronA11yAnnouncer.instance) {
        IronA11yAnnouncer.instance = this;
      }

      document.addEventListener('iron-announce', this._onIronAnnounce.bind(this));
    },

    /**
     * Cause a text string to be announced by screen readers.
     *
     * @param {string} text The text that should be announced.
     */
    announce: function (text) {
      this._text = '';
      this.async(function () {
        this._text = text;
      }, this.timeout);
    },
    _onIronAnnounce: function (event) {
      if (event.detail && event.detail.text) {
        this.announce(event.detail.text);
      }
    }
  });
  IronA11yAnnouncer.instance = null;

  IronA11yAnnouncer.requestAvailability = function () {
    if (!IronA11yAnnouncer.instance) {
      IronA11yAnnouncer.instance = document.createElement('iron-a11y-announcer');
    }

    if (document.body) {
      document.body.appendChild(IronA11yAnnouncer.instance);
    } else {
      document.addEventListener('load', function () {
        document.body.appendChild(IronA11yAnnouncer.instance);
      });
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Singleton IronMeta instance.
   */

  let IronValidatableBehaviorMeta = null;
  /**
   * `Use IronValidatableBehavior` to implement an element that validates
   * user input. Use the related `IronValidatorBehavior` to add custom
   * validation logic to an iron-input.
   *
   * By default, an `<iron-form>` element validates its fields when the user
   * presses the submit button. To validate a form imperatively, call the form's
   * `validate()` method, which in turn will call `validate()` on all its
   * children. By using `IronValidatableBehavior`, your custom element
   * will get a public `validate()`, which will return the validity of the
   * element, and a corresponding `invalid` attribute, which can be used for
   * styling.
   *
   * To implement the custom validation logic of your element, you must override
   * the protected `_getValidity()` method of this behaviour, rather than
   * `validate()`. See
   * [this](https://github.com/PolymerElements/iron-form/blob/master/demo/simple-element.html)
   * for an example.
   *
   * ### Accessibility
   *
   * Changing the `invalid` property, either manually or by calling `validate()`
   * will update the `aria-invalid` attribute.
   *
   * @demo demo/index.html
   * @polymerBehavior
   */

  const IronValidatableBehavior = {
    properties: {
      /**
       * Name of the validator to use.
       */
      validator: {
        type: String
      },

      /**
       * True if the last call to `validate` is invalid.
       */
      invalid: {
        notify: true,
        reflectToAttribute: true,
        type: Boolean,
        value: false,
        observer: '_invalidChanged'
      }
    },
    registered: function () {
      IronValidatableBehaviorMeta = new IronMeta({
        type: 'validator'
      });
    },
    _invalidChanged: function () {
      if (this.invalid) {
        this.setAttribute('aria-invalid', 'true');
      } else {
        this.removeAttribute('aria-invalid');
      }
    },

    /* Recompute this every time it's needed, because we don't know if the
     * underlying IronValidatableBehaviorMeta has changed. */
    get _validator() {
      return IronValidatableBehaviorMeta && IronValidatableBehaviorMeta.byKey(this.validator);
    },

    /**
     * @return {boolean} True if the validator `validator` exists.
     */
    hasValidator: function () {
      return this._validator != null;
    },

    /**
     * Returns true if the `value` is valid, and updates `invalid`. If you want
     * your element to have custom validation logic, do not override this method;
     * override `_getValidity(value)` instead.
      * @param {Object} value Deprecated: The value to be validated. By default,
     * it is passed to the validator's `validate()` function, if a validator is
     set.
     * If this argument is not specified, then the element's `value` property
     * is used, if it exists.
     * @return {boolean} True if `value` is valid.
     */
    validate: function (value) {
      // If this is an element that also has a value property, and there was
      // no explicit value argument passed, use the element's property instead.
      if (value === undefined && this.value !== undefined) this.invalid = !this._getValidity(this.value);else this.invalid = !this._getValidity(value);
      return !this.invalid;
    },

    /**
     * Returns true if `value` is valid.  By default, it is passed
     * to the validator's `validate()` function, if a validator is set. You
     * should override this method if you want to implement custom validity
     * logic for your element.
     *
     * @param {Object} value The value to be validated.
     * @return {boolean} True if `value` is valid.
     */
    _getValidity: function (value) {
      if (this.hasValidator()) {
        return this._validator.validate(value);
      }

      return true;
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  `<iron-input>` is a wrapper to a native `<input>` element, that adds two-way
  binding and prevention of invalid input. To use it, you must distribute a native
  `<input>` yourself. You can continue to use the native `input` as you would
  normally:

      <iron-input>
        <input>
      </iron-input>

      <iron-input>
        <input type="email" disabled>
      </iron-input>

  ### Two-way binding

  By default you can only get notified of changes to a native `<input>`'s `value`
  due to user input:

      <input value="{{myValue::input}}">

  This means that if you imperatively set the value (i.e. `someNativeInput.value =
  'foo'`), no events will be fired and this change cannot be observed.

  `iron-input` adds the `bind-value` property that mirrors the native `input`'s
  '`value` property; this property can be used for two-way data binding.
  `bind-value` will notify if it is changed either by user input or by script.

      <iron-input bind-value="{{myValue}}">
        <input>
      </iron-input>

  Note: this means that if you want to imperatively set the native `input`'s, you
  _must_ set `bind-value` instead, so that the wrapper `iron-input` can be
  notified.

  ### Validation

  `iron-input` uses the native `input`'s validation. For simplicity, `iron-input`
  has a `validate()` method (which internally just checks the distributed
  `input`'s validity), which sets an `invalid` attribute that can also be used for
  styling.

  To validate automatically as you type, you can use the `auto-validate`
  attribute.

  `iron-input` also fires an `iron-input-validate` event after `validate()` is
  called. You can use it to implement a custom validator:

      var CatsOnlyValidator = {
        validate: function(ironInput) {
          var valid = !ironInput.bindValue || ironInput.bindValue === 'cat';
          ironInput.invalid = !valid;
          return valid;
        }
      }
      ironInput.addEventListener('iron-input-validate', function() {
        CatsOnly.validate(input2);
      });

  You can also use an element implementing an
  [`IronValidatorBehavior`](/element/PolymerElements/iron-validatable-behavior).
  This example can also be found in the demo for this element:

      <iron-input validator="cats-only">
        <input>
      </iron-input>

  ### Preventing invalid input

  It may be desirable to only allow users to enter certain characters. You can use
  the `allowed-pattern` attribute to accomplish this. This feature is separate
  from validation, and `allowed-pattern` does not affect how the input is
  validated.

      // Only allow typing digits, but a valid input has exactly 5 digits.
      <iron-input allowed-pattern="[0-9]">
        <input pattern="\d{5}">
      </iron-input>

  @demo demo/index.html
  */

  Polymer({
    _template: html$1`
    <style>
      :host {
        display: inline-block;
      }
    </style>
    <slot id="content"></slot>
`,
    is: 'iron-input',
    behaviors: [IronValidatableBehavior],

    /**
     * Fired whenever `validate()` is called.
     *
     * @event iron-input-validate
     */
    properties: {
      /**
       * Use this property instead of `value` for two-way data binding, or to
       * set a default value for the input. **Do not** use the distributed
       * input's `value` property to set a default value.
       */
      bindValue: {
        type: String,
        value: ''
      },

      /**
       * Computed property that echoes `bindValue` (mostly used for Polymer 1.0
       * backcompatibility, if you were one-way binding to the Polymer 1.0
       * `input is="iron-input"` value attribute).
       */
      value: {
        type: String,
        computed: '_computeValue(bindValue)'
      },

      /**
       * Regex-like list of characters allowed as input; all characters not in the
       * list will be rejected. The recommended format should be a list of allowed
       * characters, for example, `[a-zA-Z0-9.+-!;:]`.
       *
       * This pattern represents the allowed characters for the field; as the user
       * inputs text, each individual character will be checked against the
       * pattern (rather than checking the entire value as a whole). If a
       * character is not a match, it will be rejected.
       *
       * Pasted input will have each character checked individually; if any
       * character doesn't match `allowedPattern`, the entire pasted string will
       * be rejected.
       *
       * Note: if you were using `iron-input` in 1.0, you were also required to
       * set `prevent-invalid-input`. This is no longer needed as of Polymer 2.0,
       * and will be set automatically for you if an `allowedPattern` is provided.
       *
       */
      allowedPattern: {
        type: String
      },

      /**
       * Set to true to auto-validate the input value as you type.
       */
      autoValidate: {
        type: Boolean,
        value: false
      },

      /**
       * The native input element.
       */
      _inputElement: Object
    },
    observers: ['_bindValueChanged(bindValue, _inputElement)'],
    listeners: {
      'input': '_onInput',
      'keypress': '_onKeypress'
    },
    created: function () {
      IronA11yAnnouncer.requestAvailability();
      this._previousValidInput = '';
      this._patternAlreadyChecked = false;
    },
    attached: function () {
      // If the input is added at a later time, update the internal reference.
      this._observer = dom(this).observeNodes(function (info) {
        this._initSlottedInput();
      }.bind(this));
    },
    detached: function () {
      if (this._observer) {
        dom(this).unobserveNodes(this._observer);
        this._observer = null;
      }
    },

    /**
     * Returns the distributed input element.
     */
    get inputElement() {
      return this._inputElement;
    },

    _initSlottedInput: function () {
      this._inputElement = this.getEffectiveChildren()[0];

      if (this.inputElement && this.inputElement.value) {
        this.bindValue = this.inputElement.value;
      }

      this.fire('iron-input-ready');
    },

    get _patternRegExp() {
      var pattern;

      if (this.allowedPattern) {
        pattern = new RegExp(this.allowedPattern);
      } else {
        switch (this.inputElement.type) {
          case 'number':
            pattern = /[0-9.,e-]/;
            break;
        }
      }

      return pattern;
    },

    /**
     * @suppress {checkTypes}
     */
    _bindValueChanged: function (bindValue, inputElement) {
      // The observer could have run before attached() when we have actually
      // initialized this property.
      if (!inputElement) {
        return;
      }

      if (bindValue === undefined) {
        inputElement.value = null;
      } else if (bindValue !== inputElement.value) {
        this.inputElement.value = bindValue;
      }

      if (this.autoValidate) {
        this.validate();
      } // manually notify because we don't want to notify until after setting value


      this.fire('bind-value-changed', {
        value: bindValue
      });
    },
    _onInput: function () {
      // Need to validate each of the characters pasted if they haven't
      // been validated inside `_onKeypress` already.
      if (this.allowedPattern && !this._patternAlreadyChecked) {
        var valid = this._checkPatternValidity();

        if (!valid) {
          this._announceInvalidCharacter('Invalid string of characters not entered.');

          this.inputElement.value = this._previousValidInput;
        }
      }

      this.bindValue = this._previousValidInput = this.inputElement.value;
      this._patternAlreadyChecked = false;
    },
    _isPrintable: function (event) {
      // What a control/printable character is varies wildly based on the browser.
      // - most control characters (arrows, backspace) do not send a `keypress`
      // event
      //   in Chrome, but the *do* on Firefox
      // - in Firefox, when they do send a `keypress` event, control chars have
      //   a charCode = 0, keyCode = xx (for ex. 40 for down arrow)
      // - printable characters always send a keypress event.
      // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the
      // keyCode
      //   always matches the charCode.
      // None of this makes any sense.
      // For these keys, ASCII code == browser keycode.
      var anyNonPrintable = event.keyCode == 8 || // backspace
      event.keyCode == 9 || // tab
      event.keyCode == 13 || // enter
      event.keyCode == 27; // escape
      // For these keys, make sure it's a browser keycode and not an ASCII code.

      var mozNonPrintable = event.keyCode == 19 || // pause
      event.keyCode == 20 || // caps lock
      event.keyCode == 45 || // insert
      event.keyCode == 46 || // delete
      event.keyCode == 144 || // num lock
      event.keyCode == 145 || // scroll lock
      event.keyCode > 32 && event.keyCode < 41 || // page up/down, end, home, arrows
      event.keyCode > 111 && event.keyCode < 124; // fn keys

      return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable);
    },
    _onKeypress: function (event) {
      if (!this.allowedPattern && this.inputElement.type !== 'number') {
        return;
      }

      var regexp = this._patternRegExp;

      if (!regexp) {
        return;
      } // Handle special keys and backspace


      if (event.metaKey || event.ctrlKey || event.altKey) {
        return;
      } // Check the pattern either here or in `_onInput`, but not in both.


      this._patternAlreadyChecked = true;
      var thisChar = String.fromCharCode(event.charCode);

      if (this._isPrintable(event) && !regexp.test(thisChar)) {
        event.preventDefault();

        this._announceInvalidCharacter('Invalid character ' + thisChar + ' not entered.');
      }
    },
    _checkPatternValidity: function () {
      var regexp = this._patternRegExp;

      if (!regexp) {
        return true;
      }

      for (var i = 0; i < this.inputElement.value.length; i++) {
        if (!regexp.test(this.inputElement.value[i])) {
          return false;
        }
      }

      return true;
    },

    /**
     * Returns true if `value` is valid. The validator provided in `validator`
     * will be used first, then any constraints.
     * @return {boolean} True if the value is valid.
     */
    validate: function () {
      if (!this.inputElement) {
        this.invalid = false;
        return true;
      } // Use the nested input's native validity.


      var valid = this.inputElement.checkValidity(); // Only do extra checking if the browser thought this was valid.

      if (valid) {
        // Empty, required input is invalid
        if (this.required && this.bindValue === '') {
          valid = false;
        } else if (this.hasValidator()) {
          valid = IronValidatableBehavior.validate.call(this, this.bindValue);
        }
      }

      this.invalid = !valid;
      this.fire('iron-input-validate');
      return valid;
    },
    _announceInvalidCharacter: function (message) {
      this.fire('iron-announce', {
        text: message
      });
    },
    _computeValue: function (bindValue) {
      return bindValue;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Use `Polymer.PaperInputAddonBehavior` to implement an add-on for
   * `<paper-input-container>`. A add-on appears below the input, and may display
   * information based on the input value and validity such as a character counter
   * or an error message.
   * @polymerBehavior
   */

  const PaperInputAddonBehavior = {
    attached: function () {
      this.fire('addon-attached');
    },

    /**
     * The function called by `<paper-input-container>` when the input value or
     * validity changes.
     * @param {{
     *   invalid: boolean,
     *   inputElement: (Element|undefined),
     *   value: (string|undefined)
     * }} state -
     *     inputElement: The input element.
     *     value: The input value.
     *     invalid: True if the input value is invalid.
     */
    update: function (state) {}
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `<paper-input-char-counter>` is a character counter for use with
  `<paper-input-container>`. It shows the number of characters entered in the
  input and the max length if it is specified.

      <paper-input-container>
        <input maxlength="20">
        <paper-input-char-counter></paper-input-char-counter>
      </paper-input-container>

  ### Styling

  The following mixin is available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-input-char-counter` | Mixin applied to the element | `{}`
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: inline-block;
        float: right;

        @apply --paper-font-caption;
        @apply --paper-input-char-counter;
      }

      :host([hidden]) {
        display: none !important;
      }

      :host(:dir(rtl)) {
        float: left;
      }
    </style>

    <span>[[_charCounterStr]]</span>
`,
    is: 'paper-input-char-counter',
    behaviors: [PaperInputAddonBehavior],
    properties: {
      _charCounterStr: {
        type: String,
        value: '0'
      }
    },

    /**
     * This overrides the update function in PaperInputAddonBehavior.
     * @param {{
     *   inputElement: (Element|undefined),
     *   value: (string|undefined),
     *   invalid: boolean
     * }} state -
     *     inputElement: The input element.
     *     value: The input value.
     *     invalid: True if the input value is invalid.
     */
    update: function (state) {
      if (!state.inputElement) {
        return;
      }

      state.value = state.value || '';
      var counter = state.value.toString().length.toString();

      if (state.inputElement.hasAttribute('maxlength')) {
        counter += '/' + state.inputElement.getAttribute('maxlength');
      }

      this._charCounterStr = counter;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const template$5 = html$1`
<custom-style>
  <style is="custom-style">
    html {
      --paper-input-container-shared-input-style: {
        position: relative; /* to make a stacking context */
        outline: none;
        box-shadow: none;
        padding: 0;
        margin: 0;
        width: 100%;
        max-width: 100%;
        background: transparent;
        border: none;
        color: var(--paper-input-container-input-color, var(--primary-text-color));
        -webkit-appearance: none;
        text-align: inherit;
        vertical-align: var(--paper-input-container-input-align, bottom);

        @apply --paper-font-subhead;
      };
    }
  </style>
</custom-style>
`;
  template$5.setAttribute('style', 'display: none;');
  document.head.appendChild(template$5.content);
  /*
  `<paper-input-container>` is a container for a `<label>`, an `<iron-input>` or
  `<textarea>` and optional add-on elements such as an error message or character
  counter, used to implement Material Design text fields.

  For example:

      <paper-input-container>
        <label slot="label">Your name</label>
        <iron-input slot="input">
          <input>
        </iron-input>
        // In Polymer 1.0, you would use `<input is="iron-input" slot="input">`
  instead of the above.
      </paper-input-container>

  You can style the nested `<input>` however you want; if you want it to look like
  a Material Design input, you can style it with the
  --paper-input-container-shared-input-style mixin.

  Do not wrap `<paper-input-container>` around elements that already include it,
  such as `<paper-input>`. Doing so may cause events to bounce infinitely between
  the container and its contained element.

  ### Listening for input changes

  By default, it listens for changes on the `bind-value` attribute on its children
  nodes and perform tasks such as auto-validating and label styling when the
  `bind-value` changes. You can configure the attribute it listens to with the
  `attr-for-value` attribute.

  ### Using a custom input element

  You can use a custom input element in a `<paper-input-container>`, for example
  to implement a compound input field like a social security number input. The
  custom input element should have the `paper-input-input` class, have a
  `notify:true` value property and optionally implements
  `Polymer.IronValidatableBehavior` if it is validatable.

      <paper-input-container attr-for-value="ssn-value">
        <label slot="label">Social security number</label>
        <ssn-input slot="input" class="paper-input-input"></ssn-input>
      </paper-input-container>


  If you're using a `<paper-input-container>` imperatively, it's important to make
  sure that you attach its children (the `iron-input` and the optional `label`)
  before you attach the `<paper-input-container>` itself, so that it can be set up
  correctly.

  ### Validation

  If the `auto-validate` attribute is set, the input container will validate the
  input and update the container styling when the input value changes.

  ### Add-ons

  Add-ons are child elements of a `<paper-input-container>` with the `add-on`
  attribute and implements the `Polymer.PaperInputAddonBehavior` behavior. They
  are notified when the input value or validity changes, and may implement
  functionality such as error messages or character counters. They appear at the
  bottom of the input.

  ### Prefixes and suffixes
  These are child elements of a `<paper-input-container>` with the `prefix`
  or `suffix` attribute, and are displayed inline with the input, before or after.

      <paper-input-container>
        <div slot="prefix">$</div>
        <label slot="label">Total</label>
        <iron-input slot="input">
          <input>
        </iron-input>
        // In Polymer 1.0, you would use `<input is="iron-input" slot="input">`
  instead of the above. <paper-icon-button slot="suffix"
  icon="clear"></paper-icon-button>
      </paper-input-container>

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-input-container-color` | Label and underline color when the input is not focused | `--secondary-text-color`
  `--paper-input-container-focus-color` | Label and underline color when the input is focused | `--primary-color`
  `--paper-input-container-invalid-color` | Label and underline color when the input is is invalid | `--error-color`
  `--paper-input-container-input-color` | Input foreground color | `--primary-text-color`
  `--paper-input-container` | Mixin applied to the container | `{}`
  `--paper-input-container-disabled` | Mixin applied to the container when it's disabled | `{}`
  `--paper-input-container-label` | Mixin applied to the label | `{}`
  `--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}`
  `--paper-input-container-label-floating` | Mixin applied to the label when floating | `{}`
  `--paper-input-container-input` | Mixin applied to the input | `{}`
  `--paper-input-container-input-align` | The vertical-align property of the input | `bottom`
  `--paper-input-container-input-disabled` | Mixin applied to the input when the component is disabled | `{}`
  `--paper-input-container-input-focus` | Mixin applied to the input when focused | `{}`
  `--paper-input-container-input-invalid` | Mixin applied to the input when invalid | `{}`
  `--paper-input-container-input-webkit-spinner` | Mixin applied to the webkit spinner | `{}`
  `--paper-input-container-input-webkit-clear` | Mixin applied to the webkit clear button | `{}`
  `--paper-input-container-input-webkit-calendar-picker-indicator` | Mixin applied to the webkit calendar picker indicator | `{}`
  `--paper-input-container-ms-clear` | Mixin applied to the Internet Explorer clear button | `{}`
  `--paper-input-container-underline` | Mixin applied to the underline | `{}`
  `--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}`
  `--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}`
  `--paper-input-prefix` | Mixin applied to the input prefix | `{}`
  `--paper-input-suffix` | Mixin applied to the input suffix | `{}`
  `--paper-input-container-label-before` | Mixin applied to label before pseudo element | {}
  `--paper-input-container-label-after` | Mixin applied to label after pseudo element (useful for required asterisk) | {}

  This element is `display:block` by default, but you can set the `inline`
  attribute to make it `display:inline-block`.
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: block;
        padding: 8px 0;
        @apply --paper-input-container;
      }

      :host([inline]) {
        display: inline-block;
      }

      :host([disabled]) {
        pointer-events: none;
        opacity: 0.33;

        @apply --paper-input-container-disabled;
      }

      :host([hidden]) {
        display: none !important;
      }

      [hidden] {
        display: none !important;
      }

      .floated-label-placeholder {
        @apply --paper-font-caption;
      }

      .underline {
        height: 2px;
        position: relative;
      }

      .focused-line {
        @apply --layout-fit;
        border-bottom: 2px solid var(--paper-input-container-focus-color, var(--primary-color));

        -webkit-transform-origin: center center;
        transform-origin: center center;
        -webkit-transform: scale3d(0,1,1);
        transform: scale3d(0,1,1);

        @apply --paper-input-container-underline-focus;
      }

      .underline.is-highlighted .focused-line {
        -webkit-transform: none;
        transform: none;
        -webkit-transition: -webkit-transform 0.25s;
        transition: transform 0.25s;

        @apply --paper-transition-easing;
      }

      .underline.is-invalid .focused-line {
        border-color: var(--paper-input-container-invalid-color, var(--error-color));
        -webkit-transform: none;
        transform: none;
        -webkit-transition: -webkit-transform 0.25s;
        transition: transform 0.25s;

        @apply --paper-transition-easing;
      }

      .unfocused-line {
        @apply --layout-fit;
        border-bottom: 1px solid var(--paper-input-container-color, var(--secondary-text-color));
        @apply --paper-input-container-underline;
      }

      :host([disabled]) .unfocused-line {
        border-bottom: 1px dashed;
        border-color: var(--paper-input-container-color, var(--secondary-text-color));
        @apply --paper-input-container-underline-disabled;
      }

      .input-wrapper {
        @apply --layout-horizontal;
        @apply --layout-center;
        position: relative;
      }

      .input-content {
        @apply --layout-flex-auto;
        @apply --layout-relative;
        max-width: 100%;
      }

      .input-content ::slotted(label),
      .input-content ::slotted(.paper-input-label) {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        font: inherit;
        color: var(--paper-input-container-color, var(--secondary-text-color));
        -webkit-transition: -webkit-transform 0.25s, width 0.25s;
        transition: transform 0.25s, width 0.25s;
        -webkit-transform-origin: left top;
        transform-origin: left top;
        /* Fix for safari not focusing 0-height date/time inputs with -webkit-apperance: none; */
        min-height: 1px;

        @apply --paper-font-common-nowrap;
        @apply --paper-font-subhead;
        @apply --paper-input-container-label;
        @apply --paper-transition-easing;
      }


      .input-content ::slotted(label):before,
      .input-content ::slotted(.paper-input-label):before {
        @apply --paper-input-container-label-before;
      }

      .input-content ::slotted(label):after,
      .input-content ::slotted(.paper-input-label):after {
        @apply --paper-input-container-label-after;
      }

      .input-content.label-is-floating ::slotted(label),
      .input-content.label-is-floating ::slotted(.paper-input-label) {
        -webkit-transform: translateY(-75%) scale(0.75);
        transform: translateY(-75%) scale(0.75);

        /* Since we scale to 75/100 of the size, we actually have 100/75 of the
        original space now available */
        width: 133%;

        @apply --paper-input-container-label-floating;
      }

      :host(:dir(rtl)) .input-content.label-is-floating ::slotted(label),
      :host(:dir(rtl)) .input-content.label-is-floating ::slotted(.paper-input-label) {
        right: 0;
        left: auto;
        -webkit-transform-origin: right top;
        transform-origin: right top;
      }

      .input-content.label-is-highlighted ::slotted(label),
      .input-content.label-is-highlighted ::slotted(.paper-input-label) {
        color: var(--paper-input-container-focus-color, var(--primary-color));

        @apply --paper-input-container-label-focus;
      }

      .input-content.is-invalid ::slotted(label),
      .input-content.is-invalid ::slotted(.paper-input-label) {
        color: var(--paper-input-container-invalid-color, var(--error-color));
      }

      .input-content.label-is-hidden ::slotted(label),
      .input-content.label-is-hidden ::slotted(.paper-input-label) {
        visibility: hidden;
      }

      .input-content ::slotted(input),
      .input-content ::slotted(iron-input),
      .input-content ::slotted(textarea),
      .input-content ::slotted(iron-autogrow-textarea),
      .input-content ::slotted(.paper-input-input) {
        @apply --paper-input-container-shared-input-style;
        /* The apply shim doesn't apply the nested color custom property,
          so we have to re-apply it here. */
        color: var(--paper-input-container-input-color, var(--primary-text-color));
        @apply --paper-input-container-input;
      }

      .input-content ::slotted(input)::-webkit-outer-spin-button,
      .input-content ::slotted(input)::-webkit-inner-spin-button {
        @apply --paper-input-container-input-webkit-spinner;
      }

      .input-content.focused ::slotted(input),
      .input-content.focused ::slotted(iron-input),
      .input-content.focused ::slotted(textarea),
      .input-content.focused ::slotted(iron-autogrow-textarea),
      .input-content.focused ::slotted(.paper-input-input) {
        @apply --paper-input-container-input-focus;
      }

      .input-content.is-invalid ::slotted(input),
      .input-content.is-invalid ::slotted(iron-input),
      .input-content.is-invalid ::slotted(textarea),
      .input-content.is-invalid ::slotted(iron-autogrow-textarea),
      .input-content.is-invalid ::slotted(.paper-input-input) {
        @apply --paper-input-container-input-invalid;
      }

      .prefix ::slotted(*) {
        display: inline-block;
        @apply --paper-font-subhead;
        @apply --layout-flex-none;
        @apply --paper-input-prefix;
      }

      .suffix ::slotted(*) {
        display: inline-block;
        @apply --paper-font-subhead;
        @apply --layout-flex-none;

        @apply --paper-input-suffix;
      }

      /* Firefox sets a min-width on the input, which can cause layout issues */
      .input-content ::slotted(input) {
        min-width: 0;
      }

      .input-content ::slotted(textarea) {
        resize: none;
      }

      .add-on-content {
        position: relative;
      }

      .add-on-content.is-invalid ::slotted(*) {
        color: var(--paper-input-container-invalid-color, var(--error-color));
      }

      .add-on-content.is-highlighted ::slotted(*) {
        color: var(--paper-input-container-focus-color, var(--primary-color));
      }
    </style>

    <div class="floated-label-placeholder" aria-hidden="true" hidden="[[noLabelFloat]]">&nbsp;</div>

    <div class="input-wrapper">
      <span class="prefix"><slot name="prefix"></slot></span>

      <div class$="[[_computeInputContentClass(noLabelFloat,alwaysFloatLabel,focused,invalid,_inputHasContent)]]" id="labelAndInputContainer">
        <slot name="label"></slot>
        <slot name="input"></slot>
      </div>

      <span class="suffix"><slot name="suffix"></slot></span>
    </div>

    <div class$="[[_computeUnderlineClass(focused,invalid)]]">
      <div class="unfocused-line"></div>
      <div class="focused-line"></div>
    </div>

    <div class$="[[_computeAddOnContentClass(focused,invalid)]]">
      <slot name="add-on"></slot>
    </div>
`,
    is: 'paper-input-container',
    properties: {
      /**
       * Set to true to disable the floating label. The label disappears when the
       * input value is not null.
       */
      noLabelFloat: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to always float the floating label.
       */
      alwaysFloatLabel: {
        type: Boolean,
        value: false
      },

      /**
       * The attribute to listen for value changes on.
       */
      attrForValue: {
        type: String,
        value: 'bind-value'
      },

      /**
       * Set to true to auto-validate the input value when it changes.
       */
      autoValidate: {
        type: Boolean,
        value: false
      },

      /**
       * True if the input is invalid. This property is set automatically when the
       * input value changes if auto-validating, or when the `iron-input-validate`
       * event is heard from a child.
       */
      invalid: {
        observer: '_invalidChanged',
        type: Boolean,
        value: false
      },

      /**
       * True if the input has focus.
       */
      focused: {
        readOnly: true,
        type: Boolean,
        value: false,
        notify: true
      },
      _addons: {
        type: Array // do not set a default value here intentionally - it will be initialized
        // lazily when a distributed child is attached, which may occur before
        // configuration for this element in polyfill.

      },
      _inputHasContent: {
        type: Boolean,
        value: false
      },
      _inputSelector: {
        type: String,
        value: 'input,iron-input,textarea,.paper-input-input'
      },
      _boundOnFocus: {
        type: Function,
        value: function () {
          return this._onFocus.bind(this);
        }
      },
      _boundOnBlur: {
        type: Function,
        value: function () {
          return this._onBlur.bind(this);
        }
      },
      _boundOnInput: {
        type: Function,
        value: function () {
          return this._onInput.bind(this);
        }
      },
      _boundValueChanged: {
        type: Function,
        value: function () {
          return this._onValueChanged.bind(this);
        }
      }
    },
    listeners: {
      'addon-attached': '_onAddonAttached',
      'iron-input-validate': '_onIronInputValidate'
    },

    get _valueChangedEvent() {
      return this.attrForValue + '-changed';
    },

    get _propertyForValue() {
      return dashToCamelCase(this.attrForValue);
    },

    get _inputElement() {
      return dom(this).querySelector(this._inputSelector);
    },

    get _inputElementValue() {
      return this._inputElement[this._propertyForValue] || this._inputElement.value;
    },

    /** @override */
    ready: function () {
      // Paper-input treats a value of undefined differently at startup than
      // the rest of the time (specifically: it does not validate it at startup,
      // but it does after that. We need to track whether the first time we
      // encounter the value is basically this first time, so that we can validate
      // it correctly the rest of the time. See
      // https://github.com/PolymerElements/paper-input/issues/605
      this.__isFirstValueUpdate = true;

      if (!this._addons) {
        this._addons = [];
      }

      this.addEventListener('focus', this._boundOnFocus, true);
      this.addEventListener('blur', this._boundOnBlur, true);
    },

    /** @override */
    attached: function () {
      if (this.attrForValue) {
        this._inputElement.addEventListener(this._valueChangedEvent, this._boundValueChanged);
      } else {
        this.addEventListener('input', this._onInput);
      } // Only validate when attached if the input already has a value.


      if (this._inputElementValue && this._inputElementValue != '') {
        this._handleValueAndAutoValidate(this._inputElement);
      } else {
        this._handleValue(this._inputElement);
      }
    },

    /** @private */
    _onAddonAttached: function (event) {
      if (!this._addons) {
        this._addons = [];
      }

      var target = event.target;

      if (this._addons.indexOf(target) === -1) {
        this._addons.push(target);

        if (this.isAttached) {
          this._handleValue(this._inputElement);
        }
      }
    },

    /** @private */
    _onFocus: function () {
      this._setFocused(true);
    },

    /** @private */
    _onBlur: function () {
      this._setFocused(false);

      this._handleValueAndAutoValidate(this._inputElement);
    },

    /** @private */
    _onInput: function (event) {
      this._handleValueAndAutoValidate(event.target);
    },

    /** @private */
    _onValueChanged: function (event) {
      var input = event.target; // Paper-input treats a value of undefined differently at startup than
      // the rest of the time (specifically: it does not validate it at startup,
      // but it does after that. If this is in fact the bootup case, ignore
      // validation, just this once.

      if (this.__isFirstValueUpdate) {
        this.__isFirstValueUpdate = false;

        if (input.value === undefined || input.value === '') {
          return;
        }
      }

      this._handleValueAndAutoValidate(event.target);
    },

    /** @private */
    _handleValue: function (inputElement) {
      var value = this._inputElementValue; // type="number" hack needed because this.value is empty until it's valid

      if (value || value === 0 || inputElement.type === 'number' && !inputElement.checkValidity()) {
        this._inputHasContent = true;
      } else {
        this._inputHasContent = false;
      }

      this.updateAddons({
        inputElement: inputElement,
        value: value,
        invalid: this.invalid
      });
    },

    /** @private */
    _handleValueAndAutoValidate: function (inputElement) {
      if (this.autoValidate && inputElement) {
        var valid;

        if (inputElement.validate) {
          valid = inputElement.validate(this._inputElementValue);
        } else {
          valid = inputElement.checkValidity();
        }

        this.invalid = !valid;
      } // Call this last to notify the add-ons.


      this._handleValue(inputElement);
    },

    /** @private */
    _onIronInputValidate: function (event) {
      this.invalid = this._inputElement.invalid;
    },

    /** @private */
    _invalidChanged: function () {
      if (this._addons) {
        this.updateAddons({
          invalid: this.invalid
        });
      }
    },

    /**
     * Call this to update the state of add-ons.
     * @param {Object} state Add-on state.
     */
    updateAddons: function (state) {
      for (var addon, index = 0; addon = this._addons[index]; index++) {
        addon.update(state);
      }
    },

    /** @private */
    _computeInputContentClass: function (noLabelFloat, alwaysFloatLabel, focused, invalid, _inputHasContent) {
      var cls = 'input-content';

      if (!noLabelFloat) {
        var label = this.querySelector('label');

        if (alwaysFloatLabel || _inputHasContent) {
          cls += ' label-is-floating'; // If the label is floating, ignore any offsets that may have been
          // applied from a prefix element.

          this.$.labelAndInputContainer.style.position = 'static';

          if (invalid) {
            cls += ' is-invalid';
          } else if (focused) {
            cls += ' label-is-highlighted';
          }
        } else {
          // When the label is not floating, it should overlap the input element.
          if (label) {
            this.$.labelAndInputContainer.style.position = 'relative';
          }

          if (invalid) {
            cls += ' is-invalid';
          }
        }
      } else {
        if (_inputHasContent) {
          cls += ' label-is-hidden';
        }

        if (invalid) {
          cls += ' is-invalid';
        }
      }

      if (focused) {
        cls += ' focused';
      }

      return cls;
    },

    /** @private */
    _computeUnderlineClass: function (focused, invalid) {
      var cls = 'underline';

      if (invalid) {
        cls += ' is-invalid';
      } else if (focused) {
        cls += ' is-highlighted';
      }

      return cls;
    },

    /** @private */
    _computeAddOnContentClass: function (focused, invalid) {
      var cls = 'add-on-content';

      if (invalid) {
        cls += ' is-invalid';
      } else if (focused) {
        cls += ' is-highlighted';
      }

      return cls;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `<paper-input-error>` is an error message for use with
  `<paper-input-container>`. The error is displayed when the
  `<paper-input-container>` is `invalid`.

      <paper-input-container>
        <input pattern="[0-9]*">
        <paper-input-error slot="add-on">Only numbers are
  allowed!</paper-input-error>
      </paper-input-container>

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-input-container-invalid-color` | The foreground color of the error | `--error-color`
  `--paper-input-error` | Mixin applied to the error | `{}`
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: inline-block;
        visibility: hidden;

        color: var(--paper-input-container-invalid-color, var(--error-color));

        @apply --paper-font-caption;
        @apply --paper-input-error;
        position: absolute;
        left:0;
        right:0;
      }

      :host([invalid]) {
        visibility: visible;
      }

      #a11yWrapper {
        visibility: hidden;
      }

      :host([invalid]) #a11yWrapper {
        visibility: visible;
      }
    </style>

    <!--
    If the paper-input-error element is directly referenced by an
    \`aria-describedby\` attribute, such as when used as a paper-input add-on,
    then applying \`visibility: hidden;\` to the paper-input-error element itself
    does not hide the error.

    For more information, see:
    https://www.w3.org/TR/accname-1.1/#mapping_additional_nd_description
    -->
    <div id="a11yWrapper">
      <slot></slot>
    </div>
`,
    is: 'paper-input-error',
    behaviors: [PaperInputAddonBehavior],
    properties: {
      /**
       * True if the error is showing.
       */
      invalid: {
        readOnly: true,
        reflectToAttribute: true,
        type: Boolean
      }
    },

    /**
     * This overrides the update function in PaperInputAddonBehavior.
     * @param {{
     *   inputElement: (Element|undefined),
     *   value: (string|undefined),
     *   invalid: boolean
     * }} state -
     *     inputElement: The input element.
     *     value: The input value.
     *     invalid: True if the input value is invalid.
     */
    update: function (state) {
      this._setInvalid(state.invalid);
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
    IronFormElementBehavior adds a `name`, `value` and `required` properties to
    a custom element. It mostly exists for backcompatibility with Polymer 1.x, and
    is probably not something you want to use.

    @demo demo/index.html
    @polymerBehavior
   */

  const IronFormElementBehavior = {
    properties: {
      /**
       * The name of this element.
       */
      name: {
        type: String
      },

      /**
       * The value for this element.
       * @type {*}
       */
      value: {
        notify: true,
        type: String
      },

      /**
       * Set to true to mark the input as required. If used in a form, a
       * custom element that uses this behavior should also use
       * IronValidatableBehavior and define a custom validation method.
       * Otherwise, a `required` element will always be considered valid.
       * It's also strongly recommended to provide a visual style for the element
       * when its value is invalid.
       */
      required: {
        type: Boolean,
        value: false
      }
    },
    // Empty implementations for backcompatibility.
    attached: function () {},
    detached: function () {}
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  // aria-labelledby) and add-ons.

  const PaperInputHelper = {};
  PaperInputHelper.NextLabelID = 1;
  PaperInputHelper.NextAddonID = 1;
  PaperInputHelper.NextInputID = 1;
  /**
   * Use `PaperInputBehavior` to implement inputs with `<paper-input-container>`.
   * This behavior is implemented by `<paper-input>`. It exposes a number of
   * properties from `<paper-input-container>` and `<input is="iron-input">` and
   * they should be bound in your template.
   *
   * The input element can be accessed by the `inputElement` property if you need
   * to access properties or methods that are not exposed.
   * @polymerBehavior PaperInputBehavior
   */

  const PaperInputBehaviorImpl = {
    properties: {
      /**
       * Fired when the input changes due to user interaction.
       *
       * @event change
       */

      /**
       * The label for this input. If you're using PaperInputBehavior to
       * implement your own paper-input-like element, bind this to
       * `<label>`'s content and `hidden` property, e.g.
       * `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
       */
      label: {
        type: String
      },

      /**
       * The value for this input. If you're using PaperInputBehavior to
       * implement your own paper-input-like element, bind this to
       * the `<iron-input>`'s `bindValue`
       * property, or the value property of your input that is `notify:true`.
       * @type {*}
       */
      value: {
        notify: true,
        type: String
      },

      /**
       * Set to true to disable this input. If you're using PaperInputBehavior to
       * implement your own paper-input-like element, bind this to
       * both the `<paper-input-container>`'s and the input's `disabled` property.
       */
      disabled: {
        type: Boolean,
        value: false
      },

      /**
       * Returns true if the value is invalid. If you're using PaperInputBehavior
       * to implement your own paper-input-like element, bind this to both the
       * `<paper-input-container>`'s and the input's `invalid` property.
       *
       * If `autoValidate` is true, the `invalid` attribute is managed
       * automatically, which can clobber attempts to manage it manually.
       */
      invalid: {
        type: Boolean,
        value: false,
        notify: true
      },

      /**
       * Set this to specify the pattern allowed by `preventInvalidInput`. If
       * you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `allowedPattern`
       * property.
       */
      allowedPattern: {
        type: String
      },

      /**
       * The type of the input. The supported types are the
       * [native input's
       * types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_<input>_types).
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the (Polymer 1) `<input is="iron-input">`'s or
       * (Polymer 2)
       * `<iron-input>`'s `type` property.
       */
      type: {
        type: String
      },

      /**
       * The datalist of the input (if any). This should match the id of an
       * existing `<datalist>`. If you're using PaperInputBehavior to implement
       * your own paper-input-like element, bind this to the `<input
       * is="iron-input">`'s `list` property.
       */
      list: {
        type: String
      },

      /**
       * A pattern to validate the `input` with. If you're using
       * PaperInputBehavior to implement your own paper-input-like element, bind
       * this to the `<input is="iron-input">`'s `pattern` property.
       */
      pattern: {
        type: String
      },

      /**
       * Set to true to mark the input as required. If you're using
       * PaperInputBehavior to implement your own paper-input-like element, bind
       * this to the `<input is="iron-input">`'s `required` property.
       */
      required: {
        type: Boolean,
        value: false
      },

      /**
       * The error message to display when the input is invalid. If you're using
       * PaperInputBehavior to implement your own paper-input-like element,
       * bind this to the `<paper-input-error>`'s content, if using.
       */
      errorMessage: {
        type: String
      },

      /**
       * Set to true to show a character counter.
       */
      charCounter: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable the floating label. If you're using
       * PaperInputBehavior to implement your own paper-input-like element, bind
       * this to the `<paper-input-container>`'s `noLabelFloat` property.
       */
      noLabelFloat: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to always float the label. If you're using PaperInputBehavior
       * to implement your own paper-input-like element, bind this to the
       * `<paper-input-container>`'s `alwaysFloatLabel` property.
       */
      alwaysFloatLabel: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to auto-validate the input value. If you're using
       * PaperInputBehavior to implement your own paper-input-like element, bind
       * this to the `<paper-input-container>`'s `autoValidate` property.
       */
      autoValidate: {
        type: Boolean,
        value: false
      },

      /**
       * Name of the validator to use. If you're using PaperInputBehavior to
       * implement your own paper-input-like element, bind this to
       * the `<input is="iron-input">`'s `validator` property.
       */
      validator: {
        type: String
      },
      // HTMLInputElement attributes for binding if needed

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `autocomplete`
       * property.
       */
      autocomplete: {
        type: String,
        value: 'off'
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `autofocus`
       * property.
       *
       * @type {!boolean}
       */
      autofocus: {
        type: Boolean,
        observer: '_autofocusChanged'
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `inputmode`
       * property.
       */
      inputmode: {
        type: String
      },

      /**
       * The minimum length of the input value.
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `minlength`
       * property.
       */
      minlength: {
        type: Number
      },

      /**
       * The maximum length of the input value.
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `maxlength`
       * property.
       */
      maxlength: {
        type: Number
      },

      /**
       * The minimum (numeric or date-time) input value.
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `min` property.
       */
      min: {
        type: String
      },

      /**
       * The maximum (numeric or date-time) input value.
       * Can be a String (e.g. `"2000-01-01"`) or a Number (e.g. `2`).
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `max` property.
       */
      max: {
        type: String
      },

      /**
       * Limits the numeric or date-time increments.
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `step` property.
       */
      step: {
        type: String
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `name` property.
       */
      name: {
        type: String
      },

      /**
       * A placeholder string in addition to the label. If this is set, the label
       * will always float.
       */
      placeholder: {
        type: String,
        // need to set a default so _computeAlwaysFloatLabel is run
        value: ''
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `readonly`
       * property.
       */
      readonly: {
        type: Boolean,
        value: false
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `size` property.
       */
      size: {
        type: Number
      },
      // Nonstandard attributes for binding if needed

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `autocapitalize`
       * property.
       *
       * @type {string}
       */
      autocapitalize: {
        type: String,
        value: 'none'
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `autocorrect`
       * property.
       */
      autocorrect: {
        type: String,
        value: 'off'
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `autosave`
       * property, used with type=search.
       */
      autosave: {
        type: String
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `results` property,
       * used with type=search.
       */
      results: {
        type: Number
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the `<input is="iron-input">`'s `accept` property,
       * used with type=file.
       */
      accept: {
        type: String
      },

      /**
       * If you're using PaperInputBehavior to implement your own paper-input-like
       * element, bind this to the`<input is="iron-input">`'s `multiple` property,
       * used with type=file.
       */
      multiple: {
        type: Boolean
      },

      /** @private */
      _ariaDescribedBy: {
        type: String,
        value: ''
      },

      /** @private */
      _ariaLabelledBy: {
        type: String,
        value: ''
      },

      /** @private */
      _inputId: {
        type: String,
        value: ''
      }
    },
    listeners: {
      'addon-attached': '_onAddonAttached'
    },

    /**
     * @type {!Object}
     */
    keyBindings: {
      'shift+tab:keydown': '_onShiftTabDown'
    },

    /** @private */
    hostAttributes: {
      tabindex: 0
    },

    /**
     * Returns a reference to the input element.
     * @return {!HTMLElement}
     */
    get inputElement() {
      // Chrome generates audit errors if an <input type="password"> has a
      // duplicate ID, which is almost always true in Shady DOM. Generate
      // a unique ID instead.
      if (!this.$) {
        this.$ = {};
      }

      if (!this.$.input) {
        this._generateInputId();

        this.$.input = this.$$('#' + this._inputId);
      }

      return this.$.input;
    },

    /**
     * Returns a reference to the focusable element.
     * @return {!HTMLElement}
     */
    get _focusableElement() {
      return this.inputElement;
    },

    /** @override */
    created: function () {
      // These types have some default placeholder text; overlapping
      // the label on top of it looks terrible. Auto-float the label in this case.
      this._typesThatHaveText = ['date', 'datetime', 'datetime-local', 'month', 'time', 'week', 'file'];
    },

    /** @override */
    attached: function () {
      this._updateAriaLabelledBy(); // In the 2.0 version of the element, this is handled in `onIronInputReady`,
      // i.e. after the native input has finished distributing. In the 1.0
      // version, the input is in the shadow tree, so it's already available.


      if (!PolymerElement && this.inputElement && this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) {
        this.alwaysFloatLabel = true;
      }
    },
    _appendStringWithSpace: function (str, more) {
      if (str) {
        str = str + ' ' + more;
      } else {
        str = more;
      }

      return str;
    },
    _onAddonAttached: function (event) {
      var target = dom(event).rootTarget;

      if (target.id) {
        this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, target.id);
      } else {
        var id = 'paper-input-add-on-' + PaperInputHelper.NextAddonID++;
        target.id = id;
        this._ariaDescribedBy = this._appendStringWithSpace(this._ariaDescribedBy, id);
      }
    },

    /**
     * Validates the input element and sets an error style if needed.
     *
     * @return {boolean}
     */
    validate: function () {
      return this.inputElement.validate();
    },

    /**
     * Forward focus to inputElement. Overriden from IronControlState.
     */
    _focusBlurHandler: function (event) {
      IronControlState._focusBlurHandler.call(this, event); // Forward the focus to the nested input.


      if (this.focused && !this._shiftTabPressed && this._focusableElement) {
        this._focusableElement.focus();
      }
    },

    /**
     * Handler that is called when a shift+tab keypress is detected by the menu.
     *
     * @param {CustomEvent} event A key combination event.
     */
    _onShiftTabDown: function (event) {
      var oldTabIndex = this.getAttribute('tabindex');
      this._shiftTabPressed = true;
      this.setAttribute('tabindex', '-1');
      this.async(function () {
        this.setAttribute('tabindex', oldTabIndex);
        this._shiftTabPressed = false;
      }, 1);
    },

    /**
     * If `autoValidate` is true, then validates the element.
     */
    _handleAutoValidate: function () {
      if (this.autoValidate) this.validate();
    },

    /**
     * Restores the cursor to its original position after updating the value.
     * @param {string} newValue The value that should be saved.
     */
    updateValueAndPreserveCaret: function (newValue) {
      // Not all elements might have selection, and even if they have the
      // right properties, accessing them might throw an exception (like for
      // <input type=number>)
      try {
        var start = this.inputElement.selectionStart;
        this.value = newValue; // The cursor automatically jumps to the end after re-setting the value,
        // so restore it to its original position.

        this.inputElement.selectionStart = start;
        this.inputElement.selectionEnd = start;
      } catch (e) {
        // Just set the value and give up on the caret.
        this.value = newValue;
      }
    },
    _computeAlwaysFloatLabel: function (alwaysFloatLabel, placeholder) {
      return placeholder || alwaysFloatLabel;
    },
    _updateAriaLabelledBy: function () {
      var label = dom(this.root).querySelector('label');

      if (!label) {
        this._ariaLabelledBy = '';
        return;
      }

      var labelledBy;

      if (label.id) {
        labelledBy = label.id;
      } else {
        labelledBy = 'paper-input-label-' + PaperInputHelper.NextLabelID++;
        label.id = labelledBy;
      }

      this._ariaLabelledBy = labelledBy;
    },
    _generateInputId: function () {
      if (!this._inputId || this._inputId === '') {
        this._inputId = 'input-' + PaperInputHelper.NextInputID++;
      }
    },
    _onChange: function (event) {
      // In the Shadow DOM, the `change` event is not leaked into the
      // ancestor tree, so we must do this manually.
      // See
      // https://w3c.github.io/webcomponents/spec/shadow/#events-that-are-not-leaked-into-ancestor-trees.
      if (this.shadowRoot) {
        this.fire(event.type, {
          sourceEvent: event
        }, {
          node: this,
          bubbles: event.bubbles,
          cancelable: event.cancelable
        });
      }
    },
    _autofocusChanged: function () {
      // Firefox doesn't respect the autofocus attribute if it's applied after
      // the page is loaded (Chrome/WebKit do respect it), preventing an
      // autofocus attribute specified in markup from taking effect when the
      // element is upgraded. As a workaround, if the autofocus property is set,
      // and the focus hasn't already been moved elsewhere, we take focus.
      if (this.autofocus && this._focusableElement) {
        // In IE 11, the default document.activeElement can be the page's
        // outermost html element, but there are also cases (under the
        // polyfill?) in which the activeElement is not a real HTMLElement, but
        // just a plain object. We identify the latter case as having no valid
        // activeElement.
        var activeElement = document.activeElement;
        var isActiveElementValid = activeElement instanceof HTMLElement; // Has some other element has already taken the focus?

        var isSomeElementActive = isActiveElementValid && activeElement !== document.body && activeElement !== document.documentElement;
        /* IE 11 */

        if (!isSomeElementActive) {
          // No specific element has taken the focus yet, so we can take it.
          this._focusableElement.focus();
        }
      }
    }
  };
  /** @polymerBehavior */

  const PaperInputBehavior = [IronControlState, IronA11yKeysBehavior, PaperInputBehaviorImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  Material design: [Text
  fields](https://www.google.com/design/spec/components/text-fields.html)

  `<paper-input>` is a single-line text field with Material Design styling.

      <paper-input label="Input label"></paper-input>

  It may include an optional error message or character counter.

      <paper-input error-message="Invalid input!" label="Input
      label"></paper-input> <paper-input char-counter label="Input
      label"></paper-input>

  It can also include custom prefix or suffix elements, which are displayed
  before or after the text input itself. In order for an element to be
  considered as a prefix, it must have the `prefix` attribute (and similarly
  for `suffix`).

      <paper-input label="total">
        <div prefix>$</div>
        <paper-icon-button slot="suffix" icon="clear"></paper-icon-button>
      </paper-input>

  A `paper-input` can use the native `type=search` or `type=file` features.
  However, since we can't control the native styling of the input (search icon,
  file button, date placeholder, etc.), in these cases the label will be
  automatically floated. The `placeholder` attribute can still be used for
  additional informational text.

      <paper-input label="search!" type="search"
          placeholder="search for cats" autosave="test" results="5">
      </paper-input>

  See `Polymer.PaperInputBehavior` for more API docs.

  ### Focus

  To focus a paper-input, you can call the native `focus()` method as long as the
  paper input has a tab index. Similarly, `blur()` will blur the element.

  ### Styling

  See `Polymer.PaperInputContainer` for a list of custom properties used to
  style this element.

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-input-container-ms-clear` | Mixin applied to the Internet Explorer reveal button (the eyeball) | {}

  @element paper-input
  @demo demo/index.html
  */

  Polymer({
    is: 'paper-input',

    /** @override */
    _template: html$1`
    <style>
      :host {
        display: block;
      }

      :host([focused]) {
        outline: none;
      }

      :host([hidden]) {
        display: none !important;
      }

      input {
        /* Firefox sets a min-width on the input, which can cause layout issues */
        min-width: 0;
      }

      /* In 1.x, the <input> is distributed to paper-input-container, which styles it.
      In 2.x the <iron-input> is distributed to paper-input-container, which styles
      it, but in order for this to work correctly, we need to reset some
      of the native input's properties to inherit (from the iron-input) */
      iron-input > input {
        @apply --paper-input-container-shared-input-style;
        font-family: inherit;
        font-weight: inherit;
        font-size: inherit;
        letter-spacing: inherit;
        word-spacing: inherit;
        line-height: inherit;
        text-shadow: inherit;
        color: inherit;
        cursor: inherit;
      }

      input:disabled {
        @apply --paper-input-container-input-disabled;
      }

      input::-webkit-outer-spin-button,
      input::-webkit-inner-spin-button {
        @apply --paper-input-container-input-webkit-spinner;
      }

      input::-webkit-clear-button {
        @apply --paper-input-container-input-webkit-clear;
      }

      input::-webkit-calendar-picker-indicator {
        @apply --paper-input-container-input-webkit-calendar-picker-indicator;
      }

      input::-webkit-input-placeholder {
        color: var(--paper-input-container-color, var(--secondary-text-color));
      }

      input:-moz-placeholder {
        color: var(--paper-input-container-color, var(--secondary-text-color));
      }

      input::-moz-placeholder {
        color: var(--paper-input-container-color, var(--secondary-text-color));
      }

      input::-ms-clear {
        @apply --paper-input-container-ms-clear;
      }

      input::-ms-reveal {
        @apply --paper-input-container-ms-reveal;
      }

      input:-ms-input-placeholder {
        color: var(--paper-input-container-color, var(--secondary-text-color));
      }

      label {
        pointer-events: none;
      }
    </style>

    <paper-input-container id="container" no-label-float="[[noLabelFloat]]" always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]" auto-validate$="[[autoValidate]]" disabled$="[[disabled]]" invalid="[[invalid]]">

      <slot name="prefix" slot="prefix"></slot>

      <label hidden$="[[!label]]" aria-hidden="true" for$="[[_inputId]]" slot="label">[[label]]</label>

      <!-- Need to bind maxlength so that the paper-input-char-counter works correctly -->
      <iron-input bind-value="{{value}}" slot="input" class="input-element" id$="[[_inputId]]" maxlength$="[[maxlength]]" allowed-pattern="[[allowedPattern]]" invalid="{{invalid}}" validator="[[validator]]">
        <input aria-labelledby$="[[_ariaLabelledBy]]" aria-describedby$="[[_ariaDescribedBy]]" disabled$="[[disabled]]" title$="[[title]]" type$="[[type]]" pattern$="[[pattern]]" required$="[[required]]" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" minlength$="[[minlength]]" maxlength$="[[maxlength]]" min$="[[min]]" max$="[[max]]" step$="[[step]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" list$="[[list]]" size$="[[size]]" autocapitalize$="[[autocapitalize]]" autocorrect$="[[autocorrect]]" on-change="_onChange" tabindex$="[[tabIndex]]" autosave$="[[autosave]]" results$="[[results]]" accept$="[[accept]]" multiple$="[[multiple]]" role$="[[inputRole]]" aria-haspopup$="[[inputAriaHaspopup]]">
      </iron-input>

      <slot name="suffix" slot="suffix"></slot>

      <template is="dom-if" if="[[errorMessage]]">
        <paper-input-error aria-live="assertive" slot="add-on">[[errorMessage]]</paper-input-error>
      </template>

      <template is="dom-if" if="[[charCounter]]">
        <paper-input-char-counter slot="add-on"></paper-input-char-counter>
      </template>

    </paper-input-container>
  `,
    behaviors: [PaperInputBehavior, IronFormElementBehavior],
    properties: {
      value: {
        // Required for the correct TypeScript type-generation
        type: String
      },
      inputRole: {
        type: String,
        value: undefined
      },
      inputAriaHaspopup: {
        type: String,
        value: undefined
      }
    },

    /**
     * Returns a reference to the focusable element. Overridden from
     * PaperInputBehavior to correctly focus the native input.
     *
     * @return {!HTMLElement}
     */
    get _focusableElement() {
      return this.inputElement._inputElement;
    },

    // Note: This event is only available in the 1.0 version of this element.
    // In 2.0, the functionality of `_onIronInputReady` is done in
    // PaperInputBehavior::attached.
    listeners: {
      'iron-input-ready': '_onIronInputReady'
    },
    _onIronInputReady: function () {
      // Even though this is only used in the next line, save this for
      // backwards compatibility, since the native input had this ID until 2.0.5.
      if (!this.$.nativeInput) {
        this.$.nativeInput =
        /** @type {!Element} */
        this.$$('input');
      }

      if (this.inputElement && this._typesThatHaveText.indexOf(this.$.nativeInput.type) !== -1) {
        this.alwaysFloatLabel = true;
      } // Only validate when attached if the input already has a value.


      if (!!this.inputElement.bindValue) {
        this.$.container._handleValueAndAutoValidate(this.inputElement);
      }
    }
  });

  let SearchInput = _decorate([customElement("search-input")], function (_initialize, _LitElement) {
    class SearchInput extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: SearchInput,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "filter",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          attribute: "no-label-float"
        })],
        key: "noLabelFloat",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean,
          attribute: "no-underline"
        })],
        key: "noUnderline",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "autofocus",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: String
        })],
        key: "label",
        value: void 0
      }, {
        kind: "method",
        key: "focus",
        value: function focus() {
          this.shadowRoot.querySelector("paper-input").focus();
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <style>
        .no-underline:not(.focused) {
          --paper-input-container-underline: {
            display: none;
            height: 0;
          }
        }
      </style>
      <paper-input
        class=${classMap({
          "no-underline": this.noUnderline
        })}
        .autofocus=${this.autofocus}
        .label=${this.label || "Search"}
        .value=${this.filter}
        @value-changed=${this._filterInputChanged}
        .noLabelFloat=${this.noLabelFloat}
      >
        <ha-svg-icon
          path=${mdiMagnify}
          slot="prefix"
          class="prefix"
        ></ha-svg-icon>
        ${this.filter && html`
          <mwc-icon-button
            slot="suffix"
            class="suffix"
            @click=${this._clearSearch}
            alt="Clear"
            title="Clear"
          >
            <ha-svg-icon path=${mdiClose}></ha-svg-icon>
          </mwc-icon-button>
        `}
      </paper-input>
    `;
        }
      }, {
        kind: "method",
        key: "_filterChanged",
        value: async function _filterChanged(value) {
          fireEvent(this, "value-changed", {
            value: String(value)
          });
        }
      }, {
        kind: "method",
        key: "_filterInputChanged",
        value: async function _filterInputChanged(e) {
          this._filterChanged(e.target.value);
        }
      }, {
        kind: "method",
        key: "_clearSearch",
        value: async function _clearSearch() {
          this._filterChanged("");
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      ha-svg-icon,
      mwc-icon-button {
        color: var(--primary-text-color);
      }
      mwc-icon-button {
        --mdc-icon-button-size: 24px;
      }
      ha-svg-icon.prefix {
        margin: 8px;
      }
    `;
        }
      }]
    };
  }, LitElement);

  const hacsIcon = "m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 -0.280572,0.153036 -0.605773,0.229553 -0.950094,0.229553 -0.0765,0 -0.146661,-0.0064 -0.216801,-0.01275 -0.605774,-0.05739 -1.135016,-0.344329 -1.402827,-0.7588 l 0.784304,-0.516495 c 0.0893,0.146659 0.344331,0.312448 0.707793,0.34433 0.235931,0.02551 0.471852,-0.01913 0.637643,-0.108401 0.101998,-0.05101 0.172171,-0.127529 0.17854,-0.191295 0.0065,-0.08289 -0.0255,-0.369835 -0.733293,-0.439975 -1.013854,-0.09565 -1.645127,-0.688661 -1.568606,-1.460214 0.0319,-0.382589 0.280561,-0.714165 0.663153,-0.930965 0.331571,-0.172165 0.752423,-0.25506 1.166895,-0.210424 0.599382,0.05739 1.128635,0.344329 1.402816,0.7588 l -0.784304,0.510118 c -0.0893,-0.140282 -0.344331,-0.299694 -0.707782,-0.331576 -0.235932,-0.02551 -0.471863,0.01913 -0.637654,0.10202 -0.0956,0.05739 -0.165791,0.133906 -0.17216,0.191295 -0.0255,0.293317 0.465482,0.420847 0.726913,0.439976 v 0.0064 c 1.020234,0.09565 1.638757,0.66953 1.562237,1.460213 z m -7.466854,-0.988354 c 0,-1.192401 0.962855,-2.155249 2.15525,-2.155249 0.599393,0 1.179645,0.25506 1.594117,0.707789 l -0.695033,0.624895 c -0.235931,-0.25506 -0.561133,-0.401718 -0.899084,-0.401718 -0.675903,0 -1.217906,0.542 -1.217906,1.217906 0,0.66953 0.542003,1.217908 1.217906,1.217908 0.337951,0 0.663153,-0.140283 0.899084,-0.401718 l 0.695033,0.631271 c -0.414472,0.452729 -0.988355,0.707788 -1.594117,0.707788 -1.192395,0 -2.15525,-0.969224 -2.15525,-2.148872 z M 8.6573365,23.461054 10.353474,19.14418 h 0.624893 l 1.568618,4.316874 H 11.52037 L 11.265308,22.734136 H 9.964513 l -0.274192,0.726918 z m 1.6833885,-1.68339 h 0.580263 L 10.646796,21.012487 Z M 8.1089536,19.156932 v 4.297745 H 7.1461095 v -1.645131 h -1.606867 v 1.645131 H 4.5763876 v -4.297745 h 0.9628549 v 1.696143 h 1.606867 V 19.156932 Z M 20.115859,4.2997436 C 20.090359,4.159461 19.969198,4.0574375 19.822548,4.0574375 H 14.141102 10.506516 4.8250686 c -0.14665,0 -0.2678112,0.1020202 -0.2933108,0.2423061 L 3.690064,8.8461703 c -0.00651,0.01913 -0.00651,0.03826 -0.00651,0.057391 v 1.5239797 c 0,0.165789 0.133911,0.299694 0.2996911,0.299694 H 4.5762579 20.0711 20.664112 c 0.165781,0 0.299691,-0.133905 0.299691,-0.299694 V 8.8971848 c 0,-0.01913 0,-0.03826 -0.0065,-0.05739 z M 4.5763876,17.358767 c 0,0.184917 0.1466608,0.331577 0.3315819,0.331577 h 5.5985465 3.634586 0.924594 c 0.184911,0 0.331571,-0.14666 0.331571,-0.331577 v -4.744098 c 0,-0.184918 0.146661,-0.331577 0.331582,-0.331577 h 2.894913 c 0.184921,0 0.331582,0.146659 0.331582,0.331577 v 4.744098 c 0,0.184917 0.146661,0.331577 0.331571,0.331577 h 0.446363 c 0.18491,0 0.331571,-0.14666 0.331571,-0.331577 v -5.636804 c 0,-0.184918 -0.146661,-0.331577 -0.331571,-0.331577 H 4.9079695 c -0.1849211,0 -0.3315819,0.146659 -0.3315819,0.331577 z m 1.6578879,-4.852498 h 5.6495565 c 0.15303,0 0.280561,0.12753 0.280561,0.280564 v 3.513438 c 0,0.153036 -0.127531,0.280566 -0.280561,0.280566 H 6.2342755 c -0.1530412,0 -0.2805719,-0.12753 -0.2805719,-0.280566 v -3.513438 c 0,-0.159411 0.1275307,-0.280564 0.2805719,-0.280564 z M 19.790657,3.3879075 H 4.8569594 c -0.1530412,0 -0.2805718,-0.1275296 -0.2805718,-0.2805642 V 1.3665653 C 4.5763876,1.2135296 4.7039182,1.086 4.8569594,1.086 H 19.790657 c 0.153041,0 0.280572,0.1275296 0.280572,0.2805653 v 1.740778 c 0,0.1530346 -0.127531,0.2805642 -0.280572,0.2805642 z";

  let HacsIconButton = _decorate([customElement("hacs-icon-button")], function (_initialize, _LitElement) {
    class HacsIconButton extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsIconButton,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "disabled",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: String
        })],
        key: "icon",

        value() {
          return "";
        }

      }, {
        kind: "method",
        key: "createRenderRoot",
        value: function createRenderRoot() {
          return this.attachShadow({
            mode: "open",
            delegatesFocus: true
          });
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <mwc-icon-button .disabled=${this.disabled}>
        <ha-svg-icon .path=${this.icon}></ha-svg-icon>
      </mwc-icon-button>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: inline-block;
        outline: none;
      }
      :host([disabled]) {
        pointer-events: none;
      }
      mwc-icon-button {
        --mdc-theme-on-primary: currentColor;
        --mdc-theme-text-disabled-on-light: var(--disabled-text-color);
      }
      ha-svg-icon {
        --ha-icon-display: inline;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HacsRepositoryCard = _decorate([customElement("hacs-repository-card")], function (_initialize, _LitElement) {
    class HacsRepositoryCard extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsRepositoryCard,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "removed",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "addedToLovelace",
        value: void 0
      }, {
        kind: "get",
        key: "_borderClass",
        value: function _borderClass() {
          const classes = {};

          if (!this.addedToLovelace || this.repository.status === "pending-restart") {
            classes["status-issue"] = true;
          } else if (this.repository.pending_upgrade) {
            classes["status-update"] = true;
          } else if (this.repository.new && !this.repository.installed) {
            classes["status-new"] = true;
          }

          if (Object.keys(classes).length !== 0) {
            classes["status-border"] = true;
          }

          return classes;
        }
      }, {
        kind: "get",
        key: "_headerClass",
        value: function _headerClass() {
          const classes = {};

          if (!this.addedToLovelace || this.repository.status === "pending-restart") {
            classes["issue-header"] = true;
          } else if (this.repository.pending_upgrade) {
            classes["update-header"] = true;
          } else if (this.repository.new && !this.repository.installed) {
            classes["new-header"] = true;
          } else {
            classes["default-header"] = true;
          }

          return classes;
        }
      }, {
        kind: "get",
        key: "_headerTitle",
        value: function _headerTitle() {
          if (!this.addedToLovelace) {
            return localize("repository_card.not_loaded");
          }

          if (this.repository.status === "pending-restart") {
            return localize("repository_card.pending_restart");
          }

          if (this.repository.pending_upgrade) {
            return localize("repository_card.pending_update");
          }

          if (this.repository.new && !this.repository.installed) {
            return localize("repository_card.new_repository");
          }

          return "";
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          const path = this.repository.local_path.split("/");
          return html`
      <ha-card class=${classMap(this._borderClass)} ?narrow=${this.narrow}>
        <div class="card-content">
          <div class="group-header">
            <div class="status-header ${classMap(this._headerClass)}">
              ${this._headerTitle}
            </div>
            <div class="title">
              <h1 class="pointer" @click=${this._showReopsitoryInfo}>
                ${this.repository.name}
              </h1>
              ${this.repository.category !== "integration" ? html` <hacs-chip
                    .icon=${hacsIcon}
                    .value=${localize(`common.${this.repository.category}`)}
                  ></hacs-chip>` : ""}
            </div>
          </div>
          <paper-item>
            <paper-item-body>${this.repository.description}</paper-item-body></paper-item
          >
        </div>
        <div class="card-actions">
          ${this.repository.new && !this.repository.installed ? html`<div>
                  <mwc-button @click=${this._installRepository}
                    >${localize("common.install")}</mwc-button
                  >
                </div>
                <div>
                  <mwc-button @click=${this._showReopsitoryInfo}
                    >${localize("repository_card.information")}</mwc-button
                  >
                </div>
                <div>
                  <hacs-link .url="https://github.com/${this.repository.full_name}"
                    ><mwc-button>${localize("common.repository")}</mwc-button></hacs-link
                  >
                </div>
                <div>
                  <mwc-button @click=${this._setNotNew}
                    >${localize("repository_card.dismiss")}</mwc-button
                  >
                </div>` : this.repository.pending_upgrade && this.addedToLovelace ? html`<div>
                  <mwc-button class="update-header" @click=${this._updateRepository} raised
                    >${localize("common.update")}</mwc-button
                  >
                </div>
                <div>
                  <hacs-link .url="https://github.com/${this.repository.full_name}"
                    ><mwc-button>${localize("common.repository")}</mwc-button></hacs-link
                  >
                </div>` : html`<div>
                <hacs-link .url="https://github.com/${this.repository.full_name}"
                  ><mwc-button>${localize("common.repository")}</mwc-button></hacs-link
                >
              </div>`}
          ${this.repository.installed ? html` <paper-menu-button
                horizontal-align="right"
                vertical-align="top"
                vertical-offset="40"
                close-on-activate
              >
                <hacs-icon-button
                  .icon=${mdiDotsVertical}
                  slot="dropdown-trigger"
                ></hacs-icon-button>
                <paper-listbox slot="dropdown-content">
                  <paper-item class="pointer" @tap=${this._showReopsitoryInfo}
                    >${localize("repository_card.information")}</paper-item
                  >

                  <paper-item class="pointer" @tap=${this._updateReopsitoryInfo}
                    >${localize("repository_card.update_information")}</paper-item
                  >

                  <paper-item @tap=${this._installRepository}
                    >${localize("repository_card.reinstall")}</paper-item
                  >

                  ${this.repository.category === "plugin" ? html`<hacs-link
                        .url="/hacsfiles/${path.pop()}/${this.repository.file_name}"
                        newtab
                        ><paper-item class="pointer"
                          >${localize("repository_card.open_source")}</paper-item
                        ></hacs-link
                      >` : ""}

                  <hacs-link .url="https://github.com/${this.repository.full_name}/issues"
                    ><paper-item class="pointer"
                      >${localize("repository_card.open_issue")}</paper-item
                    ></hacs-link
                  >

                  ${String(this.repository.id) !== "172733314" ? html`<hacs-link
                          .url="https://github.com/hacs/integration/issues/new?assignees=ludeeus&labels=flag&template=flag.md&title=${this.repository.full_name}"
                          ><paper-item class="pointer uninstall"
                            >${localize("repository_card.report")}</paper-item
                          ></hacs-link
                        >
                        <paper-item class="pointer uninstall" @tap=${this._uninstallRepository}
                          >${localize("common.uninstall")}</paper-item
                        >` : ""}
                </paper-listbox>
              </paper-menu-button>` : ""}
        </div>
      </ha-card>
    `;
        }
      }, {
        kind: "method",
        key: "_updateReopsitoryInfo",
        value: async function _updateReopsitoryInfo() {
          await repositoryUpdate(this.hass, this.repository.id);
        }
      }, {
        kind: "method",
        key: "_showReopsitoryInfo",
        value: async function _showReopsitoryInfo() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "repository-info",
              repository: this.repository.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_lovelaceUrl",
        value: function _lovelaceUrl() {
          var _this$repository, _this$repository2;

          return `/hacsfiles/${(_this$repository = this.repository) === null || _this$repository === void 0 ? void 0 : _this$repository.full_name.split("/")[1]}/${(_this$repository2 = this.repository) === null || _this$repository2 === void 0 ? void 0 : _this$repository2.file_name}`;
        }
      }, {
        kind: "method",
        key: "_updateRepository",
        value: async function _updateRepository() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "update",
              repository: this.repository.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_setNotNew",
        value: async function _setNotNew() {
          await repositorySetNotNew(this.hass, this.repository.id);
        }
      }, {
        kind: "method",
        key: "_installRepository",
        value: function _installRepository() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "install",
              repository: this.repository.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_uninstallRepository",
        value: async function _uninstallRepository() {
          if (this.repository.category === "plugin" && this.hacs.status.lovelace_mode !== "yaml") {
            const resources = await fetchResources(this.hass);
            resources.filter(resource => resource.url === this._lovelaceUrl()).forEach(resource => {
              deleteResource(this.hass, String(resource.id));
            });
          }

          await repositoryUninstall(this.hass, this.repository.id);
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [HacsStyles, css`
        ha-card {
          display: flex;
          flex-direction: column;
          height: 100%;
          width: 480px;
        }

        hacs-chip {
          margin: 8px 4px 0 0;
        }
        .title {
          display: flex;
          justify-content: space-between;
        }
        .card-content {
          padding: 0 0 3px 0;
          height: 100%;
        }
        .card-actions {
          border-top: none;
          bottom: 0;
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 5px;
        }
        .group-header {
          height: auto;
          align-content: center;
        }
        .group-header h1 {
          margin: 0;
          padding: 8px 16px;
        }
        h1 {
          margin-top: 0;
          min-height: 24px;
        }
        paper-menu-button {
          padding: 0;
          float: right;
        }

        .pointer {
          cursor: pointer;
        }
        paper-item-body {
          opacity: var(--dark-primary-opacity);
        }

        .status-new {
          border-color: var(--hcv-color-new);
        }

        .status-update {
          border-color: var(--hcv-color-update);
        }

        .status-issue {
          border-color: var(--hcv-color-error);
        }

        .new-header {
          background-color: var(--hcv-color-new);
          color: var(--hcv-text-color-on-background);
          border-radius: var(--ha-card-border-radius, 4px);
        }

        .issue-header {
          background-color: var(--hcv-color-error);
          color: var(--hcv-text-color-on-background);
        }

        .update-header {
          background-color: var(--hcv-color-update);
          color: var(--hcv-text-color-on-background);
        }

        .default-header {
          padding: 10px 0 !important;
        }

        mwc-button.update-header {
          --mdc-theme-primary: var(--hcv-color-update);
          --mdc-theme-on-primary: var(--hcv-text-color-on-background);
        }

        .status-border {
          border-style: solid;
          border-width: 1px;
        }

        .status-header {
          top: 0;
          padding: 6px 1px;
          margin: -1px;
          width: 100%;
          font-weight: 300;
          text-align: center;
          left: 0;
          border-top-left-radius: var(--ha-card-border-radius);
          border-top-right-radius: var(--ha-card-border-radius);
        }

        ha-card[narrow] {
          width: calc(100% - 24px);
          margin: 11px;
        }
        hacs-icon-button {
          color: var(--accent-color);
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HacsCheckbox = _decorate([customElement("hacs-checkbox")], function (_initialize, _LitElement) {
    class HacsCheckbox extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsCheckbox,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "checked",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "label",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "id",
        value: void 0
      }, {
        kind: "get",
        key: "_checkboxClass",
        value: function _checkboxClass() {
          return {
            'checkbox': true,
            'checked': this.checked
          };
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <div class="checkbox-container">
        <div class=${classMap(this._checkboxClass)} @click=${this._checkboxClicked}>
          <div class="value">${this.checked ? "✔" : ""}</div>
        </div>
        <div class="label" @click=${this._checkboxClicked}>
          ${this.label}
        </div>
      </div>
    `;
        }
      }, {
        kind: "method",
        key: "_checkboxClicked",
        value: function _checkboxClicked() {
          this.checked = !this.checked;
          this.dispatchEvent(new CustomEvent("checkbox-change", {
            detail: {
              id: this.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [HacsStyles, css`
        .checkbox-container {
          display: flex;
          color: var(--hcv-text-color-primary);
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
        }

        .label {
          line-height: 18px;
          opacity: var(--dark-primary-opacity);
          font-family: var(--paper-font-subhead_-_font-family);
          -webkit-font-smoothing: var(--paper-font-subhead_-_-webkit-font-smoothing);
          font-size: var(--paper-font-subhead_-_font-size);
          cursor: pointer;
        }

        .value {
          margin-left: 2px;
          color: var(--hcv-text-color-on-background);
        }

        .checkbox {
          cursor: pointer;
          height: 16px;
          width: 16px;
          font-size: 14px;
          margin-right: 8px;
          background-color: var(--primary-background-color);
          border: 1px solid var(--divider-color);
          border-radius: 4px;
          line-height: 16px;
        }

        .checkbox.checked {
          border-color: var(--accent-color);
          background-color: var(--accent-color);
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HacsFilter = _decorate([customElement("hacs-filter")], function (_initialize, _LitElement) {
    class HacsFilter extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsFilter,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "filters",
        value: void 0
      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          this.addEventListener("checkbox-change", e => this._filterClick(e));
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$filters;

          return html`
      <div class="filter">
        ${(_this$filters = this.filters) === null || _this$filters === void 0 ? void 0 : _this$filters.map(filter => html`
            <hacs-checkbox
              class="checkbox"
              .label=${localize(`common.${filter.id}`) || filter.value}
              .id=${filter.id}
              .checked=${filter.checked || false}
            />
            </hacs-checkbox>`)}
      </div>
    `;
        }
      }, {
        kind: "method",
        key: "_filterClick",
        value: function _filterClick(ev) {
          const filter = ev.detail;
          this.dispatchEvent(new CustomEvent("filter-change", {
            detail: {
              id: filter.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [HacsStyles, css`
        .filter {
          display: flex;
          border-bottom: 1px solid var(--divider-color);
          align-items: center;
          font-size: 16px;
          height: 32px;
          line-height: 4px;
          background-color: var(--sidebar-background-color);
          padding: 0 16px;
          box-sizing: border-box;
        }

        .checkbox:not(:first-child) {
          margin-left: 20px;
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HacsFab = _decorate([customElement("hacs-fab")], function (_initialize, _LitElement) {
    class HacsFab extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsFab,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "icon",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <div class="fab" ?narrow=${this.narrow}>
        <div><ha-icon .icon=${this.icon}></ha-icon></div>
      </div>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [css`
        .fab {
          position: fixed;
          bottom: 16px;
          right: 16px;
          z-index: 1;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          box-sizing: border-box;
          width: 56px;
          height: 56px;
          cursor: pointer;
          user-select: none;
          -webkit-appearance: none;
          background-color: var(--hcv-color-fab);
          -webkit-box-shadow: 2px 2px 8px 1px rgba(0, 0, 0, 0.3);
          -moz-box-shadow: 2px 2px 8px 1px rgba(0, 0, 0, 0.3);
          box-shadow: 2px 2px 8px 1px rgba(0, 0, 0, 0.3);
          border-radius: 50%;
        }
        .fab[narrow] {
          margin-bottom: 65px;
        }
        ha-icon {
          margin: auto;
          color: var(--hcv-text-color-fab);
          height: 100%;
          width: 100%;
          --mdc-icon-size: 32px;
        }
      `];
        }
      }]
    };
  }, LitElement);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  `Polymer.IronFitBehavior` fits an element in another element using `max-height`
  and `max-width`, and optionally centers it in the window or another element.

  The element will only be sized and/or positioned if it has not already been
  sized and/or positioned by CSS.

  CSS properties            | Action
  --------------------------|-------------------------------------------
  `position` set            | Element is not centered horizontally or vertically
  `top` or `bottom` set     | Element is not vertically centered
  `left` or `right` set     | Element is not horizontally centered
  `max-height` set          | Element respects `max-height`
  `max-width` set           | Element respects `max-width`

  `Polymer.IronFitBehavior` can position an element into another element using
  `verticalAlign` and `horizontalAlign`. This will override the element's css
  position.

      <div class="container">
        <iron-fit-impl vertical-align="top" horizontal-align="auto">
          Positioned into the container
        </iron-fit-impl>
      </div>

  Use `noOverlap` to position the element around another element without
  overlapping it.

      <div class="container">
        <iron-fit-impl no-overlap vertical-align="auto" horizontal-align="auto">
          Positioned around the container
        </iron-fit-impl>
      </div>

  Use `horizontalOffset, verticalOffset` to offset the element from its
  `positionTarget`; `Polymer.IronFitBehavior` will collapse these in order to
  keep the element within `fitInto` boundaries, while preserving the element's
  CSS margin values.

      <div class="container">
        <iron-fit-impl vertical-align="top" vertical-offset="20">
          With vertical offset
        </iron-fit-impl>
      </div>

  @demo demo/index.html
  @polymerBehavior
  */

  const IronFitBehavior = {
    properties: {
      /**
       * The element that will receive a `max-height`/`width`. By default it is
       * the same as `this`, but it can be set to a child element. This is useful,
       * for example, for implementing a scrolling region inside the element.
       * @type {!Element}
       */
      sizingTarget: {
        type: Object,
        value: function () {
          return this;
        }
      },

      /**
       * The element to fit `this` into.
       */
      fitInto: {
        type: Object,
        value: window
      },

      /**
       * Will position the element around the positionTarget without overlapping
       * it.
       */
      noOverlap: {
        type: Boolean
      },

      /**
       * The element that should be used to position the element. If not set, it
       * will default to the parent node.
       * @type {!Element}
       */
      positionTarget: {
        type: Element
      },

      /**
       * The orientation against which to align the element horizontally
       * relative to the `positionTarget`. Possible values are "left", "right",
       * "center", "auto".
       */
      horizontalAlign: {
        type: String
      },

      /**
       * The orientation against which to align the element vertically
       * relative to the `positionTarget`. Possible values are "top", "bottom",
       * "middle", "auto".
       */
      verticalAlign: {
        type: String
      },

      /**
       * If true, it will use `horizontalAlign` and `verticalAlign` values as
       * preferred alignment and if there's not enough space, it will pick the
       * values which minimize the cropping.
       */
      dynamicAlign: {
        type: Boolean
      },

      /**
       * A pixel value that will be added to the position calculated for the
       * given `horizontalAlign`, in the direction of alignment. You can think
       * of it as increasing or decreasing the distance to the side of the
       * screen given by `horizontalAlign`.
       *
       * If `horizontalAlign` is "left" or "center", this offset will increase or
       * decrease the distance to the left side of the screen: a negative offset
       * will move the dropdown to the left; a positive one, to the right.
       *
       * Conversely if `horizontalAlign` is "right", this offset will increase
       * or decrease the distance to the right side of the screen: a negative
       * offset will move the dropdown to the right; a positive one, to the left.
       */
      horizontalOffset: {
        type: Number,
        value: 0,
        notify: true
      },

      /**
       * A pixel value that will be added to the position calculated for the
       * given `verticalAlign`, in the direction of alignment. You can think
       * of it as increasing or decreasing the distance to the side of the
       * screen given by `verticalAlign`.
       *
       * If `verticalAlign` is "top" or "middle", this offset will increase or
       * decrease the distance to the top side of the screen: a negative offset
       * will move the dropdown upwards; a positive one, downwards.
       *
       * Conversely if `verticalAlign` is "bottom", this offset will increase
       * or decrease the distance to the bottom side of the screen: a negative
       * offset will move the dropdown downwards; a positive one, upwards.
       */
      verticalOffset: {
        type: Number,
        value: 0,
        notify: true
      },

      /**
       * Set to true to auto-fit on attach.
       */
      autoFitOnAttach: {
        type: Boolean,
        value: false
      },

      /** @type {?Object} */
      _fitInfo: {
        type: Object
      }
    },

    get _fitWidth() {
      var fitWidth;

      if (this.fitInto === window) {
        fitWidth = this.fitInto.innerWidth;
      } else {
        fitWidth = this.fitInto.getBoundingClientRect().width;
      }

      return fitWidth;
    },

    get _fitHeight() {
      var fitHeight;

      if (this.fitInto === window) {
        fitHeight = this.fitInto.innerHeight;
      } else {
        fitHeight = this.fitInto.getBoundingClientRect().height;
      }

      return fitHeight;
    },

    get _fitLeft() {
      var fitLeft;

      if (this.fitInto === window) {
        fitLeft = 0;
      } else {
        fitLeft = this.fitInto.getBoundingClientRect().left;
      }

      return fitLeft;
    },

    get _fitTop() {
      var fitTop;

      if (this.fitInto === window) {
        fitTop = 0;
      } else {
        fitTop = this.fitInto.getBoundingClientRect().top;
      }

      return fitTop;
    },

    /**
     * The element that should be used to position the element,
     * if no position target is configured.
     */
    get _defaultPositionTarget() {
      var parent = dom(this).parentNode;

      if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
        parent = parent.host;
      }

      return parent;
    },

    /**
     * The horizontal align value, accounting for the RTL/LTR text direction.
     */
    get _localeHorizontalAlign() {
      if (this._isRTL) {
        // In RTL, "left" becomes "right".
        if (this.horizontalAlign === 'right') {
          return 'left';
        }

        if (this.horizontalAlign === 'left') {
          return 'right';
        }
      }

      return this.horizontalAlign;
    },

    /**
     * True if the element should be positioned instead of centered.
     * @private
     */
    get __shouldPosition() {
      return (this.horizontalAlign || this.verticalAlign) && this.positionTarget;
    },

    /**
     * True if the component is RTL.
     * @private
     */
    get _isRTL() {
      // Memoize this to avoid expensive calculations & relayouts.
      // Make sure we do it only once
      if (typeof this._memoizedIsRTL === 'undefined') {
        this._memoizedIsRTL = window.getComputedStyle(this).direction == 'rtl';
      }

      return this._memoizedIsRTL;
    },

    /** @override */
    attached: function () {
      this.positionTarget = this.positionTarget || this._defaultPositionTarget;

      if (this.autoFitOnAttach) {
        if (window.getComputedStyle(this).display === 'none') {
          setTimeout(function () {
            this.fit();
          }.bind(this));
        } else {
          // NOTE: shadydom applies distribution asynchronously
          // for performance reasons webcomponents/shadydom#120
          // Flush to get correct layout info.
          window.ShadyDOM && ShadyDOM.flush();
          this.fit();
        }
      }
    },

    /** @override */
    detached: function () {
      if (this.__deferredFit) {
        clearTimeout(this.__deferredFit);
        this.__deferredFit = null;
      }
    },

    /**
     * Positions and fits the element into the `fitInto` element.
     */
    fit: function () {
      this.position();
      this.constrain();
      this.center();
    },

    /**
     * Memoize information needed to position and size the target element.
     * @suppress {deprecated}
     */
    _discoverInfo: function () {
      if (this._fitInfo) {
        return;
      }

      var target = window.getComputedStyle(this);
      var sizer = window.getComputedStyle(this.sizingTarget);
      this._fitInfo = {
        inlineStyle: {
          top: this.style.top || '',
          left: this.style.left || '',
          position: this.style.position || ''
        },
        sizerInlineStyle: {
          maxWidth: this.sizingTarget.style.maxWidth || '',
          maxHeight: this.sizingTarget.style.maxHeight || '',
          boxSizing: this.sizingTarget.style.boxSizing || ''
        },
        positionedBy: {
          vertically: target.top !== 'auto' ? 'top' : target.bottom !== 'auto' ? 'bottom' : null,
          horizontally: target.left !== 'auto' ? 'left' : target.right !== 'auto' ? 'right' : null
        },
        sizedBy: {
          height: sizer.maxHeight !== 'none',
          width: sizer.maxWidth !== 'none',
          minWidth: parseInt(sizer.minWidth, 10) || 0,
          minHeight: parseInt(sizer.minHeight, 10) || 0
        },
        margin: {
          top: parseInt(target.marginTop, 10) || 0,
          right: parseInt(target.marginRight, 10) || 0,
          bottom: parseInt(target.marginBottom, 10) || 0,
          left: parseInt(target.marginLeft, 10) || 0
        }
      };
    },

    /**
     * Resets the target element's position and size constraints, and clear
     * the memoized data.
     */
    resetFit: function () {
      var info = this._fitInfo || {};

      for (var property in info.sizerInlineStyle) {
        this.sizingTarget.style[property] = info.sizerInlineStyle[property];
      }

      for (var property in info.inlineStyle) {
        this.style[property] = info.inlineStyle[property];
      }

      this._fitInfo = null;
    },

    /**
     * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
     * the element or the `fitInto` element has been resized, or if any of the
     * positioning properties (e.g. `horizontalAlign, verticalAlign`) is updated.
     * It preserves the scroll position of the sizingTarget.
     */
    refit: function () {
      var scrollLeft = this.sizingTarget.scrollLeft;
      var scrollTop = this.sizingTarget.scrollTop;
      this.resetFit();
      this.fit();
      this.sizingTarget.scrollLeft = scrollLeft;
      this.sizingTarget.scrollTop = scrollTop;
    },

    /**
     * Positions the element according to `horizontalAlign, verticalAlign`.
     */
    position: function () {
      if (!this.__shouldPosition) {
        // needs to be centered, and it is done after constrain.
        return;
      }

      this._discoverInfo();

      this.style.position = 'fixed'; // Need border-box for margin/padding.

      this.sizingTarget.style.boxSizing = 'border-box'; // Set to 0, 0 in order to discover any offset caused by parent stacking
      // contexts.

      this.style.left = '0px';
      this.style.top = '0px';
      var rect = this.getBoundingClientRect();

      var positionRect = this.__getNormalizedRect(this.positionTarget);

      var fitRect = this.__getNormalizedRect(this.fitInto);

      var margin = this._fitInfo.margin; // Consider the margin as part of the size for position calculations.

      var size = {
        width: rect.width + margin.left + margin.right,
        height: rect.height + margin.top + margin.bottom
      };

      var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, rect, positionRect, fitRect);

      var left = position.left + margin.left;
      var top = position.top + margin.top; // We first limit right/bottom within fitInto respecting the margin,
      // then use those values to limit top/left.

      var right = Math.min(fitRect.right - margin.right, left + rect.width);
      var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height); // Keep left/top within fitInto respecting the margin.

      left = Math.max(fitRect.left + margin.left, Math.min(left, right - this._fitInfo.sizedBy.minWidth));
      top = Math.max(fitRect.top + margin.top, Math.min(top, bottom - this._fitInfo.sizedBy.minHeight)); // Use right/bottom to set maxWidth/maxHeight, and respect
      // minWidth/minHeight.

      this.sizingTarget.style.maxWidth = Math.max(right - left, this._fitInfo.sizedBy.minWidth) + 'px';
      this.sizingTarget.style.maxHeight = Math.max(bottom - top, this._fitInfo.sizedBy.minHeight) + 'px'; // Remove the offset caused by any stacking context.

      this.style.left = left - rect.left + 'px';
      this.style.top = top - rect.top + 'px';
    },

    /**
     * Constrains the size of the element to `fitInto` by setting `max-height`
     * and/or `max-width`.
     */
    constrain: function () {
      if (this.__shouldPosition) {
        return;
      }

      this._discoverInfo();

      var info = this._fitInfo; // position at (0px, 0px) if not already positioned, so we can measure the
      // natural size.

      if (!info.positionedBy.vertically) {
        this.style.position = 'fixed';
        this.style.top = '0px';
      }

      if (!info.positionedBy.horizontally) {
        this.style.position = 'fixed';
        this.style.left = '0px';
      } // need border-box for margin/padding


      this.sizingTarget.style.boxSizing = 'border-box'; // constrain the width and height if not already set

      var rect = this.getBoundingClientRect();

      if (!info.sizedBy.height) {
        this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
      }

      if (!info.sizedBy.width) {
        this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width');
      }
    },

    /**
     * @protected
     * @deprecated
     */
    _sizeDimension: function (rect, positionedBy, start, end, extent) {
      this.__sizeDimension(rect, positionedBy, start, end, extent);
    },

    /**
     * @private
     */
    __sizeDimension: function (rect, positionedBy, start, end, extent) {
      var info = this._fitInfo;

      var fitRect = this.__getNormalizedRect(this.fitInto);

      var max = extent === 'Width' ? fitRect.width : fitRect.height;
      var flip = positionedBy === end;
      var offset = flip ? max - rect[end] : rect[start];
      var margin = info.margin[flip ? start : end];
      var offsetExtent = 'offset' + extent;
      var sizingOffset = this[offsetExtent] - this.sizingTarget[offsetExtent];
      this.sizingTarget.style['max' + extent] = max - margin - offset - sizingOffset + 'px';
    },

    /**
     * Centers horizontally and vertically if not already positioned. This also
     * sets `position:fixed`.
     */
    center: function () {
      if (this.__shouldPosition) {
        return;
      }

      this._discoverInfo();

      var positionedBy = this._fitInfo.positionedBy;

      if (positionedBy.vertically && positionedBy.horizontally) {
        // Already positioned.
        return;
      } // Need position:fixed to center


      this.style.position = 'fixed'; // Take into account the offset caused by parents that create stacking
      // contexts (e.g. with transform: translate3d). Translate to 0,0 and
      // measure the bounding rect.

      if (!positionedBy.vertically) {
        this.style.top = '0px';
      }

      if (!positionedBy.horizontally) {
        this.style.left = '0px';
      } // It will take in consideration margins and transforms


      var rect = this.getBoundingClientRect();

      var fitRect = this.__getNormalizedRect(this.fitInto);

      if (!positionedBy.vertically) {
        var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;
        this.style.top = top + 'px';
      }

      if (!positionedBy.horizontally) {
        var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;
        this.style.left = left + 'px';
      }
    },
    __getNormalizedRect: function (target) {
      if (target === document.documentElement || target === window) {
        return {
          top: 0,
          left: 0,
          width: window.innerWidth,
          height: window.innerHeight,
          right: window.innerWidth,
          bottom: window.innerHeight
        };
      }

      return target.getBoundingClientRect();
    },
    __getOffscreenArea: function (position, size, fitRect) {
      var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height));
      var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width));
      return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height;
    },
    __getPosition: function (hAlign, vAlign, size, sizeNoMargins, positionRect, fitRect) {
      // All the possible configurations.
      // Ordered as top-left, top-right, bottom-left, bottom-right.
      var positions = [{
        verticalAlign: 'top',
        horizontalAlign: 'left',
        top: positionRect.top + this.verticalOffset,
        left: positionRect.left + this.horizontalOffset
      }, {
        verticalAlign: 'top',
        horizontalAlign: 'right',
        top: positionRect.top + this.verticalOffset,
        left: positionRect.right - size.width - this.horizontalOffset
      }, {
        verticalAlign: 'bottom',
        horizontalAlign: 'left',
        top: positionRect.bottom - size.height - this.verticalOffset,
        left: positionRect.left + this.horizontalOffset
      }, {
        verticalAlign: 'bottom',
        horizontalAlign: 'right',
        top: positionRect.bottom - size.height - this.verticalOffset,
        left: positionRect.right - size.width - this.horizontalOffset
      }];

      if (this.noOverlap) {
        // Duplicate.
        for (var i = 0, l = positions.length; i < l; i++) {
          var copy = {};

          for (var key in positions[i]) {
            copy[key] = positions[i][key];
          }

          positions.push(copy);
        } // Horizontal overlap only.


        positions[0].top = positions[1].top += positionRect.height;
        positions[2].top = positions[3].top -= positionRect.height; // Vertical overlap only.

        positions[4].left = positions[6].left += positionRect.width;
        positions[5].left = positions[7].left -= positionRect.width;
      } // Consider auto as null for coding convenience.


      vAlign = vAlign === 'auto' ? null : vAlign;
      hAlign = hAlign === 'auto' ? null : hAlign;

      if (!hAlign || hAlign === 'center') {
        positions.push({
          verticalAlign: 'top',
          horizontalAlign: 'center',
          top: positionRect.top + this.verticalOffset + (this.noOverlap ? positionRect.height : 0),
          left: positionRect.left - sizeNoMargins.width / 2 + positionRect.width / 2 + this.horizontalOffset
        });
        positions.push({
          verticalAlign: 'bottom',
          horizontalAlign: 'center',
          top: positionRect.bottom - size.height - this.verticalOffset - (this.noOverlap ? positionRect.height : 0),
          left: positionRect.left - sizeNoMargins.width / 2 + positionRect.width / 2 + this.horizontalOffset
        });
      }

      if (!vAlign || vAlign === 'middle') {
        positions.push({
          verticalAlign: 'middle',
          horizontalAlign: 'left',
          top: positionRect.top - sizeNoMargins.height / 2 + positionRect.height / 2 + this.verticalOffset,
          left: positionRect.left + this.horizontalOffset + (this.noOverlap ? positionRect.width : 0)
        });
        positions.push({
          verticalAlign: 'middle',
          horizontalAlign: 'right',
          top: positionRect.top - sizeNoMargins.height / 2 + positionRect.height / 2 + this.verticalOffset,
          left: positionRect.right - size.width - this.horizontalOffset - (this.noOverlap ? positionRect.width : 0)
        });
      }

      if (vAlign === 'middle' && hAlign === 'center') {
        positions.push({
          verticalAlign: 'middle',
          horizontalAlign: 'center',
          top: positionRect.top - sizeNoMargins.height / 2 + positionRect.height / 2 + this.verticalOffset,
          left: positionRect.left - sizeNoMargins.width / 2 + positionRect.width / 2 + this.horizontalOffset
        });
      }

      var position;

      for (var i = 0; i < positions.length; i++) {
        var candidate = positions[i];
        var vAlignOk = candidate.verticalAlign === vAlign;
        var hAlignOk = candidate.horizontalAlign === hAlign; // If both vAlign and hAlign are defined, return exact match.
        // For dynamicAlign and noOverlap we'll have more than one candidate, so
        // we'll have to check the offscreenArea to make the best choice.

        if (!this.dynamicAlign && !this.noOverlap && vAlignOk && hAlignOk) {
          position = candidate;
          break;
        } // Align is ok if alignment preferences are respected. If no preferences,
        // it is considered ok.


        var alignOk = (!vAlign || vAlignOk) && (!hAlign || hAlignOk); // Filter out elements that don't match the alignment (if defined).
        // With dynamicAlign, we need to consider all the positions to find the
        // one that minimizes the cropped area.

        if (!this.dynamicAlign && !alignOk) {
          continue;
        }

        candidate.offscreenArea = this.__getOffscreenArea(candidate, size, fitRect); // If not cropped and respects the align requirements, keep it.
        // This allows to prefer positions overlapping horizontally over the
        // ones overlapping vertically.

        if (candidate.offscreenArea === 0 && alignOk) {
          position = candidate;
          break;
        }

        position = position || candidate;
        var diff = candidate.offscreenArea - position.offscreenArea; // Check which crops less. If it crops equally, check if at least one
        // align setting is ok.

        if (diff < 0 || diff === 0 && (vAlignOk || hAlignOk)) {
          position = candidate;
        }
      }

      return position;
    }
  };

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  var p$1 = Element.prototype;
  var matches$2 = p$1.matches || p$1.matchesSelector || p$1.mozMatchesSelector || p$1.msMatchesSelector || p$1.oMatchesSelector || p$1.webkitMatchesSelector;

  class IronFocusablesHelperClass {
    /**
     * Returns a sorted array of tabbable nodes, including the root node.
     * It searches the tabbable nodes in the light and shadow dom of the chidren,
     * sorting the result by tabindex.
     * @param {!Node} node
     * @return {!Array<!HTMLElement>}
     */
    getTabbableNodes(node) {
      var result = []; // If there is at least one element with tabindex > 0, we need to sort
      // the final array by tabindex.

      var needsSortByTabIndex = this._collectTabbableNodes(node, result);

      if (needsSortByTabIndex) {
        return this._sortByTabIndex(result);
      }

      return result;
    }
    /**
     * Returns if a element is focusable.
     * @param {!HTMLElement} element
     * @return {boolean}
     */


    isFocusable(element) {
      // From http://stackoverflow.com/a/1600194/4228703:
      // There isn't a definite list, it's up to the browser. The only
      // standard we have is DOM Level 2 HTML
      // https://www.w3.org/TR/DOM-Level-2-HTML/html.html, according to which the
      // only elements that have a focus() method are HTMLInputElement,
      // HTMLSelectElement, HTMLTextAreaElement and HTMLAnchorElement. This
      // notably omits HTMLButtonElement and HTMLAreaElement. Referring to these
      // tests with tabbables in different browsers
      // http://allyjs.io/data-tables/focusable.html
      // Elements that cannot be focused if they have [disabled] attribute.
      if (matches$2.call(element, 'input, select, textarea, button, object')) {
        return matches$2.call(element, ':not([disabled])');
      } // Elements that can be focused even if they have [disabled] attribute.


      return matches$2.call(element, 'a[href], area[href], iframe, [tabindex], [contentEditable]');
    }
    /**
     * Returns if a element is tabbable. To be tabbable, a element must be
     * focusable, visible, and with a tabindex !== -1.
     * @param {!HTMLElement} element
     * @return {boolean}
     */


    isTabbable(element) {
      return this.isFocusable(element) && matches$2.call(element, ':not([tabindex="-1"])') && this._isVisible(element);
    }
    /**
     * Returns the normalized element tabindex. If not focusable, returns -1.
     * It checks for the attribute "tabindex" instead of the element property
     * `tabIndex` since browsers assign different values to it.
     * e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
     * @param {!HTMLElement} element
     * @return {!number}
     * @private
     */


    _normalizedTabIndex(element) {
      if (this.isFocusable(element)) {
        var tabIndex = element.getAttribute('tabindex') || 0;
        return Number(tabIndex);
      }

      return -1;
    }
    /**
     * Searches for nodes that are tabbable and adds them to the `result` array.
     * Returns if the `result` array needs to be sorted by tabindex.
     * @param {!Node} node The starting point for the search; added to `result`
     * if tabbable.
     * @param {!Array<!HTMLElement>} result
     * @return {boolean}
     * @private
     */


    _collectTabbableNodes(node, result) {
      // If not an element or not visible, no need to explore children.
      if (node.nodeType !== Node.ELEMENT_NODE) {
        return false;
      }

      var element =
      /** @type {!HTMLElement} */
      node;

      if (!this._isVisible(element)) {
        return false;
      }

      var tabIndex = this._normalizedTabIndex(element);

      var needsSort = tabIndex > 0;

      if (tabIndex >= 0) {
        result.push(element);
      } // In ShadowDOM v1, tab order is affected by the order of distrubution.
      // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];
      // in ShadowDOM v0 tab order is not affected by the distrubution order,
      // in fact getTabbableNodes(#root) returns [#B, #A].
      //  <div id="root">
      //   <!-- shadow -->
      //     <slot name="a">
      //     <slot name="b">
      //   <!-- /shadow -->
      //   <input id="A" slot="a">
      //   <input id="B" slot="b" tabindex="1">
      //  </div>
      // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.


      var children;

      if (element.localName === 'content' || element.localName === 'slot') {
        children = dom(element).getDistributedNodes();
      } else {
        // Use shadow root if possible, will check for distributed nodes.
        children = dom(element.root || element).children;
      }

      for (var i = 0; i < children.length; i++) {
        // Ensure method is always invoked to collect tabbable children.
        needsSort = this._collectTabbableNodes(children[i], result) || needsSort;
      }

      return needsSort;
    }
    /**
     * Returns false if the element has `visibility: hidden` or `display: none`
     * @param {!HTMLElement} element
     * @return {boolean}
     * @private
     */


    _isVisible(element) {
      // Check inline style first to save a re-flow. If looks good, check also
      // computed style.
      var style = element.style;

      if (style.visibility !== 'hidden' && style.display !== 'none') {
        style = window.getComputedStyle(element);
        return style.visibility !== 'hidden' && style.display !== 'none';
      }

      return false;
    }
    /**
     * Sorts an array of tabbable elements by tabindex. Returns a new array.
     * @param {!Array<!HTMLElement>} tabbables
     * @return {!Array<!HTMLElement>}
     * @private
     */


    _sortByTabIndex(tabbables) {
      // Implement a merge sort as Array.prototype.sort does a non-stable sort
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
      var len = tabbables.length;

      if (len < 2) {
        return tabbables;
      }

      var pivot = Math.ceil(len / 2);

      var left = this._sortByTabIndex(tabbables.slice(0, pivot));

      var right = this._sortByTabIndex(tabbables.slice(pivot));

      return this._mergeSortByTabIndex(left, right);
    }
    /**
     * Merge sort iterator, merges the two arrays into one, sorted by tab index.
     * @param {!Array<!HTMLElement>} left
     * @param {!Array<!HTMLElement>} right
     * @return {!Array<!HTMLElement>}
     * @private
     */


    _mergeSortByTabIndex(left, right) {
      var result = [];

      while (left.length > 0 && right.length > 0) {
        if (this._hasLowerTabOrder(left[0], right[0])) {
          result.push(right.shift());
        } else {
          result.push(left.shift());
        }
      }

      return result.concat(left, right);
    }
    /**
     * Returns if element `a` has lower tab order compared to element `b`
     * (both elements are assumed to be focusable and tabbable).
     * Elements with tabindex = 0 have lower tab order compared to elements
     * with tabindex > 0.
     * If both have same tabindex, it returns false.
     * @param {!HTMLElement} a
     * @param {!HTMLElement} b
     * @return {boolean}
     * @private
     */


    _hasLowerTabOrder(a, b) {
      // Normalize tabIndexes
      // e.g. in Firefox `<div contenteditable>` has `tabIndex = -1`
      var ati = Math.max(a.tabIndex, 0);
      var bti = Math.max(b.tabIndex, 0);
      return ati === 0 || bti === 0 ? bti > ati : ati > bti;
    }

  }

  const IronFocusablesHelper = new IronFocusablesHelperClass();

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It
  should be a singleton.

  ### Styling

  The following custom properties and mixins are available for styling.

  Custom property | Description | Default
  -------------------------------------------|------------------------|---------
  `--iron-overlay-backdrop-background-color` | Backdrop background color | #000
  `--iron-overlay-backdrop-opacity`          | Backdrop opacity | 0.6
  `--iron-overlay-backdrop`                  | Mixin applied to `iron-overlay-backdrop`.                      | {}
  `--iron-overlay-backdrop-opened`           | Mixin applied to `iron-overlay-backdrop` when it is displayed | {}
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: var(--iron-overlay-backdrop-background-color, #000);
        opacity: 0;
        transition: opacity 0.2s;
        pointer-events: none;
        @apply --iron-overlay-backdrop;
      }

      :host(.opened) {
        opacity: var(--iron-overlay-backdrop-opacity, 0.6);
        pointer-events: auto;
        @apply --iron-overlay-backdrop-opened;
      }
    </style>

    <slot></slot>
`,
    is: 'iron-overlay-backdrop',
    properties: {
      /**
       * Returns true if the backdrop is opened.
       */
      opened: {
        reflectToAttribute: true,
        type: Boolean,
        value: false,
        observer: '_openedChanged'
      }
    },
    listeners: {
      'transitionend': '_onTransitionend'
    },

    /** @override */
    created: function () {
      // Used to cancel previous requestAnimationFrame calls when opened changes.
      this.__openedRaf = null;
    },

    /** @override */
    attached: function () {
      this.opened && this._openedChanged(this.opened);
    },

    /**
     * Appends the backdrop to document body if needed.
     */
    prepare: function () {
      if (this.opened && !this.parentNode) {
        dom(document.body).appendChild(this);
      }
    },

    /**
     * Shows the backdrop.
     */
    open: function () {
      this.opened = true;
    },

    /**
     * Hides the backdrop.
     */
    close: function () {
      this.opened = false;
    },

    /**
     * Removes the backdrop from document body if needed.
     */
    complete: function () {
      if (!this.opened && this.parentNode === document.body) {
        dom(this.parentNode).removeChild(this);
      }
    },
    _onTransitionend: function (event) {
      if (event && event.target === this) {
        this.complete();
      }
    },

    /**
     * @param {boolean} opened
     * @private
     */
    _openedChanged: function (opened) {
      if (opened) {
        // Auto-attach.
        this.prepare();
      } else {
        // Animation might be disabled via the mixin or opacity custom property.
        // If it is disabled in other ways, it's up to the user to call complete.
        var cs = window.getComputedStyle(this);

        if (cs.transitionDuration === '0s' || cs.opacity == 0) {
          this.complete();
        }
      }

      if (!this.isAttached) {
        return;
      } // Always cancel previous requestAnimationFrame.


      if (this.__openedRaf) {
        window.cancelAnimationFrame(this.__openedRaf);
        this.__openedRaf = null;
      } // Force relayout to ensure proper transitions.


      this.scrollTop = this.scrollTop;
      this.__openedRaf = window.requestAnimationFrame(function () {
        this.__openedRaf = null;
        this.toggleClass('opened', this.opened);
      }.bind(this));
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @package
   */

  class IronOverlayManagerClass {
    constructor() {
      /**
       * Used to keep track of the opened overlays.
       * @private {!Array<!Element>}
       */
      this._overlays = [];
      /**
       * iframes have a default z-index of 100,
       * so this default should be at least that.
       * @private {number}
       */

      this._minimumZ = 101;
      /**
       * Memoized backdrop element.
       * @private {Element|null}
       */

      this._backdropElement = null; // Enable document-wide tap recognizer.
      // NOTE: Use useCapture=true to avoid accidentally prevention of the closing
      // of an overlay via event.stopPropagation(). The only way to prevent
      // closing of an overlay should be through its APIs.
      // NOTE: enable tap on <html> to workaround Polymer/polymer#4459
      // Pass no-op function because MSEdge 15 doesn't handle null as 2nd argument
      // https://github.com/Microsoft/ChakraCore/issues/3863

      addListener(document.documentElement, 'tap', function () {});
      document.addEventListener('tap', this._onCaptureClick.bind(this), true);
      document.addEventListener('focus', this._onCaptureFocus.bind(this), true);
      document.addEventListener('keydown', this._onCaptureKeyDown.bind(this), true);
    }
    /**
     * The shared backdrop element.
     * @return {!Element} backdropElement
     */


    get backdropElement() {
      if (!this._backdropElement) {
        this._backdropElement = document.createElement('iron-overlay-backdrop');
      }

      return this._backdropElement;
    }
    /**
     * The deepest active element.
     * @return {!Element} activeElement the active element
     */


    get deepActiveElement() {
      var active = document.activeElement; // document.activeElement can be null
      // https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
      // In IE 11, it can also be an object when operating in iframes.
      // In these cases, default it to document.body.

      if (!active || active instanceof Element === false) {
        active = document.body;
      }

      while (active.root && dom(active.root).activeElement) {
        active = dom(active.root).activeElement;
      }

      return active;
    }
    /**
     * Brings the overlay at the specified index to the front.
     * @param {number} i
     * @private
     */


    _bringOverlayAtIndexToFront(i) {
      var overlay = this._overlays[i];

      if (!overlay) {
        return;
      }

      var lastI = this._overlays.length - 1;
      var currentOverlay = this._overlays[lastI]; // Ensure always-on-top overlay stays on top.

      if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
        lastI--;
      } // If already the top element, return.


      if (i >= lastI) {
        return;
      } // Update z-index to be on top.


      var minimumZ = Math.max(this.currentOverlayZ(), this._minimumZ);

      if (this._getZ(overlay) <= minimumZ) {
        this._applyOverlayZ(overlay, minimumZ);
      } // Shift other overlays behind the new on top.


      while (i < lastI) {
        this._overlays[i] = this._overlays[i + 1];
        i++;
      }

      this._overlays[lastI] = overlay;
    }
    /**
     * Adds the overlay and updates its z-index if it's opened, or removes it if
     * it's closed. Also updates the backdrop z-index.
     * @param {!Element} overlay
     */


    addOrRemoveOverlay(overlay) {
      if (overlay.opened) {
        this.addOverlay(overlay);
      } else {
        this.removeOverlay(overlay);
      }
    }
    /**
     * Tracks overlays for z-index and focus management.
     * Ensures the last added overlay with always-on-top remains on top.
     * @param {!Element} overlay
     */


    addOverlay(overlay) {
      var i = this._overlays.indexOf(overlay);

      if (i >= 0) {
        this._bringOverlayAtIndexToFront(i);

        this.trackBackdrop();
        return;
      }

      var insertionIndex = this._overlays.length;
      var currentOverlay = this._overlays[insertionIndex - 1];
      var minimumZ = Math.max(this._getZ(currentOverlay), this._minimumZ);

      var newZ = this._getZ(overlay); // Ensure always-on-top overlay stays on top.


      if (currentOverlay && this._shouldBeBehindOverlay(overlay, currentOverlay)) {
        // This bumps the z-index of +2.
        this._applyOverlayZ(currentOverlay, minimumZ);

        insertionIndex--; // Update minimumZ to match previous overlay's z-index.

        var previousOverlay = this._overlays[insertionIndex - 1];
        minimumZ = Math.max(this._getZ(previousOverlay), this._minimumZ);
      } // Update z-index and insert overlay.


      if (newZ <= minimumZ) {
        this._applyOverlayZ(overlay, minimumZ);
      }

      this._overlays.splice(insertionIndex, 0, overlay);

      this.trackBackdrop();
    }
    /**
     * @param {!Element} overlay
     */


    removeOverlay(overlay) {
      var i = this._overlays.indexOf(overlay);

      if (i === -1) {
        return;
      }

      this._overlays.splice(i, 1);

      this.trackBackdrop();
    }
    /**
     * Returns the current overlay.
     * @return {!Element|undefined}
     */


    currentOverlay() {
      var i = this._overlays.length - 1;
      return this._overlays[i];
    }
    /**
     * Returns the current overlay z-index.
     * @return {number}
     */


    currentOverlayZ() {
      return this._getZ(this.currentOverlay());
    }
    /**
     * Ensures that the minimum z-index of new overlays is at least `minimumZ`.
     * This does not effect the z-index of any existing overlays.
     * @param {number} minimumZ
     */


    ensureMinimumZ(minimumZ) {
      this._minimumZ = Math.max(this._minimumZ, minimumZ);
    }

    focusOverlay() {
      var current =
      /** @type {?} */
      this.currentOverlay();

      if (current) {
        current._applyFocus();
      }
    }
    /**
     * Updates the backdrop z-index.
     */


    trackBackdrop() {
      var overlay = this._overlayWithBackdrop(); // Avoid creating the backdrop if there is no overlay with backdrop.


      if (!overlay && !this._backdropElement) {
        return;
      }

      this.backdropElement.style.zIndex = this._getZ(overlay) - 1;
      this.backdropElement.opened = !!overlay; // Property observers are not fired until element is attached
      // in Polymer 2.x, so we ensure element is attached if needed.
      // https://github.com/Polymer/polymer/issues/4526

      this.backdropElement.prepare();
    }
    /**
     * @return {!Array<!Element>}
     */


    getBackdrops() {
      var backdrops = [];

      for (var i = 0; i < this._overlays.length; i++) {
        if (this._overlays[i].withBackdrop) {
          backdrops.push(this._overlays[i]);
        }
      }

      return backdrops;
    }
    /**
     * Returns the z-index for the backdrop.
     * @return {number}
     */


    backdropZ() {
      return this._getZ(this._overlayWithBackdrop()) - 1;
    }
    /**
     * Returns the top opened overlay that has a backdrop.
     * @return {!Element|undefined}
     * @private
     */


    _overlayWithBackdrop() {
      for (var i = this._overlays.length - 1; i >= 0; i--) {
        if (this._overlays[i].withBackdrop) {
          return this._overlays[i];
        }
      }
    }
    /**
     * Calculates the minimum z-index for the overlay.
     * @param {Element=} overlay
     * @private
     */


    _getZ(overlay) {
      var z = this._minimumZ;

      if (overlay) {
        var z1 = Number(overlay.style.zIndex || window.getComputedStyle(overlay).zIndex); // Check if is a number
        // Number.isNaN not supported in IE 10+

        if (z1 === z1) {
          z = z1;
        }
      }

      return z;
    }
    /**
     * @param {!Element} element
     * @param {number|string} z
     * @private
     */


    _setZ(element, z) {
      element.style.zIndex = z;
    }
    /**
     * @param {!Element} overlay
     * @param {number} aboveZ
     * @private
     */


    _applyOverlayZ(overlay, aboveZ) {
      this._setZ(overlay, aboveZ + 2);
    }
    /**
     * Returns the deepest overlay in the path.
     * @param {!Array<!Element>=} path
     * @return {!Element|undefined}
     * @suppress {missingProperties}
     * @private
     */


    _overlayInPath(path) {
      path = path || [];

      for (var i = 0; i < path.length; i++) {
        if (path[i]._manager === this) {
          return path[i];
        }
      }
    }
    /**
     * Ensures the click event is delegated to the right overlay.
     * @param {!Event} event
     * @private
     */


    _onCaptureClick(event) {
      var i = this._overlays.length - 1;
      if (i === -1) return;
      var path =
      /** @type {!Array<!EventTarget>} */
      dom(event).path;
      var overlay; // Check if clicked outside of overlay.

      while ((overlay =
      /** @type {?} */
      this._overlays[i]) && this._overlayInPath(path) !== overlay) {
        overlay._onCaptureClick(event);

        if (overlay.allowClickThrough) {
          i--;
        } else {
          break;
        }
      }
    }
    /**
     * Ensures the focus event is delegated to the right overlay.
     * @param {!Event} event
     * @private
     */


    _onCaptureFocus(event) {
      var overlay =
      /** @type {?} */
      this.currentOverlay();

      if (overlay) {
        overlay._onCaptureFocus(event);
      }
    }
    /**
     * Ensures TAB and ESC keyboard events are delegated to the right overlay.
     * @param {!Event} event
     * @private
     */


    _onCaptureKeyDown(event) {
      var overlay =
      /** @type {?} */
      this.currentOverlay();

      if (overlay) {
        if (IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'esc')) {
          overlay._onCaptureEsc(event);
        } else if (IronA11yKeysBehavior.keyboardEventMatchesKeys(event, 'tab')) {
          overlay._onCaptureTab(event);
        }
      }
    }
    /**
     * Returns if the overlay1 should be behind overlay2.
     * @param {!Element} overlay1
     * @param {!Element} overlay2
     * @return {boolean}
     * @suppress {missingProperties}
     * @private
     */


    _shouldBeBehindOverlay(overlay1, overlay2) {
      return !overlay1.alwaysOnTop && overlay2.alwaysOnTop;
    }

  }
  const IronOverlayManager = new IronOverlayManagerClass();

  /**
  @license
  Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Used to calculate the scroll direction during touch events.
   * @type {!Object}
   */

  var lastTouchPosition = {
    pageX: 0,
    pageY: 0
  };
  /**
   * Used to avoid computing event.path and filter scrollable nodes (better perf).
   * @type {?EventTarget}
   */

  var lastRootTarget = null;
  /**
   * @type {!Array<!Node>}
   */

  var lastScrollableNodes = [];
  /**
   * @type {!Array<string>}
   */

  var scrollEvents = [// Modern `wheel` event for mouse wheel scrolling:
  'wheel', // Older, non-standard `mousewheel` event for some FF:
  'mousewheel', // IE:
  'DOMMouseScroll', // Touch enabled devices
  'touchstart', 'touchmove']; // must be defined for modulizer

  var _boundScrollHandler;
  /**
   * The current element that defines the DOM boundaries of the
   * scroll lock. This is always the most recently locking element.
   *
   * @type {!Node|undefined}
   */


  var currentLockingElement;
  /**
   * Push an element onto the current scroll lock stack. The most recently
   * pushed element and its children will be considered scrollable. All
   * other elements will not be scrollable.
   *
   * Scroll locking is implemented as a stack so that cases such as
   * dropdowns within dropdowns are handled well.
   *
   * @param {!HTMLElement} element The element that should lock scroll.
   */

  function pushScrollLock(element) {
    // Prevent pushing the same element twice
    if (_lockingElements.indexOf(element) >= 0) {
      return;
    }

    if (_lockingElements.length === 0) {
      _lockScrollInteractions();
    }

    _lockingElements.push(element);

    currentLockingElement = _lockingElements[_lockingElements.length - 1];
  }
  /**
   * Remove an element from the scroll lock stack. The element being
   * removed does not need to be the most recently pushed element. However,
   * the scroll lock constraints only change when the most recently pushed
   * element is removed.
   *
   * @param {!HTMLElement} element The element to remove from the scroll
   * lock stack.
   */

  function removeScrollLock(element) {
    var index = _lockingElements.indexOf(element);

    if (index === -1) {
      return;
    }

    _lockingElements.splice(index, 1);

    currentLockingElement = _lockingElements[_lockingElements.length - 1];

    if (_lockingElements.length === 0) {
      _unlockScrollInteractions();
    }
  }
  const _lockingElements = [];
  function _scrollInteractionHandler(event) {
    // Avoid canceling an event with cancelable=false, e.g. scrolling is in
    // progress and cannot be interrupted.
    if (event.cancelable && _shouldPreventScrolling(event)) {
      event.preventDefault();
    } // If event has targetTouches (touch event), update last touch position.


    if (event.targetTouches) {
      var touch = event.targetTouches[0];
      lastTouchPosition.pageX = touch.pageX;
      lastTouchPosition.pageY = touch.pageY;
    }
  }
  function _lockScrollInteractions() {
    _boundScrollHandler = _boundScrollHandler || _scrollInteractionHandler.bind(undefined);

    for (var i = 0, l = scrollEvents.length; i < l; i++) {
      // NOTE: browsers that don't support objects as third arg will
      // interpret it as boolean, hence useCapture = true in this case.
      document.addEventListener(scrollEvents[i], _boundScrollHandler, {
        capture: true,
        passive: false
      });
    }
  }
  function _unlockScrollInteractions() {
    for (var i = 0, l = scrollEvents.length; i < l; i++) {
      // NOTE: browsers that don't support objects as third arg will
      // interpret it as boolean, hence useCapture = true in this case.
      document.removeEventListener(scrollEvents[i], _boundScrollHandler, {
        capture: true,
        passive: false
      });
    }
  }
  /**
   * Returns true if the event causes scroll outside the current locking
   * element, e.g. pointer/keyboard interactions, or scroll "leaking"
   * outside the locking element when it is already at its scroll boundaries.
   * @param {!Event} event
   * @return {boolean}
   * @package
   */

  function _shouldPreventScrolling(event) {
    // Update if root target changed. For touch events, ensure we don't
    // update during touchmove.
    var target = dom(event).rootTarget;

    if (event.type !== 'touchmove' && lastRootTarget !== target) {
      lastRootTarget = target;
      lastScrollableNodes = _getScrollableNodes(dom(event).path);
    } // Prevent event if no scrollable nodes.


    if (!lastScrollableNodes.length) {
      return true;
    } // Don't prevent touchstart event inside the locking element when it has
    // scrollable nodes.


    if (event.type === 'touchstart') {
      return false;
    } // Get deltaX/Y.


    var info = _getScrollInfo(event); // Prevent if there is no child that can scroll.


    return !_getScrollingNode(lastScrollableNodes, info.deltaX, info.deltaY);
  }
  /**
   * Returns an array of scrollable nodes up to the current locking element,
   * which is included too if scrollable.
   * @param {!Array<!Node>} nodes
   * @return {!Array<!Node>} scrollables
   * @package
   */

  function _getScrollableNodes(nodes) {
    var scrollables = [];
    var lockingIndex = nodes.indexOf(
    /** @type {!Node} */
    currentLockingElement); // Loop from root target to locking element (included).

    for (var i = 0; i <= lockingIndex; i++) {
      // Skip non-Element nodes.
      if (nodes[i].nodeType !== Node.ELEMENT_NODE) {
        continue;
      }

      var node =
      /** @type {!Element} */
      nodes[i]; // Check inline style before checking computed style.

      var style = node.style;

      if (style.overflow !== 'scroll' && style.overflow !== 'auto') {
        style = window.getComputedStyle(node);
      }

      if (style.overflow === 'scroll' || style.overflow === 'auto') {
        scrollables.push(node);
      }
    }

    return scrollables;
  }
  /**
   * Returns the node that is scrolling. If there is no scrolling,
   * returns undefined.
   * @param {!Array<!Node>} nodes
   * @param {number} deltaX Scroll delta on the x-axis
   * @param {number} deltaY Scroll delta on the y-axis
   * @return {!Node|undefined}
   * @package
   */

  function _getScrollingNode(nodes, deltaX, deltaY) {
    // No scroll.
    if (!deltaX && !deltaY) {
      return;
    } // Check only one axis according to where there is more scroll.
    // Prefer vertical to horizontal.


    var verticalScroll = Math.abs(deltaY) >= Math.abs(deltaX);

    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      var canScroll = false;

      if (verticalScroll) {
        // delta < 0 is scroll up, delta > 0 is scroll down.
        canScroll = deltaY < 0 ? node.scrollTop > 0 : node.scrollTop < node.scrollHeight - node.clientHeight;
      } else {
        // delta < 0 is scroll left, delta > 0 is scroll right.
        canScroll = deltaX < 0 ? node.scrollLeft > 0 : node.scrollLeft < node.scrollWidth - node.clientWidth;
      }

      if (canScroll) {
        return node;
      }
    }
  }
  /**
   * Returns scroll `deltaX` and `deltaY`.
   * @param {!Event} event The scroll event
   * @return {{deltaX: number, deltaY: number}} Object containing the
   * x-axis scroll delta (positive: scroll right, negative: scroll left,
   * 0: no scroll), and the y-axis scroll delta (positive: scroll down,
   * negative: scroll up, 0: no scroll).
   * @package
   */

  function _getScrollInfo(event) {
    var info = {
      deltaX: event.deltaX,
      deltaY: event.deltaY
    }; // Already available.

    if ('deltaX' in event) ; // Safari has scroll info in `wheelDeltaX/Y`.
    else if ('wheelDeltaX' in event && 'wheelDeltaY' in event) {
        info.deltaX = -event.wheelDeltaX;
        info.deltaY = -event.wheelDeltaY;
      } // IE10 has only vertical scroll info in `wheelDelta`.
      else if ('wheelDelta' in event) {
          info.deltaX = 0;
          info.deltaY = -event.wheelDelta;
        } // Firefox has scroll info in `detail` and `axis`.
        else if ('axis' in event) {
            info.deltaX = event.axis === 1 ? event.detail : 0;
            info.deltaY = event.axis === 2 ? event.detail : 0;
          } // On mobile devices, calculate scroll direction.
          else if (event.targetTouches) {
              var touch = event.targetTouches[0]; // Touch moves from right to left => scrolling goes right.

              info.deltaX = lastTouchPosition.pageX - touch.pageX; // Touch moves from down to up => scrolling goes down.

              info.deltaY = lastTouchPosition.pageY - touch.pageY;
            }

    return info;
  }

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /** @polymerBehavior */

  const IronOverlayBehaviorImpl = {
    properties: {
      /**
       * True if the overlay is currently displayed.
       */
      opened: {
        observer: '_openedChanged',
        type: Boolean,
        value: false,
        notify: true
      },

      /**
       * True if the overlay was canceled when it was last closed.
       */
      canceled: {
        observer: '_canceledChanged',
        readOnly: true,
        type: Boolean,
        value: false
      },

      /**
       * Set to true to display a backdrop behind the overlay. It traps the focus
       * within the light DOM of the overlay.
       */
      withBackdrop: {
        observer: '_withBackdropChanged',
        type: Boolean
      },

      /**
       * Set to true to disable auto-focusing the overlay or child nodes with
       * the `autofocus` attribute` when the overlay is opened.
       */
      noAutoFocus: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable canceling the overlay with the ESC key.
       */
      noCancelOnEscKey: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable canceling the overlay by clicking outside it.
       */
      noCancelOnOutsideClick: {
        type: Boolean,
        value: false
      },

      /**
       * Contains the reason(s) this overlay was last closed (see
       * `iron-overlay-closed`). `IronOverlayBehavior` provides the `canceled`
       * reason; implementers of the behavior can provide other reasons in
       * addition to `canceled`.
       */
      closingReason: {
        // was a getter before, but needs to be a property so other
        // behaviors can override this.
        type: Object
      },

      /**
       * Set to true to enable restoring of focus when overlay is closed.
       */
      restoreFocusOnClose: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to allow clicks to go through overlays.
       * When the user clicks outside this overlay, the click may
       * close the overlay below.
       */
      allowClickThrough: {
        type: Boolean
      },

      /**
       * Set to true to keep overlay always on top.
       */
      alwaysOnTop: {
        type: Boolean
      },

      /**
       * Determines which action to perform when scroll outside an opened overlay
       * happens. Possible values: lock - blocks scrolling from happening, refit -
       * computes the new position on the overlay cancel - causes the overlay to
       * close
       */
      scrollAction: {
        type: String
      },

      /**
       * Shortcut to access to the overlay manager.
       * @private
       * @type {!IronOverlayManagerClass}
       */
      _manager: {
        type: Object,
        value: IronOverlayManager
      },

      /**
       * The node being focused.
       * @type {?Node}
       */
      _focusedChild: {
        type: Object
      }
    },
    listeners: {
      'iron-resize': '_onIronResize'
    },
    observers: ['__updateScrollObservers(isAttached, opened, scrollAction)'],

    /**
     * The backdrop element.
     * @return {!Element}
     */
    get backdropElement() {
      return this._manager.backdropElement;
    },

    /**
     * Returns the node to give focus to.
     * @return {!Node}
     */
    get _focusNode() {
      return this._focusedChild || dom(this).querySelector('[autofocus]') || this;
    },

    /**
     * Array of nodes that can receive focus (overlay included), ordered by
     * `tabindex`. This is used to retrieve which is the first and last focusable
     * nodes in order to wrap the focus for overlays `with-backdrop`.
     *
     * If you know what is your content (specifically the first and last focusable
     * children), you can override this method to return only `[firstFocusable,
     * lastFocusable];`
     * @return {!Array<!Node>}
     * @protected
     */
    get _focusableNodes() {
      return IronFocusablesHelper.getTabbableNodes(this);
    },

    /**
     * @return {void}
     */
    ready: function () {
      // Used to skip calls to notifyResize and refit while the overlay is
      // animating.
      this.__isAnimating = false; // with-backdrop needs tabindex to be set in order to trap the focus.
      // If it is not set, IronOverlayBehavior will set it, and remove it if
      // with-backdrop = false.

      this.__shouldRemoveTabIndex = false; // Used for wrapping the focus on TAB / Shift+TAB.

      this.__firstFocusableNode = this.__lastFocusableNode = null; // Used by to keep track of the RAF callbacks.

      this.__rafs = {}; // Focused node before overlay gets opened. Can be restored on close.

      this.__restoreFocusNode = null; // Scroll info to be restored.

      this.__scrollTop = this.__scrollLeft = null;
      this.__onCaptureScroll = this.__onCaptureScroll.bind(this); // Root nodes hosting the overlay, used to listen for scroll events on them.

      this.__rootNodes = null;

      this._ensureSetup();
    },

    /** @override */
    attached: function () {
      // Call _openedChanged here so that position can be computed correctly.
      if (this.opened) {
        this._openedChanged(this.opened);
      }

      this._observer = dom(this).observeNodes(this._onNodesChange);
    },

    /** @override */
    detached: function () {
      // TODO(bicknellr): Per spec, checking `this._observer` should never be
      // necessary because `connectedCallback` and `disconnectedCallback` should
      // always be called in alternating order. However, the custom elements
      // polyfill doesn't implement the reactions stack, so this can sometimes
      // happen, particularly if ShadyDOM is in noPatch mode where the custom
      // elements polyfill is installed before ShadyDOM. We should investigate
      // whether or not we can either implement the reactions stack without major
      // performance implications or patch ShadyDOM's functions to restore the
      // typical ShadyDOM-then-custom-elements order and remove this workaround.
      if (this._observer) {
        dom(this).unobserveNodes(this._observer);
      }

      this._observer = null;

      for (var cb in this.__rafs) {
        if (this.__rafs[cb] !== null) {
          cancelAnimationFrame(this.__rafs[cb]);
        }
      }

      this.__rafs = {};

      this._manager.removeOverlay(this); // We got detached while animating, ensure we show/hide the overlay
      // and fire iron-overlay-opened/closed event!


      if (this.__isAnimating) {
        if (this.opened) {
          this._finishRenderOpened();
        } else {
          // Restore the focus if necessary.
          this._applyFocus();

          this._finishRenderClosed();
        }
      }
    },

    /**
     * Toggle the opened state of the overlay.
     */
    toggle: function () {
      this._setCanceled(false);

      this.opened = !this.opened;
    },

    /**
     * Open the overlay.
     */
    open: function () {
      this._setCanceled(false);

      this.opened = true;
    },

    /**
     * Close the overlay.
     */
    close: function () {
      this._setCanceled(false);

      this.opened = false;
    },

    /**
     * Cancels the overlay.
     * @param {Event=} event The original event
     */
    cancel: function (event) {
      var cancelEvent = this.fire('iron-overlay-canceled', event, {
        cancelable: true
      });

      if (cancelEvent.defaultPrevented) {
        return;
      }

      this._setCanceled(true);

      this.opened = false;
    },

    /**
     * Invalidates the cached tabbable nodes. To be called when any of the
     * focusable content changes (e.g. a button is disabled).
     */
    invalidateTabbables: function () {
      this.__firstFocusableNode = this.__lastFocusableNode = null;
    },
    _ensureSetup: function () {
      if (this._overlaySetup) {
        return;
      }

      this._overlaySetup = true;
      this.style.outline = 'none';
      this.style.display = 'none';
    },

    /**
     * Called when `opened` changes.
     * @param {boolean=} opened
     * @protected
     */
    _openedChanged: function (opened) {
      if (opened) {
        this.removeAttribute('aria-hidden');
      } else {
        this.setAttribute('aria-hidden', 'true');
      } // Defer any animation-related code on attached
      // (_openedChanged gets called again on attached).


      if (!this.isAttached) {
        return;
      }

      this.__isAnimating = true; // Deraf for non-blocking rendering.

      this.__deraf('__openedChanged', this.__openedChanged);
    },
    _canceledChanged: function () {
      this.closingReason = this.closingReason || {};
      this.closingReason.canceled = this.canceled;
    },
    _withBackdropChanged: function () {
      // If tabindex is already set, no need to override it.
      if (this.withBackdrop && !this.hasAttribute('tabindex')) {
        this.setAttribute('tabindex', '-1');
        this.__shouldRemoveTabIndex = true;
      } else if (this.__shouldRemoveTabIndex) {
        this.removeAttribute('tabindex');
        this.__shouldRemoveTabIndex = false;
      }

      if (this.opened && this.isAttached) {
        this._manager.trackBackdrop();
      }
    },

    /**
     * tasks which must occur before opening; e.g. making the element visible.
     * @protected
     */
    _prepareRenderOpened: function () {
      // Store focused node.
      this.__restoreFocusNode = this._manager.deepActiveElement; // Needed to calculate the size of the overlay so that transitions on its
      // size will have the correct starting points.

      this._preparePositioning();

      this.refit();

      this._finishPositioning(); // Safari will apply the focus to the autofocus element when displayed
      // for the first time, so we make sure to return the focus where it was.


      if (this.noAutoFocus && document.activeElement === this._focusNode) {
        this._focusNode.blur();

        this.__restoreFocusNode.focus();
      }
    },

    /**
     * Tasks which cause the overlay to actually open; typically play an
     * animation.
     * @protected
     */
    _renderOpened: function () {
      this._finishRenderOpened();
    },

    /**
     * Tasks which cause the overlay to actually close; typically play an
     * animation.
     * @protected
     */
    _renderClosed: function () {
      this._finishRenderClosed();
    },

    /**
     * Tasks to be performed at the end of open action. Will fire
     * `iron-overlay-opened`.
     * @protected
     */
    _finishRenderOpened: function () {
      this.notifyResize();
      this.__isAnimating = false;
      this.fire('iron-overlay-opened');
    },

    /**
     * Tasks to be performed at the end of close action. Will fire
     * `iron-overlay-closed`.
     * @protected
     */
    _finishRenderClosed: function () {
      // Hide the overlay.
      this.style.display = 'none'; // Reset z-index only at the end of the animation.

      this.style.zIndex = '';
      this.notifyResize();
      this.__isAnimating = false;
      this.fire('iron-overlay-closed', this.closingReason);
    },
    _preparePositioning: function () {
      this.style.transition = this.style.webkitTransition = 'none';
      this.style.transform = this.style.webkitTransform = 'none';
      this.style.display = '';
    },
    _finishPositioning: function () {
      // First, make it invisible & reactivate animations.
      this.style.display = 'none'; // Force reflow before re-enabling animations so that they don't start.
      // Set scrollTop to itself so that Closure Compiler doesn't remove this.

      this.scrollTop = this.scrollTop;
      this.style.transition = this.style.webkitTransition = '';
      this.style.transform = this.style.webkitTransform = ''; // Now that animations are enabled, make it visible again

      this.style.display = ''; // Force reflow, so that following animations are properly started.
      // Set scrollTop to itself so that Closure Compiler doesn't remove this.

      this.scrollTop = this.scrollTop;
    },

    /**
     * Applies focus according to the opened state.
     * @protected
     */
    _applyFocus: function () {
      if (this.opened) {
        if (!this.noAutoFocus) {
          this._focusNode.focus();
        }
      } else {
        // Restore focus.
        if (this.restoreFocusOnClose && this.__restoreFocusNode) {
          // If the activeElement is `<body>` or inside the overlay,
          // we are allowed to restore the focus. In all the other
          // cases focus might have been moved elsewhere by another
          // component or by an user interaction (e.g. click on a
          // button outside the overlay).
          var activeElement = this._manager.deepActiveElement;

          if (activeElement === document.body || composedContains(this, activeElement)) {
            this.__restoreFocusNode.focus();
          }
        }

        this.__restoreFocusNode = null;

        this._focusNode.blur();

        this._focusedChild = null;
      }
    },

    /**
     * Cancels (closes) the overlay. Call when click happens outside the overlay.
     * @param {!Event} event
     * @protected
     */
    _onCaptureClick: function (event) {
      if (!this.noCancelOnOutsideClick) {
        this.cancel(event);
      }
    },

    /**
     * Keeps track of the focused child. If withBackdrop, traps focus within
     * overlay.
     * @param {!Event} event
     * @protected
     */
    _onCaptureFocus: function (event) {
      if (!this.withBackdrop) {
        return;
      }

      var path = dom(event).path;

      if (path.indexOf(this) === -1) {
        event.stopPropagation();

        this._applyFocus();
      } else {
        this._focusedChild =
        /** @type {Node} */
        path[0];
      }
    },

    /**
     * Handles the ESC key event and cancels (closes) the overlay.
     * @param {!Event} event
     * @protected
     */
    _onCaptureEsc: function (event) {
      if (!this.noCancelOnEscKey) {
        this.cancel(event);
      }
    },

    /**
     * Handles TAB key events to track focus changes.
     * Will wrap focus for overlays withBackdrop.
     * @param {!Event} event
     * @protected
     */
    _onCaptureTab: function (event) {
      if (!this.withBackdrop) {
        return;
      }

      this.__ensureFirstLastFocusables(); // TAB wraps from last to first focusable.
      // Shift + TAB wraps from first to last focusable.


      var shift = event.shiftKey;
      var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
      var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
      var shouldWrap = false;

      if (nodeToCheck === nodeToSet) {
        // If nodeToCheck is the same as nodeToSet, it means we have an overlay
        // with 0 or 1 focusables; in either case we still need to trap the
        // focus within the overlay.
        shouldWrap = true;
      } else {
        // In dom=shadow, the manager will receive focus changes on the main
        // root but not the ones within other shadow roots, so we can't rely on
        // _focusedChild, but we should check the deepest active element.
        var focusedNode = this._manager.deepActiveElement; // If the active element is not the nodeToCheck but the overlay itself,
        // it means the focus is about to go outside the overlay, hence we
        // should prevent that (e.g. user opens the overlay and hit Shift+TAB).

        shouldWrap = focusedNode === nodeToCheck || focusedNode === this;
      }

      if (shouldWrap) {
        // When the overlay contains the last focusable element of the document
        // and it's already focused, pressing TAB would move the focus outside
        // the document (e.g. to the browser search bar). Similarly, when the
        // overlay contains the first focusable element of the document and it's
        // already focused, pressing Shift+TAB would move the focus outside the
        // document (e.g. to the browser search bar).
        // In both cases, we would not receive a focus event, but only a blur.
        // In order to achieve focus wrapping, we prevent this TAB event and
        // force the focus. This will also prevent the focus to temporarily move
        // outside the overlay, which might cause scrolling.
        event.preventDefault();
        this._focusedChild = nodeToSet;

        this._applyFocus();
      }
    },

    /**
     * Refits if the overlay is opened and not animating.
     * @protected
     */
    _onIronResize: function () {
      if (this.opened && !this.__isAnimating) {
        this.__deraf('refit', this.refit);
      }
    },

    /**
     * Will call notifyResize if overlay is opened.
     * Can be overridden in order to avoid multiple observers on the same node.
     * @protected
     */
    _onNodesChange: function () {
      if (this.opened && !this.__isAnimating) {
        // It might have added focusable nodes, so invalidate cached values.
        this.invalidateTabbables();
        this.notifyResize();
      }
    },

    /**
     * Updates the references to the first and last focusable nodes.
     * @private
     */
    __ensureFirstLastFocusables: function () {
      var focusableNodes = this._focusableNodes;
      this.__firstFocusableNode = focusableNodes[0];
      this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
    },

    /**
     * Tasks executed when opened changes: prepare for the opening, move the
     * focus, update the manager, render opened/closed.
     * @private
     */
    __openedChanged: function () {
      if (this.opened) {
        // Make overlay visible, then add it to the manager.
        this._prepareRenderOpened();

        this._manager.addOverlay(this); // Move the focus to the child node with [autofocus].


        this._applyFocus();

        this._renderOpened();
      } else {
        // Remove overlay, then restore the focus before actually closing.
        this._manager.removeOverlay(this);

        this._applyFocus();

        this._renderClosed();
      }
    },

    /**
     * Debounces the execution of a callback to the next animation frame.
     * @param {!string} jobname
     * @param {!Function} callback Always bound to `this`
     * @private
     */
    __deraf: function (jobname, callback) {
      var rafs = this.__rafs;

      if (rafs[jobname] !== null) {
        cancelAnimationFrame(rafs[jobname]);
      }

      rafs[jobname] = requestAnimationFrame(function nextAnimationFrame() {
        rafs[jobname] = null;
        callback.call(this);
      }.bind(this));
    },

    /**
     * @param {boolean} isAttached
     * @param {boolean} opened
     * @param {string=} scrollAction
     * @private
     */
    __updateScrollObservers: function (isAttached, opened, scrollAction) {
      if (!isAttached || !opened || !this.__isValidScrollAction(scrollAction)) {
        removeScrollLock(this);

        this.__removeScrollListeners();
      } else {
        if (scrollAction === 'lock') {
          this.__saveScrollPosition();

          pushScrollLock(this);
        }

        this.__addScrollListeners();
      }
    },

    /**
     * @private
     */
    __addScrollListeners: function () {
      if (!this.__rootNodes) {
        this.__rootNodes = []; // Listen for scroll events in all shadowRoots hosting this overlay only
        // when in native ShadowDOM.

        if (useShadow) {
          var node = this;

          while (node) {
            if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE && node.host) {
              this.__rootNodes.push(node);
            }

            node = node.host || node.assignedSlot || node.parentNode;
          }
        }

        this.__rootNodes.push(document);
      }

      this.__rootNodes.forEach(function (el) {
        el.addEventListener('scroll', this.__onCaptureScroll, {
          capture: true,
          passive: true
        });
      }, this);
    },

    /**
     * @private
     */
    __removeScrollListeners: function () {
      if (this.__rootNodes) {
        this.__rootNodes.forEach(function (el) {
          el.removeEventListener('scroll', this.__onCaptureScroll, {
            capture: true,
            passive: true
          });
        }, this);
      }

      if (!this.isAttached) {
        this.__rootNodes = null;
      }
    },

    /**
     * @param {string=} scrollAction
     * @return {boolean}
     * @private
     */
    __isValidScrollAction: function (scrollAction) {
      return scrollAction === 'lock' || scrollAction === 'refit' || scrollAction === 'cancel';
    },

    /**
     * @private
     */
    __onCaptureScroll: function (event) {
      if (this.__isAnimating) {
        return;
      } // Check if scroll outside the overlay.


      if (dom(event).path.indexOf(this) >= 0) {
        return;
      }

      switch (this.scrollAction) {
        case 'lock':
          // NOTE: scrolling might happen if a scroll event is not cancellable, or
          // if user pressed keys that cause scrolling (they're not prevented in
          // order not to break a11y features like navigate with arrow keys).
          this.__restoreScrollPosition();

          break;

        case 'refit':
          this.__deraf('refit', this.refit);

          break;

        case 'cancel':
          this.cancel(event);
          break;
      }
    },

    /**
     * Memoizes the scroll position of the outside scrolling element.
     * @private
     */
    __saveScrollPosition: function () {
      if (document.scrollingElement) {
        this.__scrollTop = document.scrollingElement.scrollTop;
        this.__scrollLeft = document.scrollingElement.scrollLeft;
      } else {
        // Since we don't know if is the body or html, get max.
        this.__scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
        this.__scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
      }
    },

    /**
     * Resets the scroll position of the outside scrolling element.
     * @private
     */
    __restoreScrollPosition: function () {
      if (document.scrollingElement) {
        document.scrollingElement.scrollTop = this.__scrollTop;
        document.scrollingElement.scrollLeft = this.__scrollLeft;
      } else {
        // Since we don't know if is the body or html, set both.
        document.documentElement.scrollTop = document.body.scrollTop = this.__scrollTop;
        document.documentElement.scrollLeft = document.body.scrollLeft = this.__scrollLeft;
      }
    }
  };

  const composedParent = node => node.assignedSlot || node.parentNode || node.host;

  const composedContains = (ancestor, descendant) => {
    for (let element = descendant; element; element = composedParent(element)) {
      if (element === ancestor) {
        return true;
      }
    }

    return false;
  };
  /**
    Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden
    or shown, and displays on top of other content. It includes an optional
    backdrop, and can be used to implement a variety of UI controls including
    dialogs and drop downs. Multiple overlays may be displayed at once.

    See the [demo source
    code](https://github.com/PolymerElements/iron-overlay-behavior/blob/master/demo/simple-overlay.html)
    for an example.

    ### Closing and canceling

    An overlay may be hidden by closing or canceling. The difference between close
    and cancel is user intent. Closing generally implies that the user
    acknowledged the content on the overlay. By default, it will cancel whenever
    the user taps outside it or presses the escape key. This behavior is
    configurable with the `no-cancel-on-esc-key` and the
    `no-cancel-on-outside-click` properties. `close()` should be called explicitly
    by the implementer when the user interacts with a control in the overlay
    element. When the dialog is canceled, the overlay fires an
    'iron-overlay-canceled' event. Call `preventDefault` on this event to prevent
    the overlay from closing.

    ### Positioning

    By default the element is sized and positioned to fit and centered inside the
    window. You can position and size it manually using CSS. See
    `Polymer.IronFitBehavior`.

    ### Backdrop

    Set the `with-backdrop` attribute to display a backdrop behind the overlay.
    The backdrop is appended to `<body>` and is of type `<iron-overlay-backdrop>`.
    See its doc page for styling options.

    In addition, `with-backdrop` will wrap the focus within the content in the
    light DOM. Override the [`_focusableNodes`
    getter](#Polymer.IronOverlayBehavior:property-_focusableNodes) to achieve a
    different behavior.

    ### Limitations

    The element is styled to appear on top of other content by setting its
    `z-index` property. You must ensure no element has a stacking context with a
    higher `z-index` than its parent stacking context. You should place this
    element as a child of `<body>` whenever possible.

    @demo demo/index.html
    @polymerBehavior
   */


  const IronOverlayBehavior = [IronFitBehavior, IronResizableBehavior, IronOverlayBehaviorImpl];
  /**
   * Fired after the overlay opens.
   * @event iron-overlay-opened
   */

  /**
   * Fired when the overlay is canceled, but before it is closed.
   * @event iron-overlay-canceled
   * @param {Event} event The closing of the overlay can be prevented
   * by calling `event.preventDefault()`. The `event.detail` is the original event
   * that originated the canceling (e.g. ESC keyboard event or click event outside
   * the overlay).
   */

  /**
   * Fired after the overlay closes.
   * @event iron-overlay-closed
   * @param {Event} event The `event.detail` is the `closingReason` property
   * (contains `canceled`, whether the overlay was canceled).
   */

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * `NeonAnimatableBehavior` is implemented by elements containing
   * animations for use with elements implementing
   * `NeonAnimationRunnerBehavior`.
   * @polymerBehavior
   */

  const NeonAnimatableBehavior = {
    properties: {
      /**
       * Animation configuration. See README for more info.
       */
      animationConfig: {
        type: Object
      },

      /**
       * Convenience property for setting an 'entry' animation. Do not set
       * `animationConfig.entry` manually if using this. The animated node is set
       * to `this` if using this property.
       */
      entryAnimation: {
        observer: '_entryAnimationChanged',
        type: String
      },

      /**
       * Convenience property for setting an 'exit' animation. Do not set
       * `animationConfig.exit` manually if using this. The animated node is set
       * to `this` if using this property.
       */
      exitAnimation: {
        observer: '_exitAnimationChanged',
        type: String
      }
    },
    _entryAnimationChanged: function () {
      this.animationConfig = this.animationConfig || {};
      this.animationConfig['entry'] = [{
        name: this.entryAnimation,
        node: this
      }];
    },
    _exitAnimationChanged: function () {
      this.animationConfig = this.animationConfig || {};
      this.animationConfig['exit'] = [{
        name: this.exitAnimation,
        node: this
      }];
    },
    _copyProperties: function (config1, config2) {
      // shallowly copy properties from config2 to config1
      for (var property in config2) {
        config1[property] = config2[property];
      }
    },
    _cloneConfig: function (config) {
      var clone = {
        isClone: true
      };

      this._copyProperties(clone, config);

      return clone;
    },
    _getAnimationConfigRecursive: function (type, map, allConfigs) {
      if (!this.animationConfig) {
        return;
      }

      if (this.animationConfig.value && typeof this.animationConfig.value === 'function') {
        this._warn(this._logf('playAnimation', 'Please put \'animationConfig\' inside of your components \'properties\' object instead of outside of it.'));

        return;
      } // type is optional


      var thisConfig;

      if (type) {
        thisConfig = this.animationConfig[type];
      } else {
        thisConfig = this.animationConfig;
      }

      if (!Array.isArray(thisConfig)) {
        thisConfig = [thisConfig];
      } // iterate animations and recurse to process configurations from child nodes


      if (thisConfig) {
        for (var config, index = 0; config = thisConfig[index]; index++) {
          if (config.animatable) {
            config.animatable._getAnimationConfigRecursive(config.type || type, map, allConfigs);
          } else {
            if (config.id) {
              var cachedConfig = map[config.id];

              if (cachedConfig) {
                // merge configurations with the same id, making a clone lazily
                if (!cachedConfig.isClone) {
                  map[config.id] = this._cloneConfig(cachedConfig);
                  cachedConfig = map[config.id];
                }

                this._copyProperties(cachedConfig, config);
              } else {
                // put any configs with an id into a map
                map[config.id] = config;
              }
            } else {
              allConfigs.push(config);
            }
          }
        }
      }
    },

    /**
     * An element implementing `NeonAnimationRunnerBehavior` calls this
     * method to configure an animation with an optional type. Elements
     * implementing `NeonAnimatableBehavior` should define the property
     * `animationConfig`, which is either a configuration object or a map of
     * animation type to array of configuration objects.
     */
    getAnimationConfig: function (type) {
      var map = {};
      var allConfigs = [];

      this._getAnimationConfigRecursive(type, map, allConfigs); // append the configurations saved in the map to the array


      for (var key in map) {
        allConfigs.push(map[key]);
      }

      return allConfigs;
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * `NeonAnimationRunnerBehavior` adds a method to run animations.
   *
   * @polymerBehavior NeonAnimationRunnerBehavior
   */

  const NeonAnimationRunnerBehaviorImpl = {
    _configureAnimations: function (configs) {
      var results = [];
      var resultsToPlay = [];

      if (configs.length > 0) {
        for (let config, index = 0; config = configs[index]; index++) {
          let neonAnimation = document.createElement(config.name); // is this element actually a neon animation?

          if (neonAnimation.isNeonAnimation) {
            let result = null; // Closure compiler does not work well with a try / catch here.
            // .configure needs to be explicitly defined

            if (!neonAnimation.configure) {
              /**
               * @param {Object} config
               * @return {AnimationEffectReadOnly}
               */
              neonAnimation.configure = function (config) {
                return null;
              };
            }

            result = neonAnimation.configure(config);
            resultsToPlay.push({
              result: result,
              config: config,
              neonAnimation: neonAnimation
            });
          } else {
            console.warn(this.is + ':', config.name, 'not found!');
          }
        }
      }

      for (var i = 0; i < resultsToPlay.length; i++) {
        let result = resultsToPlay[i].result;
        let config = resultsToPlay[i].config;
        let neonAnimation = resultsToPlay[i].neonAnimation; // configuration or play could fail if polyfills aren't loaded

        try {
          // Check if we have an Effect rather than an Animation
          if (typeof result.cancel != 'function') {
            result = document.timeline.play(result);
          }
        } catch (e) {
          result = null;
          console.warn('Couldnt play', '(', config.name, ').', e);
        }

        if (result) {
          results.push({
            neonAnimation: neonAnimation,
            config: config,
            animation: result
          });
        }
      }

      return results;
    },
    _shouldComplete: function (activeEntries) {
      var finished = true;

      for (var i = 0; i < activeEntries.length; i++) {
        if (activeEntries[i].animation.playState != 'finished') {
          finished = false;
          break;
        }
      }

      return finished;
    },
    _complete: function (activeEntries) {
      for (var i = 0; i < activeEntries.length; i++) {
        activeEntries[i].neonAnimation.complete(activeEntries[i].config);
      }

      for (var i = 0; i < activeEntries.length; i++) {
        activeEntries[i].animation.cancel();
      }
    },

    /**
     * Plays an animation with an optional `type`.
     * @param {string=} type
     * @param {!Object=} cookie
     */
    playAnimation: function (type, cookie) {
      var configs = this.getAnimationConfig(type);

      if (!configs) {
        return;
      }

      this._active = this._active || {};

      if (this._active[type]) {
        this._complete(this._active[type]);

        delete this._active[type];
      }

      var activeEntries = this._configureAnimations(configs);

      if (activeEntries.length == 0) {
        this.fire('neon-animation-finish', cookie, {
          bubbles: false
        });
        return;
      }

      this._active[type] = activeEntries;

      for (var i = 0; i < activeEntries.length; i++) {
        activeEntries[i].animation.onfinish = function () {
          if (this._shouldComplete(activeEntries)) {
            this._complete(activeEntries);

            delete this._active[type];
            this.fire('neon-animation-finish', cookie, {
              bubbles: false
            });
          }
        }.bind(this);
      }
    },

    /**
     * Cancels the currently running animations.
     */
    cancelAnimation: function () {
      for (var k in this._active) {
        var entries = this._active[k];

        for (var j in entries) {
          entries[j].animation.cancel();
        }
      }

      this._active = {};
    }
  };
  /** @polymerBehavior */

  const NeonAnimationRunnerBehavior = [NeonAnimatableBehavior, NeonAnimationRunnerBehaviorImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  `<iron-dropdown>` is a generalized element that is useful when you have
  hidden content (`dropdown-content`) that is revealed due to some change in
  state that should cause it to do so.

  Note that this is a low-level element intended to be used as part of other
  composite elements that cause dropdowns to be revealed.

  Examples of elements that might be implemented using an `iron-dropdown`
  include comboboxes, menubuttons, selects. The list goes on.

  The `<iron-dropdown>` element exposes attributes that allow the position
  of the `dropdown-content` relative to the `dropdown-trigger` to be
  configured.

      <iron-dropdown horizontal-align="right" vertical-align="top">
        <div slot="dropdown-content">Hello!</div>
      </iron-dropdown>

  In the above example, the `<div>` assigned to the `dropdown-content` slot will
  be hidden until the dropdown element has `opened` set to true, or when the
  `open` method is called on the element.

  @demo demo/index.html
  */

  Polymer({
    _template: html$1`
    <style>
      :host {
        position: fixed;
      }

      #contentWrapper ::slotted(*) {
        overflow: auto;
      }

      #contentWrapper.animating ::slotted(*) {
        overflow: hidden;
        pointer-events: none;
      }
    </style>

    <div id="contentWrapper">
      <slot id="content" name="dropdown-content"></slot>
    </div>
`,
    is: 'iron-dropdown',
    behaviors: [IronControlState, IronA11yKeysBehavior, IronOverlayBehavior, NeonAnimationRunnerBehavior],
    properties: {
      /**
       * The orientation against which to align the dropdown content
       * horizontally relative to the dropdown trigger.
       * Overridden from `Polymer.IronFitBehavior`.
       */
      horizontalAlign: {
        type: String,
        value: 'left',
        reflectToAttribute: true
      },

      /**
       * The orientation against which to align the dropdown content
       * vertically relative to the dropdown trigger.
       * Overridden from `Polymer.IronFitBehavior`.
       */
      verticalAlign: {
        type: String,
        value: 'top',
        reflectToAttribute: true
      },

      /**
       * An animation config. If provided, this will be used to animate the
       * opening of the dropdown. Pass an Array for multiple animations.
       * See `neon-animation` documentation for more animation configuration
       * details.
       */
      openAnimationConfig: {
        type: Object
      },

      /**
       * An animation config. If provided, this will be used to animate the
       * closing of the dropdown. Pass an Array for multiple animations.
       * See `neon-animation` documentation for more animation configuration
       * details.
       */
      closeAnimationConfig: {
        type: Object
      },

      /**
       * If provided, this will be the element that will be focused when
       * the dropdown opens.
       */
      focusTarget: {
        type: Object
      },

      /**
       * Set to true to disable animations when opening and closing the
       * dropdown.
       */
      noAnimations: {
        type: Boolean,
        value: false
      },

      /**
       * By default, the dropdown will constrain scrolling on the page
       * to itself when opened.
       * Set to true in order to prevent scroll from being constrained
       * to the dropdown when it opens.
       * This property is a shortcut to set `scrollAction` to lock or refit.
       * Prefer directly setting the `scrollAction` property.
       */
      allowOutsideScroll: {
        type: Boolean,
        value: false,
        observer: '_allowOutsideScrollChanged'
      }
    },
    listeners: {
      'neon-animation-finish': '_onNeonAnimationFinish'
    },
    observers: ['_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)'],

    /**
     * The element that is contained by the dropdown, if any.
     */
    get containedElement() {
      // Polymer 2.x returns slot.assignedNodes which can contain text nodes.
      var nodes = dom(this.$.content).getDistributedNodes();

      for (var i = 0, l = nodes.length; i < l; i++) {
        if (nodes[i].nodeType === Node.ELEMENT_NODE) {
          return nodes[i];
        }
      }
    },

    ready: function () {
      // Ensure scrollAction is set.
      if (!this.scrollAction) {
        this.scrollAction = this.allowOutsideScroll ? 'refit' : 'lock';
      }

      this._readied = true;
    },
    attached: function () {
      if (!this.sizingTarget || this.sizingTarget === this) {
        this.sizingTarget = this.containedElement || this;
      }
    },
    detached: function () {
      this.cancelAnimation();
    },

    /**
     * Called when the value of `opened` changes.
     * Overridden from `IronOverlayBehavior`
     */
    _openedChanged: function () {
      if (this.opened && this.disabled) {
        this.cancel();
      } else {
        this.cancelAnimation();

        this._updateAnimationConfig();

        IronOverlayBehaviorImpl._openedChanged.apply(this, arguments);
      }
    },

    /**
     * Overridden from `IronOverlayBehavior`.
     */
    _renderOpened: function () {
      if (!this.noAnimations && this.animationConfig.open) {
        this.$.contentWrapper.classList.add('animating');
        this.playAnimation('open');
      } else {
        IronOverlayBehaviorImpl._renderOpened.apply(this, arguments);
      }
    },

    /**
     * Overridden from `IronOverlayBehavior`.
     */
    _renderClosed: function () {
      if (!this.noAnimations && this.animationConfig.close) {
        this.$.contentWrapper.classList.add('animating');
        this.playAnimation('close');
      } else {
        IronOverlayBehaviorImpl._renderClosed.apply(this, arguments);
      }
    },

    /**
     * Called when animation finishes on the dropdown (when opening or
     * closing). Responsible for "completing" the process of opening or
     * closing the dropdown by positioning it or setting its display to
     * none.
     */
    _onNeonAnimationFinish: function () {
      this.$.contentWrapper.classList.remove('animating');

      if (this.opened) {
        this._finishRenderOpened();
      } else {
        this._finishRenderClosed();
      }
    },

    /**
     * Constructs the final animation config from different properties used
     * to configure specific parts of the opening and closing animations.
     */
    _updateAnimationConfig: function () {
      // Update the animation node to be the containedElement.
      var animationNode = this.containedElement;
      var animations = [].concat(this.openAnimationConfig || []).concat(this.closeAnimationConfig || []);

      for (var i = 0; i < animations.length; i++) {
        animations[i].node = animationNode;
      }

      this.animationConfig = {
        open: this.openAnimationConfig,
        close: this.closeAnimationConfig
      };
    },

    /**
     * Updates the overlay position based on configured horizontal
     * and vertical alignment.
     */
    _updateOverlayPosition: function () {
      if (this.isAttached) {
        // This triggers iron-resize, and iron-overlay-behavior will call refit if
        // needed.
        this.notifyResize();
      }
    },

    /**
     * Sets scrollAction according to the value of allowOutsideScroll.
     * Prefer setting directly scrollAction.
     */
    _allowOutsideScrollChanged: function (allowOutsideScroll) {
      // Wait until initial values are all set.
      if (!this._readied) {
        return;
      }

      if (!allowOutsideScroll) {
        this.scrollAction = 'lock';
      } else if (!this.scrollAction || this.scrollAction === 'lock') {
        this.scrollAction = 'refit';
      }
    },

    /**
     * Apply focus to focusTarget or containedElement
     */
    _applyFocus: function () {
      var focusTarget = this.focusTarget || this.containedElement;

      if (focusTarget && this.opened && !this.noAutoFocus) {
        focusTarget.focus();
      } else {
        IronOverlayBehaviorImpl._applyFocus.apply(this, arguments);
      }
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * Use `NeonAnimationBehavior` to implement an animation.
   * @polymerBehavior
   */

  const NeonAnimationBehavior = {
    properties: {
      /**
       * Defines the animation timing.
       */
      animationTiming: {
        type: Object,
        value: function () {
          return {
            duration: 500,
            easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
            fill: 'both'
          };
        }
      }
    },

    /**
     * Can be used to determine that elements implement this behavior.
     */
    isNeonAnimation: true,

    /**
     * Do any animation configuration here.
     */
    // configure: function(config) {
    // },
    created: function () {
      if (!document.body.animate) {
        console.warn('No web animations detected. This element will not' + ' function without a web animations polyfill.');
      }
    },

    /**
     * Returns the animation timing by mixing in properties from `config` to the
     * defaults defined by the animation.
     */
    timingFromConfig: function (config) {
      if (config.timing) {
        for (var property in config.timing) {
          this.animationTiming[property] = config.timing[property];
        }
      }

      return this.animationTiming;
    },

    /**
     * Sets `transform` and `transformOrigin` properties along with the prefixed
     * versions.
     */
    setPrefixedProperty: function (node, property, value) {
      var map = {
        'transform': ['webkitTransform'],
        'transformOrigin': ['mozTransformOrigin', 'webkitTransformOrigin']
      };
      var prefixes = map[property];

      for (var prefix, index = 0; prefix = prefixes[index]; index++) {
        node.style[prefix] = value;
      }

      node.style[property] = value;
    },

    /**
     * Called when the animation finishes.
     */
    complete: function (config) {}
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `<fade-in-animation>` animates the opacity of an element from 0 to 1.

  Configuration:
  ```
  {
    name: 'fade-in-animation',
    node: <node>
    timing: <animation-timing>
  }
  ```
  */

  Polymer({
    is: 'fade-in-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      this._effect = new KeyframeEffect(node, [{
        'opacity': '0'
      }, {
        'opacity': '1'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /*
  `<fade-out-animation>` animates the opacity of an element from 1 to 0.

  Configuration:
  ```
  {
    name: 'fade-out-animation',
    node: <node>
    timing: <animation-timing>
  }
  ```
  */

  Polymer({
    is: 'fade-out-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      this._effect = new KeyframeEffect(node, [{
        'opacity': '1'
      }, {
        'opacity': '0'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  Polymer({
    is: 'paper-menu-grow-height-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      var rect = node.getBoundingClientRect();
      var height = rect.height;
      this._effect = new KeyframeEffect(node, [{
        height: height / 2 + 'px'
      }, {
        height: height + 'px'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });
  Polymer({
    is: 'paper-menu-grow-width-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      var rect = node.getBoundingClientRect();
      var width = rect.width;
      this._effect = new KeyframeEffect(node, [{
        width: width / 2 + 'px'
      }, {
        width: width + 'px'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });
  Polymer({
    is: 'paper-menu-shrink-width-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      var rect = node.getBoundingClientRect();
      var width = rect.width;
      this._effect = new KeyframeEffect(node, [{
        width: width + 'px'
      }, {
        width: width - width / 20 + 'px'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });
  Polymer({
    is: 'paper-menu-shrink-height-animation',
    behaviors: [NeonAnimationBehavior],
    configure: function (config) {
      var node = config.node;
      var rect = node.getBoundingClientRect();
      var height = rect.height;
      this.setPrefixedProperty(node, 'transformOrigin', '0 0');
      this._effect = new KeyframeEffect(node, [{
        height: height + 'px',
        transform: 'translateY(0)'
      }, {
        height: height / 2 + 'px',
        transform: 'translateY(-20px)'
      }], this.timingFromConfig(config));
      return this._effect;
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  var config = {
    ANIMATION_CUBIC_BEZIER: 'cubic-bezier(.3,.95,.5,1)',
    MAX_ANIMATION_TIME_MS: 400
  };
  /**
  Material design: [Dropdown
  buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)

  `paper-menu-button` allows one to compose a designated "trigger" element with
  another element that represents "content", to create a dropdown menu that
  displays the "content" when the "trigger" is clicked.

  The child element assigned to the `dropdown-trigger` slot will be used as the
  "trigger" element. The child element assigned to the `dropdown-content` slot
  will be used as the "content" element.

  The `paper-menu-button` is sensitive to its content's `iron-select` events. If
  the "content" element triggers an `iron-select` event, the `paper-menu-button`
  will close automatically.

  Example:

      <paper-menu-button>
        <paper-icon-button icon="menu"
  slot="dropdown-trigger"></paper-icon-button> <paper-listbox
  slot="dropdown-content"> <paper-item>Share</paper-item>
          <paper-item>Settings</paper-item>
          <paper-item>Help</paper-item>
        </paper-listbox>
      </paper-menu-button>

  ### Styling

  The following custom properties and mixins are also available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color`
  `--paper-menu-button` | Mixin applied to the paper-menu-button | `{}`
  `--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}`
  `--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}`
  `--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}`

  @hero hero.svg
  @demo demo/index.html
  */

  const PaperMenuButton = Polymer({
    _template: html$1`
    <style>
      :host {
        display: inline-block;
        position: relative;
        padding: 8px;
        outline: none;

        @apply --paper-menu-button;
      }

      :host([disabled]) {
        cursor: auto;
        color: var(--disabled-text-color);

        @apply --paper-menu-button-disabled;
      }

      iron-dropdown {
        @apply --paper-menu-button-dropdown;
      }

      .dropdown-content {
        @apply --shadow-elevation-2dp;

        position: relative;
        border-radius: 2px;
        background-color: var(--paper-menu-button-dropdown-background, var(--primary-background-color));

        @apply --paper-menu-button-content;
      }

      :host([vertical-align="top"]) .dropdown-content {
        margin-bottom: 20px;
        margin-top: -10px;
        top: 10px;
      }

      :host([vertical-align="bottom"]) .dropdown-content {
        bottom: 10px;
        margin-bottom: -10px;
        margin-top: 20px;
      }

      #trigger {
        cursor: pointer;
      }
    </style>

    <div id="trigger" on-tap="toggle">
      <slot name="dropdown-trigger"></slot>
    </div>

    <iron-dropdown id="dropdown" opened="{{opened}}" horizontal-align="[[horizontalAlign]]" vertical-align="[[verticalAlign]]" dynamic-align="[[dynamicAlign]]" horizontal-offset="[[horizontalOffset]]" vertical-offset="[[verticalOffset]]" no-overlap="[[noOverlap]]" open-animation-config="[[openAnimationConfig]]" close-animation-config="[[closeAnimationConfig]]" no-animations="[[noAnimations]]" focus-target="[[_dropdownContent]]" allow-outside-scroll="[[allowOutsideScroll]]" restore-focus-on-close="[[restoreFocusOnClose]]" on-iron-overlay-canceled="__onIronOverlayCanceled">
      <div slot="dropdown-content" class="dropdown-content">
        <slot id="content" name="dropdown-content"></slot>
      </div>
    </iron-dropdown>
`,
    is: 'paper-menu-button',

    /**
     * Fired when the dropdown opens.
     *
     * @event paper-dropdown-open
     */

    /**
     * Fired when the dropdown closes.
     *
     * @event paper-dropdown-close
     */
    behaviors: [IronA11yKeysBehavior, IronControlState],
    properties: {
      /**
       * True if the content is currently displayed.
       */
      opened: {
        type: Boolean,
        value: false,
        notify: true,
        observer: '_openedChanged'
      },

      /**
       * The orientation against which to align the menu dropdown
       * horizontally relative to the dropdown trigger.
       */
      horizontalAlign: {
        type: String,
        value: 'left',
        reflectToAttribute: true
      },

      /**
       * The orientation against which to align the menu dropdown
       * vertically relative to the dropdown trigger.
       */
      verticalAlign: {
        type: String,
        value: 'top',
        reflectToAttribute: true
      },

      /**
       * If true, the `horizontalAlign` and `verticalAlign` properties will
       * be considered preferences instead of strict requirements when
       * positioning the dropdown and may be changed if doing so reduces
       * the area of the dropdown falling outside of `fitInto`.
       */
      dynamicAlign: {
        type: Boolean
      },

      /**
       * A pixel value that will be added to the position calculated for the
       * given `horizontalAlign`. Use a negative value to offset to the
       * left, or a positive value to offset to the right.
       */
      horizontalOffset: {
        type: Number,
        value: 0,
        notify: true
      },

      /**
       * A pixel value that will be added to the position calculated for the
       * given `verticalAlign`. Use a negative value to offset towards the
       * top, or a positive value to offset towards the bottom.
       */
      verticalOffset: {
        type: Number,
        value: 0,
        notify: true
      },

      /**
       * If true, the dropdown will be positioned so that it doesn't overlap
       * the button.
       */
      noOverlap: {
        type: Boolean
      },

      /**
       * Set to true to disable animations when opening and closing the
       * dropdown.
       */
      noAnimations: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable automatically closing the dropdown after
       * a selection has been made.
       */
      ignoreSelect: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to enable automatically closing the dropdown after an
       * item has been activated, even if the selection did not change.
       */
      closeOnActivate: {
        type: Boolean,
        value: false
      },

      /**
       * An animation config. If provided, this will be used to animate the
       * opening of the dropdown.
       */
      openAnimationConfig: {
        type: Object,
        value: function () {
          return [{
            name: 'fade-in-animation',
            timing: {
              delay: 100,
              duration: 200
            }
          }, {
            name: 'paper-menu-grow-width-animation',
            timing: {
              delay: 100,
              duration: 150,
              easing: config.ANIMATION_CUBIC_BEZIER
            }
          }, {
            name: 'paper-menu-grow-height-animation',
            timing: {
              delay: 100,
              duration: 275,
              easing: config.ANIMATION_CUBIC_BEZIER
            }
          }];
        }
      },

      /**
       * An animation config. If provided, this will be used to animate the
       * closing of the dropdown.
       */
      closeAnimationConfig: {
        type: Object,
        value: function () {
          return [{
            name: 'fade-out-animation',
            timing: {
              duration: 150
            }
          }, {
            name: 'paper-menu-shrink-width-animation',
            timing: {
              delay: 100,
              duration: 50,
              easing: config.ANIMATION_CUBIC_BEZIER
            }
          }, {
            name: 'paper-menu-shrink-height-animation',
            timing: {
              duration: 200,
              easing: 'ease-in'
            }
          }];
        }
      },

      /**
       * By default, the dropdown will constrain scrolling on the page
       * to itself when opened.
       * Set to true in order to prevent scroll from being constrained
       * to the dropdown when it opens.
       */
      allowOutsideScroll: {
        type: Boolean,
        value: false
      },

      /**
       * Whether focus should be restored to the button when the menu closes.
       */
      restoreFocusOnClose: {
        type: Boolean,
        value: true
      },

      /**
       * This is the element intended to be bound as the focus target
       * for the `iron-dropdown` contained by `paper-menu-button`.
       */
      _dropdownContent: {
        type: Object
      }
    },
    hostAttributes: {
      role: 'group',
      'aria-haspopup': 'true'
    },
    listeners: {
      'iron-activate': '_onIronActivate',
      'iron-select': '_onIronSelect'
    },

    /**
     * The content element that is contained by the menu button, if any.
     */
    get contentElement() {
      // Polymer 2.x returns slot.assignedNodes which can contain text nodes.
      var nodes = dom(this.$.content).getDistributedNodes();

      for (var i = 0, l = nodes.length; i < l; i++) {
        if (nodes[i].nodeType === Node.ELEMENT_NODE) {
          return nodes[i];
        }
      }
    },

    /**
     * Toggles the dropdown content between opened and closed.
     */
    toggle: function () {
      if (this.opened) {
        this.close();
      } else {
        this.open();
      }
    },

    /**
     * Make the dropdown content appear as an overlay positioned relative
     * to the dropdown trigger.
     */
    open: function () {
      if (this.disabled) {
        return;
      }

      this.$.dropdown.open();
    },

    /**
     * Hide the dropdown content.
     */
    close: function () {
      this.$.dropdown.close();
    },

    /**
     * When an `iron-select` event is received, the dropdown should
     * automatically close on the assumption that a value has been chosen.
     *
     * @param {CustomEvent} event A CustomEvent instance with type
     * set to `"iron-select"`.
     */
    _onIronSelect: function (event) {
      if (!this.ignoreSelect) {
        this.close();
      }
    },

    /**
     * Closes the dropdown when an `iron-activate` event is received if
     * `closeOnActivate` is true.
     *
     * @param {CustomEvent} event A CustomEvent of type 'iron-activate'.
     */
    _onIronActivate: function (event) {
      if (this.closeOnActivate) {
        this.close();
      }
    },

    /**
     * When the dropdown opens, the `paper-menu-button` fires `paper-open`.
     * When the dropdown closes, the `paper-menu-button` fires `paper-close`.
     *
     * @param {boolean} opened True if the dropdown is opened, otherwise false.
     * @param {boolean} oldOpened The previous value of `opened`.
     */
    _openedChanged: function (opened, oldOpened) {
      if (opened) {
        // TODO(cdata): Update this when we can measure changes in distributed
        // children in an idiomatic way.
        // We poke this property in case the element has changed. This will
        // cause the focus target for the `iron-dropdown` to be updated as
        // necessary:
        this._dropdownContent = this.contentElement;
        this.fire('paper-dropdown-open');
      } else if (oldOpened != null) {
        this.fire('paper-dropdown-close');
      }
    },

    /**
     * If the dropdown is open when disabled becomes true, close the
     * dropdown.
     *
     * @param {boolean} disabled True if disabled, otherwise false.
     */
    _disabledChanged: function (disabled) {
      IronControlState._disabledChanged.apply(this, arguments);

      if (disabled && this.opened) {
        this.close();
      }
    },
    __onIronOverlayCanceled: function (event) {
      var uiEvent = event.detail;
      var trigger = this.$.trigger;
      var path = dom(uiEvent).path;

      if (path.indexOf(trigger) > -1) {
        event.preventDefault();
      }
    }
  });
  Object.keys(config).forEach(function (key) {
    PaperMenuButton[key] = config[key];
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  class IronSelection {
    /**
     * @param {!Function} selectCallback
     * @suppress {missingProvide}
     */
    constructor(selectCallback) {
      this.selection = [];
      this.selectCallback = selectCallback;
    }
    /**
     * Retrieves the selected item(s).
     *
     * @returns Returns the selected item(s). If the multi property is true,
     * `get` will return an array, otherwise it will return
     * the selected item or undefined if there is no selection.
     */


    get() {
      return this.multi ? this.selection.slice() : this.selection[0];
    }
    /**
     * Clears all the selection except the ones indicated.
     *
     * @param {Array} excludes items to be excluded.
     */


    clear(excludes) {
      this.selection.slice().forEach(function (item) {
        if (!excludes || excludes.indexOf(item) < 0) {
          this.setItemSelected(item, false);
        }
      }, this);
    }
    /**
     * Indicates if a given item is selected.
     *
     * @param {*} item The item whose selection state should be checked.
     * @return {boolean} Returns true if `item` is selected.
     */


    isSelected(item) {
      return this.selection.indexOf(item) >= 0;
    }
    /**
     * Sets the selection state for a given item to either selected or deselected.
     *
     * @param {*} item The item to select.
     * @param {boolean} isSelected True for selected, false for deselected.
     */


    setItemSelected(item, isSelected) {
      if (item != null) {
        if (isSelected !== this.isSelected(item)) {
          // proceed to update selection only if requested state differs from
          // current
          if (isSelected) {
            this.selection.push(item);
          } else {
            var i = this.selection.indexOf(item);

            if (i >= 0) {
              this.selection.splice(i, 1);
            }
          }

          if (this.selectCallback) {
            this.selectCallback(item, isSelected);
          }
        }
      }
    }
    /**
     * Sets the selection state for a given item. If the `multi` property
     * is true, then the selected state of `item` will be toggled; otherwise
     * the `item` will be selected.
     *
     * @param {*} item The item to select.
     */


    select(item) {
      if (this.multi) {
        this.toggle(item);
      } else if (this.get() !== item) {
        this.setItemSelected(this.get(), false);
        this.setItemSelected(item, true);
      }
    }
    /**
     * Toggles the selection state for `item`.
     *
     * @param {*} item The item to toggle.
     */


    toggle(item) {
      this.setItemSelected(item, !this.isSelected(item));
    }

  }

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @polymerBehavior
   */

  const IronSelectableBehavior = {
    /**
     * Fired when iron-selector is activated (selected or deselected).
     * It is fired before the selected items are changed.
     * Cancel the event to abort selection.
     *
     * @event iron-activate
     */

    /**
     * Fired when an item is selected
     *
     * @event iron-select
     */

    /**
     * Fired when an item is deselected
     *
     * @event iron-deselect
     */

    /**
     * Fired when the list of selectable items changes (e.g., items are
     * added or removed). The detail of the event is a mutation record that
     * describes what changed.
     *
     * @event iron-items-changed
     */
    properties: {
      /**
       * If you want to use an attribute value or property of an element for
       * `selected` instead of the index, set this to the name of the attribute
       * or property. Hyphenated values are converted to camel case when used to
       * look up the property of a selectable element. Camel cased values are
       * *not* converted to hyphenated values for attribute lookup. It's
       * recommended that you provide the hyphenated form of the name so that
       * selection works in both cases. (Use `attr-or-property-name` instead of
       * `attrOrPropertyName`.)
       */
      attrForSelected: {
        type: String,
        value: null
      },

      /**
       * Gets or sets the selected element. The default is to use the index of the
       * item.
       * @type {string|number}
       */
      selected: {
        type: String,
        notify: true
      },

      /**
       * Returns the currently selected item.
       *
       * @type {?Object}
       */
      selectedItem: {
        type: Object,
        readOnly: true,
        notify: true
      },

      /**
       * The event that fires from items when they are selected. Selectable
       * will listen for this event from items and update the selection state.
       * Set to empty string to listen to no events.
       */
      activateEvent: {
        type: String,
        value: 'tap',
        observer: '_activateEventChanged'
      },

      /**
       * This is a CSS selector string.  If this is set, only items that match the
       * CSS selector are selectable.
       */
      selectable: String,

      /**
       * The class to set on elements when selected.
       */
      selectedClass: {
        type: String,
        value: 'iron-selected'
      },

      /**
       * The attribute to set on elements when selected.
       */
      selectedAttribute: {
        type: String,
        value: null
      },

      /**
       * Default fallback if the selection based on selected with
       * `attrForSelected` is not found.
       */
      fallbackSelection: {
        type: String,
        value: null
      },

      /**
       * The list of items from which a selection can be made.
       */
      items: {
        type: Array,
        readOnly: true,
        notify: true,
        value: function () {
          return [];
        }
      },

      /**
       * The set of excluded elements where the key is the `localName`
       * of the element that will be ignored from the item list.
       *
       * @default {template: 1}
       */
      _excludedLocalNames: {
        type: Object,
        value: function () {
          return {
            'template': 1,
            'dom-bind': 1,
            'dom-if': 1,
            'dom-repeat': 1
          };
        }
      }
    },
    observers: ['_updateAttrForSelected(attrForSelected)', '_updateSelected(selected)', '_checkFallback(fallbackSelection)'],
    created: function () {
      this._bindFilterItem = this._filterItem.bind(this);
      this._selection = new IronSelection(this._applySelection.bind(this));
    },
    attached: function () {
      this._observer = this._observeItems(this);

      this._addListener(this.activateEvent);
    },
    detached: function () {
      if (this._observer) {
        dom(this).unobserveNodes(this._observer);
      }

      this._removeListener(this.activateEvent);
    },

    /**
     * Returns the index of the given item.
     *
     * @method indexOf
     * @param {Object} item
     * @returns Returns the index of the item
     */
    indexOf: function (item) {
      return this.items ? this.items.indexOf(item) : -1;
    },

    /**
     * Selects the given value.
     *
     * @method select
     * @param {string|number} value the value to select.
     */
    select: function (value) {
      this.selected = value;
    },

    /**
     * Selects the previous item.
     *
     * @method selectPrevious
     */
    selectPrevious: function () {
      var length = this.items.length;
      var index = length - 1;

      if (this.selected !== undefined) {
        index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length;
      }

      this.selected = this._indexToValue(index);
    },

    /**
     * Selects the next item.
     *
     * @method selectNext
     */
    selectNext: function () {
      var index = 0;

      if (this.selected !== undefined) {
        index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length;
      }

      this.selected = this._indexToValue(index);
    },

    /**
     * Selects the item at the given index.
     *
     * @method selectIndex
     */
    selectIndex: function (index) {
      this.select(this._indexToValue(index));
    },

    /**
     * Force a synchronous update of the `items` property.
     *
     * NOTE: Consider listening for the `iron-items-changed` event to respond to
     * updates to the set of selectable items after updates to the DOM list and
     * selection state have been made.
     *
     * WARNING: If you are using this method, you should probably consider an
     * alternate approach. Synchronously querying for items is potentially
     * slow for many use cases. The `items` property will update asynchronously
     * on its own to reflect selectable items in the DOM.
     */
    forceSynchronousItemUpdate: function () {
      if (this._observer && typeof this._observer.flush === 'function') {
        // NOTE(bicknellr): `dom.flush` above is no longer sufficient to trigger
        // `observeNodes` callbacks. Polymer 2.x returns an object from
        // `observeNodes` with a `flush` that synchronously gives the callback any
        // pending MutationRecords (retrieved with `takeRecords`). Any case where
        // ShadyDOM flushes were expected to synchronously trigger item updates
        // will now require calling `forceSynchronousItemUpdate`.
        this._observer.flush();
      } else {
        this._updateItems();
      }
    },

    // UNUSED, FOR API COMPATIBILITY
    get _shouldUpdateSelection() {
      return this.selected != null;
    },

    _checkFallback: function () {
      this._updateSelected();
    },
    _addListener: function (eventName) {
      this.listen(this, eventName, '_activateHandler');
    },
    _removeListener: function (eventName) {
      this.unlisten(this, eventName, '_activateHandler');
    },
    _activateEventChanged: function (eventName, old) {
      this._removeListener(old);

      this._addListener(eventName);
    },
    _updateItems: function () {
      var nodes = dom(this).queryDistributedElements(this.selectable || '*');
      nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);

      this._setItems(nodes);
    },
    _updateAttrForSelected: function () {
      if (this.selectedItem) {
        this.selected = this._valueForItem(this.selectedItem);
      }
    },
    _updateSelected: function () {
      this._selectSelected(this.selected);
    },
    _selectSelected: function (selected) {
      if (!this.items) {
        return;
      }

      var item = this._valueToItem(this.selected);

      if (item) {
        this._selection.select(item);
      } else {
        this._selection.clear();
      } // Check for items, since this array is populated only when attached
      // Since Number(0) is falsy, explicitly check for undefined


      if (this.fallbackSelection && this.items.length && this._selection.get() === undefined) {
        this.selected = this.fallbackSelection;
      }
    },
    _filterItem: function (node) {
      return !this._excludedLocalNames[node.localName];
    },
    _valueToItem: function (value) {
      return value == null ? null : this.items[this._valueToIndex(value)];
    },
    _valueToIndex: function (value) {
      if (this.attrForSelected) {
        for (var i = 0, item; item = this.items[i]; i++) {
          if (this._valueForItem(item) == value) {
            return i;
          }
        }
      } else {
        return Number(value);
      }
    },
    _indexToValue: function (index) {
      if (this.attrForSelected) {
        var item = this.items[index];

        if (item) {
          return this._valueForItem(item);
        }
      } else {
        return index;
      }
    },
    _valueForItem: function (item) {
      if (!item) {
        return null;
      }

      if (!this.attrForSelected) {
        var i = this.indexOf(item);
        return i === -1 ? null : i;
      }

      var propValue = item[dashToCamelCase(this.attrForSelected)];
      return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected);
    },
    _applySelection: function (item, isSelected) {
      if (this.selectedClass) {
        this.toggleClass(this.selectedClass, isSelected, item);
      }

      if (this.selectedAttribute) {
        this.toggleAttribute(this.selectedAttribute, isSelected, item);
      }

      this._selectionChange();

      this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {
        item: item
      });
    },
    _selectionChange: function () {
      this._setSelectedItem(this._selection.get());
    },
    // observe items change under the given node.
    _observeItems: function (node) {
      return dom(node).observeNodes(function (mutation) {
        this._updateItems();

        this._updateSelected(); // Let other interested parties know about the change so that
        // we don't have to recreate mutation observers everywhere.


        this.fire('iron-items-changed', mutation, {
          bubbles: false,
          cancelable: false
        });
      });
    },
    _activateHandler: function (e) {
      var t = e.target;
      var items = this.items;

      while (t && t != this) {
        var i = items.indexOf(t);

        if (i >= 0) {
          var value = this._indexToValue(i);

          this._itemActivate(value, t);

          return;
        }

        t = t.parentNode;
      }
    },
    _itemActivate: function (value, item) {
      if (!this.fire('iron-activate', {
        selected: value,
        item: item
      }, {
        cancelable: true
      }).defaultPrevented) {
        this.select(value);
      }
    }
  };

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * @polymerBehavior IronMultiSelectableBehavior
   */

  const IronMultiSelectableBehaviorImpl = {
    properties: {
      /**
       * If true, multiple selections are allowed.
       */
      multi: {
        type: Boolean,
        value: false,
        observer: 'multiChanged'
      },

      /**
       * Gets or sets the selected elements. This is used instead of `selected`
       * when `multi` is true.
       */
      selectedValues: {
        type: Array,
        notify: true,
        value: function () {
          return [];
        }
      },

      /**
       * Returns an array of currently selected items.
       */
      selectedItems: {
        type: Array,
        readOnly: true,
        notify: true,
        value: function () {
          return [];
        }
      }
    },
    observers: ['_updateSelected(selectedValues.splices)'],

    /**
     * Selects the given value. If the `multi` property is true, then the selected
     * state of the `value` will be toggled; otherwise the `value` will be
     * selected.
     *
     * @method select
     * @param {string|number} value the value to select.
     */
    select: function (value) {
      if (this.multi) {
        this._toggleSelected(value);
      } else {
        this.selected = value;
      }
    },
    multiChanged: function (multi) {
      this._selection.multi = multi;

      this._updateSelected();
    },

    // UNUSED, FOR API COMPATIBILITY
    get _shouldUpdateSelection() {
      return this.selected != null || this.selectedValues != null && this.selectedValues.length;
    },

    _updateAttrForSelected: function () {
      if (!this.multi) {
        IronSelectableBehavior._updateAttrForSelected.apply(this);
      } else if (this.selectedItems && this.selectedItems.length > 0) {
        this.selectedValues = this.selectedItems.map(function (selectedItem) {
          return this._indexToValue(this.indexOf(selectedItem));
        }, this).filter(function (unfilteredValue) {
          return unfilteredValue != null;
        }, this);
      }
    },
    _updateSelected: function () {
      if (this.multi) {
        this._selectMulti(this.selectedValues);
      } else {
        this._selectSelected(this.selected);
      }
    },
    _selectMulti: function (values) {
      values = values || [];
      var selectedItems = (this._valuesToItems(values) || []).filter(function (item) {
        return item !== null && item !== undefined;
      }); // clear all but the current selected items

      this._selection.clear(selectedItems); // select only those not selected yet


      for (var i = 0; i < selectedItems.length; i++) {
        this._selection.setItemSelected(selectedItems[i], true);
      } // Check for items, since this array is populated only when attached


      if (this.fallbackSelection && !this._selection.get().length) {
        var fallback = this._valueToItem(this.fallbackSelection);

        if (fallback) {
          this.select(this.fallbackSelection);
        }
      }
    },
    _selectionChange: function () {
      var s = this._selection.get();

      if (this.multi) {
        this._setSelectedItems(s);

        this._setSelectedItem(s.length ? s[0] : null);
      } else {
        if (s !== null && s !== undefined) {
          this._setSelectedItems([s]);

          this._setSelectedItem(s);
        } else {
          this._setSelectedItems([]);

          this._setSelectedItem(null);
        }
      }
    },
    _toggleSelected: function (value) {
      var i = this.selectedValues.indexOf(value);
      var unselected = i < 0;

      if (unselected) {
        this.push('selectedValues', value);
      } else {
        this.splice('selectedValues', i, 1);
      }
    },
    _valuesToItems: function (values) {
      return values == null ? null : values.map(function (value) {
        return this._valueToItem(value);
      }, this);
    }
  };
  /** @polymerBehavior */

  const IronMultiSelectableBehavior = [IronSelectableBehavior, IronMultiSelectableBehaviorImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * `IronMenuBehavior` implements accessible menu behavior.
   *
   * @demo demo/index.html
   * @polymerBehavior IronMenuBehavior
   */

  const IronMenuBehaviorImpl = {
    properties: {
      /**
       * Returns the currently focused item.
       * @type {?Object}
       */
      focusedItem: {
        observer: '_focusedItemChanged',
        readOnly: true,
        type: Object
      },

      /**
       * The attribute to use on menu items to look up the item title. Typing the
       * first letter of an item when the menu is open focuses that item. If
       * unset, `textContent` will be used.
       */
      attrForItemTitle: {
        type: String
      },

      /**
       * @type {boolean}
       */
      disabled: {
        type: Boolean,
        value: false,
        observer: '_disabledChanged'
      }
    },

    /**
     * The list of keys has been taken from
     * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState
     * @private
     */
    _MODIFIER_KEYS: ['Alt', 'AltGraph', 'CapsLock', 'Control', 'Fn', 'FnLock', 'Hyper', 'Meta', 'NumLock', 'OS', 'ScrollLock', 'Shift', 'Super', 'Symbol', 'SymbolLock'],

    /** @private */
    _SEARCH_RESET_TIMEOUT_MS: 1000,

    /** @private */
    _previousTabIndex: 0,
    hostAttributes: {
      'role': 'menu'
    },
    observers: ['_updateMultiselectable(multi)'],
    listeners: {
      'focus': '_onFocus',
      'keydown': '_onKeydown',
      'iron-items-changed': '_onIronItemsChanged'
    },

    /**
     * @type {!Object}
     */
    keyBindings: {
      'up': '_onUpKey',
      'down': '_onDownKey',
      'esc': '_onEscKey',
      'shift+tab:keydown': '_onShiftTabDown'
    },
    attached: function () {
      this._resetTabindices();
    },

    /**
     * Selects the given value. If the `multi` property is true, then the selected
     * state of the `value` will be toggled; otherwise the `value` will be
     * selected.
     *
     * @param {string|number} value the value to select.
     */
    select: function (value) {
      // Cancel automatically focusing a default item if the menu received focus
      // through a user action selecting a particular item.
      if (this._defaultFocusAsync) {
        this.cancelAsync(this._defaultFocusAsync);
        this._defaultFocusAsync = null;
      }

      var item = this._valueToItem(value);

      if (item && item.hasAttribute('disabled')) return;

      this._setFocusedItem(item);

      IronMultiSelectableBehaviorImpl.select.apply(this, arguments);
    },

    /**
     * Resets all tabindex attributes to the appropriate value based on the
     * current selection state. The appropriate value is `0` (focusable) for
     * the default selected item, and `-1` (not keyboard focusable) for all
     * other items. Also sets the correct initial values for aria-selected
     * attribute, true for default selected item and false for others.
     */
    _resetTabindices: function () {
      var firstSelectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem;
      this.items.forEach(function (item) {
        item.setAttribute('tabindex', item === firstSelectedItem ? '0' : '-1');
        item.setAttribute('aria-selected', this._selection.isSelected(item));
      }, this);
    },

    /**
     * Sets appropriate ARIA based on whether or not the menu is meant to be
     * multi-selectable.
     *
     * @param {boolean} multi True if the menu should be multi-selectable.
     */
    _updateMultiselectable: function (multi) {
      if (multi) {
        this.setAttribute('aria-multiselectable', 'true');
      } else {
        this.removeAttribute('aria-multiselectable');
      }
    },

    /**
     * Given a KeyboardEvent, this method will focus the appropriate item in the
     * menu (if there is a relevant item, and it is possible to focus it).
     *
     * @param {KeyboardEvent} event A KeyboardEvent.
     */
    _focusWithKeyboardEvent: function (event) {
      // Make sure that the key pressed is not a modifier key.
      // getModifierState is not being used, as it is not available in Safari
      // earlier than 10.0.2 (https://trac.webkit.org/changeset/206725/webkit)
      if (this._MODIFIER_KEYS.indexOf(event.key) !== -1) return;
      this.cancelDebouncer('_clearSearchText');
      var searchText = this._searchText || '';
      var key = event.key && event.key.length == 1 ? event.key : String.fromCharCode(event.keyCode);
      searchText += key.toLocaleLowerCase();
      var searchLength = searchText.length;

      for (var i = 0, item; item = this.items[i]; i++) {
        if (item.hasAttribute('disabled')) {
          continue;
        }

        var attr = this.attrForItemTitle || 'textContent';
        var title = (item[attr] || item.getAttribute(attr) || '').trim();

        if (title.length < searchLength) {
          continue;
        }

        if (title.slice(0, searchLength).toLocaleLowerCase() == searchText) {
          this._setFocusedItem(item);

          break;
        }
      }

      this._searchText = searchText;
      this.debounce('_clearSearchText', this._clearSearchText, this._SEARCH_RESET_TIMEOUT_MS);
    },
    _clearSearchText: function () {
      this._searchText = '';
    },

    /**
     * Focuses the previous item (relative to the currently focused item) in the
     * menu, disabled items will be skipped.
     * Loop until length + 1 to handle case of single item in menu.
     */
    _focusPrevious: function () {
      var length = this.items.length;
      var curFocusIndex = Number(this.indexOf(this.focusedItem));

      for (var i = 1; i < length + 1; i++) {
        var item = this.items[(curFocusIndex - i + length) % length];

        if (!item.hasAttribute('disabled')) {
          var owner = dom(item).getOwnerRoot() || document;

          this._setFocusedItem(item); // Focus might not have worked, if the element was hidden or not
          // focusable. In that case, try again.


          if (dom(owner).activeElement == item) {
            return;
          }
        }
      }
    },

    /**
     * Focuses the next item (relative to the currently focused item) in the
     * menu, disabled items will be skipped.
     * Loop until length + 1 to handle case of single item in menu.
     */
    _focusNext: function () {
      var length = this.items.length;
      var curFocusIndex = Number(this.indexOf(this.focusedItem));

      for (var i = 1; i < length + 1; i++) {
        var item = this.items[(curFocusIndex + i) % length];

        if (!item.hasAttribute('disabled')) {
          var owner = dom(item).getOwnerRoot() || document;

          this._setFocusedItem(item); // Focus might not have worked, if the element was hidden or not
          // focusable. In that case, try again.


          if (dom(owner).activeElement == item) {
            return;
          }
        }
      }
    },

    /**
     * Mutates items in the menu based on provided selection details, so that
     * all items correctly reflect selection state.
     *
     * @param {Element} item An item in the menu.
     * @param {boolean} isSelected True if the item should be shown in a
     * selected state, otherwise false.
     */
    _applySelection: function (item, isSelected) {
      if (isSelected) {
        item.setAttribute('aria-selected', 'true');
      } else {
        item.setAttribute('aria-selected', 'false');
      }

      IronSelectableBehavior._applySelection.apply(this, arguments);
    },

    /**
     * Discretely updates tabindex values among menu items as the focused item
     * changes.
     *
     * @param {Element} focusedItem The element that is currently focused.
     * @param {?Element} old The last element that was considered focused, if
     * applicable.
     */
    _focusedItemChanged: function (focusedItem, old) {
      old && old.setAttribute('tabindex', '-1');

      if (focusedItem && !focusedItem.hasAttribute('disabled') && !this.disabled) {
        focusedItem.setAttribute('tabindex', '0');
        focusedItem.focus();
      }
    },

    /**
     * A handler that responds to mutation changes related to the list of items
     * in the menu.
     *
     * @param {CustomEvent} event An event containing mutation records as its
     * detail.
     */
    _onIronItemsChanged: function (event) {
      if (event.detail.addedNodes.length) {
        this._resetTabindices();
      }
    },

    /**
     * Handler that is called when a shift+tab keypress is detected by the menu.
     *
     * @param {CustomEvent} event A key combination event.
     */
    _onShiftTabDown: function (event) {
      var oldTabIndex = this.getAttribute('tabindex');
      IronMenuBehaviorImpl._shiftTabPressed = true;

      this._setFocusedItem(null);

      this.setAttribute('tabindex', '-1');
      this.async(function () {
        this.setAttribute('tabindex', oldTabIndex);
        IronMenuBehaviorImpl._shiftTabPressed = false; // NOTE(cdata): polymer/polymer#1305
      }, 1);
    },

    /**
     * Handler that is called when the menu receives focus.
     *
     * @param {FocusEvent} event A focus event.
     */
    _onFocus: function (event) {
      if (IronMenuBehaviorImpl._shiftTabPressed) {
        // do not focus the menu itself
        return;
      } // Do not focus the selected tab if the deepest target is part of the
      // menu element's local DOM and is focusable.


      var rootTarget =
      /** @type {?HTMLElement} */
      dom(event).rootTarget;

      if (rootTarget !== this && typeof rootTarget.tabIndex !== 'undefined' && !this.isLightDescendant(rootTarget)) {
        return;
      } // clear the cached focus item


      this._defaultFocusAsync = this.async(function () {
        // focus the selected item when the menu receives focus, or the first item
        // if no item is selected
        var firstSelectedItem = this.multi ? this.selectedItems && this.selectedItems[0] : this.selectedItem;

        this._setFocusedItem(null);

        if (firstSelectedItem) {
          this._setFocusedItem(firstSelectedItem);
        } else if (this.items[0]) {
          // We find the first none-disabled item (if one exists)
          this._focusNext();
        }
      });
    },

    /**
     * Handler that is called when the up key is pressed.
     *
     * @param {CustomEvent} event A key combination event.
     */
    _onUpKey: function (event) {
      // up and down arrows moves the focus
      this._focusPrevious();

      event.detail.keyboardEvent.preventDefault();
    },

    /**
     * Handler that is called when the down key is pressed.
     *
     * @param {CustomEvent} event A key combination event.
     */
    _onDownKey: function (event) {
      this._focusNext();

      event.detail.keyboardEvent.preventDefault();
    },

    /**
     * Handler that is called when the esc key is pressed.
     *
     * @param {CustomEvent} event A key combination event.
     */
    _onEscKey: function (event) {
      var focusedItem = this.focusedItem;

      if (focusedItem) {
        focusedItem.blur();
      }
    },

    /**
     * Handler that is called when a keydown event is detected.
     *
     * @param {KeyboardEvent} event A keyboard event.
     */
    _onKeydown: function (event) {
      if (!this.keyboardEventMatchesKeys(event, 'up down esc')) {
        // all other keys focus the menu item starting with that character
        this._focusWithKeyboardEvent(event);
      }

      event.stopPropagation();
    },
    // override _activateHandler
    _activateHandler: function (event) {
      IronSelectableBehavior._activateHandler.call(this, event);

      event.stopPropagation();
    },

    /**
     * Updates this element's tab index when it's enabled/disabled.
     * @param {boolean} disabled
     */
    _disabledChanged: function (disabled) {
      if (disabled) {
        this._previousTabIndex = this.hasAttribute('tabindex') ? this.tabIndex : 0;
        this.removeAttribute('tabindex'); // No tabindex means not tab-able or select-able.
      } else if (!this.hasAttribute('tabindex')) {
        this.setAttribute('tabindex', this._previousTabIndex);
      }
    }
  };
  IronMenuBehaviorImpl._shiftTabPressed = false;
  /** @polymerBehavior */

  const IronMenuBehavior = [IronMultiSelectableBehavior, IronA11yKeysBehavior, IronMenuBehaviorImpl];

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  Material design:
  [Menus](https://www.google.com/design/spec/components/menus.html)

  `<paper-listbox>` implements an accessible listbox control with Material Design
  styling. The focused item is highlighted, and the selected item has bolded text.

      <paper-listbox>
        <paper-item>Item 1</paper-item>
        <paper-item>Item 2</paper-item>
      </paper-listbox>

  An initial selection can be specified with the `selected` attribute.

      <paper-listbox selected="0">
        <paper-item>Item 1</paper-item>
        <paper-item>Item 2</paper-item>
      </paper-listbox>

  Make a multi-select listbox with the `multi` attribute. Items in a multi-select
  listbox can be deselected, and multiple item can be selected.

      <paper-listbox multi>
        <paper-item>Item 1</paper-item>
        <paper-item>Item 2</paper-item>
      </paper-listbox>

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-listbox-background-color`   | Menu background color |
  `--primary-background-color`
  `--paper-listbox-color`              | Menu foreground color |
  `--primary-text-color`
  `--paper-listbox`                    | Mixin applied to the listbox | `{}`

  ### Accessibility

  `<paper-listbox>` has `role="listbox"` by default. A multi-select listbox will
  also have `aria-multiselectable` set. It implements key bindings to navigate
  through the listbox with the up and down arrow keys, esc to exit the listbox,
  and enter to activate a listbox item. Typing the first letter of a listbox item
  will also focus it.

  @group Paper Elements
  @element paper-listbox
  @demo demo/index.html
  */

  Polymer({
    _template: html$1`
    <style>
      :host {
        display: block;
        padding: 8px 0;

        background: var(--paper-listbox-background-color, var(--primary-background-color));
        color: var(--paper-listbox-color, var(--primary-text-color));

        @apply --paper-listbox;
      }
    </style>

    <slot></slot>
`,
    is: 'paper-listbox',
    behaviors: [IronMenuBehavior],

    /** @private */
    hostAttributes: {
      role: 'listbox'
    }
  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
  Material design:
  [Lists](https://www.google.com/design/spec/components/lists.html)

  `<paper-item>` is an interactive list item. By default, it is a horizontal
  flexbox.

      <paper-item>Item</paper-item>

  Use this element with `<paper-item-body>` to make Material Design styled
  two-line and three-line items.

      <paper-item>
        <paper-item-body two-line>
          <div>Show your status</div>
          <div secondary>Your status is visible to everyone</div>
        </paper-item-body>
        <iron-icon icon="warning"></iron-icon>
      </paper-item>

  To use `paper-item` as a link, wrap it in an anchor tag. Since `paper-item` will
  already receive focus, you may want to prevent the anchor tag from receiving
  focus as well by setting its tabindex to -1.

      <a href="https://www.polymer-project.org/" tabindex="-1">
        <paper-item raised>Polymer Project</paper-item>
      </a>

  If you are concerned about performance and want to use `paper-item` in a
  `paper-listbox` with many items, you can just use a native `button` with the
  `paper-item` class applied (provided you have correctly included the shared
  styles):

      <style is="custom-style" include="paper-item-shared-styles"></style>

      <paper-listbox>
        <button class="paper-item" role="option">Inbox</button>
        <button class="paper-item" role="option">Starred</button>
        <button class="paper-item" role="option">Sent mail</button>
      </paper-listbox>

  ### Styling

  The following custom properties and mixins are available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-item-min-height` | Minimum height of the item | `48px`
  `--paper-item` | Mixin applied to the item | `{}`
  `--paper-item-selected-weight` | The font weight of a selected item | `bold`
  `--paper-item-selected` | Mixin applied to selected paper-items | `{}`
  `--paper-item-disabled-color` | The color for disabled paper-items | `--disabled-text-color`
  `--paper-item-disabled` | Mixin applied to disabled paper-items | `{}`
  `--paper-item-focused` | Mixin applied to focused paper-items | `{}`
  `--paper-item-focused-before` | Mixin applied to :before focused paper-items | `{}`

  ### Accessibility

  This element has `role="listitem"` by default. Depending on usage, it may be
  more appropriate to set `role="menuitem"`, `role="menuitemcheckbox"` or
  `role="menuitemradio"`.

      <paper-item role="menuitemcheckbox">
        <paper-item-body>
          Show your status
        </paper-item-body>
        <paper-checkbox></paper-checkbox>
      </paper-item>

  @group Paper Elements
  @element paper-item
  @demo demo/index.html
  */

  Polymer({
    _template: html$1`
    <style include="paper-item-shared-styles">
      :host {
        @apply --layout-horizontal;
        @apply --layout-center;
        @apply --paper-font-subhead;

        @apply --paper-item;
      }
    </style>
    <slot></slot>
`,
    is: 'paper-item',
    behaviors: [PaperItemBehavior]
  });

  let HaIconButton = _decorate([customElement("ha-icon-button")], function (_initialize, _LitElement) {
    class HaIconButton extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaIconButton,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean,
          reflect: true
        })],
        key: "disabled",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: String
        })],
        key: "icon",

        value() {
          return "";
        }

      }, {
        kind: "field",
        decorators: [property({
          type: String
        })],
        key: "label",

        value() {
          return "";
        }

      }, {
        kind: "method",
        key: "createRenderRoot",
        value: function createRenderRoot() {
          return this.attachShadow({
            mode: "open",
            delegatesFocus: true
          });
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <mwc-icon-button .label=${this.label} .disabled=${this.disabled}>
        <ha-icon .icon=${this.icon}></ha-icon>
      </mwc-icon-button>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      :host {
        display: inline-block;
        outline: none;
      }
      :host([disabled]) {
        pointer-events: none;
      }
      mwc-icon-button {
        --mdc-theme-on-primary: currentColor;
        --mdc-theme-text-disabled-on-light: var(--disabled-text-color);
      }
      ha-icon {
        --ha-icon-display: inline;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HacsTabbedMenu = _decorate([customElement("hacs-tabbed-menu")], function (_initialize, _LitElement) {
    class HacsTabbedMenu extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsTabbedMenu,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "configuration",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "lovelace",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$repositories, _this$status, _this$status2;

          return html`<paper-menu-button
      slot="toolbar-icon"
      horizontal-align="right"
      vertical-align="top"
      vertical-offset="40"
      close-on-activate
    >
      <hacs-icon-button .icon=${mdiDotsVertical} slot="dropdown-trigger"></hacs-icon-button>
      <paper-listbox slot="dropdown-content">
        <hacs-link url="https://hacs.xyz/"
          ><paper-item>${localize("menu.documentation")}</paper-item></hacs-link
        >
        ${((_this$repositories = this.repositories) === null || _this$repositories === void 0 ? void 0 : _this$repositories.filter(repo => repo.new).length) !== 0 ? html` <paper-item @tap=${this._clearAllNewRepositories}
              >${localize("menu.dismiss")}</paper-item
            >` : ""}

        <hacs-link url="https://github.com/hacs"><paper-item>GitHub</paper-item></hacs-link>
        <hacs-link url="https://hacs.xyz/docs/issues"
          ><paper-item>${localize("menu.open_issue")}</paper-item></hacs-link
        >
        ${!((_this$status = this.status) === null || _this$status === void 0 ? void 0 : _this$status.disabled) && !((_this$status2 = this.status) === null || _this$status2 === void 0 ? void 0 : _this$status2.background_task) ? html`<paper-item @tap=${this._showCustomRepositoriesDialog}
              >${localize("menu.custom_repositories")}</paper-item
            >` : ""}

        <paper-item @tap=${this._showAboutDialog}>${localize("menu.about")}</paper-item>
      </paper-listbox>
    </paper-menu-button>`;
        }
      }, {
        kind: "method",
        key: "_clearAllNewRepositories",
        value: async function _clearAllNewRepositories() {
          await settingsClearAllNewRepositories(this.hass, activePanel(this.route).categories);
        }
      }, {
        kind: "method",
        key: "_showAboutDialog",
        value: function _showAboutDialog() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "about",
              configuration: this.configuration,
              repositories: this.repositories
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_showCustomRepositoriesDialog",
        value: function _showCustomRepositoriesDialog() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "custom-repositories",
              repositories: this.repositories
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      paper-menu-button {
        color: var(--hcv-text-color-secondary);
        padding: 0;
      }
      hacs-icon-button {
        color: var(--sidebar-icon-color);
      }
      paper-item {
        cursor: pointer;
      }
      paper-item-body {
        opacity: var(--dark-primary-opacity);
      }
    `;
        }
      }]
    };
  }, LitElement);

  function filterRepositoriesByInput(repositories, filter) {
    const _lowcaseFilter = stringify$1(filter);

    return repositories.filter(_repo => {
      var _stringify, _stringify2, _stringify3, _stringify4, _stringify5, _stringify6;

      return ((_stringify = stringify$1(_repo.name)) === null || _stringify === void 0 ? void 0 : _stringify.includes(_lowcaseFilter)) || ((_stringify2 = stringify$1(_repo.description)) === null || _stringify2 === void 0 ? void 0 : _stringify2.includes(_lowcaseFilter)) || ((_stringify3 = stringify$1(_repo.category)) === null || _stringify3 === void 0 ? void 0 : _stringify3.includes(_lowcaseFilter)) || ((_stringify4 = stringify$1(_repo.full_name)) === null || _stringify4 === void 0 ? void 0 : _stringify4.includes(_lowcaseFilter)) || ((_stringify5 = stringify$1(_repo.authors)) === null || _stringify5 === void 0 ? void 0 : _stringify5.includes(_lowcaseFilter)) || ((_stringify6 = stringify$1(_repo.domain)) === null || _stringify6 === void 0 ? void 0 : _stringify6.includes(_lowcaseFilter));
    });
  }

  const stringify$1 = str => {
    return String(str).toLocaleLowerCase().replace(/-|_| /g, "");
  };

  let HacsStorePanel = _decorate([customElement("hacs-store-panel")], function (_initialize, _LitElement) {
    class HacsStorePanel extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsStorePanel,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "filters",

        value() {
          return {};
        }

      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_searchInput",

        value() {
          return "";
        }

      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "isWide",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "sections",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "section",
        value: void 0
      }, {
        kind: "field",
        key: "_repositoriesInActiveSection",

        value() {
          return memoizeOne((repositories, section) => {
            const installedRepositories = repositories === null || repositories === void 0 ? void 0 : repositories.filter(repo => {
              var _this$hacs$sections, _this$hacs$sections$f, _this$hacs$sections$f2;

              return ((_this$hacs$sections = this.hacs.sections) === null || _this$hacs$sections === void 0 ? void 0 : (_this$hacs$sections$f = _this$hacs$sections.find(panel => panel.id === section)) === null || _this$hacs$sections$f === void 0 ? void 0 : (_this$hacs$sections$f2 = _this$hacs$sections$f.categories) === null || _this$hacs$sections$f2 === void 0 ? void 0 : _this$hacs$sections$f2.includes(repo.category)) && repo.installed;
            });
            const newRepositories = repositories === null || repositories === void 0 ? void 0 : repositories.filter(repo => {
              var _this$hacs$sections2, _this$hacs$sections2$, _this$hacs$sections2$2;

              return ((_this$hacs$sections2 = this.hacs.sections) === null || _this$hacs$sections2 === void 0 ? void 0 : (_this$hacs$sections2$ = _this$hacs$sections2.find(panel => panel.id === section)) === null || _this$hacs$sections2$ === void 0 ? void 0 : (_this$hacs$sections2$2 = _this$hacs$sections2$.categories) === null || _this$hacs$sections2$2 === void 0 ? void 0 : _this$hacs$sections2$2.includes(repo.category)) && repo.new && !repo.installed;
            });
            return [installedRepositories || [], newRepositories || []];
          });
        }

      }, {
        kind: "get",
        key: "allRepositories",
        value: function allRepositories() {
          const [installedRepositories, newRepositories] = this._repositoriesInActiveSection(this.repositories, this.section);

          return newRepositories.concat(installedRepositories);
        }
      }, {
        kind: "field",
        key: "_filterRepositories",

        value() {
          return memoizeOne(filterRepositoriesByInput);
        }

      }, {
        kind: "get",
        key: "visibleRepositories",
        value: function visibleRepositories() {
          const repositories = this.allRepositories.filter(repo => {
            var _this$filters$this$se, _this$filters$this$se2;

            return (_this$filters$this$se = this.filters[this.section]) === null || _this$filters$this$se === void 0 ? void 0 : (_this$filters$this$se2 = _this$filters$this$se.find(filter => filter.id === repo.category)) === null || _this$filters$this$se2 === void 0 ? void 0 : _this$filters$this$se2.checked;
          });
          return this._filterRepositories(repositories, this._searchInput);
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          this.addEventListener("filter-change", e => this._updateFilters(e));
        }
      }, {
        kind: "method",
        key: "_updateFilters",
        value: function _updateFilters(e) {
          var _this$filters$this$se3;

          const current = (_this$filters$this$se3 = this.filters[this.section]) === null || _this$filters$this$se3 === void 0 ? void 0 : _this$filters$this$se3.find(filter => filter.id === e.detail.id);
          this.filters[this.section].find(filter => filter.id === current.id).checked = !current.checked;
          this.requestUpdate();
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$filters$this$se4;

          const newRepositories = this._repositoriesInActiveSection(this.repositories, this.section)[1];

          if (!this.filters[this.section] && this.hacs.configuration.categories) {
            var _activePanel;

            const categories = (_activePanel = activePanel(this.route)) === null || _activePanel === void 0 ? void 0 : _activePanel.categories;
            this.filters[this.section] = [];
            categories === null || categories === void 0 ? void 0 : categories.filter(c => {
              var _this$hacs$configurat;

              return (_this$hacs$configurat = this.hacs.configuration) === null || _this$hacs$configurat === void 0 ? void 0 : _this$hacs$configurat.categories.includes(c);
            }).forEach(category => {
              this.filters[this.section].push({
                id: category,
                value: category,
                checked: true
              });
            });
          }

          return html`<hass-tabs-subpage
      back-path="/hacs/entry"
      .hass=${this.hass}
      .narrow=${this.narrow}
      .route=${this.route}
      .tabs=${this.hacs.sections}
      hasFab
    >
      <hacs-tabbed-menu
        slot="toolbar-icon"
        .hass=${this.hass}
        .route=${this.route}
        .narrow=${this.narrow}
        .configuration=${this.hacs.configuration}
        .lovelace=${this.hacs.resources}
        .status=${this.hacs.status}
        .repositories=${this.repositories}
      >
      </hacs-tabbed-menu>
      ${this.narrow ? html`
            <div slot="header">
              <slot name="header">
                <search-input
                  class="header"
                  no-label-float
                  .label=${this.hacs.localize("search.installed")}
                  .filter=${this._searchInput || ""}
                  @value-changed=${this._inputValueChanged}
                ></search-input>
              </slot>
            </div>
          ` : html`<div class="search">
            <search-input
              no-label-float
              .label=${newRepositories.length === 0 ? this.hacs.localize("search.installed") : this.hacs.localize("search.installed_new")}
              .filter=${this._searchInput || ""}
              @value-changed=${this._inputValueChanged}
            ></search-input>
          </div>`}
      <div class="content ${this.narrow ? "narrow-content" : ""}">
        ${((_this$filters$this$se4 = this.filters[this.section]) === null || _this$filters$this$se4 === void 0 ? void 0 : _this$filters$this$se4.length) > 1 ? html`<div class="filters">
              <hacs-filter .filters="${this.filters[this.section]}"></hacs-filter>
            </div>` : ""}
        ${(newRepositories === null || newRepositories === void 0 ? void 0 : newRepositories.length) > 10 ? html`<div class="new-repositories">
              ${localize("store.new_repositories_note")}
            </div>` : ""}
        <div class="container ${this.narrow ? "narrow" : ""}">
          ${this.repositories === undefined ? "" : this.allRepositories.length === 0 ? this._renderEmpty() : this.visibleRepositories.length === 0 ? this._renderNoResultsFound() : this._renderRepositories()}
        </div>
      </div>
      <mwc-fab ?is-wide=${this.isWide} ?narrow=${this.narrow} @click=${this._addRepository}>
        <ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
      </mwc-fab>
    </hass-tabs-subpage>`;
        }
      }, {
        kind: "method",
        key: "_renderRepositories",
        value: function _renderRepositories() {
          return this.visibleRepositories.map(repo => html`<hacs-repository-card
          .hass=${this.hass}
          .hacs=${this.hacs}
          .repository=${repo}
          .narrow=${this.narrow}
          ?narrow=${this.narrow}
          .status=${this.hacs.status}
          .removed=${this.hacs.removed}
          .addedToLovelace=${this.hacs.addedToLovelace(this.hacs, repo)}
        ></hacs-repository-card>`);
        }
      }, {
        kind: "method",
        key: "_renderNoResultsFound",
        value: function _renderNoResultsFound() {
          return html`<ha-card class="no-repositories">
      <div class="header">${localize("store.no_repositories")} 😕</div>
      <p>
        ${localize("store.no_repositories_found_desc1").replace("{searchInput}", this._searchInput)}
        <br />
        ${localize("store.no_repositories_found_desc2")}
      </p>
    </ha-card>`;
        }
      }, {
        kind: "method",
        key: "_renderEmpty",
        value: function _renderEmpty() {
          return html`<ha-card class="no-repositories">
      <div class="header">${localize("store.no_repositories")} 😕</div>
      <p>
        ${localize("store.no_repositories_desc1")}<br />${localize("store.no_repositories_desc2")}
      </p>
    </ha-card>`;
        }
      }, {
        kind: "method",
        key: "_inputValueChanged",
        value: function _inputValueChanged(ev) {
          this._searchInput = ev.detail.value;
          window.localStorage.setItem("hacs-search", this._searchInput);
        }
      }, {
        kind: "method",
        key: "_addRepository",
        value: function _addRepository() {
          this.dispatchEvent(new CustomEvent("hacs-dialog", {
            detail: {
              type: "add-repository",
              repositories: this.repositories,
              section: this.section
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [HacsStyles, hassTabsSubpage, fabStyles, searchStyles, scrollBarStyle, css`
        .filter {
          border-bottom: 1px solid var(--divider-color);
        }
        .content {
          height: calc(100vh - 128px);
          overflow: auto;
        }
        .narrow-content {
          height: calc(100vh - 128px);
        }
        .container {
          display: grid;
          grid-template-columns: repeat(auto-fit, minmax(480px, 1fr));
          justify-items: center;
          grid-gap: 8px 8px;
          padding: 8px 16px 16px;
          margin-bottom: 64px;
        }
        .no-repositories {
          width: 100%;
          text-align: center;
          margin-top: 12px;
        }
        .new-repositories {
          margin: 4px 16px 0 16px;
          color: var(--hcv-text-color-primary);
        }
        hacs-repository-card {
          max-width: 500px;
          display: flex;
          flex-direction: column;
          justify-content: space-between;
        }
        hacs-repository-card[narrow] {
          width: 100%;
        }
        hacs-repository-card[narrow]:last-of-type {
          margin-bottom: 64px;
        }
        .narrow {
          width: 100%;
          display: block;
          padding: 0px;
          margin: 0;
        }

        .container .narrow {
          margin-bottom: 128px;
        }

        .bottom-bar {
          position: fixed !important;
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HacsRouter = _decorate([customElement("hacs-router")], function (_initialize, _HassRouterPage) {
    class HacsRouter extends _HassRouterPage {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsRouter,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "configuration",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "critical",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "lovelace",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "removed",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_wideSidebar",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_wide",

        value() {
          return false;
        }

      }, {
        kind: "field",
        key: "_listeners",

        value() {
          return [];
        }

      }, {
        kind: "method",
        key: "connectedCallback",
        value: function connectedCallback() {
          _get(_getPrototypeOf(HacsRouter.prototype), "connectedCallback", this).call(this);

          this._listeners.push(listenMediaQuery("(min-width: 1040px)", matches => {
            this._wide = matches;
          }));

          this._listeners.push(listenMediaQuery("(min-width: 1296px)", matches => {
            this._wideSidebar = matches;
          }));
        }
      }, {
        kind: "method",
        key: "disconnectedCallback",
        value: function disconnectedCallback() {
          _get(_getPrototypeOf(HacsRouter.prototype), "disconnectedCallback", this).call(this);

          while (this._listeners.length) {
            this._listeners.pop()();
          }
        }
      }, {
        kind: "field",
        key: "routerOptions",

        value() {
          return {
            defaultPage: "entry",
            routes: {
              entry: {
                tag: "hacs-entry-panel"
              },
              integrations: {
                tag: "hacs-store-panel"
              },
              frontend: {
                tag: "hacs-store-panel"
              },
              automation: {
                tag: "hacs-store-panel"
              }
            }
          };
        }

      }, {
        kind: "method",
        key: "updatePageEl",
        value: function updatePageEl(el) {
          const section = this.route.path.replace("/", "");
          const isWide = this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide;
          el.hass = this.hass;
          el.hacs = this.hacs;
          el.route = this.route;
          el.narrow = this.narrow;
          el.isWide = isWide;
          el.configuration = this.configuration;
          el.critical = this.critical;
          el.lovelace = this.lovelace;
          el.removed = this.removed;
          el.repositories = this.repositories;
          el.status = this.status;
          el.section = section;
        }
      }]
    };
  }, HassRouterPage);

  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

  function createCommonjsModule(fn, basedir, module) {
  	return module = {
  	  path: basedir,
  	  exports: {},
  	  require: function (path, base) {
        return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
      }
  	}, fn(module, module.exports), module.exports;
  }

  function commonjsRequire () {
  	throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
  }

  var marked = createCommonjsModule(function (module, exports) {
    /**
     * marked - a markdown parser
     * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed)
     * https://github.com/markedjs/marked
     */

    /**
     * DO NOT EDIT THIS FILE
     * The code in this file is generated from files in ./src/
     */
    (function (global, factory) {
       module.exports = factory() ;
    })(commonjsGlobal, function () {

      function _defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
          var descriptor = props[i];
          descriptor.enumerable = descriptor.enumerable || false;
          descriptor.configurable = true;
          if ("value" in descriptor) descriptor.writable = true;
          Object.defineProperty(target, descriptor.key, descriptor);
        }
      }

      function _createClass(Constructor, protoProps, staticProps) {
        if (protoProps) _defineProperties(Constructor.prototype, protoProps);
        if (staticProps) _defineProperties(Constructor, staticProps);
        return Constructor;
      }

      function _unsupportedIterableToArray(o, minLen) {
        if (!o) return;
        if (typeof o === "string") return _arrayLikeToArray(o, minLen);
        var n = Object.prototype.toString.call(o).slice(8, -1);
        if (n === "Object" && o.constructor) n = o.constructor.name;
        if (n === "Map" || n === "Set") return Array.from(o);
        if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
      }

      function _arrayLikeToArray(arr, len) {
        if (len == null || len > arr.length) len = arr.length;

        for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];

        return arr2;
      }

      function _createForOfIteratorHelperLoose(o, allowArrayLike) {
        var it;

        if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
          if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
            if (it) o = it;
            var i = 0;
            return function () {
              if (i >= o.length) return {
                done: true
              };
              return {
                done: false,
                value: o[i++]
              };
            };
          }

          throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
        }

        it = o[Symbol.iterator]();
        return it.next.bind(it);
      }

      function createCommonjsModule(fn, module) {
        return module = {
          exports: {}
        }, fn(module, module.exports), module.exports;
      }

      var defaults = createCommonjsModule(function (module) {
        function getDefaults() {
          return {
            baseUrl: null,
            breaks: false,
            gfm: true,
            headerIds: true,
            headerPrefix: '',
            highlight: null,
            langPrefix: 'language-',
            mangle: true,
            pedantic: false,
            renderer: null,
            sanitize: false,
            sanitizer: null,
            silent: false,
            smartLists: false,
            smartypants: false,
            tokenizer: null,
            walkTokens: null,
            xhtml: false
          };
        }

        function changeDefaults(newDefaults) {
          module.exports.defaults = newDefaults;
        }

        module.exports = {
          defaults: getDefaults(),
          getDefaults: getDefaults,
          changeDefaults: changeDefaults
        };
      });
      var defaults_1 = defaults.defaults;
      var defaults_2 = defaults.getDefaults;
      var defaults_3 = defaults.changeDefaults;
      /**
       * Helpers
       */

      var escapeTest = /[&<>"']/;
      var escapeReplace = /[&<>"']/g;
      var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
      var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
      var escapeReplacements = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#39;'
      };

      var getEscapeReplacement = function getEscapeReplacement(ch) {
        return escapeReplacements[ch];
      };

      function escape(html, encode) {
        if (encode) {
          if (escapeTest.test(html)) {
            return html.replace(escapeReplace, getEscapeReplacement);
          }
        } else {
          if (escapeTestNoEncode.test(html)) {
            return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
          }
        }

        return html;
      }

      var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;

      function unescape(html) {
        // explicitly match decimal, hex, and named HTML entities
        return html.replace(unescapeTest, function (_, n) {
          n = n.toLowerCase();
          if (n === 'colon') return ':';

          if (n.charAt(0) === '#') {
            return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
          }

          return '';
        });
      }

      var caret = /(^|[^\[])\^/g;

      function edit(regex, opt) {
        regex = regex.source || regex;
        opt = opt || '';
        var obj = {
          replace: function replace(name, val) {
            val = val.source || val;
            val = val.replace(caret, '$1');
            regex = regex.replace(name, val);
            return obj;
          },
          getRegex: function getRegex() {
            return new RegExp(regex, opt);
          }
        };
        return obj;
      }

      var nonWordAndColonTest = /[^\w:]/g;
      var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;

      function cleanUrl(sanitize, base, href) {
        if (sanitize) {
          var prot;

          try {
            prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase();
          } catch (e) {
            return null;
          }

          if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
            return null;
          }
        }

        if (base && !originIndependentUrl.test(href)) {
          href = resolveUrl(base, href);
        }

        try {
          href = encodeURI(href).replace(/%25/g, '%');
        } catch (e) {
          return null;
        }

        return href;
      }

      var baseUrls = {};
      var justDomain = /^[^:]+:\/*[^/]*$/;
      var protocol = /^([^:]+:)[\s\S]*$/;
      var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;

      function resolveUrl(base, href) {
        if (!baseUrls[' ' + base]) {
          // we can ignore everything in base after the last slash of its path component,
          // but we might need to add _that_
          // https://tools.ietf.org/html/rfc3986#section-3
          if (justDomain.test(base)) {
            baseUrls[' ' + base] = base + '/';
          } else {
            baseUrls[' ' + base] = rtrim(base, '/', true);
          }
        }

        base = baseUrls[' ' + base];
        var relativeBase = base.indexOf(':') === -1;

        if (href.substring(0, 2) === '//') {
          if (relativeBase) {
            return href;
          }

          return base.replace(protocol, '$1') + href;
        } else if (href.charAt(0) === '/') {
          if (relativeBase) {
            return href;
          }

          return base.replace(domain, '$1') + href;
        } else {
          return base + href;
        }
      }

      var noopTest = {
        exec: function noopTest() {}
      };

      function merge(obj) {
        var i = 1,
            target,
            key;

        for (; i < arguments.length; i++) {
          target = arguments[i];

          for (key in target) {
            if (Object.prototype.hasOwnProperty.call(target, key)) {
              obj[key] = target[key];
            }
          }
        }

        return obj;
      }

      function splitCells(tableRow, count) {
        // ensure that every cell-delimiting pipe has a space
        // before it to distinguish it from an escaped pipe
        var row = tableRow.replace(/\|/g, function (match, offset, str) {
          var escaped = false,
              curr = offset;

          while (--curr >= 0 && str[curr] === '\\') {
            escaped = !escaped;
          }

          if (escaped) {
            // odd number of slashes means | is escaped
            // so we leave it alone
            return '|';
          } else {
            // add space before unescaped |
            return ' |';
          }
        }),
            cells = row.split(/ \|/);
        var i = 0;

        if (cells.length > count) {
          cells.splice(count);
        } else {
          while (cells.length < count) {
            cells.push('');
          }
        }

        for (; i < cells.length; i++) {
          // leading or trailing whitespace is ignored per the gfm spec
          cells[i] = cells[i].trim().replace(/\\\|/g, '|');
        }

        return cells;
      } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
      // /c*$/ is vulnerable to REDOS.
      // invert: Remove suffix of non-c chars instead. Default falsey.


      function rtrim(str, c, invert) {
        var l = str.length;

        if (l === 0) {
          return '';
        } // Length of suffix matching the invert condition.


        var suffLen = 0; // Step left until we fail to match the invert condition.

        while (suffLen < l) {
          var currChar = str.charAt(l - suffLen - 1);

          if (currChar === c && !invert) {
            suffLen++;
          } else if (currChar !== c && invert) {
            suffLen++;
          } else {
            break;
          }
        }

        return str.substr(0, l - suffLen);
      }

      function findClosingBracket(str, b) {
        if (str.indexOf(b[1]) === -1) {
          return -1;
        }

        var l = str.length;
        var level = 0,
            i = 0;

        for (; i < l; i++) {
          if (str[i] === '\\') {
            i++;
          } else if (str[i] === b[0]) {
            level++;
          } else if (str[i] === b[1]) {
            level--;

            if (level < 0) {
              return i;
            }
          }
        }

        return -1;
      }

      function checkSanitizeDeprecation(opt) {
        if (opt && opt.sanitize && !opt.silent) {
          console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
        }
      }

      var helpers = {
        escape: escape,
        unescape: unescape,
        edit: edit,
        cleanUrl: cleanUrl,
        resolveUrl: resolveUrl,
        noopTest: noopTest,
        merge: merge,
        splitCells: splitCells,
        rtrim: rtrim,
        findClosingBracket: findClosingBracket,
        checkSanitizeDeprecation: checkSanitizeDeprecation
      };
      var defaults$1 = defaults.defaults;
      var rtrim$1 = helpers.rtrim,
          splitCells$1 = helpers.splitCells,
          _escape = helpers.escape,
          findClosingBracket$1 = helpers.findClosingBracket;

      function outputLink(cap, link, raw) {
        var href = link.href;
        var title = link.title ? _escape(link.title) : null;
        var text = cap[1].replace(/\\([\[\]])/g, '$1');

        if (cap[0].charAt(0) !== '!') {
          return {
            type: 'link',
            raw: raw,
            href: href,
            title: title,
            text: text
          };
        } else {
          return {
            type: 'image',
            raw: raw,
            href: href,
            title: title,
            text: _escape(text)
          };
        }
      }

      function indentCodeCompensation(raw, text) {
        var matchIndentToCode = raw.match(/^(\s+)(?:```)/);

        if (matchIndentToCode === null) {
          return text;
        }

        var indentToCode = matchIndentToCode[1];
        return text.split('\n').map(function (node) {
          var matchIndentInNode = node.match(/^\s+/);

          if (matchIndentInNode === null) {
            return node;
          }

          var indentInNode = matchIndentInNode[0];

          if (indentInNode.length >= indentToCode.length) {
            return node.slice(indentToCode.length);
          }

          return node;
        }).join('\n');
      }
      /**
       * Tokenizer
       */


      var Tokenizer_1 = /*#__PURE__*/function () {
        function Tokenizer(options) {
          this.options = options || defaults$1;
        }

        var _proto = Tokenizer.prototype;

        _proto.space = function space(src) {
          var cap = this.rules.block.newline.exec(src);

          if (cap) {
            if (cap[0].length > 1) {
              return {
                type: 'space',
                raw: cap[0]
              };
            }

            return {
              raw: '\n'
            };
          }
        };

        _proto.code = function code(src, tokens) {
          var cap = this.rules.block.code.exec(src);

          if (cap) {
            var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.

            if (lastToken && lastToken.type === 'paragraph') {
              return {
                raw: cap[0],
                text: cap[0].trimRight()
              };
            }

            var text = cap[0].replace(/^ {4}/gm, '');
            return {
              type: 'code',
              raw: cap[0],
              codeBlockStyle: 'indented',
              text: !this.options.pedantic ? rtrim$1(text, '\n') : text
            };
          }
        };

        _proto.fences = function fences(src) {
          var cap = this.rules.block.fences.exec(src);

          if (cap) {
            var raw = cap[0];
            var text = indentCodeCompensation(raw, cap[3] || '');
            return {
              type: 'code',
              raw: raw,
              lang: cap[2] ? cap[2].trim() : cap[2],
              text: text
            };
          }
        };

        _proto.heading = function heading(src) {
          var cap = this.rules.block.heading.exec(src);

          if (cap) {
            return {
              type: 'heading',
              raw: cap[0],
              depth: cap[1].length,
              text: cap[2]
            };
          }
        };

        _proto.nptable = function nptable(src) {
          var cap = this.rules.block.nptable.exec(src);

          if (cap) {
            var item = {
              type: 'table',
              header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
              align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
              cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
              raw: cap[0]
            };

            if (item.header.length === item.align.length) {
              var l = item.align.length;
              var i;

              for (i = 0; i < l; i++) {
                if (/^ *-+: *$/.test(item.align[i])) {
                  item.align[i] = 'right';
                } else if (/^ *:-+: *$/.test(item.align[i])) {
                  item.align[i] = 'center';
                } else if (/^ *:-+ *$/.test(item.align[i])) {
                  item.align[i] = 'left';
                } else {
                  item.align[i] = null;
                }
              }

              l = item.cells.length;

              for (i = 0; i < l; i++) {
                item.cells[i] = splitCells$1(item.cells[i], item.header.length);
              }

              return item;
            }
          }
        };

        _proto.hr = function hr(src) {
          var cap = this.rules.block.hr.exec(src);

          if (cap) {
            return {
              type: 'hr',
              raw: cap[0]
            };
          }
        };

        _proto.blockquote = function blockquote(src) {
          var cap = this.rules.block.blockquote.exec(src);

          if (cap) {
            var text = cap[0].replace(/^ *> ?/gm, '');
            return {
              type: 'blockquote',
              raw: cap[0],
              text: text
            };
          }
        };

        _proto.list = function list(src) {
          var cap = this.rules.block.list.exec(src);

          if (cap) {
            var raw = cap[0];
            var bull = cap[2];
            var isordered = bull.length > 1;
            var isparen = bull[bull.length - 1] === ')';
            var list = {
              type: 'list',
              raw: raw,
              ordered: isordered,
              start: isordered ? +bull.slice(0, -1) : '',
              loose: false,
              items: []
            }; // Get each top-level item.

            var itemMatch = cap[0].match(this.rules.block.item);
            var next = false,
                item,
                space,
                b,
                addBack,
                loose,
                istask,
                ischecked;
            var l = itemMatch.length;

            for (var i = 0; i < l; i++) {
              item = itemMatch[i];
              raw = item; // Remove the list item's bullet
              // so it is seen as the next token.

              space = item.length;
              item = item.replace(/^ *([*+-]|\d+[.)]) */, ''); // Outdent whatever the
              // list item contains. Hacky.

              if (~item.indexOf('\n ')) {
                space -= item.length;
                item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
              } // Determine whether the next list item belongs here.
              // Backpedal if it does not belong in this list.


              if (i !== l - 1) {
                b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];

                if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) {
                  addBack = itemMatch.slice(i + 1).join('\n');
                  list.raw = list.raw.substring(0, list.raw.length - addBack.length);
                  i = l - 1;
                }
              } // Determine whether item is loose or not.
              // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
              // for discount behavior.


              loose = next || /\n\n(?!\s*$)/.test(item);

              if (i !== l - 1) {
                next = item.charAt(item.length - 1) === '\n';
                if (!loose) loose = next;
              }

              if (loose) {
                list.loose = true;
              } // Check for task list items


              istask = /^\[[ xX]\] /.test(item);
              ischecked = undefined;

              if (istask) {
                ischecked = item[1] !== ' ';
                item = item.replace(/^\[[ xX]\] +/, '');
              }

              list.items.push({
                type: 'list_item',
                raw: raw,
                task: istask,
                checked: ischecked,
                loose: loose,
                text: item
              });
            }

            return list;
          }
        };

        _proto.html = function html(src) {
          var cap = this.rules.block.html.exec(src);

          if (cap) {
            return {
              type: this.options.sanitize ? 'paragraph' : 'html',
              raw: cap[0],
              pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
              text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
            };
          }
        };

        _proto.def = function def(src) {
          var cap = this.rules.block.def.exec(src);

          if (cap) {
            if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
            var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
            return {
              tag: tag,
              raw: cap[0],
              href: cap[2],
              title: cap[3]
            };
          }
        };

        _proto.table = function table(src) {
          var cap = this.rules.block.table.exec(src);

          if (cap) {
            var item = {
              type: 'table',
              header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
              align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
              cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
            };

            if (item.header.length === item.align.length) {
              item.raw = cap[0];
              var l = item.align.length;
              var i;

              for (i = 0; i < l; i++) {
                if (/^ *-+: *$/.test(item.align[i])) {
                  item.align[i] = 'right';
                } else if (/^ *:-+: *$/.test(item.align[i])) {
                  item.align[i] = 'center';
                } else if (/^ *:-+ *$/.test(item.align[i])) {
                  item.align[i] = 'left';
                } else {
                  item.align[i] = null;
                }
              }

              l = item.cells.length;

              for (i = 0; i < l; i++) {
                item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
              }

              return item;
            }
          }
        };

        _proto.lheading = function lheading(src) {
          var cap = this.rules.block.lheading.exec(src);

          if (cap) {
            return {
              type: 'heading',
              raw: cap[0],
              depth: cap[2].charAt(0) === '=' ? 1 : 2,
              text: cap[1]
            };
          }
        };

        _proto.paragraph = function paragraph(src) {
          var cap = this.rules.block.paragraph.exec(src);

          if (cap) {
            return {
              type: 'paragraph',
              raw: cap[0],
              text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
            };
          }
        };

        _proto.text = function text(src, tokens) {
          var cap = this.rules.block.text.exec(src);

          if (cap) {
            var lastToken = tokens[tokens.length - 1];

            if (lastToken && lastToken.type === 'text') {
              return {
                raw: cap[0],
                text: cap[0]
              };
            }

            return {
              type: 'text',
              raw: cap[0],
              text: cap[0]
            };
          }
        };

        _proto.escape = function escape(src) {
          var cap = this.rules.inline.escape.exec(src);

          if (cap) {
            return {
              type: 'escape',
              raw: cap[0],
              text: _escape(cap[1])
            };
          }
        };

        _proto.tag = function tag(src, inLink, inRawBlock) {
          var cap = this.rules.inline.tag.exec(src);

          if (cap) {
            if (!inLink && /^<a /i.test(cap[0])) {
              inLink = true;
            } else if (inLink && /^<\/a>/i.test(cap[0])) {
              inLink = false;
            }

            if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
              inRawBlock = true;
            } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
              inRawBlock = false;
            }

            return {
              type: this.options.sanitize ? 'text' : 'html',
              raw: cap[0],
              inLink: inLink,
              inRawBlock: inRawBlock,
              text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
            };
          }
        };

        _proto.link = function link(src) {
          var cap = this.rules.inline.link.exec(src);

          if (cap) {
            var lastParenIndex = findClosingBracket$1(cap[2], '()');

            if (lastParenIndex > -1) {
              var start = cap[0].indexOf('!') === 0 ? 5 : 4;
              var linkLen = start + cap[1].length + lastParenIndex;
              cap[2] = cap[2].substring(0, lastParenIndex);
              cap[0] = cap[0].substring(0, linkLen).trim();
              cap[3] = '';
            }

            var href = cap[2];
            var title = '';

            if (this.options.pedantic) {
              var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);

              if (link) {
                href = link[1];
                title = link[3];
              } else {
                title = '';
              }
            } else {
              title = cap[3] ? cap[3].slice(1, -1) : '';
            }

            href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
            var token = outputLink(cap, {
              href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
              title: title ? title.replace(this.rules.inline._escapes, '$1') : title
            }, cap[0]);
            return token;
          }
        };

        _proto.reflink = function reflink(src, links) {
          var cap;

          if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
            var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
            link = links[link.toLowerCase()];

            if (!link || !link.href) {
              var text = cap[0].charAt(0);
              return {
                type: 'text',
                raw: text,
                text: text
              };
            }

            var token = outputLink(cap, link, cap[0]);
            return token;
          }
        };

        _proto.strong = function strong(src, maskedSrc, prevChar) {
          if (prevChar === void 0) {
            prevChar = '';
          }

          var match = this.rules.inline.strong.start.exec(src);

          if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
            maskedSrc = maskedSrc.slice(-1 * src.length);
            var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd;
            endReg.lastIndex = 0;
            var cap;

            while ((match = endReg.exec(maskedSrc)) != null) {
              cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3));

              if (cap) {
                return {
                  type: 'strong',
                  raw: src.slice(0, cap[0].length),
                  text: src.slice(2, cap[0].length - 2)
                };
              }
            }
          }
        };

        _proto.em = function em(src, maskedSrc, prevChar) {
          if (prevChar === void 0) {
            prevChar = '';
          }

          var match = this.rules.inline.em.start.exec(src);

          if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
            maskedSrc = maskedSrc.slice(-1 * src.length);
            var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd;
            endReg.lastIndex = 0;
            var cap;

            while ((match = endReg.exec(maskedSrc)) != null) {
              cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2));

              if (cap) {
                return {
                  type: 'em',
                  raw: src.slice(0, cap[0].length),
                  text: src.slice(1, cap[0].length - 1)
                };
              }
            }
          }
        };

        _proto.codespan = function codespan(src) {
          var cap = this.rules.inline.code.exec(src);

          if (cap) {
            var text = cap[2].replace(/\n/g, ' ');
            var hasNonSpaceChars = /[^ ]/.test(text);
            var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');

            if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
              text = text.substring(1, text.length - 1);
            }

            text = _escape(text, true);
            return {
              type: 'codespan',
              raw: cap[0],
              text: text
            };
          }
        };

        _proto.br = function br(src) {
          var cap = this.rules.inline.br.exec(src);

          if (cap) {
            return {
              type: 'br',
              raw: cap[0]
            };
          }
        };

        _proto.del = function del(src) {
          var cap = this.rules.inline.del.exec(src);

          if (cap) {
            return {
              type: 'del',
              raw: cap[0],
              text: cap[1]
            };
          }
        };

        _proto.autolink = function autolink(src, mangle) {
          var cap = this.rules.inline.autolink.exec(src);

          if (cap) {
            var text, href;

            if (cap[2] === '@') {
              text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
              href = 'mailto:' + text;
            } else {
              text = _escape(cap[1]);
              href = text;
            }

            return {
              type: 'link',
              raw: cap[0],
              text: text,
              href: href,
              tokens: [{
                type: 'text',
                raw: text,
                text: text
              }]
            };
          }
        };

        _proto.url = function url(src, mangle) {
          var cap;

          if (cap = this.rules.inline.url.exec(src)) {
            var text, href;

            if (cap[2] === '@') {
              text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
              href = 'mailto:' + text;
            } else {
              // do extended autolink path validation
              var prevCapZero;

              do {
                prevCapZero = cap[0];
                cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
              } while (prevCapZero !== cap[0]);

              text = _escape(cap[0]);

              if (cap[1] === 'www.') {
                href = 'http://' + text;
              } else {
                href = text;
              }
            }

            return {
              type: 'link',
              raw: cap[0],
              text: text,
              href: href,
              tokens: [{
                type: 'text',
                raw: text,
                text: text
              }]
            };
          }
        };

        _proto.inlineText = function inlineText(src, inRawBlock, smartypants) {
          var cap = this.rules.inline.text.exec(src);

          if (cap) {
            var text;

            if (inRawBlock) {
              text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
            } else {
              text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
            }

            return {
              type: 'text',
              raw: cap[0],
              text: text
            };
          }
        };

        return Tokenizer;
      }();

      var noopTest$1 = helpers.noopTest,
          edit$1 = helpers.edit,
          merge$1 = helpers.merge;
      /**
       * Block-Level Grammar
       */

      var block = {
        newline: /^\n+/,
        code: /^( {4}[^\n]+\n*)+/,
        fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
        hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
        heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
        blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
        list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
        html: '^ {0,3}(?:' // optional indentation
        + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
        + '|comment[^\\n]*(\\n+|$)' // (2)
        + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
        + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
        + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
        + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
        + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
        + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
        + ')',
        def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
        nptable: noopTest$1,
        table: noopTest$1,
        lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
        // regex template, placeholders will be replaced according to different paragraph
        // interruption rules of commonmark and the original markdown spec:
        _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
        text: /^[^\n]+/
      };
      block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
      block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
      block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
      block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
      block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
      block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
      block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex();
      block._tag = 'address|article|aside|base|basefont|blockquote|body|caption' + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption' + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe' + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option' + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr' + '|track|ul';
      block._comment = /<!--(?!-?>)[\s\S]*?-->/;
      block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
      block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
      .replace('blockquote', ' {0,3}>').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
      .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
      .getRegex();
      block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex();
      /**
       * Normal Block Grammar
       */

      block.normal = merge$1({}, block);
      /**
       * GFM Block Grammar
       */

      block.gfm = merge$1({}, block.normal, {
        nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
        + ' *([-:]+ *\\|[-| :]*)' // Align
        + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
        // Cells
        table: '^ *\\|(.+)\\n' // Header
        + ' *\\|?( *[-:]+[-| :]*)' // Align
        + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells

      });
      block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
      .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
      .getRegex();
      block.gfm.table = edit$1(block.gfm.table).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('blockquote', ' {0,3}>').replace('code', ' {4}[^\\n]').replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n').replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
      .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
      .getRegex();
      /**
       * Pedantic grammar (original John Gruber's loose markdown specification)
       */

      block.pedantic = merge$1({}, block.normal, {
        html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
        + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._comment).replace(/tag/g, '(?!(?:' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub' + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)' + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b').getRegex(),
        def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
        heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
        fences: noopTest$1,
        // fences not supported
        paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex()
      });
      /**
       * Inline-Level Grammar
       */

      var inline = {
        escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
        autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
        url: noopTest$1,
        tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
        + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
        + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
        + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
        + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
        // CDATA section
        link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
        reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
        nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
        reflinkSearch: 'reflink|nolink(?!\\()',
        strong: {
          start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,
          // (1) returns if starts w/ punctuation
          middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,
          endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation\s]|$))/,
          // last char can't be punct, or final * must also be followed by punct (or endline)
          endUnd: /[^\s]__(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)

        },
        em: {
          start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,
          // (1) returns if starts w/ punctuation
          middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,
          endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation\s]|$))/,
          // last char can't be punct, or final * must also be followed by punct (or endline)
          endUnd: /[^\s]_(?!_)(?:(?=[punctuation\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)

        },
        code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
        br: /^( {2,}|\\)\n(?!\s*$)/,
        del: noopTest$1,
        text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/,
        punctuation: /^([\s*punctuation])/
      }; // list of punctuation marks from common mark spec
      // without * and _ to workaround cases with double emphasis

      inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
      inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>

      inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>';
      inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*';
      inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex();
      inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
      inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
      inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
      inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex();
      inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/blockSkip/g, inline._blockSkip).getRegex();
      inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
      inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
      inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex();
      inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex();
      inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
      inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
      inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
      inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex();
      inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
      inline.tag = edit$1(inline.tag).replace('comment', block._comment).replace('attribute', inline._attribute).getRegex();
      inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
      inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
      inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
      inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex();
      inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex();
      inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex();
      /**
       * Normal Inline Grammar
       */

      inline.normal = merge$1({}, inline);
      /**
       * Pedantic Inline Grammar
       */

      inline.pedantic = merge$1({}, inline.normal, {
        strong: {
          start: /^__|\*\*/,
          middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
          endAst: /\*\*(?!\*)/g,
          endUnd: /__(?!_)/g
        },
        em: {
          start: /^_|\*/,
          middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
          endAst: /\*(?!\*)/g,
          endUnd: /_(?!_)/g
        },
        link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(),
        reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex()
      });
      /**
       * GFM Inline Grammar
       */

      inline.gfm = merge$1({}, inline.normal, {
        escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
        _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
        url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
        _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
        del: /^~+(?=\S)([\s\S]*?\S)~+/,
        text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
      });
      inline.gfm.url = edit$1(inline.gfm.url, 'i').replace('email', inline.gfm._extended_email).getRegex();
      /**
       * GFM + Line Breaks Inline Grammar
       */

      inline.breaks = merge$1({}, inline.gfm, {
        br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
        text: edit$1(inline.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
      });
      var rules = {
        block: block,
        inline: inline
      };
      var defaults$2 = defaults.defaults;
      var block$1 = rules.block,
          inline$1 = rules.inline;
      /**
       * smartypants text replacement
       */

      function smartypants(text) {
        return text // em-dashes
        .replace(/---/g, "\u2014") // en-dashes
        .replace(/--/g, "\u2013") // opening singles
        .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
        .replace(/'/g, "\u2019") // opening doubles
        .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
        .replace(/"/g, "\u201D") // ellipses
        .replace(/\.{3}/g, "\u2026");
      }
      /**
       * mangle email addresses
       */


      function mangle(text) {
        var out = '',
            i,
            ch;
        var l = text.length;

        for (i = 0; i < l; i++) {
          ch = text.charCodeAt(i);

          if (Math.random() > 0.5) {
            ch = 'x' + ch.toString(16);
          }

          out += '&#' + ch + ';';
        }

        return out;
      }
      /**
       * Block Lexer
       */


      var Lexer_1 = /*#__PURE__*/function () {
        function Lexer(options) {
          this.tokens = [];
          this.tokens.links = Object.create(null);
          this.options = options || defaults$2;
          this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
          this.tokenizer = this.options.tokenizer;
          this.tokenizer.options = this.options;
          var rules = {
            block: block$1.normal,
            inline: inline$1.normal
          };

          if (this.options.pedantic) {
            rules.block = block$1.pedantic;
            rules.inline = inline$1.pedantic;
          } else if (this.options.gfm) {
            rules.block = block$1.gfm;

            if (this.options.breaks) {
              rules.inline = inline$1.breaks;
            } else {
              rules.inline = inline$1.gfm;
            }
          }

          this.tokenizer.rules = rules;
        }
        /**
         * Expose Rules
         */

        /**
         * Static Lex Method
         */


        Lexer.lex = function lex(src, options) {
          var lexer = new Lexer(options);
          return lexer.lex(src);
        }
        /**
         * Preprocessing
         */
        ;

        var _proto = Lexer.prototype;

        _proto.lex = function lex(src) {
          src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, '    ');
          this.blockTokens(src, this.tokens, true);
          this.inline(this.tokens);
          return this.tokens;
        }
        /**
         * Lexing
         */
        ;

        _proto.blockTokens = function blockTokens(src, tokens, top) {
          if (tokens === void 0) {
            tokens = [];
          }

          if (top === void 0) {
            top = true;
          }

          src = src.replace(/^ +$/gm, '');
          var token, i, l, lastToken;

          while (src) {
            // newline
            if (token = this.tokenizer.space(src)) {
              src = src.substring(token.raw.length);

              if (token.type) {
                tokens.push(token);
              }

              continue;
            } // code


            if (token = this.tokenizer.code(src, tokens)) {
              src = src.substring(token.raw.length);

              if (token.type) {
                tokens.push(token);
              } else {
                lastToken = tokens[tokens.length - 1];
                lastToken.raw += '\n' + token.raw;
                lastToken.text += '\n' + token.text;
              }

              continue;
            } // fences


            if (token = this.tokenizer.fences(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // heading


            if (token = this.tokenizer.heading(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // table no leading pipe (gfm)


            if (token = this.tokenizer.nptable(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // hr


            if (token = this.tokenizer.hr(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // blockquote


            if (token = this.tokenizer.blockquote(src)) {
              src = src.substring(token.raw.length);
              token.tokens = this.blockTokens(token.text, [], top);
              tokens.push(token);
              continue;
            } // list


            if (token = this.tokenizer.list(src)) {
              src = src.substring(token.raw.length);
              l = token.items.length;

              for (i = 0; i < l; i++) {
                token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
              }

              tokens.push(token);
              continue;
            } // html


            if (token = this.tokenizer.html(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // def


            if (top && (token = this.tokenizer.def(src))) {
              src = src.substring(token.raw.length);

              if (!this.tokens.links[token.tag]) {
                this.tokens.links[token.tag] = {
                  href: token.href,
                  title: token.title
                };
              }

              continue;
            } // table (gfm)


            if (token = this.tokenizer.table(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // lheading


            if (token = this.tokenizer.lheading(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // top-level paragraph


            if (top && (token = this.tokenizer.paragraph(src))) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // text


            if (token = this.tokenizer.text(src, tokens)) {
              src = src.substring(token.raw.length);

              if (token.type) {
                tokens.push(token);
              } else {
                lastToken = tokens[tokens.length - 1];
                lastToken.raw += '\n' + token.raw;
                lastToken.text += '\n' + token.text;
              }

              continue;
            }

            if (src) {
              var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);

              if (this.options.silent) {
                console.error(errMsg);
                break;
              } else {
                throw new Error(errMsg);
              }
            }
          }

          return tokens;
        };

        _proto.inline = function inline(tokens) {
          var i, j, k, l2, row, token;
          var l = tokens.length;

          for (i = 0; i < l; i++) {
            token = tokens[i];

            switch (token.type) {
              case 'paragraph':
              case 'text':
              case 'heading':
                {
                  token.tokens = [];
                  this.inlineTokens(token.text, token.tokens);
                  break;
                }

              case 'table':
                {
                  token.tokens = {
                    header: [],
                    cells: []
                  }; // header

                  l2 = token.header.length;

                  for (j = 0; j < l2; j++) {
                    token.tokens.header[j] = [];
                    this.inlineTokens(token.header[j], token.tokens.header[j]);
                  } // cells


                  l2 = token.cells.length;

                  for (j = 0; j < l2; j++) {
                    row = token.cells[j];
                    token.tokens.cells[j] = [];

                    for (k = 0; k < row.length; k++) {
                      token.tokens.cells[j][k] = [];
                      this.inlineTokens(row[k], token.tokens.cells[j][k]);
                    }
                  }

                  break;
                }

              case 'blockquote':
                {
                  this.inline(token.tokens);
                  break;
                }

              case 'list':
                {
                  l2 = token.items.length;

                  for (j = 0; j < l2; j++) {
                    this.inline(token.items[j].tokens);
                  }

                  break;
                }
            }
          }

          return tokens;
        }
        /**
         * Lexing/Compiling
         */
        ;

        _proto.inlineTokens = function inlineTokens(src, tokens, inLink, inRawBlock, prevChar) {
          if (tokens === void 0) {
            tokens = [];
          }

          if (inLink === void 0) {
            inLink = false;
          }

          if (inRawBlock === void 0) {
            inRawBlock = false;
          }

          if (prevChar === void 0) {
            prevChar = '';
          }

          var token; // String with links masked to avoid interference with em and strong

          var maskedSrc = src;
          var match; // Mask out reflinks

          if (this.tokens.links) {
            var links = Object.keys(this.tokens.links);

            if (links.length > 0) {
              while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
                if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
                  maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
                }
              }
            }
          } // Mask out other blocks


          while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
            maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
          }

          while (src) {
            // escape
            if (token = this.tokenizer.escape(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // tag


            if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
              src = src.substring(token.raw.length);
              inLink = token.inLink;
              inRawBlock = token.inRawBlock;
              tokens.push(token);
              continue;
            } // link


            if (token = this.tokenizer.link(src)) {
              src = src.substring(token.raw.length);

              if (token.type === 'link') {
                token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
              }

              tokens.push(token);
              continue;
            } // reflink, nolink


            if (token = this.tokenizer.reflink(src, this.tokens.links)) {
              src = src.substring(token.raw.length);

              if (token.type === 'link') {
                token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
              }

              tokens.push(token);
              continue;
            } // strong


            if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) {
              src = src.substring(token.raw.length);
              token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
              tokens.push(token);
              continue;
            } // em


            if (token = this.tokenizer.em(src, maskedSrc, prevChar)) {
              src = src.substring(token.raw.length);
              token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
              tokens.push(token);
              continue;
            } // code


            if (token = this.tokenizer.codespan(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // br


            if (token = this.tokenizer.br(src)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // del (gfm)


            if (token = this.tokenizer.del(src)) {
              src = src.substring(token.raw.length);
              token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
              tokens.push(token);
              continue;
            } // autolink


            if (token = this.tokenizer.autolink(src, mangle)) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // url (gfm)


            if (!inLink && (token = this.tokenizer.url(src, mangle))) {
              src = src.substring(token.raw.length);
              tokens.push(token);
              continue;
            } // text


            if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
              src = src.substring(token.raw.length);
              prevChar = token.raw.slice(-1);
              tokens.push(token);
              continue;
            }

            if (src) {
              var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);

              if (this.options.silent) {
                console.error(errMsg);
                break;
              } else {
                throw new Error(errMsg);
              }
            }
          }

          return tokens;
        };

        _createClass(Lexer, null, [{
          key: "rules",
          get: function get() {
            return {
              block: block$1,
              inline: inline$1
            };
          }
        }]);

        return Lexer;
      }();

      var defaults$3 = defaults.defaults;
      var cleanUrl$1 = helpers.cleanUrl,
          escape$1 = helpers.escape;
      /**
       * Renderer
       */

      var Renderer_1 = /*#__PURE__*/function () {
        function Renderer(options) {
          this.options = options || defaults$3;
        }

        var _proto = Renderer.prototype;

        _proto.code = function code(_code, infostring, escaped) {
          var lang = (infostring || '').match(/\S*/)[0];

          if (this.options.highlight) {
            var out = this.options.highlight(_code, lang);

            if (out != null && out !== _code) {
              escaped = true;
              _code = out;
            }
          }

          if (!lang) {
            return '<pre><code>' + (escaped ? _code : escape$1(_code, true)) + '</code></pre>\n';
          }

          return '<pre><code class="' + this.options.langPrefix + escape$1(lang, true) + '">' + (escaped ? _code : escape$1(_code, true)) + '</code></pre>\n';
        };

        _proto.blockquote = function blockquote(quote) {
          return '<blockquote>\n' + quote + '</blockquote>\n';
        };

        _proto.html = function html(_html) {
          return _html;
        };

        _proto.heading = function heading(text, level, raw, slugger) {
          if (this.options.headerIds) {
            return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
          } // ignore IDs


          return '<h' + level + '>' + text + '</h' + level + '>\n';
        };

        _proto.hr = function hr() {
          return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
        };

        _proto.list = function list(body, ordered, start) {
          var type = ordered ? 'ol' : 'ul',
              startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
          return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
        };

        _proto.listitem = function listitem(text) {
          return '<li>' + text + '</li>\n';
        };

        _proto.checkbox = function checkbox(checked) {
          return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
        };

        _proto.paragraph = function paragraph(text) {
          return '<p>' + text + '</p>\n';
        };

        _proto.table = function table(header, body) {
          if (body) body = '<tbody>' + body + '</tbody>';
          return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
        };

        _proto.tablerow = function tablerow(content) {
          return '<tr>\n' + content + '</tr>\n';
        };

        _proto.tablecell = function tablecell(content, flags) {
          var type = flags.header ? 'th' : 'td';
          var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
          return tag + content + '</' + type + '>\n';
        } // span level renderer
        ;

        _proto.strong = function strong(text) {
          return '<strong>' + text + '</strong>';
        };

        _proto.em = function em(text) {
          return '<em>' + text + '</em>';
        };

        _proto.codespan = function codespan(text) {
          return '<code>' + text + '</code>';
        };

        _proto.br = function br() {
          return this.options.xhtml ? '<br/>' : '<br>';
        };

        _proto.del = function del(text) {
          return '<del>' + text + '</del>';
        };

        _proto.link = function link(href, title, text) {
          href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);

          if (href === null) {
            return text;
          }

          var out = '<a href="' + escape$1(href) + '"';

          if (title) {
            out += ' title="' + title + '"';
          }

          out += '>' + text + '</a>';
          return out;
        };

        _proto.image = function image(href, title, text) {
          href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);

          if (href === null) {
            return text;
          }

          var out = '<img src="' + href + '" alt="' + text + '"';

          if (title) {
            out += ' title="' + title + '"';
          }

          out += this.options.xhtml ? '/>' : '>';
          return out;
        };

        _proto.text = function text(_text) {
          return _text;
        };

        return Renderer;
      }();
      /**
       * TextRenderer
       * returns only the textual part of the token
       */


      var TextRenderer_1 = /*#__PURE__*/function () {
        function TextRenderer() {}

        var _proto = TextRenderer.prototype; // no need for block level renderers

        _proto.strong = function strong(text) {
          return text;
        };

        _proto.em = function em(text) {
          return text;
        };

        _proto.codespan = function codespan(text) {
          return text;
        };

        _proto.del = function del(text) {
          return text;
        };

        _proto.html = function html(text) {
          return text;
        };

        _proto.text = function text(_text) {
          return _text;
        };

        _proto.link = function link(href, title, text) {
          return '' + text;
        };

        _proto.image = function image(href, title, text) {
          return '' + text;
        };

        _proto.br = function br() {
          return '';
        };

        return TextRenderer;
      }();
      /**
       * Slugger generates header id
       */


      var Slugger_1 = /*#__PURE__*/function () {
        function Slugger() {
          this.seen = {};
        }
        /**
         * Convert string to unique id
         */


        var _proto = Slugger.prototype;

        _proto.slug = function slug(value) {
          var slug = value.toLowerCase().trim() // remove html tags
          .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
          .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');

          if (this.seen.hasOwnProperty(slug)) {
            var originalSlug = slug;

            do {
              this.seen[originalSlug]++;
              slug = originalSlug + '-' + this.seen[originalSlug];
            } while (this.seen.hasOwnProperty(slug));
          }

          this.seen[slug] = 0;
          return slug;
        };

        return Slugger;
      }();

      var defaults$4 = defaults.defaults;
      var unescape$1 = helpers.unescape;
      /**
       * Parsing & Compiling
       */

      var Parser_1 = /*#__PURE__*/function () {
        function Parser(options) {
          this.options = options || defaults$4;
          this.options.renderer = this.options.renderer || new Renderer_1();
          this.renderer = this.options.renderer;
          this.renderer.options = this.options;
          this.textRenderer = new TextRenderer_1();
          this.slugger = new Slugger_1();
        }
        /**
         * Static Parse Method
         */


        Parser.parse = function parse(tokens, options) {
          var parser = new Parser(options);
          return parser.parse(tokens);
        }
        /**
         * Parse Loop
         */
        ;

        var _proto = Parser.prototype;

        _proto.parse = function parse(tokens, top) {
          if (top === void 0) {
            top = true;
          }

          var out = '',
              i,
              j,
              k,
              l2,
              l3,
              row,
              cell,
              header,
              body,
              token,
              ordered,
              start,
              loose,
              itemBody,
              item,
              checked,
              task,
              checkbox;
          var l = tokens.length;

          for (i = 0; i < l; i++) {
            token = tokens[i];

            switch (token.type) {
              case 'space':
                {
                  continue;
                }

              case 'hr':
                {
                  out += this.renderer.hr();
                  continue;
                }

              case 'heading':
                {
                  out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$1(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
                  continue;
                }

              case 'code':
                {
                  out += this.renderer.code(token.text, token.lang, token.escaped);
                  continue;
                }

              case 'table':
                {
                  header = ''; // header

                  cell = '';
                  l2 = token.header.length;

                  for (j = 0; j < l2; j++) {
                    cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
                      header: true,
                      align: token.align[j]
                    });
                  }

                  header += this.renderer.tablerow(cell);
                  body = '';
                  l2 = token.cells.length;

                  for (j = 0; j < l2; j++) {
                    row = token.tokens.cells[j];
                    cell = '';
                    l3 = row.length;

                    for (k = 0; k < l3; k++) {
                      cell += this.renderer.tablecell(this.parseInline(row[k]), {
                        header: false,
                        align: token.align[k]
                      });
                    }

                    body += this.renderer.tablerow(cell);
                  }

                  out += this.renderer.table(header, body);
                  continue;
                }

              case 'blockquote':
                {
                  body = this.parse(token.tokens);
                  out += this.renderer.blockquote(body);
                  continue;
                }

              case 'list':
                {
                  ordered = token.ordered;
                  start = token.start;
                  loose = token.loose;
                  l2 = token.items.length;
                  body = '';

                  for (j = 0; j < l2; j++) {
                    item = token.items[j];
                    checked = item.checked;
                    task = item.task;
                    itemBody = '';

                    if (item.task) {
                      checkbox = this.renderer.checkbox(checked);

                      if (loose) {
                        if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
                          item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;

                          if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
                            item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
                          }
                        } else {
                          item.tokens.unshift({
                            type: 'text',
                            text: checkbox
                          });
                        }
                      } else {
                        itemBody += checkbox;
                      }
                    }

                    itemBody += this.parse(item.tokens, loose);
                    body += this.renderer.listitem(itemBody, task, checked);
                  }

                  out += this.renderer.list(body, ordered, start);
                  continue;
                }

              case 'html':
                {
                  // TODO parse inline content if parameter markdown=1
                  out += this.renderer.html(token.text);
                  continue;
                }

              case 'paragraph':
                {
                  out += this.renderer.paragraph(this.parseInline(token.tokens));
                  continue;
                }

              case 'text':
                {
                  body = token.tokens ? this.parseInline(token.tokens) : token.text;

                  while (i + 1 < l && tokens[i + 1].type === 'text') {
                    token = tokens[++i];
                    body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
                  }

                  out += top ? this.renderer.paragraph(body) : body;
                  continue;
                }

              default:
                {
                  var errMsg = 'Token with "' + token.type + '" type was not found.';

                  if (this.options.silent) {
                    console.error(errMsg);
                    return;
                  } else {
                    throw new Error(errMsg);
                  }
                }
            }
          }

          return out;
        }
        /**
         * Parse Inline Tokens
         */
        ;

        _proto.parseInline = function parseInline(tokens, renderer) {
          renderer = renderer || this.renderer;
          var out = '',
              i,
              token;
          var l = tokens.length;

          for (i = 0; i < l; i++) {
            token = tokens[i];

            switch (token.type) {
              case 'escape':
                {
                  out += renderer.text(token.text);
                  break;
                }

              case 'html':
                {
                  out += renderer.html(token.text);
                  break;
                }

              case 'link':
                {
                  out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
                  break;
                }

              case 'image':
                {
                  out += renderer.image(token.href, token.title, token.text);
                  break;
                }

              case 'strong':
                {
                  out += renderer.strong(this.parseInline(token.tokens, renderer));
                  break;
                }

              case 'em':
                {
                  out += renderer.em(this.parseInline(token.tokens, renderer));
                  break;
                }

              case 'codespan':
                {
                  out += renderer.codespan(token.text);
                  break;
                }

              case 'br':
                {
                  out += renderer.br();
                  break;
                }

              case 'del':
                {
                  out += renderer.del(this.parseInline(token.tokens, renderer));
                  break;
                }

              case 'text':
                {
                  out += renderer.text(token.text);
                  break;
                }

              default:
                {
                  var errMsg = 'Token with "' + token.type + '" type was not found.';

                  if (this.options.silent) {
                    console.error(errMsg);
                    return;
                  } else {
                    throw new Error(errMsg);
                  }
                }
            }
          }

          return out;
        };

        return Parser;
      }();

      var merge$2 = helpers.merge,
          checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation,
          escape$2 = helpers.escape;
      var getDefaults = defaults.getDefaults,
          changeDefaults = defaults.changeDefaults,
          defaults$5 = defaults.defaults;
      /**
       * Marked
       */

      function marked(src, opt, callback) {
        // throw error in case of non string input
        if (typeof src === 'undefined' || src === null) {
          throw new Error('marked(): input parameter is undefined or null');
        }

        if (typeof src !== 'string') {
          throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
        }

        if (typeof opt === 'function') {
          callback = opt;
          opt = null;
        }

        opt = merge$2({}, marked.defaults, opt || {});
        checkSanitizeDeprecation$1(opt);

        if (callback) {
          var highlight = opt.highlight;
          var tokens;

          try {
            tokens = Lexer_1.lex(src, opt);
          } catch (e) {
            return callback(e);
          }

          var done = function done(err) {
            var out;

            if (!err) {
              try {
                out = Parser_1.parse(tokens, opt);
              } catch (e) {
                err = e;
              }
            }

            opt.highlight = highlight;
            return err ? callback(err) : callback(null, out);
          };

          if (!highlight || highlight.length < 3) {
            return done();
          }

          delete opt.highlight;
          if (!tokens.length) return done();
          var pending = 0;
          marked.walkTokens(tokens, function (token) {
            if (token.type === 'code') {
              pending++;
              setTimeout(function () {
                highlight(token.text, token.lang, function (err, code) {
                  if (err) {
                    return done(err);
                  }

                  if (code != null && code !== token.text) {
                    token.text = code;
                    token.escaped = true;
                  }

                  pending--;

                  if (pending === 0) {
                    done();
                  }
                });
              }, 0);
            }
          });

          if (pending === 0) {
            done();
          }

          return;
        }

        try {
          var _tokens = Lexer_1.lex(src, opt);

          if (opt.walkTokens) {
            marked.walkTokens(_tokens, opt.walkTokens);
          }

          return Parser_1.parse(_tokens, opt);
        } catch (e) {
          e.message += '\nPlease report this to https://github.com/markedjs/marked.';

          if (opt.silent) {
            return '<p>An error occurred:</p><pre>' + escape$2(e.message + '', true) + '</pre>';
          }

          throw e;
        }
      }
      /**
       * Options
       */


      marked.options = marked.setOptions = function (opt) {
        merge$2(marked.defaults, opt);
        changeDefaults(marked.defaults);
        return marked;
      };

      marked.getDefaults = getDefaults;
      marked.defaults = defaults$5;
      /**
       * Use Extension
       */

      marked.use = function (extension) {
        var opts = merge$2({}, extension);

        if (extension.renderer) {
          (function () {
            var renderer = marked.defaults.renderer || new Renderer_1();

            var _loop = function _loop(prop) {
              var prevRenderer = renderer[prop];

              renderer[prop] = function () {
                for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
                  args[_key] = arguments[_key];
                }

                var ret = extension.renderer[prop].apply(renderer, args);

                if (ret === false) {
                  ret = prevRenderer.apply(renderer, args);
                }

                return ret;
              };
            };

            for (var prop in extension.renderer) {
              _loop(prop);
            }

            opts.renderer = renderer;
          })();
        }

        if (extension.tokenizer) {
          (function () {
            var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();

            var _loop2 = function _loop2(prop) {
              var prevTokenizer = tokenizer[prop];

              tokenizer[prop] = function () {
                for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
                  args[_key2] = arguments[_key2];
                }

                var ret = extension.tokenizer[prop].apply(tokenizer, args);

                if (ret === false) {
                  ret = prevTokenizer.apply(tokenizer, args);
                }

                return ret;
              };
            };

            for (var prop in extension.tokenizer) {
              _loop2(prop);
            }

            opts.tokenizer = tokenizer;
          })();
        }

        if (extension.walkTokens) {
          var walkTokens = marked.defaults.walkTokens;

          opts.walkTokens = function (token) {
            extension.walkTokens(token);

            if (walkTokens) {
              walkTokens(token);
            }
          };
        }

        marked.setOptions(opts);
      };
      /**
       * Run callback for every token
       */


      marked.walkTokens = function (tokens, callback) {
        for (var _iterator = _createForOfIteratorHelperLoose(tokens), _step; !(_step = _iterator()).done;) {
          var token = _step.value;
          callback(token);

          switch (token.type) {
            case 'table':
              {
                for (var _iterator2 = _createForOfIteratorHelperLoose(token.tokens.header), _step2; !(_step2 = _iterator2()).done;) {
                  var cell = _step2.value;
                  marked.walkTokens(cell, callback);
                }

                for (var _iterator3 = _createForOfIteratorHelperLoose(token.tokens.cells), _step3; !(_step3 = _iterator3()).done;) {
                  var row = _step3.value;

                  for (var _iterator4 = _createForOfIteratorHelperLoose(row), _step4; !(_step4 = _iterator4()).done;) {
                    var _cell = _step4.value;
                    marked.walkTokens(_cell, callback);
                  }
                }

                break;
              }

            case 'list':
              {
                marked.walkTokens(token.items, callback);
                break;
              }

            default:
              {
                if (token.tokens) {
                  marked.walkTokens(token.tokens, callback);
                }
              }
          }
        }
      };
      /**
       * Expose
       */


      marked.Parser = Parser_1;
      marked.parser = Parser_1.parse;
      marked.Renderer = Renderer_1;
      marked.TextRenderer = TextRenderer_1;
      marked.Lexer = Lexer_1;
      marked.lexer = Lexer_1.lex;
      marked.Tokenizer = Tokenizer_1;
      marked.Slugger = Slugger_1;
      marked.parse = marked;
      var marked_1 = marked;
      return marked_1;
    });
  });

  var purify = createCommonjsModule(function (module, exports) {
    /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.0.8/LICENSE */
    (function (global, factory) {
       module.exports = factory() ;
    })(commonjsGlobal, function () {

      function _toConsumableArray(arr) {
        if (Array.isArray(arr)) {
          for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
            arr2[i] = arr[i];
          }

          return arr2;
        } else {
          return Array.from(arr);
        }
      }

      var hasOwnProperty = Object.hasOwnProperty,
          setPrototypeOf = Object.setPrototypeOf,
          isFrozen = Object.isFrozen,
          objectKeys = Object.keys;
      var freeze = Object.freeze,
          seal = Object.seal; // eslint-disable-line import/no-mutable-exports

      var _ref = typeof Reflect !== 'undefined' && Reflect,
          apply = _ref.apply,
          construct = _ref.construct;

      if (!apply) {
        apply = function apply(fun, thisValue, args) {
          return fun.apply(thisValue, args);
        };
      }

      if (!freeze) {
        freeze = function freeze(x) {
          return x;
        };
      }

      if (!seal) {
        seal = function seal(x) {
          return x;
        };
      }

      if (!construct) {
        construct = function construct(Func, args) {
          return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
        };
      }

      var arrayForEach = unapply(Array.prototype.forEach);
      var arrayIndexOf = unapply(Array.prototype.indexOf);
      var arrayJoin = unapply(Array.prototype.join);
      var arrayPop = unapply(Array.prototype.pop);
      var arrayPush = unapply(Array.prototype.push);
      var arraySlice = unapply(Array.prototype.slice);
      var stringToLowerCase = unapply(String.prototype.toLowerCase);
      var stringMatch = unapply(String.prototype.match);
      var stringReplace = unapply(String.prototype.replace);
      var stringIndexOf = unapply(String.prototype.indexOf);
      var stringTrim = unapply(String.prototype.trim);
      var regExpTest = unapply(RegExp.prototype.test);
      var regExpCreate = unconstruct(RegExp);
      var typeErrorCreate = unconstruct(TypeError);

      function unapply(func) {
        return function (thisArg) {
          for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }

          return apply(func, thisArg, args);
        };
      }

      function unconstruct(func) {
        return function () {
          for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
            args[_key2] = arguments[_key2];
          }

          return construct(func, args);
        };
      }
      /* Add properties to a lookup table */


      function addToSet(set, array) {
        if (setPrototypeOf) {
          // Make 'in' and truthy checks like Boolean(set.constructor)
          // independent of any properties defined on Object.prototype.
          // Prevent prototype setters from intercepting set as a this value.
          setPrototypeOf(set, null);
        }

        var l = array.length;

        while (l--) {
          var element = array[l];

          if (typeof element === 'string') {
            var lcElement = stringToLowerCase(element);

            if (lcElement !== element) {
              // Config presets (e.g. tags.js, attrs.js) are immutable.
              if (!isFrozen(array)) {
                array[l] = lcElement;
              }

              element = lcElement;
            }
          }

          set[element] = true;
        }

        return set;
      }
      /* Shallow clone an object */


      function clone(object) {
        var newObject = {};
        var property = void 0;

        for (property in object) {
          if (apply(hasOwnProperty, object, [property])) {
            newObject[property] = object[property];
          }
        }

        return newObject;
      }

      var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG

      var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'video', 'view', 'vkern']);
      var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);
      var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);
      var text = freeze(['#text']);
      var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']);
      var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
      var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
      var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); // eslint-disable-next-line unicorn/better-regex

      var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode

      var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
      var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape

      var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape

      var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
      );
      var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
      var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g // eslint-disable-line no-control-regex
      );

      var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
        return typeof obj;
      } : function (obj) {
        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
      };

      function _toConsumableArray$1(arr) {
        if (Array.isArray(arr)) {
          for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
            arr2[i] = arr[i];
          }

          return arr2;
        } else {
          return Array.from(arr);
        }
      }

      var getGlobal = function getGlobal() {
        return typeof window === 'undefined' ? null : window;
      };
      /**
       * Creates a no-op policy for internal use only.
       * Don't export this function outside this module!
       * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
       * @param {Document} document The document object (to determine policy name suffix)
       * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
       * are not supported).
       */


      var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
        if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
          return null;
        } // Allow the callers to control the unique policy name
        // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
        // Policy creation with duplicate names throws in Trusted Types.


        var suffix = null;
        var ATTR_NAME = 'data-tt-policy-suffix';

        if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
          suffix = document.currentScript.getAttribute(ATTR_NAME);
        }

        var policyName = 'dompurify' + (suffix ? '#' + suffix : '');

        try {
          return trustedTypes.createPolicy(policyName, {
            createHTML: function createHTML(html$$1) {
              return html$$1;
            }
          });
        } catch (_) {
          // Policy creation failed (most likely another DOMPurify script has
          // already run). Skip creating the policy, as this will only cause errors
          // if TT are enforced.
          console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
          return null;
        }
      };

      function createDOMPurify() {
        var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();

        var DOMPurify = function DOMPurify(root) {
          return createDOMPurify(root);
        };
        /**
         * Version label, exposed for easier checks
         * if DOMPurify is up to date or not
         */


        DOMPurify.version = '2.0.12';
        /**
         * Array of elements that DOMPurify removed during sanitation.
         * Empty if nothing was removed.
         */

        DOMPurify.removed = [];

        if (!window || !window.document || window.document.nodeType !== 9) {
          // Not running in a browser, provide a factory function
          // so that you can pass your own Window
          DOMPurify.isSupported = false;
          return DOMPurify;
        }

        var originalDocument = window.document;
        var removeTitle = false;
        var document = window.document;
        var DocumentFragment = window.DocumentFragment,
            HTMLTemplateElement = window.HTMLTemplateElement,
            Node = window.Node,
            NodeFilter = window.NodeFilter,
            _window$NamedNodeMap = window.NamedNodeMap,
            NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
            Text = window.Text,
            Comment = window.Comment,
            DOMParser = window.DOMParser,
            trustedTypes = window.trustedTypes; // As per issue #47, the web-components registry is inherited by a
        // new document created via createHTMLDocument. As per the spec
        // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
        // a new empty registry is used when creating a template contents owner
        // document, so we use that as our parent document to ensure nothing
        // is inherited.

        if (typeof HTMLTemplateElement === 'function') {
          var template = document.createElement('template');

          if (template.content && template.content.ownerDocument) {
            document = template.content.ownerDocument;
          }
        }

        var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);

        var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
        var _document = document,
            implementation = _document.implementation,
            createNodeIterator = _document.createNodeIterator,
            getElementsByTagName = _document.getElementsByTagName,
            createDocumentFragment = _document.createDocumentFragment;
        var importNode = originalDocument.importNode;
        var hooks = {};
        /**
         * Expose whether this browser supports running the full DOMPurify.
         */

        DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && document.documentMode !== 9;
        var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
            ERB_EXPR$$1 = ERB_EXPR,
            DATA_ATTR$$1 = DATA_ATTR,
            ARIA_ATTR$$1 = ARIA_ATTR,
            IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
            ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
        var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
        /**
         * We consider the elements and attributes below to be safe. Ideally
         * don't add any new ones but feel free to remove unwanted ones.
         */

        /* allowed element names */

        var ALLOWED_TAGS = null;
        var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));
        /* Allowed attribute names */

        var ALLOWED_ATTR = null;
        var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
        /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */

        var FORBID_TAGS = null;
        /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */

        var FORBID_ATTR = null;
        /* Decide if ARIA attributes are okay */

        var ALLOW_ARIA_ATTR = true;
        /* Decide if custom data attributes are okay */

        var ALLOW_DATA_ATTR = true;
        /* Decide if unknown protocols are okay */

        var ALLOW_UNKNOWN_PROTOCOLS = false;
        /* Output should be safe for jQuery's $() factory? */

        var SAFE_FOR_JQUERY = false;
        /* Output should be safe for common template engines.
         * This means, DOMPurify removes data attributes, mustaches and ERB
         */

        var SAFE_FOR_TEMPLATES = false;
        /* Decide if document with <html>... should be returned */

        var WHOLE_DOCUMENT = false;
        /* Track whether config is already set on this instance of DOMPurify. */

        var SET_CONFIG = false;
        /* Decide if all elements (e.g. style, script) must be children of
         * document.body. By default, browsers might move them to document.head */

        var FORCE_BODY = false;
        /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
         * string (or a TrustedHTML object if Trusted Types are supported).
         * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
         */

        var RETURN_DOM = false;
        /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
         * string  (or a TrustedHTML object if Trusted Types are supported) */

        var RETURN_DOM_FRAGMENT = false;
        /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
         * `Node` is imported into the current `Document`. If this flag is not enabled the
         * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
         * DOMPurify. */

        var RETURN_DOM_IMPORT = false;
        /* Try to return a Trusted Type object instead of a string, retrun a string in
         * case Trusted Types are not supported  */

        var RETURN_TRUSTED_TYPE = false;
        /* Output should be free from DOM clobbering attacks? */

        var SANITIZE_DOM = true;
        /* Keep element content when removing element? */

        var KEEP_CONTENT = true;
        /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
         * of importing it into a new Document and returning a sanitized copy */

        var IN_PLACE = false;
        /* Allow usage of profiles like html, svg and mathMl */

        var USE_PROFILES = {};
        /* Tags to ignore content of when KEEP_CONTENT is true */

        var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
        /* Tags that are safe for data: URIs */

        var DATA_URI_TAGS = null;
        var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
        /* Attributes safe for values like "javascript:" */

        var URI_SAFE_ATTRIBUTES = null;
        var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
        /* Keep a reference to config to pass to hooks */

        var CONFIG = null;
        /* Ideally, do not touch anything below this line */

        /* ______________________________________________ */

        var formElement = document.createElement('form');
        /**
         * _parseConfig
         *
         * @param  {Object} cfg optional config literal
         */
        // eslint-disable-next-line complexity

        var _parseConfig = function _parseConfig(cfg) {
          if (CONFIG && CONFIG === cfg) {
            return;
          }
          /* Shield configuration object from tampering */


          if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
            cfg = {};
          }
          /* Set configuration parameters */


          ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
          ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
          URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
          DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
          FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
          FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
          USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
          ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true

          ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true

          ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false

          SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false

          SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false

          WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false

          RETURN_DOM = cfg.RETURN_DOM || false; // Default false

          RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false

          RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false

          RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false

          FORCE_BODY = cfg.FORCE_BODY || false; // Default false

          SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true

          KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true

          IN_PLACE = cfg.IN_PLACE || false; // Default false

          IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;

          if (SAFE_FOR_TEMPLATES) {
            ALLOW_DATA_ATTR = false;
          }

          if (RETURN_DOM_FRAGMENT) {
            RETURN_DOM = true;
          }
          /* Parse profile info */


          if (USE_PROFILES) {
            ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
            ALLOWED_ATTR = [];

            if (USE_PROFILES.html === true) {
              addToSet(ALLOWED_TAGS, html);
              addToSet(ALLOWED_ATTR, html$1);
            }

            if (USE_PROFILES.svg === true) {
              addToSet(ALLOWED_TAGS, svg);
              addToSet(ALLOWED_ATTR, svg$1);
              addToSet(ALLOWED_ATTR, xml);
            }

            if (USE_PROFILES.svgFilters === true) {
              addToSet(ALLOWED_TAGS, svgFilters);
              addToSet(ALLOWED_ATTR, svg$1);
              addToSet(ALLOWED_ATTR, xml);
            }

            if (USE_PROFILES.mathMl === true) {
              addToSet(ALLOWED_TAGS, mathMl);
              addToSet(ALLOWED_ATTR, mathMl$1);
              addToSet(ALLOWED_ATTR, xml);
            }
          }
          /* Merge configuration parameters */


          if (cfg.ADD_TAGS) {
            if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
              ALLOWED_TAGS = clone(ALLOWED_TAGS);
            }

            addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
          }

          if (cfg.ADD_ATTR) {
            if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
              ALLOWED_ATTR = clone(ALLOWED_ATTR);
            }

            addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
          }

          if (cfg.ADD_URI_SAFE_ATTR) {
            addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
          }
          /* Add #text in case KEEP_CONTENT is set to true */


          if (KEEP_CONTENT) {
            ALLOWED_TAGS['#text'] = true;
          }
          /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */


          if (WHOLE_DOCUMENT) {
            addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
          }
          /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */


          if (ALLOWED_TAGS.table) {
            addToSet(ALLOWED_TAGS, ['tbody']);
            delete FORBID_TAGS.tbody;
          } // Prevent further manipulation of configuration.
          // Not available in IE8, Safari 5, etc.


          if (freeze) {
            freeze(cfg);
          }

          CONFIG = cfg;
        };
        /**
         * _forceRemove
         *
         * @param  {Node} node a DOM node
         */


        var _forceRemove = function _forceRemove(node) {
          arrayPush(DOMPurify.removed, {
            element: node
          });

          try {
            // eslint-disable-next-line unicorn/prefer-node-remove
            node.parentNode.removeChild(node);
          } catch (_) {
            node.outerHTML = emptyHTML;
          }
        };
        /**
         * _removeAttribute
         *
         * @param  {String} name an Attribute name
         * @param  {Node} node a DOM node
         */


        var _removeAttribute = function _removeAttribute(name, node) {
          try {
            arrayPush(DOMPurify.removed, {
              attribute: node.getAttributeNode(name),
              from: node
            });
          } catch (_) {
            arrayPush(DOMPurify.removed, {
              attribute: null,
              from: node
            });
          }

          node.removeAttribute(name);
        };
        /**
         * _initDocument
         *
         * @param  {String} dirty a string of dirty markup
         * @return {Document} a DOM, filled with the dirty markup
         */


        var _initDocument = function _initDocument(dirty) {
          /* Create a HTML document */
          var doc = void 0;
          var leadingWhitespace = void 0;

          if (FORCE_BODY) {
            dirty = '<remove></remove>' + dirty;
          } else {
            /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
            var matches = stringMatch(dirty, /^[\r\n\t ]+/);
            leadingWhitespace = matches && matches[0];
          }

          var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
          /* Use the DOMParser API by default, fallback later if needs be */

          try {
            doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
          } catch (_) {}
          /* Remove title to fix a mXSS bug in older MS Edge */


          if (removeTitle) {
            addToSet(FORBID_TAGS, ['title']);
          }
          /* Use createHTMLDocument in case DOMParser is not available */


          if (!doc || !doc.documentElement) {
            doc = implementation.createHTMLDocument('');
            var _doc = doc,
                body = _doc.body;
            body.parentNode.removeChild(body.parentNode.firstElementChild);
            body.outerHTML = dirtyPayload;
          }

          if (dirty && leadingWhitespace) {
            doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
          }
          /* Work on whole document or just its body */


          return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
        };
        /* Here we test for a broken feature in Edge that might cause mXSS */


        if (DOMPurify.isSupported) {
          (function () {
            try {
              var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');

              if (regExpTest(/<\/title/, doc.querySelector('title').innerHTML)) {
                removeTitle = true;
              }
            } catch (_) {}
          })();
        }
        /**
         * _createIterator
         *
         * @param  {Document} root document/fragment to create iterator for
         * @return {Iterator} iterator instance
         */


        var _createIterator = function _createIterator(root) {
          return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
            return NodeFilter.FILTER_ACCEPT;
          }, false);
        };
        /**
         * _isClobbered
         *
         * @param  {Node} elm element to check for clobbering attacks
         * @return {Boolean} true if clobbered, false if safe
         */


        var _isClobbered = function _isClobbered(elm) {
          if (elm instanceof Text || elm instanceof Comment) {
            return false;
          }

          if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string') {
            return true;
          }

          return false;
        };
        /**
         * _isNode
         *
         * @param  {Node} obj object to check whether it's a DOM node
         * @return {Boolean} true is object is a DOM node
         */


        var _isNode = function _isNode(object) {
          return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
        };
        /**
         * _executeHook
         * Execute user configurable hooks
         *
         * @param  {String} entryPoint  Name of the hook's entry point
         * @param  {Node} currentNode node to work on with the hook
         * @param  {Object} data additional hook parameters
         */


        var _executeHook = function _executeHook(entryPoint, currentNode, data) {
          if (!hooks[entryPoint]) {
            return;
          }

          arrayForEach(hooks[entryPoint], function (hook) {
            hook.call(DOMPurify, currentNode, data, CONFIG);
          });
        };
        /**
         * _sanitizeElements
         *
         * @protect nodeName
         * @protect textContent
         * @protect removeChild
         *
         * @param   {Node} currentNode to check for permission to exist
         * @return  {Boolean} true if node was killed, false if left alive
         */
        // eslint-disable-next-line complexity


        var _sanitizeElements = function _sanitizeElements(currentNode) {
          var content = void 0;
          /* Execute a hook if present */

          _executeHook('beforeSanitizeElements', currentNode, null);
          /* Check if element is clobbered or can clobber */


          if (_isClobbered(currentNode)) {
            _forceRemove(currentNode);

            return true;
          }
          /* Now let's check the element's type and name */


          var tagName = stringToLowerCase(currentNode.nodeName);
          /* Execute a hook if present */

          _executeHook('uponSanitizeElement', currentNode, {
            tagName: tagName,
            allowedTags: ALLOWED_TAGS
          });
          /* Take care of an mXSS pattern using p, br inside svg, math */


          if ((tagName === 'svg' || tagName === 'math') && currentNode.querySelectorAll('p, br').length !== 0) {
            _forceRemove(currentNode);

            return true;
          }
          /* Remove element if anything forbids its presence */


          if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
            /* Keep content except for bad-listed elements */
            if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
              try {
                var htmlToInsert = currentNode.innerHTML;
                currentNode.insertAdjacentHTML('AfterEnd', trustedTypesPolicy ? trustedTypesPolicy.createHTML(htmlToInsert) : htmlToInsert);
              } catch (_) {}
            }

            _forceRemove(currentNode);

            return true;
          }
          /* Remove in case a noscript/noembed XSS is suspected */


          if (tagName === 'noscript' && regExpTest(/<\/noscript/i, currentNode.innerHTML)) {
            _forceRemove(currentNode);

            return true;
          }

          if (tagName === 'noembed' && regExpTest(/<\/noembed/i, currentNode.innerHTML)) {
            _forceRemove(currentNode);

            return true;
          }
          /* Convert markup to cover jQuery behavior */


          if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && (!currentNode.content || !currentNode.content.firstElementChild) && regExpTest(/</g, currentNode.textContent)) {
            arrayPush(DOMPurify.removed, {
              element: currentNode.cloneNode()
            });

            if (currentNode.innerHTML) {
              currentNode.innerHTML = stringReplace(currentNode.innerHTML, /</g, '&lt;');
            } else {
              currentNode.innerHTML = stringReplace(currentNode.textContent, /</g, '&lt;');
            }
          }
          /* Sanitize element content to be template-safe */


          if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
            /* Get the element's text content */
            content = currentNode.textContent;
            content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
            content = stringReplace(content, ERB_EXPR$$1, ' ');

            if (currentNode.textContent !== content) {
              arrayPush(DOMPurify.removed, {
                element: currentNode.cloneNode()
              });
              currentNode.textContent = content;
            }
          }
          /* Execute a hook if present */


          _executeHook('afterSanitizeElements', currentNode, null);

          return false;
        };
        /**
         * _isValidAttribute
         *
         * @param  {string} lcTag Lowercase tag name of containing element.
         * @param  {string} lcName Lowercase attribute name.
         * @param  {string} value Attribute value.
         * @return {Boolean} Returns true if `value` is valid, otherwise false.
         */
        // eslint-disable-next-line complexity


        var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
          /* Make sure attribute cannot clobber */
          if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
            return false;
          }
          /* Allow valid data-* attributes: At least one character after "-"
              (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
              XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
              We don't need to check the value; it's always URI safe. */


          if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ;else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ;else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
            return false;
            /* Check value is safe. First, is attr inert? If so, is safe */
          } else if (URI_SAFE_ATTRIBUTES[lcName]) ;else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ;else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ;else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ;else if (!value) ;else {
            return false;
          }
          return true;
        };
        /**
         * _sanitizeAttributes
         *
         * @protect attributes
         * @protect nodeName
         * @protect removeAttribute
         * @protect setAttribute
         *
         * @param  {Node} currentNode to sanitize
         */
        // eslint-disable-next-line complexity


        var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
          var attr = void 0;
          var value = void 0;
          var lcName = void 0;
          var idAttr = void 0;
          var l = void 0;
          /* Execute a hook if present */

          _executeHook('beforeSanitizeAttributes', currentNode, null);

          var attributes = currentNode.attributes;
          /* Check if we have attributes; if not we might have a text node */

          if (!attributes) {
            return;
          }

          var hookEvent = {
            attrName: '',
            attrValue: '',
            keepAttr: true,
            allowedAttributes: ALLOWED_ATTR
          };
          l = attributes.length;
          /* Go backwards over all attributes; safely remove bad ones */

          while (l--) {
            attr = attributes[l];
            var _attr = attr,
                name = _attr.name,
                namespaceURI = _attr.namespaceURI;
            value = stringTrim(attr.value);
            lcName = stringToLowerCase(name);
            /* Execute a hook if present */

            hookEvent.attrName = lcName;
            hookEvent.attrValue = value;
            hookEvent.keepAttr = true;
            hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set

            _executeHook('uponSanitizeAttribute', currentNode, hookEvent);

            value = hookEvent.attrValue;
            /* Did the hooks approve of the attribute? */

            if (hookEvent.forceKeepAttr) {
              continue;
            }
            /* Remove attribute */
            // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
            // remove a "name" attribute from an <img> tag that has an "id"
            // attribute at the time.


            if (lcName === 'name' && currentNode.nodeName === 'IMG' && attributes.id) {
              idAttr = attributes.id;
              attributes = arraySlice(attributes, []);

              _removeAttribute('id', currentNode);

              _removeAttribute(name, currentNode);

              if (arrayIndexOf(attributes, idAttr) > l) {
                currentNode.setAttribute('id', idAttr.value);
              }
            } else if ( // This works around a bug in Safari, where input[type=file]
            // cannot be dynamically set after type has been removed
            currentNode.nodeName === 'INPUT' && lcName === 'type' && value === 'file' && hookEvent.keepAttr && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
              continue;
            } else {
              // This avoids a crash in Safari v9.0 with double-ids.
              // The trick is to first set the id to be empty and then to
              // remove the attribute
              if (name === 'id') {
                currentNode.setAttribute(name, '');
              }

              _removeAttribute(name, currentNode);
            }
            /* Did the hooks approve of the attribute? */


            if (!hookEvent.keepAttr) {
              continue;
            }
            /* Work around a security issue in jQuery 3.0 */


            if (SAFE_FOR_JQUERY && regExpTest(/\/>/i, value)) {
              _removeAttribute(name, currentNode);

              continue;
            }
            /* Take care of an mXSS pattern using namespace switches */


            if (regExpTest(/svg|math/i, currentNode.namespaceURI) && regExpTest(regExpCreate('</(' + arrayJoin(objectKeys(FORBID_CONTENTS), '|') + ')', 'i'), value)) {
              _removeAttribute(name, currentNode);

              continue;
            }
            /* Sanitize attribute content to be template-safe */


            if (SAFE_FOR_TEMPLATES) {
              value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
              value = stringReplace(value, ERB_EXPR$$1, ' ');
            }
            /* Is `value` valid for this attribute? */


            var lcTag = currentNode.nodeName.toLowerCase();

            if (!_isValidAttribute(lcTag, lcName, value)) {
              continue;
            }
            /* Handle invalid data-* attribute set by try-catching it */


            try {
              if (namespaceURI) {
                currentNode.setAttributeNS(namespaceURI, name, value);
              } else {
                /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
                currentNode.setAttribute(name, value);
              }

              arrayPop(DOMPurify.removed);
            } catch (_) {}
          }
          /* Execute a hook if present */


          _executeHook('afterSanitizeAttributes', currentNode, null);
        };
        /**
         * _sanitizeShadowDOM
         *
         * @param  {DocumentFragment} fragment to iterate over recursively
         */


        var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
          var shadowNode = void 0;

          var shadowIterator = _createIterator(fragment);
          /* Execute a hook if present */


          _executeHook('beforeSanitizeShadowDOM', fragment, null);

          while (shadowNode = shadowIterator.nextNode()) {
            /* Execute a hook if present */
            _executeHook('uponSanitizeShadowNode', shadowNode, null);
            /* Sanitize tags and elements */


            if (_sanitizeElements(shadowNode)) {
              continue;
            }
            /* Deep shadow DOM detected */


            if (shadowNode.content instanceof DocumentFragment) {
              _sanitizeShadowDOM(shadowNode.content);
            }
            /* Check attributes, sanitize if necessary */


            _sanitizeAttributes(shadowNode);
          }
          /* Execute a hook if present */


          _executeHook('afterSanitizeShadowDOM', fragment, null);
        };
        /**
         * Sanitize
         * Public method providing core sanitation functionality
         *
         * @param {String|Node} dirty string or DOM node
         * @param {Object} configuration object
         */
        // eslint-disable-next-line complexity


        DOMPurify.sanitize = function (dirty, cfg) {
          var body = void 0;
          var importedNode = void 0;
          var currentNode = void 0;
          var oldNode = void 0;
          var returnNode = void 0;
          /* Make sure we have a string to sanitize.
            DO NOT return early, as this will return the wrong type if
            the user has requested a DOM object rather than a string */

          if (!dirty) {
            dirty = '<!-->';
          }
          /* Stringify, in case dirty is an object */


          if (typeof dirty !== 'string' && !_isNode(dirty)) {
            // eslint-disable-next-line no-negated-condition
            if (typeof dirty.toString !== 'function') {
              throw typeErrorCreate('toString is not a function');
            } else {
              dirty = dirty.toString();

              if (typeof dirty !== 'string') {
                throw typeErrorCreate('dirty is not a string, aborting');
              }
            }
          }
          /* Check we can run. Otherwise fall back or ignore */


          if (!DOMPurify.isSupported) {
            if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
              if (typeof dirty === 'string') {
                return window.toStaticHTML(dirty);
              }

              if (_isNode(dirty)) {
                return window.toStaticHTML(dirty.outerHTML);
              }
            }

            return dirty;
          }
          /* Assign config vars */


          if (!SET_CONFIG) {
            _parseConfig(cfg);
          }
          /* Clean up removed elements */


          DOMPurify.removed = [];
          /* Check if dirty is correctly typed for IN_PLACE */

          if (typeof dirty === 'string') {
            IN_PLACE = false;
          }

          if (IN_PLACE) ;else if (dirty instanceof Node) {
            /* If dirty is a DOM element, append to an empty document to avoid
               elements being stripped by the parser */
            body = _initDocument('<!-->');
            importedNode = body.ownerDocument.importNode(dirty, true);

            if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
              /* Node is already a body, use as is */
              body = importedNode;
            } else if (importedNode.nodeName === 'HTML') {
              body = importedNode;
            } else {
              // eslint-disable-next-line unicorn/prefer-node-append
              body.appendChild(importedNode);
            }
          } else {
            /* Exit directly if we have nothing to do */
            if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
            dirty.indexOf('<') === -1) {
              return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
            }
            /* Initialize the document to work on */


            body = _initDocument(dirty);
            /* Check we have a DOM node from the data */

            if (!body) {
              return RETURN_DOM ? null : emptyHTML;
            }
          }
          /* Remove first element node (ours) if FORCE_BODY is set */

          if (body && FORCE_BODY) {
            _forceRemove(body.firstChild);
          }
          /* Get node iterator */


          var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
          /* Now start iterating over the created document */


          while (currentNode = nodeIterator.nextNode()) {
            /* Fix IE's strange behavior with manipulated textNodes #89 */
            if (currentNode.nodeType === 3 && currentNode === oldNode) {
              continue;
            }
            /* Sanitize tags and elements */


            if (_sanitizeElements(currentNode)) {
              continue;
            }
            /* Shadow DOM detected, sanitize it */


            if (currentNode.content instanceof DocumentFragment) {
              _sanitizeShadowDOM(currentNode.content);
            }
            /* Check attributes, sanitize if necessary */


            _sanitizeAttributes(currentNode);

            oldNode = currentNode;
          }

          oldNode = null;
          /* If we sanitized `dirty` in-place, return it. */

          if (IN_PLACE) {
            return dirty;
          }
          /* Return sanitized string or DOM */


          if (RETURN_DOM) {
            if (RETURN_DOM_FRAGMENT) {
              returnNode = createDocumentFragment.call(body.ownerDocument);

              while (body.firstChild) {
                // eslint-disable-next-line unicorn/prefer-node-append
                returnNode.appendChild(body.firstChild);
              }
            } else {
              returnNode = body;
            }

            if (RETURN_DOM_IMPORT) {
              /*
                AdoptNode() is not used because internal state is not reset
                (e.g. the past names map of a HTMLFormElement), this is safe
                in theory but we would rather not risk another attack vector.
                The state that is cloned by importNode() is explicitly defined
                by the specs.
              */
              returnNode = importNode.call(originalDocument, returnNode, true);
            }

            return returnNode;
          }

          var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
          /* Sanitize final string template-safe */

          if (SAFE_FOR_TEMPLATES) {
            serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
            serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
          }

          return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
        };
        /**
         * Public method to set the configuration once
         * setConfig
         *
         * @param {Object} cfg configuration object
         */


        DOMPurify.setConfig = function (cfg) {
          _parseConfig(cfg);

          SET_CONFIG = true;
        };
        /**
         * Public method to remove the configuration
         * clearConfig
         *
         */


        DOMPurify.clearConfig = function () {
          CONFIG = null;
          SET_CONFIG = false;
        };
        /**
         * Public method to check if an attribute value is valid.
         * Uses last set config, if any. Otherwise, uses config defaults.
         * isValidAttribute
         *
         * @param  {string} tag Tag name of containing element.
         * @param  {string} attr Attribute name.
         * @param  {string} value Attribute value.
         * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
         */


        DOMPurify.isValidAttribute = function (tag, attr, value) {
          /* Initialize shared config vars if necessary. */
          if (!CONFIG) {
            _parseConfig({});
          }

          var lcTag = stringToLowerCase(tag);
          var lcName = stringToLowerCase(attr);
          return _isValidAttribute(lcTag, lcName, value);
        };
        /**
         * AddHook
         * Public method to add DOMPurify hooks
         *
         * @param {String} entryPoint entry point for the hook to add
         * @param {Function} hookFunction function to execute
         */


        DOMPurify.addHook = function (entryPoint, hookFunction) {
          if (typeof hookFunction !== 'function') {
            return;
          }

          hooks[entryPoint] = hooks[entryPoint] || [];
          arrayPush(hooks[entryPoint], hookFunction);
        };
        /**
         * RemoveHook
         * Public method to remove a DOMPurify hook at a given entryPoint
         * (pops it from the stack of hooks if more are present)
         *
         * @param {String} entryPoint entry point for the hook to remove
         */


        DOMPurify.removeHook = function (entryPoint) {
          if (hooks[entryPoint]) {
            arrayPop(hooks[entryPoint]);
          }
        };
        /**
         * RemoveHooks
         * Public method to remove all DOMPurify hooks at a given entryPoint
         *
         * @param  {String} entryPoint entry point for the hooks to remove
         */


        DOMPurify.removeHooks = function (entryPoint) {
          if (hooks[entryPoint]) {
            hooks[entryPoint] = [];
          }
        };
        /**
         * RemoveAllHooks
         * Public method to remove all DOMPurify hooks
         *
         */


        DOMPurify.removeAllHooks = function () {
          hooks = {};
        };

        return DOMPurify;
      }

      var purify = createDOMPurify();
      return purify;
    });
  });

  /**
   * lodash (Custom Build) <https://lodash.com/>
   * Build: `lodash modularize exports="npm" -o ./`
   * Copyright jQuery Foundation and other contributors <https://jquery.org/>
   * Released under MIT license <https://lodash.com/license>
   * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
   * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
   */

  /** Used as references for various `Number` constants. */

  var MAX_SAFE_INTEGER = 9007199254740991;
  /** `Object#toString` result references. */

  var argsTag = '[object Arguments]',
      funcTag = '[object Function]',
      genTag = '[object GeneratorFunction]',
      mapTag = '[object Map]',
      objectTag = '[object Object]',
      promiseTag = '[object Promise]',
      setTag = '[object Set]',
      stringTag = '[object String]',
      weakMapTag = '[object WeakMap]';
  var dataViewTag = '[object DataView]';
  /**
   * Used to match `RegExp`
   * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
   */

  var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
  /** Used to detect host constructors (Safari). */

  var reIsHostCtor = /^\[object .+?Constructor\]$/;
  /** Used to detect unsigned integer values. */

  var reIsUint = /^(?:0|[1-9]\d*)$/;
  /** Used to compose unicode character classes. */

  var rsAstralRange = '\\ud800-\\udfff',
      rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
      rsComboSymbolsRange = '\\u20d0-\\u20f0',
      rsVarRange = '\\ufe0e\\ufe0f';
  /** Used to compose unicode capture groups. */

  var rsAstral = '[' + rsAstralRange + ']',
      rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
      rsFitz = '\\ud83c[\\udffb-\\udfff]',
      rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
      rsNonAstral = '[^' + rsAstralRange + ']',
      rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
      rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
      rsZWJ = '\\u200d';
  /** Used to compose unicode regexes. */

  var reOptMod = rsModifier + '?',
      rsOptVar = '[' + rsVarRange + ']?',
      rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
      rsSeq = rsOptVar + reOptMod + rsOptJoin,
      rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */

  var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */

  var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
  /** Detect free variable `global` from Node.js. */

  var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
  /** Detect free variable `self`. */

  var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
  /** Used as a reference to the global object. */

  var root$1 = freeGlobal || freeSelf || Function('return this')();
  /**
   * A specialized version of `_.map` for arrays without support for iteratee
   * shorthands.
   *
   * @private
   * @param {Array} [array] The array to iterate over.
   * @param {Function} iteratee The function invoked per iteration.
   * @returns {Array} Returns the new mapped array.
   */

  function arrayMap(array, iteratee) {
    var index = -1,
        length = array ? array.length : 0,
        result = Array(length);

    while (++index < length) {
      result[index] = iteratee(array[index], index, array);
    }

    return result;
  }
  /**
   * Converts an ASCII `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */


  function asciiToArray(string) {
    return string.split('');
  }
  /**
   * The base implementation of `_.times` without support for iteratee shorthands
   * or max array length checks.
   *
   * @private
   * @param {number} n The number of times to invoke `iteratee`.
   * @param {Function} iteratee The function invoked per iteration.
   * @returns {Array} Returns the array of results.
   */


  function baseTimes(n, iteratee) {
    var index = -1,
        result = Array(n);

    while (++index < n) {
      result[index] = iteratee(index);
    }

    return result;
  }
  /**
   * The base implementation of `_.values` and `_.valuesIn` which creates an
   * array of `object` property values corresponding to the property names
   * of `props`.
   *
   * @private
   * @param {Object} object The object to query.
   * @param {Array} props The property names to get values for.
   * @returns {Object} Returns the array of property values.
   */


  function baseValues(object, props) {
    return arrayMap(props, function (key) {
      return object[key];
    });
  }
  /**
   * Gets the value at `key` of `object`.
   *
   * @private
   * @param {Object} [object] The object to query.
   * @param {string} key The key of the property to get.
   * @returns {*} Returns the property value.
   */


  function getValue(object, key) {
    return object == null ? undefined : object[key];
  }
  /**
   * Checks if `string` contains Unicode symbols.
   *
   * @private
   * @param {string} string The string to inspect.
   * @returns {boolean} Returns `true` if a symbol is found, else `false`.
   */


  function hasUnicode(string) {
    return reHasUnicode.test(string);
  }
  /**
   * Checks if `value` is a host object in IE < 9.
   *
   * @private
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
   */


  function isHostObject(value) {
    // Many host objects are `Object` objects that can coerce to strings
    // despite having improperly defined `toString` methods.
    var result = false;

    if (value != null && typeof value.toString != 'function') {
      try {
        result = !!(value + '');
      } catch (e) {}
    }

    return result;
  }
  /**
   * Converts `iterator` to an array.
   *
   * @private
   * @param {Object} iterator The iterator to convert.
   * @returns {Array} Returns the converted array.
   */


  function iteratorToArray(iterator) {
    var data,
        result = [];

    while (!(data = iterator.next()).done) {
      result.push(data.value);
    }

    return result;
  }
  /**
   * Converts `map` to its key-value pairs.
   *
   * @private
   * @param {Object} map The map to convert.
   * @returns {Array} Returns the key-value pairs.
   */


  function mapToArray(map) {
    var index = -1,
        result = Array(map.size);
    map.forEach(function (value, key) {
      result[++index] = [key, value];
    });
    return result;
  }
  /**
   * Creates a unary function that invokes `func` with its argument transformed.
   *
   * @private
   * @param {Function} func The function to wrap.
   * @param {Function} transform The argument transform.
   * @returns {Function} Returns the new function.
   */


  function overArg(func, transform) {
    return function (arg) {
      return func(transform(arg));
    };
  }
  /**
   * Converts `set` to an array of its values.
   *
   * @private
   * @param {Object} set The set to convert.
   * @returns {Array} Returns the values.
   */


  function setToArray(set) {
    var index = -1,
        result = Array(set.size);
    set.forEach(function (value) {
      result[++index] = value;
    });
    return result;
  }
  /**
   * Converts `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */


  function stringToArray(string) {
    return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
  }
  /**
   * Converts a Unicode `string` to an array.
   *
   * @private
   * @param {string} string The string to convert.
   * @returns {Array} Returns the converted array.
   */


  function unicodeToArray(string) {
    return string.match(reUnicode) || [];
  }
  /** Used for built-in method references. */


  var funcProto = Function.prototype,
      objectProto = Object.prototype;
  /** Used to detect overreaching core-js shims. */

  var coreJsData = root$1['__core-js_shared__'];
  /** Used to detect methods masquerading as native. */

  var maskSrcKey = function () {
    var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
    return uid ? 'Symbol(src)_1.' + uid : '';
  }();
  /** Used to resolve the decompiled source of functions. */


  var funcToString = funcProto.toString;
  /** Used to check objects for own properties. */

  var hasOwnProperty = objectProto.hasOwnProperty;
  /**
   * Used to resolve the
   * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
   * of values.
   */

  var objectToString = objectProto.toString;
  /** Used to detect if a method is native. */

  var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');
  /** Built-in value references. */

  var Symbol$1 = root$1.Symbol,
      iteratorSymbol = Symbol$1 ? Symbol$1.iterator : undefined,
      propertyIsEnumerable = objectProto.propertyIsEnumerable;
  /* Built-in method references for those with the same name as other `lodash` methods. */

  var nativeKeys = overArg(Object.keys, Object);
  /* Built-in method references that are verified to be native. */

  var DataView = getNative(root$1, 'DataView'),
      Map$1 = getNative(root$1, 'Map'),
      Promise$1 = getNative(root$1, 'Promise'),
      Set$1 = getNative(root$1, 'Set'),
      WeakMap$1 = getNative(root$1, 'WeakMap');
  /** Used to detect maps, sets, and weakmaps. */

  var dataViewCtorString = toSource(DataView),
      mapCtorString = toSource(Map$1),
      promiseCtorString = toSource(Promise$1),
      setCtorString = toSource(Set$1),
      weakMapCtorString = toSource(WeakMap$1);
  /**
   * Creates an array of the enumerable property names of the array-like `value`.
   *
   * @private
   * @param {*} value The value to query.
   * @param {boolean} inherited Specify returning inherited property names.
   * @returns {Array} Returns the array of property names.
   */

  function arrayLikeKeys(value, inherited) {
    // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
    // Safari 9 makes `arguments.length` enumerable in strict mode.
    var result = isArray(value) || isArguments(value) ? baseTimes(value.length, String) : [];
    var length = result.length,
        skipIndexes = !!length;

    for (var key in value) {
      if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || isIndex(key, length)))) {
        result.push(key);
      }
    }

    return result;
  }
  /**
   * The base implementation of `getTag`.
   *
   * @private
   * @param {*} value The value to query.
   * @returns {string} Returns the `toStringTag`.
   */


  function baseGetTag(value) {
    return objectToString.call(value);
  }
  /**
   * The base implementation of `_.isNative` without bad shim checks.
   *
   * @private
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a native function,
   *  else `false`.
   */


  function baseIsNative(value) {
    if (!isObject(value) || isMasked(value)) {
      return false;
    }

    var pattern = isFunction(value) || isHostObject(value) ? reIsNative : reIsHostCtor;
    return pattern.test(toSource(value));
  }
  /**
   * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
   *
   * @private
   * @param {Object} object The object to query.
   * @returns {Array} Returns the array of property names.
   */


  function baseKeys(object) {
    if (!isPrototype(object)) {
      return nativeKeys(object);
    }

    var result = [];

    for (var key in Object(object)) {
      if (hasOwnProperty.call(object, key) && key != 'constructor') {
        result.push(key);
      }
    }

    return result;
  }
  /**
   * Copies the values of `source` to `array`.
   *
   * @private
   * @param {Array} source The array to copy values from.
   * @param {Array} [array=[]] The array to copy values to.
   * @returns {Array} Returns `array`.
   */


  function copyArray(source, array) {
    var index = -1,
        length = source.length;
    array || (array = Array(length));

    while (++index < length) {
      array[index] = source[index];
    }

    return array;
  }
  /**
   * Gets the native function at `key` of `object`.
   *
   * @private
   * @param {Object} object The object to query.
   * @param {string} key The key of the method to get.
   * @returns {*} Returns the function if it's native, else `undefined`.
   */


  function getNative(object, key) {
    var value = getValue(object, key);
    return baseIsNative(value) ? value : undefined;
  }
  /**
   * Gets the `toStringTag` of `value`.
   *
   * @private
   * @param {*} value The value to query.
   * @returns {string} Returns the `toStringTag`.
   */


  var getTag = baseGetTag; // Fallback for data views, maps, sets, and weak maps in IE 11,
  // for data views in Edge < 14, and promises in Node.js.

  if (DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag || Map$1 && getTag(new Map$1()) != mapTag || Promise$1 && getTag(Promise$1.resolve()) != promiseTag || Set$1 && getTag(new Set$1()) != setTag || WeakMap$1 && getTag(new WeakMap$1()) != weakMapTag) {
    getTag = function (value) {
      var result = objectToString.call(value),
          Ctor = result == objectTag ? value.constructor : undefined,
          ctorString = Ctor ? toSource(Ctor) : undefined;

      if (ctorString) {
        switch (ctorString) {
          case dataViewCtorString:
            return dataViewTag;

          case mapCtorString:
            return mapTag;

          case promiseCtorString:
            return promiseTag;

          case setCtorString:
            return setTag;

          case weakMapCtorString:
            return weakMapTag;
        }
      }

      return result;
    };
  }
  /**
   * Checks if `value` is a valid array-like index.
   *
   * @private
   * @param {*} value The value to check.
   * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
   * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
   */


  function isIndex(value, length) {
    length = length == null ? MAX_SAFE_INTEGER : length;
    return !!length && (typeof value == 'number' || reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length;
  }
  /**
   * Checks if `func` has its source masked.
   *
   * @private
   * @param {Function} func The function to check.
   * @returns {boolean} Returns `true` if `func` is masked, else `false`.
   */


  function isMasked(func) {
    return !!maskSrcKey && maskSrcKey in func;
  }
  /**
   * Checks if `value` is likely a prototype object.
   *
   * @private
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
   */


  function isPrototype(value) {
    var Ctor = value && value.constructor,
        proto = typeof Ctor == 'function' && Ctor.prototype || objectProto;
    return value === proto;
  }
  /**
   * Converts `func` to its source code.
   *
   * @private
   * @param {Function} func The function to process.
   * @returns {string} Returns the source code.
   */


  function toSource(func) {
    if (func != null) {
      try {
        return funcToString.call(func);
      } catch (e) {}

      try {
        return func + '';
      } catch (e) {}
    }

    return '';
  }
  /**
   * Checks if `value` is likely an `arguments` object.
   *
   * @static
   * @memberOf _
   * @since 0.1.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is an `arguments` object,
   *  else `false`.
   * @example
   *
   * _.isArguments(function() { return arguments; }());
   * // => true
   *
   * _.isArguments([1, 2, 3]);
   * // => false
   */


  function isArguments(value) {
    // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
    return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
  }
  /**
   * Checks if `value` is classified as an `Array` object.
   *
   * @static
   * @memberOf _
   * @since 0.1.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is an array, else `false`.
   * @example
   *
   * _.isArray([1, 2, 3]);
   * // => true
   *
   * _.isArray(document.body.children);
   * // => false
   *
   * _.isArray('abc');
   * // => false
   *
   * _.isArray(_.noop);
   * // => false
   */


  var isArray = Array.isArray;
  /**
   * Checks if `value` is array-like. A value is considered array-like if it's
   * not a function and has a `value.length` that's an integer greater than or
   * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
   *
   * @static
   * @memberOf _
   * @since 4.0.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
   * @example
   *
   * _.isArrayLike([1, 2, 3]);
   * // => true
   *
   * _.isArrayLike(document.body.children);
   * // => true
   *
   * _.isArrayLike('abc');
   * // => true
   *
   * _.isArrayLike(_.noop);
   * // => false
   */

  function isArrayLike(value) {
    return value != null && isLength(value.length) && !isFunction(value);
  }
  /**
   * This method is like `_.isArrayLike` except that it also checks if `value`
   * is an object.
   *
   * @static
   * @memberOf _
   * @since 4.0.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is an array-like object,
   *  else `false`.
   * @example
   *
   * _.isArrayLikeObject([1, 2, 3]);
   * // => true
   *
   * _.isArrayLikeObject(document.body.children);
   * // => true
   *
   * _.isArrayLikeObject('abc');
   * // => false
   *
   * _.isArrayLikeObject(_.noop);
   * // => false
   */


  function isArrayLikeObject(value) {
    return isObjectLike(value) && isArrayLike(value);
  }
  /**
   * Checks if `value` is classified as a `Function` object.
   *
   * @static
   * @memberOf _
   * @since 0.1.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a function, else `false`.
   * @example
   *
   * _.isFunction(_);
   * // => true
   *
   * _.isFunction(/abc/);
   * // => false
   */


  function isFunction(value) {
    // The use of `Object#toString` avoids issues with the `typeof` operator
    // in Safari 8-9 which returns 'object' for typed array and other constructors.
    var tag = isObject(value) ? objectToString.call(value) : '';
    return tag == funcTag || tag == genTag;
  }
  /**
   * Checks if `value` is a valid array-like length.
   *
   * **Note:** This method is loosely based on
   * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
   *
   * @static
   * @memberOf _
   * @since 4.0.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
   * @example
   *
   * _.isLength(3);
   * // => true
   *
   * _.isLength(Number.MIN_VALUE);
   * // => false
   *
   * _.isLength(Infinity);
   * // => false
   *
   * _.isLength('3');
   * // => false
   */


  function isLength(value) {
    return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
  }
  /**
   * Checks if `value` is the
   * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
   * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
   *
   * @static
   * @memberOf _
   * @since 0.1.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is an object, else `false`.
   * @example
   *
   * _.isObject({});
   * // => true
   *
   * _.isObject([1, 2, 3]);
   * // => true
   *
   * _.isObject(_.noop);
   * // => true
   *
   * _.isObject(null);
   * // => false
   */


  function isObject(value) {
    var type = typeof value;
    return !!value && (type == 'object' || type == 'function');
  }
  /**
   * Checks if `value` is object-like. A value is object-like if it's not `null`
   * and has a `typeof` result of "object".
   *
   * @static
   * @memberOf _
   * @since 4.0.0
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
   * @example
   *
   * _.isObjectLike({});
   * // => true
   *
   * _.isObjectLike([1, 2, 3]);
   * // => true
   *
   * _.isObjectLike(_.noop);
   * // => false
   *
   * _.isObjectLike(null);
   * // => false
   */


  function isObjectLike(value) {
    return !!value && typeof value == 'object';
  }
  /**
   * Checks if `value` is classified as a `String` primitive or object.
   *
   * @static
   * @since 0.1.0
   * @memberOf _
   * @category Lang
   * @param {*} value The value to check.
   * @returns {boolean} Returns `true` if `value` is a string, else `false`.
   * @example
   *
   * _.isString('abc');
   * // => true
   *
   * _.isString(1);
   * // => false
   */


  function isString(value) {
    return typeof value == 'string' || !isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag;
  }
  /**
   * Converts `value` to an array.
   *
   * @static
   * @since 0.1.0
   * @memberOf _
   * @category Lang
   * @param {*} value The value to convert.
   * @returns {Array} Returns the converted array.
   * @example
   *
   * _.toArray({ 'a': 1, 'b': 2 });
   * // => [1, 2]
   *
   * _.toArray('abc');
   * // => ['a', 'b', 'c']
   *
   * _.toArray(1);
   * // => []
   *
   * _.toArray(null);
   * // => []
   */


  function toArray(value) {
    if (!value) {
      return [];
    }

    if (isArrayLike(value)) {
      return isString(value) ? stringToArray(value) : copyArray(value);
    }

    if (iteratorSymbol && value[iteratorSymbol]) {
      return iteratorToArray(value[iteratorSymbol]());
    }

    var tag = getTag(value),
        func = tag == mapTag ? mapToArray : tag == setTag ? setToArray : values;
    return func(value);
  }
  /**
   * Creates an array of the own enumerable property names of `object`.
   *
   * **Note:** Non-object values are coerced to objects. See the
   * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
   * for more details.
   *
   * @static
   * @since 0.1.0
   * @memberOf _
   * @category Object
   * @param {Object} object The object to query.
   * @returns {Array} Returns the array of property names.
   * @example
   *
   * function Foo() {
   *   this.a = 1;
   *   this.b = 2;
   * }
   *
   * Foo.prototype.c = 3;
   *
   * _.keys(new Foo);
   * // => ['a', 'b'] (iteration order is not guaranteed)
   *
   * _.keys('hi');
   * // => ['0', '1']
   */


  function keys(object) {
    return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
  }
  /**
   * Creates an array of the own enumerable string keyed property values of `object`.
   *
   * **Note:** Non-object values are coerced to objects.
   *
   * @static
   * @since 0.1.0
   * @memberOf _
   * @category Object
   * @param {Object} object The object to query.
   * @returns {Array} Returns the array of property values.
   * @example
   *
   * function Foo() {
   *   this.a = 1;
   *   this.b = 2;
   * }
   *
   * Foo.prototype.c = 3;
   *
   * _.values(new Foo);
   * // => [1, 2] (iteration order is not guaranteed)
   *
   * _.values('hi');
   * // => ['h', 'i']
   */


  function values(object) {
    return object ? baseValues(object, keys(object)) : [];
  }

  var lodash_toarray = toArray;

  const umbrella_with_rain_drops="☔";const coffee="☕";const aries="♈";const taurus="♉";const sagittarius="♐";const capricorn="♑";const aquarius="♒";const pisces="♓";const anchor="⚓";const white_check_mark="✅";const sparkles="✨";const question="❓";const grey_question="❔";const grey_exclamation="❕";const exclamation="❗";const heavy_exclamation_mark="❗";const heavy_plus_sign="➕";const heavy_minus_sign="➖";const heavy_division_sign="➗";const hash="#️⃣";const keycap_star="*️⃣";const zero="0️⃣";const one="1️⃣";const two="2️⃣";const three="3️⃣";const four="4️⃣";const five="5️⃣";const six="6️⃣";const seven="7️⃣";const eight="8️⃣";const nine="9️⃣";const copyright="©️";const registered="®️";const mahjong="🀄";const black_joker="🃏";const a="🅰️";const b="🅱️";const o2="🅾️";const parking="🅿️";const ab="🆎";const cl="🆑";const cool="🆒";const free="🆓";const id="🆔";const ng="🆖";const ok="🆗";const sos="🆘";const up="🆙";const vs="🆚";const koko="🈁";const sa="🈂️";const u7121="🈚";const u6307="🈯";const u7981="🈲";const u7a7a="🈳";const u5408="🈴";const u6e80="🈵";const u6709="🈶";const u6708="🈷️";const u7533="🈸";const u5272="🈹";const u55b6="🈺";const ideograph_advantage="🉐";const accept="🉑";const cyclone="🌀";const foggy="🌁";const closed_umbrella="🌂";const night_with_stars="🌃";const sunrise_over_mountains="🌄";const sunrise="🌅";const city_sunset="🌆";const city_sunrise="🌇";const rainbow="🌈";const bridge_at_night="🌉";const ocean="🌊";const volcano="🌋";const milky_way="🌌";const earth_africa="🌍";const earth_americas="🌎";const earth_asia="🌏";const globe_with_meridians="🌐";const new_moon="🌑";const waxing_crescent_moon="🌒";const first_quarter_moon="🌓";const moon="🌔";const waxing_gibbous_moon="🌔";const full_moon="🌕";const waning_gibbous_moon="🌖";const last_quarter_moon="🌗";const waning_crescent_moon="🌘";const crescent_moon="🌙";const new_moon_with_face="🌚";const first_quarter_moon_with_face="🌛";const last_quarter_moon_with_face="🌜";const full_moon_with_face="🌝";const sun_with_face="🌞";const star2="🌟";const stars="🌠";const thermometer="🌡️";const mostly_sunny="🌤️";const sun_small_cloud="🌤️";const barely_sunny="🌥️";const sun_behind_cloud="🌥️";const partly_sunny_rain="🌦️";const sun_behind_rain_cloud="🌦️";const rain_cloud="🌧️";const snow_cloud="🌨️";const lightning="🌩️";const lightning_cloud="🌩️";const tornado="🌪️";const tornado_cloud="🌪️";const fog="🌫️";const wind_blowing_face="🌬️";const hotdog="🌭";const taco="🌮";const burrito="🌯";const chestnut="🌰";const seedling="🌱";const evergreen_tree="🌲";const deciduous_tree="🌳";const palm_tree="🌴";const cactus="🌵";const hot_pepper="🌶️";const tulip="🌷";const cherry_blossom="🌸";const rose="🌹";const hibiscus="🌺";const sunflower="🌻";const blossom="🌼";const corn="🌽";const ear_of_rice="🌾";const herb="🌿";const four_leaf_clover="🍀";const maple_leaf="🍁";const fallen_leaf="🍂";const leaves="🍃";const mushroom="🍄";const tomato="🍅";const eggplant="🍆";const grapes="🍇";const melon="🍈";const watermelon="🍉";const tangerine="🍊";const lemon="🍋";const banana="🍌";const pineapple="🍍";const apple="🍎";const green_apple="🍏";const pear="🍐";const peach="🍑";const cherries="🍒";const strawberry="🍓";const hamburger="🍔";const pizza="🍕";const meat_on_bone="🍖";const poultry_leg="🍗";const rice_cracker="🍘";const rice_ball="🍙";const rice="🍚";const curry="🍛";const ramen="🍜";const spaghetti="🍝";const bread="🍞";const fries="🍟";const sweet_potato="🍠";const dango="🍡";const oden="🍢";const sushi="🍣";const fried_shrimp="🍤";const fish_cake="🍥";const icecream="🍦";const shaved_ice="🍧";const ice_cream="🍨";const doughnut="🍩";const cookie="🍪";const chocolate_bar="🍫";const candy="🍬";const lollipop="🍭";const custard="🍮";const honey_pot="🍯";const cake="🍰";const bento="🍱";const stew="🍲";const fried_egg="🍳";const cooking="🍳";const fork_and_knife="🍴";const tea="🍵";const sake="🍶";const wine_glass="🍷";const cocktail="🍸";const tropical_drink="🍹";const beer="🍺";const beers="🍻";const baby_bottle="🍼";const knife_fork_plate="🍽️";const champagne="🍾";const popcorn="🍿";const ribbon="🎀";const gift="🎁";const birthday="🎂";const jack_o_lantern="🎃";const christmas_tree="🎄";const santa="🎅";const fireworks="🎆";const sparkler="🎇";const balloon="🎈";const tada="🎉";const confetti_ball="🎊";const tanabata_tree="🎋";const crossed_flags="🎌";const bamboo="🎍";const dolls="🎎";const flags="🎏";const wind_chime="🎐";const rice_scene="🎑";const school_satchel="🎒";const mortar_board="🎓";const medal="🎖️";const reminder_ribbon="🎗️";const studio_microphone="🎙️";const level_slider="🎚️";const control_knobs="🎛️";const film_frames="🎞️";const admission_tickets="🎟️";const carousel_horse="🎠";const ferris_wheel="🎡";const roller_coaster="🎢";const fishing_pole_and_fish="🎣";const microphone="🎤";const movie_camera="🎥";const cinema="🎦";const headphones="🎧";const art="🎨";const tophat="🎩";const circus_tent="🎪";const ticket="🎫";const clapper="🎬";const performing_arts="🎭";const video_game="🎮";const dart="🎯";const slot_machine="🎰";const game_die="🎲";const bowling="🎳";const flower_playing_cards="🎴";const musical_note="🎵";const notes="🎶";const saxophone="🎷";const guitar="🎸";const musical_keyboard="🎹";const trumpet="🎺";const violin="🎻";const musical_score="🎼";const running_shirt_with_sash="🎽";const tennis="🎾";const ski="🎿";const basketball="🏀";const checkered_flag="🏁";const snowboarder="🏂";const runner="🏃‍♂️";const running="🏃‍♂️";const surfer="🏄‍♂️";const sports_medal="🏅";const trophy="🏆";const horse_racing="🏇";const football="🏈";const rugby_football="🏉";const swimmer="🏊‍♂️";const weight_lifter="🏋️‍♂️";const golfer="🏌️‍♂️";const racing_motorcycle="🏍️";const racing_car="🏎️";const cricket_bat_and_ball="🏏";const volleyball="🏐";const field_hockey_stick_and_ball="🏑";const ice_hockey_stick_and_puck="🏒";const table_tennis_paddle_and_ball="🏓";const snow_capped_mountain="🏔️";const camping="🏕️";const beach_with_umbrella="🏖️";const building_construction="🏗️";const house_buildings="🏘️";const cityscape="🏙️";const derelict_house_building="🏚️";const classical_building="🏛️";const desert="🏜️";const desert_island="🏝️";const national_park="🏞️";const stadium="🏟️";const house="🏠";const house_with_garden="🏡";const office="🏢";const post_office="🏣";const european_post_office="🏤";const hospital="🏥";const bank="🏦";const atm="🏧";const hotel="🏨";const love_hotel="🏩";const convenience_store="🏪";const school="🏫";const department_store="🏬";const factory="🏭";const izakaya_lantern="🏮";const lantern="🏮";const japanese_castle="🏯";const european_castle="🏰";const waving_white_flag="🏳️";const waving_black_flag="🏴";const rosette="🏵️";const label="🏷️";const badminton_racquet_and_shuttlecock="🏸";const bow_and_arrow="🏹";const amphora="🏺";const rat="🐀";const mouse2="🐁";const ox="🐂";const water_buffalo="🐃";const cow2="🐄";const tiger2="🐅";const leopard="🐆";const rabbit2="🐇";const cat2="🐈";const dragon="🐉";const crocodile="🐊";const whale2="🐋";const snail="🐌";const snake="🐍";const racehorse="🐎";const ram="🐏";const goat="🐐";const sheep="🐑";const monkey="🐒";const rooster="🐓";const chicken="🐔";const dog2="🐕";const pig2="🐖";const boar="🐗";const elephant="🐘";const octopus="🐙";const shell="🐚";const bug="🐛";const ant="🐜";const bee="🐝";const honeybee="🐝";const beetle="🐞";const fish="🐟";const tropical_fish="🐠";const blowfish="🐡";const turtle="🐢";const hatching_chick="🐣";const baby_chick="🐤";const hatched_chick="🐥";const bird="🐦";const penguin="🐧";const koala="🐨";const poodle="🐩";const dromedary_camel="🐪";const camel="🐫";const dolphin="🐬";const flipper="🐬";const mouse="🐭";const cow="🐮";const tiger="🐯";const rabbit="🐰";const cat="🐱";const dragon_face="🐲";const whale="🐳";const horse="🐴";const monkey_face="🐵";const dog="🐶";const pig="🐷";const frog="🐸";const hamster="🐹";const wolf="🐺";const bear="🐻";const panda_face="🐼";const pig_nose="🐽";const feet="🐾";const paw_prints="🐾";const chipmunk="🐿️";const eyes="👀";const eye="👁️";const ear="👂";const nose="👃";const lips="👄";const tongue="👅";const point_up_2="👆";const point_down="👇";const point_left="👈";const point_right="👉";const facepunch="👊";const punch="👊";const wave="👋";const ok_hand="👌";const thumbsup="👍";const thumbsdown="👎";const clap="👏";const open_hands="👐";const crown="👑";const womans_hat="👒";const eyeglasses="👓";const necktie="👔";const shirt="👕";const tshirt="👕";const jeans="👖";const dress="👗";const kimono="👘";const bikini="👙";const womans_clothes="👚";const purse="👛";const handbag="👜";const pouch="👝";const mans_shoe="👞";const shoe="👞";const athletic_shoe="👟";const high_heel="👠";const sandal="👡";const boot="👢";const footprints="👣";const bust_in_silhouette="👤";const busts_in_silhouette="👥";const boy="👦";const girl="👧";const family="👨‍👩‍👦";const man="👨";const couple_with_heart="👩‍❤️‍👨";const couplekiss="👩‍❤️‍💋‍👨";const woman="👩";const couple="👫";const man_and_woman_holding_hands="👫";const two_men_holding_hands="👬";const two_women_holding_hands="👭";const cop="👮‍♂️";const dancers="👯‍♀️";const bride_with_veil="👰";const person_with_blond_hair="👱‍♂️";const man_with_gua_pi_mao="👲";const man_with_turban="👳‍♂️";const older_man="👴";const older_woman="👵";const baby="👶";const construction_worker="👷‍♂️";const princess="👸";const japanese_ogre="👹";const japanese_goblin="👺";const ghost="👻";const angel="👼";const alien="👽";const space_invader="👾";const imp="👿";const skull="💀";const information_desk_person="💁‍♀️";const guardsman="💂‍♂️";const dancer="💃";const lipstick="💄";const nail_care="💅";const massage="💆‍♀️";const haircut="💇‍♀️";const barber="💈";const syringe="💉";const pill="💊";const kiss="💋";const love_letter="💌";const ring="💍";const gem="💎";const bouquet="💐";const wedding="💒";const heartbeat="💓";const broken_heart="💔";const two_hearts="💕";const sparkling_heart="💖";const heartpulse="💗";const cupid="💘";const blue_heart="💙";const green_heart="💚";const yellow_heart="💛";const purple_heart="💜";const gift_heart="💝";const revolving_hearts="💞";const heart_decoration="💟";const diamond_shape_with_a_dot_inside="💠";const bulb="💡";const anger="💢";const bomb="💣";const zzz="💤";const boom="💥";const collision="💥";const sweat_drops="💦";const droplet="💧";const dash="💨";const poop="💩";const muscle="💪";const dizzy="💫";const speech_balloon="💬";const thought_balloon="💭";const white_flower="💮";const moneybag="💰";const currency_exchange="💱";const heavy_dollar_sign="💲";const credit_card="💳";const yen="💴";const dollar="💵";const euro="💶";const pound="💷";const money_with_wings="💸";const chart="💹";const seat="💺";const computer="💻";const briefcase="💼";const minidisc="💽";const floppy_disk="💾";const cd="💿";const dvd="📀";const file_folder="📁";const open_file_folder="📂";const page_with_curl="📃";const page_facing_up="📄";const date="📅";const calendar="📆";const card_index="📇";const chart_with_upwards_trend="📈";const chart_with_downwards_trend="📉";const bar_chart="📊";const clipboard="📋";const pushpin="📌";const round_pushpin="📍";const paperclip="📎";const straight_ruler="📏";const triangular_ruler="📐";const bookmark_tabs="📑";const ledger="📒";const notebook="📓";const notebook_with_decorative_cover="📔";const closed_book="📕";const book="📖";const open_book="📖";const green_book="📗";const blue_book="📘";const orange_book="📙";const books="📚";const name_badge="📛";const scroll="📜";const memo="📝";const pencil="📝";const telephone_receiver="📞";const pager="📟";const fax="📠";const satellite_antenna="📡";const loudspeaker="📢";const mega="📣";const outbox_tray="📤";const inbox_tray="📥";const incoming_envelope="📨";const envelope_with_arrow="📩";const mailbox_closed="📪";const mailbox="📫";const mailbox_with_mail="📬";const mailbox_with_no_mail="📭";const postbox="📮";const postal_horn="📯";const newspaper="📰";const iphone="📱";const calling="📲";const vibration_mode="📳";const mobile_phone_off="📴";const no_mobile_phones="📵";const signal_strength="📶";const camera="📷";const camera_with_flash="📸";const video_camera="📹";const tv="📺";const radio="📻";const vhs="📼";const film_projector="📽️";const prayer_beads="📿";const twisted_rightwards_arrows="🔀";const repeat="🔁";const repeat_one="🔂";const arrows_clockwise="🔃";const arrows_counterclockwise="🔄";const low_brightness="🔅";const high_brightness="🔆";const mute="🔇";const speaker="🔈";const sound="🔉";const loud_sound="🔊";const battery="🔋";const electric_plug="🔌";const mag="🔍";const mag_right="🔎";const lock_with_ink_pen="🔏";const closed_lock_with_key="🔐";const key="🔑";const lock="🔒";const unlock="🔓";const bell="🔔";const no_bell="🔕";const bookmark="🔖";const link="🔗";const radio_button="🔘";const back="🔙";const end="🔚";const on="🔛";const soon="🔜";const underage="🔞";const keycap_ten="🔟";const capital_abcd="🔠";const abcd="🔡";const symbols="🔣";const abc="🔤";const fire="🔥";const flashlight="🔦";const wrench="🔧";const hammer="🔨";const nut_and_bolt="🔩";const hocho="🔪";const knife="🔪";const gun="🔫";const microscope="🔬";const telescope="🔭";const crystal_ball="🔮";const six_pointed_star="🔯";const beginner="🔰";const trident="🔱";const black_square_button="🔲";const white_square_button="🔳";const red_circle="🔴";const large_blue_circle="🔵";const large_orange_diamond="🔶";const large_blue_diamond="🔷";const small_orange_diamond="🔸";const small_blue_diamond="🔹";const small_red_triangle="🔺";const small_red_triangle_down="🔻";const arrow_up_small="🔼";const arrow_down_small="🔽";const om_symbol="🕉️";const dove_of_peace="🕊️";const kaaba="🕋";const mosque="🕌";const synagogue="🕍";const menorah_with_nine_branches="🕎";const candle="🕯️";const mantelpiece_clock="🕰️";const hole="🕳️";const man_in_business_suit_levitating="🕴️";const sleuth_or_spy="🕵️‍♂️";const dark_sunglasses="🕶️";const spider="🕷️";const spider_web="🕸️";const joystick="🕹️";const man_dancing="🕺";const linked_paperclips="🖇️";const lower_left_ballpoint_pen="🖊️";const lower_left_fountain_pen="🖋️";const lower_left_paintbrush="🖌️";const lower_left_crayon="🖍️";const raised_hand_with_fingers_splayed="🖐️";const middle_finger="🖕";const reversed_hand_with_middle_finger_extended="🖕";const black_heart="🖤";const desktop_computer="🖥️";const printer="🖨️";const three_button_mouse="🖱️";const trackball="🖲️";const frame_with_picture="🖼️";const card_index_dividers="🗂️";const card_file_box="🗃️";const file_cabinet="🗄️";const wastebasket="🗑️";const spiral_note_pad="🗒️";const spiral_calendar_pad="🗓️";const compression="🗜️";const old_key="🗝️";const rolled_up_newspaper="🗞️";const dagger_knife="🗡️";const speaking_head_in_silhouette="🗣️";const left_speech_bubble="🗨️";const right_anger_bubble="🗯️";const ballot_box_with_ballot="🗳️";const world_map="🗺️";const mount_fuji="🗻";const tokyo_tower="🗼";const statue_of_liberty="🗽";const japan="🗾";const moyai="🗿";const grinning="😀";const grin="😁";const joy="😂";const smiley="😃";const smile="😄";const sweat_smile="😅";const laughing="😆";const satisfied="😆";const innocent="😇";const smiling_imp="😈";const wink="😉";const blush="😊";const yum="😋";const relieved="😌";const heart_eyes="😍";const sunglasses="😎";const smirk="😏";const neutral_face="😐";const expressionless="😑";const unamused="😒";const sweat="😓";const pensive="😔";const confused="😕";const confounded="😖";const kissing="😗";const kissing_heart="😘";const kissing_smiling_eyes="😙";const kissing_closed_eyes="😚";const stuck_out_tongue="😛";const stuck_out_tongue_winking_eye="😜";const stuck_out_tongue_closed_eyes="😝";const disappointed="😞";const worried="😟";const angry="😠";const rage="😡";const cry="😢";const persevere="😣";const triumph="😤";const disappointed_relieved="😥";const frowning="😦";const anguished="😧";const fearful="😨";const weary="😩";const sleepy="😪";const tired_face="😫";const grimacing="😬";const sob="😭";const open_mouth="😮";const hushed="😯";const cold_sweat="😰";const scream="😱";const astonished="😲";const flushed="😳";const sleeping="😴";const dizzy_face="😵";const no_mouth="😶";const mask="😷";const smile_cat="😸";const joy_cat="😹";const smiley_cat="😺";const heart_eyes_cat="😻";const smirk_cat="😼";const kissing_cat="😽";const pouting_cat="😾";const crying_cat_face="😿";const scream_cat="🙀";const slightly_frowning_face="🙁";const slightly_smiling_face="🙂";const upside_down_face="🙃";const face_with_rolling_eyes="🙄";const no_good="🙅‍♀️";const ok_woman="🙆‍♀️";const bow="🙇‍♂️";const see_no_evil="🙈";const hear_no_evil="🙉";const speak_no_evil="🙊";const raising_hand="🙋‍♀️";const raised_hands="🙌";const person_frowning="🙍‍♀️";const person_with_pouting_face="🙎‍♀️";const pray="🙏";const rocket="🚀";const helicopter="🚁";const steam_locomotive="🚂";const railway_car="🚃";const bullettrain_side="🚄";const bullettrain_front="🚅";const train2="🚆";const metro="🚇";const light_rail="🚈";const station="🚉";const tram="🚊";const train="🚋";const bus="🚌";const oncoming_bus="🚍";const trolleybus="🚎";const busstop="🚏";const minibus="🚐";const ambulance="🚑";const fire_engine="🚒";const police_car="🚓";const oncoming_police_car="🚔";const taxi="🚕";const oncoming_taxi="🚖";const car="🚗";const red_car="🚗";const oncoming_automobile="🚘";const blue_car="🚙";const truck="🚚";const articulated_lorry="🚛";const tractor="🚜";const monorail="🚝";const mountain_railway="🚞";const suspension_railway="🚟";const mountain_cableway="🚠";const aerial_tramway="🚡";const ship="🚢";const rowboat="🚣‍♂️";const speedboat="🚤";const traffic_light="🚥";const vertical_traffic_light="🚦";const construction="🚧";const rotating_light="🚨";const triangular_flag_on_post="🚩";const door="🚪";const no_entry_sign="🚫";const smoking="🚬";const no_smoking="🚭";const put_litter_in_its_place="🚮";const do_not_litter="🚯";const potable_water="🚰";const bike="🚲";const no_bicycles="🚳";const bicyclist="🚴‍♂️";const mountain_bicyclist="🚵‍♂️";const walking="🚶‍♂️";const no_pedestrians="🚷";const children_crossing="🚸";const mens="🚹";const womens="🚺";const restroom="🚻";const baby_symbol="🚼";const toilet="🚽";const wc="🚾";const shower="🚿";const bath="🛀";const bathtub="🛁";const passport_control="🛂";const customs="🛃";const baggage_claim="🛄";const left_luggage="🛅";const couch_and_lamp="🛋️";const sleeping_accommodation="🛌";const shopping_bags="🛍️";const bellhop_bell="🛎️";const bed="🛏️";const place_of_worship="🛐";const octagonal_sign="🛑";const shopping_trolley="🛒";const hammer_and_wrench="🛠️";const shield="🛡️";const oil_drum="🛢️";const motorway="🛣️";const railway_track="🛤️";const motor_boat="🛥️";const small_airplane="🛩️";const airplane_departure="🛫";const airplane_arriving="🛬";const satellite="🛰️";const passenger_ship="🛳️";const scooter="🛴";const motor_scooter="🛵";const canoe="🛶";const sled="🛷";const flying_saucer="🛸";const zipper_mouth_face="🤐";const money_mouth_face="🤑";const face_with_thermometer="🤒";const nerd_face="🤓";const thinking_face="🤔";const face_with_head_bandage="🤕";const robot_face="🤖";const hugging_face="🤗";const the_horns="🤘";const sign_of_the_horns="🤘";const call_me_hand="🤙";const raised_back_of_hand="🤚";const handshake="🤝";const crossed_fingers="🤞";const hand_with_index_and_middle_fingers_crossed="🤞";const i_love_you_hand_sign="🤟";const face_with_cowboy_hat="🤠";const clown_face="🤡";const nauseated_face="🤢";const rolling_on_the_floor_laughing="🤣";const drooling_face="🤤";const lying_face="🤥";const face_palm="🤦";const sneezing_face="🤧";const face_with_raised_eyebrow="🤨";const face_with_one_eyebrow_raised="🤨";const grinning_face_with_star_eyes="🤩";const zany_face="🤪";const grinning_face_with_one_large_and_one_small_eye="🤪";const shushing_face="🤫";const face_with_finger_covering_closed_lips="🤫";const face_with_symbols_on_mouth="🤬";const serious_face_with_symbols_covering_mouth="🤬";const face_with_hand_over_mouth="🤭";const smiling_face_with_smiling_eyes_and_hand_covering_mouth="🤭";const face_vomiting="🤮";const face_with_open_mouth_vomiting="🤮";const exploding_head="🤯";const shocked_face_with_exploding_head="🤯";const pregnant_woman="🤰";const palms_up_together="🤲";const selfie="🤳";const prince="🤴";const man_in_tuxedo="🤵";const mrs_claus="🤶";const mother_christmas="🤶";const shrug="🤷";const person_doing_cartwheel="🤸";const juggling="🤹";const fencer="🤺";const wrestlers="🤼";const water_polo="🤽";const handball="🤾";const wilted_flower="🥀";const drum_with_drumsticks="🥁";const clinking_glasses="🥂";const tumbler_glass="🥃";const spoon="🥄";const goal_net="🥅";const first_place_medal="🥇";const second_place_medal="🥈";const third_place_medal="🥉";const boxing_glove="🥊";const martial_arts_uniform="🥋";const curling_stone="🥌";const croissant="🥐";const avocado="🥑";const cucumber="🥒";const bacon="🥓";const potato="🥔";const carrot="🥕";const baguette_bread="🥖";const green_salad="🥗";const shallow_pan_of_food="🥘";const stuffed_flatbread="🥙";const egg="🥚";const glass_of_milk="🥛";const peanuts="🥜";const kiwifruit="🥝";const pancakes="🥞";const dumpling="🥟";const fortune_cookie="🥠";const takeout_box="🥡";const chopsticks="🥢";const bowl_with_spoon="🥣";const cup_with_straw="🥤";const coconut="🥥";const broccoli="🥦";const pie="🥧";const pretzel="🥨";const cut_of_meat="🥩";const sandwich="🥪";const canned_food="🥫";const crab="🦀";const lion_face="🦁";const scorpion="🦂";const turkey="🦃";const unicorn_face="🦄";const eagle="🦅";const duck="🦆";const bat="🦇";const shark="🦈";const owl="🦉";const fox_face="🦊";const butterfly="🦋";const deer="🦌";const gorilla="🦍";const lizard="🦎";const rhinoceros="🦏";const shrimp="🦐";const squid="🦑";const giraffe_face="🦒";const zebra_face="🦓";const hedgehog="🦔";const sauropod="🦕";const cricket="🦗";const cheese_wedge="🧀";const face_with_monocle="🧐";const adult="🧑";const child="🧒";const older_adult="🧓";const bearded_person="🧔";const person_with_headscarf="🧕";const woman_in_steamy_room="🧖‍♀️";const man_in_steamy_room="🧖‍♂️";const person_in_steamy_room="🧖‍♂️";const woman_climbing="🧗‍♀️";const person_climbing="🧗‍♀️";const man_climbing="🧗‍♂️";const woman_in_lotus_position="🧘‍♀️";const person_in_lotus_position="🧘‍♀️";const man_in_lotus_position="🧘‍♂️";const female_mage="🧙‍♀️";const mage="🧙‍♀️";const male_mage="🧙‍♂️";const female_fairy="🧚‍♀️";const fairy="🧚‍♀️";const male_fairy="🧚‍♂️";const female_vampire="🧛‍♀️";const vampire="🧛‍♀️";const male_vampire="🧛‍♂️";const mermaid="🧜‍♀️";const merman="🧜‍♂️";const merperson="🧜‍♂️";const female_elf="🧝‍♀️";const male_elf="🧝‍♂️";const elf="🧝‍♂️";const female_genie="🧞‍♀️";const male_genie="🧞‍♂️";const genie="🧞‍♂️";const female_zombie="🧟‍♀️";const male_zombie="🧟‍♂️";const zombie="🧟‍♂️";const brain="🧠";const orange_heart="🧡";const billed_cap="🧢";const scarf="🧣";const gloves="🧤";const coat="🧥";const socks="🧦";const bangbang="‼️";const interrobang="⁉️";const tm="™️";const information_source="ℹ️";const left_right_arrow="↔️";const arrow_up_down="↕️";const arrow_upper_left="↖️";const arrow_upper_right="↗️";const arrow_lower_right="↘️";const arrow_lower_left="↙️";const leftwards_arrow_with_hook="↩️";const arrow_right_hook="↪️";const watch="⌚";const hourglass="⌛";const keyboard="⌨️";const eject="⏏️";const fast_forward="⏩";const rewind="⏪";const arrow_double_up="⏫";const arrow_double_down="⏬";const black_right_pointing_double_triangle_with_vertical_bar="⏭️";const black_left_pointing_double_triangle_with_vertical_bar="⏮️";const black_right_pointing_triangle_with_double_vertical_bar="⏯️";const alarm_clock="⏰";const stopwatch="⏱️";const timer_clock="⏲️";const hourglass_flowing_sand="⏳";const double_vertical_bar="⏸️";const black_square_for_stop="⏹️";const black_circle_for_record="⏺️";const m="Ⓜ️";const black_small_square="▪️";const white_small_square="▫️";const arrow_forward="▶️";const arrow_backward="◀️";const white_medium_square="◻️";const black_medium_square="◼️";const white_medium_small_square="◽";const black_medium_small_square="◾";const sunny="☀️";const cloud="☁️";const umbrella="☂️";const snowman="☃️";const comet="☄️";const phone="☎️";const telephone="☎️";const ballot_box_with_check="☑️";const shamrock="☘️";const point_up="☝️";const skull_and_crossbones="☠️";const radioactive_sign="☢️";const biohazard_sign="☣️";const orthodox_cross="☦️";const star_and_crescent="☪️";const peace_symbol="☮️";const yin_yang="☯️";const wheel_of_dharma="☸️";const white_frowning_face="☹️";const relaxed="☺️";const female_sign="♀️";const male_sign="♂️";const gemini="♊";const cancer="♋";const leo="♌";const virgo="♍";const libra="♎";const scorpius="♏";const spades="♠️";const clubs="♣️";const hearts="♥️";const diamonds="♦️";const hotsprings="♨️";const recycle="♻️";const wheelchair="♿";const hammer_and_pick="⚒️";const crossed_swords="⚔️";const medical_symbol="⚕️";const staff_of_aesculapius="⚕️";const scales="⚖️";const alembic="⚗️";const gear="⚙️";const atom_symbol="⚛️";const fleur_de_lis="⚜️";const warning="⚠️";const zap="⚡";const white_circle="⚪";const black_circle="⚫";const coffin="⚰️";const funeral_urn="⚱️";const soccer="⚽";const baseball="⚾";const snowman_without_snow="⛄";const partly_sunny="⛅";const thunder_cloud_and_rain="⛈️";const ophiuchus="⛎";const pick="⛏️";const helmet_with_white_cross="⛑️";const chains="⛓️";const no_entry="⛔";const shinto_shrine="⛩️";const church="⛪";const mountain="⛰️";const umbrella_on_ground="⛱️";const fountain="⛲";const golf="⛳";const ferry="⛴️";const boat="⛵";const sailboat="⛵";const skier="⛷️";const ice_skate="⛸️";const person_with_ball="⛹️‍♂️";const tent="⛺";const fuelpump="⛽";const scissors="✂️";const airplane="✈️";const email="✉️";const envelope="✉️";const fist="✊";const hand="✋";const raised_hand="✋";const v="✌️";const writing_hand="✍️";const pencil2="✏️";const black_nib="✒️";const heavy_check_mark="✔️";const heavy_multiplication_x="✖️";const latin_cross="✝️";const star_of_david="✡️";const eight_spoked_asterisk="✳️";const eight_pointed_black_star="✴️";const snowflake="❄️";const sparkle="❇️";const x="❌";const negative_squared_cross_mark="❎";const heavy_heart_exclamation_mark_ornament="❣️";const heart="❤️";const arrow_right="➡️";const curly_loop="➰";const loop="➿";const arrow_heading_up="⤴️";const arrow_heading_down="⤵️";const arrow_left="⬅️";const arrow_up="⬆️";const arrow_down="⬇️";const black_large_square="⬛";const white_large_square="⬜";const star="⭐";const o="⭕";const wavy_dash="〰️";const part_alternation_mark="〽️";const congratulations="㊗️";const secret="㊙️";var emojiByName = {"100":"💯","1234":"🔢",umbrella_with_rain_drops:umbrella_with_rain_drops,coffee:coffee,aries:aries,taurus:taurus,sagittarius:sagittarius,capricorn:capricorn,aquarius:aquarius,pisces:pisces,anchor:anchor,white_check_mark:white_check_mark,sparkles:sparkles,question:question,grey_question:grey_question,grey_exclamation:grey_exclamation,exclamation:exclamation,heavy_exclamation_mark:heavy_exclamation_mark,heavy_plus_sign:heavy_plus_sign,heavy_minus_sign:heavy_minus_sign,heavy_division_sign:heavy_division_sign,hash:hash,keycap_star:keycap_star,zero:zero,one:one,two:two,three:three,four:four,five:five,six:six,seven:seven,eight:eight,nine:nine,copyright:copyright,registered:registered,mahjong:mahjong,black_joker:black_joker,a:a,b:b,o2:o2,parking:parking,ab:ab,cl:cl,cool:cool,free:free,id:id,"new":"🆕",ng:ng,ok:ok,sos:sos,up:up,vs:vs,koko:koko,sa:sa,u7121:u7121,u6307:u6307,u7981:u7981,u7a7a:u7a7a,u5408:u5408,u6e80:u6e80,u6709:u6709,u6708:u6708,u7533:u7533,u5272:u5272,u55b6:u55b6,ideograph_advantage:ideograph_advantage,accept:accept,cyclone:cyclone,foggy:foggy,closed_umbrella:closed_umbrella,night_with_stars:night_with_stars,sunrise_over_mountains:sunrise_over_mountains,sunrise:sunrise,city_sunset:city_sunset,city_sunrise:city_sunrise,rainbow:rainbow,bridge_at_night:bridge_at_night,ocean:ocean,volcano:volcano,milky_way:milky_way,earth_africa:earth_africa,earth_americas:earth_americas,earth_asia:earth_asia,globe_with_meridians:globe_with_meridians,new_moon:new_moon,waxing_crescent_moon:waxing_crescent_moon,first_quarter_moon:first_quarter_moon,moon:moon,waxing_gibbous_moon:waxing_gibbous_moon,full_moon:full_moon,waning_gibbous_moon:waning_gibbous_moon,last_quarter_moon:last_quarter_moon,waning_crescent_moon:waning_crescent_moon,crescent_moon:crescent_moon,new_moon_with_face:new_moon_with_face,first_quarter_moon_with_face:first_quarter_moon_with_face,last_quarter_moon_with_face:last_quarter_moon_with_face,full_moon_with_face:full_moon_with_face,sun_with_face:sun_with_face,star2:star2,stars:stars,thermometer:thermometer,mostly_sunny:mostly_sunny,sun_small_cloud:sun_small_cloud,barely_sunny:barely_sunny,sun_behind_cloud:sun_behind_cloud,partly_sunny_rain:partly_sunny_rain,sun_behind_rain_cloud:sun_behind_rain_cloud,rain_cloud:rain_cloud,snow_cloud:snow_cloud,lightning:lightning,lightning_cloud:lightning_cloud,tornado:tornado,tornado_cloud:tornado_cloud,fog:fog,wind_blowing_face:wind_blowing_face,hotdog:hotdog,taco:taco,burrito:burrito,chestnut:chestnut,seedling:seedling,evergreen_tree:evergreen_tree,deciduous_tree:deciduous_tree,palm_tree:palm_tree,cactus:cactus,hot_pepper:hot_pepper,tulip:tulip,cherry_blossom:cherry_blossom,rose:rose,hibiscus:hibiscus,sunflower:sunflower,blossom:blossom,corn:corn,ear_of_rice:ear_of_rice,herb:herb,four_leaf_clover:four_leaf_clover,maple_leaf:maple_leaf,fallen_leaf:fallen_leaf,leaves:leaves,mushroom:mushroom,tomato:tomato,eggplant:eggplant,grapes:grapes,melon:melon,watermelon:watermelon,tangerine:tangerine,lemon:lemon,banana:banana,pineapple:pineapple,apple:apple,green_apple:green_apple,pear:pear,peach:peach,cherries:cherries,strawberry:strawberry,hamburger:hamburger,pizza:pizza,meat_on_bone:meat_on_bone,poultry_leg:poultry_leg,rice_cracker:rice_cracker,rice_ball:rice_ball,rice:rice,curry:curry,ramen:ramen,spaghetti:spaghetti,bread:bread,fries:fries,sweet_potato:sweet_potato,dango:dango,oden:oden,sushi:sushi,fried_shrimp:fried_shrimp,fish_cake:fish_cake,icecream:icecream,shaved_ice:shaved_ice,ice_cream:ice_cream,doughnut:doughnut,cookie:cookie,chocolate_bar:chocolate_bar,candy:candy,lollipop:lollipop,custard:custard,honey_pot:honey_pot,cake:cake,bento:bento,stew:stew,fried_egg:fried_egg,cooking:cooking,fork_and_knife:fork_and_knife,tea:tea,sake:sake,wine_glass:wine_glass,cocktail:cocktail,tropical_drink:tropical_drink,beer:beer,beers:beers,baby_bottle:baby_bottle,knife_fork_plate:knife_fork_plate,champagne:champagne,popcorn:popcorn,ribbon:ribbon,gift:gift,birthday:birthday,jack_o_lantern:jack_o_lantern,christmas_tree:christmas_tree,santa:santa,fireworks:fireworks,sparkler:sparkler,balloon:balloon,tada:tada,confetti_ball:confetti_ball,tanabata_tree:tanabata_tree,crossed_flags:crossed_flags,bamboo:bamboo,dolls:dolls,flags:flags,wind_chime:wind_chime,rice_scene:rice_scene,school_satchel:school_satchel,mortar_board:mortar_board,medal:medal,reminder_ribbon:reminder_ribbon,studio_microphone:studio_microphone,level_slider:level_slider,control_knobs:control_knobs,film_frames:film_frames,admission_tickets:admission_tickets,carousel_horse:carousel_horse,ferris_wheel:ferris_wheel,roller_coaster:roller_coaster,fishing_pole_and_fish:fishing_pole_and_fish,microphone:microphone,movie_camera:movie_camera,cinema:cinema,headphones:headphones,art:art,tophat:tophat,circus_tent:circus_tent,ticket:ticket,clapper:clapper,performing_arts:performing_arts,video_game:video_game,dart:dart,slot_machine:slot_machine,"8ball":"🎱",game_die:game_die,bowling:bowling,flower_playing_cards:flower_playing_cards,musical_note:musical_note,notes:notes,saxophone:saxophone,guitar:guitar,musical_keyboard:musical_keyboard,trumpet:trumpet,violin:violin,musical_score:musical_score,running_shirt_with_sash:running_shirt_with_sash,tennis:tennis,ski:ski,basketball:basketball,checkered_flag:checkered_flag,snowboarder:snowboarder,"woman-running":"🏃‍♀️","man-running":"🏃‍♂️",runner:runner,running:running,"woman-surfing":"🏄‍♀️","man-surfing":"🏄‍♂️",surfer:surfer,sports_medal:sports_medal,trophy:trophy,horse_racing:horse_racing,football:football,rugby_football:rugby_football,"woman-swimming":"🏊‍♀️","man-swimming":"🏊‍♂️",swimmer:swimmer,"woman-lifting-weights":"🏋️‍♀️","man-lifting-weights":"🏋️‍♂️",weight_lifter:weight_lifter,"woman-golfing":"🏌️‍♀️","man-golfing":"🏌️‍♂️",golfer:golfer,racing_motorcycle:racing_motorcycle,racing_car:racing_car,cricket_bat_and_ball:cricket_bat_and_ball,volleyball:volleyball,field_hockey_stick_and_ball:field_hockey_stick_and_ball,ice_hockey_stick_and_puck:ice_hockey_stick_and_puck,table_tennis_paddle_and_ball:table_tennis_paddle_and_ball,snow_capped_mountain:snow_capped_mountain,camping:camping,beach_with_umbrella:beach_with_umbrella,building_construction:building_construction,house_buildings:house_buildings,cityscape:cityscape,derelict_house_building:derelict_house_building,classical_building:classical_building,desert:desert,desert_island:desert_island,national_park:national_park,stadium:stadium,house:house,house_with_garden:house_with_garden,office:office,post_office:post_office,european_post_office:european_post_office,hospital:hospital,bank:bank,atm:atm,hotel:hotel,love_hotel:love_hotel,convenience_store:convenience_store,school:school,department_store:department_store,factory:factory,izakaya_lantern:izakaya_lantern,lantern:lantern,japanese_castle:japanese_castle,european_castle:european_castle,"rainbow-flag":"🏳️‍🌈",waving_white_flag:waving_white_flag,"flag-england":"🏴󠁧󠁢󠁥󠁮󠁧󠁿","flag-scotland":"🏴󠁧󠁢󠁳󠁣󠁴󠁿","flag-wales":"🏴󠁧󠁢󠁷󠁬󠁳󠁿",waving_black_flag:waving_black_flag,rosette:rosette,label:label,badminton_racquet_and_shuttlecock:badminton_racquet_and_shuttlecock,bow_and_arrow:bow_and_arrow,amphora:amphora,rat:rat,mouse2:mouse2,ox:ox,water_buffalo:water_buffalo,cow2:cow2,tiger2:tiger2,leopard:leopard,rabbit2:rabbit2,cat2:cat2,dragon:dragon,crocodile:crocodile,whale2:whale2,snail:snail,snake:snake,racehorse:racehorse,ram:ram,goat:goat,sheep:sheep,monkey:monkey,rooster:rooster,chicken:chicken,dog2:dog2,pig2:pig2,boar:boar,elephant:elephant,octopus:octopus,shell:shell,bug:bug,ant:ant,bee:bee,honeybee:honeybee,beetle:beetle,fish:fish,tropical_fish:tropical_fish,blowfish:blowfish,turtle:turtle,hatching_chick:hatching_chick,baby_chick:baby_chick,hatched_chick:hatched_chick,bird:bird,penguin:penguin,koala:koala,poodle:poodle,dromedary_camel:dromedary_camel,camel:camel,dolphin:dolphin,flipper:flipper,mouse:mouse,cow:cow,tiger:tiger,rabbit:rabbit,cat:cat,dragon_face:dragon_face,whale:whale,horse:horse,monkey_face:monkey_face,dog:dog,pig:pig,frog:frog,hamster:hamster,wolf:wolf,bear:bear,panda_face:panda_face,pig_nose:pig_nose,feet:feet,paw_prints:paw_prints,chipmunk:chipmunk,eyes:eyes,"eye-in-speech-bubble":"👁️‍🗨️",eye:eye,ear:ear,nose:nose,lips:lips,tongue:tongue,point_up_2:point_up_2,point_down:point_down,point_left:point_left,point_right:point_right,facepunch:facepunch,punch:punch,wave:wave,ok_hand:ok_hand,"+1":"👍",thumbsup:thumbsup,"-1":"👎",thumbsdown:thumbsdown,clap:clap,open_hands:open_hands,crown:crown,womans_hat:womans_hat,eyeglasses:eyeglasses,necktie:necktie,shirt:shirt,tshirt:tshirt,jeans:jeans,dress:dress,kimono:kimono,bikini:bikini,womans_clothes:womans_clothes,purse:purse,handbag:handbag,pouch:pouch,mans_shoe:mans_shoe,shoe:shoe,athletic_shoe:athletic_shoe,high_heel:high_heel,sandal:sandal,boot:boot,footprints:footprints,bust_in_silhouette:bust_in_silhouette,busts_in_silhouette:busts_in_silhouette,boy:boy,girl:girl,"male-farmer":"👨‍🌾","male-cook":"👨‍🍳","male-student":"👨‍🎓","male-singer":"👨‍🎤","male-artist":"👨‍🎨","male-teacher":"👨‍🏫","male-factory-worker":"👨‍🏭","man-boy-boy":"👨‍👦‍👦","man-boy":"👨‍👦","man-girl-boy":"👨‍👧‍👦","man-girl-girl":"👨‍👧‍👧","man-girl":"👨‍👧","man-man-boy":"👨‍👨‍👦","man-man-boy-boy":"👨‍👨‍👦‍👦","man-man-girl":"👨‍👨‍👧","man-man-girl-boy":"👨‍👨‍👧‍👦","man-man-girl-girl":"👨‍👨‍👧‍👧","man-woman-boy":"👨‍👩‍👦",family:family,"man-woman-boy-boy":"👨‍👩‍👦‍👦","man-woman-girl":"👨‍👩‍👧","man-woman-girl-boy":"👨‍👩‍👧‍👦","man-woman-girl-girl":"👨‍👩‍👧‍👧","male-technologist":"👨‍💻","male-office-worker":"👨‍💼","male-mechanic":"👨‍🔧","male-scientist":"👨‍🔬","male-astronaut":"👨‍🚀","male-firefighter":"👨‍🚒","male-doctor":"👨‍⚕️","male-judge":"👨‍⚖️","male-pilot":"👨‍✈️","man-heart-man":"👨‍❤️‍👨","man-kiss-man":"👨‍❤️‍💋‍👨",man:man,"female-farmer":"👩‍🌾","female-cook":"👩‍🍳","female-student":"👩‍🎓","female-singer":"👩‍🎤","female-artist":"👩‍🎨","female-teacher":"👩‍🏫","female-factory-worker":"👩‍🏭","woman-boy-boy":"👩‍👦‍👦","woman-boy":"👩‍👦","woman-girl-boy":"👩‍👧‍👦","woman-girl-girl":"👩‍👧‍👧","woman-girl":"👩‍👧","woman-woman-boy":"👩‍👩‍👦","woman-woman-boy-boy":"👩‍👩‍👦‍👦","woman-woman-girl":"👩‍👩‍👧","woman-woman-girl-boy":"👩‍👩‍👧‍👦","woman-woman-girl-girl":"👩‍👩‍👧‍👧","female-technologist":"👩‍💻","female-office-worker":"👩‍💼","female-mechanic":"👩‍🔧","female-scientist":"👩‍🔬","female-astronaut":"👩‍🚀","female-firefighter":"👩‍🚒","female-doctor":"👩‍⚕️","female-judge":"👩‍⚖️","female-pilot":"👩‍✈️","woman-heart-man":"👩‍❤️‍👨",couple_with_heart:couple_with_heart,"woman-heart-woman":"👩‍❤️‍👩","woman-kiss-man":"👩‍❤️‍💋‍👨",couplekiss:couplekiss,"woman-kiss-woman":"👩‍❤️‍💋‍👩",woman:woman,couple:couple,man_and_woman_holding_hands:man_and_woman_holding_hands,two_men_holding_hands:two_men_holding_hands,two_women_holding_hands:two_women_holding_hands,"female-police-officer":"👮‍♀️","male-police-officer":"👮‍♂️",cop:cop,"woman-with-bunny-ears-partying":"👯‍♀️",dancers:dancers,"man-with-bunny-ears-partying":"👯‍♂️",bride_with_veil:bride_with_veil,"blond-haired-woman":"👱‍♀️","blond-haired-man":"👱‍♂️",person_with_blond_hair:person_with_blond_hair,man_with_gua_pi_mao:man_with_gua_pi_mao,"woman-wearing-turban":"👳‍♀️","man-wearing-turban":"👳‍♂️",man_with_turban:man_with_turban,older_man:older_man,older_woman:older_woman,baby:baby,"female-construction-worker":"👷‍♀️","male-construction-worker":"👷‍♂️",construction_worker:construction_worker,princess:princess,japanese_ogre:japanese_ogre,japanese_goblin:japanese_goblin,ghost:ghost,angel:angel,alien:alien,space_invader:space_invader,imp:imp,skull:skull,"woman-tipping-hand":"💁‍♀️",information_desk_person:information_desk_person,"man-tipping-hand":"💁‍♂️","female-guard":"💂‍♀️","male-guard":"💂‍♂️",guardsman:guardsman,dancer:dancer,lipstick:lipstick,nail_care:nail_care,"woman-getting-massage":"💆‍♀️",massage:massage,"man-getting-massage":"💆‍♂️","woman-getting-haircut":"💇‍♀️",haircut:haircut,"man-getting-haircut":"💇‍♂️",barber:barber,syringe:syringe,pill:pill,kiss:kiss,love_letter:love_letter,ring:ring,gem:gem,bouquet:bouquet,wedding:wedding,heartbeat:heartbeat,broken_heart:broken_heart,two_hearts:two_hearts,sparkling_heart:sparkling_heart,heartpulse:heartpulse,cupid:cupid,blue_heart:blue_heart,green_heart:green_heart,yellow_heart:yellow_heart,purple_heart:purple_heart,gift_heart:gift_heart,revolving_hearts:revolving_hearts,heart_decoration:heart_decoration,diamond_shape_with_a_dot_inside:diamond_shape_with_a_dot_inside,bulb:bulb,anger:anger,bomb:bomb,zzz:zzz,boom:boom,collision:collision,sweat_drops:sweat_drops,droplet:droplet,dash:dash,poop:poop,muscle:muscle,dizzy:dizzy,speech_balloon:speech_balloon,thought_balloon:thought_balloon,white_flower:white_flower,moneybag:moneybag,currency_exchange:currency_exchange,heavy_dollar_sign:heavy_dollar_sign,credit_card:credit_card,yen:yen,dollar:dollar,euro:euro,pound:pound,money_with_wings:money_with_wings,chart:chart,seat:seat,computer:computer,briefcase:briefcase,minidisc:minidisc,floppy_disk:floppy_disk,cd:cd,dvd:dvd,file_folder:file_folder,open_file_folder:open_file_folder,page_with_curl:page_with_curl,page_facing_up:page_facing_up,date:date,calendar:calendar,card_index:card_index,chart_with_upwards_trend:chart_with_upwards_trend,chart_with_downwards_trend:chart_with_downwards_trend,bar_chart:bar_chart,clipboard:clipboard,pushpin:pushpin,round_pushpin:round_pushpin,paperclip:paperclip,straight_ruler:straight_ruler,triangular_ruler:triangular_ruler,bookmark_tabs:bookmark_tabs,ledger:ledger,notebook:notebook,notebook_with_decorative_cover:notebook_with_decorative_cover,closed_book:closed_book,book:book,open_book:open_book,green_book:green_book,blue_book:blue_book,orange_book:orange_book,books:books,name_badge:name_badge,scroll:scroll,memo:memo,pencil:pencil,telephone_receiver:telephone_receiver,pager:pager,fax:fax,satellite_antenna:satellite_antenna,loudspeaker:loudspeaker,mega:mega,outbox_tray:outbox_tray,inbox_tray:inbox_tray,"package":"📦","e-mail":"📧",incoming_envelope:incoming_envelope,envelope_with_arrow:envelope_with_arrow,mailbox_closed:mailbox_closed,mailbox:mailbox,mailbox_with_mail:mailbox_with_mail,mailbox_with_no_mail:mailbox_with_no_mail,postbox:postbox,postal_horn:postal_horn,newspaper:newspaper,iphone:iphone,calling:calling,vibration_mode:vibration_mode,mobile_phone_off:mobile_phone_off,no_mobile_phones:no_mobile_phones,signal_strength:signal_strength,camera:camera,camera_with_flash:camera_with_flash,video_camera:video_camera,tv:tv,radio:radio,vhs:vhs,film_projector:film_projector,prayer_beads:prayer_beads,twisted_rightwards_arrows:twisted_rightwards_arrows,repeat:repeat,repeat_one:repeat_one,arrows_clockwise:arrows_clockwise,arrows_counterclockwise:arrows_counterclockwise,low_brightness:low_brightness,high_brightness:high_brightness,mute:mute,speaker:speaker,sound:sound,loud_sound:loud_sound,battery:battery,electric_plug:electric_plug,mag:mag,mag_right:mag_right,lock_with_ink_pen:lock_with_ink_pen,closed_lock_with_key:closed_lock_with_key,key:key,lock:lock,unlock:unlock,bell:bell,no_bell:no_bell,bookmark:bookmark,link:link,radio_button:radio_button,back:back,end:end,on:on,soon:soon,underage:underage,keycap_ten:keycap_ten,capital_abcd:capital_abcd,abcd:abcd,symbols:symbols,abc:abc,fire:fire,flashlight:flashlight,wrench:wrench,hammer:hammer,nut_and_bolt:nut_and_bolt,hocho:hocho,knife:knife,gun:gun,microscope:microscope,telescope:telescope,crystal_ball:crystal_ball,six_pointed_star:six_pointed_star,beginner:beginner,trident:trident,black_square_button:black_square_button,white_square_button:white_square_button,red_circle:red_circle,large_blue_circle:large_blue_circle,large_orange_diamond:large_orange_diamond,large_blue_diamond:large_blue_diamond,small_orange_diamond:small_orange_diamond,small_blue_diamond:small_blue_diamond,small_red_triangle:small_red_triangle,small_red_triangle_down:small_red_triangle_down,arrow_up_small:arrow_up_small,arrow_down_small:arrow_down_small,om_symbol:om_symbol,dove_of_peace:dove_of_peace,kaaba:kaaba,mosque:mosque,synagogue:synagogue,menorah_with_nine_branches:menorah_with_nine_branches,candle:candle,mantelpiece_clock:mantelpiece_clock,hole:hole,man_in_business_suit_levitating:man_in_business_suit_levitating,"female-detective":"🕵️‍♀️","male-detective":"🕵️‍♂️",sleuth_or_spy:sleuth_or_spy,dark_sunglasses:dark_sunglasses,spider:spider,spider_web:spider_web,joystick:joystick,man_dancing:man_dancing,linked_paperclips:linked_paperclips,lower_left_ballpoint_pen:lower_left_ballpoint_pen,lower_left_fountain_pen:lower_left_fountain_pen,lower_left_paintbrush:lower_left_paintbrush,lower_left_crayon:lower_left_crayon,raised_hand_with_fingers_splayed:raised_hand_with_fingers_splayed,middle_finger:middle_finger,reversed_hand_with_middle_finger_extended:reversed_hand_with_middle_finger_extended,"spock-hand":"🖖",black_heart:black_heart,desktop_computer:desktop_computer,printer:printer,three_button_mouse:three_button_mouse,trackball:trackball,frame_with_picture:frame_with_picture,card_index_dividers:card_index_dividers,card_file_box:card_file_box,file_cabinet:file_cabinet,wastebasket:wastebasket,spiral_note_pad:spiral_note_pad,spiral_calendar_pad:spiral_calendar_pad,compression:compression,old_key:old_key,rolled_up_newspaper:rolled_up_newspaper,dagger_knife:dagger_knife,speaking_head_in_silhouette:speaking_head_in_silhouette,left_speech_bubble:left_speech_bubble,right_anger_bubble:right_anger_bubble,ballot_box_with_ballot:ballot_box_with_ballot,world_map:world_map,mount_fuji:mount_fuji,tokyo_tower:tokyo_tower,statue_of_liberty:statue_of_liberty,japan:japan,moyai:moyai,grinning:grinning,grin:grin,joy:joy,smiley:smiley,smile:smile,sweat_smile:sweat_smile,laughing:laughing,satisfied:satisfied,innocent:innocent,smiling_imp:smiling_imp,wink:wink,blush:blush,yum:yum,relieved:relieved,heart_eyes:heart_eyes,sunglasses:sunglasses,smirk:smirk,neutral_face:neutral_face,expressionless:expressionless,unamused:unamused,sweat:sweat,pensive:pensive,confused:confused,confounded:confounded,kissing:kissing,kissing_heart:kissing_heart,kissing_smiling_eyes:kissing_smiling_eyes,kissing_closed_eyes:kissing_closed_eyes,stuck_out_tongue:stuck_out_tongue,stuck_out_tongue_winking_eye:stuck_out_tongue_winking_eye,stuck_out_tongue_closed_eyes:stuck_out_tongue_closed_eyes,disappointed:disappointed,worried:worried,angry:angry,rage:rage,cry:cry,persevere:persevere,triumph:triumph,disappointed_relieved:disappointed_relieved,frowning:frowning,anguished:anguished,fearful:fearful,weary:weary,sleepy:sleepy,tired_face:tired_face,grimacing:grimacing,sob:sob,open_mouth:open_mouth,hushed:hushed,cold_sweat:cold_sweat,scream:scream,astonished:astonished,flushed:flushed,sleeping:sleeping,dizzy_face:dizzy_face,no_mouth:no_mouth,mask:mask,smile_cat:smile_cat,joy_cat:joy_cat,smiley_cat:smiley_cat,heart_eyes_cat:heart_eyes_cat,smirk_cat:smirk_cat,kissing_cat:kissing_cat,pouting_cat:pouting_cat,crying_cat_face:crying_cat_face,scream_cat:scream_cat,slightly_frowning_face:slightly_frowning_face,slightly_smiling_face:slightly_smiling_face,upside_down_face:upside_down_face,face_with_rolling_eyes:face_with_rolling_eyes,"woman-gesturing-no":"🙅‍♀️",no_good:no_good,"man-gesturing-no":"🙅‍♂️","woman-gesturing-ok":"🙆‍♀️",ok_woman:ok_woman,"man-gesturing-ok":"🙆‍♂️","woman-bowing":"🙇‍♀️","man-bowing":"🙇‍♂️",bow:bow,see_no_evil:see_no_evil,hear_no_evil:hear_no_evil,speak_no_evil:speak_no_evil,"woman-raising-hand":"🙋‍♀️",raising_hand:raising_hand,"man-raising-hand":"🙋‍♂️",raised_hands:raised_hands,"woman-frowning":"🙍‍♀️",person_frowning:person_frowning,"man-frowning":"🙍‍♂️","woman-pouting":"🙎‍♀️",person_with_pouting_face:person_with_pouting_face,"man-pouting":"🙎‍♂️",pray:pray,rocket:rocket,helicopter:helicopter,steam_locomotive:steam_locomotive,railway_car:railway_car,bullettrain_side:bullettrain_side,bullettrain_front:bullettrain_front,train2:train2,metro:metro,light_rail:light_rail,station:station,tram:tram,train:train,bus:bus,oncoming_bus:oncoming_bus,trolleybus:trolleybus,busstop:busstop,minibus:minibus,ambulance:ambulance,fire_engine:fire_engine,police_car:police_car,oncoming_police_car:oncoming_police_car,taxi:taxi,oncoming_taxi:oncoming_taxi,car:car,red_car:red_car,oncoming_automobile:oncoming_automobile,blue_car:blue_car,truck:truck,articulated_lorry:articulated_lorry,tractor:tractor,monorail:monorail,mountain_railway:mountain_railway,suspension_railway:suspension_railway,mountain_cableway:mountain_cableway,aerial_tramway:aerial_tramway,ship:ship,"woman-rowing-boat":"🚣‍♀️","man-rowing-boat":"🚣‍♂️",rowboat:rowboat,speedboat:speedboat,traffic_light:traffic_light,vertical_traffic_light:vertical_traffic_light,construction:construction,rotating_light:rotating_light,triangular_flag_on_post:triangular_flag_on_post,door:door,no_entry_sign:no_entry_sign,smoking:smoking,no_smoking:no_smoking,put_litter_in_its_place:put_litter_in_its_place,do_not_litter:do_not_litter,potable_water:potable_water,"non-potable_water":"🚱",bike:bike,no_bicycles:no_bicycles,"woman-biking":"🚴‍♀️","man-biking":"🚴‍♂️",bicyclist:bicyclist,"woman-mountain-biking":"🚵‍♀️","man-mountain-biking":"🚵‍♂️",mountain_bicyclist:mountain_bicyclist,"woman-walking":"🚶‍♀️","man-walking":"🚶‍♂️",walking:walking,no_pedestrians:no_pedestrians,children_crossing:children_crossing,mens:mens,womens:womens,restroom:restroom,baby_symbol:baby_symbol,toilet:toilet,wc:wc,shower:shower,bath:bath,bathtub:bathtub,passport_control:passport_control,customs:customs,baggage_claim:baggage_claim,left_luggage:left_luggage,couch_and_lamp:couch_and_lamp,sleeping_accommodation:sleeping_accommodation,shopping_bags:shopping_bags,bellhop_bell:bellhop_bell,bed:bed,place_of_worship:place_of_worship,octagonal_sign:octagonal_sign,shopping_trolley:shopping_trolley,hammer_and_wrench:hammer_and_wrench,shield:shield,oil_drum:oil_drum,motorway:motorway,railway_track:railway_track,motor_boat:motor_boat,small_airplane:small_airplane,airplane_departure:airplane_departure,airplane_arriving:airplane_arriving,satellite:satellite,passenger_ship:passenger_ship,scooter:scooter,motor_scooter:motor_scooter,canoe:canoe,sled:sled,flying_saucer:flying_saucer,zipper_mouth_face:zipper_mouth_face,money_mouth_face:money_mouth_face,face_with_thermometer:face_with_thermometer,nerd_face:nerd_face,thinking_face:thinking_face,face_with_head_bandage:face_with_head_bandage,robot_face:robot_face,hugging_face:hugging_face,the_horns:the_horns,sign_of_the_horns:sign_of_the_horns,call_me_hand:call_me_hand,raised_back_of_hand:raised_back_of_hand,"left-facing_fist":"🤛","right-facing_fist":"🤜",handshake:handshake,crossed_fingers:crossed_fingers,hand_with_index_and_middle_fingers_crossed:hand_with_index_and_middle_fingers_crossed,i_love_you_hand_sign:i_love_you_hand_sign,face_with_cowboy_hat:face_with_cowboy_hat,clown_face:clown_face,nauseated_face:nauseated_face,rolling_on_the_floor_laughing:rolling_on_the_floor_laughing,drooling_face:drooling_face,lying_face:lying_face,"woman-facepalming":"🤦‍♀️","man-facepalming":"🤦‍♂️",face_palm:face_palm,sneezing_face:sneezing_face,face_with_raised_eyebrow:face_with_raised_eyebrow,face_with_one_eyebrow_raised:face_with_one_eyebrow_raised,"star-struck":"🤩",grinning_face_with_star_eyes:grinning_face_with_star_eyes,zany_face:zany_face,grinning_face_with_one_large_and_one_small_eye:grinning_face_with_one_large_and_one_small_eye,shushing_face:shushing_face,face_with_finger_covering_closed_lips:face_with_finger_covering_closed_lips,face_with_symbols_on_mouth:face_with_symbols_on_mouth,serious_face_with_symbols_covering_mouth:serious_face_with_symbols_covering_mouth,face_with_hand_over_mouth:face_with_hand_over_mouth,smiling_face_with_smiling_eyes_and_hand_covering_mouth:smiling_face_with_smiling_eyes_and_hand_covering_mouth,face_vomiting:face_vomiting,face_with_open_mouth_vomiting:face_with_open_mouth_vomiting,exploding_head:exploding_head,shocked_face_with_exploding_head:shocked_face_with_exploding_head,pregnant_woman:pregnant_woman,"breast-feeding":"🤱",palms_up_together:palms_up_together,selfie:selfie,prince:prince,man_in_tuxedo:man_in_tuxedo,mrs_claus:mrs_claus,mother_christmas:mother_christmas,"woman-shrugging":"🤷‍♀️","man-shrugging":"🤷‍♂️",shrug:shrug,"woman-cartwheeling":"🤸‍♀️","man-cartwheeling":"🤸‍♂️",person_doing_cartwheel:person_doing_cartwheel,"woman-juggling":"🤹‍♀️","man-juggling":"🤹‍♂️",juggling:juggling,fencer:fencer,"woman-wrestling":"🤼‍♀️","man-wrestling":"🤼‍♂️",wrestlers:wrestlers,"woman-playing-water-polo":"🤽‍♀️","man-playing-water-polo":"🤽‍♂️",water_polo:water_polo,"woman-playing-handball":"🤾‍♀️","man-playing-handball":"🤾‍♂️",handball:handball,wilted_flower:wilted_flower,drum_with_drumsticks:drum_with_drumsticks,clinking_glasses:clinking_glasses,tumbler_glass:tumbler_glass,spoon:spoon,goal_net:goal_net,first_place_medal:first_place_medal,second_place_medal:second_place_medal,third_place_medal:third_place_medal,boxing_glove:boxing_glove,martial_arts_uniform:martial_arts_uniform,curling_stone:curling_stone,croissant:croissant,avocado:avocado,cucumber:cucumber,bacon:bacon,potato:potato,carrot:carrot,baguette_bread:baguette_bread,green_salad:green_salad,shallow_pan_of_food:shallow_pan_of_food,stuffed_flatbread:stuffed_flatbread,egg:egg,glass_of_milk:glass_of_milk,peanuts:peanuts,kiwifruit:kiwifruit,pancakes:pancakes,dumpling:dumpling,fortune_cookie:fortune_cookie,takeout_box:takeout_box,chopsticks:chopsticks,bowl_with_spoon:bowl_with_spoon,cup_with_straw:cup_with_straw,coconut:coconut,broccoli:broccoli,pie:pie,pretzel:pretzel,cut_of_meat:cut_of_meat,sandwich:sandwich,canned_food:canned_food,crab:crab,lion_face:lion_face,scorpion:scorpion,turkey:turkey,unicorn_face:unicorn_face,eagle:eagle,duck:duck,bat:bat,shark:shark,owl:owl,fox_face:fox_face,butterfly:butterfly,deer:deer,gorilla:gorilla,lizard:lizard,rhinoceros:rhinoceros,shrimp:shrimp,squid:squid,giraffe_face:giraffe_face,zebra_face:zebra_face,hedgehog:hedgehog,sauropod:sauropod,"t-rex":"🦖",cricket:cricket,cheese_wedge:cheese_wedge,face_with_monocle:face_with_monocle,adult:adult,child:child,older_adult:older_adult,bearded_person:bearded_person,person_with_headscarf:person_with_headscarf,woman_in_steamy_room:woman_in_steamy_room,man_in_steamy_room:man_in_steamy_room,person_in_steamy_room:person_in_steamy_room,woman_climbing:woman_climbing,person_climbing:person_climbing,man_climbing:man_climbing,woman_in_lotus_position:woman_in_lotus_position,person_in_lotus_position:person_in_lotus_position,man_in_lotus_position:man_in_lotus_position,female_mage:female_mage,mage:mage,male_mage:male_mage,female_fairy:female_fairy,fairy:fairy,male_fairy:male_fairy,female_vampire:female_vampire,vampire:vampire,male_vampire:male_vampire,mermaid:mermaid,merman:merman,merperson:merperson,female_elf:female_elf,male_elf:male_elf,elf:elf,female_genie:female_genie,male_genie:male_genie,genie:genie,female_zombie:female_zombie,male_zombie:male_zombie,zombie:zombie,brain:brain,orange_heart:orange_heart,billed_cap:billed_cap,scarf:scarf,gloves:gloves,coat:coat,socks:socks,bangbang:bangbang,interrobang:interrobang,tm:tm,information_source:information_source,left_right_arrow:left_right_arrow,arrow_up_down:arrow_up_down,arrow_upper_left:arrow_upper_left,arrow_upper_right:arrow_upper_right,arrow_lower_right:arrow_lower_right,arrow_lower_left:arrow_lower_left,leftwards_arrow_with_hook:leftwards_arrow_with_hook,arrow_right_hook:arrow_right_hook,watch:watch,hourglass:hourglass,keyboard:keyboard,eject:eject,fast_forward:fast_forward,rewind:rewind,arrow_double_up:arrow_double_up,arrow_double_down:arrow_double_down,black_right_pointing_double_triangle_with_vertical_bar:black_right_pointing_double_triangle_with_vertical_bar,black_left_pointing_double_triangle_with_vertical_bar:black_left_pointing_double_triangle_with_vertical_bar,black_right_pointing_triangle_with_double_vertical_bar:black_right_pointing_triangle_with_double_vertical_bar,alarm_clock:alarm_clock,stopwatch:stopwatch,timer_clock:timer_clock,hourglass_flowing_sand:hourglass_flowing_sand,double_vertical_bar:double_vertical_bar,black_square_for_stop:black_square_for_stop,black_circle_for_record:black_circle_for_record,m:m,black_small_square:black_small_square,white_small_square:white_small_square,arrow_forward:arrow_forward,arrow_backward:arrow_backward,white_medium_square:white_medium_square,black_medium_square:black_medium_square,white_medium_small_square:white_medium_small_square,black_medium_small_square:black_medium_small_square,sunny:sunny,cloud:cloud,umbrella:umbrella,snowman:snowman,comet:comet,phone:phone,telephone:telephone,ballot_box_with_check:ballot_box_with_check,shamrock:shamrock,point_up:point_up,skull_and_crossbones:skull_and_crossbones,radioactive_sign:radioactive_sign,biohazard_sign:biohazard_sign,orthodox_cross:orthodox_cross,star_and_crescent:star_and_crescent,peace_symbol:peace_symbol,yin_yang:yin_yang,wheel_of_dharma:wheel_of_dharma,white_frowning_face:white_frowning_face,relaxed:relaxed,female_sign:female_sign,male_sign:male_sign,gemini:gemini,cancer:cancer,leo:leo,virgo:virgo,libra:libra,scorpius:scorpius,spades:spades,clubs:clubs,hearts:hearts,diamonds:diamonds,hotsprings:hotsprings,recycle:recycle,wheelchair:wheelchair,hammer_and_pick:hammer_and_pick,crossed_swords:crossed_swords,medical_symbol:medical_symbol,staff_of_aesculapius:staff_of_aesculapius,scales:scales,alembic:alembic,gear:gear,atom_symbol:atom_symbol,fleur_de_lis:fleur_de_lis,warning:warning,zap:zap,white_circle:white_circle,black_circle:black_circle,coffin:coffin,funeral_urn:funeral_urn,soccer:soccer,baseball:baseball,snowman_without_snow:snowman_without_snow,partly_sunny:partly_sunny,thunder_cloud_and_rain:thunder_cloud_and_rain,ophiuchus:ophiuchus,pick:pick,helmet_with_white_cross:helmet_with_white_cross,chains:chains,no_entry:no_entry,shinto_shrine:shinto_shrine,church:church,mountain:mountain,umbrella_on_ground:umbrella_on_ground,fountain:fountain,golf:golf,ferry:ferry,boat:boat,sailboat:sailboat,skier:skier,ice_skate:ice_skate,"woman-bouncing-ball":"⛹️‍♀️","man-bouncing-ball":"⛹️‍♂️",person_with_ball:person_with_ball,tent:tent,fuelpump:fuelpump,scissors:scissors,airplane:airplane,email:email,envelope:envelope,fist:fist,hand:hand,raised_hand:raised_hand,v:v,writing_hand:writing_hand,pencil2:pencil2,black_nib:black_nib,heavy_check_mark:heavy_check_mark,heavy_multiplication_x:heavy_multiplication_x,latin_cross:latin_cross,star_of_david:star_of_david,eight_spoked_asterisk:eight_spoked_asterisk,eight_pointed_black_star:eight_pointed_black_star,snowflake:snowflake,sparkle:sparkle,x:x,negative_squared_cross_mark:negative_squared_cross_mark,heavy_heart_exclamation_mark_ornament:heavy_heart_exclamation_mark_ornament,heart:heart,arrow_right:arrow_right,curly_loop:curly_loop,loop:loop,arrow_heading_up:arrow_heading_up,arrow_heading_down:arrow_heading_down,arrow_left:arrow_left,arrow_up:arrow_up,arrow_down:arrow_down,black_large_square:black_large_square,white_large_square:white_large_square,star:star,o:o,wavy_dash:wavy_dash,part_alternation_mark:part_alternation_mark,congratulations:congratulations,secret:secret};

  /**
   * regex to parse emoji in a string - finds emoji, e.g. :coffee:
   */


  var emojiNameRegex = /:([a-zA-Z0-9_\-\+]+):/g;
  /**
   * regex to trim whitespace
   * use instead of String.prototype.trim() for IE8 support
   */

  var trimSpaceRegex = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
  /**
   * Removes colons on either side
   * of the string if present
   * @param  {string} str
   * @return {string}
   */

  function stripColons(str) {
    var colonIndex = str.indexOf(':');

    if (colonIndex > -1) {
      // :emoji: (http://www.emoji-cheat-sheet.com/)
      if (colonIndex === str.length - 1) {
        str = str.substring(0, colonIndex);
        return stripColons(str);
      } else {
        str = str.substr(colonIndex + 1);
        return stripColons(str);
      }
    }

    return str;
  }
  /**
   * Adds colons to either side
   * of the string
   * @param {string} str
   * @return {string}
   */


  function wrapColons(str) {
    return typeof str === 'string' && str.length > 0 ? ':' + str + ':' : str;
  }
  /**
   * Ensure that the word is wrapped in colons
   * by only adding them, if they are not there.
   * @param {string} str
   * @return {string}
   */


  function ensureColons(str) {
    return typeof str === 'string' && str[0] !== ':' ? wrapColons(str) : str;
  } // Non spacing mark, some emoticons have them. It's the 'Variant Form',
  // to the client, as it kills graphical emoticons.

  function stripNSB(code) {
    return code;
  }
  // dictionary emojiByCode contains { ❤: 'heart' }. The codes are normalized
  // to the text version.

  var emojiByCode = Object.keys(emojiByName).reduce(function (h, k) {
    h[stripNSB(emojiByName[k])] = k;
    return h;
  }, {});
  /**
   * Emoji namespace
   */

  var Emoji = {
    emoji: emojiByName
  };
  /**
   * get emoji code from name
   * @param  {string} emoji
   * @return {string}
   */

  Emoji._get = function _get(emoji) {
    if (emojiByName.hasOwnProperty(emoji)) {
      return emojiByName[emoji];
    }

    return ensureColons(emoji);
  };
  /**
   * get emoji code from :emoji: string or name
   * @param  {string} emoji
   * @return {string}
   */


  Emoji.get = function get(emoji) {
    emoji = stripColons(emoji);
    return Emoji._get(emoji);
  };
  /**
   * find the emoji by either code or name
   * @param {string} nameOrCode The emoji to find, either `coffee`, `:coffee:` or `☕`;
   * @return {object}
   */


  Emoji.find = function find(nameOrCode) {
    return Emoji.findByName(nameOrCode) || Emoji.findByCode(nameOrCode);
  };
  /**
   * find the emoji by name
   * @param {string} name The emoji to find either `coffee` or `:coffee:`;
   * @return {object}
   */


  Emoji.findByName = function findByName(name) {
    var stripped = stripColons(name);
    var emoji = emojiByName[stripped];
    return emoji ? {
      emoji: emoji,
      key: stripped
    } : undefined;
  };
  /**
   * find the emoji by code (emoji)
   * @param {string} code The emoji to find; for example `☕` or `☔`
   * @return {object}
   */


  Emoji.findByCode = function findByCode(code) {
    var stripped = stripNSB(code);
    var name = emojiByCode[stripped]; // lookup emoji to ensure the Variant Form is returned

    return name ? {
      emoji: emojiByName[name],
      key: name
    } : undefined;
  };
  /**
   * Check if an emoji is known by this library
   * @param {string} nameOrCode The emoji to validate, either `coffee`, `:coffee:` or `☕`;
   * @return {object}
   */


  Emoji.hasEmoji = function hasEmoji(nameOrCode) {
    return Emoji.hasEmojiByName(nameOrCode) || Emoji.hasEmojiByCode(nameOrCode);
  };
  /**
   * Check if an emoji with given name is known by this library
   * @param {string} name The emoji to validate either `coffee` or `:coffee:`;
   * @return {object}
   */


  Emoji.hasEmojiByName = function hasEmojiByName(name) {
    var result = Emoji.findByName(name);
    return !!result && result.key === stripColons(name);
  };
  /**
   * Check if a given emoji is known by this library
   * @param {string} code The emoji to validate; for example `☕` or `☔`
   * @return {object}
   */


  Emoji.hasEmojiByCode = function hasEmojiByCode(code) {
    var result = Emoji.findByCode(code);
    return !!result && stripNSB(result.emoji) === stripNSB(code);
  };
  /**
   * get emoji name from code
   * @param  {string} emoji
   * @param  {boolean} includeColons should the result include the ::
   * @return {string}
   */


  Emoji.which = function which(emoji_code, includeColons) {
    var code = stripNSB(emoji_code);
    var word = emojiByCode[code];
    return includeColons ? wrapColons(word) : word;
  };
  /**
   * emojify a string (replace :emoji: with an emoji)
   * @param  {string} str
   * @param  {function} on_missing (gets emoji name without :: and returns a proper emoji if no emoji was found)
   * @param  {function} format (wrap the returned emoji in a custom element)
   * @return {string}
   */


  Emoji.emojify = function emojify(str, on_missing, format) {
    if (!str) return '';
    return str.split(emojiNameRegex) // parse emoji via regex
    .map(function parseEmoji(s, i) {
      // every second element is an emoji, e.g. "test :fast_forward:" -> [ "test ", "fast_forward" ]
      if (i % 2 === 0) return s;

      var emoji = Emoji._get(s);

      var isMissing = emoji.indexOf(':') > -1;

      if (isMissing && typeof on_missing === 'function') {
        return on_missing(s);
      }

      if (!isMissing && typeof format === 'function') {
        return format(emoji, s);
      }

      return emoji;
    }).join('') // convert back to string
    ;
  };
  /**
   * return a random emoji
   * @return {string}
   */


  Emoji.random = function random() {
    var emojiKeys = Object.keys(emojiByName);
    var randomIndex = Math.floor(Math.random() * emojiKeys.length);
    var key = emojiKeys[randomIndex];

    var emoji = Emoji._get(key);

    return {
      key: key,
      emoji: emoji
    };
  };
  /**
   *  return an collection of potential emoji matches
   *  @param {string} str
   *  @return {Array.<Object>}
   */


  Emoji.search = function search(str) {
    var emojiKeys = Object.keys(emojiByName);
    var matcher = stripColons(str);
    var matchingKeys = emojiKeys.filter(function (key) {
      return key.toString().indexOf(matcher) === 0;
    });
    return matchingKeys.map(function (key) {
      return {
        key: key,
        emoji: Emoji._get(key)
      };
    });
  };
  /**
   * unemojify a string (replace emoji with :emoji:)
   * @param  {string} str
   * @return {string}
   */


  Emoji.unemojify = function unemojify(str) {
    if (!str) return '';
    var words = lodash_toarray(str);
    return words.map(function (word) {
      return Emoji.which(word, true) || word;
    }).join('');
  };
  /**
   * replace emojis with replacement value
   * @param {string} str
   * @param {function|string} the string or callback function to replace the emoji with
   * @param {boolean} should trailing whitespaces be cleaned? Defaults false
   * @return {string}
   */


  Emoji.replace = function replace(str, replacement, cleanSpaces) {
    if (!str) return '';
    var replace = typeof replacement === 'function' ? replacement : function () {
      return replacement;
    };
    var words = lodash_toarray(str);
    var replaced = words.map(function (word, idx) {
      var emoji = Emoji.findByCode(word);

      if (emoji && cleanSpaces && words[idx + 1] === ' ') {
        words[idx + 1] = '';
      }

      return emoji ? replace(emoji) : word;
    }).join('');
    return cleanSpaces ? replaced.replace(trimSpaceRegex, '') : replaced;
  };
  /**
   * remove all emojis from a string
   * @param {string} str
   * @return {string}
   */


  Emoji.strip = function strip(str) {
    return Emoji.replace(str, '', true);
  };

  var emoji = Emoji;

  var nodeEmoji = emoji;

  // https://github.com/substack/deep-freeze/blob/master/index.js

  /** @param {any} obj */
  function deepFreeze(obj) {
    Object.freeze(obj);
    var objIsFunction = typeof obj === 'function';
    Object.getOwnPropertyNames(obj).forEach(function (prop) {
      if (Object.hasOwnProperty.call(obj, prop) && obj[prop] !== null && (typeof obj[prop] === "object" || typeof obj[prop] === "function") // IE11 fix: https://github.com/highlightjs/highlight.js/issues/2318
      // TODO: remove in the future
      && (objIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true) && !Object.isFrozen(obj[prop])) {
        deepFreeze(obj[prop]);
      }
    });
    return obj;
  }

  class Response {
    /**
     * @param {CompiledMode} mode
     */
    constructor(mode) {
      // eslint-disable-next-line no-undefined
      if (mode.data === undefined) mode.data = {};
      this.data = mode.data;
    }

    ignoreMatch() {
      this.ignore = true;
    }

  }
  /**
   * @param {string} value
   * @returns {string}
   */


  function escapeHTML(value) {
    return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
  }
  /**
   * performs a shallow merge of multiple objects into one
   *
   * @template T
   * @param {T} original
   * @param {Record<string,any>[]} objects
   * @returns {T} a single new object
   */


  function inherit(original, ...objects) {
    /** @type Record<string,any> */
    var result = {};

    for (const key in original) {
      result[key] = original[key];
    }

    objects.forEach(function (obj) {
      for (const key in obj) {
        result[key] = obj[key];
      }
    });
    return (
      /** @type {T} */
      result
    );
  }
  /* Stream merging */

  /**
   * @typedef Event
   * @property {'start'|'stop'} event
   * @property {number} offset
   * @property {Node} node
   */

  /**
   * @param {Node} node
   */


  function tag(node) {
    return node.nodeName.toLowerCase();
  }
  /**
   * @param {Node} node
   */


  function nodeStream(node) {
    /** @type Event[] */
    var result = [];

    (function _nodeStream(node, offset) {
      for (var child = node.firstChild; child; child = child.nextSibling) {
        if (child.nodeType === 3) {
          offset += child.nodeValue.length;
        } else if (child.nodeType === 1) {
          result.push({
            event: 'start',
            offset: offset,
            node: child
          });
          offset = _nodeStream(child, offset); // Prevent void elements from having an end tag that would actually
          // double them in the output. There are more void elements in HTML
          // but we list only those realistically expected in code display.

          if (!tag(child).match(/br|hr|img|input/)) {
            result.push({
              event: 'stop',
              offset: offset,
              node: child
            });
          }
        }
      }

      return offset;
    })(node, 0);

    return result;
  }
  /**
   * @param {any} original - the original stream
   * @param {any} highlighted - stream of the highlighted source
   * @param {string} value - the original source itself
   */


  function mergeStreams(original, highlighted, value) {
    var processed = 0;
    var result = '';
    var nodeStack = [];

    function selectStream() {
      if (!original.length || !highlighted.length) {
        return original.length ? original : highlighted;
      }

      if (original[0].offset !== highlighted[0].offset) {
        return original[0].offset < highlighted[0].offset ? original : highlighted;
      }
      /*
      To avoid starting the stream just before it should stop the order is
      ensured that original always starts first and closes last:
       if (event1 == 'start' && event2 == 'start')
        return original;
      if (event1 == 'start' && event2 == 'stop')
        return highlighted;
      if (event1 == 'stop' && event2 == 'start')
        return original;
      if (event1 == 'stop' && event2 == 'stop')
        return highlighted;
       ... which is collapsed to:
      */


      return highlighted[0].event === 'start' ? original : highlighted;
    }
    /**
     * @param {Node} node
     */


    function open(node) {
      /** @param {Attr} attr */
      function attr_str(attr) {
        return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
      } // @ts-ignore


      result += '<' + tag(node) + [].map.call(node.attributes, attr_str).join('') + '>';
    }
    /**
     * @param {Node} node
     */


    function close(node) {
      result += '</' + tag(node) + '>';
    }
    /**
     * @param {Event} event
     */


    function render(event) {
      (event.event === 'start' ? open : close)(event.node);
    }

    while (original.length || highlighted.length) {
      var stream = selectStream();
      result += escapeHTML(value.substring(processed, stream[0].offset));
      processed = stream[0].offset;

      if (stream === original) {
        /*
        On any opening or closing tag of the original markup we first close
        the entire highlighted node stack, then render the original tag along
        with all the following original tags at the same offset and then
        reopen all the tags on the highlighted stack.
        */
        nodeStack.reverse().forEach(close);

        do {
          render(stream.splice(0, 1)[0]);
          stream = selectStream();
        } while (stream === original && stream.length && stream[0].offset === processed);

        nodeStack.reverse().forEach(open);
      } else {
        if (stream[0].event === 'start') {
          nodeStack.push(stream[0].node);
        } else {
          nodeStack.pop();
        }

        render(stream.splice(0, 1)[0]);
      }
    }

    return result + escapeHTML(value.substr(processed));
  }

  var utils = /*#__PURE__*/Object.freeze({
    __proto__: null,
    escapeHTML: escapeHTML,
    inherit: inherit,
    nodeStream: nodeStream,
    mergeStreams: mergeStreams
  });
  /**
   * @typedef {object} Renderer
   * @property {(text: string) => void} addText
   * @property {(node: Node) => void} openNode
   * @property {(node: Node) => void} closeNode
   * @property {() => string} value
   */

  /** @typedef {{kind?: string, sublanguage?: boolean}} Node */

  /** @typedef {{walk: (r: Renderer) => void}} Tree */

  /** */

  const SPAN_CLOSE = '</span>';
  /**
   * Determines if a node needs to be wrapped in <span>
   *
   * @param {Node} node */

  const emitsWrappingTags = node => {
    return !!node.kind;
  };
  /** @type {Renderer} */


  class HTMLRenderer {
    /**
     * Creates a new HTMLRenderer
     *
     * @param {Tree} parseTree - the parse tree (must support `walk` API)
     * @param {{classPrefix: string}} options
     */
    constructor(parseTree, options) {
      this.buffer = "";
      this.classPrefix = options.classPrefix;
      parseTree.walk(this);
    }
    /**
     * Adds texts to the output stream
     *
     * @param {string} text */


    addText(text) {
      this.buffer += escapeHTML(text);
    }
    /**
     * Adds a node open to the output stream (if needed)
     *
     * @param {Node} node */


    openNode(node) {
      if (!emitsWrappingTags(node)) return;
      let className = node.kind;

      if (!node.sublanguage) {
        className = `${this.classPrefix}${className}`;
      }

      this.span(className);
    }
    /**
     * Adds a node close to the output stream (if needed)
     *
     * @param {Node} node */


    closeNode(node) {
      if (!emitsWrappingTags(node)) return;
      this.buffer += SPAN_CLOSE;
    }
    /**
     * returns the accumulated buffer
    */


    value() {
      return this.buffer;
    } // helpers

    /**
     * Builds a span element
     *
     * @param {string} className */


    span(className) {
      this.buffer += `<span class="${className}">`;
    }

  }
  /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} | string} Node */

  /** @typedef {{kind?: string, sublanguage?: boolean, children: Node[]} } DataNode */

  /**  */


  class TokenTree {
    constructor() {
      /** @type DataNode */
      this.rootNode = {
        children: []
      };
      this.stack = [this.rootNode];
    }

    get top() {
      return this.stack[this.stack.length - 1];
    }

    get root() {
      return this.rootNode;
    }
    /** @param {Node} node */


    add(node) {
      this.top.children.push(node);
    }
    /** @param {string} kind */


    openNode(kind) {
      /** @type Node */
      const node = {
        kind,
        children: []
      };
      this.add(node);
      this.stack.push(node);
    }

    closeNode() {
      if (this.stack.length > 1) {
        return this.stack.pop();
      } // eslint-disable-next-line no-undefined


      return undefined;
    }

    closeAllNodes() {
      while (this.closeNode());
    }

    toJSON() {
      return JSON.stringify(this.rootNode, null, 4);
    }
    /**
     * @typedef { import("./html_renderer").Renderer } Renderer
     * @param {Renderer} builder
     */


    walk(builder) {
      // this does not
      return this.constructor._walk(builder, this.rootNode); // this works
      // return TokenTree._walk(builder, this.rootNode);
    }
    /**
     * @param {Renderer} builder
     * @param {Node} node
     */


    static _walk(builder, node) {
      if (typeof node === "string") {
        builder.addText(node);
      } else if (node.children) {
        builder.openNode(node);
        node.children.forEach(child => this._walk(builder, child));
        builder.closeNode(node);
      }

      return builder;
    }
    /**
     * @param {Node} node
     */


    static _collapse(node) {
      if (typeof node === "string") return;
      if (!node.children) return;

      if (node.children.every(el => typeof el === "string")) {
        // node.text = node.children.join("");
        // delete node.children;
        node.children = [node.children.join("")];
      } else {
        node.children.forEach(child => {
          TokenTree._collapse(child);
        });
      }
    }

  }
  /**
    Currently this is all private API, but this is the minimal API necessary
    that an Emitter must implement to fully support the parser.

    Minimal interface:

    - addKeyword(text, kind)
    - addText(text)
    - addSublanguage(emitter, subLanguageName)
    - finalize()
    - openNode(kind)
    - closeNode()
    - closeAllNodes()
    - toHTML()

  */

  /**
   * @implements {Emitter}
   */


  class TokenTreeEmitter extends TokenTree {
    /**
     * @param {*} options
     */
    constructor(options) {
      super();
      this.options = options;
    }
    /**
     * @param {string} text
     * @param {string} kind
     */


    addKeyword(text, kind) {
      if (text === "") {
        return;
      }

      this.openNode(kind);
      this.addText(text);
      this.closeNode();
    }
    /**
     * @param {string} text
     */


    addText(text) {
      if (text === "") {
        return;
      }

      this.add(text);
    }
    /**
     * @param {Emitter & {root: DataNode}} emitter
     * @param {string} name
     */


    addSublanguage(emitter, name) {
      /** @type DataNode */
      const node = emitter.root;
      node.kind = name;
      node.sublanguage = true;
      this.add(node);
    }

    toHTML() {
      const renderer = new HTMLRenderer(this, this.options);
      return renderer.value();
    }

    finalize() {
      return true;
    }

  }
  /**
   * @param {string} value
   * @returns {RegExp}
   * */


  function escape(value) {
    return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
  }
  /**
   * @param {RegExp | string } re
   * @returns {string}
   */


  function source(re) {
    if (!re) return null;
    if (typeof re === "string") return re;
    return re.source;
  }
  /**
   * @param {...(RegExp | string) } args
   * @returns {string}
   */


  function concat(...args) {
    const joined = args.map(x => source(x)).join("");
    return joined;
  }
  /**
   * @param {RegExp} re
   * @returns {number}
   */


  function countMatchGroups(re) {
    return new RegExp(re.toString() + '|').exec('').length - 1;
  }
  /**
   * Does lexeme start with a regular expression match at the beginning
   * @param {RegExp} re
   * @param {string} lexeme
   */


  function startsWith(re, lexeme) {
    var match = re && re.exec(lexeme);
    return match && match.index === 0;
  } // join logically computes regexps.join(separator), but fixes the
  // backreferences so they continue to match.
  // it also places each individual regular expression into it's own
  // match group, keeping track of the sequencing of those match groups
  // is currently an exercise for the caller. :-)

  /**
   * @param {(string | RegExp)[]} regexps
   * @param {string} separator
   * @returns {string}
   */


  function join(regexps, separator = "|") {
    // backreferenceRe matches an open parenthesis or backreference. To avoid
    // an incorrect parse, it additionally matches the following:
    // - [...] elements, where the meaning of parentheses and escapes change
    // - other escape sequences, so we do not misparse escape sequences as
    //   interesting elements
    // - non-matching or lookahead parentheses, which do not capture. These
    //   follow the '(' with a '?'.
    var backreferenceRe = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
    var numCaptures = 0;
    var ret = '';

    for (var i = 0; i < regexps.length; i++) {
      numCaptures += 1;
      var offset = numCaptures;
      var re = source(regexps[i]);

      if (i > 0) {
        ret += separator;
      }

      ret += "(";

      while (re.length > 0) {
        var match = backreferenceRe.exec(re);

        if (match == null) {
          ret += re;
          break;
        }

        ret += re.substring(0, match.index);
        re = re.substring(match.index + match[0].length);

        if (match[0][0] === '\\' && match[1]) {
          // Adjust the backreference.
          ret += '\\' + String(Number(match[1]) + offset);
        } else {
          ret += match[0];

          if (match[0] === '(') {
            numCaptures++;
          }
        }
      }

      ret += ")";
    }

    return ret;
  } // Common regexps


  const IDENT_RE = '[a-zA-Z]\\w*';
  const UNDERSCORE_IDENT_RE = '[a-zA-Z_]\\w*';
  const NUMBER_RE = '\\b\\d+(\\.\\d+)?';
  const C_NUMBER_RE = '(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)'; // 0x..., 0..., decimal, float

  const BINARY_NUMBER_RE = '\\b(0b[01]+)'; // 0b...

  const RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
  /**
  * @param { Partial<Mode> & {binary?: string | RegExp} } opts
  */

  const SHEBANG = (opts = {}) => {
    const beginShebang = /^#![ ]*\//;

    if (opts.binary) {
      opts.begin = concat(beginShebang, /.*\b/, opts.binary, /\b.*/);
    }

    return inherit({
      className: 'meta',
      begin: beginShebang,
      end: /$/,
      relevance: 0,

      /** @type {ModeCallback} */
      "on:begin": (m, resp) => {
        if (m.index !== 0) resp.ignoreMatch();
      }
    }, opts);
  }; // Common modes


  const BACKSLASH_ESCAPE = {
    begin: '\\\\[\\s\\S]',
    relevance: 0
  };
  const APOS_STRING_MODE = {
    className: 'string',
    begin: '\'',
    end: '\'',
    illegal: '\\n',
    contains: [BACKSLASH_ESCAPE]
  };
  const QUOTE_STRING_MODE = {
    className: 'string',
    begin: '"',
    end: '"',
    illegal: '\\n',
    contains: [BACKSLASH_ESCAPE]
  };
  const PHRASAL_WORDS_MODE = {
    begin: /\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
  };
  /**
   * Creates a comment mode
   *
   * @param {string | RegExp} begin
   * @param {string | RegExp} end
   * @param {Mode | {}} [modeOptions]
   * @returns {Partial<Mode>}
   */

  const COMMENT = function (begin, end, modeOptions = {}) {
    var mode = inherit({
      className: 'comment',
      begin,
      end,
      contains: []
    }, modeOptions);
    mode.contains.push(PHRASAL_WORDS_MODE);
    mode.contains.push({
      className: 'doctag',
      begin: '(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):',
      relevance: 0
    });
    return mode;
  };

  const C_LINE_COMMENT_MODE = COMMENT('//', '$');
  const C_BLOCK_COMMENT_MODE = COMMENT('/\\*', '\\*/');
  const HASH_COMMENT_MODE = COMMENT('#', '$');
  const NUMBER_MODE = {
    className: 'number',
    begin: NUMBER_RE,
    relevance: 0
  };
  const C_NUMBER_MODE = {
    className: 'number',
    begin: C_NUMBER_RE,
    relevance: 0
  };
  const BINARY_NUMBER_MODE = {
    className: 'number',
    begin: BINARY_NUMBER_RE,
    relevance: 0
  };
  const CSS_NUMBER_MODE = {
    className: 'number',
    begin: NUMBER_RE + '(' + '%|em|ex|ch|rem' + '|vw|vh|vmin|vmax' + '|cm|mm|in|pt|pc|px' + '|deg|grad|rad|turn' + '|s|ms' + '|Hz|kHz' + '|dpi|dpcm|dppx' + ')?',
    relevance: 0
  };
  const REGEXP_MODE = {
    // this outer rule makes sure we actually have a WHOLE regex and not simply
    // an expression such as:
    //
    //     3 / something
    //
    // (which will then blow up when regex's `illegal` sees the newline)
    begin: /(?=\/[^/\n]*\/)/,
    contains: [{
      className: 'regexp',
      begin: /\//,
      end: /\/[gimuy]*/,
      illegal: /\n/,
      contains: [BACKSLASH_ESCAPE, {
        begin: /\[/,
        end: /\]/,
        relevance: 0,
        contains: [BACKSLASH_ESCAPE]
      }]
    }]
  };
  const TITLE_MODE = {
    className: 'title',
    begin: IDENT_RE,
    relevance: 0
  };
  const UNDERSCORE_TITLE_MODE = {
    className: 'title',
    begin: UNDERSCORE_IDENT_RE,
    relevance: 0
  };
  const METHOD_GUARD = {
    // excludes method names from keyword processing
    begin: '\\.\\s*' + UNDERSCORE_IDENT_RE,
    relevance: 0
  };
  /**
   * Adds end same as begin mechanics to a mode
   *
   * Your mode must include at least a single () match group as that first match
   * group is what is used for comparison
   * @param {Partial<Mode>} mode
   */

  const END_SAME_AS_BEGIN = function (mode) {
    return Object.assign(mode, {
      /** @type {ModeCallback} */
      'on:begin': (m, resp) => {
        resp.data._beginMatch = m[1];
      },

      /** @type {ModeCallback} */
      'on:end': (m, resp) => {
        if (resp.data._beginMatch !== m[1]) resp.ignoreMatch();
      }
    });
  };

  var MODES = /*#__PURE__*/Object.freeze({
    __proto__: null,
    IDENT_RE: IDENT_RE,
    UNDERSCORE_IDENT_RE: UNDERSCORE_IDENT_RE,
    NUMBER_RE: NUMBER_RE,
    C_NUMBER_RE: C_NUMBER_RE,
    BINARY_NUMBER_RE: BINARY_NUMBER_RE,
    RE_STARTERS_RE: RE_STARTERS_RE,
    SHEBANG: SHEBANG,
    BACKSLASH_ESCAPE: BACKSLASH_ESCAPE,
    APOS_STRING_MODE: APOS_STRING_MODE,
    QUOTE_STRING_MODE: QUOTE_STRING_MODE,
    PHRASAL_WORDS_MODE: PHRASAL_WORDS_MODE,
    COMMENT: COMMENT,
    C_LINE_COMMENT_MODE: C_LINE_COMMENT_MODE,
    C_BLOCK_COMMENT_MODE: C_BLOCK_COMMENT_MODE,
    HASH_COMMENT_MODE: HASH_COMMENT_MODE,
    NUMBER_MODE: NUMBER_MODE,
    C_NUMBER_MODE: C_NUMBER_MODE,
    BINARY_NUMBER_MODE: BINARY_NUMBER_MODE,
    CSS_NUMBER_MODE: CSS_NUMBER_MODE,
    REGEXP_MODE: REGEXP_MODE,
    TITLE_MODE: TITLE_MODE,
    UNDERSCORE_TITLE_MODE: UNDERSCORE_TITLE_MODE,
    METHOD_GUARD: METHOD_GUARD,
    END_SAME_AS_BEGIN: END_SAME_AS_BEGIN
  }); // keywords that should have no default relevance value

  var COMMON_KEYWORDS = 'of and for in not or if then'.split(' '); // compilation

  /**
   * Compiles a language definition result
   *
   * Given the raw result of a language definition (Language), compiles this so
   * that it is ready for highlighting code.
   * @param {Language} language
   * @returns {CompiledLanguage}
   */

  function compileLanguage(language) {
    /**
     * Builds a regex with the case sensativility of the current language
     *
     * @param {RegExp | string} value
     * @param {boolean} [global]
     */
    function langRe(value, global) {
      return new RegExp(source(value), 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : ''));
    }
    /**
      Stores multiple regular expressions and allows you to quickly search for
      them all in a string simultaneously - returning the first match.  It does
      this by creating a huge (a|b|c) regex - each individual item wrapped with ()
      and joined by `|` - using match groups to track position.  When a match is
      found checking which position in the array has content allows us to figure
      out which of the original regexes / match groups triggered the match.
       The match object itself (the result of `Regex.exec`) is returned but also
      enhanced by merging in any meta-data that was registered with the regex.
      This is how we keep track of which mode matched, and what type of rule
      (`illegal`, `begin`, end, etc).
    */


    class MultiRegex {
      constructor() {
        this.matchIndexes = {}; // @ts-ignore

        this.regexes = [];
        this.matchAt = 1;
        this.position = 0;
      } // @ts-ignore


      addRule(re, opts) {
        opts.position = this.position++; // @ts-ignore

        this.matchIndexes[this.matchAt] = opts;
        this.regexes.push([opts, re]);
        this.matchAt += countMatchGroups(re) + 1;
      }

      compile() {
        if (this.regexes.length === 0) {
          // avoids the need to check length every time exec is called
          // @ts-ignore
          this.exec = () => null;
        }

        const terminators = this.regexes.map(el => el[1]);
        this.matcherRe = langRe(join(terminators), true);
        this.lastIndex = 0;
      }
      /** @param {string} s */


      exec(s) {
        this.matcherRe.lastIndex = this.lastIndex;
        const match = this.matcherRe.exec(s);

        if (!match) {
          return null;
        } // eslint-disable-next-line no-undefined


        const i = match.findIndex((el, i) => i > 0 && el !== undefined); // @ts-ignore

        const matchData = this.matchIndexes[i]; // trim off any earlier non-relevant match groups (ie, the other regex
        // match groups that make up the multi-matcher)

        match.splice(0, i);
        return Object.assign(match, matchData);
      }

    }
    /*
      Created to solve the key deficiently with MultiRegex - there is no way to
      test for multiple matches at a single location.  Why would we need to do
      that?  In the future a more dynamic engine will allow certain matches to be
      ignored.  An example: if we matched say the 3rd regex in a large group but
      decided to ignore it - we'd need to started testing again at the 4th
      regex... but MultiRegex itself gives us no real way to do that.
       So what this class creates MultiRegexs on the fly for whatever search
      position they are needed.
       NOTE: These additional MultiRegex objects are created dynamically.  For most
      grammars most of the time we will never actually need anything more than the
      first MultiRegex - so this shouldn't have too much overhead.
       Say this is our search group, and we match regex3, but wish to ignore it.
         regex1 | regex2 | regex3 | regex4 | regex5    ' ie, startAt = 0
       What we need is a new MultiRegex that only includes the remaining
      possibilities:
         regex4 | regex5                               ' ie, startAt = 3
       This class wraps all that complexity up in a simple API... `startAt` decides
      where in the array of expressions to start doing the matching. It
      auto-increments, so if a match is found at position 2, then startAt will be
      set to 3.  If the end is reached startAt will return to 0.
       MOST of the time the parser will be setting startAt manually to 0.
    */


    class ResumableMultiRegex {
      constructor() {
        // @ts-ignore
        this.rules = []; // @ts-ignore

        this.multiRegexes = [];
        this.count = 0;
        this.lastIndex = 0;
        this.regexIndex = 0;
      } // @ts-ignore


      getMatcher(index) {
        if (this.multiRegexes[index]) return this.multiRegexes[index];
        const matcher = new MultiRegex();
        this.rules.slice(index).forEach(([re, opts]) => matcher.addRule(re, opts));
        matcher.compile();
        this.multiRegexes[index] = matcher;
        return matcher;
      }

      considerAll() {
        this.regexIndex = 0;
      } // @ts-ignore


      addRule(re, opts) {
        this.rules.push([re, opts]);
        if (opts.type === "begin") this.count++;
      }
      /** @param {string} s */


      exec(s) {
        const m = this.getMatcher(this.regexIndex);
        m.lastIndex = this.lastIndex;
        const result = m.exec(s);

        if (result) {
          this.regexIndex += result.position + 1;

          if (this.regexIndex === this.count) {
            // wrap-around
            this.regexIndex = 0;
          }
        } // this.regexIndex = 0;


        return result;
      }

    }
    /**
     * Given a mode, builds a huge ResumableMultiRegex that can be used to walk
     * the content and find matches.
     *
     * @param {CompiledMode} mode
     * @returns {ResumableMultiRegex}
     */


    function buildModeRegex(mode) {
      const mm = new ResumableMultiRegex();
      mode.contains.forEach(term => mm.addRule(term.begin, {
        rule: term,
        type: "begin"
      }));

      if (mode.terminator_end) {
        mm.addRule(mode.terminator_end, {
          type: "end"
        });
      }

      if (mode.illegal) {
        mm.addRule(mode.illegal, {
          type: "illegal"
        });
      }

      return mm;
    } // TODO: We need negative look-behind support to do this properly

    /**
     * Skip a match if it has a preceding or trailing dot
     *
     * This is used for `beginKeywords` to prevent matching expressions such as
     * `bob.keyword.do()`. The mode compiler automatically wires this up as a
     * special _internal_ 'on:begin' callback for modes with `beginKeywords`
     * @param {RegExpMatchArray} match
     * @param {CallbackResponse} response
     */


    function skipIfhasPrecedingOrTrailingDot(match, response) {
      const before = match.input[match.index - 1];
      const after = match.input[match.index + match[0].length];

      if (before === "." || after === ".") {
        response.ignoreMatch();
      }
    }
    /** skip vs abort vs ignore
     *
     * @skip   - The mode is still entered and exited normally (and contains rules apply),
     *           but all content is held and added to the parent buffer rather than being
     *           output when the mode ends.  Mostly used with `sublanguage` to build up
     *           a single large buffer than can be parsed by sublanguage.
     *
     *             - The mode begin ands ends normally.
     *             - Content matched is added to the parent mode buffer.
     *             - The parser cursor is moved forward normally.
     *
     * @abort  - A hack placeholder until we have ignore.  Aborts the mode (as if it
     *           never matched) but DOES NOT continue to match subsequent `contains`
     *           modes.  Abort is bad/suboptimal because it can result in modes
     *           farther down not getting applied because an earlier rule eats the
     *           content but then aborts.
     *
     *             - The mode does not begin.
     *             - Content matched by `begin` is added to the mode buffer.
     *             - The parser cursor is moved forward accordingly.
     *
     * @ignore - Ignores the mode (as if it never matched) and continues to match any
     *           subsequent `contains` modes.  Ignore isn't technically possible with
     *           the current parser implementation.
     *
     *             - The mode does not begin.
     *             - Content matched by `begin` is ignored.
     *             - The parser cursor is not moved forward.
     */

    /**
     * Compiles an individual mode
     *
     * This can raise an error if the mode contains certain detectable known logic
     * issues.
     * @param {Mode} mode
     * @param {CompiledMode | null} [parent]
     * @returns {CompiledMode | never}
     */


    function compileMode(mode, parent) {
      const cmode =
      /** @type CompiledMode */
      mode;
      if (mode.compiled) return cmode;
      mode.compiled = true; // __beforeBegin is considered private API, internal use only

      mode.__beforeBegin = null;
      mode.keywords = mode.keywords || mode.beginKeywords;
      let kw_pattern = null;

      if (typeof mode.keywords === "object") {
        kw_pattern = mode.keywords.$pattern;
        delete mode.keywords.$pattern;
      }

      if (mode.keywords) {
        mode.keywords = compileKeywords(mode.keywords, language.case_insensitive);
      } // both are not allowed


      if (mode.lexemes && kw_pattern) {
        throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");
      } // `mode.lexemes` was the old standard before we added and now recommend
      // using `keywords.$pattern` to pass the keyword pattern


      cmode.keywordPatternRe = langRe(mode.lexemes || kw_pattern || /\w+/, true);

      if (parent) {
        if (mode.beginKeywords) {
          // for languages with keywords that include non-word characters checking for
          // a word boundary is not sufficient, so instead we check for a word boundary
          // or whitespace - this does no harm in any case since our keyword engine
          // doesn't allow spaces in keywords anyways and we still check for the boundary
          // first
          mode.begin = '\\b(' + mode.beginKeywords.split(' ').join('|') + ')(?=\\b|\\s)';
          mode.__beforeBegin = skipIfhasPrecedingOrTrailingDot;
        }

        if (!mode.begin) mode.begin = /\B|\b/;
        cmode.beginRe = langRe(mode.begin);
        if (mode.endSameAsBegin) mode.end = mode.begin;
        if (!mode.end && !mode.endsWithParent) mode.end = /\B|\b/;
        if (mode.end) cmode.endRe = langRe(mode.end);
        cmode.terminator_end = source(mode.end) || '';

        if (mode.endsWithParent && parent.terminator_end) {
          cmode.terminator_end += (mode.end ? '|' : '') + parent.terminator_end;
        }
      }

      if (mode.illegal) cmode.illegalRe = langRe(mode.illegal); // eslint-disable-next-line no-undefined

      if (mode.relevance === undefined) mode.relevance = 1;
      if (!mode.contains) mode.contains = [];
      mode.contains = [].concat(...mode.contains.map(function (c) {
        return expand_or_clone_mode(c === 'self' ? mode : c);
      }));
      mode.contains.forEach(function (c) {
        compileMode(
        /** @type Mode */
        c, cmode);
      });

      if (mode.starts) {
        compileMode(mode.starts, parent);
      }

      cmode.matcher = buildModeRegex(cmode);
      return cmode;
    } // self is not valid at the top-level


    if (language.contains && language.contains.includes('self')) {
      throw new Error("ERR: contains `self` is not supported at the top-level of a language.  See documentation.");
    }

    return compileMode(
    /** @type Mode */
    language);
  }
  /**
   * Determines if a mode has a dependency on it's parent or not
   *
   * If a mode does have a parent dependency then often we need to clone it if
   * it's used in multiple places so that each copy points to the correct parent,
   * where-as modes without a parent can often safely be re-used at the bottom of
   * a mode chain.
   *
   * @param {Mode | null} mode
   * @returns {boolean} - is there a dependency on the parent?
   * */


  function dependencyOnParent(mode) {
    if (!mode) return false;
    return mode.endsWithParent || dependencyOnParent(mode.starts);
  }
  /**
   * Expands a mode or clones it if necessary
   *
   * This is necessary for modes with parental dependenceis (see notes on
   * `dependencyOnParent`) and for nodes that have `variants` - which must then be
   * exploded into their own individual modes at compile time.
   *
   * @param {Mode} mode
   * @returns {Mode | Mode[]}
   * */


  function expand_or_clone_mode(mode) {
    if (mode.variants && !mode.cached_variants) {
      mode.cached_variants = mode.variants.map(function (variant) {
        return inherit(mode, {
          variants: null
        }, variant);
      });
    } // EXPAND
    // if we have variants then essentially "replace" the mode with the variants
    // this happens in compileMode, where this function is called from


    if (mode.cached_variants) {
      return mode.cached_variants;
    } // CLONE
    // if we have dependencies on parents then we need a unique
    // instance of ourselves, so we can be reused with many
    // different parents without issue


    if (dependencyOnParent(mode)) {
      return inherit(mode, {
        starts: mode.starts ? inherit(mode.starts) : null
      });
    }

    if (Object.isFrozen(mode)) {
      return inherit(mode);
    } // no special dependency issues, just return ourselves


    return mode;
  }
  /***********************************************
    Keywords
  ***********************************************/

  /**
   * Given raw keywords from a language definition, compile them.
   *
   * @param {string | Record<string,string>} rawKeywords
   * @param {boolean} case_insensitive
   */


  function compileKeywords(rawKeywords, case_insensitive) {
    /** @type KeywordDict */
    var compiled_keywords = {};

    if (typeof rawKeywords === 'string') {
      // string
      splitAndCompile('keyword', rawKeywords);
    } else {
      Object.keys(rawKeywords).forEach(function (className) {
        splitAndCompile(className, rawKeywords[className]);
      });
    }

    return compiled_keywords; // ---

    /**
     * Compiles an individual list of keywords
     *
     * Ex: "for if when while|5"
     *
     * @param {string} className
     * @param {string} keywordList
     */

    function splitAndCompile(className, keywordList) {
      if (case_insensitive) {
        keywordList = keywordList.toLowerCase();
      }

      keywordList.split(' ').forEach(function (keyword) {
        var pair = keyword.split('|');
        compiled_keywords[pair[0]] = [className, scoreForKeyword(pair[0], pair[1])];
      });
    }
  }
  /**
   * Returns the proper score for a given keyword
   *
   * Also takes into account comment keywords, which will be scored 0 UNLESS
   * another score has been manually assigned.
   * @param {string} keyword
   * @param {string} [providedScore]
   */


  function scoreForKeyword(keyword, providedScore) {
    // manual scores always win over common keywords
    // so you can force a score of 1 if you really insist
    if (providedScore) {
      return Number(providedScore);
    }

    return commonKeyword(keyword) ? 0 : 1;
  }
  /**
   * Determines if a given keyword is common or not
   *
   * @param {string} keyword */


  function commonKeyword(keyword) {
    return COMMON_KEYWORDS.includes(keyword.toLowerCase());
  }

  var version$3 = "10.1.2";
  /*
  Syntax highlighting with language autodetection.
  https://highlightjs.org/
  */

  const escape$1 = escapeHTML;
  const inherit$1 = inherit;
  const {
    nodeStream: nodeStream$1,
    mergeStreams: mergeStreams$1
  } = utils;
  const NO_MATCH = Symbol("nomatch");
  /**
   * @param {any} hljs - object that is extended (legacy)
   * @returns {HLJSApi}
   */

  const HLJS = function (hljs) {
    // Convenience variables for build-in objects

    /** @type {unknown[]} */
    var ArrayProto = []; // Global internal variables used within the highlight.js library.

    /** @type {Record<string, Language>} */

    var languages = Object.create(null);
    /** @type {Record<string, string>} */

    var aliases = Object.create(null);
    /** @type {HLJSPlugin[]} */

    var plugins = []; // safe/production mode - swallows more errors, tries to keep running
    // even if a single syntax or parse hits a fatal error

    var SAFE_MODE = true;
    var fixMarkupRe = /(^(<[^>]+>|\t|)+|\n)/gm;
    var LANGUAGE_NOT_FOUND = "Could not find the language '{}', did you forget to load/include a language module?";
    /** @type {Language} */

    const PLAINTEXT_LANGUAGE = {
      disableAutodetect: true,
      name: 'Plain text',
      contains: []
    }; // Global options used when within external APIs. This is modified when
    // calling the `hljs.configure` function.

    /** @type HLJSOptions */

    var options = {
      noHighlightRe: /^(no-?highlight)$/i,
      languageDetectRe: /\blang(?:uage)?-([\w-]+)\b/i,
      classPrefix: 'hljs-',
      tabReplace: null,
      useBR: false,
      languages: null,
      // beta configuration options, subject to change, welcome to discuss
      // https://github.com/highlightjs/highlight.js/issues/1086
      __emitter: TokenTreeEmitter
    };
    /* Utility functions */

    /**
     * Tests a language name to see if highlighting should be skipped
     * @param {string} languageName
     */

    function shouldNotHighlight(languageName) {
      return options.noHighlightRe.test(languageName);
    }
    /**
     * @param {HighlightedHTMLElement} block - the HTML element to determine language for
     */


    function blockLanguage(block) {
      var classes = block.className + ' ';
      classes += block.parentNode ? block.parentNode.className : ''; // language-* takes precedence over non-prefixed class names.

      const match = options.languageDetectRe.exec(classes);

      if (match) {
        var language = getLanguage(match[1]);

        if (!language) {
          console.warn(LANGUAGE_NOT_FOUND.replace("{}", match[1]));
          console.warn("Falling back to no-highlight mode for this block.", block);
        }

        return language ? match[1] : 'no-highlight';
      }

      return classes.split(/\s+/).find(_class => shouldNotHighlight(_class) || getLanguage(_class));
    }
    /**
     * Core highlighting function.
     *
     * @param {string} languageName - the language to use for highlighting
     * @param {string} code - the code to highlight
     * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
     * @param {Mode} [continuation] - current continuation mode, if any
     *
     * @returns {HighlightResult} Result - an object that represents the result
     * @property {string} language - the language name
     * @property {number} relevance - the relevance score
     * @property {string} value - the highlighted HTML code
     * @property {string} code - the original raw code
     * @property {Mode} top - top of the current mode stack
     * @property {boolean} illegal - indicates whether any illegal matches were found
    */


    function highlight(languageName, code, ignoreIllegals, continuation) {
      /** @type {{ code: string, language: string, result?: any }} */
      var context = {
        code,
        language: languageName
      }; // the plugin can change the desired language or the code to be highlighted
      // just be changing the object it was passed

      fire("before:highlight", context); // a before plugin can usurp the result completely by providing it's own
      // in which case we don't even need to call highlight

      var result = context.result ? context.result : _highlight(context.language, context.code, ignoreIllegals, continuation);
      result.code = context.code; // the plugin can change anything in result to suite it

      fire("after:highlight", result);
      return result;
    }
    /**
     * private highlight that's used internally and does not fire callbacks
     *
     * @param {string} languageName - the language to use for highlighting
     * @param {string} code - the code to highlight
     * @param {boolean} [ignoreIllegals] - whether to ignore illegal matches, default is to bail
     * @param {Mode} [continuation] - current continuation mode, if any
    */


    function _highlight(languageName, code, ignoreIllegals, continuation) {
      var codeToHighlight = code;
      /**
       * Return keyword data if a match is a keyword
       * @param {CompiledMode} mode - current mode
       * @param {RegExpMatchArray} match - regexp match data
       * @returns {KeywordData | false}
       */

      function keywordData(mode, match) {
        var matchText = language.case_insensitive ? match[0].toLowerCase() : match[0];
        return Object.prototype.hasOwnProperty.call(mode.keywords, matchText) && mode.keywords[matchText];
      }

      function processKeywords() {
        if (!top.keywords) {
          emitter.addText(mode_buffer);
          return;
        }

        let last_index = 0;
        top.keywordPatternRe.lastIndex = 0;
        let match = top.keywordPatternRe.exec(mode_buffer);
        let buf = "";

        while (match) {
          buf += mode_buffer.substring(last_index, match.index);
          const data = keywordData(top, match);

          if (data) {
            const [kind, keywordRelevance] = data;
            emitter.addText(buf);
            buf = "";
            relevance += keywordRelevance;
            emitter.addKeyword(match[0], kind);
          } else {
            buf += match[0];
          }

          last_index = top.keywordPatternRe.lastIndex;
          match = top.keywordPatternRe.exec(mode_buffer);
        }

        buf += mode_buffer.substr(last_index);
        emitter.addText(buf);
      }

      function processSubLanguage() {
        if (mode_buffer === "") return;
        /** @type HighlightResult */

        var result = null;

        if (typeof top.subLanguage === 'string') {
          if (!languages[top.subLanguage]) {
            emitter.addText(mode_buffer);
            return;
          }

          result = _highlight(top.subLanguage, mode_buffer, true, continuations[top.subLanguage]);
          continuations[top.subLanguage] = result.top;
        } else {
          result = highlightAuto(mode_buffer, top.subLanguage.length ? top.subLanguage : null);
        } // Counting embedded language score towards the host language may be disabled
        // with zeroing the containing mode relevance. Use case in point is Markdown that
        // allows XML everywhere and makes every XML snippet to have a much larger Markdown
        // score.


        if (top.relevance > 0) {
          relevance += result.relevance;
        }

        emitter.addSublanguage(result.emitter, result.language);
      }

      function processBuffer() {
        if (top.subLanguage != null) {
          processSubLanguage();
        } else {
          processKeywords();
        }

        mode_buffer = '';
      }
      /**
       * @param {Mode} mode - new mode to start
       */


      function startNewMode(mode) {
        if (mode.className) {
          emitter.openNode(mode.className);
        }

        top = Object.create(mode, {
          parent: {
            value: top
          }
        });
        return top;
      }
      /**
       * @param {CompiledMode } mode - the mode to potentially end
       * @param {RegExpMatchArray} match - the latest match
       * @param {string} matchPlusRemainder - match plus remainder of content
       * @returns {CompiledMode | void} - the next mode, or if void continue on in current mode
       */


      function endOfMode(mode, match, matchPlusRemainder) {
        let matched = startsWith(mode.endRe, matchPlusRemainder);

        if (matched) {
          if (mode["on:end"]) {
            const resp = new Response(mode);
            mode["on:end"](match, resp);
            if (resp.ignore) matched = false;
          }

          if (matched) {
            while (mode.endsParent && mode.parent) {
              mode = mode.parent;
            }

            return mode;
          }
        } // even if on:end fires an `ignore` it's still possible
        // that we might trigger the end node because of a parent mode


        if (mode.endsWithParent) {
          return endOfMode(mode.parent, match, matchPlusRemainder);
        }
      }
      /**
       * Handle matching but then ignoring a sequence of text
       *
       * @param {string} lexeme - string containing full match text
       */


      function doIgnore(lexeme) {
        if (top.matcher.regexIndex === 0) {
          // no more regexs to potentially match here, so we move the cursor forward one
          // space
          mode_buffer += lexeme[0];
          return 1;
        } else {
          // no need to move the cursor, we still have additional regexes to try and
          // match at this very spot
          continueScanAtSamePosition = true;
          return 0;
        }
      }
      /**
       * Handle the start of a new potential mode match
       *
       * @param {EnhancedMatch} match - the current match
       * @returns {number} how far to advance the parse cursor
       */


      function doBeginMatch(match) {
        var lexeme = match[0];
        var new_mode = match.rule;
        const resp = new Response(new_mode); // first internal before callbacks, then the public ones

        const beforeCallbacks = [new_mode.__beforeBegin, new_mode["on:begin"]];

        for (const cb of beforeCallbacks) {
          if (!cb) continue;
          cb(match, resp);
          if (resp.ignore) return doIgnore(lexeme);
        }

        if (new_mode && new_mode.endSameAsBegin) {
          new_mode.endRe = escape(lexeme);
        }

        if (new_mode.skip) {
          mode_buffer += lexeme;
        } else {
          if (new_mode.excludeBegin) {
            mode_buffer += lexeme;
          }

          processBuffer();

          if (!new_mode.returnBegin && !new_mode.excludeBegin) {
            mode_buffer = lexeme;
          }
        }

        startNewMode(new_mode); // if (mode["after:begin"]) {
        //   let resp = new Response(mode);
        //   mode["after:begin"](match, resp);
        // }

        return new_mode.returnBegin ? 0 : lexeme.length;
      }
      /**
       * Handle the potential end of mode
       *
       * @param {RegExpMatchArray} match - the current match
       */


      function doEndMatch(match) {
        var lexeme = match[0];
        var matchPlusRemainder = codeToHighlight.substr(match.index);
        var end_mode = endOfMode(top, match, matchPlusRemainder);

        if (!end_mode) {
          return NO_MATCH;
        }

        var origin = top;

        if (origin.skip) {
          mode_buffer += lexeme;
        } else {
          if (!(origin.returnEnd || origin.excludeEnd)) {
            mode_buffer += lexeme;
          }

          processBuffer();

          if (origin.excludeEnd) {
            mode_buffer = lexeme;
          }
        }

        do {
          if (top.className) {
            emitter.closeNode();
          }

          if (!top.skip && !top.subLanguage) {
            relevance += top.relevance;
          }

          top = top.parent;
        } while (top !== end_mode.parent);

        if (end_mode.starts) {
          if (end_mode.endSameAsBegin) {
            end_mode.starts.endRe = end_mode.endRe;
          }

          startNewMode(end_mode.starts);
        }

        return origin.returnEnd ? 0 : lexeme.length;
      }

      function processContinuations() {
        var list = [];

        for (var current = top; current !== language; current = current.parent) {
          if (current.className) {
            list.unshift(current.className);
          }
        }

        list.forEach(item => emitter.openNode(item));
      }
      /** @type {{type?: MatchType, index?: number, rule?: Mode}}} */


      var lastMatch = {};
      /**
       *  Process an individual match
       *
       * @param {string} textBeforeMatch - text preceeding the match (since the last match)
       * @param {EnhancedMatch} [match] - the match itself
       */

      function processLexeme(textBeforeMatch, match) {
        var lexeme = match && match[0]; // add non-matched text to the current mode buffer

        mode_buffer += textBeforeMatch;

        if (lexeme == null) {
          processBuffer();
          return 0;
        } // we've found a 0 width match and we're stuck, so we need to advance
        // this happens when we have badly behaved rules that have optional matchers to the degree that
        // sometimes they can end up matching nothing at all
        // Ref: https://github.com/highlightjs/highlight.js/issues/2140


        if (lastMatch.type === "begin" && match.type === "end" && lastMatch.index === match.index && lexeme === "") {
          // spit the "skipped" character that our regex choked on back into the output sequence
          mode_buffer += codeToHighlight.slice(match.index, match.index + 1);

          if (!SAFE_MODE) {
            /** @type {AnnotatedError} */
            const err = new Error('0 width match regex');
            err.languageName = languageName;
            err.badRule = lastMatch.rule;
            throw err;
          }

          return 1;
        }

        lastMatch = match;

        if (match.type === "begin") {
          return doBeginMatch(match);
        } else if (match.type === "illegal" && !ignoreIllegals) {
          // illegal match, we do not continue processing

          /** @type {AnnotatedError} */
          const err = new Error('Illegal lexeme "' + lexeme + '" for mode "' + (top.className || '<unnamed>') + '"');
          err.mode = top;
          throw err;
        } else if (match.type === "end") {
          var processed = doEndMatch(match);

          if (processed !== NO_MATCH) {
            return processed;
          }
        } // edge case for when illegal matches $ (end of line) which is technically
        // a 0 width match but not a begin/end match so it's not caught by the
        // first handler (when ignoreIllegals is true)


        if (match.type === "illegal" && lexeme === "") {
          // advance so we aren't stuck in an infinite loop
          return 1;
        } // infinite loops are BAD, this is a last ditch catch all. if we have a
        // decent number of iterations yet our index (cursor position in our
        // parsing) still 3x behind our index then something is very wrong
        // so we bail


        if (iterations > 100000 && iterations > match.index * 3) {
          const err = new Error('potential infinite loop, way more iterations than matches');
          throw err;
        }
        /*
        Why might be find ourselves here?  Only one occasion now.  An end match that was
        triggered but could not be completed.  When might this happen?  When an `endSameasBegin`
        rule sets the end rule to a specific match.  Since the overall mode termination rule that's
        being used to scan the text isn't recompiled that means that any match that LOOKS like
        the end (but is not, because it is not an exact match to the beginning) will
        end up here.  A definite end match, but when `doEndMatch` tries to "reapply"
        the end rule and fails to match, we wind up here, and just silently ignore the end.
         This causes no real harm other than stopping a few times too many.
        */


        mode_buffer += lexeme;
        return lexeme.length;
      }

      var language = getLanguage(languageName);

      if (!language) {
        console.error(LANGUAGE_NOT_FOUND.replace("{}", languageName));
        throw new Error('Unknown language: "' + languageName + '"');
      }

      var md = compileLanguage(language);
      var result = '';
      /** @type {CompiledMode} */

      var top = continuation || md;
      /** @type Record<string,Mode> */

      var continuations = {}; // keep continuations for sub-languages

      var emitter = new options.__emitter(options);
      processContinuations();
      var mode_buffer = '';
      var relevance = 0;
      var index = 0;
      var iterations = 0;
      var continueScanAtSamePosition = false;

      try {
        top.matcher.considerAll();

        for (;;) {
          iterations++;

          if (continueScanAtSamePosition) {
            // only regexes not matched previously will now be
            // considered for a potential match
            continueScanAtSamePosition = false;
          } else {
            top.matcher.lastIndex = index;
            top.matcher.considerAll();
          }

          const match = top.matcher.exec(codeToHighlight); // console.log("match", match[0], match.rule && match.rule.begin)

          if (!match) break;
          const beforeMatch = codeToHighlight.substring(index, match.index);
          const processedCount = processLexeme(beforeMatch, match);
          index = match.index + processedCount;
        }

        processLexeme(codeToHighlight.substr(index));
        emitter.closeAllNodes();
        emitter.finalize();
        result = emitter.toHTML();
        return {
          relevance: relevance,
          value: result,
          language: languageName,
          illegal: false,
          emitter: emitter,
          top: top
        };
      } catch (err) {
        if (err.message && err.message.includes('Illegal')) {
          return {
            illegal: true,
            illegalBy: {
              msg: err.message,
              context: codeToHighlight.slice(index - 100, index + 100),
              mode: err.mode
            },
            sofar: result,
            relevance: 0,
            value: escape$1(codeToHighlight),
            emitter: emitter
          };
        } else if (SAFE_MODE) {
          return {
            illegal: false,
            relevance: 0,
            value: escape$1(codeToHighlight),
            emitter: emitter,
            language: languageName,
            top: top,
            errorRaised: err
          };
        } else {
          throw err;
        }
      }
    }
    /**
     * returns a valid highlight result, without actually doing any actual work,
     * auto highlight starts with this and it's possible for small snippets that
     * auto-detection may not find a better match
     * @param {string} code
     * @returns {HighlightResult}
     */


    function justTextHighlightResult(code) {
      const result = {
        relevance: 0,
        emitter: new options.__emitter(options),
        value: escape$1(code),
        illegal: false,
        top: PLAINTEXT_LANGUAGE
      };
      result.emitter.addText(code);
      return result;
    }
    /**
    Highlighting with language detection. Accepts a string with the code to
    highlight. Returns an object with the following properties:
     - language (detected language)
    - relevance (int)
    - value (an HTML string with highlighting markup)
    - second_best (object with the same structure for second-best heuristically
      detected language, may be absent)
       @param {string} code
      @param {Array<string>} [languageSubset]
      @returns {AutoHighlightResult}
    */


    function highlightAuto(code, languageSubset) {
      languageSubset = languageSubset || options.languages || Object.keys(languages);
      var result = justTextHighlightResult(code);
      var secondBest = result;
      languageSubset.filter(getLanguage).filter(autoDetection).forEach(function (name) {
        var current = _highlight(name, code, false);

        current.language = name;

        if (current.relevance > secondBest.relevance) {
          secondBest = current;
        }

        if (current.relevance > result.relevance) {
          secondBest = result;
          result = current;
        }
      });

      if (secondBest.language) {
        // second_best (with underscore) is the expected API
        result.second_best = secondBest;
      }

      return result;
    }
    /**
    Post-processing of the highlighted markup:
     - replace TABs with something more useful
    - replace real line-breaks with '<br>' for non-pre containers
       @param {string} html
      @returns {string}
    */


    function fixMarkup(html) {
      if (!(options.tabReplace || options.useBR)) {
        return html;
      }

      return html.replace(fixMarkupRe, match => {
        if (match === '\n') {
          return options.useBR ? '<br>' : match;
        } else if (options.tabReplace) {
          return match.replace(/\t/g, options.tabReplace);
        }

        return match;
      });
    }
    /**
     * Builds new class name for block given the language name
     *
     * @param {string} prevClassName
     * @param {string} [currentLang]
     * @param {string} [resultLang]
     */


    function buildClassName(prevClassName, currentLang, resultLang) {
      var language = currentLang ? aliases[currentLang] : resultLang;
      var result = [prevClassName.trim()];

      if (!prevClassName.match(/\bhljs\b/)) {
        result.push('hljs');
      }

      if (!prevClassName.includes(language)) {
        result.push(language);
      }

      return result.join(' ').trim();
    }
    /**
     * Applies highlighting to a DOM node containing code. Accepts a DOM node and
     * two optional parameters for fixMarkup.
     *
     * @param {HighlightedHTMLElement} element - the HTML element to highlight
    */


    function highlightBlock(element) {
      /** @type HTMLElement */
      let node = null;
      const language = blockLanguage(element);
      if (shouldNotHighlight(language)) return;
      fire("before:highlightBlock", {
        block: element,
        language: language
      });

      if (options.useBR) {
        node = document.createElement('div');
        node.innerHTML = element.innerHTML.replace(/\n/g, '').replace(/<br[ /]*>/g, '\n');
      } else {
        node = element;
      }

      const text = node.textContent;
      const result = language ? highlight(language, text, true) : highlightAuto(text);
      const originalStream = nodeStream$1(node);

      if (originalStream.length) {
        const resultNode = document.createElement('div');
        resultNode.innerHTML = result.value;
        result.value = mergeStreams$1(originalStream, nodeStream$1(resultNode), text);
      }

      result.value = fixMarkup(result.value);
      fire("after:highlightBlock", {
        block: element,
        result: result
      });
      element.innerHTML = result.value;
      element.className = buildClassName(element.className, language, result.language);
      element.result = {
        language: result.language,
        // TODO: remove with version 11.0
        re: result.relevance,
        relavance: result.relevance
      };

      if (result.second_best) {
        element.second_best = {
          language: result.second_best.language,
          // TODO: remove with version 11.0
          re: result.second_best.relevance,
          relavance: result.second_best.relevance
        };
      }
    }
    /**
     * Updates highlight.js global options with the passed options
     *
     * @param {{}} userOptions
     */


    function configure(userOptions) {
      options = inherit$1(options, userOptions);
    }
    /**
     * Highlights to all <pre><code> blocks on a page
     *
     * @type {Function & {called?: boolean}}
     */


    const initHighlighting = () => {
      if (initHighlighting.called) return;
      initHighlighting.called = true;
      var blocks = document.querySelectorAll('pre code');
      ArrayProto.forEach.call(blocks, highlightBlock);
    }; // Higlights all when DOMContentLoaded fires


    function initHighlightingOnLoad() {
      // @ts-ignore
      window.addEventListener('DOMContentLoaded', initHighlighting, false);
    }
    /**
     * Register a language grammar module
     *
     * @param {string} languageName
     * @param {LanguageFn} languageDefinition
     */


    function registerLanguage(languageName, languageDefinition) {
      var lang = null;

      try {
        lang = languageDefinition(hljs);
      } catch (error) {
        console.error("Language definition for '{}' could not be registered.".replace("{}", languageName)); // hard or soft error

        if (!SAFE_MODE) {
          throw error;
        } else {
          console.error(error);
        } // languages that have serious errors are replaced with essentially a
        // "plaintext" stand-in so that the code blocks will still get normal
        // css classes applied to them - and one bad language won't break the
        // entire highlighter


        lang = PLAINTEXT_LANGUAGE;
      } // give it a temporary name if it doesn't have one in the meta-data


      if (!lang.name) lang.name = languageName;
      languages[languageName] = lang;
      lang.rawDefinition = languageDefinition.bind(null, hljs);

      if (lang.aliases) {
        registerAliases(lang.aliases, {
          languageName
        });
      }
    }
    /**
     * @returns {string[]} List of language internal names
     */


    function listLanguages() {
      return Object.keys(languages);
    }
    /**
      intended usage: When one language truly requires another
       Unlike `getLanguage`, this will throw when the requested language
      is not available.
       @param {string} name - name of the language to fetch/require
      @returns {Language | never}
    */


    function requireLanguage(name) {
      var lang = getLanguage(name);

      if (lang) {
        return lang;
      }

      var err = new Error('The \'{}\' language is required, but not loaded.'.replace('{}', name));
      throw err;
    }
    /**
     * @param {string} name - name of the language to retrieve
     * @returns {Language | undefined}
     */


    function getLanguage(name) {
      name = (name || '').toLowerCase();
      return languages[name] || languages[aliases[name]];
    }
    /**
     *
     * @param {string|string[]} aliasList - single alias or list of aliases
     * @param {{languageName: string}} opts
     */


    function registerAliases(aliasList, {
      languageName
    }) {
      if (typeof aliasList === 'string') {
        aliasList = [aliasList];
      }

      aliasList.forEach(alias => {
        aliases[alias] = languageName;
      });
    }
    /**
     * Determines if a given language has auto-detection enabled
     * @param {string} name - name of the language
     */


    function autoDetection(name) {
      var lang = getLanguage(name);
      return lang && !lang.disableAutodetect;
    }
    /**
     * @param {HLJSPlugin} plugin
     */


    function addPlugin(plugin) {
      plugins.push(plugin);
    }
    /**
     *
     * @param {PluginEvent} event
     * @param {any} args
     */


    function fire(event, args) {
      var cb = event;
      plugins.forEach(function (plugin) {
        if (plugin[cb]) {
          plugin[cb](args);
        }
      });
    }
    /* Interface definition */


    Object.assign(hljs, {
      highlight,
      highlightAuto,
      fixMarkup,
      highlightBlock,
      configure,
      initHighlighting,
      initHighlightingOnLoad,
      registerLanguage,
      listLanguages,
      getLanguage,
      registerAliases,
      requireLanguage,
      autoDetection,
      inherit: inherit$1,
      addPlugin
    });

    hljs.debugMode = function () {
      SAFE_MODE = false;
    };

    hljs.safeMode = function () {
      SAFE_MODE = true;
    };

    hljs.versionString = version$3;

    for (const key in MODES) {
      // @ts-ignore
      if (typeof MODES[key] === "object") {
        // @ts-ignore
        deepFreeze(MODES[key]);
      }
    } // merge all the modes/regexs into our main object


    Object.assign(hljs, MODES);
    return hljs;
  }; // export an "instance" of the highlighter


  var highlight = HLJS({});
  var core = highlight;

  /*
  Language: YAML
  Description: Yet Another Markdown Language
  Author: Stefan Wienert <stwienert@gmail.com>
  Contributors: Carl Baxter <carl@cbax.tech>
  Requires: ruby.js
  Website: https://yaml.org
  Category: common, config
  */
  function yaml(hljs) {
    var LITERALS = 'true false yes no null'; // YAML spec allows non-reserved URI characters in tags.

    var URI_CHARACTERS = '[\\w#;/?:@&=+$,.~*\\\'()[\\]]+'; // Define keys as starting with a word character
    // ...containing word chars, spaces, colons, forward-slashes, hyphens and periods
    // ...and ending with a colon followed immediately by a space, tab or newline.
    // The YAML spec allows for much more than this, but this covers most use-cases.

    var KEY = {
      className: 'attr',
      variants: [{
        begin: '\\w[\\w :\\/.-]*:(?=[ \t]|$)'
      }, {
        begin: '"\\w[\\w :\\/.-]*":(?=[ \t]|$)'
      }, // double quoted keys
      {
        begin: '\'\\w[\\w :\\/.-]*\':(?=[ \t]|$)'
      } // single quoted keys
      ]
    };
    var TEMPLATE_VARIABLES = {
      className: 'template-variable',
      variants: [{
        begin: '{{',
        end: '}}'
      }, // jinja templates Ansible
      {
        begin: '%{',
        end: '}'
      } // Ruby i18n
      ]
    };
    var STRING = {
      className: 'string',
      relevance: 0,
      variants: [{
        begin: /'/,
        end: /'/
      }, {
        begin: /"/,
        end: /"/
      }, {
        begin: /\S+/
      }],
      contains: [hljs.BACKSLASH_ESCAPE, TEMPLATE_VARIABLES]
    }; // Strings inside of value containers (objects) can't contain braces,
    // brackets, or commas

    var CONTAINER_STRING = hljs.inherit(STRING, {
      variants: [{
        begin: /'/,
        end: /'/
      }, {
        begin: /"/,
        end: /"/
      }, {
        begin: /[^\s,{}[\]]+/
      }]
    });
    var DATE_RE = '[0-9]{4}(-[0-9][0-9]){0,2}';
    var TIME_RE = '([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?';
    var FRACTION_RE = '(\\.[0-9]*)?';
    var ZONE_RE = '([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?';
    var TIMESTAMP = {
      className: 'number',
      begin: '\\b' + DATE_RE + TIME_RE + FRACTION_RE + ZONE_RE + '\\b'
    };
    var VALUE_CONTAINER = {
      end: ',',
      endsWithParent: true,
      excludeEnd: true,
      contains: [],
      keywords: LITERALS,
      relevance: 0
    };
    var OBJECT = {
      begin: '{',
      end: '}',
      contains: [VALUE_CONTAINER],
      illegal: '\\n',
      relevance: 0
    };
    var ARRAY = {
      begin: '\\[',
      end: '\\]',
      contains: [VALUE_CONTAINER],
      illegal: '\\n',
      relevance: 0
    };
    var MODES = [KEY, {
      className: 'meta',
      begin: '^---\s*$',
      relevance: 10
    }, {
      // multi line string
      // Blocks start with a | or > followed by a newline
      //
      // Indentation of subsequent lines must be the same to
      // be considered part of the block
      className: 'string',
      begin: '[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*'
    }, {
      // Ruby/Rails erb
      begin: '<%[%=-]?',
      end: '[%-]?%>',
      subLanguage: 'ruby',
      excludeBegin: true,
      excludeEnd: true,
      relevance: 0
    }, {
      // named tags
      className: 'type',
      begin: '!\\w+!' + URI_CHARACTERS
    }, // https://yaml.org/spec/1.2/spec.html#id2784064
    {
      // verbatim tags
      className: 'type',
      begin: '!<' + URI_CHARACTERS + ">"
    }, {
      // primary tags
      className: 'type',
      begin: '!' + URI_CHARACTERS
    }, {
      // secondary tags
      className: 'type',
      begin: '!!' + URI_CHARACTERS
    }, {
      // fragment id &ref
      className: 'meta',
      begin: '&' + hljs.UNDERSCORE_IDENT_RE + '$'
    }, {
      // fragment reference *ref
      className: 'meta',
      begin: '\\*' + hljs.UNDERSCORE_IDENT_RE + '$'
    }, {
      // array listing
      className: 'bullet',
      // TODO: remove |$ hack when we have proper look-ahead support
      begin: '\\-(?=[ ]|$)',
      relevance: 0
    }, hljs.HASH_COMMENT_MODE, {
      beginKeywords: LITERALS,
      keywords: {
        literal: LITERALS
      }
    }, TIMESTAMP, // numbers are any valid C-style number that
    // sit isolated from other words
    {
      className: 'number',
      begin: hljs.C_NUMBER_RE + '\\b'
    }, OBJECT, ARRAY, STRING];
    var VALUE_MODES = [...MODES];
    VALUE_MODES.pop();
    VALUE_MODES.push(CONTAINER_STRING);
    VALUE_CONTAINER.contains = VALUE_MODES;
    return {
      name: 'YAML',
      case_insensitive: true,
      aliases: ['yml', 'YAML'],
      contains: MODES
    };
  }

  var yaml_1 = yaml;

  const IDENT_RE$1 = '[A-Za-z$_][0-9A-Za-z$_]*';
  const KEYWORDS = ["as", // for exports
  "in", "of", "if", "for", "while", "finally", "var", "new", "function", "do", "return", "void", "else", "break", "catch", "instanceof", "with", "throw", "case", "default", "try", "switch", "continue", "typeof", "delete", "let", "yield", "const", "class", // JS handles these with a special rule
  // "get",
  // "set",
  "debugger", "async", "await", "static", "import", "from", "export", "extends"];
  const LITERALS = ["true", "false", "null", "undefined", "NaN", "Infinity"];
  const TYPES$1 = ["Intl", "DataView", "Number", "Math", "Date", "String", "RegExp", "Object", "Function", "Boolean", "Error", "Symbol", "Set", "Map", "WeakSet", "WeakMap", "Proxy", "Reflect", "JSON", "Promise", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "Uint16Array", "Uint32Array", "Float32Array", "Array", "Uint8Array", "Uint8ClampedArray", "ArrayBuffer"];
  const ERROR_TYPES = ["EvalError", "InternalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"];
  const BUILT_IN_GLOBALS = ["setInterval", "setTimeout", "clearInterval", "clearTimeout", "require", "exports", "eval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "unescape"];
  const BUILT_IN_VARIABLES = ["arguments", "this", "super", "console", "window", "document", "localStorage", "module", "global" // Node.js
  ];
  const BUILT_INS = [].concat(BUILT_IN_GLOBALS, BUILT_IN_VARIABLES, TYPES$1, ERROR_TYPES);
  /**
   * @param {string} value
   * @returns {RegExp}
   * */

  /**
   * @param {RegExp | string } re
   * @returns {string}
   */

  function source$1(re) {
    if (!re) return null;
    if (typeof re === "string") return re;
    return re.source;
  }
  /**
   * @param {RegExp | string } re
   * @returns {string}
   */


  function lookahead(re) {
    return concat$1('(?=', re, ')');
  }
  /**
   * @param {...(RegExp | string) } args
   * @returns {string}
   */


  function concat$1(...args) {
    const joined = args.map(x => source$1(x)).join("");
    return joined;
  }
  /*
  Language: JavaScript
  Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.
  Category: common, scripting
  Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript
  */


  function javascript(hljs) {
    var IDENT_RE$1$1 = IDENT_RE$1;
    var FRAGMENT = {
      begin: '<>',
      end: '</>'
    };
    var XML_TAG = {
      begin: /<[A-Za-z0-9\\._:-]+/,
      end: /\/[A-Za-z0-9\\._:-]+>|\/>/
    };
    var KEYWORDS$1 = {
      $pattern: IDENT_RE$1,
      keyword: KEYWORDS.join(" "),
      literal: LITERALS.join(" "),
      built_in: BUILT_INS.join(" ")
    };
    var NUMBER = {
      className: 'number',
      variants: [{
        begin: '\\b(0[bB][01]+)n?'
      }, {
        begin: '\\b(0[oO][0-7]+)n?'
      }, {
        begin: hljs.C_NUMBER_RE + 'n?'
      }],
      relevance: 0
    };
    var SUBST = {
      className: 'subst',
      begin: '\\$\\{',
      end: '\\}',
      keywords: KEYWORDS$1,
      contains: [] // defined later

    };
    var HTML_TEMPLATE = {
      begin: 'html`',
      end: '',
      starts: {
        end: '`',
        returnEnd: false,
        contains: [hljs.BACKSLASH_ESCAPE, SUBST],
        subLanguage: 'xml'
      }
    };
    var CSS_TEMPLATE = {
      begin: 'css`',
      end: '',
      starts: {
        end: '`',
        returnEnd: false,
        contains: [hljs.BACKSLASH_ESCAPE, SUBST],
        subLanguage: 'css'
      }
    };
    var TEMPLATE_STRING = {
      className: 'string',
      begin: '`',
      end: '`',
      contains: [hljs.BACKSLASH_ESCAPE, SUBST]
    };
    SUBST.contains = [hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, TEMPLATE_STRING, NUMBER, hljs.REGEXP_MODE];
    var PARAMS_CONTAINS = SUBST.contains.concat([// eat recursive parens in sub expressions
    {
      begin: /\(/,
      end: /\)/,
      contains: ["self"].concat(SUBST.contains, [hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE])
    }, hljs.C_BLOCK_COMMENT_MODE, hljs.C_LINE_COMMENT_MODE]);
    var PARAMS = {
      className: 'params',
      begin: /\(/,
      end: /\)/,
      excludeBegin: true,
      excludeEnd: true,
      contains: PARAMS_CONTAINS
    };
    return {
      name: 'JavaScript',
      aliases: ['js', 'jsx', 'mjs', 'cjs'],
      keywords: KEYWORDS$1,
      contains: [hljs.SHEBANG({
        binary: "node",
        relevance: 5
      }), {
        className: 'meta',
        relevance: 10,
        begin: /^\s*['"]use (strict|asm)['"]/
      }, hljs.APOS_STRING_MODE, hljs.QUOTE_STRING_MODE, HTML_TEMPLATE, CSS_TEMPLATE, TEMPLATE_STRING, hljs.C_LINE_COMMENT_MODE, hljs.COMMENT('/\\*\\*', '\\*/', {
        relevance: 0,
        contains: [{
          className: 'doctag',
          begin: '@[A-Za-z]+',
          contains: [{
            className: 'type',
            begin: '\\{',
            end: '\\}',
            relevance: 0
          }, {
            className: 'variable',
            begin: IDENT_RE$1$1 + '(?=\\s*(-)|$)',
            endsParent: true,
            relevance: 0
          }, // eat spaces (not newlines) so we can find
          // types or variables
          {
            begin: /(?=[^\n])\s/,
            relevance: 0
          }]
        }]
      }), hljs.C_BLOCK_COMMENT_MODE, NUMBER, {
        // object attr container
        begin: concat$1(/[{,\n]\s*/, // we need to look ahead to make sure that we actually have an
        // attribute coming up so we don't steal a comma from a potential
        // "value" container
        //
        // NOTE: this might not work how you think.  We don't actually always
        // enter this mode and stay.  Instead it might merely match `,
        // <comments up next>` and then immediately end after the , because it
        // fails to find any actual attrs. But this still does the job because
        // it prevents the value contain rule from grabbing this instead and
        // prevening this rule from firing when we actually DO have keys.
        lookahead(concat$1( // we also need to allow for multiple possible comments inbetween
        // the first key:value pairing
        /(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/, IDENT_RE$1$1 + '\\s*:'))),
        relevance: 0,
        contains: [{
          className: 'attr',
          begin: IDENT_RE$1$1 + lookahead('\\s*:'),
          relevance: 0
        }]
      }, {
        // "value" container
        begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
        keywords: 'return throw case',
        contains: [hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE, hljs.REGEXP_MODE, {
          className: 'function',
          // we have to count the parens to make sure we actually have the
          // correct bounding ( ) before the =>.  There could be any number of
          // sub-expressions inside also surrounded by parens.
          begin: '(\\([^(]*' + '(\\([^(]*' + '(\\([^(]*' + '\\))?' + '\\))?' + '\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>',
          returnBegin: true,
          end: '\\s*=>',
          contains: [{
            className: 'params',
            variants: [{
              begin: hljs.UNDERSCORE_IDENT_RE
            }, {
              className: null,
              begin: /\(\s*\)/,
              skip: true
            }, {
              begin: /\(/,
              end: /\)/,
              excludeBegin: true,
              excludeEnd: true,
              keywords: KEYWORDS$1,
              contains: PARAMS_CONTAINS
            }]
          }]
        }, {
          // could be a comma delimited list of params to a function call
          begin: /,/,
          relevance: 0
        }, {
          className: '',
          begin: /\s/,
          end: /\s*/,
          skip: true
        }, {
          // JSX
          variants: [{
            begin: FRAGMENT.begin,
            end: FRAGMENT.end
          }, {
            begin: XML_TAG.begin,
            end: XML_TAG.end
          }],
          subLanguage: 'xml',
          contains: [{
            begin: XML_TAG.begin,
            end: XML_TAG.end,
            skip: true,
            contains: ['self']
          }]
        }],
        relevance: 0
      }, {
        className: 'function',
        beginKeywords: 'function',
        end: /\{/,
        excludeEnd: true,
        contains: [hljs.inherit(hljs.TITLE_MODE, {
          begin: IDENT_RE$1$1
        }), PARAMS],
        illegal: /\[|%/
      }, {
        begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`

      }, hljs.METHOD_GUARD, {
        // ES6 class
        className: 'class',
        beginKeywords: 'class',
        end: /[{;=]/,
        excludeEnd: true,
        illegal: /[:"\[\]]/,
        contains: [{
          beginKeywords: 'extends'
        }, hljs.UNDERSCORE_TITLE_MODE]
      }, {
        beginKeywords: 'constructor',
        end: /\{/,
        excludeEnd: true
      }, {
        begin: '(get|set)\\s+(?=' + IDENT_RE$1$1 + '\\()',
        end: /{/,
        keywords: "get set",
        contains: [hljs.inherit(hljs.TITLE_MODE, {
          begin: IDENT_RE$1$1
        }), {
          begin: /\(\)/
        }, // eat to avoid empty params
        PARAMS]
      }],
      illegal: /#(?!!)/
    };
  }

  var javascript_1 = javascript;

  /*
  Language: JSON
  Description: JSON (JavaScript Object Notation) is a lightweight data-interchange format.
  Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
  Website: http://www.json.org
  Category: common, protocols
  */
  function json(hljs) {
    var LITERALS = {
      literal: 'true false null'
    };
    var ALLOWED_COMMENTS = [hljs.C_LINE_COMMENT_MODE, hljs.C_BLOCK_COMMENT_MODE];
    var TYPES = [hljs.QUOTE_STRING_MODE, hljs.C_NUMBER_MODE];
    var VALUE_CONTAINER = {
      end: ',',
      endsWithParent: true,
      excludeEnd: true,
      contains: TYPES,
      keywords: LITERALS
    };
    var OBJECT = {
      begin: '{',
      end: '}',
      contains: [{
        className: 'attr',
        begin: /"/,
        end: /"/,
        contains: [hljs.BACKSLASH_ESCAPE],
        illegal: '\\n'
      }, hljs.inherit(VALUE_CONTAINER, {
        begin: /:/
      })].concat(ALLOWED_COMMENTS),
      illegal: '\\S'
    };
    var ARRAY = {
      begin: '\\[',
      end: '\\]',
      contains: [hljs.inherit(VALUE_CONTAINER)],
      // inherit is a workaround for a bug that makes shared modes with endsWithParent compile only the ending of one of the parents
      illegal: '\\S'
    };
    TYPES.push(OBJECT, ARRAY);
    ALLOWED_COMMENTS.forEach(function (rule) {
      TYPES.push(rule);
    });
    return {
      name: 'JSON',
      contains: TYPES,
      keywords: LITERALS,
      illegal: '\\S'
    };
  }

  var json_1 = json;

  // from https://raw.githubusercontent.com/highlightjs/highlight.js/master/src/styles/github.css
  const HLJS$1 = `
  /*

github.com style (c) Vasily Polovnyov <vast@whiteants.net>

*/

  .hljs {
    display: block;
    overflow-x: auto;
    padding: 0.5em;
    color: #333;
    background: #f8f8f8;
  }

  .hljs-comment,
  .hljs-quote {
    color: #998;
    font-style: italic;
  }

  .hljs-keyword,
  .hljs-selector-tag,
  .hljs-subst {
    color: #333;
    font-weight: bold;
  }

  .hljs-number,
  .hljs-literal,
  .hljs-variable,
  .hljs-template-variable,
  .hljs-tag .hljs-attr {
    color: #008080;
  }

  .hljs-string,
  .hljs-doctag {
    color: #d14;
  }

  .hljs-title,
  .hljs-section,
  .hljs-selector-id {
    color: #900;
    font-weight: bold;
  }

  .hljs-subst {
    font-weight: normal;
  }

  .hljs-type,
  .hljs-class .hljs-title {
    color: #458;
    font-weight: bold;
  }

  .hljs-tag,
  .hljs-name,
  .hljs-attribute {
    color: #000080;
    font-weight: normal;
  }

  .hljs-regexp,
  .hljs-link {
    color: #009926;
  }

  .hljs-symbol,
  .hljs-bullet {
    color: #990073;
  }

  .hljs-built_in,
  .hljs-builtin-name {
    color: #0086b3;
  }

  .hljs-meta {
    color: #999;
    font-weight: bold;
  }

  .hljs-deletion {
    background: #fdd;
  }

  .hljs-addition {
    background: #dfd;
  }

  .hljs-emphasis {
    font-style: italic;
  }

  .hljs-strong {
    font-weight: bold;
  }
`; // From https://github.com/sindresorhus/github-markdown-css/blob/gh-pages/github-markdown.css

  const GFM1 = `
  @font-face {
    font-family: octicons-link;
    src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==)
      format("woff");
  }
`;
  const GFM2 = `
  .markdown-body .octicon {
    display: inline-block;
    fill: currentColor;
    vertical-align: text-bottom;
  }

  .markdown-body .anchor {
    float: left;
    line-height: 1;
    margin-left: -20px;
    padding-right: 4px;
  }

  .markdown-body .anchor:focus {
    outline: none;
  }

  .markdown-body h1 .octicon-link,
  .markdown-body h2 .octicon-link,
  .markdown-body h3 .octicon-link,
  .markdown-body h4 .octicon-link,
  .markdown-body h5 .octicon-link,
  .markdown-body h6 .octicon-link {
    color: #1b1f23;
    vertical-align: middle;
    visibility: hidden;
  }

  .markdown-body h1:hover .anchor,
  .markdown-body h2:hover .anchor,
  .markdown-body h3:hover .anchor,
  .markdown-body h4:hover .anchor,
  .markdown-body h5:hover .anchor,
  .markdown-body h6:hover .anchor {
    text-decoration: none;
  }

  .markdown-body h1:hover .anchor .octicon-link,
  .markdown-body h2:hover .anchor .octicon-link,
  .markdown-body h3:hover .anchor .octicon-link,
  .markdown-body h4:hover .anchor .octicon-link,
  .markdown-body h5:hover .anchor .octicon-link,
  .markdown-body h6:hover .anchor .octicon-link {
    visibility: visible;
  }
  .markdown-body {
    -ms-text-size-adjust: 100%;
    -webkit-text-size-adjust: 100%;
    line-height: 1.5;
    font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
      sans-serif, Apple Color Emoji, Segoe UI Emoji;
    font-size: 16px;
    line-height: 1.5;
    word-wrap: break-word;
  }

  .markdown-body .pl-c {
    color: #6a737d;
  }

  .markdown-body .pl-c1,
  .markdown-body .pl-s .pl-v {
    color: #005cc5;
  }

  .markdown-body .pl-e,
  .markdown-body .pl-en {
    color: #6f42c1;
  }

  .markdown-body .pl-s .pl-s1,
  .markdown-body .pl-smi {
    color: #24292e;
  }

  .markdown-body .pl-ent {
    color: #22863a;
  }

  .markdown-body .pl-k {
    color: #d73a49;
  }

  .markdown-body .pl-pds,
  .markdown-body .pl-s,
  .markdown-body .pl-s .pl-pse .pl-s1,
  .markdown-body .pl-sr,
  .markdown-body .pl-sr .pl-cce,
  .markdown-body .pl-sr .pl-sra,
  .markdown-body .pl-sr .pl-sre {
    color: #032f62;
  }

  .markdown-body .pl-smw,
  .markdown-body .pl-v {
    color: #e36209;
  }

  .markdown-body .pl-bu {
    color: #b31d28;
  }

  .markdown-body .pl-ii {
    color: #fafbfc;
    background-color: #b31d28;
  }

  .markdown-body .pl-c2 {
    color: #fafbfc;
    background-color: #d73a49;
  }

  .markdown-body .pl-c2:before {
    content: "^M";
  }

  .markdown-body .pl-sr .pl-cce {
    font-weight: 700;
    color: #22863a;
  }

  .markdown-body .pl-ml {
    color: #735c0f;
  }

  .markdown-body .pl-mh,
  .markdown-body .pl-mh .pl-en,
  .markdown-body .pl-ms {
    font-weight: 700;
    color: #005cc5;
  }

  .markdown-body .pl-mi {
    font-style: italic;
    color: #24292e;
  }

  .markdown-body .pl-mb {
    font-weight: 700;
    color: #24292e;
  }

  .markdown-body .pl-md {
    color: #b31d28;
    background-color: #ffeef0;
  }

  .markdown-body .pl-mi1 {
    color: #22863a;
    background-color: #f0fff4;
  }

  .markdown-body .pl-mc {
    color: #e36209;
    background-color: #ffebda;
  }

  .markdown-body .pl-mi2 {
    color: #f6f8fa;
    background-color: #005cc5;
  }

  .markdown-body .pl-mdr {
    font-weight: 700;
    color: #6f42c1;
  }

  .markdown-body .pl-ba {
    color: #586069;
  }

  .markdown-body .pl-sg {
    color: #959da5;
  }

  .markdown-body .pl-corl {
    text-decoration: underline;
    color: #032f62;
  }

  .markdown-body details {
    display: block;
  }

  .markdown-body summary {
    display: list-item;
  }

  .markdown-body a {
    background-color: initial;
  }

  .markdown-body a:active,
  .markdown-body a:hover {
    outline-width: 0;
  }

  .markdown-body strong {
    font-weight: inherit;
    font-weight: bolder;
  }

  .markdown-body h1 {
    font-size: 2em;
    margin: 0.67em 0;
  }

  .markdown-body img {
    border-style: none;
  }

  .markdown-body code,
  .markdown-body kbd,
  .markdown-body pre {
    font-family: monospace, monospace;
    font-size: 1em;
    background-color: var(--hcv-color-markdown-background);
  }

  .markdown-body hr {
    box-sizing: initial;
    height: 0;
    overflow: visible;
  }

  .markdown-body input {
    font: inherit;
    margin: 0;
  }

  .markdown-body input {
    overflow: visible;
  }

  .markdown-body [type="checkbox"] {
    box-sizing: border-box;
    padding: 0;
  }

  .markdown-body * {
    box-sizing: border-box;
  }

  .markdown-body input {
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
  }

  .markdown-body a {
    color: var(--link-text-color, var(--accent-color));
    font-weight: 600;
    text-decoration: none;
  }

  .markdown-body a:hover {
    text-decoration: underline;
  }

  .markdown-body strong {
    font-weight: 600;
  }

  .markdown-body hr {
    height: 0;
    margin: 15px 0;
    overflow: hidden;
    background: transparent;
    border: 0;
    border-bottom: 1px solid #dfe2e5;
  }

  .markdown-body hr:after,
  .markdown-body hr:before {
    display: table;
    content: "";
  }

  .markdown-body hr:after {
    clear: both;
  }

  .markdown-body table {
    border-spacing: 0;
    border-collapse: collapse;
  }

  .markdown-body td,
  .markdown-body th {
    padding: 0;
  }

  .markdown-body details summary {
    cursor: pointer;
  }

  .markdown-body kbd {
    display: inline-block;
    padding: 3px 5px;
    font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    line-height: 10px;
    color: #444d56;
    vertical-align: middle;
    background-color: var(--hcv-color-markdown-background);
    border: 1px solid #d1d5da;
    border-radius: 3px;
    box-shadow: inset 0 -1px 0 #d1d5da;
  }

  .markdown-body h1,
  .markdown-body h2,
  .markdown-body h3,
  .markdown-body h4,
  .markdown-body h5,
  .markdown-body h6 {
    margin-top: 0;
    margin-bottom: 0;
  }

  .markdown-body h1 {
    font-size: 32px;
  }

  .markdown-body h1,
  .markdown-body h2 {
    font-weight: 600;
  }

  .markdown-body h2 {
    font-size: 24px;
  }

  .markdown-body h3 {
    font-size: 20px;
  }

  .markdown-body h3,
  .markdown-body h4 {
    font-weight: 600;
  }

  .markdown-body h4 {
    font-size: 16px;
  }

  .markdown-body h5 {
    font-size: 14px;
  }

  .markdown-body h5,
  .markdown-body h6 {
    font-weight: 600;
  }

  .markdown-body h6 {
    font-size: 12px;
  }

  .markdown-body p {
    margin-top: 0;
    margin-bottom: 10px;
  }

  .markdown-body blockquote {
    margin: 0;
  }

  .markdown-body ol,
  .markdown-body ul {
    padding-left: 0;
    margin-top: 0;
    margin-bottom: 0;
  }

  .markdown-body ol ol,
  .markdown-body ul ol {
    list-style-type: lower-roman;
  }

  .markdown-body ol ol ol,
  .markdown-body ol ul ol,
  .markdown-body ul ol ol,
  .markdown-body ul ul ol {
    list-style-type: lower-alpha;
  }

  .markdown-body dd {
    margin-left: 0;
  }

  .markdown-body code,
  .markdown-body pre {
    font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    font-size: 12px;
  }

  .markdown-body pre {
    margin-top: 0;
    margin-bottom: 0;
  }

  .markdown-body input::-webkit-inner-spin-button,
  .markdown-body input::-webkit-outer-spin-button {
    margin: 0;
    -webkit-appearance: none;
    appearance: none;
  }

  .markdown-body .border {
    border: 1px solid #e1e4e8 !important;
  }

  .markdown-body .border-0 {
    border: 0 !important;
  }

  .markdown-body .border-bottom {
    border-bottom: 1px solid #e1e4e8 !important;
  }

  .markdown-body .rounded-1 {
    border-radius: 3px !important;
  }

  .markdown-body .bg-white {
    background-color: #fff !important;
  }

  .markdown-body .bg-gray-light {
    background-color: #fafbfc !important;
  }

  .markdown-body .text-gray-light {
    color: #6a737d !important;
  }

  .markdown-body .mb-0 {
    margin-bottom: 0 !important;
  }

  .markdown-body .my-2 {
    margin-top: 8px !important;
    margin-bottom: 8px !important;
  }

  .markdown-body .pl-0 {
    padding-left: 0 !important;
  }

  .markdown-body .py-0 {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }

  .markdown-body .pl-1 {
    padding-left: 4px !important;
  }

  .markdown-body .pl-2 {
    padding-left: 8px !important;
  }

  .markdown-body .py-2 {
    padding-top: 8px !important;
    padding-bottom: 8px !important;
  }

  .markdown-body .pl-3,
  .markdown-body .px-3 {
    padding-left: 16px !important;
  }

  .markdown-body .px-3 {
    padding-right: 16px !important;
  }

  .markdown-body .pl-4 {
    padding-left: 24px !important;
  }

  .markdown-body .pl-5 {
    padding-left: 32px !important;
  }

  .markdown-body .pl-6 {
    padding-left: 40px !important;
  }

  .markdown-body .f6 {
    font-size: 12px !important;
  }

  .markdown-body .lh-condensed {
    line-height: 1.25 !important;
  }

  .markdown-body .text-bold {
    font-weight: 600 !important;
  }

  .markdown-body .pl-7 {
    padding-left: 48px !important;
  }

  .markdown-body .pl-8 {
    padding-left: 64px !important;
  }

  .markdown-body .pl-9 {
    padding-left: 80px !important;
  }

  .markdown-body .pl-10 {
    padding-left: 96px !important;
  }

  .markdown-body .pl-11 {
    padding-left: 112px !important;
  }

  .markdown-body .pl-12 {
    padding-left: 128px !important;
  }

  .markdown-body hr {
    border-bottom-color: #eee;
  }

  .markdown-body kbd {
    display: inline-block;
    padding: 3px 5px;
    font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    line-height: 10px;
    color: #444d56;
    vertical-align: middle;
    background-color: var(--hcv-color-markdown-background);
    border: 1px solid #d1d5da;
    border-radius: 3px;
    box-shadow: inset 0 -1px 0 #d1d5da;
  }

  .markdown-body:after,
  .markdown-body:before {
    display: table;
    content: "";
  }

  .markdown-body:after {
    clear: both;
  }

  .markdown-body > :first-child {
    margin-top: 0 !important;
  }

  .markdown-body > :last-child {
    margin-bottom: 0 !important;
  }

  .markdown-body a:not([href]) {
    color: inherit;
    text-decoration: none;
  }
`;
  const GFM3 = `
  .markdown-body blockquote,
  .markdown-body details,
  .markdown-body dl,
  .markdown-body ol,
  .markdown-body p,
  .markdown-body pre,
  .markdown-body table,
  .markdown-body ul {
    margin-top: 0;
    margin-bottom: 16px;
  }

  .markdown-body hr {
    height: 0.25em;
    padding: 0;
    margin: 24px 0;
    background-color: #e1e4e8;
    border: 0;
  }

  .markdown-body blockquote {
    padding: 0 1em;
    color: #6a737d;
    border-left: 0.25em solid #dfe2e5;
  }

  .markdown-body blockquote > :first-child {
    margin-top: 0;
  }

  .markdown-body blockquote > :last-child {
    margin-bottom: 0;
  }

  .markdown-body h1,
  .markdown-body h2,
  .markdown-body h3,
  .markdown-body h4,
  .markdown-body h5,
  .markdown-body h6 {
    margin-top: 24px;
    margin-bottom: 16px;
    font-weight: 600;
    line-height: 1.25;
  }

  .markdown-body h1 {
    font-size: 2em;
  }

  .markdown-body h1,
  .markdown-body h2 {
    padding-bottom: 0.3em;
    border-bottom: 1px solid #eaecef;
  }

  .markdown-body h2 {
    font-size: 1.5em;
  }

  .markdown-body h3 {
    font-size: 1.25em;
  }

  .markdown-body h4 {
    font-size: 1em;
  }

  .markdown-body h5 {
    font-size: 0.875em;
  }

  .markdown-body h6 {
    font-size: 0.85em;
    color: #6a737d;
  }

  .markdown-body ol,
  .markdown-body ul {
    padding-left: 2em;
  }

  .markdown-body ol ol,
  .markdown-body ol ul,
  .markdown-body ul ol,
  .markdown-body ul ul {
    margin-top: 0;
    margin-bottom: 0;
  }

  .markdown-body li {
    word-wrap: break-all;
  }

  .markdown-body li > p {
    margin-top: 16px;
  }

  .markdown-body li + li {
    margin-top: 0.25em;
  }

  .markdown-body dl {
    padding: 0;
  }

  .markdown-body dl dt {
    padding: 0;
    margin-top: 16px;
    font-size: 1em;
    font-style: italic;
    font-weight: 600;
  }

  .markdown-body dl dd {
    padding: 0 16px;
    margin-bottom: 16px;
  }

  .markdown-body table {
    display: block;
    width: 100%;
    overflow: auto;
  }

  .markdown-body table th {
    font-weight: 600;
  }

  .markdown-body table td,
  .markdown-body table th {
    padding: 6px 13px;
    border: 1px solid #dfe2e5;
  }

  .markdown-body table th {
    background-color: var(--primary-background-color, #fff);
  }

  .markdown-body table tr {
    background-color: hsla(var(--primary-background-color, #fff), 0.1);
    border-top: 1px solid #c6cbd1;
  }

  .markdown-body table tr:nth-child(2n) {
    background-color: var(--primary-background-color, #fff);
  }

  .markdown-body img {
    max-width: 100%;
    box-sizing: initial;
    background-color: #fff;
    border-radius: var(--ha-card-border-radius);
  }

  .markdown-body img[align="right"] {
    padding-left: 20px;
  }

  .markdown-body img[align="left"] {
    padding-right: 20px;
  }

  .markdown-body code {
    padding: 0.2em 0.4em;
    margin: 0;
    font-size: 85%;
    background-color: var(--hcv-color-markdown-background);
    border-radius: 3px;
  }

  .markdown-body pre {
    word-wrap: normal;
  }

  .markdown-body pre > code {
    padding: 0;
    margin: 0;
    font-size: 100%;
    word-break: normal;
    white-space: pre;
    background: transparent;
    border: 0;
  }

  .markdown-body .highlight {
    margin-bottom: 16px;
  }

  .markdown-body .highlight pre {
    margin-bottom: 0;
    word-break: normal;
  }

  .markdown-body .highlight pre,
  .markdown-body pre {
    padding: 16px;
    overflow: auto;
    font-size: 85%;
    line-height: 1.45;
    background-color: var(--hcv-color-markdown-background);
    border-radius: var(--ha-card-border-radius, 3px);
  }

  .markdown-body pre code {
    display: inline;
    max-width: auto;
    padding: 0;
    margin: 0;
    overflow: visible;
    line-height: inherit;
    word-wrap: normal;
    background-color: initial;
    border: 0;
  }

  .markdown-body .commit-tease-sha {
    display: inline-block;
    font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    font-size: 90%;
    color: #444d56;
  }

  .markdown-body .full-commit .btn-outline:not(:disabled):hover {
    color: #005cc5;
    border-color: #005cc5;
  }

  .markdown-body .blob-wrapper {
    overflow-x: auto;
    overflow-y: hidden;
  }

  .markdown-body .blob-wrapper-embedded {
    max-height: 240px;
    overflow-y: auto;
  }

  .markdown-body .blob-num {
    width: 1%;
    min-width: 50px;
    padding-right: 10px;
    padding-left: 10px;
    font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    font-size: 12px;
    line-height: 20px;
    color: rgba(27, 31, 35, 0.3);
    text-align: right;
    white-space: nowrap;
    vertical-align: top;
    cursor: pointer;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
  }

  .markdown-body .blob-num:hover {
    color: rgba(27, 31, 35, 0.6);
  }

  .markdown-body .blob-num:before {
    content: attr(data-line-number);
  }

  .markdown-body .blob-code {
    position: relative;
    padding-right: 10px;
    padding-left: 10px;
    line-height: 20px;
    vertical-align: top;
  }

  .markdown-body .blob-code-inner {
    overflow: visible;
    font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
    font-size: 12px;
    color: #24292e;
    word-wrap: normal;
    white-space: pre;
  }

  .markdown-body .pl-token.active,
  .markdown-body .pl-token:hover {
    cursor: pointer;
    background: #ffea7f;
  }

  .markdown-body :checked + .radio-label {
    position: relative;
    z-index: 1;
    border-color: var(--link-text-color, var(--accent-color));
  }

  .markdown-body
    .select-menu-item
    input[type="radio"]:not(:checked)
    + .octicon-check,
  .markdown-body
    .select-menu-item
    input[type="radio"]:not(:checked)
    + .octicon-circle-slash {
    visibility: hidden;
  }

  .markdown-body .pl-7 {
    padding-left: 48px !important;
  }

  .markdown-body .pl-8 {
    padding-left: 64px !important;
  }

  .markdown-body .pl-9 {
    padding-left: 80px !important;
  }

  .markdown-body .pl-10 {
    padding-left: 96px !important;
  }

  .markdown-body .pl-11 {
    padding-left: 112px !important;
  }

  .markdown-body .pl-12 {
    padding-left: 128px !important;
  }

  .markdown-body .tab-size[data-tab-size="1"] {
    -moz-tab-size: 1;
    tab-size: 1;
  }

  .markdown-body .tab-size[data-tab-size="2"] {
    -moz-tab-size: 2;
    tab-size: 2;
  }

  .markdown-body .tab-size[data-tab-size="3"] {
    -moz-tab-size: 3;
    tab-size: 3;
  }

  .markdown-body .tab-size[data-tab-size="4"] {
    -moz-tab-size: 4;
    tab-size: 4;
  }

  .markdown-body .tab-size[data-tab-size="5"] {
    -moz-tab-size: 5;
    tab-size: 5;
  }

  .markdown-body .tab-size[data-tab-size="6"] {
    -moz-tab-size: 6;
    tab-size: 6;
  }

  .markdown-body .tab-size[data-tab-size="7"] {
    -moz-tab-size: 7;
    tab-size: 7;
  }

  .markdown-body .tab-size[data-tab-size="8"] {
    -moz-tab-size: 8;
    tab-size: 8;
  }

  .markdown-body .tab-size[data-tab-size="9"] {
    -moz-tab-size: 9;
    tab-size: 9;
  }

  .markdown-body .tab-size[data-tab-size="10"] {
    -moz-tab-size: 10;
    tab-size: 10;
  }

  .markdown-body .tab-size[data-tab-size="11"] {
    -moz-tab-size: 11;
    tab-size: 11;
  }

  .markdown-body .tab-size[data-tab-size="12"] {
    -moz-tab-size: 12;
    tab-size: 12;
  }

  .markdown-body .task-list-item {
    list-style-type: none;
  }

  .markdown-body .task-list-item + .task-list-item {
    margin-top: 3px;
  }

  .markdown-body .task-list-item input {
    margin: 0 0.2em 0.25em -1.6em;
    vertical-align: middle;
  }
`;
  const GFM = `${GFM1}${GFM2}${GFM3}`;

  core.registerLanguage("yaml", yaml_1);
  core.registerLanguage("javascript", javascript_1);
  core.registerLanguage("json", json_1);
  const marked$1 = marked;
  marked$1.setOptions({
    highlight: function (code, lang) {
      if (lang && core.getLanguage(lang)) {
        return core.highlight(lang, code, true).value;
      } else {
        return core.highlightAuto(code).value;
      }
    },
    breaks: true,
    gfm: true,
    tables: true,
    langPrefix: ""
  });
  class markdown {
    static convert(input) {
      return marked$1(input);
    }

    static html(input, repo) {
      // Convert emoji short codes to real emojis
      input = nodeEmoji.emojify(input); // Handle convertion to raw GitHub URL

      input = input.replace(/(https:\/\/github\.com\/.*.\/blob*.[^\s]+)/g, function (x) {
        if (x.includes(".md")) {
          return x;
        }

        let url = x.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/");
        return url;
      }); // Handle relative links

      input = input.replace(/\!\[*.*\]\(\w*\.\w*\)/g, function (x) {
        let url = x.replace("(", `(https://raw.githubusercontent.com/${repo === null || repo === void 0 ? void 0 : repo.full_name}/master/`).replace("/blob/", "/");
        return url;
      });
      const content = document.createElement("div");
      content.className = "markdown-body";
      content.innerHTML = purify.sanitize(marked$1(input), {
        css: false
      }).replace(/\<a href="http\w:\/\/.*.\">.*.\<\/a>\W/g, function (x) {
        return x.replace(/<a href=/gm, "<hacs-link url=").replace(/<\/a>/gm, "</hacs-link>");
      });
      const style = document.createElement("style");
      style.innerText = `${HLJS$1}${GFM}`;
      return html`${style}${content} `;
    }

  }

  let HacsDialogBase = _decorate(null, function (_initialize, _LitElement) {
    class HacsDialogBase extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsDialogBase,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "configuration",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hacs",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "critical",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "lovelace",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "removed",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "active",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "secondary",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "loading",

        value() {
          return true;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "sidebarDocked",
        value: void 0
      }, {
        kind: "method",
        key: "shouldUpdate",
        value: function shouldUpdate(changedProperties) {
          changedProperties.forEach((_oldValue, propName) => {
            if (propName === "hass") {
              this.sidebarDocked = window.localStorage.getItem("dockedSidebar") === '"docked"';
            }
          });
          return changedProperties.has("sidebarDocked") || changedProperties.has("narrow") || changedProperties.has("active") || changedProperties.has("params") || changedProperties.has("_error") || changedProperties.has("_progress") || changedProperties.has("_releaseNotes") || changedProperties.has("_updating");
        }
      }, {
        kind: "method",
        key: "connectedCallback",
        value: function connectedCallback() {
          _get(_getPrototypeOf(HacsDialogBase.prototype), "connectedCallback", this).call(this);

          this.sidebarDocked = window.localStorage.getItem("dockedSidebar") === '"docked"';
        }
      }]
    };
  }, LitElement);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$5(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /**
   * @license
   * Copyright 2016 Google Inc. All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  (() => {
    var _a, _b, _c;
    /* Symbols for private properties */


    const _blockingElements = Symbol();

    const _alreadyInertElements = Symbol();

    const _topElParents = Symbol();

    const _siblingsToRestore = Symbol();

    const _parentMO = Symbol();
    /* Symbols for private static methods */


    const _topChanged = Symbol();

    const _swapInertedSibling = Symbol();

    const _inertSiblings = Symbol();

    const _restoreInertedSiblings = Symbol();

    const _getParents = Symbol();

    const _getDistributedChildren = Symbol();

    const _isInertable = Symbol();

    const _handleMutations = Symbol();

    class BlockingElementsImpl {
      constructor() {
        /**
         * The blocking elements.
         */
        this[_a] = [];
        /**
         * Used to keep track of the parents of the top element, from the element
         * itself up to body. When top changes, the old top might have been removed
         * from the document, so we need to memoize the inerted parents' siblings
         * in order to restore their inerteness when top changes.
         */

        this[_b] = [];
        /**
         * Elements that are already inert before the first blocking element is
         * pushed.
         */

        this[_c] = new Set();
      }

      destructor() {
        // Restore original inertness.
        this[_restoreInertedSiblings](this[_topElParents]); // Note we don't want to make these properties nullable on the class,
        // since then we'd need non-null casts in many places. Calling a method on
        // a BlockingElements instance after calling destructor will result in an
        // exception.


        const nullable = this;
        nullable[_blockingElements] = null;
        nullable[_topElParents] = null;
        nullable[_alreadyInertElements] = null;
      }

      get top() {
        const elems = this[_blockingElements];
        return elems[elems.length - 1] || null;
      }

      push(element) {
        if (!element || element === this.top) {
          return;
        } // Remove it from the stack, we'll bring it to the top.


        this.remove(element);

        this[_topChanged](element);

        this[_blockingElements].push(element);
      }

      remove(element) {
        const i = this[_blockingElements].indexOf(element);

        if (i === -1) {
          return false;
        }

        this[_blockingElements].splice(i, 1); // Top changed only if the removed element was the top element.


        if (i === this[_blockingElements].length) {
          this[_topChanged](this.top);
        }

        return true;
      }

      pop() {
        const top = this.top;
        top && this.remove(top);
        return top;
      }

      has(element) {
        return this[_blockingElements].indexOf(element) !== -1;
      }
      /**
       * Sets `inert` to all document elements except the new top element, its
       * parents, and its distributed content.
       */


      [(_a = _blockingElements, _b = _topElParents, _c = _alreadyInertElements, _topChanged)](newTop) {
        const toKeepInert = this[_alreadyInertElements];
        const oldParents = this[_topElParents]; // No new top, reset old top if any.

        if (!newTop) {
          this[_restoreInertedSiblings](oldParents);

          toKeepInert.clear();
          this[_topElParents] = [];
          return;
        }

        const newParents = this[_getParents](newTop); // New top is not contained in the main document!


        if (newParents[newParents.length - 1].parentNode !== document.body) {
          throw Error('Non-connected element cannot be a blocking element');
        } // Cast here because we know we'll call _inertSiblings on newParents
        // below.


        this[_topElParents] = newParents;

        const toSkip = this[_getDistributedChildren](newTop); // No previous top element.


        if (!oldParents.length) {
          this[_inertSiblings](newParents, toSkip, toKeepInert);

          return;
        }

        let i = oldParents.length - 1;
        let j = newParents.length - 1; // Find common parent. Index 0 is the element itself (so stop before it).

        while (i > 0 && j > 0 && oldParents[i] === newParents[j]) {
          i--;
          j--;
        } // If up the parents tree there are 2 elements that are siblings, swap
        // the inerted sibling.


        if (oldParents[i] !== newParents[j]) {
          this[_swapInertedSibling](oldParents[i], newParents[j]);
        } // Restore old parents siblings inertness.


        i > 0 && this[_restoreInertedSiblings](oldParents.slice(0, i)); // Make new parents siblings inert.

        j > 0 && this[_inertSiblings](newParents.slice(0, j), toSkip, null);
      }
      /**
       * Swaps inertness between two sibling elements.
       * Sets the property `inert` over the attribute since the inert spec
       * doesn't specify if it should be reflected.
       * https://html.spec.whatwg.org/multipage/interaction.html#inert
       */


      [_swapInertedSibling](oldInert, newInert) {
        const siblingsToRestore = oldInert[_siblingsToRestore]; // oldInert is not contained in siblings to restore, so we have to check
        // if it's inertable and if already inert.

        if (this[_isInertable](oldInert) && !oldInert.inert) {
          oldInert.inert = true;
          siblingsToRestore.add(oldInert);
        } // If newInert was already between the siblings to restore, it means it is
        // inertable and must be restored.


        if (siblingsToRestore.has(newInert)) {
          newInert.inert = false;
          siblingsToRestore.delete(newInert);
        }

        newInert[_parentMO] = oldInert[_parentMO];
        newInert[_siblingsToRestore] = siblingsToRestore;
        oldInert[_parentMO] = undefined;
        oldInert[_siblingsToRestore] = undefined;
      }
      /**
       * Restores original inertness to the siblings of the elements.
       * Sets the property `inert` over the attribute since the inert spec
       * doesn't specify if it should be reflected.
       * https://html.spec.whatwg.org/multipage/interaction.html#inert
       */


      [_restoreInertedSiblings](elements) {
        for (const element of elements) {
          const mo = element[_parentMO];
          mo.disconnect();
          element[_parentMO] = undefined;
          const siblings = element[_siblingsToRestore];

          for (const sibling of siblings) {
            sibling.inert = false;
          }

          element[_siblingsToRestore] = undefined;
        }
      }
      /**
       * Inerts the siblings of the elements except the elements to skip. Stores
       * the inerted siblings into the element's symbol `_siblingsToRestore`.
       * Pass `toKeepInert` to collect the already inert elements.
       * Sets the property `inert` over the attribute since the inert spec
       * doesn't specify if it should be reflected.
       * https://html.spec.whatwg.org/multipage/interaction.html#inert
       */


      [_inertSiblings](elements, toSkip, toKeepInert) {
        for (const element of elements) {
          // Assume element is not a Document, so it must have a parentNode.
          const parent = element.parentNode;
          const children = parent.children;
          const inertedSiblings = new Set();

          for (let j = 0; j < children.length; j++) {
            const sibling = children[j]; // Skip the input element, if not inertable or to be skipped.

            if (sibling === element || !this[_isInertable](sibling) || toSkip && toSkip.has(sibling)) {
              continue;
            } // Should be collected since already inerted.


            if (toKeepInert && sibling.inert) {
              toKeepInert.add(sibling);
            } else {
              sibling.inert = true;
              inertedSiblings.add(sibling);
            }
          } // Store the siblings that were inerted.


          element[_siblingsToRestore] = inertedSiblings; // Observe only immediate children mutations on the parent.

          const mo = new MutationObserver(this[_handleMutations].bind(this));
          element[_parentMO] = mo;
          let parentToObserve = parent; // If we're using the ShadyDOM polyfill, then our parent could be a
          // shady root, which is an object that acts like a ShadowRoot, but isn't
          // actually a node in the real DOM. Observe the real DOM parent instead.

          const maybeShadyRoot = parentToObserve;

          if (maybeShadyRoot.__shady && maybeShadyRoot.host) {
            parentToObserve = maybeShadyRoot.host;
          }

          mo.observe(parentToObserve, {
            childList: true
          });
        }
      }
      /**
       * Handles newly added/removed nodes by toggling their inertness.
       * It also checks if the current top Blocking Element has been removed,
       * notifying and removing it.
       */


      [_handleMutations](mutations) {
        const parents = this[_topElParents];
        const toKeepInert = this[_alreadyInertElements];

        for (const mutation of mutations) {
          // If the target is a shadowRoot, get its host as we skip shadowRoots when
          // computing _topElParents.
          const target = mutation.target.host || mutation.target;
          const idx = target === document.body ? parents.length : parents.indexOf(target);
          const inertedChild = parents[idx - 1];
          const inertedSiblings = inertedChild[_siblingsToRestore]; // To restore.

          for (let i = 0; i < mutation.removedNodes.length; i++) {
            const sibling = mutation.removedNodes[i];

            if (sibling === inertedChild) {
              console.info('Detected removal of the top Blocking Element.');
              this.pop();
              return;
            }

            if (inertedSiblings.has(sibling)) {
              sibling.inert = false;
              inertedSiblings.delete(sibling);
            }
          } // To inert.


          for (let i = 0; i < mutation.addedNodes.length; i++) {
            const sibling = mutation.addedNodes[i];

            if (!this[_isInertable](sibling)) {
              continue;
            }

            if (toKeepInert && sibling.inert) {
              toKeepInert.add(sibling);
            } else {
              sibling.inert = true;
              inertedSiblings.add(sibling);
            }
          }
        }
      }
      /**
       * Returns if the element is inertable.
       */


      [_isInertable](element) {
        return false === /^(style|template|script)$/.test(element.localName);
      }
      /**
       * Returns the list of newParents of an element, starting from element
       * (included) up to `document.body` (excluded).
       */


      [_getParents](element) {
        const parents = [];
        let current = element; // Stop to body.

        while (current && current !== document.body) {
          // Skip shadow roots.
          if (current.nodeType === Node.ELEMENT_NODE) {
            parents.push(current);
          } // ShadowDom v1


          if (current.assignedSlot) {
            // Collect slots from deepest slot to top.
            while (current = current.assignedSlot) {
              parents.push(current);
            } // Continue the search on the top slot.


            current = parents.pop();
            continue;
          }

          current = current.parentNode || current.host;
        }

        return parents;
      }
      /**
       * Returns the distributed children of the element's shadow root.
       * Returns null if the element doesn't have a shadow root.
       */


      [_getDistributedChildren](element) {
        const shadowRoot = element.shadowRoot;

        if (!shadowRoot) {
          return null;
        }

        const result = new Set();
        let i;
        let j;
        let nodes;
        const slots = shadowRoot.querySelectorAll('slot');

        if (slots.length && slots[0].assignedNodes) {
          for (i = 0; i < slots.length; i++) {
            nodes = slots[i].assignedNodes({
              flatten: true
            });

            for (j = 0; j < nodes.length; j++) {
              if (nodes[j].nodeType === Node.ELEMENT_NODE) {
                result.add(nodes[j]);
              }
            }
          } // No need to search for <content>.

        }

        return result;
      }

    }

    document.$blockingElements = new BlockingElementsImpl();
  })();

  /**
   * This work is licensed under the W3C Software and Document License
   * (http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document).
   */
  (function () {
    // Return early if we're not running inside of the browser.
    if (typeof window === 'undefined') {
      return;
    } // Convenience function for converting NodeLists.

    /** @type {typeof Array.prototype.slice} */


    const slice = Array.prototype.slice;
    /**
     * IE has a non-standard name for "matches".
     * @type {typeof Element.prototype.matches}
     */

    const matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
    /** @type {string} */

    const _focusableElementsString = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'details', 'summary', 'iframe', 'object', 'embed', '[contenteditable]'].join(',');
    /**
     * `InertRoot` manages a single inert subtree, i.e. a DOM subtree whose root element has an `inert`
     * attribute.
     *
     * Its main functions are:
     *
     * - to create and maintain a set of managed `InertNode`s, including when mutations occur in the
     *   subtree. The `makeSubtreeUnfocusable()` method handles collecting `InertNode`s via registering
     *   each focusable node in the subtree with the singleton `InertManager` which manages all known
     *   focusable nodes within inert subtrees. `InertManager` ensures that a single `InertNode`
     *   instance exists for each focusable node which has at least one inert root as an ancestor.
     *
     * - to notify all managed `InertNode`s when this subtree stops being inert (i.e. when the `inert`
     *   attribute is removed from the root node). This is handled in the destructor, which calls the
     *   `deregister` method on `InertManager` for each managed inert node.
     */


    class InertRoot {
      /**
       * @param {!Element} rootElement The Element at the root of the inert subtree.
       * @param {!InertManager} inertManager The global singleton InertManager object.
       */
      constructor(rootElement, inertManager) {
        /** @type {!InertManager} */
        this._inertManager = inertManager;
        /** @type {!Element} */

        this._rootElement = rootElement;
        /**
         * @type {!Set<!InertNode>}
         * All managed focusable nodes in this InertRoot's subtree.
         */

        this._managedNodes = new Set(); // Make the subtree hidden from assistive technology

        if (this._rootElement.hasAttribute('aria-hidden')) {
          /** @type {?string} */
          this._savedAriaHidden = this._rootElement.getAttribute('aria-hidden');
        } else {
          this._savedAriaHidden = null;
        }

        this._rootElement.setAttribute('aria-hidden', 'true'); // Make all focusable elements in the subtree unfocusable and add them to _managedNodes


        this._makeSubtreeUnfocusable(this._rootElement); // Watch for:
        // - any additions in the subtree: make them unfocusable too
        // - any removals from the subtree: remove them from this inert root's managed nodes
        // - attribute changes: if `tabindex` is added, or removed from an intrinsically focusable
        //   element, make that node a managed node.


        this._observer = new MutationObserver(this._onMutation.bind(this));

        this._observer.observe(this._rootElement, {
          attributes: true,
          childList: true,
          subtree: true
        });
      }
      /**
       * Call this whenever this object is about to become obsolete.  This unwinds all of the state
       * stored in this object and updates the state of all of the managed nodes.
       */


      destructor() {
        this._observer.disconnect();

        if (this._rootElement) {
          if (this._savedAriaHidden !== null) {
            this._rootElement.setAttribute('aria-hidden', this._savedAriaHidden);
          } else {
            this._rootElement.removeAttribute('aria-hidden');
          }
        }

        this._managedNodes.forEach(function (inertNode) {
          this._unmanageNode(inertNode.node);
        }, this); // Note we cast the nulls to the ANY type here because:
        // 1) We want the class properties to be declared as non-null, or else we
        //    need even more casts throughout this code. All bets are off if an
        //    instance has been destroyed and a method is called.
        // 2) We don't want to cast "this", because we want type-aware optimizations
        //    to know which properties we're setting.


        this._observer =
        /** @type {?} */
        null;
        this._rootElement =
        /** @type {?} */
        null;
        this._managedNodes =
        /** @type {?} */
        null;
        this._inertManager =
        /** @type {?} */
        null;
      }
      /**
       * @return {!Set<!InertNode>} A copy of this InertRoot's managed nodes set.
       */


      get managedNodes() {
        return new Set(this._managedNodes);
      }
      /** @return {boolean} */


      get hasSavedAriaHidden() {
        return this._savedAriaHidden !== null;
      }
      /** @param {?string} ariaHidden */


      set savedAriaHidden(ariaHidden) {
        this._savedAriaHidden = ariaHidden;
      }
      /** @return {?string} */


      get savedAriaHidden() {
        return this._savedAriaHidden;
      }
      /**
       * @param {!Node} startNode
       */


      _makeSubtreeUnfocusable(startNode) {
        composedTreeWalk(startNode, node => this._visitNode(node));
        let activeElement = document.activeElement;

        if (!document.body.contains(startNode)) {
          // startNode may be in shadow DOM, so find its nearest shadowRoot to get the activeElement.
          let node = startNode;
          /** @type {!ShadowRoot|undefined} */

          let root = undefined;

          while (node) {
            if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
              root =
              /** @type {!ShadowRoot} */
              node;
              break;
            }

            node = node.parentNode;
          }

          if (root) {
            activeElement = root.activeElement;
          }
        }

        if (startNode.contains(activeElement)) {
          activeElement.blur(); // In IE11, if an element is already focused, and then set to tabindex=-1
          // calling blur() will not actually move the focus.
          // To work around this we call focus() on the body instead.

          if (activeElement === document.activeElement) {
            document.body.focus();
          }
        }
      }
      /**
       * @param {!Node} node
       */


      _visitNode(node) {
        if (node.nodeType !== Node.ELEMENT_NODE) {
          return;
        }

        const element =
        /** @type {!Element} */
        node; // If a descendant inert root becomes un-inert, its descendants will still be inert because of
        // this inert root, so all of its managed nodes need to be adopted by this InertRoot.

        if (element !== this._rootElement && element.hasAttribute('inert')) {
          this._adoptInertRoot(element);
        }

        if (matches.call(element, _focusableElementsString) || element.hasAttribute('tabindex')) {
          this._manageNode(element);
        }
      }
      /**
       * Register the given node with this InertRoot and with InertManager.
       * @param {!Node} node
       */


      _manageNode(node) {
        const inertNode = this._inertManager.register(node, this);

        this._managedNodes.add(inertNode);
      }
      /**
       * Unregister the given node with this InertRoot and with InertManager.
       * @param {!Node} node
       */


      _unmanageNode(node) {
        const inertNode = this._inertManager.deregister(node, this);

        if (inertNode) {
          this._managedNodes.delete(inertNode);
        }
      }
      /**
       * Unregister the entire subtree starting at `startNode`.
       * @param {!Node} startNode
       */


      _unmanageSubtree(startNode) {
        composedTreeWalk(startNode, node => this._unmanageNode(node));
      }
      /**
       * If a descendant node is found with an `inert` attribute, adopt its managed nodes.
       * @param {!Element} node
       */


      _adoptInertRoot(node) {
        let inertSubroot = this._inertManager.getInertRoot(node); // During initialisation this inert root may not have been registered yet,
        // so register it now if need be.


        if (!inertSubroot) {
          this._inertManager.setInert(node, true);

          inertSubroot = this._inertManager.getInertRoot(node);
        }

        inertSubroot.managedNodes.forEach(function (savedInertNode) {
          this._manageNode(savedInertNode.node);
        }, this);
      }
      /**
       * Callback used when mutation observer detects subtree additions, removals, or attribute changes.
       * @param {!Array<!MutationRecord>} records
       * @param {!MutationObserver} self
       */


      _onMutation(records, self) {
        records.forEach(function (record) {
          const target =
          /** @type {!Element} */
          record.target;

          if (record.type === 'childList') {
            // Manage added nodes
            slice.call(record.addedNodes).forEach(function (node) {
              this._makeSubtreeUnfocusable(node);
            }, this); // Un-manage removed nodes

            slice.call(record.removedNodes).forEach(function (node) {
              this._unmanageSubtree(node);
            }, this);
          } else if (record.type === 'attributes') {
            if (record.attributeName === 'tabindex') {
              // Re-initialise inert node if tabindex changes
              this._manageNode(target);
            } else if (target !== this._rootElement && record.attributeName === 'inert' && target.hasAttribute('inert')) {
              // If a new inert root is added, adopt its managed nodes and make sure it knows about the
              // already managed nodes from this inert subroot.
              this._adoptInertRoot(target);

              const inertSubroot = this._inertManager.getInertRoot(target);

              this._managedNodes.forEach(function (managedNode) {
                if (target.contains(managedNode.node)) {
                  inertSubroot._manageNode(managedNode.node);
                }
              });
            }
          }
        }, this);
      }

    }
    /**
     * `InertNode` initialises and manages a single inert node.
     * A node is inert if it is a descendant of one or more inert root elements.
     *
     * On construction, `InertNode` saves the existing `tabindex` value for the node, if any, and
     * either removes the `tabindex` attribute or sets it to `-1`, depending on whether the element
     * is intrinsically focusable or not.
     *
     * `InertNode` maintains a set of `InertRoot`s which are descendants of this `InertNode`. When an
     * `InertRoot` is destroyed, and calls `InertManager.deregister()`, the `InertManager` notifies the
     * `InertNode` via `removeInertRoot()`, which in turn destroys the `InertNode` if no `InertRoot`s
     * remain in the set. On destruction, `InertNode` reinstates the stored `tabindex` if one exists,
     * or removes the `tabindex` attribute if the element is intrinsically focusable.
     */


    class InertNode {
      /**
       * @param {!Node} node A focusable element to be made inert.
       * @param {!InertRoot} inertRoot The inert root element associated with this inert node.
       */
      constructor(node, inertRoot) {
        /** @type {!Node} */
        this._node = node;
        /** @type {boolean} */

        this._overrodeFocusMethod = false;
        /**
         * @type {!Set<!InertRoot>} The set of descendant inert roots.
         *    If and only if this set becomes empty, this node is no longer inert.
         */

        this._inertRoots = new Set([inertRoot]);
        /** @type {?number} */

        this._savedTabIndex = null;
        /** @type {boolean} */

        this._destroyed = false; // Save any prior tabindex info and make this node untabbable

        this.ensureUntabbable();
      }
      /**
       * Call this whenever this object is about to become obsolete.
       * This makes the managed node focusable again and deletes all of the previously stored state.
       */


      destructor() {
        this._throwIfDestroyed();

        if (this._node && this._node.nodeType === Node.ELEMENT_NODE) {
          const element =
          /** @type {!Element} */
          this._node;

          if (this._savedTabIndex !== null) {
            element.setAttribute('tabindex', this._savedTabIndex);
          } else {
            element.removeAttribute('tabindex');
          } // Use `delete` to restore native focus method.


          if (this._overrodeFocusMethod) {
            delete element.focus;
          }
        } // See note in InertRoot.destructor for why we cast these nulls to ANY.


        this._node =
        /** @type {?} */
        null;
        this._inertRoots =
        /** @type {?} */
        null;
        this._destroyed = true;
      }
      /**
       * @type {boolean} Whether this object is obsolete because the managed node is no longer inert.
       * If the object has been destroyed, any attempt to access it will cause an exception.
       */


      get destroyed() {
        return (
          /** @type {!InertNode} */
          this._destroyed
        );
      }
      /**
       * Throw if user tries to access destroyed InertNode.
       */


      _throwIfDestroyed() {
        if (this.destroyed) {
          throw new Error('Trying to access destroyed InertNode');
        }
      }
      /** @return {boolean} */


      get hasSavedTabIndex() {
        return this._savedTabIndex !== null;
      }
      /** @return {!Node} */


      get node() {
        this._throwIfDestroyed();

        return this._node;
      }
      /** @param {?number} tabIndex */


      set savedTabIndex(tabIndex) {
        this._throwIfDestroyed();

        this._savedTabIndex = tabIndex;
      }
      /** @return {?number} */


      get savedTabIndex() {
        this._throwIfDestroyed();

        return this._savedTabIndex;
      }
      /** Save the existing tabindex value and make the node untabbable and unfocusable */


      ensureUntabbable() {
        if (this.node.nodeType !== Node.ELEMENT_NODE) {
          return;
        }

        const element =
        /** @type {!Element} */
        this.node;

        if (matches.call(element, _focusableElementsString)) {
          if (
          /** @type {!HTMLElement} */
          element.tabIndex === -1 && this.hasSavedTabIndex) {
            return;
          }

          if (element.hasAttribute('tabindex')) {
            this._savedTabIndex =
            /** @type {!HTMLElement} */
            element.tabIndex;
          }

          element.setAttribute('tabindex', '-1');

          if (element.nodeType === Node.ELEMENT_NODE) {
            element.focus = function () {};

            this._overrodeFocusMethod = true;
          }
        } else if (element.hasAttribute('tabindex')) {
          this._savedTabIndex =
          /** @type {!HTMLElement} */
          element.tabIndex;
          element.removeAttribute('tabindex');
        }
      }
      /**
       * Add another inert root to this inert node's set of managing inert roots.
       * @param {!InertRoot} inertRoot
       */


      addInertRoot(inertRoot) {
        this._throwIfDestroyed();

        this._inertRoots.add(inertRoot);
      }
      /**
       * Remove the given inert root from this inert node's set of managing inert roots.
       * If the set of managing inert roots becomes empty, this node is no longer inert,
       * so the object should be destroyed.
       * @param {!InertRoot} inertRoot
       */


      removeInertRoot(inertRoot) {
        this._throwIfDestroyed();

        this._inertRoots.delete(inertRoot);

        if (this._inertRoots.size === 0) {
          this.destructor();
        }
      }

    }
    /**
     * InertManager is a per-document singleton object which manages all inert roots and nodes.
     *
     * When an element becomes an inert root by having an `inert` attribute set and/or its `inert`
     * property set to `true`, the `setInert` method creates an `InertRoot` object for the element.
     * The `InertRoot` in turn registers itself as managing all of the element's focusable descendant
     * nodes via the `register()` method. The `InertManager` ensures that a single `InertNode` instance
     * is created for each such node, via the `_managedNodes` map.
     */


    class InertManager {
      /**
       * @param {!Document} document
       */
      constructor(document) {
        if (!document) {
          throw new Error('Missing required argument; InertManager needs to wrap a document.');
        }
        /** @type {!Document} */


        this._document = document;
        /**
         * All managed nodes known to this InertManager. In a map to allow looking up by Node.
         * @type {!Map<!Node, !InertNode>}
         */

        this._managedNodes = new Map();
        /**
         * All inert roots known to this InertManager. In a map to allow looking up by Node.
         * @type {!Map<!Node, !InertRoot>}
         */

        this._inertRoots = new Map();
        /**
         * Observer for mutations on `document.body`.
         * @type {!MutationObserver}
         */

        this._observer = new MutationObserver(this._watchForInert.bind(this)); // Add inert style.

        addInertStyle(document.head || document.body || document.documentElement); // Wait for document to be loaded.

        if (document.readyState === 'loading') {
          document.addEventListener('DOMContentLoaded', this._onDocumentLoaded.bind(this));
        } else {
          this._onDocumentLoaded();
        }
      }
      /**
       * Set whether the given element should be an inert root or not.
       * @param {!Element} root
       * @param {boolean} inert
       */


      setInert(root, inert) {
        if (inert) {
          if (this._inertRoots.has(root)) {
            // element is already inert
            return;
          }

          const inertRoot = new InertRoot(root, this);
          root.setAttribute('inert', '');

          this._inertRoots.set(root, inertRoot); // If not contained in the document, it must be in a shadowRoot.
          // Ensure inert styles are added there.


          if (!this._document.body.contains(root)) {
            let parent = root.parentNode;

            while (parent) {
              if (parent.nodeType === 11) {
                addInertStyle(parent);
              }

              parent = parent.parentNode;
            }
          }
        } else {
          if (!this._inertRoots.has(root)) {
            // element is already non-inert
            return;
          }

          const inertRoot = this._inertRoots.get(root);

          inertRoot.destructor();

          this._inertRoots.delete(root);

          root.removeAttribute('inert');
        }
      }
      /**
       * Get the InertRoot object corresponding to the given inert root element, if any.
       * @param {!Node} element
       * @return {!InertRoot|undefined}
       */


      getInertRoot(element) {
        return this._inertRoots.get(element);
      }
      /**
       * Register the given InertRoot as managing the given node.
       * In the case where the node has a previously existing inert root, this inert root will
       * be added to its set of inert roots.
       * @param {!Node} node
       * @param {!InertRoot} inertRoot
       * @return {!InertNode} inertNode
       */


      register(node, inertRoot) {
        let inertNode = this._managedNodes.get(node);

        if (inertNode !== undefined) {
          // node was already in an inert subtree
          inertNode.addInertRoot(inertRoot);
        } else {
          inertNode = new InertNode(node, inertRoot);
        }

        this._managedNodes.set(node, inertNode);

        return inertNode;
      }
      /**
       * De-register the given InertRoot as managing the given inert node.
       * Removes the inert root from the InertNode's set of managing inert roots, and remove the inert
       * node from the InertManager's set of managed nodes if it is destroyed.
       * If the node is not currently managed, this is essentially a no-op.
       * @param {!Node} node
       * @param {!InertRoot} inertRoot
       * @return {?InertNode} The potentially destroyed InertNode associated with this node, if any.
       */


      deregister(node, inertRoot) {
        const inertNode = this._managedNodes.get(node);

        if (!inertNode) {
          return null;
        }

        inertNode.removeInertRoot(inertRoot);

        if (inertNode.destroyed) {
          this._managedNodes.delete(node);
        }

        return inertNode;
      }
      /**
       * Callback used when document has finished loading.
       */


      _onDocumentLoaded() {
        // Find all inert roots in document and make them actually inert.
        const inertElements = slice.call(this._document.querySelectorAll('[inert]'));
        inertElements.forEach(function (inertElement) {
          this.setInert(inertElement, true);
        }, this); // Comment this out to use programmatic API only.

        this._observer.observe(this._document.body || this._document.documentElement, {
          attributes: true,
          subtree: true,
          childList: true
        });
      }
      /**
       * Callback used when mutation observer detects attribute changes.
       * @param {!Array<!MutationRecord>} records
       * @param {!MutationObserver} self
       */


      _watchForInert(records, self) {
        const _this = this;

        records.forEach(function (record) {
          switch (record.type) {
            case 'childList':
              slice.call(record.addedNodes).forEach(function (node) {
                if (node.nodeType !== Node.ELEMENT_NODE) {
                  return;
                }

                const inertElements = slice.call(node.querySelectorAll('[inert]'));

                if (matches.call(node, '[inert]')) {
                  inertElements.unshift(node);
                }

                inertElements.forEach(function (inertElement) {
                  this.setInert(inertElement, true);
                }, _this);
              }, _this);
              break;

            case 'attributes':
              if (record.attributeName !== 'inert') {
                return;
              }

              const target =
              /** @type {!Element} */
              record.target;
              const inert = target.hasAttribute('inert');

              _this.setInert(target, inert);

              break;
          }
        }, this);
      }

    }
    /**
     * Recursively walk the composed tree from |node|.
     * @param {!Node} node
     * @param {(function (!Element))=} callback Callback to be called for each element traversed,
     *     before descending into child nodes.
     * @param {?ShadowRoot=} shadowRootAncestor The nearest ShadowRoot ancestor, if any.
     */


    function composedTreeWalk(node, callback, shadowRootAncestor) {
      if (node.nodeType == Node.ELEMENT_NODE) {
        const element =
        /** @type {!Element} */
        node;

        if (callback) {
          callback(element);
        } // Descend into node:
        // If it has a ShadowRoot, ignore all child elements - these will be picked
        // up by the <content> or <shadow> elements. Descend straight into the
        // ShadowRoot.


        const shadowRoot =
        /** @type {!HTMLElement} */
        element.shadowRoot;

        if (shadowRoot) {
          composedTreeWalk(shadowRoot, callback);
          return;
        } // If it is a <content> element, descend into distributed elements - these
        // are elements from outside the shadow root which are rendered inside the
        // shadow DOM.


        if (element.localName == 'content') {
          const content =
          /** @type {!HTMLContentElement} */
          element; // Verifies if ShadowDom v0 is supported.

          const distributedNodes = content.getDistributedNodes ? content.getDistributedNodes() : [];

          for (let i = 0; i < distributedNodes.length; i++) {
            composedTreeWalk(distributedNodes[i], callback);
          }

          return;
        } // If it is a <slot> element, descend into assigned nodes - these
        // are elements from outside the shadow root which are rendered inside the
        // shadow DOM.


        if (element.localName == 'slot') {
          const slot =
          /** @type {!HTMLSlotElement} */
          element; // Verify if ShadowDom v1 is supported.

          const distributedNodes = slot.assignedNodes ? slot.assignedNodes({
            flatten: true
          }) : [];

          for (let i = 0; i < distributedNodes.length; i++) {
            composedTreeWalk(distributedNodes[i], callback);
          }

          return;
        }
      } // If it is neither the parent of a ShadowRoot, a <content> element, a <slot>
      // element, nor a <shadow> element recurse normally.


      let child = node.firstChild;

      while (child != null) {
        composedTreeWalk(child, callback);
        child = child.nextSibling;
      }
    }
    /**
     * Adds a style element to the node containing the inert specific styles
     * @param {!Node} node
     */


    function addInertStyle(node) {
      if (node.querySelector('style#inert-style, link#inert-style')) {
        return;
      }

      const style = document.createElement('style');
      style.setAttribute('id', 'inert-style');
      style.textContent = '\n' + '[inert] {\n' + '  pointer-events: none;\n' + '  cursor: default;\n' + '}\n' + '\n' + '[inert], [inert] * {\n' + '  -webkit-user-select: none;\n' + '  -moz-user-select: none;\n' + '  -ms-user-select: none;\n' + '  user-select: none;\n' + '}\n';
      node.appendChild(style);
    }
    /** @type {!InertManager} */


    const inertManager = new InertManager(document);

    if (!Element.prototype.hasOwnProperty('inert')) {
      Object.defineProperty(Element.prototype, 'inert', {
        enumerable: true,

        /** @this {!Element} */
        get: function () {
          return this.hasAttribute('inert');
        },

        /** @this {!Element} */
        set: function (inert) {
          inertManager.setInert(this, inert);
        }
      });
    }
  })();

  /**
   * @license
   * Copyright 2016 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  var cssClasses$1 = {
    CLOSING: 'mdc-dialog--closing',
    OPEN: 'mdc-dialog--open',
    OPENING: 'mdc-dialog--opening',
    SCROLLABLE: 'mdc-dialog--scrollable',
    SCROLL_LOCK: 'mdc-dialog-scroll-lock',
    STACKED: 'mdc-dialog--stacked'
  };
  var strings$1 = {
    ACTION_ATTRIBUTE: 'data-mdc-dialog-action',
    BUTTON_DEFAULT_ATTRIBUTE: 'data-mdc-dialog-button-default',
    BUTTON_SELECTOR: '.mdc-dialog__button',
    CLOSED_EVENT: 'MDCDialog:closed',
    CLOSE_ACTION: 'close',
    CLOSING_EVENT: 'MDCDialog:closing',
    CONTAINER_SELECTOR: '.mdc-dialog__container',
    CONTENT_SELECTOR: '.mdc-dialog__content',
    DESTROY_ACTION: 'destroy',
    INITIAL_FOCUS_ATTRIBUTE: 'data-mdc-dialog-initial-focus',
    OPENED_EVENT: 'MDCDialog:opened',
    OPENING_EVENT: 'MDCDialog:opening',
    SCRIM_SELECTOR: '.mdc-dialog__scrim',
    SUPPRESS_DEFAULT_PRESS_SELECTOR: ['textarea', '.mdc-menu .mdc-list-item'].join(', '),
    SURFACE_SELECTOR: '.mdc-dialog__surface'
  };
  var numbers$1 = {
    DIALOG_ANIMATION_CLOSE_TIME_MS: 75,
    DIALOG_ANIMATION_OPEN_TIME_MS: 150
  };

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */

  /* global Reflect, Promise */
  var extendStatics$1 = function (d, b) {
    extendStatics$1 = Object.setPrototypeOf || {
      __proto__: []
    } instanceof Array && function (d, b) {
      d.__proto__ = b;
    } || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    };

    return extendStatics$1(d, b);
  };

  function __extends$1(d, b) {
    extendStatics$1(d, b);

    function __() {
      this.constructor = d;
    }

    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  }
  var __assign$1 = function () {
    __assign$1 = Object.assign || function __assign(t) {
      for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];

        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
      }

      return t;
    };

    return __assign$1.apply(this, arguments);
  };

  /**
   * @license
   * Copyright 2017 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  var MDCDialogFoundation =
  /** @class */
  function (_super) {
    __extends$1(MDCDialogFoundation, _super);

    function MDCDialogFoundation(adapter) {
      var _this = _super.call(this, __assign$1(__assign$1({}, MDCDialogFoundation.defaultAdapter), adapter)) || this;

      _this.isOpen_ = false;
      _this.animationFrame_ = 0;
      _this.animationTimer_ = 0;
      _this.layoutFrame_ = 0;
      _this.escapeKeyAction_ = strings$1.CLOSE_ACTION;
      _this.scrimClickAction_ = strings$1.CLOSE_ACTION;
      _this.autoStackButtons_ = true;
      _this.areButtonsStacked_ = false;
      return _this;
    }

    Object.defineProperty(MDCDialogFoundation, "cssClasses", {
      get: function () {
        return cssClasses$1;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCDialogFoundation, "strings", {
      get: function () {
        return strings$1;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCDialogFoundation, "numbers", {
      get: function () {
        return numbers$1;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCDialogFoundation, "defaultAdapter", {
      get: function () {
        return {
          addBodyClass: function () {
            return undefined;
          },
          addClass: function () {
            return undefined;
          },
          areButtonsStacked: function () {
            return false;
          },
          clickDefaultButton: function () {
            return undefined;
          },
          eventTargetMatches: function () {
            return false;
          },
          getActionFromEvent: function () {
            return '';
          },
          getInitialFocusEl: function () {
            return null;
          },
          hasClass: function () {
            return false;
          },
          isContentScrollable: function () {
            return false;
          },
          notifyClosed: function () {
            return undefined;
          },
          notifyClosing: function () {
            return undefined;
          },
          notifyOpened: function () {
            return undefined;
          },
          notifyOpening: function () {
            return undefined;
          },
          releaseFocus: function () {
            return undefined;
          },
          removeBodyClass: function () {
            return undefined;
          },
          removeClass: function () {
            return undefined;
          },
          reverseButtons: function () {
            return undefined;
          },
          trapFocus: function () {
            return undefined;
          }
        };
      },
      enumerable: true,
      configurable: true
    });

    MDCDialogFoundation.prototype.init = function () {
      if (this.adapter.hasClass(cssClasses$1.STACKED)) {
        this.setAutoStackButtons(false);
      }
    };

    MDCDialogFoundation.prototype.destroy = function () {
      if (this.isOpen_) {
        this.close(strings$1.DESTROY_ACTION);
      }

      if (this.animationTimer_) {
        clearTimeout(this.animationTimer_);
        this.handleAnimationTimerEnd_();
      }

      if (this.layoutFrame_) {
        cancelAnimationFrame(this.layoutFrame_);
        this.layoutFrame_ = 0;
      }
    };

    MDCDialogFoundation.prototype.open = function () {
      var _this = this;

      this.isOpen_ = true;
      this.adapter.notifyOpening();
      this.adapter.addClass(cssClasses$1.OPENING); // Wait a frame once display is no longer "none", to establish basis for animation

      this.runNextAnimationFrame_(function () {
        _this.adapter.addClass(cssClasses$1.OPEN);

        _this.adapter.addBodyClass(cssClasses$1.SCROLL_LOCK);

        _this.layout();

        _this.animationTimer_ = setTimeout(function () {
          _this.handleAnimationTimerEnd_();

          _this.adapter.trapFocus(_this.adapter.getInitialFocusEl());

          _this.adapter.notifyOpened();
        }, numbers$1.DIALOG_ANIMATION_OPEN_TIME_MS);
      });
    };

    MDCDialogFoundation.prototype.close = function (action) {
      var _this = this;

      if (action === void 0) {
        action = '';
      }

      if (!this.isOpen_) {
        // Avoid redundant close calls (and events), e.g. from keydown on elements that inherently emit click
        return;
      }

      this.isOpen_ = false;
      this.adapter.notifyClosing(action);
      this.adapter.addClass(cssClasses$1.CLOSING);
      this.adapter.removeClass(cssClasses$1.OPEN);
      this.adapter.removeBodyClass(cssClasses$1.SCROLL_LOCK);
      cancelAnimationFrame(this.animationFrame_);
      this.animationFrame_ = 0;
      clearTimeout(this.animationTimer_);
      this.animationTimer_ = setTimeout(function () {
        _this.adapter.releaseFocus();

        _this.handleAnimationTimerEnd_();

        _this.adapter.notifyClosed(action);
      }, numbers$1.DIALOG_ANIMATION_CLOSE_TIME_MS);
    };

    MDCDialogFoundation.prototype.isOpen = function () {
      return this.isOpen_;
    };

    MDCDialogFoundation.prototype.getEscapeKeyAction = function () {
      return this.escapeKeyAction_;
    };

    MDCDialogFoundation.prototype.setEscapeKeyAction = function (action) {
      this.escapeKeyAction_ = action;
    };

    MDCDialogFoundation.prototype.getScrimClickAction = function () {
      return this.scrimClickAction_;
    };

    MDCDialogFoundation.prototype.setScrimClickAction = function (action) {
      this.scrimClickAction_ = action;
    };

    MDCDialogFoundation.prototype.getAutoStackButtons = function () {
      return this.autoStackButtons_;
    };

    MDCDialogFoundation.prototype.setAutoStackButtons = function (autoStack) {
      this.autoStackButtons_ = autoStack;
    };

    MDCDialogFoundation.prototype.layout = function () {
      var _this = this;

      if (this.layoutFrame_) {
        cancelAnimationFrame(this.layoutFrame_);
      }

      this.layoutFrame_ = requestAnimationFrame(function () {
        _this.layoutInternal_();

        _this.layoutFrame_ = 0;
      });
    };
    /** Handles click on the dialog root element. */


    MDCDialogFoundation.prototype.handleClick = function (evt) {
      var isScrim = this.adapter.eventTargetMatches(evt.target, strings$1.SCRIM_SELECTOR); // Check for scrim click first since it doesn't require querying ancestors.

      if (isScrim && this.scrimClickAction_ !== '') {
        this.close(this.scrimClickAction_);
      } else {
        var action = this.adapter.getActionFromEvent(evt);

        if (action) {
          this.close(action);
        }
      }
    };
    /** Handles keydown on the dialog root element. */


    MDCDialogFoundation.prototype.handleKeydown = function (evt) {
      var isEnter = evt.key === 'Enter' || evt.keyCode === 13;

      if (!isEnter) {
        return;
      }

      var action = this.adapter.getActionFromEvent(evt);

      if (action) {
        // Action button callback is handled in `handleClick`,
        // since space/enter keydowns on buttons trigger click events.
        return;
      }

      var isDefault = !this.adapter.eventTargetMatches(evt.target, strings$1.SUPPRESS_DEFAULT_PRESS_SELECTOR);

      if (isEnter && isDefault) {
        this.adapter.clickDefaultButton();
      }
    };
    /** Handles keydown on the document. */


    MDCDialogFoundation.prototype.handleDocumentKeydown = function (evt) {
      var isEscape = evt.key === 'Escape' || evt.keyCode === 27;

      if (isEscape && this.escapeKeyAction_ !== '') {
        this.close(this.escapeKeyAction_);
      }
    };

    MDCDialogFoundation.prototype.layoutInternal_ = function () {
      if (this.autoStackButtons_) {
        this.detectStackedButtons_();
      }

      this.detectScrollableContent_();
    };

    MDCDialogFoundation.prototype.handleAnimationTimerEnd_ = function () {
      this.animationTimer_ = 0;
      this.adapter.removeClass(cssClasses$1.OPENING);
      this.adapter.removeClass(cssClasses$1.CLOSING);
    };
    /**
     * Runs the given logic on the next animation frame, using setTimeout to factor in Firefox reflow behavior.
     */


    MDCDialogFoundation.prototype.runNextAnimationFrame_ = function (callback) {
      var _this = this;

      cancelAnimationFrame(this.animationFrame_);
      this.animationFrame_ = requestAnimationFrame(function () {
        _this.animationFrame_ = 0;
        clearTimeout(_this.animationTimer_);
        _this.animationTimer_ = setTimeout(callback, 0);
      });
    };

    MDCDialogFoundation.prototype.detectStackedButtons_ = function () {
      // Remove the class first to let us measure the buttons' natural positions.
      this.adapter.removeClass(cssClasses$1.STACKED);
      var areButtonsStacked = this.adapter.areButtonsStacked();

      if (areButtonsStacked) {
        this.adapter.addClass(cssClasses$1.STACKED);
      }

      if (areButtonsStacked !== this.areButtonsStacked_) {
        this.adapter.reverseButtons();
        this.areButtonsStacked_ = areButtonsStacked;
      }
    };

    MDCDialogFoundation.prototype.detectScrollableContent_ = function () {
      // Remove the class first to let us measure the natural height of the content.
      this.adapter.removeClass(cssClasses$1.SCROLLABLE);

      if (this.adapter.isContentScrollable()) {
        this.adapter.addClass(cssClasses$1.SCROLLABLE);
      }
    };

    return MDCDialogFoundation;
  }(MDCFoundation);

  /**
   * @license
   * Copyright 2019 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  /**
   * Determine whether the current browser supports passive event listeners, and
   * if so, use them.
   */
  function applyPassive(globalObj) {
    if (globalObj === void 0) {
      globalObj = window;
    }

    return supportsPassiveOption(globalObj) ? {
      passive: true
    } : false;
  }

  function supportsPassiveOption(globalObj) {
    if (globalObj === void 0) {
      globalObj = window;
    } // See
    // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener


    var passiveSupported = false;

    try {
      var options = {
        // This function will be called when the browser
        // attempts to access the passive property.
        get passive() {
          passiveSupported = true;
          return false;
        }

      };

      var handler = function () {};

      globalObj.document.addEventListener('test', handler, options);
      globalObj.document.removeEventListener('test', handler, options);
    } catch (err) {
      passiveSupported = false;
    }

    return passiveSupported;
  }

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  // tslint:disable:no-any

  /**
   * Specifies an observer callback that is run when the decorated property
   * changes. The observer receives the current and old value as arguments.
   */
  const observer$1 = observer => // eslint-disable-next-line @typescript-eslint/no-explicit-any
  (proto, propName) => {
    // if we haven't wrapped `updated` in this class, do so
    if (!proto.constructor._observers) {
      proto.constructor._observers = new Map();
      const userUpdated = proto.updated;

      proto.updated = function (changedProperties) {
        userUpdated.call(this, changedProperties);
        changedProperties.forEach((v, k) => {
          const observers = this.constructor._observers;
          const observer = observers.get(k);

          if (observer !== undefined) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            observer.call(this, this[k], v);
          }
        });
      }; // clone any existing observers (superclasses)

    } else if (!proto.constructor.hasOwnProperty('_observers')) {
      const observers = proto.constructor._observers;
      proto.constructor._observers = new Map();
      observers.forEach( // eslint-disable-next-line @typescript-eslint/no-explicit-any
      (v, k) => proto.constructor._observers.set(k, v));
    } // set this method


    proto.constructor._observers.set(propName, observer);
  };

  const blockingElements = document.$blockingElements;
  class DialogBase extends BaseElement {
    constructor() {
      super(...arguments);
      this.hideActions = false;
      this.stacked = false;
      this.heading = '';
      this.scrimClickAction = 'close';
      this.escapeKeyAction = 'close';
      this.open = false;
      this.defaultAction = 'close';
      this.actionAttribute = 'dialogAction';
      this.initialFocusAttribute = 'dialogInitialFocus';
      this.mdcFoundationClass = MDCDialogFoundation;
      this.boundLayout = null;
      this.boundHandleClick = null;
      this.boundHandleKeydown = null;
      this.boundHandleDocumentKeydown = null;
    }

    get primaryButton() {
      let assignedNodes = this.primarySlot.assignedNodes();
      assignedNodes = assignedNodes.filter(node => node instanceof HTMLElement);
      const button = assignedNodes[0];
      return button ? button : null;
    }

    emitNotification(name, action) {
      const init = {
        detail: action ? {
          action
        } : {}
      };
      const ev = new CustomEvent(name, init);
      this.dispatchEvent(ev);
    }

    getInitialFocusEl() {
      const initFocusSelector = `[${this.initialFocusAttribute}]`; // only search light DOM. This typically handles all the cases

      const lightDomQs = this.querySelector(initFocusSelector);

      if (lightDomQs) {
        return lightDomQs;
      } // if not in light dom, search each flattened distributed node.


      const primarySlot = this.primarySlot;
      const primaryNodes = primarySlot.assignedNodes({
        flatten: true
      });
      const primaryFocusElement = this.searchNodeTreesForAttribute(primaryNodes, this.initialFocusAttribute);

      if (primaryFocusElement) {
        return primaryFocusElement;
      }

      const secondarySlot = this.secondarySlot;
      const secondaryNodes = secondarySlot.assignedNodes({
        flatten: true
      });
      const secondaryFocusElement = this.searchNodeTreesForAttribute(secondaryNodes, this.initialFocusAttribute);

      if (secondaryFocusElement) {
        return secondaryFocusElement;
      }

      const contentSlot = this.contentSlot;
      const contentNodes = contentSlot.assignedNodes({
        flatten: true
      });
      const initFocusElement = this.searchNodeTreesForAttribute(contentNodes, this.initialFocusAttribute);
      return initFocusElement;
    }

    searchNodeTreesForAttribute(nodes, attribute) {
      for (const node of nodes) {
        if (!(node instanceof HTMLElement)) {
          continue;
        }

        if (node.hasAttribute(attribute)) {
          return node;
        } else {
          const selection = node.querySelector(`[${attribute}]`);

          if (selection) {
            return selection;
          }
        }
      }

      return null;
    }

    createAdapter() {
      return Object.assign(Object.assign({}, addHasRemoveClass(this.mdcRoot)), {
        addBodyClass: () => document.body.style.overflow = 'hidden',
        removeBodyClass: () => document.body.style.overflow = '',
        areButtonsStacked: () => this.stacked,
        clickDefaultButton: () => {
          const primary = this.primaryButton;

          if (primary) {
            primary.click();
          }
        },
        eventTargetMatches: (target, selector) => target ? matches$1(target, selector) : false,
        getActionFromEvent: e => {
          if (!e.target) {
            return '';
          }

          const element = closest(e.target, `[${this.actionAttribute}]`);
          const action = element && element.getAttribute(this.actionAttribute);
          return action;
        },
        getInitialFocusEl: () => {
          return this.getInitialFocusEl();
        },
        isContentScrollable: () => {
          const el = this.contentElement;
          return el ? el.scrollHeight > el.offsetHeight : false;
        },
        notifyClosed: action => this.emitNotification('closed', action),
        notifyClosing: action => {
          if (!this.closingDueToDisconnect) {
            // Don't set our open state to closed just because we were
            // disconnected. That way if we get reconnected, we'll know to
            // re-open.
            this.open = false;
          }

          this.emitNotification('closing', action);
        },
        notifyOpened: () => this.emitNotification('opened'),
        notifyOpening: () => {
          this.open = true;
          this.emitNotification('opening');
        },
        reverseButtons: () => {},
        releaseFocus: () => {
          blockingElements.remove(this);
        },
        trapFocus: el => {
          blockingElements.push(this);

          if (el) {
            el.focus();
          }
        }
      });
    }

    render() {
      const classes = {
        [cssClasses$1.STACKED]: this.stacked
      };
      let heading = html``;

      if (this.heading) {
        heading = this.renderHeading();
      }

      const actionsClasses = {
        'mdc-dialog__actions': !this.hideActions
      };
      return html`
    <div class="mdc-dialog ${classMap(classes)}"
        role="alertdialog"
        aria-modal="true"
        aria-labelledby="title"
        aria-describedby="content">
      <div class="mdc-dialog__container">
        <div class="mdc-dialog__surface">
          ${heading}
          <div id="content" class="mdc-dialog__content">
            <slot id="contentSlot"></slot>
          </div>
          <footer
              id="actions"
              class="${classMap(actionsClasses)}">
            <span>
              <slot name="secondaryAction"></slot>
            </span>
            <span>
             <slot name="primaryAction"></slot>
            </span>
          </footer>
        </div>
      </div>
      <div class="mdc-dialog__scrim"></div>
    </div>`;
    }

    renderHeading() {
      return html`
      <h2 id="title" class="mdc-dialog__title">${this.heading}</h2>`;
    }

    firstUpdated() {
      super.firstUpdated();
      this.mdcFoundation.setAutoStackButtons(true);
    }

    connectedCallback() {
      super.connectedCallback();

      if (this.open && this.mdcFoundation && !this.mdcFoundation.isOpen()) {
        // We probably got disconnected while we were still open. Re-open,
        // matching the behavior of native <dialog>.
        this.setEventListeners();
        this.mdcFoundation.open();
      }
    }

    disconnectedCallback() {
      super.disconnectedCallback();

      if (this.open && this.mdcFoundation) {
        // If this dialog is opened and then disconnected, we want to close
        // the foundation, so that 1) any pending timers are cancelled
        // (in particular for trapFocus), and 2) if we reconnect, we can open
        // the foundation again to retrigger animations and focus.
        this.removeEventListeners();
        this.closingDueToDisconnect = true;
        this.mdcFoundation.close(this.currentAction || this.defaultAction);
        this.closingDueToDisconnect = false;
        this.currentAction = undefined; // When we close normally, the releaseFocus callback handles removing
        // ourselves from the blocking elements stack. However, that callback
        // happens on a delay, and when we are closing due to a disconnect we
        // need to remove ourselves before the blocking element polyfill's
        // mutation observer notices and logs a warning, since it's not valid to
        // be in the blocking elements stack while disconnected.

        blockingElements.remove(this);
      }
    }

    forceLayout() {
      this.mdcFoundation.layout();
    }

    focus() {
      const initialFocusEl = this.getInitialFocusEl();
      initialFocusEl && initialFocusEl.focus();
    }

    blur() {
      if (!this.shadowRoot) {
        return;
      }

      const activeEl = this.shadowRoot.activeElement;

      if (activeEl) {
        if (activeEl instanceof HTMLElement) {
          activeEl.blur();
        }
      } else {
        const root = this.getRootNode();
        const activeEl = root instanceof Document ? root.activeElement : null;

        if (activeEl instanceof HTMLElement) {
          activeEl.blur();
        }
      }
    }

    setEventListeners() {
      this.boundHandleClick = this.mdcFoundation.handleClick.bind(this.mdcFoundation);

      this.boundLayout = () => {
        if (this.open) {
          this.mdcFoundation.layout.bind(this.mdcFoundation);
        }
      };

      this.boundHandleKeydown = this.mdcFoundation.handleKeydown.bind(this.mdcFoundation);
      this.boundHandleDocumentKeydown = this.mdcFoundation.handleDocumentKeydown.bind(this.mdcFoundation);
      this.mdcRoot.addEventListener('click', this.boundHandleClick);
      window.addEventListener('resize', this.boundLayout, applyPassive());
      window.addEventListener('orientationchange', this.boundLayout, applyPassive());
      this.mdcRoot.addEventListener('keydown', this.boundHandleKeydown, applyPassive());
      document.addEventListener('keydown', this.boundHandleDocumentKeydown, applyPassive());
    }

    removeEventListeners() {
      if (this.boundHandleClick) {
        this.mdcRoot.removeEventListener('click', this.boundHandleClick);
      }

      if (this.boundLayout) {
        window.removeEventListener('resize', this.boundLayout);
        window.removeEventListener('orientationchange', this.boundLayout);
      }

      if (this.boundHandleKeydown) {
        this.mdcRoot.removeEventListener('keydown', this.boundHandleKeydown);
      }

      if (this.boundHandleDocumentKeydown) {
        this.mdcRoot.removeEventListener('keydown', this.boundHandleDocumentKeydown);
      }
    }

    close() {
      this.open = false;
    }

    show() {
      this.open = true;
    }

  }

  __decorate$5([query('.mdc-dialog')], DialogBase.prototype, "mdcRoot", void 0);

  __decorate$5([query('slot[name="primaryAction"]')], DialogBase.prototype, "primarySlot", void 0);

  __decorate$5([query('slot[name="secondaryAction"]')], DialogBase.prototype, "secondarySlot", void 0);

  __decorate$5([query('#contentSlot')], DialogBase.prototype, "contentSlot", void 0);

  __decorate$5([query('.mdc-dialog__content')], DialogBase.prototype, "contentElement", void 0);

  __decorate$5([query('.mdc-container')], DialogBase.prototype, "conatinerElement", void 0);

  __decorate$5([property({
    type: Boolean
  })], DialogBase.prototype, "hideActions", void 0);

  __decorate$5([property({
    type: Boolean
  }), observer$1(function () {
    this.forceLayout();
  })], DialogBase.prototype, "stacked", void 0);

  __decorate$5([property({
    type: String
  })], DialogBase.prototype, "heading", void 0);

  __decorate$5([property({
    type: String
  }), observer$1(function (newAction) {
    this.mdcFoundation.setScrimClickAction(newAction);
  })], DialogBase.prototype, "scrimClickAction", void 0);

  __decorate$5([property({
    type: String
  }), observer$1(function (newAction) {
    this.mdcFoundation.setEscapeKeyAction(newAction);
  })], DialogBase.prototype, "escapeKeyAction", void 0);

  __decorate$5([property({
    type: Boolean,
    reflect: true
  }), observer$1(function (isOpen) {
    // Check isConnected because we could have been disconnected before first
    // update. If we're now closed, then we shouldn't start the MDC foundation
    // opening animation. If we're now closed, then we've already closed the
    // foundation in disconnectedCallback.
    if (this.mdcFoundation && this.isConnected) {
      if (isOpen) {
        this.setEventListeners();
        this.mdcFoundation.open();
      } else {
        this.removeEventListeners();
        this.mdcFoundation.close(this.currentAction || this.defaultAction);
        this.currentAction = undefined;
      }
    }
  })], DialogBase.prototype, "open", void 0);

  __decorate$5([property()], DialogBase.prototype, "defaultAction", void 0);

  __decorate$5([property()], DialogBase.prototype, "actionAttribute", void 0);

  __decorate$5([property()], DialogBase.prototype, "initialFocusAttribute", void 0);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$6 = css`.mdc-elevation-overlay{position:absolute;border-radius:inherit;pointer-events:none;opacity:0;opacity:var(--mdc-elevation-overlay-opacity, 0);transition:opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);background-color:#fff;background-color:var(--mdc-elevation-overlay-color, #fff)}.mdc-dialog,.mdc-dialog__scrim{position:fixed;top:0;left:0;align-items:center;justify-content:center;box-sizing:border-box;width:100%;height:100%}.mdc-dialog{display:none;z-index:7}.mdc-dialog .mdc-dialog__surface{background-color:#fff;background-color:var(--mdc-theme-surface, #fff)}.mdc-dialog .mdc-dialog__scrim{background-color:rgba(0,0,0,.32)}.mdc-dialog .mdc-dialog__title{color:rgba(0,0,0,.87)}.mdc-dialog .mdc-dialog__content{color:rgba(0,0,0,.6)}.mdc-dialog.mdc-dialog--scrollable .mdc-dialog__title,.mdc-dialog.mdc-dialog--scrollable .mdc-dialog__actions{border-color:rgba(0,0,0,.12)}.mdc-dialog .mdc-dialog__content{padding:20px 24px 20px 24px}.mdc-dialog .mdc-dialog__surface{min-width:280px}@media(max-width: 592px){.mdc-dialog .mdc-dialog__surface{max-width:calc(100vw - 32px)}}@media(min-width: 592px){.mdc-dialog .mdc-dialog__surface{max-width:560px}}.mdc-dialog .mdc-dialog__surface{max-height:calc(100% - 32px)}.mdc-dialog .mdc-dialog__surface{border-radius:4px;border-radius:var(--mdc-shape-medium, 4px)}.mdc-dialog__scrim{opacity:0;z-index:-1}.mdc-dialog__container{display:flex;flex-direction:row;align-items:center;justify-content:space-around;box-sizing:border-box;height:100%;transform:scale(0.8);opacity:0;pointer-events:none}.mdc-dialog__surface{position:relative;box-shadow:0px 11px 15px -7px rgba(0, 0, 0, 0.2),0px 24px 38px 3px rgba(0, 0, 0, 0.14),0px 9px 46px 8px rgba(0,0,0,.12);display:flex;flex-direction:column;flex-grow:0;flex-shrink:0;box-sizing:border-box;max-width:100%;max-height:100%;pointer-events:auto;overflow-y:auto}.mdc-dialog__surface .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}.mdc-dialog[dir=rtl] .mdc-dialog__surface,[dir=rtl] .mdc-dialog .mdc-dialog__surface{text-align:right}.mdc-dialog__title{display:block;margin-top:0;line-height:normal;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-headline6-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1.25rem;font-size:var(--mdc-typography-headline6-font-size, 1.25rem);line-height:2rem;line-height:var(--mdc-typography-headline6-line-height, 2rem);font-weight:500;font-weight:var(--mdc-typography-headline6-font-weight, 500);letter-spacing:0.0125em;letter-spacing:var(--mdc-typography-headline6-letter-spacing, 0.0125em);text-decoration:inherit;text-decoration:var(--mdc-typography-headline6-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-headline6-text-transform, inherit);position:relative;flex-shrink:0;box-sizing:border-box;margin:0;padding:0 24px 9px;border-bottom:1px solid transparent}.mdc-dialog__title::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-dialog[dir=rtl] .mdc-dialog__title,[dir=rtl] .mdc-dialog .mdc-dialog__title{text-align:right}.mdc-dialog--scrollable .mdc-dialog__title{padding-bottom:15px}.mdc-dialog__content{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-body1-font-size, 1rem);line-height:1.5rem;line-height:var(--mdc-typography-body1-line-height, 1.5rem);font-weight:400;font-weight:var(--mdc-typography-body1-font-weight, 400);letter-spacing:0.03125em;letter-spacing:var(--mdc-typography-body1-letter-spacing, 0.03125em);text-decoration:inherit;text-decoration:var(--mdc-typography-body1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body1-text-transform, inherit);flex-grow:1;box-sizing:border-box;margin:0;overflow:auto;-webkit-overflow-scrolling:touch}.mdc-dialog__content>:first-child{margin-top:0}.mdc-dialog__content>:last-child{margin-bottom:0}.mdc-dialog__title+.mdc-dialog__content{padding-top:0}.mdc-dialog--scrollable .mdc-dialog__title+.mdc-dialog__content{padding-top:8px;padding-bottom:8px}.mdc-dialog__content .mdc-list:first-child:last-child{padding:6px 0 0}.mdc-dialog--scrollable .mdc-dialog__content .mdc-list:first-child:last-child{padding:0}.mdc-dialog__actions{display:flex;position:relative;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;box-sizing:border-box;min-height:52px;margin:0;padding:8px;border-top:1px solid transparent}.mdc-dialog--stacked .mdc-dialog__actions{flex-direction:column;align-items:flex-end}.mdc-dialog__button{margin-left:8px;margin-right:0;max-width:100%;text-align:right}[dir=rtl] .mdc-dialog__button,.mdc-dialog__button[dir=rtl]{margin-left:0;margin-right:8px}.mdc-dialog__button:first-child{margin-left:0;margin-right:0}[dir=rtl] .mdc-dialog__button:first-child,.mdc-dialog__button:first-child[dir=rtl]{margin-left:0;margin-right:0}.mdc-dialog[dir=rtl] .mdc-dialog__button,[dir=rtl] .mdc-dialog .mdc-dialog__button{text-align:left}.mdc-dialog--stacked .mdc-dialog__button:not(:first-child){margin-top:12px}.mdc-dialog--open,.mdc-dialog--opening,.mdc-dialog--closing{display:flex}.mdc-dialog--opening .mdc-dialog__scrim{transition:opacity 150ms linear}.mdc-dialog--opening .mdc-dialog__container{transition:opacity 75ms linear,transform 150ms 0ms cubic-bezier(0, 0, 0.2, 1)}.mdc-dialog--closing .mdc-dialog__scrim,.mdc-dialog--closing .mdc-dialog__container{transition:opacity 75ms linear}.mdc-dialog--closing .mdc-dialog__container{transform:none}.mdc-dialog--open .mdc-dialog__scrim{opacity:1}.mdc-dialog--open .mdc-dialog__container{transform:none;opacity:1}.mdc-dialog-scroll-lock{overflow:hidden}#actions:not(.mdc-dialog__actions){display:none}.mdc-dialog__surface{box-shadow:var(--mdc-dialog-box-shadow, 0px 11px 15px -7px rgba(0, 0, 0, 0.2), 0px 24px 38px 3px rgba(0, 0, 0, 0.14), 0px 9px 46px 8px rgba(0, 0, 0, 0.12))}@media(min-width: 560px){.mdc-dialog .mdc-dialog__surface{max-width:560px;max-width:var(--mdc-dialog-max-width, 560px)}}.mdc-dialog .mdc-dialog__scrim{background-color:rgba(0, 0, 0, 0.32);background-color:var(--mdc-dialog-scrim-color, rgba(0, 0, 0, 0.32))}.mdc-dialog .mdc-dialog__title{color:rgba(0, 0, 0, 0.87);color:var(--mdc-dialog-heading-ink-color, rgba(0, 0, 0, 0.87))}.mdc-dialog .mdc-dialog__content{color:rgba(0, 0, 0, 0.6);color:var(--mdc-dialog-content-ink-color, rgba(0, 0, 0, 0.6))}.mdc-dialog.mdc-dialog--scrollable .mdc-dialog__title,.mdc-dialog.mdc-dialog--scrollable .mdc-dialog__actions{border-color:rgba(0, 0, 0, 0.12);border-color:var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12))}.mdc-dialog .mdc-dialog__surface{min-width:280px;min-width:var(--mdc-dialog-min-width, 280px)}.mdc-dialog .mdc-dialog__surface{max-height:var(--mdc-dialog-max-height, calc(100% - 32px))}#actions ::slotted(*){margin-left:8px;margin-right:0;max-width:100%;text-align:right}[dir=rtl] #actions ::slotted(*),#actions ::slotted(*)[dir=rtl]{margin-left:0;margin-right:8px}.mdc-dialog[dir=rtl] #actions ::slotted(*),[dir=rtl] .mdc-dialog #actions ::slotted(*){text-align:left}.mdc-dialog--stacked #actions{flex-direction:column-reverse}.mdc-dialog--stacked #actions *:not(:last-child) ::slotted(*){flex-basis:1e-9px;margin-top:12px}`;

  let Dialog = class Dialog extends DialogBase {};
  Dialog.styles = style$6;
  Dialog = __decorate$5([customElement('mwc-dialog')], Dialog);

  const MwcDialog = customElements.get("mwc-dialog");
  const createCloseHeading = (hass, title) => html`
  <span class="header_title">${title}</span>
  <mwc-icon-button
    aria-label=${hass.localize("ui.dialogs.generic.close")}
    dialogAction="close"
    class="header_button"
    dir=${computeRTLDirection(hass)}
  >
    <ha-svg-icon path=${mdiClose}></ha-svg-icon>
  </mwc-icon-button>
`;
  let HaDialog = _decorate([customElement("ha-dialog")], function (_initialize, _MwcDialog) {
    class HaDialog extends _MwcDialog {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaDialog,
      d: [{
        kind: "method",
        key: "scrollToPos",
        value: function scrollToPos(x, y) {
          this.contentElement.scrollTo(x, y);
        }
      }, {
        kind: "method",
        key: "renderHeading",
        value: function renderHeading() {
          return html`<slot name="heading">
      ${_get(_getPrototypeOf(HaDialog.prototype), "renderHeading", this).call(this)}
    </slot>`;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [style$6, css`
        .mdc-dialog {
          --mdc-dialog-scroll-divider-color: var(--divider-color);
          z-index: var(--dialog-z-index, 7);
        }
        .mdc-dialog__actions {
          justify-content: var(--justify-action-buttons, flex-end);
          padding-bottom: max(env(safe-area-inset-bottom), 8px);
        }
        .mdc-dialog__container {
          align-items: var(--vertial-align-dialog, center);
        }
        .mdc-dialog__title::before {
          display: block;
          height: 20px;
        }
        .mdc-dialog .mdc-dialog__content {
          position: var(--dialog-content-position, relative);
          padding: var(--dialog-content-padding, 20px 24px);
        }
        :host([hideactions]) .mdc-dialog .mdc-dialog__content {
          padding-bottom: max(
            var(--dialog-content-padding, 20px),
            env(safe-area-inset-bottom)
          );
        }
        .mdc-dialog .mdc-dialog__surface {
          position: var(--dialog-surface-position, relative);
          top: var(--dialog-surface-top);
          min-height: var(--mdc-dialog-min-height, auto);
        }
        :host([flexContent]) .mdc-dialog .mdc-dialog__content {
          display: flex;
          flex-direction: column;
        }
        .header_button {
          position: absolute;
          right: 16px;
          top: 10px;
          text-decoration: none;
          color: inherit;
        }
        .header_title {
          margin-right: 40px;
        }
        [dir="rtl"].header_button {
          right: auto;
          left: 16px;
        }
        [dir="rtl"].header_title {
          margin-left: 40px;
          margin-right: 0px;
        }
      `];
        }
      }]
    };
  }, MwcDialog);

  let HacsDialog = _decorate([customElement("hacs-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsDialog,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "hideActions",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "scrimClickAction",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "escapeKeyAction",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "noClose",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "title",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) {
            return html``;
          }

          return html` <ha-dialog
      ?open=${this.active}
      ?scrimClickAction=${this.scrimClickAction}
      ?escapeKeyAction=${this.escapeKeyAction}
      @closed=${this.closeDialog}
      ?hideActions=${this.hideActions}
      .heading=${!this.noClose ? createCloseHeading(this.hass, this.title) : this.title}
    >
      <div class="content" ?narrow=${this.narrow}>
        <slot></slot>
      </div>
      <slot class="primary" name="primaryaction" slot="primaryAction"></slot>
      <slot class="secondary" name="secondaryaction" slot="secondaryAction"></slot>
    </ha-dialog>`;
        }
      }, {
        kind: "method",
        key: "closeDialog",
        value: function closeDialog() {
          this.active = false;
          this.dispatchEvent(new CustomEvent("closed", {
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [hacsStyleDialog, scrollBarStyle, HacsStyles, css`
        .content {
          overflow: auto;
        }
        ha-dialog {
          --mdc-dialog-max-width: var(--hacs-dialog-max-width, 990px);
          --mdc-dialog-min-width: var(--hacs-dialog-max-width, 280px);
        }
        .primary {
          margin-left: 52px;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  let HacsAboutDialog = _decorate([customElement("hacs-about-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsAboutDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsAboutDialog,
      d: [{
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;
          return html`
      <hacs-dialog
        .active=${this.active}
        .hass=${this.hass}
        .title=${this.narrow ? "HACS" : "Home Assistant Community Store"}
        hideActions
      >
        <div class="content">
          ${markdown.html(`
**${localize("dialog_about.integration_version")}:** | ${this.hacs.configuration.version}
--|--
**${localize("dialog_about.frontend_version")}:** | ${version$1}
**${localize("common.repositories")}:** | ${this.repositories.length}
**${localize("dialog_about.installed_repositories")}:** | ${this.repositories.filter(repo => repo.installed).length}

**${localize("dialog_about.useful_links")}:**

- [General documentation](https://hacs.xyz/)
- [Configuration](https://hacs.xyz/docs/configuration/start)
- [FAQ](https://hacs.xyz/docs/faq/what)
- [GitHub](https://github.com/hacs)
- [Discord](https://discord.gg/apgchf8)
- [Become a GitHub sponsor? ❤️](https://github.com/sponsors/ludeeus)
- [BuyMe~~Coffee~~Beer? 🍺🙈](https://buymeacoffee.com/ludeeus)

***

_Everything you find in HACS is **not** tested by Home Assistant, that includes HACS itself._
_The HACS and Home Assistant teams do not support **anything** you find here._
        `)}
        </div>
      </hacs-dialog>
    `;
        }
      }]
    };
  }, HacsDialogBase);

  /**
  @license
  Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  var Utility = {
    distance: function (x1, y1, x2, y2) {
      var xDelta = x1 - x2;
      var yDelta = y1 - y2;
      return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
    },
    now: window.performance && window.performance.now ? window.performance.now.bind(window.performance) : Date.now
  };
  /**
   * @param {HTMLElement} element
   * @constructor
   */

  function ElementMetrics(element) {
    this.element = element;
    this.width = this.boundingRect.width;
    this.height = this.boundingRect.height;
    this.size = Math.max(this.width, this.height);
  }

  ElementMetrics.prototype = {
    get boundingRect() {
      return this.element.getBoundingClientRect();
    },

    furthestCornerDistanceFrom: function (x, y) {
      var topLeft = Utility.distance(x, y, 0, 0);
      var topRight = Utility.distance(x, y, this.width, 0);
      var bottomLeft = Utility.distance(x, y, 0, this.height);
      var bottomRight = Utility.distance(x, y, this.width, this.height);
      return Math.max(topLeft, topRight, bottomLeft, bottomRight);
    }
  };
  /**
   * @param {HTMLElement} element
   * @constructor
   */

  function Ripple$1(element) {
    this.element = element;
    this.color = window.getComputedStyle(element).color;
    this.wave = document.createElement('div');
    this.waveContainer = document.createElement('div');
    this.wave.style.backgroundColor = this.color;
    this.wave.classList.add('wave');
    this.waveContainer.classList.add('wave-container');
    dom(this.waveContainer).appendChild(this.wave);
    this.resetInteractionState();
  }

  Ripple$1.MAX_RADIUS = 300;
  Ripple$1.prototype = {
    get recenters() {
      return this.element.recenters;
    },

    get center() {
      return this.element.center;
    },

    get mouseDownElapsed() {
      var elapsed;

      if (!this.mouseDownStart) {
        return 0;
      }

      elapsed = Utility.now() - this.mouseDownStart;

      if (this.mouseUpStart) {
        elapsed -= this.mouseUpElapsed;
      }

      return elapsed;
    },

    get mouseUpElapsed() {
      return this.mouseUpStart ? Utility.now() - this.mouseUpStart : 0;
    },

    get mouseDownElapsedSeconds() {
      return this.mouseDownElapsed / 1000;
    },

    get mouseUpElapsedSeconds() {
      return this.mouseUpElapsed / 1000;
    },

    get mouseInteractionSeconds() {
      return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;
    },

    get initialOpacity() {
      return this.element.initialOpacity;
    },

    get opacityDecayVelocity() {
      return this.element.opacityDecayVelocity;
    },

    get radius() {
      var width2 = this.containerMetrics.width * this.containerMetrics.width;
      var height2 = this.containerMetrics.height * this.containerMetrics.height;
      var waveRadius = Math.min(Math.sqrt(width2 + height2), Ripple$1.MAX_RADIUS) * 1.1 + 5;
      var duration = 1.1 - 0.2 * (waveRadius / Ripple$1.MAX_RADIUS);
      var timeNow = this.mouseInteractionSeconds / duration;
      var size = waveRadius * (1 - Math.pow(80, -timeNow));
      return Math.abs(size);
    },

    get opacity() {
      if (!this.mouseUpStart) {
        return this.initialOpacity;
      }

      return Math.max(0, this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVelocity);
    },

    get outerOpacity() {
      // Linear increase in background opacity, capped at the opacity
      // of the wavefront (waveOpacity).
      var outerOpacity = this.mouseUpElapsedSeconds * 0.3;
      var waveOpacity = this.opacity;
      return Math.max(0, Math.min(outerOpacity, waveOpacity));
    },

    get isOpacityFullyDecayed() {
      return this.opacity < 0.01 && this.radius >= Math.min(this.maxRadius, Ripple$1.MAX_RADIUS);
    },

    get isRestingAtMaxRadius() {
      return this.opacity >= this.initialOpacity && this.radius >= Math.min(this.maxRadius, Ripple$1.MAX_RADIUS);
    },

    get isAnimationComplete() {
      return this.mouseUpStart ? this.isOpacityFullyDecayed : this.isRestingAtMaxRadius;
    },

    get translationFraction() {
      return Math.min(1, this.radius / this.containerMetrics.size * 2 / Math.sqrt(2));
    },

    get xNow() {
      if (this.xEnd) {
        return this.xStart + this.translationFraction * (this.xEnd - this.xStart);
      }

      return this.xStart;
    },

    get yNow() {
      if (this.yEnd) {
        return this.yStart + this.translationFraction * (this.yEnd - this.yStart);
      }

      return this.yStart;
    },

    get isMouseDown() {
      return this.mouseDownStart && !this.mouseUpStart;
    },

    resetInteractionState: function () {
      this.maxRadius = 0;
      this.mouseDownStart = 0;
      this.mouseUpStart = 0;
      this.xStart = 0;
      this.yStart = 0;
      this.xEnd = 0;
      this.yEnd = 0;
      this.slideDistance = 0;
      this.containerMetrics = new ElementMetrics(this.element);
    },
    draw: function () {
      var scale;
      var dx;
      var dy;
      this.wave.style.opacity = this.opacity;
      scale = this.radius / (this.containerMetrics.size / 2);
      dx = this.xNow - this.containerMetrics.width / 2;
      dy = this.yNow - this.containerMetrics.height / 2; // 2d transform for safari because of border-radius and overflow:hidden
      // clipping bug. https://bugs.webkit.org/show_bug.cgi?id=98538

      this.waveContainer.style.webkitTransform = 'translate(' + dx + 'px, ' + dy + 'px)';
      this.waveContainer.style.transform = 'translate3d(' + dx + 'px, ' + dy + 'px, 0)';
      this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')';
      this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)';
    },

    /** @param {Event=} event */
    downAction: function (event) {
      var xCenter = this.containerMetrics.width / 2;
      var yCenter = this.containerMetrics.height / 2;
      this.resetInteractionState();
      this.mouseDownStart = Utility.now();

      if (this.center) {
        this.xStart = xCenter;
        this.yStart = yCenter;
        this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd);
      } else {
        this.xStart = event ? event.detail.x - this.containerMetrics.boundingRect.left : this.containerMetrics.width / 2;
        this.yStart = event ? event.detail.y - this.containerMetrics.boundingRect.top : this.containerMetrics.height / 2;
      }

      if (this.recenters) {
        this.xEnd = xCenter;
        this.yEnd = yCenter;
        this.slideDistance = Utility.distance(this.xStart, this.yStart, this.xEnd, this.yEnd);
      }

      this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(this.xStart, this.yStart);
      this.waveContainer.style.top = (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px';
      this.waveContainer.style.left = (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';
      this.waveContainer.style.width = this.containerMetrics.size + 'px';
      this.waveContainer.style.height = this.containerMetrics.size + 'px';
    },

    /** @param {Event=} event */
    upAction: function (event) {
      if (!this.isMouseDown) {
        return;
      }

      this.mouseUpStart = Utility.now();
    },
    remove: function () {
      dom(dom(this.waveContainer).parentNode).removeChild(this.waveContainer);
    }
  };
  /**
  Material design: [Surface
  reaction](https://www.google.com/design/spec/animation/responsive-interaction.html#responsive-interaction-surface-reaction)

  `paper-ripple` provides a visual effect that other paper elements can
  use to simulate a rippling effect emanating from the point of contact.  The
  effect can be visualized as a concentric circle with motion.

  Example:

      <div style="position:relative">
        <paper-ripple></paper-ripple>
      </div>

  Note, it's important that the parent container of the ripple be relative
  position, otherwise the ripple will emanate outside of the desired container.

  `paper-ripple` listens to "mousedown" and "mouseup" events so it would display
  ripple effect when touches on it.  You can also defeat the default behavior and
  manually route the down and up actions to the ripple element.  Note that it is
  important if you call `downAction()` you will have to make sure to call
  `upAction()` so that `paper-ripple` would end the animation loop.

  Example:

      <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple>
      ...
      downAction: function(e) {
        this.$.ripple.downAction(e.detail);
      },
      upAction: function(e) {
        this.$.ripple.upAction();
      }

  Styling ripple effect:

    Use CSS color property to style the ripple:

      paper-ripple {
        color: #4285f4;
      }

    Note that CSS color property is inherited so it is not required to set it on
    the `paper-ripple` element directly.

  By default, the ripple is centered on the point of contact.  Apply the
  `recenters` attribute to have the ripple grow toward the center of its
  container.

      <paper-ripple recenters></paper-ripple>

  You can also  center the ripple inside its container from the start.

      <paper-ripple center></paper-ripple>

  Apply `circle` class to make the rippling effect within a circle.

      <paper-ripple class="circle"></paper-ripple>

  @element paper-ripple
  @demo demo/index.html
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style>
      :host {
        display: block;
        position: absolute;
        border-radius: inherit;
        overflow: hidden;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;

        /* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers,
         * creating a node (with a position:absolute) in the middle of an event
         * handler "interrupts" that event handler (which happens when the
         * ripple is created on demand) */
        pointer-events: none;
      }

      :host([animating]) {
        /* This resolves a rendering issue in Chrome (as of 40) where the
           ripple is not properly clipped by its parent (which may have
           rounded corners). See: http://jsbin.com/temexa/4

           Note: We only apply this style conditionally. Otherwise, the browser
           will create a new compositing layer for every ripple element on the
           page, and that would be bad. */
        -webkit-transform: translate(0, 0);
        transform: translate3d(0, 0, 0);
      }

      #background,
      #waves,
      .wave-container,
      .wave {
        pointer-events: none;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
      }

      #background,
      .wave {
        opacity: 0;
      }

      #waves,
      .wave {
        overflow: hidden;
      }

      .wave-container,
      .wave {
        border-radius: 50%;
      }

      :host(.circle) #background,
      :host(.circle) #waves {
        border-radius: 50%;
      }

      :host(.circle) .wave-container {
        overflow: hidden;
      }
    </style>

    <div id="background"></div>
    <div id="waves"></div>
`,
    is: 'paper-ripple',
    behaviors: [IronA11yKeysBehavior],
    properties: {
      /**
       * The initial opacity set on the wave.
       * @type number
       * @default 0.25
       */
      initialOpacity: {
        type: Number,
        value: 0.25
      },

      /**
       * How fast (opacity per second) the wave fades out.
       *
       * @type number
       * @default 0.8
       */
      opacityDecayVelocity: {
        type: Number,
        value: 0.8
      },

      /**
       * If true, ripples will exhibit a gravitational pull towards
       * the center of their container as they fade away.
       *
       * @type boolean
       * @default false
       */
      recenters: {
        type: Boolean,
        value: false
      },

      /**
       * If true, ripples will center inside its container
       *
       * @type boolean
       * @default false
       */
      center: {
        type: Boolean,
        value: false
      },

      /**
       * A list of the visual ripples.
       *
       * @type Array
       * @default []
       */
      ripples: {
        type: Array,
        value: function () {
          return [];
        }
      },

      /**
       * True when there are visible ripples animating within the
       * element.
       */
      animating: {
        type: Boolean,
        readOnly: true,
        reflectToAttribute: true,
        value: false
      },

      /**
       * If true, the ripple will remain in the "down" state until `holdDown`
       * is set to false again.
       */
      holdDown: {
        type: Boolean,
        value: false,
        observer: '_holdDownChanged'
      },

      /**
       * If true, the ripple will not generate a ripple effect
       * via pointer interaction.
       * Calling ripple's imperative api like `simulatedRipple` will
       * still generate the ripple effect.
       */
      noink: {
        type: Boolean,
        value: false
      },
      _animating: {
        type: Boolean
      },
      _boundAnimate: {
        type: Function,
        value: function () {
          return this.animate.bind(this);
        }
      }
    },

    get target() {
      return this.keyEventTarget;
    },

    /**
     * @type {!Object}
     */
    keyBindings: {
      'enter:keydown': '_onEnterKeydown',
      'space:keydown': '_onSpaceKeydown',
      'space:keyup': '_onSpaceKeyup'
    },

    /** @override */
    attached: function () {
      // Set up a11yKeysBehavior to listen to key events on the target,
      // so that space and enter activate the ripple even if the target doesn't
      // handle key events. The key handlers deal with `noink` themselves.
      if (dom(this).parentNode.nodeType == 11) {
        // DOCUMENT_FRAGMENT_NODE
        this.keyEventTarget = dom(this).getOwnerRoot().host;
      } else {
        this.keyEventTarget = dom(this).parentNode;
      }

      var keyEventTarget =
      /** @type {!EventTarget} */
      this.keyEventTarget;
      this.listen(keyEventTarget, 'up', 'uiUpAction');
      this.listen(keyEventTarget, 'down', 'uiDownAction');
    },

    /** @override */
    detached: function () {
      this.unlisten(this.keyEventTarget, 'up', 'uiUpAction');
      this.unlisten(this.keyEventTarget, 'down', 'uiDownAction');
      this.keyEventTarget = null;
    },

    get shouldKeepAnimating() {
      for (var index = 0; index < this.ripples.length; ++index) {
        if (!this.ripples[index].isAnimationComplete) {
          return true;
        }
      }

      return false;
    },

    simulatedRipple: function () {
      this.downAction(null); // Please see polymer/polymer#1305

      this.async(function () {
        this.upAction();
      }, 1);
    },

    /**
     * Provokes a ripple down effect via a UI event,
     * respecting the `noink` property.
     * @param {Event=} event
     */
    uiDownAction: function (event) {
      if (!this.noink) {
        this.downAction(event);
      }
    },

    /**
     * Provokes a ripple down effect via a UI event,
     * *not* respecting the `noink` property.
     * @param {Event=} event
     */
    downAction: function (event) {
      if (this.holdDown && this.ripples.length > 0) {
        return;
      }

      var ripple = this.addRipple();
      ripple.downAction(event);

      if (!this._animating) {
        this._animating = true;
        this.animate();
      }
    },

    /**
     * Provokes a ripple up effect via a UI event,
     * respecting the `noink` property.
     * @param {Event=} event
     */
    uiUpAction: function (event) {
      if (!this.noink) {
        this.upAction(event);
      }
    },

    /**
     * Provokes a ripple up effect via a UI event,
     * *not* respecting the `noink` property.
     * @param {Event=} event
     */
    upAction: function (event) {
      if (this.holdDown) {
        return;
      }

      this.ripples.forEach(function (ripple) {
        ripple.upAction(event);
      });
      this._animating = true;
      this.animate();
    },
    onAnimationComplete: function () {
      this._animating = false;
      this.$.background.style.backgroundColor = '';
      this.fire('transitionend');
    },
    addRipple: function () {
      var ripple = new Ripple$1(this);
      dom(this.$.waves).appendChild(ripple.waveContainer);
      this.$.background.style.backgroundColor = ripple.color;
      this.ripples.push(ripple);

      this._setAnimating(true);

      return ripple;
    },
    removeRipple: function (ripple) {
      var rippleIndex = this.ripples.indexOf(ripple);

      if (rippleIndex < 0) {
        return;
      }

      this.ripples.splice(rippleIndex, 1);
      ripple.remove();

      if (!this.ripples.length) {
        this._setAnimating(false);
      }
    },

    /**
     * Deprecated. Please use animateRipple() instead.
     *
     * This method name conflicts with Element#animate().
     * https://developer.mozilla.org/en-US/docs/Web/API/Element/animate.
     *
     * @suppress {checkTypes}
     * @override
     */
    animate: function () {
      if (!this._animating) {
        return;
      }

      var index;
      var ripple;

      for (index = 0; index < this.ripples.length; ++index) {
        ripple = this.ripples[index];
        ripple.draw();
        this.$.background.style.opacity = ripple.outerOpacity;

        if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
          this.removeRipple(ripple);
        }
      }

      if (!this.shouldKeepAnimating && this.ripples.length === 0) {
        this.onAnimationComplete();
      } else {
        window.requestAnimationFrame(this._boundAnimate);
      }
    },

    /**
     * An alias for animate() whose name does not conflict with the platform
     * Element.animate() method.
     */
    animateRipple: function () {
      return this.animate();
    },
    _onEnterKeydown: function () {
      this.uiDownAction();
      this.async(this.uiUpAction, 1);
    },
    _onSpaceKeydown: function () {
      this.uiDownAction();
    },
    _onSpaceKeyup: function () {
      this.uiUpAction();
    },
    // note: holdDown does not respect noink since it can be a focus based
    // effect.
    _holdDownChanged: function (newVal, oldVal) {
      if (oldVal === undefined) {
        return;
      }

      if (newVal) {
        this.downAction();
      } else {
        this.upAction();
      }
    }
    /**
    Fired when the animation finishes.
    This is useful if you want to wait until
    the ripple animation finishes to perform some action.
     @event transitionend
    @param {{node: Object}} detail Contains the animated node.
    */

  });

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  /**
   * The `iron-iconset-svg` element allows users to define their own icon sets
   * that contain svg icons. The svg icon elements should be children of the
   * `iron-iconset-svg` element. Multiple icons should be given distinct id's.
   *
   * Using svg elements to create icons has a few advantages over traditional
   * bitmap graphics like jpg or png. Icons that use svg are vector based so
   * they are resolution independent and should look good on any device. They
   * are stylable via css. Icons can be themed, colorized, and even animated.
   *
   * Example:
   *
   *     <iron-iconset-svg name="my-svg-icons" size="24">
   *       <svg>
   *         <defs>
   *           <g id="shape">
   *             <rect x="12" y="0" width="12" height="24" />
   *             <circle cx="12" cy="12" r="12" />
   *           </g>
   *         </defs>
   *       </svg>
   *     </iron-iconset-svg>
   *
   * This will automatically register the icon set "my-svg-icons" to the iconset
   * database.  To use these icons from within another element, make a
   * `iron-iconset` element and call the `byId` method
   * to retrieve a given iconset. To apply a particular icon inside an
   * element use the `applyIcon` method. For example:
   *
   *     iconset.applyIcon(iconNode, 'car');
   *
   * @element iron-iconset-svg
   * @demo demo/index.html
   * @implements {Polymer.Iconset}
   */

  Polymer({
    is: 'iron-iconset-svg',
    properties: {
      /**
       * The name of the iconset.
       */
      name: {
        type: String,
        observer: '_nameChanged'
      },

      /**
       * The size of an individual icon. Note that icons must be square.
       */
      size: {
        type: Number,
        value: 24
      },

      /**
       * Set to true to enable mirroring of icons where specified when they are
       * stamped. Icons that should be mirrored should be decorated with a
       * `mirror-in-rtl` attribute.
       *
       * NOTE: For performance reasons, direction will be resolved once per
       * document per iconset, so moving icons in and out of RTL subtrees will
       * not cause their mirrored state to change.
       */
      rtlMirroring: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to measure RTL based on the dir attribute on the body or
       * html elements (measured on document.body or document.documentElement as
       * available).
       */
      useGlobalRtlAttribute: {
        type: Boolean,
        value: false
      }
    },
    created: function () {
      this._meta = new IronMeta({
        type: 'iconset',
        key: null,
        value: null
      });
    },
    attached: function () {
      this.style.display = 'none';
    },

    /**
     * Construct an array of all icon names in this iconset.
     *
     * @return {!Array} Array of icon names.
     */
    getIconNames: function () {
      this._icons = this._createIconMap();
      return Object.keys(this._icons).map(function (n) {
        return this.name + ':' + n;
      }, this);
    },

    /**
     * Applies an icon to the given element.
     *
     * An svg icon is prepended to the element's shadowRoot if it exists,
     * otherwise to the element itself.
     *
     * If RTL mirroring is enabled, and the icon is marked to be mirrored in
     * RTL, the element will be tested (once and only once ever for each
     * iconset) to determine the direction of the subtree the element is in.
     * This direction will apply to all future icon applications, although only
     * icons marked to be mirrored will be affected.
     *
     * @method applyIcon
     * @param {Element} element Element to which the icon is applied.
     * @param {string} iconName Name of the icon to apply.
     * @return {?Element} The svg element which renders the icon.
     */
    applyIcon: function (element, iconName) {
      // Remove old svg element
      this.removeIcon(element); // install new svg element

      var svg = this._cloneIcon(iconName, this.rtlMirroring && this._targetIsRTL(element));

      if (svg) {
        // insert svg element into shadow root, if it exists
        var pde = dom(element.root || element);
        pde.insertBefore(svg, pde.childNodes[0]);
        return element._svgIcon = svg;
      }

      return null;
    },

    /**
     * Remove an icon from the given element by undoing the changes effected
     * by `applyIcon`.
     *
     * @param {Element} element The element from which the icon is removed.
     */
    removeIcon: function (element) {
      // Remove old svg element
      if (element._svgIcon) {
        dom(element.root || element).removeChild(element._svgIcon);
        element._svgIcon = null;
      }
    },

    /**
     * Measures and memoizes the direction of the element. Note that this
     * measurement is only done once and the result is memoized for future
     * invocations.
     */
    _targetIsRTL: function (target) {
      if (this.__targetIsRTL == null) {
        if (this.useGlobalRtlAttribute) {
          var globalElement = document.body && document.body.hasAttribute('dir') ? document.body : document.documentElement;
          this.__targetIsRTL = globalElement.getAttribute('dir') === 'rtl';
        } else {
          if (target && target.nodeType !== Node.ELEMENT_NODE) {
            target = target.host;
          }

          this.__targetIsRTL = target && window.getComputedStyle(target)['direction'] === 'rtl';
        }
      }

      return this.__targetIsRTL;
    },

    /**
     *
     * When name is changed, register iconset metadata
     *
     */
    _nameChanged: function () {
      this._meta.value = null;
      this._meta.key = this.name;
      this._meta.value = this;
      this.async(function () {
        this.fire('iron-iconset-added', this, {
          node: window
        });
      });
    },

    /**
     * Create a map of child SVG elements by id.
     *
     * @return {!Object} Map of id's to SVG elements.
     */
    _createIconMap: function () {
      // Objects chained to Object.prototype (`{}`) have members. Specifically,
      // on FF there is a `watch` method that confuses the icon map, so we
      // need to use a null-based object here.
      var icons = Object.create(null);
      dom(this).querySelectorAll('[id]').forEach(function (icon) {
        icons[icon.id] = icon;
      });
      return icons;
    },

    /**
     * Produce installable clone of the SVG element matching `id` in this
     * iconset, or `undefined` if there is no matching element.
     *
     * @return {Element} Returns an installable clone of the SVG element
     * matching `id`.
     */
    _cloneIcon: function (id, mirrorAllowed) {
      // create the icon map on-demand, since the iconset itself has no discrete
      // signal to know when it's children are fully parsed
      this._icons = this._icons || this._createIconMap();
      return this._prepareSvgClone(this._icons[id], this.size, mirrorAllowed);
    },

    /**
     * @param {Element} sourceSvg
     * @param {number} size
     * @param {Boolean} mirrorAllowed
     * @return {Element}
     */
    _prepareSvgClone: function (sourceSvg, size, mirrorAllowed) {
      if (sourceSvg) {
        var content = sourceSvg.cloneNode(true),
            svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
            viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size,
            cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';

        if (mirrorAllowed && content.hasAttribute('mirror-in-rtl')) {
          cssText += '-webkit-transform:scale(-1,1);transform:scale(-1,1);transform-origin:center;';
        }

        svg.setAttribute('viewBox', viewBox);
        svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
        svg.setAttribute('focusable', 'false'); // TODO(dfreedm): `pointer-events: none` works around
        // https://crbug.com/370136
        // TODO(sjmiles): inline style may not be ideal, but avoids requiring a
        // shadow-root

        svg.style.cssText = cssText;
        svg.appendChild(content).removeAttribute('id');
        return svg;
      }

      return null;
    }
  });

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const $_documentContainer$1 = document.createElement('template');
  $_documentContainer$1.setAttribute('style', 'display: none;');
  $_documentContainer$1.innerHTML = `<iron-iconset-svg name="paper-dropdown-menu" size="24">
<svg><defs>
<g id="arrow-drop-down"><path d="M7 10l5 5 5-5z"></path></g>
</defs></svg>
</iron-iconset-svg>`;
  document.head.appendChild($_documentContainer$1.content);

  /**
  @license
  Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  const $_documentContainer$2 = document.createElement('template');
  $_documentContainer$2.setAttribute('style', 'display: none;');
  $_documentContainer$2.innerHTML = `<dom-module id="paper-dropdown-menu-shared-styles">
  <template>
    <style>
      :host {
        display: inline-block;
        position: relative;
        text-align: left;

        /* NOTE(cdata): Both values are needed, since some phones require the
         * value to be \`transparent\`.
         */
        -webkit-tap-highlight-color: rgba(0,0,0,0);
        -webkit-tap-highlight-color: transparent;

        --paper-input-container-input: {
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
          max-width: 100%;
          box-sizing: border-box;
          cursor: pointer;
        };

        @apply --paper-dropdown-menu;
      }

      /* paper-dropdown-menu and paper-dropdown-menu-light both delegate focus
       * to other internal elements which manage focus styling. */
      :host(:focus) {
        outline: none;
      }

      :host(:dir(rtl)) {
        text-align: right;

        @apply(--paper-dropdown-menu);
      }

      :host([disabled]) {
        @apply --paper-dropdown-menu-disabled;
      }

      :host([noink]) paper-ripple {
        display: none;
      }

      :host([no-label-float]) paper-ripple {
        top: 8px;
      }

      paper-ripple {
        top: 12px;
        left: 0px;
        bottom: 8px;
        right: 0px;

        @apply --paper-dropdown-menu-ripple;
      }

      paper-menu-button {
        display: block;
        padding: 0;

        @apply --paper-dropdown-menu-button;
      }

      paper-input {
        @apply --paper-dropdown-menu-input;
      }

      iron-icon {
        color: var(--disabled-text-color);

        @apply --paper-dropdown-menu-icon;
      }
    </style>
  </template>
</dom-module>`;
  document.head.appendChild($_documentContainer$2.content);

  /**
  @license
  Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
  This code may only be used under the BSD style license found at
  http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
  http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
  found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
  part of the polymer project is also subject to an additional IP rights grant
  found at http://polymer.github.io/PATENTS.txt
  */
  // with the `Polymer` function, so this is only a cache lookup.
  // https://github.com/Polymer/polymer/blob/640bc80ac7177b761d46b2fa9c455c318f2b85c6/lib/legacy/class.js#L533-L534

  const LegacyPolymerElementBase = LegacyElementMixin(HTMLElement);
  /**
  Material design: [Dropdown
  menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)

  `paper-dropdown-menu` is similar to a native browser select element.
  `paper-dropdown-menu` works with selectable content. The currently selected
  item is displayed in the control. If no item is selected, the `label` is
  displayed instead.

  Example:

      <paper-dropdown-menu label="Your favourite pastry">
        <paper-listbox slot="dropdown-content">
          <paper-item>Croissant</paper-item>
          <paper-item>Donut</paper-item>
          <paper-item>Financier</paper-item>
          <paper-item>Madeleine</paper-item>
        </paper-listbox>
      </paper-dropdown-menu>

  This example renders a dropdown menu with 4 options.

  The child element with the slot `dropdown-content` is used as the dropdown
  menu. This can be a [`paper-listbox`](paper-listbox), or any other or
  element that acts like an [`iron-selector`](iron-selector).

  Specifically, the menu child must fire an
  [`iron-select`](iron-selector#event-iron-select) event when one of its
  children is selected, and an
  [`iron-deselect`](iron-selector#event-iron-deselect) event when a child is
  deselected. The selected or deselected item must be passed as the event's
  `detail.item` property.

  Applications can listen for the `iron-select` and `iron-deselect` events
  to react when options are selected and deselected.

  ### Styling

  The following custom properties and mixins are also available for styling:

  Custom property | Description | Default
  ----------------|-------------|----------
  `--paper-dropdown-menu` | A mixin that is applied to the element host | `{}`
  `--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}`
  `--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}`
  `--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}`
  `--paper-dropdown-menu-input` | A mixin that is applied to the internal paper input | `{}`
  `--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}`

  You can also use any of the `paper-input-container` and `paper-menu-button`
  style mixins and custom properties to style the internal input and menu button
  respectively.

  @element paper-dropdown-menu
  @demo demo/index.html
  */

  Polymer({
    /** @override */
    _template: html$1`
    <style include="paper-dropdown-menu-shared-styles"></style>

    <paper-menu-button id="menuButton" vertical-align="[[verticalAlign]]" horizontal-align="[[horizontalAlign]]" dynamic-align="[[dynamicAlign]]" vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat, verticalOffset)]]" disabled="[[disabled]]" no-animations="[[noAnimations]]" on-iron-select="_onIronSelect" on-iron-deselect="_onIronDeselect" opened="{{opened}}" close-on-activate allow-outside-scroll="[[allowOutsideScroll]]" restore-focus-on-close="[[restoreFocusOnClose]]">
      <!-- support hybrid mode: user might be using paper-menu-button 1.x which distributes via <content> -->
      <div class="dropdown-trigger" slot="dropdown-trigger">
        <paper-ripple></paper-ripple>
        <!-- paper-input has type="text" for a11y, do not remove -->
        <paper-input id="input" type="text" invalid="[[invalid]]" readonly disabled="[[disabled]]" value="[[value]]" placeholder="[[placeholder]]" error-message="[[errorMessage]]" always-float-label="[[alwaysFloatLabel]]" no-label-float="[[noLabelFloat]]" label="[[label]]" input-role="button" input-aria-haspopup="listbox" autocomplete="off">
          <!-- support hybrid mode: user might be using paper-input 1.x which distributes via <content> -->
          <iron-icon icon="paper-dropdown-menu:arrow-drop-down" suffix slot="suffix"></iron-icon>
        </paper-input>
      </div>
      <slot id="content" name="dropdown-content" slot="dropdown-content"></slot>
    </paper-menu-button>
`,
    is: 'paper-dropdown-menu',
    behaviors: [IronButtonState, IronControlState, IronFormElementBehavior, IronValidatableBehavior],
    properties: {
      /**
       * The derived "label" of the currently selected item. This value
       * is the `label` property on the selected item if set, or else the
       * trimmed text content of the selected item.
       */
      selectedItemLabel: {
        type: String,
        notify: true,
        readOnly: true
      },

      /**
       * The last selected item. An item is selected if the dropdown menu has
       * a child with slot `dropdown-content`, and that child triggers an
       * `iron-select` event with the selected `item` in the `detail`.
       *
       * @type {?Object}
       */
      selectedItem: {
        type: Object,
        notify: true,
        readOnly: true
      },

      /**
       * The value for this element that will be used when submitting in
       * a form. It reflects the value of `selectedItemLabel`. If set directly,
       * it will not update the `selectedItemLabel` value.
       */
      value: {
        type: String,
        notify: true
      },

      /**
       * The label for the dropdown.
       */
      label: {
        type: String
      },

      /**
       * The placeholder for the dropdown.
       */
      placeholder: {
        type: String
      },

      /**
       * The error message to display when invalid.
       */
      errorMessage: {
        type: String
      },

      /**
       * True if the dropdown is open. Otherwise, false.
       */
      opened: {
        type: Boolean,
        notify: true,
        value: false,
        observer: '_openedChanged'
      },

      /**
       * By default, the dropdown will constrain scrolling on the page
       * to itself when opened.
       * Set to true in order to prevent scroll from being constrained
       * to the dropdown when it opens.
       */
      allowOutsideScroll: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable the floating label. Bind this to the
       * `<paper-input-container>`'s `noLabelFloat` property.
       */
      noLabelFloat: {
        type: Boolean,
        value: false,
        reflectToAttribute: true
      },

      /**
       * Set to true to always float the label. Bind this to the
       * `<paper-input-container>`'s `alwaysFloatLabel` property.
       */
      alwaysFloatLabel: {
        type: Boolean,
        value: false
      },

      /**
       * Set to true to disable animations when opening and closing the
       * dropdown.
       */
      noAnimations: {
        type: Boolean,
        value: false
      },

      /**
       * The orientation against which to align the menu dropdown
       * horizontally relative to the dropdown trigger.
       */
      horizontalAlign: {
        type: String,
        value: 'right'
      },

      /**
       * The orientation against which to align the menu dropdown
       * vertically relative to the dropdown trigger.
       */
      verticalAlign: {
        type: String,
        value: 'top'
      },

      /**
       * Overrides the vertical offset computed in
       * _computeMenuVerticalOffset.
       */
      verticalOffset: Number,

      /**
       * If true, the `horizontalAlign` and `verticalAlign` properties will
       * be considered preferences instead of strict requirements when
       * positioning the dropdown and may be changed if doing so reduces
       * the area of the dropdown falling outside of `fitInto`.
       */
      dynamicAlign: {
        type: Boolean
      },

      /**
       * Whether focus should be restored to the dropdown when the menu closes.
       */
      restoreFocusOnClose: {
        type: Boolean,
        value: true
      }
    },
    listeners: {
      'tap': '_onTap'
    },

    /**
     * @type {!Object}
     */
    keyBindings: {
      'up down': 'open',
      'esc': 'close'
    },
    observers: ['_selectedItemChanged(selectedItem)'],

    /**
     * Override `_attachDom` so that we can pass `delegatesFocus`. The overridden
     * implementation of `_attachDom` specifically skips the steps performed here
     * if the node already hosts a shadow root:
     * https://github.com/Polymer/polymer/blob/640bc80ac7177b761d46b2fa9c455c318f2b85c6/lib/mixins/element-mixin.js#L691-L694
     * @override
     */
    _attachDom(dom) {
      const wrappedThis = wrap$1(this);
      wrappedThis.attachShadow({
        mode: 'open',
        delegatesFocus: true,
        shadyUpgradeFragment: dom
      });
      wrappedThis.shadowRoot.appendChild(dom);
      return LegacyPolymerElementBase.prototype._attachDom.call(this, dom);
    },

    /** @override */
    focus() {
      // When using Shady DOM and in browsers that don't support
      // `delegatesFocus`, attempting to focus this element with the browser's
      // native `HTMLElement#focus` will cause focus to be lost because this
      // element isn't focusable in those situations. To work around this, the
      // element in the shadow root that this element intends to delegate focus
      // to is manually focused instead.
      this.$.input._focusableElement.focus();
    },

    /** @override */
    attached: function () {
      // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable`
      // child will cause an `iron-select` event to fire while the element is
      // still in a `DocumentFragment`. This has the effect of causing
      // handlers not to fire. So, we double check this value on attached:
      var contentElement = this.contentElement;

      if (contentElement && contentElement.selectedItem) {
        this._setSelectedItem(contentElement.selectedItem);
      }
    },

    /**
     * The content element that is contained by the dropdown menu, if any.
     */
    get contentElement() {
      // Polymer 2.x returns slot.assignedNodes which can contain text nodes.
      var nodes = dom(this.$.content).getDistributedNodes();

      for (var i = 0, l = nodes.length; i < l; i++) {
        if (nodes[i].nodeType === Node.ELEMENT_NODE) {
          return nodes[i];
        }
      }
    },

    /**
     * Show the dropdown content.
     */
    open: function () {
      this.$.menuButton.open();
    },

    /**
     * Hide the dropdown content.
     */
    close: function () {
      this.$.menuButton.close();
    },

    /**
     * A handler that is called when `iron-select` is fired.
     *
     * @param {CustomEvent} event An `iron-select` event.
     */
    _onIronSelect: function (event) {
      this._setSelectedItem(event.detail.item);
    },

    /**
     * A handler that is called when `iron-deselect` is fired.
     *
     * @param {CustomEvent} event An `iron-deselect` event.
     */
    _onIronDeselect: function (event) {
      this._setSelectedItem(null);
    },

    /**
     * A handler that is called when the dropdown is tapped.
     *
     * @param {CustomEvent} event A tap event.
     */
    _onTap: function (event) {
      if (findOriginalTarget(event) === this) {
        this.open();
      }
    },

    /**
     * Compute the label for the dropdown given a selected item.
     *
     * @param {Element} selectedItem A selected Element item, with an
     * optional `label` property.
     */
    _selectedItemChanged: function (selectedItem) {
      var value = '';

      if (!selectedItem) {
        value = '';
      } else {
        value = selectedItem.label || selectedItem.getAttribute('label') || selectedItem.textContent.trim();
      }

      this.value = value;

      this._setSelectedItemLabel(value);
    },

    /**
     * Compute the vertical offset of the menu based on the value of
     * `noLabelFloat`.
     *
     * @param {boolean} noLabelFloat True if the label should not float
     * @param {number=} opt_verticalOffset Optional offset from the user
     * above the input, otherwise false.
     */
    _computeMenuVerticalOffset: function (noLabelFloat, opt_verticalOffset) {
      // Override offset if it's passed from the user.
      if (opt_verticalOffset) {
        return opt_verticalOffset;
      } // NOTE(cdata): These numbers are somewhat magical because they are
      // derived from the metrics of elements internal to `paper-input`'s
      // template. The metrics will change depending on whether or not the
      // input has a floating label.


      return noLabelFloat ? -4 : 8;
    },

    /**
     * Returns false if the element is required and does not have a selection,
     * and true otherwise.
     * @param {*=} _value Ignored.
     * @return {boolean} true if `required` is false, or if `required` is true
     * and the element has a valid selection.
     */
    _getValidity: function (_value) {
      return this.disabled || !this.required || this.required && !!this.value;
    },
    _openedChanged: function () {
      var openState = this.opened ? 'true' : 'false';
      var e = this.contentElement;

      if (e) {
        e.setAttribute('aria-expanded', openState);
      }
    }
  });

  let HacsChip = _decorate([customElement("hacs-chip")], function (_initialize, _LitElement) {
    class HacsChip extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsChip,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "icon",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "value",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "url",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          return html`
      <div
        class="chip ${classMap({
          pointer: this.url !== undefined
        })}"
        @tap=${this._openURL}
      >
        <div class="icon"><ha-svg-icon .path=${this.icon}></ha-svg-icon></div>
        <div class="value">${String(this.value) || ""}</div>
      </div>
    `;
        }
      }, {
        kind: "method",
        key: "_openURL",
        value: function _openURL(ev) {
          ev.stopPropagation();

          if (this.url === undefined) {
            return;
          }

          window.open(this.url, "_blank", "rel=noreferer");
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [css`
        .chip {
          background-color: var(--hcv-color-chip);
          height: 24px;
          color: var(--hcv-text-color-chip);
          max-width: fit-content;
          display: flex;
          border-radius: 50px;
          padding: 0 4px;
          box-shadow: 2px 2px 8px 1px rgba(0, 0, 0, 0.3);
        }
        .icon {
          margin: auto;
          color: var(--hcv-color-chip);
          height: 20px;
          width: 20px;
          line-height: 20px;
          text-align: center;

          margin-left: -2px;
          background-color: var(--hcv-text-color-chip);
          border-radius: 50px;
        }
        .value {
          width: max-content;
          margin: 2px 5px auto;
        }
        .pointer {
          cursor: pointer;
        }
        ha-svg-icon {
          --mdc-icon-size: 16px;
        }
      `];
        }
      }]
    };
  }, LitElement);

  let HacsAddRepositoryDialog = _decorate([customElement("hacs-add-repository-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsAddRepositoryDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsAddRepositoryDialog,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "filters",

        value() {
          return [];
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Number
        })],
        key: "_load",

        value() {
          return 30;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Number
        })],
        key: "_top",

        value() {
          return 0;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_searchInput",

        value() {
          return "";
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_sortBy",

        value() {
          return "stars";
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "section",
        value: void 0
      }, {
        kind: "method",
        key: "shouldUpdate",
        value: function shouldUpdate(changedProperties) {
          changedProperties.forEach((_oldValue, propName) => {
            if (propName === "hass") {
              this.sidebarDocked = window.localStorage.getItem("dockedSidebar") === '"docked"';
            }
          });
          return changedProperties.has("narrow") || changedProperties.has("filters") || changedProperties.has("active") || changedProperties.has("_searchInput") || changedProperties.has("_load") || changedProperties.has("_sortBy");
        }
      }, {
        kind: "field",
        key: "_repositoriesInActiveCategory",

        value() {
          return (repositories, categories) => repositories === null || repositories === void 0 ? void 0 : repositories.filter(repo => {
            var _this$hacs$sections, _this$hacs$sections$f;

            return !repo.installed && ((_this$hacs$sections = this.hacs.sections) === null || _this$hacs$sections === void 0 ? void 0 : (_this$hacs$sections$f = _this$hacs$sections.find(section => section.id === this.section).categories) === null || _this$hacs$sections$f === void 0 ? void 0 : _this$hacs$sections$f.includes(repo.category)) && !repo.installed && (categories === null || categories === void 0 ? void 0 : categories.includes(repo.category));
          });
        }

      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          var _this$filters;

          this.addEventListener("filter-change", e => this._updateFilters(e));

          if (((_this$filters = this.filters) === null || _this$filters === void 0 ? void 0 : _this$filters.length) === 0) {
            var _activePanel;

            const categories = (_activePanel = activePanel(this.route)) === null || _activePanel === void 0 ? void 0 : _activePanel.categories;
            categories === null || categories === void 0 ? void 0 : categories.filter(c => {
              var _this$hacs$configurat;

              return (_this$hacs$configurat = this.hacs.configuration) === null || _this$hacs$configurat === void 0 ? void 0 : _this$hacs$configurat.categories.includes(c);
            }).forEach(category => {
              this.filters.push({
                id: category,
                value: category,
                checked: true
              });
            });
            this.requestUpdate("filters");
          }
        }
      }, {
        kind: "method",
        key: "_updateFilters",
        value: function _updateFilters(e) {
          const current = this.filters.find(filter => filter.id === e.detail.id);
          this.filters.find(filter => filter.id === current.id).checked = !current.checked;
          this.requestUpdate("filters");
        }
      }, {
        kind: "field",
        key: "_filterRepositories",

        value() {
          return memoizeOne(filterRepositoriesByInput);
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$hacs$configurat2;

          if (!this.active) return html``;
          this._searchInput = window.localStorage.getItem("hacs-search") || "";

          let repositories = this._filterRepositories(this._repositoriesInActiveCategory(this.repositories, (_this$hacs$configurat2 = this.hacs.configuration) === null || _this$hacs$configurat2 === void 0 ? void 0 : _this$hacs$configurat2.categories), this._searchInput);

          if (this.filters.length !== 0) {
            repositories = repositories.filter(repository => {
              var _this$filters$find;

              return (_this$filters$find = this.filters.find(filter => filter.id === repository.category)) === null || _this$filters$find === void 0 ? void 0 : _this$filters$find.checked;
            });
          }

          return html`
      <hacs-dialog
        .active=${this.active}
        .hass=${this.hass}
        .title=${localize("dialog_add_repo.title")}
        hideActions
      >
        <div class="searchandfilter">
          <search-input
            no-label-float
            .label=${localize("search.placeholder")}
            .filter=${this._searchInput || ""}
            @value-changed=${this._inputValueChanged}
            ?narrow=${this.narrow}
          ></search-input>
          <div class="filter" ?narrow=${this.narrow}>
            <paper-dropdown-menu
              label="${localize("dialog_add_repo.sort_by")}"
              ?narrow=${this.narrow}
            >
              <paper-listbox slot="dropdown-content" selected="0">
                <paper-item @tap=${() => this._sortBy = "stars"}
                  >${localize("store.stars")}</paper-item
                >
                <paper-item @tap=${() => this._sortBy = "name"}
                  >${localize("store.name")}</paper-item
                >
                <paper-item @tap=${() => this._sortBy = "last_updated"}
                  >${localize("store.last_updated")}</paper-item
                >
              </paper-listbox>
            </paper-dropdown-menu>
          </div>
        </div>
        ${this.filters.length > 1 ? html`<div class="filters">
              <hacs-filter .filters="${this.filters}"></hacs-filter>
            </div>` : ""}
        <div class=${classMap({
          content: true,
          narrow: this.narrow
        })} @scroll=${this._loadMore}>
          <div class=${classMap({
          list: true,
          narrow: this.narrow
        })}>
            ${repositories.sort((a, b) => {
          if (this._sortBy === "name") {
            return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
          }

          return a[this._sortBy] > b[this._sortBy] ? -1 : 1;
        }).slice(0, this._load).map(repo => html`<paper-icon-item
                  class=${classMap({
          narrow: this.narrow
        })}
                  @click=${() => this._openInformation(repo)}
                >
                  ${repo.category === "integration" ? html`
                        <img
                          src="https://brands.home-assistant.io/_/${repo.domain}/icon.png"
                          referrerpolicy="no-referrer"
                          @error=${this._onImageError}
                          @load=${this._onImageLoad}
                        />
                      ` : html`<ha-svg-icon .path=${mdiGithub} slot="item-icon"></ha-svg-icon>`}
                  <paper-item-body two-line
                    >${repo.name}
                    <div class="category-chip">
                      <hacs-chip
                        .icon=${hacsIcon}
                        .value=${localize(`common.${repo.category}`)}
                      ></hacs-chip>
                    </div>
                    <div secondary>${repo.description}</div>
                  </paper-item-body>
                </paper-icon-item>`)}
            ${repositories.length === 0 ? html`<p>${localize("dialog_add_repo.no_match")}</p>` : ""}
          </div>
        </div>
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "_loadMore",
        value: function _loadMore(ev) {
          const top = ev.target.scrollTop;

          if (top >= this._top) {
            this._load += 1;
          } else {
            this._load -= 1;
          }

          this._top = top;
        }
      }, {
        kind: "method",
        key: "_inputValueChanged",
        value: function _inputValueChanged(ev) {
          this._searchInput = ev.detail.value;
          window.localStorage.setItem("hacs-search", this._searchInput);
        }
      }, {
        kind: "method",
        key: "_openInformation",
        value: function _openInformation(repo) {
          this.dispatchEvent(new CustomEvent("hacs-dialog-secondary", {
            detail: {
              type: "repository-info",
              repository: repo.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "method",
        key: "_onImageLoad",
        value: function _onImageLoad(ev) {
          ev.target.style.visibility = "initial";
        }
      }, {
        kind: "method",
        key: "_onImageError",
        value: function _onImageError(ev) {
          if (ev.target) {
            ev.target.outerHTML = `<ha-svg-icon .path=${mdiGithub} slot="item-icon"></ha-svg-icon>`;
          }
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [searchStyles, scrollBarStyle, css`
        .content {
          width: 100%;
          overflow: auto;
          max-height: 870px;
        }

        .filter {
          margin-top: -12px;
          display: flex;
          width: 200px;
          float: right;
        }

        .narrow {
          max-height: 480px;
          min-width: unset !important;
          width: 100% !important;
        }
        .list {
          margin-top: 16px;
          width: 1024px;
          max-width: 100%;
        }
        .category-chip {
          position: absolute;
          top: 8px;
          right: 8px;
        }
        ha-icon {
          --mdc-icon-size: 36px;
        }
        search-input {
          float: left;
          width: 60%;
        }
        search-input[narrow],
        div.filter[narrow],
        paper-dropdown-menu[narrow] {
          width: 100%;
        }
        img {
          align-items: center;
          display: block;
          justify-content: center;
          margin-bottom: 16px;
          max-height: 36px;
          max-width: 36px;
          position: absolute;
        }

        paper-icon-item:focus {
          background-color: var(--divider-color);
        }

        paper-icon-item {
          cursor: pointer;
          padding: 2px 0;
        }

        paper-dropdown-menu {
          margin: 0 12px 4px 0;
        }

        paper-item-body {
          width: 100%;
          min-height: var(--paper-item-body-two-line-min-height, 72px);
          display: var(--layout-vertical_-_display);
          flex-direction: var(--layout-vertical_-_flex-direction);
          justify-content: var(--layout-center-justified_-_justify-content);
        }
        paper-icon-item.narrow {
          border-bottom: 1px solid var(--divider-color);
          padding: 8px 0;
        }
        paper-item-body div {
          font-size: 14px;
          color: var(--secondary-text-color);
        }
        .add {
          border-top: 1px solid var(--divider-color);
          margin-top: 32px;
        }
        .filters {
          width: 100%;
          display: flex;
        }
        .add-actions {
          justify-content: space-between;
        }
        .add,
        .add-actions {
          display: flex;
          align-items: center;
          font-size: 20px;
          height: 65px;
          background-color: var(--sidebar-background-color);
          border-bottom: 1px solid var(--divider-color);
          padding: 0 16px;
          box-sizing: border-box;
        }
        .add-input {
          width: calc(100% - 80px);
          height: 40px;
          border: 0;
          padding: 0 16px;
          font-size: initial;
          color: var(--sidebar-text-color);
          font-family: var(--paper-font-body1_-_font-family);
        }
        input:focus {
          outline-offset: 0;
          outline: 0;
        }
        input {
          background-color: var(--sidebar-background-color);
        }

        hacs-filter {
          width: 100%;
        }
        div[secondary] {
          width: 88%;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  let HacsCustomRepositoriesDialog = _decorate([customElement("hacs-custom-repositories-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsCustomRepositoriesDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsCustomRepositoriesDialog,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "_inputRepository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_error",
        value: void 0
      }, {
        kind: "field",
        decorators: [query("#add-input")],
        key: "_addInput",
        value: void 0
      }, {
        kind: "field",
        decorators: [query("#category")],
        key: "_addCategory",
        value: void 0
      }, {
        kind: "method",
        key: "shouldUpdate",
        value: function shouldUpdate(changedProperties) {
          return changedProperties.has("narrow") || changedProperties.has("active") || changedProperties.has("_error") || changedProperties.has("repositories");
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          var _this$repositories;

          if (!this.active) return html``;
          const repositories = (_this$repositories = this.repositories) === null || _this$repositories === void 0 ? void 0 : _this$repositories.filter(repo => repo.custom);
          return html`
      <hacs-dialog
        .active=${this.active}
        .hass=${this.hass}
        .title=${localize("dialog_custom_repositories.title")}
      >
        <div class="content">
          <div class="list">
            ${this._error ? html`<div class="error">${this._error.message}</div>` : ""}
            ${repositories === null || repositories === void 0 ? void 0 : repositories.map(repo => html`<paper-icon-item>
                ${repo.category === "integration" ? html`
                      <img
                        src="https://brands.home-assistant.io/_/${repo.domain}/icon.png"
                        referrerpolicy="no-referrer"
                        @error=${this._onImageError}
                        @load=${this._onImageLoad}
                      />
                    ` : html`<ha-svg-icon .path=${mdiGithub} slot="item-icon"></ha-svg-icon>`}
                <paper-item-body
                  @click=${() => this._showReopsitoryInfo(String(repo.id))}
                  three-line
                  >${repo.name}
                  <div secondary>${repo.description}</div>
                  <div secondary>
                    Category: ${repo.category}
                  </div></paper-item-body
                ><hacs-icon-button
                  class="delete"
                  .icon=${mdiDelete}
                  @click=${() => this._removeRepository(repo.id)}
                ></hacs-icon-button>
              </paper-icon-item>`)}
          </div>
        </div>
        <input
          id="add-input"
          class="add-input"
          slot="secondaryaction"
          placeholder="${localize("dialog_custom_repositories.url_placeholder")}"
          .value=${this._inputRepository || ""}
          @input=${this._inputValueChanged}
          ?narrow=${this.narrow}
        />

        <div class="add" slot="primaryaction" ?narrow=${this.narrow}>
          <paper-dropdown-menu
            ?narrow=${this.narrow}
            class="category"
            label="${localize("dialog_custom_repositories.category")}"
          >
            <paper-listbox id="category" slot="dropdown-content" selected="-1">
              ${this.hacs.configuration.categories.map(category => html`
                  <paper-item class="categoryitem" .category=${category}>
                    ${localize(`common.${category}`)}
                  </paper-item>
                `)}
            </paper-listbox>
          </paper-dropdown-menu>
          <mwc-button
            ?narrow=${this.narrow}
            slot="primaryaction"
            raised
            @click=${this._addRepository}
            >${localize("common.add")}</mwc-button
          >
        </div>
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: function firstUpdated() {
          this.hass.connection.subscribeEvents(msg => this._error = msg.data, "hacs/error");
        }
      }, {
        kind: "method",
        key: "_inputValueChanged",
        value: function _inputValueChanged() {
          var _this$_addInput;

          this._inputRepository = (_this$_addInput = this._addInput) === null || _this$_addInput === void 0 ? void 0 : _this$_addInput.value;
        }
      }, {
        kind: "method",
        key: "_addRepository",
        value: async function _addRepository() {
          var _this$_addCategory, _this$_addCategory$se;

          this._error = undefined;
          const repository = this._inputRepository;
          const category = (_this$_addCategory = this._addCategory) === null || _this$_addCategory === void 0 ? void 0 : (_this$_addCategory$se = _this$_addCategory.selectedItem) === null || _this$_addCategory$se === void 0 ? void 0 : _this$_addCategory$se.category;

          if (!category) {
            this._error = {
              message: localize("dialog_custom_repositories.no_category")
            };
            return;
          }

          if (!repository) {
            this._error = {
              message: localize("dialog_custom_repositories.no_repository")
            };
            return;
          }

          await repositoryAdd(this.hass, repository, category);
          this.repositories = await getRepositories(this.hass);
        }
      }, {
        kind: "method",
        key: "_removeRepository",
        value: async function _removeRepository(repository) {
          this._error = undefined;
          await repositoryDelete(this.hass, repository);
          this.repositories = await getRepositories(this.hass);
        }
      }, {
        kind: "method",
        key: "_onImageLoad",
        value: function _onImageLoad(ev) {
          ev.target.style.visibility = "initial";
        }
      }, {
        kind: "method",
        key: "_onImageError",
        value: function _onImageError(ev) {
          ev.target.outerHTML = `<ha-icon
      icon="mdi:github-circle"
      slot="item-icon"
    ></ha-icon>`;
        }
      }, {
        kind: "method",
        key: "_showReopsitoryInfo",
        value: async function _showReopsitoryInfo(repository) {
          this.dispatchEvent(new CustomEvent("hacs-dialog-secondary", {
            detail: {
              type: "repository-info",
              repository
            },
            bubbles: true,
            composed: true
          }));
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [scrollBarStyle, css`
        .content {
          width: 1024px;
          display: contents;
        }
        .list {
          position: relative;
          margin-top: 16px;
          max-height: 870px;
          overflow: auto;
        }
        ha-icon-button,
        ha-icon {
          color: var(--secondary-text-color);
        }
        ha-icon {
          --mdc-icon-size: 36px;
        }
        img {
          align-items: center;
          display: block;
          justify-content: center;
          margin-bottom: 16px;
          max-height: 36px;
          max-width: 36px;
          position: absolute;
        }
        .delete,
        paper-item-body {
          cursor: pointer;
        }
        .error {
          line-height: 0px;
          margin: 12px;
          color: var(--hacs-error-color, var(--google-red-500));
        }

        paper-item-body {
          width: 100%;
          min-height: var(--paper-item-body-two-line-min-height, 72px);
          display: var(--layout-vertical_-_display);
          flex-direction: var(--layout-vertical_-_flex-direction);
          justify-content: var(--layout-center-justified_-_justify-content);
        }
        paper-item-body div {
          font-size: 14px;
          color: var(--secondary-text-color);
        }
        .add {
          display: flex;
          width: 100%;
          align-items: center;
        }

        .add-input {
          width: 100%;
          height: 40px;
          border: 0;
          padding: 0;
          text-align: left;
          padding-right: 71px;
          font-size: initial;
          color: var(--sidebar-text-color);
          font-family: var(--paper-font-body1_-_font-family);
        }
        input:focus {
          outline-offset: 0;
          outline: 0;
        }
        input {
          background-color: var(--sidebar-background-color);
        }
        paper-dropdown-menu {
          width: 100%;
          left: -50px;
          top: -8px;
        }
        mwc-button {
          margin-right: 10px;
        }

        input[narrow],
        .add[narrow],
        paper-dropdown-menu[narrow],
        mwc-button[narrow] {
          margin: 0;
          padding: 0;
          left: 0;
          top: 0;
          width: 100%;
          max-width: 100%;
        }
        .add[narrow] {
          display: contents;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  let HacsGenericDialog = _decorate([customElement("hacs-generic-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsGenericDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsGenericDialog,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "markdown",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "header",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "content",
        value: void 0
      }, {
        kind: "field",
        key: "_getRepository",

        value() {
          return memoizeOne((repositories, repository) => repositories === null || repositories === void 0 ? void 0 : repositories.find(repo => repo.id === repository));
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;

          const repository = this._getRepository(this.repositories, this.repository);

          return html`
      <hacs-dialog .active=${this.active} .narrow=${this.narrow} .hass=${this.hass}>
        <div slot="header">${this.header || ""}</div>
        ${this.markdown ? this.repository ? markdown.html(this.content || "", repository) : markdown.html(this.content || "") : this.content || ""}
      </hacs-dialog>
    `;
        }
      }]
    };
  }, HacsDialogBase);

  async function updateLovelaceResources(hass, repository) {
    const logger = new HacsLogger();
    const resources = await fetchResources(hass);
    const namespace = `/hacsfiles/${repository.full_name.split("/")[1]}`;
    const url = `${namespace}/${repository.file_name}`;
    const exsisting = resources.find(resource => resource.url.includes(namespace));
    logger.debug({
      namespace,
      url,
      exsisting
    }, "updateLovelaceResources");

    if (exsisting && exsisting.url !== url) {
      logger.debug(`Updating exsusting resource for ${namespace}`);
      updateResource(hass, {
        url,
        resource_id: exsisting.id,
        res_type: exsisting.type
      });
    } else if (!resources.map(resource => resource.url).includes(url)) {
      logger.debug(`Adding ${url} to Lovelace resources`);
      createResource(hass, {
        url,
        res_type: "module"
      });
    }
  }

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$6(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  /** @soyCompatible */

  class FormElement extends BaseElement {
    createRenderRoot() {
      return this.attachShadow({
        mode: 'open',
        delegatesFocus: true
      });
    }

    click() {
      if (this.formElement) {
        this.formElement.focus();
        this.formElement.click();
      }
    }

    setAriaLabel(label) {
      if (this.formElement) {
        this.formElement.setAttribute('aria-label', label);
      }
    }

    firstUpdated() {
      super.firstUpdated();
      this.mdcRoot.addEventListener('change', e => {
        this.dispatchEvent(new Event('change', e));
      });
    }

  }

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */

  /* global Reflect, Promise */
  var extendStatics$2 = function (d, b) {
    extendStatics$2 = Object.setPrototypeOf || {
      __proto__: []
    } instanceof Array && function (d, b) {
      d.__proto__ = b;
    } || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    };

    return extendStatics$2(d, b);
  };

  function __extends$2(d, b) {
    extendStatics$2(d, b);

    function __() {
      this.constructor = d;
    }

    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  }
  var __assign$2 = function () {
    __assign$2 = Object.assign || function __assign(t) {
      for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];

        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
      }

      return t;
    };

    return __assign$2.apply(this, arguments);
  };

  /**
   * @license
   * Copyright 2018 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  /** CSS classes used by the switch. */
  var cssClasses$2 = {
    /** Class used for a switch that is in the "checked" (on) position. */
    CHECKED: 'mdc-switch--checked',

    /** Class used for a switch that is disabled. */
    DISABLED: 'mdc-switch--disabled'
  };
  /** String constants used by the switch. */

  var strings$2 = {
    /** Aria attribute for checked or unchecked state of switch */
    ARIA_CHECKED_ATTR: 'aria-checked',

    /** A CSS selector used to locate the native HTML control for the switch.  */
    NATIVE_CONTROL_SELECTOR: '.mdc-switch__native-control',

    /** A CSS selector used to locate the ripple surface element for the switch. */
    RIPPLE_SURFACE_SELECTOR: '.mdc-switch__thumb-underlay'
  };

  /**
   * @license
   * Copyright 2018 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  var MDCSwitchFoundation =
  /** @class */
  function (_super) {
    __extends$2(MDCSwitchFoundation, _super);

    function MDCSwitchFoundation(adapter) {
      return _super.call(this, __assign$2(__assign$2({}, MDCSwitchFoundation.defaultAdapter), adapter)) || this;
    }

    Object.defineProperty(MDCSwitchFoundation, "strings", {
      /** The string constants used by the switch. */
      get: function () {
        return strings$2;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCSwitchFoundation, "cssClasses", {
      /** The CSS classes used by the switch. */
      get: function () {
        return cssClasses$2;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCSwitchFoundation, "defaultAdapter", {
      /** The default Adapter for the switch. */
      get: function () {
        return {
          addClass: function () {
            return undefined;
          },
          removeClass: function () {
            return undefined;
          },
          setNativeControlChecked: function () {
            return undefined;
          },
          setNativeControlDisabled: function () {
            return undefined;
          },
          setNativeControlAttr: function () {
            return undefined;
          }
        };
      },
      enumerable: true,
      configurable: true
    });
    /** Sets the checked state of the switch. */

    MDCSwitchFoundation.prototype.setChecked = function (checked) {
      this.adapter.setNativeControlChecked(checked);
      this.updateAriaChecked_(checked);
      this.updateCheckedStyling_(checked);
    };
    /** Sets the disabled state of the switch. */


    MDCSwitchFoundation.prototype.setDisabled = function (disabled) {
      this.adapter.setNativeControlDisabled(disabled);

      if (disabled) {
        this.adapter.addClass(cssClasses$2.DISABLED);
      } else {
        this.adapter.removeClass(cssClasses$2.DISABLED);
      }
    };
    /** Handles the change event for the switch native control. */


    MDCSwitchFoundation.prototype.handleChange = function (evt) {
      var nativeControl = evt.target;
      this.updateAriaChecked_(nativeControl.checked);
      this.updateCheckedStyling_(nativeControl.checked);
    };
    /** Updates the styling of the switch based on its checked state. */


    MDCSwitchFoundation.prototype.updateCheckedStyling_ = function (checked) {
      if (checked) {
        this.adapter.addClass(cssClasses$2.CHECKED);
      } else {
        this.adapter.removeClass(cssClasses$2.CHECKED);
      }
    };

    MDCSwitchFoundation.prototype.updateAriaChecked_ = function (checked) {
      this.adapter.setNativeControlAttr(strings$2.ARIA_CHECKED_ATTR, "" + !!checked);
    };

    return MDCSwitchFoundation;
  }(MDCFoundation);

  class SwitchBase extends FormElement {
    constructor() {
      super(...arguments);
      this.checked = false;
      this.disabled = false;
      this.shouldRenderRipple = false;
      this.mdcFoundationClass = MDCSwitchFoundation;
      this.rippleHandlers = new RippleHandlers(() => {
        this.shouldRenderRipple = true;
        return this.ripple;
      });
    }

    changeHandler(e) {
      this.mdcFoundation.handleChange(e); // catch "click" event and sync properties

      this.checked = this.formElement.checked;
    }

    createAdapter() {
      return Object.assign(Object.assign({}, addHasRemoveClass(this.mdcRoot)), {
        setNativeControlChecked: checked => {
          this.formElement.checked = checked;
        },
        setNativeControlDisabled: disabled => {
          this.formElement.disabled = disabled;
        },
        setNativeControlAttr: (attr, value) => {
          this.formElement.setAttribute(attr, value);
        }
      });
    }

    renderRipple() {
      return html`${this.shouldRenderRipple ? html`
        <mwc-ripple 
          .accent="${this.checked}" 
          .disabled="${this.disabled}" 
          unbounded>
        </mwc-ripple>` : html``}`;
    }

    focus() {
      const formElement = this.formElement;

      if (formElement) {
        this.rippleHandlers.startFocus();
        formElement.focus();
      }
    }

    blur() {
      const formElement = this.formElement;

      if (formElement) {
        this.rippleHandlers.endFocus();
        formElement.blur();
      }
    }

    render() {
      return html`
      <div class="mdc-switch">
        <div class="mdc-switch__track"></div>
        <div class="mdc-switch__thumb-underlay">
          ${this.renderRipple()}
          <div class="mdc-switch__thumb">
            <input
              type="checkbox"
              id="basic-switch"
              class="mdc-switch__native-control"
              role="switch"
              @change="${this.changeHandler}"
              @focus="${this.handleRippleFocus}"
              @blur="${this.handleRippleBlur}"
              @mousedown="${this.handleRippleMouseDown}"
              @mouseenter="${this.handleRippleMouseEnter}"
              @mouseleave="${this.handleRippleMouseLeave}"
              @touchstart="${this.handleRippleTouchStart}"
              @touchend="${this.handleRippleDeactivate}"
              @touchcancel="${this.handleRippleDeactivate}">
          </div>
        </div>
      </div>`;
    }

    handleRippleMouseDown(event) {
      const onUp = () => {
        window.removeEventListener('mouseup', onUp);
        this.handleRippleDeactivate();
      };

      window.addEventListener('mouseup', onUp);
      this.rippleHandlers.startPress(event);
    }

    handleRippleTouchStart(event) {
      this.rippleHandlers.startPress(event);
    }

    handleRippleDeactivate() {
      this.rippleHandlers.endPress();
    }

    handleRippleMouseEnter() {
      this.rippleHandlers.startHover();
    }

    handleRippleMouseLeave() {
      this.rippleHandlers.endHover();
    }

    handleRippleFocus() {
      this.rippleHandlers.startFocus();
    }

    handleRippleBlur() {
      this.rippleHandlers.endFocus();
    }

  }

  __decorate$6([property({
    type: Boolean
  }), observer$1(function (value) {
    this.mdcFoundation.setChecked(value);
  })], SwitchBase.prototype, "checked", void 0);

  __decorate$6([property({
    type: Boolean
  }), observer$1(function (value) {
    this.mdcFoundation.setDisabled(value);
  })], SwitchBase.prototype, "disabled", void 0);

  __decorate$6([query('.mdc-switch')], SwitchBase.prototype, "mdcRoot", void 0);

  __decorate$6([query('input')], SwitchBase.prototype, "formElement", void 0);

  __decorate$6([queryAsync('mwc-ripple')], SwitchBase.prototype, "ripple", void 0);

  __decorate$6([internalProperty()], SwitchBase.prototype, "shouldRenderRipple", void 0);

  __decorate$6([eventOptions({
    passive: true
  })], SwitchBase.prototype, "handleRippleMouseDown", null);

  __decorate$6([eventOptions({
    passive: true
  })], SwitchBase.prototype, "handleRippleTouchStart", null);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$7 = css`.mdc-switch__thumb-underlay{left:-18px;right:initial;top:-17px;width:48px;height:48px}[dir=rtl] .mdc-switch__thumb-underlay,.mdc-switch__thumb-underlay[dir=rtl]{left:initial;right:-18px}.mdc-switch__native-control{width:68px;height:48px}.mdc-switch{display:inline-block;position:relative;outline:none;user-select:none}.mdc-switch.mdc-switch--checked .mdc-switch__track{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786)}.mdc-switch.mdc-switch--checked .mdc-switch__thumb{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786);border-color:#018786;border-color:var(--mdc-theme-secondary, #018786)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__track{background-color:#000;background-color:var(--mdc-theme-on-surface, #000)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__thumb{background-color:#fff;background-color:var(--mdc-theme-surface, #fff);border-color:#fff;border-color:var(--mdc-theme-surface, #fff)}.mdc-switch__native-control{left:0;right:initial;position:absolute;top:0;margin:0;opacity:0;cursor:pointer;pointer-events:auto;transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-switch__native-control,.mdc-switch__native-control[dir=rtl]{left:initial;right:0}.mdc-switch__track{box-sizing:border-box;width:32px;height:14px;border:1px solid transparent;border-radius:7px;opacity:.38;transition:opacity 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb-underlay{display:flex;position:absolute;align-items:center;justify-content:center;transform:translateX(0);transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb{box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);box-sizing:border-box;width:20px;height:20px;border:10px solid;border-radius:50%;pointer-events:none;z-index:1}.mdc-switch--checked .mdc-switch__track{opacity:.54}.mdc-switch--checked .mdc-switch__thumb-underlay{transform:translateX(20px)}[dir=rtl] .mdc-switch--checked .mdc-switch__thumb-underlay,.mdc-switch--checked .mdc-switch__thumb-underlay[dir=rtl]{transform:translateX(-20px)}.mdc-switch--checked .mdc-switch__native-control{transform:translateX(-20px)}[dir=rtl] .mdc-switch--checked .mdc-switch__native-control,.mdc-switch--checked .mdc-switch__native-control[dir=rtl]{transform:translateX(20px)}.mdc-switch--disabled{opacity:.38;pointer-events:none}.mdc-switch--disabled .mdc-switch__thumb{border-width:1px}.mdc-switch--disabled .mdc-switch__native-control{cursor:default;pointer-events:none}:host{display:inline-flex;outline:none}`;

  let Switch = class Switch extends SwitchBase {};
  Switch.styles = style$7;
  Switch = __decorate$6([customElement('mwc-switch')], Switch);

  /**
   * Broadcast haptic feedback requests
   */
  // https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/feedback/#haptics
  // Implementors on platforms other than iOS should attempt to match the patterns (shown in HIG) as closely as possible.

  const forwardHaptic = hapticType => {
    fireEvent(window, "haptic", hapticType);
  };

  const MwcSwitch = customElements.get("mwc-switch");
  let HaSwitch = _decorate([customElement("ha-switch")], function (_initialize, _MwcSwitch) {
    class HaSwitch extends _MwcSwitch {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaSwitch,
      d: [{
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "haptic",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "firstUpdated",
        value: // Generate a haptic vibration.
        // Only set to true if the new value of the switch is applied right away when toggling.
        // Do not add haptic when a user is required to press save.
        function firstUpdated() {
          _get(_getPrototypeOf(HaSwitch.prototype), "firstUpdated", this).call(this);

          this.style.setProperty("--mdc-theme-secondary", "var(--switch-checked-color)");
          this.addEventListener("change", () => {
            if (this.haptic) {
              forwardHaptic("light");
            }
          });
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [style$7, css`
        .mdc-switch.mdc-switch--checked .mdc-switch__thumb {
          background-color: var(--switch-checked-button-color);
          border-color: var(--switch-checked-button-color);
        }
        .mdc-switch.mdc-switch--checked .mdc-switch__track {
          background-color: var(--switch-checked-track-color);
          border-color: var(--switch-checked-track-color);
        }
        .mdc-switch:not(.mdc-switch--checked) .mdc-switch__thumb {
          background-color: var(--switch-unchecked-button-color);
          border-color: var(--switch-unchecked-button-color);
        }
        .mdc-switch:not(.mdc-switch--checked) .mdc-switch__track {
          background-color: var(--switch-unchecked-track-color);
          border-color: var(--switch-unchecked-track-color);
        }
      `];
        }
      }]
    };
  }, MwcSwitch);

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */
  function __decorate$7(decorators, target, key, desc) {
    var c = arguments.length,
        r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
        d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
  }

  /*! *****************************************************************************
  Copyright (c) Microsoft Corporation.

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
  ***************************************************************************** */

  /* global Reflect, Promise */
  var extendStatics$3 = function (d, b) {
    extendStatics$3 = Object.setPrototypeOf || {
      __proto__: []
    } instanceof Array && function (d, b) {
      d.__proto__ = b;
    } || function (d, b) {
      for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    };

    return extendStatics$3(d, b);
  };

  function __extends$3(d, b) {
    extendStatics$3(d, b);

    function __() {
      this.constructor = d;
    }

    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  }
  var __assign$3 = function () {
    __assign$3 = Object.assign || function __assign(t) {
      for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];

        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
      }

      return t;
    };

    return __assign$3.apply(this, arguments);
  };

  /**
   * @license
   * Copyright 2017 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  var cssClasses$3 = {
    ROOT: 'mdc-form-field'
  };
  var strings$3 = {
    LABEL_SELECTOR: '.mdc-form-field > label'
  };

  /**
   * @license
   * Copyright 2017 Google Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */

  var MDCFormFieldFoundation =
  /** @class */
  function (_super) {
    __extends$3(MDCFormFieldFoundation, _super);

    function MDCFormFieldFoundation(adapter) {
      var _this = _super.call(this, __assign$3(__assign$3({}, MDCFormFieldFoundation.defaultAdapter), adapter)) || this;

      _this.click = function () {
        _this.handleClick();
      };

      return _this;
    }

    Object.defineProperty(MDCFormFieldFoundation, "cssClasses", {
      get: function () {
        return cssClasses$3;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCFormFieldFoundation, "strings", {
      get: function () {
        return strings$3;
      },
      enumerable: true,
      configurable: true
    });
    Object.defineProperty(MDCFormFieldFoundation, "defaultAdapter", {
      get: function () {
        return {
          activateInputRipple: function () {
            return undefined;
          },
          deactivateInputRipple: function () {
            return undefined;
          },
          deregisterInteractionHandler: function () {
            return undefined;
          },
          registerInteractionHandler: function () {
            return undefined;
          }
        };
      },
      enumerable: true,
      configurable: true
    });

    MDCFormFieldFoundation.prototype.init = function () {
      this.adapter.registerInteractionHandler('click', this.click);
    };

    MDCFormFieldFoundation.prototype.destroy = function () {
      this.adapter.deregisterInteractionHandler('click', this.click);
    };

    MDCFormFieldFoundation.prototype.handleClick = function () {
      var _this = this;

      this.adapter.activateInputRipple();
      requestAnimationFrame(function () {
        _this.adapter.deactivateInputRipple();
      });
    };

    return MDCFormFieldFoundation;
  }(MDCFoundation);

  /**
   * @license
   * Copyright 2018 Google Inc. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  class FormfieldBase extends BaseElement {
    constructor() {
      super(...arguments);
      this.alignEnd = false;
      this.spaceBetween = false;
      this.nowrap = false;
      this.label = '';
      this.mdcFoundationClass = MDCFormFieldFoundation;
    }

    createAdapter() {
      return {
        registerInteractionHandler: (type, handler) => {
          this.labelEl.addEventListener(type, handler);
        },
        deregisterInteractionHandler: (type, handler) => {
          this.labelEl.removeEventListener(type, handler);
        },
        activateInputRipple: async () => {
          const input = this.input;

          if (input instanceof FormElement) {
            const ripple = await input.ripple;

            if (ripple) {
              ripple.startPress();
            }
          }
        },
        deactivateInputRipple: async () => {
          const input = this.input;

          if (input instanceof FormElement) {
            const ripple = await input.ripple;

            if (ripple) {
              ripple.endPress();
            }
          }
        }
      };
    }

    get input() {
      return findAssignedElement(this.slotEl, '*');
    }

    render() {
      const classes = {
        'mdc-form-field--align-end': this.alignEnd,
        'mdc-form-field--space-between': this.spaceBetween,
        'mdc-form-field--nowrap': this.nowrap
      };
      return html`
      <div class="mdc-form-field ${classMap(classes)}">
        <slot></slot>
        <label class="mdc-label"
               @click="${this._labelClick}">${this.label}</label>
      </div>`;
    }

    _labelClick() {
      const input = this.input;

      if (input) {
        input.focus();
        input.click();
      }
    }

  }

  __decorate$7([property({
    type: Boolean
  })], FormfieldBase.prototype, "alignEnd", void 0);

  __decorate$7([property({
    type: Boolean
  })], FormfieldBase.prototype, "spaceBetween", void 0);

  __decorate$7([property({
    type: Boolean
  })], FormfieldBase.prototype, "nowrap", void 0);

  __decorate$7([property({
    type: String
  }), observer$1(async function (label) {
    const input = this.input;

    if (input) {
      if (input.localName === 'input') {
        input.setAttribute('aria-label', label);
      } else if (input instanceof FormElement) {
        await input.updateComplete;
        input.setAriaLabel(label);
      }
    }
  })], FormfieldBase.prototype, "label", void 0);

  __decorate$7([query('.mdc-form-field')], FormfieldBase.prototype, "mdcRoot", void 0);

  __decorate$7([query('slot')], FormfieldBase.prototype, "slotEl", void 0);

  __decorate$7([query('label')], FormfieldBase.prototype, "labelEl", void 0);

  /**
  @license
  Copyright 2018 Google Inc. All Rights Reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  */
  const style$8 = css`.mdc-form-field{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body2-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-body2-font-size, 0.875rem);line-height:1.25rem;line-height:var(--mdc-typography-body2-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-body2-font-weight, 400);letter-spacing:0.0178571429em;letter-spacing:var(--mdc-typography-body2-letter-spacing, 0.0178571429em);text-decoration:inherit;text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body2-text-transform, inherit);color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));display:inline-flex;align-items:center;vertical-align:middle}.mdc-form-field>label{margin-left:0;margin-right:auto;padding-left:4px;padding-right:0;order:0}[dir=rtl] .mdc-form-field>label,.mdc-form-field>label[dir=rtl]{margin-left:auto;margin-right:0}[dir=rtl] .mdc-form-field>label,.mdc-form-field>label[dir=rtl]{padding-left:0;padding-right:4px}.mdc-form-field--nowrap>label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.mdc-form-field--align-end>label{margin-left:auto;margin-right:0;padding-left:0;padding-right:4px;order:-1}[dir=rtl] .mdc-form-field--align-end>label,.mdc-form-field--align-end>label[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-form-field--align-end>label,.mdc-form-field--align-end>label[dir=rtl]{padding-left:4px;padding-right:0}.mdc-form-field--space-between{justify-content:space-between}.mdc-form-field--space-between>label{margin:0}[dir=rtl] .mdc-form-field--space-between>label,.mdc-form-field--space-between>label[dir=rtl]{margin:0}:host{display:inline-flex}.mdc-form-field{width:100%}::slotted(*){-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body2-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-body2-font-size, 0.875rem);line-height:1.25rem;line-height:var(--mdc-typography-body2-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-body2-font-weight, 400);letter-spacing:0.0178571429em;letter-spacing:var(--mdc-typography-body2-letter-spacing, 0.0178571429em);text-decoration:inherit;text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body2-text-transform, inherit);color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}::slotted(mwc-switch){margin-right:10px}[dir=rtl] ::slotted(mwc-switch),::slotted(mwc-switch)[dir=rtl]{margin-left:10px}`;

  let Formfield = class Formfield extends FormfieldBase {};
  Formfield.styles = style$8;
  Formfield = __decorate$7([customElement('mwc-formfield')], Formfield);

  const MwcFormfield = customElements.get("mwc-formfield");
  let HaFormfield = _decorate([customElement("ha-formfield")], function (_initialize, _MwcFormfield) {
    class HaFormfield extends _MwcFormfield {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaFormfield,
      d: [{
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [style$8, css`
        :host(:not([alignEnd])) ::slotted(ha-switch) {
          margin-right: 10px;
        }
        :host([dir="rtl"]:not([alignEnd])) ::slotted(ha-switch) {
          margin-left: 10px;
          margin-right: auto;
        }
      `];
        }
      }]
    };
  }, MwcFormfield);

  let HacsInstallDialog = _decorate([customElement("hacs-install-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsInstallDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsInstallDialog,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_toggle",

        value() {
          return true;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_installing",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_error",
        value: void 0
      }, {
        kind: "field",
        decorators: [query("#version")],
        key: "_version",
        value: void 0
      }, {
        kind: "method",
        key: "shouldUpdate",
        value: function shouldUpdate(changedProperties) {
          changedProperties.forEach((_oldValue, propName) => {
            if (propName === "hass") {
              this.sidebarDocked = window.localStorage.getItem("dockedSidebar") === '"docked"';
            }

            if (propName === "repositories") {
              this._repository = this._getRepository(this.repositories, this.repository);
            }
          });
          return changedProperties.has("sidebarDocked") || changedProperties.has("narrow") || changedProperties.has("active") || changedProperties.has("_toggle") || changedProperties.has("_error") || changedProperties.has("_repository") || changedProperties.has("_installing");
        }
      }, {
        kind: "field",
        key: "_getRepository",

        value() {
          return memoizeOne((repositories, repository) => repositories === null || repositories === void 0 ? void 0 : repositories.find(repo => repo.id === repository));
        }

      }, {
        kind: "field",
        key: "_getInstallPath",

        value() {
          return memoizeOne(repository => {
            let path = repository.local_path;

            if (repository.category === "theme") {
              path = `${path}/${repository.file_name}`;
            }

            return path;
          });
        }

      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          this._repository = this._getRepository(this.repositories, this.repository);

          if (!this._repository.updated_info) {
            await repositoryUpdate(this.hass, this._repository.id);
            this.repositories = await getRepositories(this.hass);
          }

          this._toggle = false;
          this.hass.connection.subscribeEvents(msg => this._error = msg.data, "hacs/error");
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;

          const installPath = this._getInstallPath(this._repository);

          return html`
      <hacs-dialog
        .active=${this.active}
        .narrow=${this.narrow}
        .hass=${this.hass}
        .secondary=${this.secondary}
        .title=${this._repository.name}
        dynamicHeight
      >
        <div class="content">
          ${this._repository.version_or_commit === "version" ? html`<div class="beta-container">
                  <ha-formfield .label=${localize("dialog_install.show_beta")}>
                    <ha-switch
                      ?disabled=${this._toggle}
                      .checked=${this._repository.beta}
                      @change=${this._toggleBeta}
                    ></ha-switch>
                  </ha-formfield>
                </div>
                <div class="version-select-container">
                  <paper-dropdown-menu
                    ?disabled=${this._toggle}
                    class="version-select-dropdown"
                    label="${localize("dialog_install.select_version")}"
                  >
                    <paper-listbox
                      id="version"
                      class="version-select-list"
                      slot="dropdown-content"
                      selected="0"
                    >
                      ${this._repository.releases.map(release => {
          return html`<paper-item .version=${release} class="version-select-item"
                          >${release}</paper-item
                        >`;
        })}
                      ${this._repository.full_name === "hacs/integration" || this._repository.hide_default_branch ? "" : html`
                            <paper-item
                              .version=${this._repository.default_branch}
                              class="version-select-item"
                              >${this._repository.default_branch}</paper-item
                            >
                          `}
                    </paper-listbox>
                  </paper-dropdown-menu>
                </div>` : ""}
          ${!this._repository.can_install ? html`<p class="error">
                ${localize("confirm.home_assistant_version_not_correct").replace("{haversion}", this.hass.config.version).replace("{minversion}", this._repository.homeassistant)}
              </p>` : ""}
          <div class="note">
            ${localize(`repository.note_installed`)}
            <code>'${installPath}'</code>
            ${this._repository.category === "plugin" && this.hacs.status.lovelace_mode !== "storage" ? html`
                  <p>${localize(`repository.lovelace_instruction`)}</p>
                  <pre>
                url: ${this._lovelaceUrl()}
                type: module
                </pre
                  >
                ` : ""}
            ${this._repository.category === "integration" ? html`<p>${localize("dialog_install.restart")}</p>` : ""}
          </div>
          ${this._error ? html`<div class="error">${this._error.message}</div>` : ""}
        </div>
        <mwc-button
          slot="primaryaction"
          ?disabled=${!this._repository.can_install || this._toggle}
          @click=${this._installRepository}
          >${this._installing ? html`<ha-circular-progress active></ha-circular-progress>` : localize("common.install")}</mwc-button
        >
        <hacs-link slot="secondaryaction" .url="https://github.com/${this._repository.full_name}"
          ><mwc-button>${localize("common.repository")}</mwc-button></hacs-link
        >
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "_lovelaceUrl",
        value: function _lovelaceUrl() {
          var _this$_repository, _this$_repository2;

          return `/hacsfiles/${(_this$_repository = this._repository) === null || _this$_repository === void 0 ? void 0 : _this$_repository.full_name.split("/")[1]}/${(_this$_repository2 = this._repository) === null || _this$_repository2 === void 0 ? void 0 : _this$_repository2.file_name}`;
        }
      }, {
        kind: "method",
        key: "_toggleBeta",
        value: async function _toggleBeta() {
          this._toggle = true;
          await repositoryToggleBeta(this.hass, this.repository);
          this.repositories = await getRepositories(this.hass);
          this._toggle = false;
        }
      }, {
        kind: "method",
        key: "_installRepository",
        value: async function _installRepository() {
          this._installing = true;

          if (this._repository.version_or_commit !== "commit") {
            var _this$_version, _this$_version$select;

            const selectedVersion = ((_this$_version = this._version) === null || _this$_version === void 0 ? void 0 : (_this$_version$select = _this$_version.selectedItem) === null || _this$_version$select === void 0 ? void 0 : _this$_version$select.version) || this._repository.available_version || this._repository.default_branch;
            await repositoryInstallVersion(this.hass, this._repository.id, selectedVersion);
          } else {
            await repositoryInstall(this.hass, this._repository.id);
          }

          this.hacs.log.debug(this._repository.category, "_installRepository");
          this.hacs.log.debug(this.hacs.status.lovelace_mode, "_installRepository");

          if (this._repository.category === "plugin" && this.hacs.status.lovelace_mode === "storage") {
            await updateLovelaceResources(this.hass, this._repository);
          }

          this._installing = false;
          this.dispatchEvent(new Event("hacs-secondary-dialog-closed", {
            bubbles: true,
            composed: true
          }));
          this.dispatchEvent(new Event("hacs-dialog-closed", {
            bubbles: true,
            composed: true
          }));

          if (this._repository.category === "plugin" && this.hacs.status.lovelace_mode === "storage") {
            this.dispatchEvent(new CustomEvent("hacs-dialog", {
              detail: {
                type: "reload"
              },
              bubbles: true,
              composed: true
            }));
          }
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [css`
        .version-select-dropdown {
          width: 100%;
        }
        .content {
          padding: 32px 8px;
        }
        .note {
          margin-bottom: -32px;
          margin-top: 12px;
        }
        .lovelace {
          margin-top: 8px;
        }
        .error {
          color: var(--hacs-error-color, var(--google-red-500));
        }
        paper-menu-button {
          color: var(--secondary-text-color);
          padding: 0;
        }
        paper-item {
          cursor: pointer;
        }
        paper-item-body {
          opacity: var(--dark-primary-opacity);
        }
        pre {
          white-space: pre-line;
          user-select: all;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  const normalize$1 = (value, min, max) => {
    if (isNaN(value) || isNaN(min) || isNaN(max)) {
      // Not a number, return 0
      return 0;
    }

    if (value > max) return max;
    if (value < min) return min;
    return value;
  };
  const getValueInPercentage = (value, min, max) => {
    const newMax = max - min;
    const newVal = value - min;
    return 100 * newVal / newMax;
  };
  const roundWithOneDecimal = value => {
    return Math.round(value * 10) / 10;
  };

  let HaBar = _decorate([customElement("ha-bar")], function (_initialize, _LitElement) {
    class HaBar extends _LitElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HaBar,
      d: [{
        kind: "field",
        decorators: [property({
          type: Number
        })],
        key: "min",

        value() {
          return 0;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Number
        })],
        key: "max",

        value() {
          return 100;
        }

      }, {
        kind: "field",
        decorators: [property({
          type: Number
        })],
        key: "value",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          const valuePrecentage = roundWithOneDecimal(getValueInPercentage(normalize$1(this.value, this.min, this.max), this.min, this.max));
          return svg`
      <svg>
        <g>
          <rect></rect>
          <rect width="${valuePrecentage}%"></rect>
        </g>
      </svg>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      rect:first-child {
        width: 100%;
        fill: var(--ha-bar-background-color, var(--secondary-background-color));
      }
      rect:last-child {
        fill: var(--ha-bar-primary-color, var(--primary-color));
        rx: var(--ha-bar-border-radius, 4px);
      }
      svg {
        border-radius: var(--ha-bar-border-radius, 4px);
        height: 12px;
        width: 100%;
      }
    `;
        }
      }]
    };
  }, LitElement);

  let HacsNavigateDialog = _decorate([customElement("hacs-navigate-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsNavigateDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsNavigateDialog,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "path",
        value: void 0
      }, {
        kind: "field",
        decorators: [internalProperty()],
        key: "_progress",

        value() {
          return 0;
        }

      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          this._updateProgress();
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;
          return html`
      <hacs-dialog
        @closed=${this.closeDialog}
        .active=${this.active}
        .hass=${this.hass}
        title="Navigating away from HACS"
        >
        <div class="content">
          This takes you away from HACS and to another page, what you see on that page is not a part
          of HACS.
          </br></br>
          Redirect will happen automatically in 10 seconds, if you do not want to wait
          click the "GO NOW" button.
        </div>
        <ha-bar .value=${this._progress}></ha-bar>
        <mwc-button slot="primaryaction" @click=${this._navigate}>
          Go now
        </mwc-button>
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "closeDialog",
        value: function closeDialog() {
          this.active = false;
        }
      }, {
        kind: "method",
        key: "_updateProgress",
        value: function _updateProgress() {
          setTimeout(() => {
            if (!this.active) {
              return;
            }

            this._progress += 10;

            if (this._progress === 100) {
              this._navigate();
            } else {
              this._updateProgress();
            }
          }, 1000);
        }
      }, {
        kind: "method",
        key: "_navigate",
        value: function _navigate() {
          if (this.active) {
            navigate(this, this.path);
          }
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [css`
        hacs-dialog {
          --hacs-dialog-max-width: 460px;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  let HacsReloadDialog = _decorate([customElement("hacs-reload-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsReloadDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsReloadDialog,
      d: [{
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;
          return html`
      <hacs-dialog .active=${this.active} .hass=${this.hass} title="Reload">
        <div class="content">
          ${localize("dialog.reload.description")}
          </br>
          ${localize("dialog.reload.confirm")}
        </div>
        <mwc-button slot="secondaryaction" @click=${this._close}>
          ${localize("common.cancel")}
        </mwc-button>
        <mwc-button slot="primaryaction" @click=${this._reload}>
          ${localize("common.reload")}
        </mwc-button>
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "_reload",
        value: function _reload() {
          window.top.location.reload(true);
        }
      }, {
        kind: "method",
        key: "_close",
        value: function _close() {
          this.active = false;
          this.dispatchEvent(new Event("hacs-dialog-closed", {
            bubbles: true,
            composed: true
          }));
        }
      }]
    };
  }, HacsDialogBase);

  let HacsRemovedDialog = _decorate([customElement("hacs-removed-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsRemovedDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsRemovedDialog,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          type: Boolean
        })],
        key: "_updating",

        value() {
          return false;
        }

      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;
          const removedrepo = this.hacs.removed.find(r => r.repository === this.repository.full_name);
          return html`
      <hacs-dialog
        .active=${this.active}
        .hass=${this.hass}
        .title=${this.hacs.localize("entry.messages.removed").replace("'{repository}'", "")}
      >
        <div class="content">
          <div><b>${this.hacs.localize("dialog_removed.name")}:</b> ${this.repository.name}</div>
          ${removedrepo.removal_type ? html` <div>
                <b>${this.hacs.localize("dialog_removed.type")}:</b> ${removedrepo.removal_type}
              </div>` : ""}
          ${removedrepo.reason ? html` <div>
                <b>${this.hacs.localize("dialog_removed.reason")}:</b> ${removedrepo.reason}
              </div>` : ""}
          ${removedrepo.link ? html`          <div>
            </b><hacs-link .url=${removedrepo.link}>${this.hacs.localize("dialog_removed.link")}</hacs-link>
          </div>` : ""}
        </div>
        <mwc-button class="uninstall" slot="primaryaction" @click=${this._uninstallRepository}
          >${this._updating ? html`<ha-circular-progress active></ha-circular-progress>` : this.hacs.localize("common.uninstall")}</mwc-button
        >
        <!--<mwc-button slot="secondaryaction" @click=${this._ignoreMessage}
          >${this.hacs.localize("common.ignore")}</mwc-button
        >-->
      </hacs-dialog>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return css`
      .uninstall {
        --mdc-theme-primary: var(--hcv-color-error);
      }
    `;
        }
      }, {
        kind: "method",
        key: "_lovelaceUrl",
        value: function _lovelaceUrl() {
          var _this$repository, _this$repository2;

          return `/hacsfiles/${(_this$repository = this.repository) === null || _this$repository === void 0 ? void 0 : _this$repository.full_name.split("/")[1]}/${(_this$repository2 = this.repository) === null || _this$repository2 === void 0 ? void 0 : _this$repository2.file_name}`;
        }
      }, {
        kind: "method",
        key: "_uninstallRepository",
        value: async function _uninstallRepository() {
          this._updating = true;

          if (this.repository.category === "plugin" && this.hacs.status && this.hacs.status.lovelace_mode !== "yaml") {
            const resources = await fetchResources(this.hass);
            resources.filter(resource => resource.url === this._lovelaceUrl()).forEach(resource => {
              deleteResource(this.hass, String(resource.id));
            });
          }

          await repositoryUninstall(this.hass, this.repository.id);
          this._updating = false;
          this.active = false;
        }
      }, {
        kind: "method",
        key: "_ignoreMessage",
        value: async function _ignoreMessage() {
          this._updating = false;
          this.active = false;
        }
      }]
    };
  }, HacsDialogBase);

  let HacsRepositoryDialog = _decorate([customElement("hacs-repository-info-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsRepositoryDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsRepositoryDialog,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_repository",
        value: void 0
      }, {
        kind: "field",
        key: "_getRepository",

        value() {
          return memoizeOne((repositories, repository) => repositories === null || repositories === void 0 ? void 0 : repositories.find(repo => repo.id === repository));
        }

      }, {
        kind: "field",
        key: "_getAuthors",

        value() {
          return memoizeOne(repository => {
            const authors = [];
            if (!repository.authors) return authors;
            repository.authors.forEach(author => authors.push(author.replace("@", "")));

            if (authors.length === 0) {
              const author = repository.full_name.split("/")[0];

              if (["custom-cards", "custom-components", "home-assistant-community-themes"].includes(author)) {
                return authors;
              }

              authors.push(author);
            }

            return authors;
          });
        }

      }, {
        kind: "method",
        key: "shouldUpdate",
        value: function shouldUpdate(changedProperties) {
          changedProperties.forEach((_oldValue, propName) => {
            if (propName === "hass") {
              this.sidebarDocked = window.localStorage.getItem("dockedSidebar") === '"docked"';
            }

            if (propName === "repositories") {
              this._repository = this._getRepository(this.repositories, this.repository);
            }
          });
          return changedProperties.has("sidebarDocked") || changedProperties.has("narrow") || changedProperties.has("active") || changedProperties.has("_repository");
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          this._repository = this._getRepository(this.repositories, this.repository);

          if (!this._repository.updated_info) {
            await repositoryUpdate(this.hass, this._repository.id);
            this.repositories = await getRepositories(this.hass);
          }
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;

          const authors = this._getAuthors(this._repository);

          return html`
      <hacs-dialog
        .noActions=${this._repository.installed}
        .active=${this.active}
        .title=${this._repository.name || ""}
        .hass=${this.hass}
        ><div class=${classMap({
          content: true,
          narrow: this.narrow
        })}>
          <div class="chips">
            ${this._repository.installed ? html`<hacs-chip
                  title="${localize("dialog_info.version_installed")}"
                  .icon=${mdiCube}
                  .value=${this._repository.installed_version}
                ></hacs-chip>` : ""}
            ${authors ? authors.map(author => html`<hacs-chip
                    title="${localize("dialog_info.author")}"
                    .url="https://github.com/${author}"
                    .icon=${mdiAccount}
                    .value="@${author}"
                  ></hacs-chip>`) : ""}
            ${this._repository.downloads ? html` <hacs-chip
                  title="${localize("dialog_info.downloads")}"
                  .icon=${mdiArrowDownBold}
                  .value=${this._repository.downloads}
                ></hacs-chip>` : ""}
            <hacs-chip
              title="${localize("dialog_info.stars")}"
              .icon=${mdiStar}
              .value=${this._repository.stars}
            ></hacs-chip>
            <hacs-chip
              title="${localize("dialog_info.open_issues")}"
              .icon=${mdiExclamationThick}
              .value=${this._repository.issues}
              .url="https://github.com/${this._repository.full_name}/issues"
            ></hacs-chip>
          </div>
          ${this._repository.updated_info ? markdown.html(this._repository.additional_info || localize("dialog_info.no_info"), this._repository) : localize("dialog_info.loading")}
        </div>
        ${!this._repository.installed && this._repository.updated_info ? html`
              <mwc-button slot="primaryaction" @click=${this._installRepository}
                >${localize("dialog_info.install")}</mwc-button
              ><hacs-link
                slot="secondaryaction"
                .url="https://github.com/${this._repository.full_name}"
                ><mwc-button>${localize("dialog_info.open_repo")}</mwc-button></hacs-link
              >
            ` : ""}
      </hacs-dialog>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [scrollBarStyle, css`
        .content {
          width: 100%;
          overflow: auto;
          max-height: 870px;
        }
        .narrow {
          max-height: 480px;
          min-width: unset !important;
          width: 100% !important;
        }
        img {
          max-width: 100%;
        }
        .chips {
          display: flex;
          padding-bottom: 8px;
        }
        hacs-chip {
          margin: 0 4px;
        }
        div.chips hacs-link {
          margin: -24px 4px;
        }
        hacs-link mwc-button {
          margin-top: -18px;
        }
      `];
        }
      }, {
        kind: "method",
        key: "_installRepository",
        value: async function _installRepository() {
          this.dispatchEvent(new CustomEvent("hacs-dialog-secondary", {
            detail: {
              type: "install",
              repository: this._repository.id
            },
            bubbles: true,
            composed: true
          }));
        }
      }]
    };
  }, HacsDialogBase);

  let HacsUpdateDialog = _decorate([customElement("hacs-update-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsUpdateDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsUpdateDialog,
      d: [{
        kind: "field",
        decorators: [property()],
        key: "repository",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_updating",

        value() {
          return false;
        }

      }, {
        kind: "field",
        decorators: [property()],
        key: "_error",
        value: void 0
      }, {
        kind: "field",
        decorators: [property()],
        key: "_releaseNotes",

        value() {
          return [];
        }

      }, {
        kind: "field",
        key: "_getRepository",

        value() {
          return memoizeOne((repositories, repository) => repositories === null || repositories === void 0 ? void 0 : repositories.find(repo => repo.id === repository));
        }

      }, {
        kind: "method",
        key: "firstUpdated",
        value: async function firstUpdated() {
          const repository = this._getRepository(this.repositories, this.repository);

          if (repository.version_or_commit !== "commit") {
            this._releaseNotes = await repositoryReleasenotes(this.hass, repository.id);
            this._releaseNotes = this._releaseNotes.filter(release => release.tag > repository.installed_version);
          }

          this.hass.connection.subscribeEvents(msg => this._error = msg.data, "hacs/error");
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;

          const repository = this._getRepository(this.repositories, this.repository);

          return html`
      <hacs-dialog
        .active=${this.active}
        .title=${localize("dialog_update.title")}
        .hass=${this.hass}
      >
        <div class=${classMap({
          content: true,
          narrow: this.narrow
        })}>
          ${repository.name}
          <p>
            <b>${localize("dialog_update.installed_version")}:</b>
            ${repository.installed_version}
          </p>
          <p>
            <b>${localize("dialog_update.available_version")}:</b>
            ${repository.available_version}
          </p>
          ${this._releaseNotes.length > 0 ? this._releaseNotes.map(release => html`<details>
                  <summary>${release.name}</summary>
                  ${markdown.html(release.body)}
                </details>`) : ""}
          ${!repository.can_install ? html`<p class="error">
                ${localize("confirm.home_assistant_version_not_correct").replace("{haversion}", this.hass.config.version).replace("{minversion}", repository.homeassistant)}
              </p>` : ""}
          ${repository.category === "integration" ? html`<p>${localize("dialog_install.restart")}</p>` : ""}
          ${this._error ? html`<div class="error">${this._error.message}</div>` : ""}
        </div>
        <mwc-button
          slot="primaryaction"
          ?disabled=${!repository.can_install}
          @click=${this._updateRepository}
          >${this._updating ? html`<ha-circular-progress active></ha-circular-progress>` : localize("common.update")}</mwc-button
        >
        <div class="secondary" slot="secondaryaction">
          <hacs-link .url=${this._getChanglogURL()}
            ><mwc-button>${localize("dialog_update.changelog")}</mwc-button></hacs-link
          >
          <hacs-link .url="https://github.com/${repository.full_name}"
            ><mwc-button>${localize("common.repository")}</mwc-button></hacs-link
          >
        </div>
      </hacs-dialog>
    `;
        }
      }, {
        kind: "method",
        key: "_updateRepository",
        value: async function _updateRepository() {
          this._updating = true;

          const repository = this._getRepository(this.repositories, this.repository);

          if (repository.version_or_commit !== "commit") {
            await repositoryInstallVersion(this.hass, repository.id, repository.available_version);
          } else {
            await repositoryInstall(this.hass, repository.id);
          }

          if (repository.category === "plugin") {
            if (this.hacs.status.lovelace_mode === "storage") {
              await updateLovelaceResources(this.hass, repository);
            }
          }

          this._updating = false;
          this.dispatchEvent(new Event("hacs-dialog-closed", {
            bubbles: true,
            composed: true
          }));

          if (repository.category === "plugin") {
            this.dispatchEvent(new CustomEvent("hacs-dialog", {
              detail: {
                type: "reload"
              },
              bubbles: true,
              composed: true
            }));
          }
        }
      }, {
        kind: "method",
        key: "_getChanglogURL",
        value: function _getChanglogURL() {
          const repository = this._getRepository(this.repositories, this.repository);

          if (repository.version_or_commit === "commit") {
            return `https://github.com/${repository.full_name}/compare/${repository.installed_version}...${repository.available_version}`;
          }

          return `https://github.com/${repository.full_name}/releases`;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [scrollBarStyle, css`
        .content {
          padding: 32px 8px;
        }
        .error {
          color: var(--hacs-error-color, var(--google-red-500));
        }
        details {
          padding: 12px 0;
        }
        summary {
          padding: 4px;
          cursor: pointer;
        }
        .secondary {
          display: flex;
        }
      `];
        }
      }]
    };
  }, HacsDialogBase);

  let HacsEventDialog = _decorate([customElement("hacs-event-dialog")], function (_initialize, _HacsDialogBase) {
    class HacsEventDialog extends _HacsDialogBase {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsEventDialog,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "params",
        value: void 0
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.active) return html``;
          const el = document.createElement(`hacs-${this.params.type || "generic"}-dialog`);
          el.active = true;
          el.hass = this.hass;
          el.hacs = this.hacs;
          el.narrow = this.narrow;
          el.configuration = this.configuration;
          el.lovelace = this.lovelace;
          el.secondary = this.secondary;
          el.repositories = this.repositories;
          el.route = this.route;
          el.status = this.status;

          if (this.params) {
            for (let [key, value] of Object.entries(this.params)) {
              el[key] = value;
            }
          }

          return html`${el}`;
        }
      }]
    };
  }, HacsDialogBase);

  let HacsFrontend = _decorate([customElement("hacs-frontend")], function (_initialize, _HacsElement) {
    class HacsFrontend extends _HacsElement {
      constructor(...args) {
        super(...args);

        _initialize(this);
      }

    }

    return {
      F: HacsFrontend,
      d: [{
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "configuration",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "critical",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "hass",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "lovelace",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "narrow",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "removed",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "repositories",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "route",
        value: void 0
      }, {
        kind: "field",
        decorators: [property({
          attribute: false
        })],
        key: "status",
        value: void 0
      }, {
        kind: "field",
        decorators: [query("#hacs-dialog")],
        key: "_hacsDialog",
        value: void 0
      }, {
        kind: "field",
        decorators: [query("#hacs-dialog-secondary")],
        key: "_hacsDialogSecondary",
        value: void 0
      }, {
        kind: "method",
        key: "connectedCallback",
        value: async function connectedCallback() {
          _get(_getPrototypeOf(HacsFrontend.prototype), "connectedCallback", this).call(this);

          this.addEventListener("hacs-location-changed", e => this._setRoute(e));
          this.addEventListener("hacs-dialog", e => this._showDialog(e));
          this.addEventListener("hacs-dialog-secondary", e => this._showDialogSecondary(e));
          this.hass.connection.subscribeEvents(async () => await this._updateProperties("configuration"), "hacs/config");
          this.hass.connection.subscribeEvents(async () => await this._updateProperties("status"), "hacs/status");
          this.hass.connection.subscribeEvents(async () => await this._updateProperties("status"), "hacs/stage");
          this.hass.connection.subscribeEvents(async () => await this._updateProperties("repositories"), "hacs/repository");
          this.hass.connection.subscribeEvents(async () => await this._updateProperties("lovelace"), "lovelace_updated");
          await this._updateProperties();
        }
      }, {
        kind: "method",
        key: "_updateProperties",
        value: async function _updateProperties(prop = "all") {
          let repositories;
          const _updates = {};
          const _fetch = {};

          if (prop === "all") {
            [repositories, _fetch.configuration, _fetch.status, _fetch.critical, _fetch.resources, _fetch.removed] = await Promise.all([getRepositories(this.hass), getConfiguration(this.hass), getStatus(this.hass), getCritical(this.hass), getLovelaceConfiguration(this.hass), getRemovedRepositories(this.hass)]); //this.removed = removed;
            //this.critical = critical;

            this.lovelace = _fetch.resources;
            this.repositories = repositories;
          } else if (prop === "configuration") {
            _fetch.configuration = await getConfiguration(this.hass);
          } else if (prop === "status") {
            _fetch.status = await getStatus(this.hass);
          } else if (prop === "repositories") {
            this.repositories = await getRepositories(this.hass);
          } else if (prop === "lovelace") {
            _fetch.resources = await getLovelaceConfiguration(this.hass);
          }

          Object.keys(_fetch).forEach(update => {
            if (_fetch[update] !== undefined) {
              _updates[update] = _fetch[update];
            }
          });

          if (_updates) {
            this._updateHacs(_updates);
          }
        }
      }, {
        kind: "method",
        key: "firstUpdated",
        value: function firstUpdated(changedProps) {
          _get(_getPrototypeOf(HacsFrontend.prototype), "firstUpdated", this).call(this, changedProps);

          if (this.route.path === "") {
            navigate(this, "/hacs/entry", true);
          }

          this._applyTheme();
        }
      }, {
        kind: "method",
        key: "render",
        value: function render() {
          if (!this.hass || !this.hacs) return html``;
          return html`
      <hacs-router
        .hass=${this.hass}
        .hacs=${this.hacs}
        .route=${this.route}
        .narrow=${this.narrow}
        .configuration=${this.configuration}
        .lovelace=${this.lovelace}
        .status=${this.status}
        .critical=${this.critical}
        .removed=${this.removed}
        .repositories=${this.repositories}
      ></hacs-router>
      <hacs-event-dialog
        .hass=${this.hass}
        .hacs=${this.hacs}
        .route=${this.route}
        .narrow=${this.narrow}
        .configuration=${this.configuration}
        .lovelace=${this.lovelace}
        .status=${this.status}
        .removed=${this.removed}
        .repositories=${this.repositories}
        id="hacs-dialog"
      ></hacs-event-dialog>
      <hacs-event-dialog
        .hass=${this.hass}
        .hacs=${this.hacs}
        .route=${this.route}
        .narrow=${this.narrow}
        .configuration=${this.configuration}
        .lovelace=${this.lovelace}
        .status=${this.status}
        .removed=${this.removed}
        .repositories=${this.repositories}
        id="hacs-dialog-secondary"
      ></hacs-event-dialog>
    `;
        }
      }, {
        kind: "get",
        static: true,
        key: "styles",
        value: function styles() {
          return [HacsStyles, hacsStyleVariables];
        }
      }, {
        kind: "method",
        key: "_showDialog",
        value: function _showDialog(ev) {
          const dialogParams = ev.detail;
          this._hacsDialog.active = true;
          this._hacsDialog.params = dialogParams;
          this.addEventListener("hacs-dialog-closed", () => this._hacsDialog.active = false);
        }
      }, {
        kind: "method",
        key: "_showDialogSecondary",
        value: function _showDialogSecondary(ev) {
          const dialogParams = ev.detail;
          this._hacsDialogSecondary.active = true;
          this._hacsDialogSecondary.secondary = true;
          this._hacsDialogSecondary.params = dialogParams;
          this.addEventListener("hacs-secondary-dialog-closed", () => this._hacsDialogSecondary.active = false);
        }
      }, {
        kind: "method",
        key: "_setRoute",
        value: function _setRoute(ev) {
          this.route = ev.detail.route;
          navigate(this, this.route.path, true);
          this.requestUpdate();
        }
      }, {
        kind: "method",
        key: "_applyTheme",
        value: function _applyTheme() {
          let themeName;
          let options;

          if (atLeastVersion(this.hass.config.version, 0, 114)) {
            var _this$hass$selectedTh, _options;

            themeName = ((_this$hass$selectedTh = this.hass.selectedTheme) === null || _this$hass$selectedTh === void 0 ? void 0 : _this$hass$selectedTh.theme) || (this.hass.themes.darkMode && this.hass.themes.default_dark_theme ? this.hass.themes.default_dark_theme : this.hass.themes.default_theme);
            options = this.hass.selectedTheme;

            if (themeName === "default" && ((_options = options) === null || _options === void 0 ? void 0 : _options.dark) === undefined) {
              options = { ...this.hass.selectedTheme,
                dark: this.hass.themes.darkMode
              };
            }
          } else {
            themeName = this.hass.selectedTheme || this.hass.themes.default_theme;
          }

          applyThemesOnElement(this.parentElement, this.hass.themes, themeName, options);
        }
      }]
    };
  }, HacsElement);

}());
