` 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,