es5-lit-html
Version:
An ES5 transpiled version of lit-html
283 lines (234 loc) • 9.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.lastAttributeNameRegex = exports.createMarker = exports.isTemplatePartActive = exports.Template = exports.boundAttributeSuffix = exports.markerRegex = exports.nodeMarker = exports.marker = void 0;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* @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.
*/
var marker = "{{lit-".concat(String(Math.random()).slice(2), "}}");
/**
* An expression marker used text-positions, multi-binding attributes, and
* attributes with markup-like text values.
*/
exports.marker = marker;
var nodeMarker = "<!--".concat(marker, "-->");
exports.nodeMarker = nodeMarker;
var markerRegex = new RegExp("".concat(marker, "|").concat(nodeMarker));
/**
* Suffix appended to all bound attribute names.
*/
exports.markerRegex = markerRegex;
var boundAttributeSuffix = '$lit$';
/**
* An updateable Template that tracks the location of dynamic parts.
*/
exports.boundAttributeSuffix = boundAttributeSuffix;
var Template = function Template(result, element) {
_classCallCheck(this, Template);
this.parts = [];
this.element = element;
var nodesToRemove = [];
var stack = []; // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null
var 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.
var lastPartIndex = 0;
var index = -1;
var partIndex = 0;
var strings = result.strings,
length = result.values.length;
while (partIndex < length) {
var 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()) {
var attributes = node.attributes;
var _length = attributes.length; // 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.
var count = 0;
for (var 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
var stringForPart = strings[partIndex]; // Find the attribute name
var 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.
var attributeLookupName = name.toLowerCase() + boundAttributeSuffix;
var attributeValue = node.getAttribute(attributeLookupName);
node.removeAttribute(attributeLookupName);
var statics = attributeValue.split(markerRegex);
this.parts.push({
type: 'attribute',
index: index,
name: 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 */
) {
var data = node.data;
if (data.indexOf(marker) >= 0) {
var parent = node.parentNode;
var _strings = data.split(markerRegex);
var 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 (var _i = 0; _i < lastIndex; _i++) {
var insert = void 0;
var s = _strings[_i];
if (s === '') {
insert = createMarker();
} else {
var 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) {
var _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: 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 {
var _i2 = -1;
while ((_i2 = node.data.indexOf(marker, _i2 + 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 (var _i3 = 0, _nodesToRemove = nodesToRemove; _i3 < _nodesToRemove.length; _i3++) {
var n = _nodesToRemove[_i3];
n.parentNode.removeChild(n);
}
};
exports.Template = Template;
var endsWith = function endsWith(str, suffix) {
var index = str.length - suffix.length;
return index >= 0 && str.slice(index) === suffix;
};
var isTemplatePartActive = function isTemplatePartActive(part) {
return part.index !== -1;
}; // Allows `document.createComment('')` to be renamed for a
// small manual size-savings.
exports.isTemplatePartActive = isTemplatePartActive;
var createMarker = function createMarker() {
return 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-(')
*/
exports.createMarker = createMarker;
var lastAttributeNameRegex = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
exports.lastAttributeNameRegex = lastAttributeNameRegex;