UNPKG

@suyouwanggang/p-ui

Version:

`p-ui`是一套使用原生`Web Components`规范开发的跨框架UI组件库,基于`lit-elment`库开发。 [github项目地址](https://github.com/suyouwanggang/p-ui)

1,530 lines (1,307 loc) 462 kB
import _classCallCheck from '@babel/runtime/helpers/classCallCheck'; import _createClass from '@babel/runtime/helpers/createClass'; import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn'; import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf'; import _get from '@babel/runtime/helpers/get'; import _inherits from '@babel/runtime/helpers/inherits'; import _typeof from '@babel/runtime/helpers/typeof'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _regeneratorRuntime from '@babel/runtime/regenerator'; import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator'; import _wrapNativeSuper from '@babel/runtime/helpers/wrapNativeSuper'; /** * @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. */ var isCEPolyfill = typeof window !== 'undefined' && window.customElements != null && window.customElements.polyfillWrapFlushCallback !== undefined; /** * Reparents nodes, starting from `start` (inclusive) to `end` (exclusive), * into another container (could be the same container), before `before`. If * `before` is null, it appends the nodes to the container. */ var reparentNodes = function reparentNodes(container, start) { var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var before = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; while (start !== end) { var n = start.nextSibling; container.insertBefore(start, before); start = n; } }; /** * Removes nodes, starting from `start` (inclusive) to `end` (exclusive), from * `container`. */ var removeNodes = function removeNodes(container, start) { var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; while (start !== end) { var 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. */ 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. */ var nodeMarker = "<!--".concat(marker, "-->"); var markerRegex = new RegExp("".concat(marker, "|").concat(nodeMarker)); /** * Suffix appended to all bound attribute names. */ var boundAttributeSuffix = '$lit$'; /** * An updatable Template that tracks the location of dynamic parts. */ 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); } }; 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. 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-(') */ var 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 */ var 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) { var content = template.element.content, parts = template.parts; var walker = document.createTreeWalker(content, walkerNodeFilter, null, false); var partIndex = nextActiveIndexInTemplateParts(parts); var part = parts[partIndex]; var nodeIndex = -1; var removeCount = 0; var nodesToRemoveInTemplate = []; var currentRemovingNode = null; while (walker.nextNode()) { nodeIndex++; var 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(function (n) { return n.parentNode.removeChild(n); }); } var countNodes = function countNodes(node) { var count = node.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ? 0 : 1; var walker = document.createTreeWalker(node, walkerNodeFilter, null, false); while (walker.nextNode()) { count++; } return count; }; var nextActiveIndexInTemplateParts = function nextActiveIndexInTemplateParts(parts) { var startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1; for (var i = startIndex + 1; i < parts.length; i++) { var 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) { var refNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var content = template.element.content, parts = template.parts; // 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; } var walker = document.createTreeWalker(content, walkerNodeFilter, null, false); var partIndex = nextActiveIndexInTemplateParts(parts); var insertCount = 0; var walkerIndex = -1; while (walker.nextNode()) { walkerIndex++; var 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 */ var directives = new WeakMap(); var isDirective = function 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. */ var noChange = {}; /** * A sentinel value that signals a NodePart to fully clear its content. */ var nothing = {}; /** * An instance of a `Template` that can be attached to the DOM and updated * with new values. */ var TemplateInstance = /*#__PURE__*/ function () { function TemplateInstance(template, processor, options) { _classCallCheck(this, TemplateInstance); this.__parts = []; this.template = template; this.processor = processor; this.options = options; } _createClass(TemplateInstance, [{ key: "update", value: function update(values) { var i = 0; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.__parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var part = _step.value; if (part !== undefined) { part.setValue(values[i]); } i++; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.__parts[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _part = _step2.value; if (_part !== undefined) { _part.commit(); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } }, { key: "_clone", value: function _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. var fragment = isCEPolyfill ? this.template.element.content.cloneNode(true) : document.importNode(this.template.element.content, true); var stack = []; var parts = this.template.parts; // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null var walker = document.createTreeWalker(fragment, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */ , null, false); var partIndex = 0; var nodeIndex = 0; var part; var 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') { var _part2 = this.processor.handleTextExpression(this.options); _part2.insertAfterNode(node.previousSibling); this.__parts.push(_part2); } else { var _this$__parts; (_this$__parts = this.__parts).push.apply(_this$__parts, _toConsumableArray(this.processor.handleAttributeExpressions(node, part.name, part.strings, this.options))); } partIndex++; } if (isCEPolyfill) { document.adoptNode(fragment); customElements.upgrade(fragment); } return fragment; } }]); return TemplateInstance; }(); /** * 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. */ var policy = window.trustedTypes && trustedTypes.createPolicy('lit-html', { createHTML: function createHTML(s) { return s; } }); var commentMarker = " ".concat(marker, " "); /** * The return type of `html`, which holds a Template and the values from * interpolated expressions. */ var TemplateResult = /*#__PURE__*/ function () { function TemplateResult(strings, values, type, processor) { _classCallCheck(this, TemplateResult); this.strings = strings; this.values = values; this.type = type; this.processor = processor; } /** * Returns a string of HTML used to create a `<template>` element. */ _createClass(TemplateResult, [{ key: "getHTML", value: function getHTML() { var l = this.strings.length - 1; var html = ''; var isCommentBinding = false; for (var i = 0; i < l; i++) { var 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. var 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. var 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; } }, { key: "getTemplateElement", value: function getTemplateElement() { var template = document.createElement('template'); var 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; } }]); return TemplateResult; }(); /** * 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. */ var SVGTemplateResult = /*#__PURE__*/ function (_TemplateResult) { _inherits(SVGTemplateResult, _TemplateResult); function SVGTemplateResult() { _classCallCheck(this, SVGTemplateResult); return _possibleConstructorReturn(this, _getPrototypeOf(SVGTemplateResult).apply(this, arguments)); } _createClass(SVGTemplateResult, [{ key: "getHTML", value: function getHTML() { return "<svg>".concat(_get(_getPrototypeOf(SVGTemplateResult.prototype), "getHTML", this).call(this), "</svg>"); } }, { key: "getTemplateElement", value: function getTemplateElement() { var template = _get(_getPrototypeOf(SVGTemplateResult.prototype), "getTemplateElement", this).call(this); var content = template.content; var svgElement = content.firstChild; content.removeChild(svgElement); reparentNodes(content, svgElement.firstChild); return template; } }]); return SVGTemplateResult; }(TemplateResult); var isPrimitive = function isPrimitive(value) { return value === null || !(_typeof(value) === 'object' || typeof value === 'function'); }; var isIterable = function 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. */ var AttributeCommitter = /*#__PURE__*/ function () { function AttributeCommitter(element, name, strings) { _classCallCheck(this, AttributeCommitter); this.dirty = true; this.element = element; this.name = name; this.strings = strings; this.parts = []; for (var 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. */ _createClass(AttributeCommitter, [{ key: "_createPart", value: function _createPart() { return new AttributePart(this); } }, { key: "_getValue", value: function _getValue() { var strings = this.strings; var l = strings.length - 1; var 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] === '') { var v = parts[0].value; if (_typeof(v) === 'symbol') { return String(v); } if (typeof v === 'string' || !isIterable(v)) { return v; } } var text = ''; for (var i = 0; i < l; i++) { text += strings[i]; var part = parts[i]; if (part !== undefined) { var _v = part.value; if (isPrimitive(_v) || !isIterable(_v)) { text += typeof _v === 'string' ? _v : String(_v); } else { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _v[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var t = _step.value; text += typeof t === 'string' ? t : String(t); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } } } text += strings[l]; return text; } }, { key: "commit", value: function commit() { if (this.dirty) { this.dirty = false; this.element.setAttribute(this.name, this._getValue()); } } }]); return AttributeCommitter; }(); /** * A Part that controls all or part of an attribute value. */ var AttributePart = /*#__PURE__*/ function () { function AttributePart(committer) { _classCallCheck(this, AttributePart); this.value = undefined; this.committer = committer; } _createClass(AttributePart, [{ key: "setValue", value: function 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; } } } }, { key: "commit", value: function commit() { while (isDirective(this.value)) { var directive = this.value; this.value = noChange; directive(this); } if (this.value === noChange) { return; } this.committer.commit(); } }]); return AttributePart; }(); /** * 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. */ var NodePart = /*#__PURE__*/ function () { function NodePart(options) { _classCallCheck(this, NodePart); 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. */ _createClass(NodePart, [{ key: "appendInto", value: function 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. */ }, { key: "insertAfterNode", value: function 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. */ }, { key: "appendIntoPart", value: function 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. */ }, { key: "insertAfterPart", value: function insertAfterPart(ref) { ref.__insert(this.startNode = createMarker()); this.endNode = ref.endNode; ref.endNode = this.startNode; } }, { key: "setValue", value: function setValue(value) { this.__pendingValue = value; } }, { key: "commit", value: function commit() { if (this.startNode.parentNode === null) { return; } while (isDirective(this.__pendingValue)) { var directive = this.__pendingValue; this.__pendingValue = noChange; directive(this); } var 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); } } }, { key: "__insert", value: function __insert(node) { this.endNode.parentNode.insertBefore(node, this.endNode); } }, { key: "__commitNode", value: function __commitNode(value) { if (this.value === value) { return; } this.clear(); this.__insert(value); this.value = value; } }, { key: "__commitText", value: function __commitText(value) { var 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. var 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; } }, { key: "__commitTemplateResult", value: function __commitTemplateResult(value) { var 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. var instance = new TemplateInstance(template, value.processor, this.options); var fragment = instance._clone(); instance.update(value.values); this.__commitNode(fragment); this.value = instance; } } }, { key: "__commitIterable", value: function __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 var itemParts = this.value; var partIndex = 0; var itemPart; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = value[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var item = _step2.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++; } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } if (partIndex < itemParts.length) { // Truncate the parts array so _value reflects the current state itemParts.length = partIndex; this.clear(itemPart && itemPart.endNode); } } }, { key: "clear", value: function clear() { var startNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.startNode; removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode); } }]); return NodePart; }(); /** * 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. */ var BooleanAttributePart = /*#__PURE__*/ function () { function BooleanAttributePart(element, name, strings) { _classCallCheck(this, BooleanAttributePart); 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; } _createClass(BooleanAttributePart, [{ key: "setValue", value: function setValue(value) { this.__pendingValue = value; } }, { key: "commit", value: function commit() { while (isDirective(this.__pendingValue)) { var directive = this.__pendingValue; this.__pendingValue = noChange; directive(this); } if (this.__pendingValue === noChange) { return; } var 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; } }]); return BooleanAttributePart; }(); /** * 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. */ var PropertyCommitter = /*#__PURE__*/ function (_AttributeCommitter) { _inherits(PropertyCommitter, _AttributeCommitter); function PropertyCommitter(element, name, strings) { var _this; _classCallCheck(this, PropertyCommitter); _this = _possibleConstructorReturn(this, _getPrototypeOf(PropertyCommitter).call(this, element, name, strings)); _this.single = strings.length === 2 && strings[0] === '' && strings[1] === ''; return _this; } _createClass(PropertyCommitter, [{ key: "_createPart", value: function _createPart() { return new PropertyPart(this); } }, { key: "_getValue", value: function _getValue() { if (this.single) { return this.parts[0].value; } return _get(_getPrototypeOf(PropertyCommitter.prototype), "_getValue", this).call(this); } }, { key: "commit", value: function commit() { if (this.dirty) { this.dirty = false; // eslint-disable-next-line @typescript-eslint/no-explicit-any this.element[this.name] = this._getValue(); } } }]); return PropertyCommitter; }(AttributeCommitter); var PropertyPart = /*#__PURE__*/ function (_AttributePart) { _inherits(PropertyPart, _AttributePart); function PropertyPart() { _classCallCheck(this, PropertyPart); return _possibleConstructorReturn(this, _getPrototypeOf(PropertyPart).apply(this, arguments)); } return PropertyPart; }(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. var eventOptionsSupported = false; // Wrap into an IIFE because MS Edge <= v41 does not support having try/catch // blocks right into the body of a module (function () { try { var 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 } })(); var EventPart = /*#__PURE__*/ function () { function EventPart(element, eventName, eventContext) { var _this2 = this; _classCallCheck(this, EventPart); this.value = undefined; this.__pendingValue = undefined; this.element = element; this.eventName = eventName; this.eventContext = eventContext; this.__boundHandleEvent = function (e) { return _this2.handleEvent(e); }; } _createClass(EventPart, [{ key: "setValue", value: function setValue(value) { this.__pendingValue = value; } }, { key: "commit", value: function commit() { while (isDirective(this.__pendingValue)) { var directive = this.__pendingValue; this.__pendingValue = noChange; directive(this); } if (this.__pendingValue === noChange) { return; } var newListener = this.__pendingValue; var oldListener = this.value; var shouldRemoveListener = newListener == null || oldListener != null && (newListener.capture !== oldListener.capture || newListener.once !== oldListener.once || newListener.passive !== oldListener.passive); var 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; } }, { key: "handleEvent", value: function handleEvent(event) { if (typeof this.value === 'function') { this.value.call(this.eventContext || this.element, event); } else { this.value.handleEvent(event); } } }]); return EventPart; }(); // 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. var getOptions = function getOptions(o) { return 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 */ /