diff --git a/.npmignore b/.npmignore
index b61770c..52359a5 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,6 +1,4 @@
/node_modules
-/src
/examples
-.DS_Store
-rollup.config.js
\ No newline at end of file
+.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index a0cfcd0..bc0a21f 100644
--- a/README.md
+++ b/README.md
@@ -318,7 +318,7 @@ Instead of using config in the HOC, you can put it in the Component class. This
- initialValues: `{ propName: value }`
- validationSchema: `{ propName: (value, props) => 'error'||{} }`
- validateOnBlur: `boolean`. Default: `true`
-- validateOnChang: `boolean`. Default: `true`
+- validateOnChange: `boolean`. Default: `true`
##### Props
- values: `object`
@@ -326,8 +326,9 @@ Instead of using config in the HOC, you can put it in the Component class. This
- touched: `object`
- isValid: `boolean`
- handleSubmit: `function`
-- handleChange: `(path || event, value) => {}`. event: `Event: {target: {name || id, value}} || CustomEvent: {detail: {name || id, value}}`
-- handleBlur: `(path || event) => {}`. event: `Event: {target: {name || id}} || CustomEvent: {detail: {name || id}}`
+TODO: new signature for 2 following events + added SetTouched + validate params to setValue
+- handleChange: `(event, value) => {}`. event: `Event: {target: {name || id, value}} || CustomEvent: {detail: {name || id, value}}`
+- handleBlur: `(event) => {}`. event: `Event: {target: {name || id}} || CustomEvent: {detail: {name || id}}`
- handleValidate: `function`
- handleReset: `function`
@@ -364,7 +365,7 @@ Component must have `name` (means path) or `id` attribute.
##### Usage
`withError(Component)`
-Component must have `name` (means path) or `id` attribute.
+Component must have `name`.
##### Props
- error: `object`
diff --git a/examples/bundle.js b/examples/bundle.js
new file mode 100644
index 0000000..1608097
--- /dev/null
+++ b/examples/bundle.js
@@ -0,0 +1,5883 @@
+(function (factory) {
+ typeof define === 'function' && define.amd ? define(factory) :
+ factory();
+}((function () { 'use strict';
+
+ /**
+ * @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;
+ /**
+ * 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 = ``;
+ 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`
<${repeat(items, (item) => html`- ${item}
`)}
`
+ * ```
+ *
+ * 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
+ */
+ /**
+ * Our TrustedTypePolicy for HTML which is declared using the html template
+ * tag function.
+ *
+ * That HTML is a developer-authored constant, and is parsed with innerHTML
+ * before any untrusted expressions have been mixed in. Therefor it is
+ * considered safe by construction.
+ */
+ const policy = window.trustedTypes &&
+ trustedTypes.createPolicy('lit-html', { createHTML: (s) => s });
+ 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 `` 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 .
+ // * 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('', 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 . Cases like
+ // 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');
+ let value = this.getHTML();
+ if (policy !== undefined) {
+ // this is secure because `this.strings` is a TemplateStringsArray.
+ // TODO: validate this when
+ // https://github.com/tc39/proposal-array-is-template-object is
+ // implemented.
+ value = policy.createHTML(value);
+ }
+ template.innerHTML = value;
+ 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;
+ const parts = this.parts;
+ // If we're assigning an attribute via syntax like:
+ // attr="${foo}" or attr=${foo}
+ // but not
+ // attr="${foo} ${bar}" or attr="${foo} baz"
+ // then we don't want to coerce the attribute value into one long
+ // string. Instead we want to just return the value itself directly,
+ // so that sanitizeDOMValue can get the actual value rather than
+ // String(value)
+ // The exception is if v is an array, in which case we do want to smash
+ // it together into a string without calling String() on the array.
+ //
+ // This also allows trusted values (when using TrustedTypes) being
+ // assigned to DOM sinks without being stringified in the process.
+ if (l === 1 && strings[0] === '' && strings[1] === '') {
+ const v = parts[0].value;
+ if (typeof v === 'symbol') {
+ return String(v);
+ }
+ if (typeof v === 'string' || !isIterable(v)) {
+ return v;
+ }
+ }
+ let text = '';
+ for (let i = 0; i < l; i++) {
+ text += strings[i];
+ const part = 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
+ */
+ // IMPORTANT: do not change the property name or the assignment expression.
+ // 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.3.0');
+ }
+ /**
+ * 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);
+
+ /**
+ * @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
+ */
+ // Get a key to lookup in `templateCaches`.
+ 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,