| /** | |
| * @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 lit-html | |
| */ | |
| import {reparentNodes} from './dom.js'; | |
| import {TemplateProcessor} from './template-processor.js'; | |
| import {boundAttributeSuffix, lastAttributeNameRegex, marker, nodeMarker} from './template.js'; | |
| declare const trustedTypes: typeof window.trustedTypes; | |
| /** | |
| * 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. | |
| */ | |
| export class TemplateResult { | |
| readonly strings: TemplateStringsArray; | |
| readonly values: readonly unknown[]; | |
| readonly type: string; | |
| readonly processor: TemplateProcessor; | |
| constructor( | |
| strings: TemplateStringsArray, values: readonly unknown[], type: string, | |
| processor: TemplateProcessor) { | |
| this.strings = strings; | |
| this.values = values; | |
| this.type = type; | |
| this.processor = processor; | |
| } | |
| /** | |
| * Returns a string of HTML used to create a `<template>` element. | |
| */ | |
| getHTML(): string { | |
| 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(): HTMLTemplateElement { | |
| 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) as unknown as string; | |
| } | |
| template.innerHTML = value; | |
| 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. | |
| */ | |
| export class SVGTemplateResult extends TemplateResult { | |
| getHTML(): string { | |
| return `<svg>${super.getHTML()}</svg>`; | |
| } | |
| getTemplateElement(): HTMLTemplateElement { | |
| const template = super.getTemplateElement(); | |
| const content = template.content; | |
| const svgElement = content.firstChild!; | |
| content.removeChild(svgElement); | |
| reparentNodes(content, svgElement.firstChild); | |
| return template; | |
| } | |
| } |