1/** 2 * @license 3 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 4 * This code may only be used under the BSD style license found at 5 * http://polymer.github.io/LICENSE.txt 6 * The complete set of authors may be found at 7 * http://polymer.github.io/AUTHORS.txt 8 * The complete set of contributors may be found at 9 * http://polymer.github.io/CONTRIBUTORS.txt 10 * Code distributed by Google as part of the polymer project is also 11 * subject to an additional IP rights grant found at 12 * http://polymer.github.io/PATENTS.txt 13 */ 14/** 15 * True if the custom elements polyfill is in use. 16 */ 17const isCEPolyfill = typeof window !== 'undefined' && 18 window.customElements != null && 19 window.customElements.polyfillWrapFlushCallback !== 20 undefined; 21/** 22 * Removes nodes, starting from `start` (inclusive) to `end` (exclusive), from 23 * `container`. 24 */ 25const removeNodes = (container, start, end = null) => { 26 while (start !== end) { 27 const n = start.nextSibling; 28 container.removeChild(start); 29 start = n; 30 } 31};/** 32 * @license 33 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 34 * This code may only be used under the BSD style license found at 35 * http://polymer.github.io/LICENSE.txt 36 * The complete set of authors may be found at 37 * http://polymer.github.io/AUTHORS.txt 38 * The complete set of contributors may be found at 39 * http://polymer.github.io/CONTRIBUTORS.txt 40 * Code distributed by Google as part of the polymer project is also 41 * subject to an additional IP rights grant found at 42 * http://polymer.github.io/PATENTS.txt 43 */ 44/** 45 * An expression marker with embedded unique key to avoid collision with 46 * possible text in templates. 47 */ 48const marker = `{{lit-${String(Math.random()).slice(2)}}}`; 49/** 50 * An expression marker used text-positions, multi-binding attributes, and 51 * attributes with markup-like text values. 52 */ 53const nodeMarker = `<!--${marker}-->`; 54const markerRegex = new RegExp(`${marker}|${nodeMarker}`); 55/** 56 * Suffix appended to all bound attribute names. 57 */ 58const boundAttributeSuffix = '$lit$'; 59/** 60 * An updatable Template that tracks the location of dynamic parts. 61 */ 62class Template { 63 constructor(result, element) { 64 this.parts = []; 65 this.element = element; 66 const nodesToRemove = []; 67 const stack = []; 68 // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null 69 const walker = document.createTreeWalker(element.content, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false); 70 // Keeps track of the last index associated with a part. We try to delete
vendor: 7,052 bytes, lines 71-207
71 // unnecessary nodes, but we never want to associate two different parts 72 // to the same index. They must have a constant node between. 73 let lastPartIndex = 0; 74 let index = -1; 75 let partIndex = 0; 76 const { strings, values: { length } } = result; 77 while (partIndex < length) { 78 const node = walker.nextNode(); 79 if (node === null) { 80 // We've exhausted the content inside a nested template element. 81 // Because we still have parts (the outer for-loop), we know: 82 // - There is a template in the stack 83 // - The walker will find a nextNode outside the template 84 walker.currentNode = stack.pop(); 85 continue; 86 } 87 index++; 88 if (node.nodeType === 1 /* Node.ELEMENT_NODE */) { 89 if (node.hasAttributes()) { 90 const attributes = node.attributes; 91 const { length } = attributes; 92 // Per 93 // https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap, 94 // attributes are not guaranteed to be returned in document order. 95 // In particular, Edge/IE can return them out of order, so we cannot 96 // assume a correspondence between part index and attribute index. 97 let count = 0; 98 for (let i = 0; i < length; i++) { 99 if (endsWith(attributes[i].name, boundAttributeSuffix)) { 100 count++; 101 } 102 } 103 while (count-- > 0) { 104 // Get the template literal section leading up to the first 105 // expression in this attribute 106 const stringForPart = strings[partIndex]; 107 // Find the attribute name 108 const name = lastAttributeNameRegex.exec(stringForPart)[2]; 109 // Find the corresponding attribute 110 // All bound attributes have had a suffix added in 111 // TemplateResult#getHTML to opt out of special attribute 112 // handling. To look up the attribute value we also need to add 113 // the suffix. 114 const attributeLookupName = name.toLowerCase() + boundAttributeSuffix; 115 const attributeValue = node.getAttribute(attributeLookupName); 116 node.removeAttribute(attributeLookupName); 117 const statics = attributeValue.split(markerRegex); 118 this.parts.push({ type: 'attribute', index, name, strings: statics }); 119 partIndex += statics.length - 1; 120 } 121 } 122 if (node.tagName === 'TEMPLATE') { 123 stack.push(node); 124 walker.currentNode = node.content; 125 } 126 } 127 else if (node.nodeType === 3 /* Node.TEXT_NODE */) { 128 const data = node.data; 129 if (data.indexOf(marker) >= 0) { 130 const parent = node.parentNode; 131 const strings = data.split(markerRegex); 132 const lastIndex = strings.length - 1; 133 // Generate a new text node for each literal section 134 // These nodes are also used as the markers for node parts 135 for (let i = 0; i < lastIndex; i++) { 136 let insert; 137 let s = strings[i]; 138 if (s === '') { 139 insert = createMarker(); 140 } 141 else { 142 const match = lastAttributeNameRegex.exec(s); 143 if (match !== null && endsWith(match[2], boundAttributeSuffix)) { 144 s = s.slice(0, match.index) + match[1] + 145 match[2].slice(0, -boundAttributeSuffix.length) + match[3]; 146 } 147 insert = document.createTextNode(s); 148 } 149 parent.insertBefore(insert, node); 150 this.parts.push({ type: 'node', index: ++index }); 151 } 152 // If there's no text, we must insert a comment to mark our place. 153 // Else, we can trust it will stick around after cloning. 154 if (strings[lastIndex] === '') { 155 parent.insertBefore(createMarker(), node); 156 nodesToRemove.push(node); 157 } 158 else { 159 node.data = strings[lastIndex]; 160 } 161 // We have a part for each match found 162 partIndex += lastIndex; 163 } 164 } 165 else if (node.nodeType === 8 /* Node.COMMENT_NODE */) { 166 if (node.data === marker) { 167 const parent = node.parentNode; 168 // Add a new marker node to be the startNode of the Part if any of 169 // the following are true: 170 // * We don't have a previousSibling 171 // * The previousSibling is already the start of a previous part 172 if (node.previousSibling === null || index === lastPartIndex) { 173 index++; 174 parent.insertBefore(createMarker(), node); 175 } 176 lastPartIndex = index; 177 this.parts.push({ type: 'node', index }); 178 // If we don't have a nextSibling, keep this node so we have an end. 179 // Else, we can remove it to save future costs. 180 if (node.nextSibling === null) { 181 node.data = ''; 182 } 183 else { 184 nodesToRemove.push(node); 185 index--; 186 } 187 partIndex++; 188 } 189 else { 190 let i = -1; 191 while ((i = node.data.indexOf(marker, i + 1)) !== -1) { 192 // Comment node has a binding marker inside, make an inactive part 193 // The binding won't work, but subsequent bindings will 194 // TODO (justinfagnani): consider whether it's even worth it to 195 // make bindings in comments work 196 this.parts.push({ type: 'node', index: -1 }); 197 partIndex++; 198 } 199 } 200 } 201 } 202 // Remove text binding nodes after the walk to not disturb the TreeWalker 203 for (const n of nodesToRemove) { 204 n.parentNode.removeChild(n); 205 } 206 } 207}
208const endsWith = (str, suffix) => { 209 const index = str.length - suffix.length; 210 return index >= 0 && str.slice(index) === suffix; 211}; 212const isTemplatePartActive = (part) => part.index !== -1; 213// Allows `document.createComment('')` to be renamed for a 214// small manual size-savings. 215const createMarker = () => document.createComment(''); 216/** 217 * This regex extracts the attribute name preceding an attribute-position 218 * expression. It does this by matching the syntax allowed for attributes 219 * against the string literal directly preceding the expression, assuming that 220 * the expression is in an attribute-value position. 221 * 222 * See attributes in the HTML spec: 223 * https://www.w3.org/TR/html5/syntax.html#elements-attributes 224 * 225 * " \x09\x0a\x0c\x0d" are HTML space characters: 226 * https://www.w3.org/TR/html5/infrastructure.html#space-characters 227 * 228 * "\0-\x1F\x7F-\x9F" are Unicode control characters, which includes every 229 * space character except " ". 230 * 231 * So an attribute is: 232 * * The name: any character except a control character, space character, ('), 233 * ("), ">", "=", or "/" 234 * * Followed by zero or more space characters 235 * * Followed by "=" 236 * * Followed by zero or more space characters 237 * * Followed by: 238 * * Any character except space, ('), ("), "<", ">", "=", (`), or 239 * * (") then any non-("), or 240 * * (') then any non-(') 241 */ 242const lastAttributeNameRegex = 243// eslint-disable-next-line no-control-regex 244/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;/** 245 * @license 246 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 247 * This code may only be used under the BSD style license found at 248 * http://polymer.github.io/LICENSE.txt 249 * The complete set of authors may be found at 250 * http://polymer.github.io/AUTHORS.txt 251 * The complete set of contributors may be found at 252 * http://polymer.github.io/CONTRIBUTORS.txt 253 * Code distributed by Google as part of the polymer project is also 254 * subject to an additional IP rights grant found at 255 * http://polymer.github.io/PATENTS.txt 256 */ 257const walkerNodeFilter = 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */; 258/** 259 * Removes the list of nodes from a Template safely. In addition to removing 260 * nodes from the Template, the Template part indices are updated to match 261 * the mutated Template DOM. 262 * 263 * As the template is walked the removal state is tracked and 264 * part indices are adjusted as needed. 265 * 266 * div 267 * div#1 (remove) <-- start removing (removing node is div#1) 268 * div 269 * div#2 (remove) <-- continue removing (removing node is still div#1) 270 * div 271 * div <-- stop removing since previous sibling is the removing node (div#1, 272 * removed 4 nodes) 273 */ 274function removeNodesFromTemplate(template, nodesToRemove) { 275 const { element: { content }, parts } = template; 276 const walker = document.createTreeWalker(content, walkerNodeFilter, null, false); 277 let partIndex = nextActiveIndexInTemplateParts(parts); 278 let part = parts[partIndex]; 279 let nodeIndex = -1; 280 let removeCount = 0; 281 const nodesToRemoveInTemplate = []; 282 let currentRemovingNode = null; 283 while (walker.nextNode()) { 284 nodeIndex++; 285 const node = walker.currentNode; 286 // End removal if stepped past the removing node 287 if (node.previousSibling === currentRemovingNode) { 288 currentRemovingNode = null; 289 } 290 // A node to remove was found in the template 291 if (nodesToRemove.has(node)) { 292 nodesToRemoveInTemplate.push(node); 293 // Track node we're removing 294 if (currentRemovingNode === null) { 295 currentRemovingNode = node; 296 } 297 } 298 // When removing, increment count by which to adjust subsequent part indices 299 if (currentRemovingNode !== null) { 300 removeCount++; 301 } 302 while (part !== undefined && part.index === nodeIndex) { 303 // If part is in a removed node deactivate it by setting index to -1 or 304 // adjust the index as needed. 305 part.index = currentRemovingNode !== null ? -1 : part.index - removeCount; 306 // go to the next active part. 307 partIndex = nextActiveIndexInTemplateParts(parts, partIndex); 308 part = parts[partIndex]; 309 } 310 } 311 nodesToRemoveInTemplate.forEach((n) => n.parentNode.removeChild(n)); 312} 313const countNodes = (node) => {
314 let count = (node.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */) ? 0 : 1; 315 const walker = document.createTreeWalker(node, walkerNodeFilter, null, false); 316 while (walker.nextNode()) { 317 count++; 318 } 319 return count; 320}; 321const nextActiveIndexInTemplateParts = (parts, startIndex = -1) => { 322 for (let i = startIndex + 1; i < parts.length; i++) { 323 const part = parts[i]; 324 if (isTemplatePartActive(part)) { 325 return i; 326 } 327 } 328 return -1; 329}; 330/** 331 * Inserts the given node into the Template, optionally before the given 332 * refNode. In addition to inserting the node into the Template, the Template 333 * part indices are updated to match the mutated Template DOM. 334 */ 335function insertNodeIntoTemplate(template, node, refNode = null) { 336 const { element: { content }, parts } = template; 337 // If there's no refNode, then put node at end of template. 338 // No part indices need to be shifted in this case. 339 if (refNode === null || refNode === undefined) { 340 content.appendChild(node); 341 return; 342 } 343 const walker = document.createTreeWalker(content, walkerNodeFilter, null, false); 344 let partIndex = nextActiveIndexInTemplateParts(parts); 345 let insertCount = 0; 346 let walkerIndex = -1; 347 while (walker.nextNode()) { 348 walkerIndex++; 349 const walkerNode = walker.currentNode; 350 if (walkerNode === refNode) { 351 insertCount = countNodes(node); 352 refNode.parentNode.insertBefore(node, refNode); 353 } 354 while (partIndex !== -1 && parts[partIndex].index === walkerIndex) { 355 // If we've inserted the node, simply adjust all subsequent parts 356 if (insertCount > 0) { 357 while (partIndex !== -1) { 358 parts[partIndex].index += insertCount; 359 partIndex = nextActiveIndexInTemplateParts(parts, partIndex); 360 } 361 return; 362 } 363 partIndex = nextActiveIndexInTemplateParts(parts, partIndex); 364 } 365 } 366}/** 367 * @license 368 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 369 * This code may only be used under the BSD style license found at 370 * http://polymer.github.io/LICENSE.txt 371 * The complete set of authors may be found at 372 * http://polymer.github.io/AUTHORS.txt 373 * The complete set of contributors may be found at 374 * http://polymer.github.io/CONTRIBUTORS.txt 375 * Code distributed by Google as part of the polymer project is also 376 * subject to an additional IP rights grant found at 377 * http://polymer.github.io/PATENTS.txt 378 */ 379const directives = new WeakMap(); 380const isDirective = (o) => { 381 return typeof o === 'function' && directives.has(o); 382};/** 383 * @license 384 * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. 385 * This code may only be used under the BSD style license found at 386 * http://polymer.github.io/LICENSE.txt 387 * The complete set of authors may be found at 388 * http://polymer.github.io/AUTHORS.txt 389 * The complete set of contributors may be found at 390 * http://polymer.github.io/CONTRIBUTORS.txt 391 * Code distributed by Google as part of the polymer project is also 392 * subject to an additional IP rights grant found at 393 * http://polymer.github.io/PATENTS.txt 394 */ 395/** 396 * A sentinel value that signals that a value was handled by a directive and 397 * should not be written to the DOM. 398 */ 399const noChange = {}; 400/** 401 * A sentinel value that signals a NodePart to fully clear its content. 402 */ 403const nothing = {};/** 404 * @license 405 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 406 * This code may only be used under the BSD style license found at 407 * http://polymer.github.io/LICENSE.txt 408 * The complete set of authors may be found at 409 * http://polymer.github.io/AUTHORS.txt 410 * The complete set of contributors may be found at 411 * http://polymer.github.io/CONTRIBUTORS.txt 412 * Code distributed by Google as part of the polymer project is also 413 * subject to an additional IP rights grant found at 414 * http://polymer.github.io/PATENTS.txt 415 */ 416/** 417 * An instance of a `Template` that can be attached to the DOM and updated
418 * with new values. 419 */ 420class TemplateInstance { 421 constructor(template, processor, options) { 422 this.__parts = []; 423 this.template = template; 424 this.processor = processor; 425 this.options = options; 426 } 427 update(values) { 428 let i = 0; 429 for (const part of this.__parts) { 430 if (part !== undefined) { 431 part.setValue(values[i]); 432 } 433 i++; 434 } 435 for (const part of this.__parts) { 436 if (part !== undefined) { 437 part.commit(); 438 } 439 } 440 } 441 _clone() { 442 // There are a number of steps in the lifecycle of a template instance's 443 // DOM fragment: 444 // 1. Clone - create the instance fragment 445 // 2. Adopt - adopt into the main document 446 // 3. Process - find part markers and create parts 447 // 4. Upgrade - upgrade custom elements 448 // 5. Update - set node, attribute, property, etc., values 449 // 6. Connect - connect to the document. Optional and outside of this 450 // method. 451 // 452 // We have a few constraints on the ordering of these steps: 453 // * We need to upgrade before updating, so that property values will pass 454 // through any property setters. 455 // * We would like to process before upgrading so that we're sure that the 456 // cloned fragment is inert and not disturbed by self-modifying DOM. 457 // * We want custom elements to upgrade even in disconnected fragments. 458 // 459 // Given these constraints, with full custom elements support we would 460 // prefer the order: Clone, Process, Adopt, Upgrade, Update, Connect 461 // 462 // But Safari does not implement CustomElementRegistry#upgrade, so we 463 // can not implement that order and still have upgrade-before-update and 464 // upgrade disconnected fragments. So we instead sacrifice the 465 // process-before-upgrade constraint, since in Custom Elements v1 elements 466 // must not modify their light DOM in the constructor. We still have issues 467 // when co-existing with CEv0 elements like Polymer 1, and with polyfills 468 // that don't strictly adhere to the no-modification rule because shadow 469 // DOM, which may be created in the constructor, is emulated by being placed 470 // in the light DOM. 471 // 472 // The resulting order is on native is: Clone, Adopt, Upgrade, Process, 473 // Update, Connect. document.importNode() performs Clone, Adopt, and Upgrade 474 // in one step. 475 // 476 // The Custom Elements v1 polyfill supports upgrade(), so the order when 477 // polyfilled is the more ideal: Clone, Process, Adopt, Upgrade, Update, 478 // Connect. 479 const fragment = isCEPolyfill ? 480 this.template.element.content.cloneNode(true) : 481 document.importNode(this.template.element.content, true); 482 const stack = []; 483 const parts = this.template.parts; 484 // Edge needs all 4 parameters present; IE11 needs 3rd parameter to be null 485 const walker = document.createTreeWalker(fragment, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false); 486 let partIndex = 0; 487 let nodeIndex = 0; 488 let part; 489 let node = walker.nextNode(); 490 // Loop through all the nodes and parts of a template 491 while (partIndex < parts.length) { 492 part = parts[partIndex]; 493 if (!isTemplatePartActive(part)) { 494 this.__parts.push(undefined); 495 partIndex++; 496 continue; 497 } 498 // Progress the tree walker until we find our next part's node. 499 // Note that multiple parts may share the same node (attribute parts 500 // on a single element), so this loop may not run at all. 501 while (nodeIndex < part.index) { 502 nodeIndex++; 503 if (node.nodeName === 'TEMPLATE') { 504 stack.push(node); 505 walker.currentNode = node.content; 506 } 507 if ((node = walker.nextNode()) === null) { 508 // We've exhausted the content inside a nested template element. 509 // Because we still have parts (the outer for-loop), we know: 510 // - There is a template in the stack 511 // - The walker will find a nextNode outside the template 512 walker.currentNode = stack.pop(); 513 node = walker.nextNode(); 514 } 515 } 516 // We've arrived at our part's node. 517 if (part.type === 'node') { 518 const part = this.processor.handleTextExpression(this.options); 519 part.insertAfterNode(node.previousSibling); 520 this.__parts.push(part); 521 } 522 else { 523 this.__parts.push(...this.processor.handleAttributeExpressions(node, part.name, part.strings, this.options)); 524 } 525 partIndex++; 526 } 527 if (isCEPolyfill) { 528 document.adoptNode(fragment); 529 customElements.upgrade(fragment); 530 } 531 return fragment; 532 } 533}/** 534 * @license 535 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 536 * This code may only be used under the BSD style license found at 537 * http://polymer.github.io/LICENSE.txt 538 * The complete set of authors may be found at 539 * http://polymer.github.io/AUTHORS.txt 540 * The complete set of contributors may be found at 541 * http://polymer.github.io/CONTRIBUTORS.txt 542 * Code distributed by Google as part of the polymer project is also 543 * subject to an additional IP rights grant found at 544 * http://polymer.github.io/PATENTS.txt 545 */ 546/** 547 * @module lit-html 548 */ 549/** 550 * Our TrustedTypePolicy for HTML which is declared using the html template 551 * tag function. 552 * 553 * That HTML is a developer-authored constant, and is parsed with innerHTML 554 * before any untrusted expressions have been mixed in. Therefor it is 555 * considered safe by construction. 556 */ 557const policy = window.trustedTypes && 558 trustedTypes.createPolicy('lit-html', { createHTML: (s) => s }); 559const commentMarker = ` ${marker} `; 560/** 561 * The return type of `html`, which holds a Template and the values from 562 * interpolated expressions. 563 */ 564class TemplateResult { 565 constructor(strings, values, type, processor) { 566 this.strings = strings; 567 this.values = values; 568 this.type = type; 569 this.processor = processor; 570 } 571 /** 572 * Returns a string of HTML used to create a `<template>` element. 573 */ 574 getHTML() { 575 const l = this.strings.length - 1; 576 let html = ''; 577 let isCommentBinding = false;
578 for (let i = 0; i < l; i++) { 579 const s = this.strings[i]; 580 // For each binding we want to determine the kind of marker to insert 581 // into the template source before it's parsed by the browser's HTML 582 // parser. The marker type is based on whether the expression is in an 583 // attribute, text, or comment position. 584 // * For node-position bindings we insert a comment with the marker 585 // sentinel as its text content, like <!--{{lit-guid}}-->. 586 // * For attribute bindings we insert just the marker sentinel for the 587 // first binding, so that we support unquoted attribute bindings. 588 // Subsequent bindings can use a comment marker because multi-binding 589 // attributes must be quoted. 590 // * For comment bindings we insert just the marker sentinel so we don't 591 // close the comment. 592 // 593 // The following code scans the template source, but is *not* an HTML 594 // parser. We don't need to track the tree structure of the HTML, only 595 // whether a binding is inside a comment, and if not, if it appears to be 596 // the first binding in an attribute. 597 const commentOpen = s.lastIndexOf('<!--'); 598 // We're in comment position if we have a comment open with no following 599 // comment close. Because <-- can appear in an attribute value there can 600 // be false positives. 601 isCommentBinding = (commentOpen > -1 || isCommentBinding) && 602 s.indexOf('-->', commentOpen + 1) === -1; 603 // Check to see if we have an attribute-like sequence preceding the 604 // expression. This can match "name=value" like structures in text, 605 // comments, and attribute values, so there can be false-positives. 606 const attributeMatch = lastAttributeNameRegex.exec(s); 607 if (attributeMatch === null) { 608 // We're only in this branch if we don't have a attribute-like 609 // preceding sequence. For comments, this guards against unusual 610 // attribute values like <div foo="<!--${'bar'}">. Cases like 611 // <!-- foo=${'bar'}--> are handled correctly in the attribute branch 612 // below. 613 html += s + (isCommentBinding ? commentMarker : nodeMarker); 614 } 615 else { 616 // For attributes we use just a marker sentinel, and also append a 617 // $lit$ suffix to the name to opt-out of attribute-specific parsing 618 // that IE and Edge do for style and certain SVG attributes. 619 html += s.substr(0, attributeMatch.index) + attributeMatch[1] + 620 attributeMatch[2] + boundAttributeSuffix + attributeMatch[3] + 621 marker; 622 } 623 } 624 html += this.strings[l]; 625 return html; 626 } 627 getTemplateElement() { 628 const template = document.createElement('template'); 629 let value = this.getHTML(); 630 if (policy !== undefined) { 631 // this is secure because `this.strings` is a TemplateStringsArray. 632 // TODO: validate this when 633 // https://github.com/tc39/proposal-array-is-template-object is 634 // implemented. 635 value = policy.createHTML(value); 636 } 637 template.innerHTML = value; 638 return template; 639 } 640}/** 641 * @license 642 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 643 * This code may only be used under the BSD style license found at 644 * http://polymer.github.io/LICENSE.txt 645 * The complete set of authors may be found at 646 * http://polymer.github.io/AUTHORS.txt 647 * The complete set of contributors may be found at 648 * http://polymer.github.io/CONTRIBUTORS.txt 649 * Code distributed by Google as part of the polymer project is also 650 * subject to an additional IP rights grant found at 651 * http://polymer.github.io/PATENTS.txt 652 */ 653const isPrimitive = (value) => { 654 return (value === null || 655 !(typeof value === 'object' || typeof value === 'function')); 656}; 657const isIterable = (value) => { 658 return Array.isArray(value) || 659 // eslint-disable-next-line @typescript-eslint/no-explicit-any 660 !!(value && value[Symbol.iterator]); 661}; 662/** 663 * Writes attribute values to the DOM for a group of AttributeParts bound to a 664 * single attribute. The value is only set once even if there are multiple parts 665 * for an attribute. 666 */ 667class AttributeCommitter { 668 constructor(element, name, strings) { 669 this.dirty = true; 670 this.element = element; 671 this.name = name; 672 this.strings = strings; 673 this.parts = []; 674 for (let i = 0; i < strings.length - 1; i++) { 675 this.parts[i] = this._createPart(); 676 } 677 } 678 /** 679 * Creates a single part. Override this to create a differnt type of part. 680 */ 681 _createPart() { 682 return new AttributePart(this); 683 } 684 _getValue() { 685 const strings = this.strings; 686 const l = strings.length - 1; 687 const parts = this.parts; 688 // If we're assigning an attribute via syntax like: 689 // attr="${foo}" or attr=${foo} 690 // but not 691 // attr="${foo} ${bar}" or attr="${foo} baz" 692 // then we don't want to coerce the attribute value into one long 693 // string. Instead we want to just return the value itself directly, 694 // so that sanitizeDOMValue can get the actual value rather than 695 // String(value) 696 // The exception is if v is an array, in which case we do want to smash 697 // it together into a string without calling String() on the array. 698 //
699 // This also allows trusted values (when using TrustedTypes) being 700 // assigned to DOM sinks without being stringified in the process. 701 if (l === 1 && strings[0] === '' && strings[1] === '') { 702 const v = parts[0].value; 703 if (typeof v === 'symbol') { 704 return String(v); 705 } 706 if (typeof v === 'string' || !isIterable(v)) { 707 return v; 708 } 709 } 710 let text = ''; 711 for (let i = 0; i < l; i++) { 712 text += strings[i]; 713 const part = parts[i]; 714 if (part !== undefined) { 715 const v = part.value; 716 if (isPrimitive(v) || !isIterable(v)) { 717 text += typeof v === 'string' ? v : String(v); 718 } 719 else { 720 for (const t of v) { 721 text += typeof t === 'string' ? t : String(t); 722 } 723 } 724 } 725 } 726 text += strings[l]; 727 return text; 728 } 729 commit() { 730 if (this.dirty) { 731 this.dirty = false; 732 this.element.setAttribute(this.name, this._getValue()); 733 } 734 } 735} 736/** 737 * A Part that controls all or part of an attribute value. 738 */ 739class AttributePart { 740 constructor(committer) { 741 this.value = undefined; 742 this.committer = committer; 743 } 744 setValue(value) { 745 if (value !== noChange && (!isPrimitive(value) || value !== this.value)) { 746 this.value = value; 747 // If the value is a not a directive, dirty the committer so that it'll 748 // call setAttribute. If the value is a directive, it'll dirty the 749 // committer if it calls setValue(). 750 if (!isDirective(value)) { 751 this.committer.dirty = true; 752 } 753 } 754 } 755 commit() { 756 while (isDirective(this.value)) { 757 const directive = this.value; 758 this.value = noChange; 759 directive(this); 760 } 761 if (this.value === noChange) { 762 return; 763 } 764 this.committer.commit(); 765 } 766} 767/** 768 * A Part that controls a location within a Node tree. Like a Range, NodePart 769 * has start and end locations and can set and update the Nodes between those 770 * locations. 771 * 772 * NodeParts support several value types: primitives, Nodes, TemplateResults, 773 * as well as arrays and iterables of those types. 774 */ 775class NodePart { 776 constructor(options) { 777 this.value = undefined; 778 this.__pendingValue = undefined; 779 this.options = options; 780 } 781 /** 782 * Appends this part into a container. 783 * 784 * This part must be empty, as its contents are not automatically moved. 785 */ 786 appendInto(container) { 787 this.startNode = container.appendChild(createMarker()); 788 this.endNode = container.appendChild(createMarker()); 789 } 790 /** 791 * Inserts this part after the `ref` node (between `ref` and `ref`'s next 792 * sibling). Both `ref` and its next sibling must be static, unchanging nodes 793 * such as those that appear in a literal section of a template. 794 * 795 * This part must be empty, as its contents are not automatically moved. 796 */ 797 insertAfterNode(ref) { 798 this.startNode = ref; 799 this.endNode = ref.nextSibling; 800 } 801 /** 802 * Appends this part into a parent part. 803 * 804 * This part must be empty, as its contents are not automatically moved. 805 */ 806 appendIntoPart(part) { 807 part.__insert(this.startNode = createMarker()); 808 part.__insert(this.endNode = createMarker()); 809 } 810 /** 811 * Inserts this part after the `ref` part. 812 * 813 * This part must be empty, as its contents are not automatically moved. 814 */ 815 insertAfterPart(ref) { 816 ref.__insert(this.startNode = createMarker()); 817 this.endNode = ref.endNode; 818 ref.endNode = this.startNode;
vendor: 7,378 bytes, lines 819-1022
819 } 820 setValue(value) { 821 this.__pendingValue = value; 822 } 823 commit() { 824 if (this.startNode.parentNode === null) { 825 return; 826 } 827 while (isDirective(this.__pendingValue)) { 828 const directive = this.__pendingValue; 829 this.__pendingValue = noChange; 830 directive(this); 831 } 832 const value = this.__pendingValue; 833 if (value === noChange) { 834 return; 835 } 836 if (isPrimitive(value)) { 837 if (value !== this.value) { 838 this.__commitText(value); 839 } 840 } 841 else if (value instanceof TemplateResult) { 842 this.__commitTemplateResult(value); 843 } 844 else if (value instanceof Node) { 845 this.__commitNode(value); 846 } 847 else if (isIterable(value)) { 848 this.__commitIterable(value); 849 } 850 else if (value === nothing) { 851 this.value = nothing; 852 this.clear(); 853 } 854 else { 855 // Fallback, will render the string representation 856 this.__commitText(value); 857 } 858 } 859 __insert(node) { 860 this.endNode.parentNode.insertBefore(node, this.endNode); 861 } 862 __commitNode(value) { 863 if (this.value === value) { 864 return; 865 } 866 this.clear(); 867 this.__insert(value); 868 this.value = value; 869 } 870 __commitText(value) { 871 const node = this.startNode.nextSibling; 872 value = value == null ? '' : value; 873 // If `value` isn't already a string, we explicitly convert it here in case 874 // it can't be implicitly converted - i.e. it's a symbol. 875 const valueAsString = typeof value === 'string' ? value : String(value); 876 if (node === this.endNode.previousSibling && 877 node.nodeType === 3 /* Node.TEXT_NODE */) { 878 // If we only have a single text node between the markers, we can just 879 // set its value, rather than replacing it. 880 // TODO(justinfagnani): Can we just check if this.value is primitive? 881 node.data = valueAsString; 882 } 883 else { 884 this.__commitNode(document.createTextNode(valueAsString)); 885 } 886 this.value = value; 887 } 888 __commitTemplateResult(value) { 889 const template = this.options.templateFactory(value); 890 if (this.value instanceof TemplateInstance && 891 this.value.template === template) { 892 this.value.update(value.values); 893 } 894 else { 895 // Make sure we propagate the template processor from the TemplateResult 896 // so that we use its syntax extension, etc. The template factory comes 897 // from the render function options so that it can control template 898 // caching and preprocessing. 899 const instance = new TemplateInstance(template, value.processor, this.options); 900 const fragment = instance._clone(); 901 instance.update(value.values); 902 this.__commitNode(fragment); 903 this.value = instance; 904 } 905 } 906 __commitIterable(value) { 907 // For an Iterable, we create a new InstancePart per item, then set its 908 // value to the item. This is a little bit of overhead for every item in 909 // an Iterable, but it lets us recurse easily and efficiently update Arrays 910 // of TemplateResults that will be commonly returned from expressions like: 911 // array.map((i) => html`${i}`), by reusing existing TemplateInstances. 912 // If _value is an array, then the previous render was of an 913 // iterable and _value will contain the NodeParts from the previous 914 // render. If _value is not an array, clear this part and make a new 915 // array for NodeParts. 916 if (!Array.isArray(this.value)) { 917 this.value = []; 918 this.clear(); 919 } 920 // Lets us keep track of how many items we stamped so we can clear leftover 921 // items from a previous render 922 const itemParts = this.value; 923 let partIndex = 0; 924 let itemPart; 925 for (const item of value) { 926 // Try to reuse an existing part 927 itemPart = itemParts[partIndex]; 928 // If no existing part, create a new one 929 if (itemPart === undefined) { 930 itemPart = new NodePart(this.options); 931 itemParts.push(itemPart); 932 if (partIndex === 0) { 933 itemPart.appendIntoPart(this); 934 } 935 else { 936 itemPart.insertAfterPart(itemParts[partIndex - 1]); 937 } 938 } 939 itemPart.setValue(item); 940 itemPart.commit(); 941 partIndex++; 942 } 943 if (partIndex < itemParts.length) { 944 // Truncate the parts array so _value reflects the current state 945 itemParts.length = partIndex; 946 this.clear(itemPart && itemPart.endNode); 947 } 948 } 949 clear(startNode = this.startNode) { 950 removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode); 951 } 952} 953/** 954 * Implements a boolean attribute, roughly as defined in the HTML 955 * specification. 956 * 957 * If the value is truthy, then the attribute is present with a value of 958 * ''. If the value is falsey, the attribute is removed. 959 */ 960class BooleanAttributePart { 961 constructor(element, name, strings) { 962 this.value = undefined; 963 this.__pendingValue = undefined; 964 if (strings.length !== 2 || strings[0] !== '' || strings[1] !== '') { 965 throw new Error('Boolean attributes can only contain a single expression'); 966 } 967 this.element = element; 968 this.name = name; 969 this.strings = strings; 970 } 971 setValue(value) { 972 this.__pendingValue = value; 973 } 974 commit() { 975 while (isDirective(this.__pendingValue)) { 976 const directive = this.__pendingValue; 977 this.__pendingValue = noChange; 978 directive(this); 979 } 980 if (this.__pendingValue === noChange) { 981 return; 982 } 983 const value = !!this.__pendingValue; 984 if (this.value !== value) { 985 if (value) { 986 this.element.setAttribute(this.name, ''); 987 } 988 else { 989 this.element.removeAttribute(this.name); 990 } 991 this.value = value; 992 } 993 this.__pendingValue = noChange; 994 } 995} 996/** 997 * Sets attribute values for PropertyParts, so that the value is only set once 998 * even if there are multiple parts for a property. 999 * 1000 * If an expression controls the whole property value, then the value is simply 1001 * assigned to the property under control. If there are string literals or 1002 * multiple expressions, then the strings are expressions are interpolated into 1003 * a string first. 1004 */ 1005class PropertyCommitter extends AttributeCommitter { 1006 constructor(element, name, strings) { 1007 super(element, name, strings); 1008 this.single = 1009 (strings.length === 2 && strings[0] === '' && strings[1] === ''); 1010 } 1011 _createPart() { 1012 return new PropertyPart(this); 1013 } 1014 _getValue() { 1015 if (this.single) { 1016 return this.parts[0].value; 1017 } 1018 return super._getValue(); 1019 } 1020 commit() { 1021 if (this.dirty) { 1022 this.dirty = false;
1023 // eslint-disable-next-line @typescript-eslint/no-explicit-any 1024 this.element[this.name] = this._getValue(); 1025 } 1026 } 1027} 1028class PropertyPart extends AttributePart { 1029} 1030// Detect event listener options support. If the `capture` property is read 1031// from the options object, then options are supported. If not, then the third 1032// argument to add/removeEventListener is interpreted as the boolean capture 1033// value so we should only pass the `capture` property. 1034let eventOptionsSupported = false; 1035// Wrap into an IIFE because MS Edge <= v41 does not support having try/catch 1036// blocks right into the body of a module 1037(() => { 1038 try { 1039 const options = { 1040 get capture() { 1041 eventOptionsSupported = true; 1042 return false; 1043 } 1044 }; 1045 // eslint-disable-next-line @typescript-eslint/no-explicit-any 1046 window.addEventListener('test', options, options); 1047 // eslint-disable-next-line @typescript-eslint/no-explicit-any 1048 window.removeEventListener('test', options, options); 1049 } 1050 catch (_e) { 1051 // event options not supported 1052 } 1053})(); 1054class EventPart { 1055 constructor(element, eventName, eventContext) { 1056 this.value = undefined; 1057 this.__pendingValue = undefined; 1058 this.element = element; 1059 this.eventName = eventName; 1060 this.eventContext = eventContext; 1061 this.__boundHandleEvent = (e) => this.handleEvent(e); 1062 } 1063 setValue(value) { 1064 this.__pendingValue = value; 1065 } 1066 commit() { 1067 while (isDirective(this.__pendingValue)) { 1068 const directive = this.__pendingValue; 1069 this.__pendingValue = noChange; 1070 directive(this); 1071 } 1072 if (this.__pendingValue === noChange) { 1073 return; 1074 } 1075 const newListener = this.__pendingValue; 1076 const oldListener = this.value; 1077 const shouldRemoveListener = newListener == null || 1078 oldListener != null && 1079 (newListener.capture !== oldListener.capture || 1080 newListener.once !== oldListener.once || 1081 newListener.passive !== oldListener.passive); 1082 const shouldAddListener = newListener != null && (oldListener == null || shouldRemoveListener); 1083 if (shouldRemoveListener) { 1084 this.element.removeEventListener(this.eventName, this.__boundHandleEvent, this.__options); 1085 } 1086 if (shouldAddListener) { 1087 this.__options = getOptions(newListener); 1088 this.element.addEventListener(this.eventName, this.__boundHandleEvent, this.__options); 1089 } 1090 this.value = newListener; 1091 this.__pendingValue = noChange; 1092 } 1093 handleEvent(event) { 1094 if (typeof this.value === 'function') { 1095 this.value.call(this.eventContext || this.element, event); 1096 } 1097 else { 1098 this.value.handleEvent(event); 1099 } 1100 } 1101} 1102// We copy options because of the inconsistent behavior of browsers when reading 1103// the third argument of add/removeEventListener. IE11 doesn't support options 1104// at all. Chrome 41 only reads `capture` if the argument is an object. 1105const getOptions = (o) => o && 1106 (eventOptionsSupported ? 1107 { capture: o.capture, passive: o.passive, once: o.once } : 1108 o.capture);/** 1109 * @license 1110 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1111 * This code may only be used under the BSD style license found at 1112 * http://polymer.github.io/LICENSE.txt 1113 * The complete set of authors may be found at 1114 * http://polymer.github.io/AUTHORS.txt 1115 * The complete set of contributors may be found at 1116 * http://polymer.github.io/CONTRIBUTORS.txt 1117 * Code distributed by Google as part of the polymer project is also 1118 * subject to an additional IP rights grant found at 1119 * http://polymer.github.io/PATENTS.txt 1120 */ 1121/** 1122 * The default TemplateFactory which caches Templates keyed on 1123 * result.type and result.strings. 1124 */ 1125function templateFactory(result) { 1126 let templateCache = templateCaches.get(result.type); 1127 if (templateCache === undefined) { 1128 templateCache = {
1129 stringsArray: new WeakMap(), 1130 keyString: new Map() 1131 }; 1132 templateCaches.set(result.type, templateCache); 1133 } 1134 let template = templateCache.stringsArray.get(result.strings); 1135 if (template !== undefined) { 1136 return template; 1137 } 1138 // If the TemplateStringsArray is new, generate a key from the strings 1139 // This key is shared between all templates with identical content 1140 const key = result.strings.join(marker); 1141 // Check if we already have a Template for this key 1142 template = templateCache.keyString.get(key); 1143 if (template === undefined) { 1144 // If we have not seen this key before, create a new Template 1145 template = new Template(result, result.getTemplateElement()); 1146 // Cache the Template for this key 1147 templateCache.keyString.set(key, template); 1148 } 1149 // Cache all future queries for this TemplateStringsArray 1150 templateCache.stringsArray.set(result.strings, template); 1151 return template; 1152} 1153const templateCaches = new Map();/** 1154 * @license 1155 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1156 * This code may only be used under the BSD style license found at 1157 * http://polymer.github.io/LICENSE.txt 1158 * The complete set of authors may be found at 1159 * http://polymer.github.io/AUTHORS.txt 1160 * The complete set of contributors may be found at 1161 * http://polymer.github.io/CONTRIBUTORS.txt 1162 * Code distributed by Google as part of the polymer project is also 1163 * subject to an additional IP rights grant found at 1164 * http://polymer.github.io/PATENTS.txt 1165 */ 1166const parts = new WeakMap(); 1167/** 1168 * Renders a template result or other value to a container. 1169 * 1170 * To update a container with new values, reevaluate the template literal and 1171 * call `render` with the new result. 1172 * 1173 * @param result Any value renderable by NodePart - typically a TemplateResult 1174 * created by evaluating a template tag like `html` or `svg`. 1175 * @param container A DOM parent to render to. The entire contents are either 1176 * replaced, or efficiently updated if the same result type was previous 1177 * rendered there. 1178 * @param options RenderOptions for the entire render tree rendered to this 1179 * container. Render options must *not* change between renders to the same 1180 * container, as those changes will not effect previously rendered DOM. 1181 */ 1182const render$1 = (result, container, options) => { 1183 let part = parts.get(container); 1184 if (part === undefined) { 1185 removeNodes(container, container.firstChild); 1186 parts.set(container, part = new NodePart(Object.assign({ templateFactory }, options))); 1187 part.appendInto(container); 1188 } 1189 part.setValue(result); 1190 part.commit(); 1191};/** 1192 * @license 1193 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1194 * This code may only be used under the BSD style license found at 1195 * http://polymer.github.io/LICENSE.txt 1196 * The complete set of authors may be found at 1197 * http://polymer.github.io/AUTHORS.txt 1198 * The complete set of contributors may be found at 1199 * http://polymer.github.io/CONTRIBUTORS.txt 1200 * Code distributed by Google as part of the polymer project is also 1201 * subject to an additional IP rights grant found at 1202 * http://polymer.github.io/PATENTS.txt 1203 */ 1204/** 1205 * Creates Parts when a template is instantiated. 1206 */ 1207class DefaultTemplateProcessor { 1208 /** 1209 * Create parts for an attribute-position binding, given the event, attribute 1210 * name, and string literals. 1211 * 1212 * @param element The element containing the binding 1213 * @param name The attribute name 1214 * @param strings The string literals. There are always at least two strings, 1215 * event for fully-controlled bindings with a single expression. 1216 */ 1217 handleAttributeExpressions(element, name, strings, options) { 1218 const prefix = name[0]; 1219 if (prefix === '.') { 1220 const committer = new PropertyCommitter(element, name.slice(1), strings); 1221 return committer.parts; 1222 } 1223 if (prefix === '@') { 1224 return [new EventPart(element, name.slice(1), options.eventContext)]; 1225 } 1226 if (prefix === '?') { 1227 return [new BooleanAttributePart(element, name.slice(1), strings)]; 1228 } 1229 const committer = new AttributeCommitter(element, name, strings); 1230 return committer.parts; 1231 } 1232 /** 1233 * Create parts for a text-position binding. 1234 * @param templateFactory 1235 */ 1236 handleTextExpression(options) { 1237 return new NodePart(options); 1238 } 1239} 1240const defaultTemplateProcessor = new DefaultTemplateProcessor();/** 1241 * @license 1242 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1243 * This code may only be used under the BSD style license found at 1244 * http://polymer.github.io/LICENSE.txt 1245 * The complete set of authors may be found at 1246 * http://polymer.github.io/AUTHORS.txt 1247 * The complete set of contributors may be found at 1248 * http://polymer.github.io/CONTRIBUTORS.txt 1249 * Code distributed by Google as part of the polymer project is also 1250 * subject to an additional IP rights grant found at 1251 * http://polymer.github.io/PATENTS.txt 1252 */ 1253/** 1254 * 1255 * Main lit-html module. 1256 * 1257 * Main exports: 1258 * 1259 * - [[html]] 1260 * - [[svg]] 1261 * - [[render]] 1262 * 1263 * @packageDocumentation 1264 */ 1265/** 1266 * Do not remove this comment; it keeps typedoc from misplacing the module 1267 * docs. 1268 */ 1269// IMPORTANT: do not change the property name or the assignment expression.
1270// This line will be used in regexes to search for lit-html usage. 1271// TODO(justinfagnani): inject version number at build time 1272if (typeof window !== 'undefined') { 1273 (window['litHtmlVersions'] || (window['litHtmlVersions'] = [])).push('1.4.1'); 1274} 1275/** 1276 * Interprets a template literal as an HTML template that can efficiently 1277 * render to and update a container. 1278 */ 1279const html = (strings, ...values) => new TemplateResult(strings, values, 'html', defaultTemplateProcessor);/** 1280 * @license 1281 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1282 * This code may only be used under the BSD style license found at 1283 * http://polymer.github.io/LICENSE.txt 1284 * The complete set of authors may be found at 1285 * http://polymer.github.io/AUTHORS.txt 1286 * The complete set of contributors may be found at 1287 * http://polymer.github.io/CONTRIBUTORS.txt 1288 * Code distributed by Google as part of the polymer project is also 1289 * subject to an additional IP rights grant found at 1290 * http://polymer.github.io/PATENTS.txt 1291 */ 1292/** 1293 * Module to add shady DOM/shady CSS polyfill support to lit-html template 1294 * rendering. See the [[render]] method for details. 1295 * 1296 * @packageDocumentation 1297 */ 1298/** 1299 * Do not remove this comment; it keeps typedoc from misplacing the module 1300 * docs. 1301 */ 1302// Get a key to lookup in `templateCaches`. 1303const getTemplateCacheKey = (type, scopeName) => `${type}--${scopeName}`; 1304let compatibleShadyCSSVersion = true; 1305if (typeof window.ShadyCSS === 'undefined') { 1306 compatibleShadyCSSVersion = false; 1307} 1308else if (typeof window.ShadyCSS.prepareTemplateDom === 'undefined') { 1309 console.warn(`Incompatible ShadyCSS version detected. ` + 1310 `Please update to at least @webcomponents/[email protected] and ` + 1311 `@webcomponents/[email protected].`); 1312 compatibleShadyCSSVersion = false; 1313} 1314/** 1315 * Template factory which scopes template DOM using ShadyCSS. 1316 * @param scopeName {string} 1317 */ 1318const shadyTemplateFactory = (scopeName) => (result) => { 1319 const cacheKey = getTemplateCacheKey(result.type, scopeName); 1320 let templateCache = templateCaches.get(cacheKey); 1321 if (templateCache === undefined) { 1322 templateCache = { 1323 stringsArray: new WeakMap(), 1324 keyString: new Map() 1325 }; 1326 templateCaches.set(cacheKey, templateCache); 1327 } 1328 let template = templateCache.stringsArray.get(result.strings); 1329 if (template !== undefined) { 1330 return template; 1331 } 1332 const key = result.strings.join(marker); 1333 template = templateCache.keyString.get(key); 1334 if (template === undefined) { 1335 const element = result.getTemplateElement(); 1336 if (compatibleShadyCSSVersion) { 1337 window.ShadyCSS.prepareTemplateDom(element, scopeName); 1338 } 1339 template = new Template(result, element); 1340 templateCache.keyString.set(key, template); 1341 } 1342 templateCache.stringsArray.set(result.strings, template); 1343 return template; 1344}; 1345const TEMPLATE_TYPES = ['html', 'svg']; 1346/** 1347 * Removes all style elements from Templates for the given scopeName. 1348 */ 1349const removeStylesFromLitTemplates = (scopeName) => { 1350 TEMPLATE_TYPES.forEach((type) => { 1351 const templates = templateCaches.get(getTemplateCacheKey(type, scopeName)); 1352 if (templates !== undefined) { 1353 templates.keyString.forEach((template) => { 1354 const { element: { content } } = template; 1355 // IE 11 doesn't support the iterable param Set constructor 1356 const styles = new Set(); 1357 Array.from(content.querySelectorAll('style')).forEach((s) => { 1358 styles.add(s); 1359 }); 1360 removeNodesFromTemplate(template, styles); 1361 }); 1362 } 1363 }); 1364}; 1365const shadyRenderSet = new Set(); 1366/** 1367 * For the given scope name, ensures that ShadyCSS style scoping is performed. 1368 * This is done just once per scope name so the fragment and template cannot 1369 * be modified. 1370 * (1) extracts styles from the rendered fragment and hands them to ShadyCSS 1371 * to be scoped and appended to the document 1372 * (2) removes style elements from all lit-html Templates for this scope name. 1373 *
1374 * Note, <style> elements can only be placed into templates for the 1375 * initial rendering of the scope. If <style> elements are included in templates 1376 * dynamically rendered to the scope (after the first scope render), they will 1377 * not be scoped and the <style> will be left in the template and rendered 1378 * output. 1379 */ 1380const prepareTemplateStyles = (scopeName, renderedDOM, template) => { 1381 shadyRenderSet.add(scopeName); 1382 // If `renderedDOM` is stamped from a Template, then we need to edit that 1383 // Template's underlying template element. Otherwise, we create one here 1384 // to give to ShadyCSS, which still requires one while scoping. 1385 const templateElement = !!template ? template.element : document.createElement('template'); 1386 // Move styles out of rendered DOM and store. 1387 const styles = renderedDOM.querySelectorAll('style'); 1388 const { length } = styles; 1389 // If there are no styles, skip unnecessary work 1390 if (length === 0) { 1391 // Ensure prepareTemplateStyles is called to support adding 1392 // styles via `prepareAdoptedCssText` since that requires that 1393 // `prepareTemplateStyles` is called. 1394 // 1395 // ShadyCSS will only update styles containing @apply in the template 1396 // given to `prepareTemplateStyles`. If no lit Template was given, 1397 // ShadyCSS will not be able to update uses of @apply in any relevant 1398 // template. However, this is not a problem because we only create the 1399 // template for the purpose of supporting `prepareAdoptedCssText`, 1400 // which doesn't support @apply at all. 1401 window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName); 1402 return; 1403 } 1404 const condensedStyle = document.createElement('style'); 1405 // Collect styles into a single style. This helps us make sure ShadyCSS 1406 // manipulations will not prevent us from being able to fix up template 1407 // part indices. 1408 // NOTE: collecting styles is inefficient for browsers but ShadyCSS 1409 // currently does this anyway. When it does not, this should be changed. 1410 for (let i = 0; i < length; i++) { 1411 const style = styles[i]; 1412 style.parentNode.removeChild(style); 1413 condensedStyle.textContent += style.textContent; 1414 } 1415 // Remove styles from nested templates in this scope. 1416 removeStylesFromLitTemplates(scopeName); 1417 // And then put the condensed style into the "root" template passed in as 1418 // `template`. 1419 const content = templateElement.content; 1420 if (!!template) { 1421 insertNodeIntoTemplate(template, condensedStyle, content.firstChild); 1422 } 1423 else { 1424 content.insertBefore(condensedStyle, content.firstChild); 1425 } 1426 // Note, it's important that ShadyCSS gets the template that `lit-html` 1427 // will actually render so that it can update the style inside when 1428 // needed (e.g. @apply native Shadow DOM case). 1429 window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName); 1430 const style = content.querySelector('style'); 1431 if (window.ShadyCSS.nativeShadow && style !== null) { 1432 // When in native Shadow DOM, ensure the style created by ShadyCSS is 1433 // included in initially rendered output (`renderedDOM`). 1434 renderedDOM.insertBefore(style.cloneNode(true), renderedDOM.firstChild); 1435 } 1436 else if (!!template) { 1437 // When no style is left in the template, parts will be broken as a 1438 // result. To fix this, we put back the style node ShadyCSS removed 1439 // and then tell lit to remove that node from the template. 1440 // There can be no style in the template in 2 cases (1) when Shady DOM 1441 // is in use, ShadyCSS removes all styles, (2) when native Shadow DOM 1442 // is in use ShadyCSS removes the style if it contains no content. 1443 // NOTE, ShadyCSS creates its own style so we can safely add/remove 1444 // `condensedStyle` here. 1445 content.insertBefore(condensedStyle, content.firstChild); 1446 const removes = new Set(); 1447 removes.add(condensedStyle); 1448 removeNodesFromTemplate(template, removes); 1449 } 1450}; 1451/** 1452 * Extension to the standard `render` method which supports rendering 1453 * to ShadowRoots when the ShadyDOM (https://github.com/webcomponents/shadydom)
1454 * and ShadyCSS (https://github.com/webcomponents/shadycss) polyfills are used 1455 * or when the webcomponentsjs 1456 * (https://github.com/webcomponents/webcomponentsjs) polyfill is used. 1457 * 1458 * Adds a `scopeName` option which is used to scope element DOM and stylesheets 1459 * when native ShadowDOM is unavailable. The `scopeName` will be added to 1460 * the class attribute of all rendered DOM. In addition, any style elements will 1461 * be automatically re-written with this `scopeName` selector and moved out 1462 * of the rendered DOM and into the document `<head>`. 1463 * 1464 * It is common to use this render method in conjunction with a custom element 1465 * which renders a shadowRoot. When this is done, typically the element's 1466 * `localName` should be used as the `scopeName`. 1467 * 1468 * In addition to DOM scoping, ShadyCSS also supports a basic shim for css 1469 * custom properties (needed only on older browsers like IE11) and a shim for 1470 * a deprecated feature called `@apply` that supports applying a set of css 1471 * custom properties to a given location. 1472 * 1473 * Usage considerations: 1474 * 1475 * * Part values in `<style>` elements are only applied the first time a given 1476 * `scopeName` renders. Subsequent changes to parts in style elements will have 1477 * no effect. Because of this, parts in style elements should only be used for 1478 * values that will never change, for example parts that set scope-wide theme 1479 * values or parts which render shared style elements. 1480 * 1481 * * Note, due to a limitation of the ShadyDOM polyfill, rendering in a 1482 * custom element's `constructor` is not supported. Instead rendering should 1483 * either done asynchronously, for example at microtask timing (for example 1484 * `Promise.resolve()`), or be deferred until the first time the element's 1485 * `connectedCallback` runs. 1486 * 1487 * Usage considerations when using shimmed custom properties or `@apply`: 1488 * 1489 * * Whenever any dynamic changes are made which affect 1490 * css custom properties, `ShadyCSS.styleElement(element)` must be called 1491 * to update the element. There are two cases when this is needed: 1492 * (1) the element is connected to a new parent, (2) a class is added to the 1493 * element that causes it to match different custom properties. 1494 * To address the first case when rendering a custom element, `styleElement` 1495 * should be called in the element's `connectedCallback`. 1496 * 1497 * * Shimmed custom properties may only be defined either for an entire 1498 * shadowRoot (for example, in a `:host` rule) or via a rule that directly 1499 * matches an element with a shadowRoot. In other words, instead of flowing from 1500 * parent to child as do native css custom properties, shimmed custom properties 1501 * flow only from shadowRoots to nested shadowRoots. 1502 * 1503 * * When using `@apply` mixing css shorthand property names with 1504 * non-shorthand names (for example `border` and `border-width`) is not 1505 * supported. 1506 */ 1507const render = (result, container, options) => { 1508 if (!options || typeof options !== 'object' || !options.scopeName) { 1509 throw new Error('The `scopeName` option is required.'); 1510 } 1511 const scopeName = options.scopeName; 1512 const hasRendered = parts.has(container); 1513 const needsScoping = compatibleShadyCSSVersion && 1514 container.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */ && 1515 !!container.host; 1516 // Handle first render to a scope specially... 1517 const firstScopeRender = needsScoping && !shadyRenderSet.has(scopeName); 1518 // On first scope render, render into a fragment; this cannot be a single 1519 // fragment that is reused since nested renders can occur synchronously. 1520 const renderContainer = firstScopeRender ? document.createDocumentFragment() : container; 1521 render$1(result, renderContainer, Object.assign({ templateFactory: shadyTemplateFactory(scopeName) }, options)); 1522 // When performing first scope render, 1523 // (1) We've rendered into a fragment so that there's a chance to 1524 // `prepareTemplateStyles` before sub-elements hit the DOM 1525 // (which might cause them to render based on a common pattern of 1526 // rendering in a custom element's `connectedCallback`); 1527 // (2) Scope the template with ShadyCSS one time only for this scope. 1528 // (3) Render the fragment into the container and make sure the 1529 // container knows its `part` is the one we just rendered. This ensures 1530 // DOM will be re-used on subsequent renders. 1531 if (firstScopeRender) { 1532 const part = parts.get(renderContainer); 1533 parts.delete(renderContainer); 1534 // ShadyCSS might have style sheets (e.g. from `prepareAdoptedCssText`) 1535 // that should apply to `renderContainer` even if the rendered value is 1536 // not a TemplateInstance. However, it will only insert scoped styles 1537 // into the document if `prepareTemplateStyles` has already been called 1538 // for the given scope name. 1539 const template = part.value instanceof TemplateInstance ? 1540 part.value.template : 1541 undefined; 1542 prepareTemplateStyles(scopeName, renderContainer, template); 1543 removeNodes(container, container.firstChild); 1544 container.appendChild(renderContainer); 1545 parts.set(container, part); 1546 } 1547 // After elements have hit the DOM, update styling if this is the 1548 // initial render to this container. 1549 // This is needed whenever dynamic changes are made so it would be 1550 // safest to do every render; however, this would regress performance 1551 // so we leave it up to the user to call `ShadyCSS.styleElement` 1552 // for dynamic changes. 1553 if (!hasRendered && needsScoping) { 1554 window.ShadyCSS.styleElement(container.host); 1555 } 1556};/** 1557 * @license 1558 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 1559 * This code may only be used under the BSD style license found at 1560 * http://polymer.github.io/LICENSE.txt 1561 * The complete set of authors may be found at 1562 * http://polymer.github.io/AUTHORS.txt 1563 * The complete set of contributors may be found at 1564 * http://polymer.github.io/CONTRIBUTORS.txt 1565 * Code distributed by Google as part of the polymer project is also 1566 * subject to an additional IP rights grant found at 1567 * http://polymer.github.io/PATENTS.txt 1568 */ 1569var _a; 1570/** 1571 * Use this module if you want to create your own base class extending 1572 * [[UpdatingElement]]. 1573 * @packageDocumentation 1574 */ 1575/* 1576 * When using Closure Compiler, JSCompiler_renameProperty(property, object) is 1577 * replaced at compile time by the munged name for object[property]. We cannot 1578 * alias this function, so we have to use a small shim that has the same 1579 * behavior when not compiling. 1580 */ 1581window.JSCompiler_renameProperty = 1582 (prop, _obj) => prop; 1583const defaultConverter = { 1584 toAttribute(value, type) { 1585 switch (type) { 1586 case Boolean: 1587 return value ? '' : null; 1588 case Object: 1589 case Array: 1590 // if the value is `null` or `undefined` pass this through 1591 // to allow removing/no change behavior. 1592 return value == null ? value : JSON.stringify(value); 1593 } 1594 return value; 1595 }, 1596 fromAttribute(value, type) { 1597 switch (type) { 1598 case Boolean: 1599 return value !== null; 1600 case Number: 1601 return value === null ? null : Number(value); 1602 case Object: 1603 case Array: 1604 // Type assert to adhere to Bazel's "must type assert JSON parse" rule. 1605 return JSON.parse(value); 1606 } 1607 return value; 1608 } 1609}; 1610/** 1611 * Change function that returns true if `value` is different from `oldValue`. 1612 * This method is used as the default for a property's `hasChanged` function. 1613 */ 1614const notEqual = (value, old) => { 1615 // This ensures (old==NaN, value==NaN) always returns false 1616 return old !== value && (old === old || value === value); 1617}; 1618const defaultPropertyDeclaration = { 1619 attribute: true, 1620 type: String, 1621 converter: defaultConverter, 1622 reflect: false, 1623 hasChanged: notEqual 1624}; 1625const STATE_HAS_UPDATED = 1; 1626const STATE_UPDATE_REQUESTED = 1 << 2; 1627const STATE_IS_REFLECTING_TO_ATTRIBUTE = 1 << 3; 1628const STATE_IS_REFLECTING_TO_PROPERTY = 1 << 4; 1629/** 1630 * The Closure JS Compiler doesn't currently have good support for static 1631 * property semantics where "this" is dynamic (e.g. 1632 * https://github.com/google/closure-compiler/issues/3177 and others) so we use 1633 * this hack to bypass any rewriting by the compiler. 1634 */ 1635const finalized = 'finalized'; 1636/** 1637 * Base element class which manages element properties and attributes. When 1638 * properties change, the `update` method is asynchronously called. This method 1639 * should be supplied by subclassers to render updates as desired. 1640 * @noInheritDoc 1641 */ 1642class UpdatingElement extends HTMLElement { 1643 constructor() { 1644 super(); 1645 this.initialize(); 1646 } 1647 /** 1648 * Returns a list of attributes corresponding to the registered properties. 1649 * @nocollapse 1650 */ 1651 static get observedAttributes() { 1652 // note: piggy backing on this to ensure we're finalized. 1653 this.finalize(); 1654 const attributes = []; 1655 // Use forEach so this works even if for/of loops are compiled to for loops 1656 // expecting arrays
1657 this._classProperties.forEach((v, p) => { 1658 const attr = this._attributeNameForProperty(p, v); 1659 if (attr !== undefined) { 1660 this._attributeToPropertyMap.set(attr, p); 1661 attributes.push(attr); 1662 } 1663 }); 1664 return attributes; 1665 } 1666 /** 1667 * Ensures the private `_classProperties` property metadata is created. 1668 * In addition to `finalize` this is also called in `createProperty` to 1669 * ensure the `@property` decorator can add property metadata. 1670 */ 1671 /** @nocollapse */ 1672 static _ensureClassProperties() { 1673 // ensure private storage for property declarations. 1674 if (!this.hasOwnProperty(JSCompiler_renameProperty('_classProperties', this))) { 1675 this._classProperties = new Map(); 1676 // NOTE: Workaround IE11 not supporting Map constructor argument. 1677 const superProperties = Object.getPrototypeOf(this)._classProperties; 1678 if (superProperties !== undefined) { 1679 superProperties.forEach((v, k) => this._classProperties.set(k, v)); 1680 } 1681 } 1682 } 1683 /** 1684 * Creates a property accessor on the element prototype if one does not exist 1685 * and stores a PropertyDeclaration for the property with the given options. 1686 * The property setter calls the property's `hasChanged` property option 1687 * or uses a strict identity check to determine whether or not to request 1688 * an update. 1689 * 1690 * This method may be overridden to customize properties; however, 1691 * when doing so, it's important to call `super.createProperty` to ensure 1692 * the property is setup correctly. This method calls 1693 * `getPropertyDescriptor` internally to get a descriptor to install. 1694 * To customize what properties do when they are get or set, override 1695 * `getPropertyDescriptor`. To customize the options for a property, 1696 * implement `createProperty` like this: 1697 * 1698 * static createProperty(name, options) { 1699 * options = Object.assign(options, {myOption: true}); 1700 * super.createProperty(name, options); 1701 * } 1702 * 1703 * @nocollapse 1704 */ 1705 static createProperty(name, options = defaultPropertyDeclaration) { 1706 // Note, since this can be called by the `@property` decorator which 1707 // is called before `finalize`, we ensure storage exists for property 1708 // metadata. 1709 this._ensureClassProperties(); 1710 this._classProperties.set(name, options); 1711 // Do not generate an accessor if the prototype already has one, since 1712 // it would be lost otherwise and that would never be the user's intention; 1713 // Instead, we expect users to call `requestUpdate` themselves from 1714 // user-defined accessors. Note that if the super has an accessor we will 1715 // still overwrite it 1716 if (options.noAccessor || this.prototype.hasOwnProperty(name)) { 1717 return; 1718 } 1719 const key = typeof name === 'symbol' ? Symbol() : `__${name}`; 1720 const descriptor = this.getPropertyDescriptor(name, key, options); 1721 if (descriptor !== undefined) { 1722 Object.defineProperty(this.prototype, name, descriptor); 1723 } 1724 } 1725 /** 1726 * Returns a property descriptor to be defined on the given named property. 1727 * If no descriptor is returned, the property will not become an accessor. 1728 * For example, 1729 * 1730 * class MyElement extends LitElement { 1731 * static getPropertyDescriptor(name, key, options) { 1732 * const defaultDescriptor = 1733 * super.getPropertyDescriptor(name, key, options); 1734 * const setter = defaultDescriptor.set; 1735 * return { 1736 * get: defaultDescriptor.get, 1737 * set(value) { 1738 * setter.call(this, value); 1739 * // custom action. 1740 * }, 1741 * configurable: true, 1742 * enumerable: true 1743 * } 1744 * } 1745 * } 1746 * 1747 * @nocollapse 1748 */ 1749 static getPropertyDescriptor(name, key, options) { 1750 return { 1751 // tslint:disable-next-line:no-any no symbol in index 1752 get() { 1753 return this[key]; 1754 }, 1755 set(value) { 1756 const oldValue = this[name]; 1757 this[key] = value; 1758 this 1759 .requestUpdateInternal(name, oldValue, options); 1760 }, 1761 configurable: true, 1762 enumerable: true 1763 }; 1764 } 1765 /** 1766 * Returns the property options associated with the given property. 1767 * These options are defined with a PropertyDeclaration via the `properties` 1768 * object or the `@property` decorator and are registered in 1769 * `createProperty(...)`. 1770 * 1771 * Note, this method should be considered "final" and not overridden. To 1772 * customize the options for a given property, override `createProperty`. 1773 * 1774 * @nocollapse 1775 * @final 1776 */ 1777 static getPropertyOptions(name) { 1778 return this._classProperties && this._classProperties.get(name) || 1779 defaultPropertyDeclaration; 1780 } 1781 /** 1782 * Creates property accessors for registered properties and ensures 1783 * any superclasses are also finalized. 1784 * @nocollapse 1785 */ 1786 static finalize() { 1787 // finalize any superclasses 1788 const superCtor = Object.getPrototypeOf(this); 1789 if (!superCtor.hasOwnProperty(finalized)) { 1790 superCtor.finalize(); 1791 } 1792 this[finalized] = true; 1793 this._ensureClassProperties(); 1794 // initialize Map populated in observedAttributes 1795 this._attributeToPropertyMap = new Map(); 1796 // make any properties 1797 // Note, only process "own" properties since this element will inherit 1798 // any properties defined on the superClass, and finalization ensures 1799 // the entire prototype chain is finalized. 1800 if (this.hasOwnProperty(JSCompiler_renameProperty('properties', this))) { 1801 const props = this.properties; 1802 // support symbols in properties (IE11 does not support this) 1803 const propKeys = [ 1804 ...Object.getOwnPropertyNames(props), 1805 ...(typeof Object.getOwnPropertySymbols === 'function') ? 1806 Object.getOwnPropertySymbols(props) : 1807 [] 1808 ]; 1809 // This for/of is ok because propKeys is an array 1810 for (const p of propKeys) { 1811 // note, use of `any` is due to TypeSript lack of support for symbol in 1812 // index types 1813 // tslint:disable-next-line:no-any no symbol in index 1814 this.createProperty(p, props[p]); 1815 } 1816 } 1817 } 1818 /** 1819 * Returns the property name for the given attribute `name`. 1820 * @nocollapse 1821 */ 1822 static _attributeNameForProperty(name, options) { 1823 const attribute = options.attribute; 1824 return attribute === false ? 1825 undefined : 1826 (typeof attribute === 'string' ? 1827 attribute : 1828 (typeof name === 'string' ? name.toLowerCase() : undefined)); 1829 } 1830 /** 1831 * Returns true if a property should request an update. 1832 * Called when a property value is set and uses the `hasChanged` 1833 * option for the property if present or a strict identity check. 1834 * @nocollapse 1835 */ 1836 static _valueHasChanged(value, old, hasChanged = notEqual) { 1837 return hasChanged(value, old); 1838 } 1839 /** 1840 * Returns the property value for the given attribute value. 1841 * Called via the `attributeChangedCallback` and uses the property's 1842 * `converter` or `converter.fromAttribute` property option. 1843 * @nocollapse 1844 */ 1845 static _propertyValueFromAttribute(value, options) { 1846 const type = options.type; 1847 const converter = options.converter || defaultConverter; 1848 const fromAttribute = (typeof converter === 'function' ? converter : converter.fromAttribute); 1849 return fromAttribute ? fromAttribute(value, type) : value; 1850 } 1851 /** 1852 * Returns the attribute value for the given property value. If this 1853 * returns undefined, the property will *not* be reflected to an attribute. 1854 * If this returns null, the attribute will be removed, otherwise the 1855 * attribute will be set to the value. 1856 * This uses the property's `reflect` and `type.toAttribute` property options. 1857 * @nocollapse 1858 */ 1859 static _propertyValueToAttribute(value, options) { 1860 if (options.reflect === undefined) { 1861 return; 1862 } 1863 const type = options.type; 1864 const converter = options.converter; 1865 const toAttribute = converter && converter.toAttribute || 1866 defaultConverter.toAttribute; 1867 return toAttribute(value, type); 1868 } 1869 /** 1870 * Performs element initialization. By default captures any pre-set values for 1871 * registered properties. 1872 */ 1873 initialize() { 1874 this._updateState = 0; 1875 this._updatePromise = 1876 new Promise((res) => this._enableUpdatingResolver = res); 1877 this._changedProperties = new Map(); 1878 this._saveInstanceProperties(); 1879 // ensures first update will be caught by an early access of 1880 // `updateComplete` 1881 this.requestUpdateInternal(); 1882 } 1883 /** 1884 * Fixes any properties set on the instance before upgrade time. 1885 * Otherwise these would shadow the accessor and break these properties. 1886 * The properties are stored in a Map which is played back after the 1887 * constructor runs. Note, on very old versions of Safari (<=9) or Chrome 1888 * (<=41), properties created for native platform properties like (`id` or 1889 * `name`) may not have default values set in the element constructor. On 1890 * these browsers native properties appear on instances and therefore their
1891 * default value will overwrite any element default (e.g. if the element sets 1892 * this.id = 'id' in the constructor, the 'id' will become '' since this is 1893 * the native platform default). 1894 */ 1895 _saveInstanceProperties() { 1896 // Use forEach so this works even if for/of loops are compiled to for loops 1897 // expecting arrays 1898 this.constructor 1899 ._classProperties.forEach((_v, p) => { 1900 if (this.hasOwnProperty(p)) { 1901 const value = this[p]; 1902 delete this[p]; 1903 if (!this._instanceProperties) { 1904 this._instanceProperties = new Map(); 1905 } 1906 this._instanceProperties.set(p, value); 1907 } 1908 }); 1909 } 1910 /** 1911 * Applies previously saved instance properties. 1912 */ 1913 _applyInstanceProperties() { 1914 // Use forEach so this works even if for/of loops are compiled to for loops 1915 // expecting arrays 1916 // tslint:disable-next-line:no-any 1917 this._instanceProperties.forEach((v, p) => this[p] = v); 1918 this._instanceProperties = undefined; 1919 } 1920 connectedCallback() { 1921 // Ensure first connection completes an update. Updates cannot complete 1922 // before connection. 1923 this.enableUpdating(); 1924 } 1925 enableUpdating() { 1926 if (this._enableUpdatingResolver !== undefined) { 1927 this._enableUpdatingResolver(); 1928 this._enableUpdatingResolver = undefined; 1929 } 1930 } 1931 /** 1932 * Allows for `super.disconnectedCallback()` in extensions while 1933 * reserving the possibility of making non-breaking feature additions 1934 * when disconnecting at some point in the future. 1935 */ 1936 disconnectedCallback() { 1937 } 1938 /** 1939 * Synchronizes property values when attributes change. 1940 */ 1941 attributeChangedCallback(name, old, value) { 1942 if (old !== value) { 1943 this._attributeToProperty(name, value); 1944 } 1945 } 1946 _propertyToAttribute(name, value, options = defaultPropertyDeclaration) { 1947 const ctor = this.constructor; 1948 const attr = ctor._attributeNameForProperty(name, options); 1949 if (attr !== undefined) { 1950 const attrValue = ctor._propertyValueToAttribute(value, options); 1951 // an undefined value does not change the attribute. 1952 if (attrValue === undefined) { 1953 return; 1954 } 1955 // Track if the property is being reflected to avoid 1956 // setting the property again via `attributeChangedCallback`. Note: 1957 // 1. this takes advantage of the fact that the callback is synchronous. 1958 // 2. will behave incorrectly if multiple attributes are in the reaction 1959 // stack at time of calling. However, since we process attributes 1960 // in `update` this should not be possible (or an extreme corner case 1961 // that we'd like to discover). 1962 // mark state reflecting 1963 this._updateState = this._updateState | STATE_IS_REFLECTING_TO_ATTRIBUTE; 1964 if (attrValue == null) { 1965 this.removeAttribute(attr); 1966 } 1967 else { 1968 this.setAttribute(attr, attrValue); 1969 } 1970 // mark state not reflecting 1971 this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_ATTRIBUTE; 1972 } 1973 } 1974 _attributeToProperty(name, value) { 1975 // Use tracking info to avoid deserializing attribute value if it was 1976 // just set from a property setter. 1977 if (this._updateState & STATE_IS_REFLECTING_TO_ATTRIBUTE) { 1978 return; 1979 } 1980 const ctor = this.constructor; 1981 // Note, hint this as an `AttributeMap` so closure clearly understands 1982 // the type; it has issues with tracking types through statics 1983 // tslint:disable-next-line:no-unnecessary-type-assertion 1984 const propName = ctor._attributeToPropertyMap.get(name); 1985 if (propName !== undefined) { 1986 const options = ctor.getPropertyOptions(propName); 1987 // mark state reflecting 1988 this._updateState = this._updateState | STATE_IS_REFLECTING_TO_PROPERTY; 1989 this[propName] = 1990 // tslint:disable-next-line:no-any 1991 ctor._propertyValueFromAttribute(value, options); 1992 // mark state not reflecting 1993 this._updateState = this._updateState & ~STATE_IS_REFLECTING_TO_PROPERTY; 1994 } 1995 } 1996 /** 1997 * This protected version of `requestUpdate` does not access or return the 1998 * `updateComplete` promise. This promise can be overridden and is therefore 1999 * not free to access. 2000 */ 2001 requestUpdateInternal(name, oldValue, options) { 2002 let shouldRequestUpdate = true; 2003 // If we have a property key, perform property update steps. 2004 if (name !== undefined) { 2005 const ctor = this.constructor; 2006 options = options || ctor.getPropertyOptions(name); 2007 if (ctor._valueHasChanged(this[name], oldValue, options.hasChanged)) { 2008 if (!this._changedProperties.has(name)) { 2009 this._changedProperties.set(name, oldValue); 2010 } 2011 // Add to reflecting properties set. 2012 // Note, it's important that every change has a chance to add the 2013 // property to `_reflectingProperties`. This ensures setting 2014 // attribute + property reflects correctly. 2015 if (options.reflect === true && 2016 !(this._updateState & STATE_IS_REFLECTING_TO_PROPERTY)) { 2017 if (this._reflectingProperties === undefined) { 2018 this._reflectingProperties = new Map(); 2019 } 2020 this._reflectingProperties.set(name, options); 2021 } 2022 } 2023 else { 2024 // Abort the request if the property should not be considered changed. 2025 shouldRequestUpdate = false;
2026 } 2027 } 2028 if (!this._hasRequestedUpdate && shouldRequestUpdate) { 2029 this._updatePromise = this._enqueueUpdate(); 2030 } 2031 } 2032 /** 2033 * Requests an update which is processed asynchronously. This should 2034 * be called when an element should update based on some state not triggered 2035 * by setting a property. In this case, pass no arguments. It should also be 2036 * called when manually implementing a property setter. In this case, pass the 2037 * property `name` and `oldValue` to ensure that any configured property 2038 * options are honored. Returns the `updateComplete` Promise which is resolved 2039 * when the update completes. 2040 * 2041 * @param name {PropertyKey} (optional) name of requesting property 2042 * @param oldValue {any} (optional) old value of requesting property 2043 * @returns {Promise} A Promise that is resolved when the update completes. 2044 */ 2045 requestUpdate(name, oldValue) { 2046 this.requestUpdateInternal(name, oldValue); 2047 return this.updateComplete; 2048 } 2049 /** 2050 * Sets up the element to asynchronously update. 2051 */ 2052 async _enqueueUpdate() { 2053 this._updateState = this._updateState | STATE_UPDATE_REQUESTED; 2054 try { 2055 // Ensure any previous update has resolved before updating. 2056 // This `await` also ensures that property changes are batched. 2057 await this._updatePromise; 2058 } 2059 catch (e) { 2060 // Ignore any previous errors. We only care that the previous cycle is 2061 // done. Any error should have been handled in the previous update. 2062 } 2063 const result = this.performUpdate(); 2064 // If `performUpdate` returns a Promise, we await it. This is done to 2065 // enable coordinating updates with a scheduler. Note, the result is 2066 // checked to avoid delaying an additional microtask unless we need to. 2067 if (result != null) { 2068 await result; 2069 } 2070 return !this._hasRequestedUpdate; 2071 } 2072 get _hasRequestedUpdate() { 2073 return (this._updateState & STATE_UPDATE_REQUESTED); 2074 } 2075 get hasUpdated() { 2076 return (this._updateState & STATE_HAS_UPDATED); 2077 } 2078 /** 2079 * Performs an element update. Note, if an exception is thrown during the 2080 * update, `firstUpdated` and `updated` will not be called. 2081 * 2082 * You can override this method to change the timing of updates. If this 2083 * method is overridden, `super.performUpdate()` must be called. 2084 * 2085 * For instance, to schedule updates to occur just before the next frame: 2086 * 2087 * ``` 2088 * protected async performUpdate(): Promise<unknown> { 2089 * await new Promise((resolve) => requestAnimationFrame(() => resolve())); 2090 * super.performUpdate(); 2091 * } 2092 * ``` 2093 */ 2094 performUpdate() { 2095 // Abort any update if one is not pending when this is called. 2096 // This can happen if `performUpdate` is called early to "flush" 2097 // the update. 2098 if (!this._hasRequestedUpdate) { 2099 return; 2100 } 2101 // Mixin instance properties once, if they exist. 2102 if (this._instanceProperties) { 2103 this._applyInstanceProperties(); 2104 } 2105 let shouldUpdate = false; 2106 const changedProperties = this._changedProperties; 2107 try { 2108 shouldUpdate = this.shouldUpdate(changedProperties); 2109 if (shouldUpdate) { 2110 this.update(changedProperties); 2111 } 2112 else { 2113 this._markUpdated(); 2114 } 2115 } 2116 catch (e) { 2117 // Prevent `firstUpdated` and `updated` from running when there's an 2118 // update exception. 2119 shouldUpdate = false; 2120 // Ensure element can accept additional updates after an exception. 2121 this._markUpdated(); 2122 throw e; 2123 } 2124 if (shouldUpdate) { 2125 if (!(this._updateState & STATE_HAS_UPDATED)) { 2126 this._updateState = this._updateState | STATE_HAS_UPDATED; 2127 this.firstUpdated(changedProperties); 2128 } 2129 this.updated(changedProperties); 2130 } 2131 } 2132 _markUpdated() { 2133 this._changedProperties = new Map(); 2134 this._updateState = this._updateState & ~STATE_UPDATE_REQUESTED; 2135 } 2136 /** 2137 * Returns a Promise that resolves when the element has completed updating. 2138 * The Promise value is a boolean that is `true` if the element completed the 2139 * update without triggering another update. The Promise result is `false` if 2140 * a property was set inside `updated()`. If the Promise is rejected, an 2141 * exception was thrown during the update. 2142 * 2143 * To await additional asynchronous work, override the `_getUpd
2143ateComplete` 2144 * method. For example, it is sometimes useful to await a rendered element 2145 * before fulfilling this Promise. To do this, first await 2146 * `super._getUpdateComplete()`, then any subsequent state. 2147 * 2148 * @returns {Promise} The Promise returns a boolean that indicates if the 2149 * update resolved without triggering another update. 2150 */ 2151 get updateComplete() { 2152 return this._getUpdateComplete(); 2153 } 2154 /** 2155 * Override point for the `updateComplete` promise. 2156 * 2157 * It is not safe to override the `updateComplete` getter directly due to a 2158 * limitation in TypeScript which means it is not possible to call a 2159 * superclass getter (e.g. `super.updateComplete.then(...)`) when the target 2160 * language is ES5 (https://github.com/microsoft/TypeScript/issues/338). 2161 * This method should be overridden instead. For example: 2162 * 2163 * class MyElement extends LitElement { 2164 * async _getUpdateComplete() { 2165 * await super._getUpdateComplete(); 2166 * await this._myChild.updateComplete; 2167 * } 2168 * } 2169 * @deprecated Override `getUpdateComplete()` instead for forward 2170 * compatibility with `lit-element` 3.0 / `@lit/reactive-element`. 2171 */
2172 _getUpdateComplete() { 2173 return this.getUpdateComplete(); 2174 } 2175 /** 2176 * Override point for the `updateComplete` promise. 2177 * 2178 * It is not safe to override the `updateComplete` getter directly due to a 2179 * limitation in TypeScript which means it is not possible to call a 2180 * superclass getter (e.g. `super.updateComplete.then(...)`) when the target 2181 * language is ES5 (https://github.com/microsoft/TypeScript/issues/338). 2182 * This method should be overridden instead. For example: 2183 * 2184 * class MyElement extends LitElement { 2185 * async getUpdateComplete() { 2186 * await super.getUpdateComplete(); 2187 * await this._myChild.updateComplete; 2188 * } 2189 * } 2190 */ 2191 getUpdateComplete() { 2192 return this._updatePromise; 2193 } 2194 /** 2195 * Controls whether or not `update` should be called when the element requests 2196 * an update. By default, this method always returns `true`, but this can be 2197 * customized to control when to update. 2198 * 2199 * @param _changedProperties Map of changed properties with old values 2200 */ 2201 shouldUpdate(_changedProperties) { 2202 return true; 2203 } 2204 /** 2205 * Updates the element. This method reflects property values to attributes. 2206 * It can be overridden to render and keep updated element DOM. 2207 * Setting properties inside this method will *not* trigger 2208 * another update. 2209 * 2210 * @param _changedProperties Map of changed properties with old values 2211 */ 2212 update(_changedProperties) { 2213 if (this._reflectingProperties !== undefined && 2214 this._reflectingProperties.size > 0) { 2215 // Use forEach so this works even if for/of loops are compiled to for 2216 // loops expecting arrays 2217 this._reflectingProperties.forEach((v, k) => this._propertyToAttribute(k, this[k], v)); 2218 this._reflectingProperties = undefined; 2219 } 2220 this._markUpdated(); 2221 } 2222 /** 2223 * Invoked whenever the element is updated. Implement to perform 2224 * post-updating tasks via DOM APIs, for example, focusing an element. 2225 * 2226 * Setting properties inside this method will trigger the element to update 2227 * again after this update cycle completes. 2228 * 2229 * @param _changedProperties Map of changed properties with old values 2230 */ 2231 updated(_changedProperties) { 2232 } 2233 /** 2234 * Invoked when the element is first updated. Implement to perform one time 2235 * work on the element after update. 2236 * 2237 * Setting properties inside this method will trigger the element to update 2238 * again after this update cycle completes. 2239 * 2240 * @param _changedProperties Map of changed properties with old values 2241 */ 2242 firstUpdated(_changedProperties) { 2243 } 2244} 2245_a = finalized; 2246/** 2247 * Marks class as having finished creating properties. 2248 */ 2249UpdatingElement[_a] = true;/** 2250@license 2251Copyright (c) 2019 The Polymer Project Authors. All rights reserved. 2252This code may only be used under the BSD style license found at 2253http://polymer.github.io/LICENSE.txt The complete set of authors may be found at 2254http://polymer.github.io/AUTHORS.txt The complete set of contributors may be 2255found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as 2256part of the polymer project is also subject to an additional IP rights grant 2257found at http://polymer.github.io/PATENTS.txt 2258*/ 2259/** 2260 * Whether the current browser supports `adoptedStyleSheets`. 2261 */ 2262const supportsAdoptingStyleSheets = (window.ShadowRoot) && 2263 (window.ShadyCSS === undefined || window.ShadyCSS.nativeShadow) && 2264 ('adoptedStyleSheets' in Document.prototype) && 2265 ('replace' in CSSStyleSheet.prototype); 2266const constructionToken = Symbol(); 2267class CSSResult { 2268 constructor(cssText, safeToken) { 2269 if (safeToken !== constructionToken) { 2270 throw new Error('CSSResult is not constructable. Use `unsafeCSS` or `css` instead.'); 2271 } 2272 this.cssText = cssText; 2273 } 2274 // Note, this is a getter so that it's lazy. In practice, this means 2275 // stylesheets are not created until the first element instance is made. 2276 get styleSheet() { 2277 if (this._styleSheet === undefined) { 2278 // Note, if `supportsAdoptingStyleSheets` is true then we assume 2279 // CSSStyleSheet is constructable. 2280 if (supportsAdoptingStyleSheets) { 2281 this._styleSheet = new CSSStyleSheet(); 2282 this._styleSheet.replaceSync(this.cssText); 2283 } 2284 else { 2285 this._styleSheet = null; 2286 } 2287 } 2288 return this._styleSheet; 2289 } 2290 toString() { 2291 return this.cssText; 2292 } 2293} 2294/** 2295 * Wrap a value for interpolation in a [[`css`]] tagged template literal. 2296 * 2297 * This is unsafe because untrusted CSS text can be used to phone home 2298 * or exfiltrate data to an attacker controlled site. Take care to only use 2299 * this with trusted input. 2300 */ 2301const unsafeCSS = (value) => { 2302 return new CSSResult(String(value), constructionToken); 2303}; 2304const textFromCSSResult = (value) => { 2305 if (value instanceof CSSResult) { 2306 return value.cssText; 2307 } 2308 else if (typeof value === 'number') { 2309 return value; 2310 } 2311 else { 2312 throw new Error(`Value passed to 'css' function must be a 'css' function result: ${value}. Use 'unsafeCSS' to pass non-literal values, but 2313 take care to ensure page security.`); 2314 } 2315}; 2316/** 2317 * Template tag which which can be used with LitElement's [[LitElement.styles | 2318 * `styles`]] property to set element styles. For security reasons, only literal 2319 * string values may be used. To incorporate non-literal values [[`unsafeCSS`]] 2320 * may be used inside a template string part. 2321 */ 2322const css = (strings, ...values) => { 2323 const cssText = values.reduce((acc, v, idx) => acc + textFromCSSResult(v) + strings[idx + 1], strings[0]); 2324 return new CSSResult(cssText, constructionToken); 2325};/** 2326 * @license 2327 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 2328 * This code may only be used under the BSD style license found at 2329 * http://polymer.github.io/LICENSE.txt 2330 * The complete set of authors may be found at 2331 * http://polymer.github.io/AUTHORS.txt 2332 * The complete set of contributors may be found at 2333 * http://polymer.github.io/CONTRIBUTORS.txt 2334 * Code distributed by Google as part of the polymer project is also 2335 * subject to an additional IP rights grant found at 2336 * http://polymer.github.io/PATENTS.txt 2337 */ 2338/** 2339 * The main LitElement module, which defines the [[`LitElement`]] base class and 2340 * related APIs. 2341 * 2342 * LitElement components can define a template and a set of observed 2343 * properties. Changing an observed property triggers a re-render of the 2344 * element. 2345 * 2346 * Import [[`LitElement`]] and [[`html`]] from this module to create a 2347 * component: 2348 * 2349 * ```js 2350 * import {LitElement, html} from 'lit-element'; 2351 * 2352 * class MyElement extends LitElement { 2353 * 2354 * // Declare observed properties 2355 * static get properties() { 2356 * return { 2357 * adjective: {} 2358 * } 2359 * } 2360 * 2361 * constructor() { 2362 * this.adjective = 'awesome'; 2363 * } 2364 * 2365 * // Define the element's template 2366 * render() { 2367 * return html`<p>your ${adjective} template here</p>`; 2368 * } 2369 * } 2370 * 2371 * customElements.define('my-element', MyElement); 2372 * ``` 2373 * 2374 * `LitElement` extends [[`UpdatingElement`]] and adds lit-html templating. 2375 * The `UpdatingElement` class is provided for users that want to build 2376 * their own custom element base classes that don't use lit-html. 2377 * 2378 * @packageDocumentation 2379 */ 2380// IMPORTANT: do not change the property name or the assignment expression.
2381// This line will be used in regexes to search for LitElement usage. 2382// TODO(justinfagnani): inject version number at build time 2383(window['litElementVersions'] || (window['litElementVersions'] = [])) 2384 .push('2.5.1'); 2385/** 2386 * Sentinal value used to avoid calling lit-html's render function when 2387 * subclasses do not implement `render` 2388 */ 2389const renderNotImplemented = {}; 2390/** 2391 * Base element class that manages element properties and attributes, and 2392 * renders a lit-html template. 2393 * 2394 * To define a component, subclass `LitElement` and implement a 2395 * `render` method to provide the component's template. Define properties 2396 * using the [[`properties`]] property or the [[`property`]] decorator. 2397 */ 2398class LitElement extends UpdatingElement { 2399 /** 2400 * Return the array of styles to apply to the element. 2401 * Override this method to integrate into a style management system. 2402 * 2403 * @nocollapse 2404 */ 2405 static getStyles() { 2406 return this.styles; 2407 } 2408 /** @nocollapse */ 2409 static _getUniqueStyles() { 2410 // Only gather styles once per class 2411 if (this.hasOwnProperty(JSCompiler_renameProperty('_styles', this))) { 2412 return; 2413 }
2414 // Take care not to call `this.getStyles()` multiple times since this 2415 // generates new CSSResults each time. 2416 // TODO(sorvell): Since we do not cache CSSResults by input, any 2417 // shared styles will generate new stylesheet objects, which is wasteful. 2418 // This should be addressed when a browser ships constructable 2419 // stylesheets. 2420 const userStyles = this.getStyles(); 2421 if (Array.isArray(userStyles)) { 2422 // De-duplicate styles preserving the _last_ instance in the set. 2423 // This is a performance optimization to avoid duplicated styles that can 2424 // occur especially when composing via subclassing. 2425 // The last item is kept to try to preserve the cascade order with the 2426 // assumption that it's most important that last added styles override 2427 // previous styles. 2428 const addStyles = (styles, set) => styles.reduceRight((set, s) => 2429 // Note: On IE set.add() does not return the set 2430 Array.isArray(s) ? addStyles(s, set) : (set.add(s), set), set); 2431 // Array.from does not work on Set in IE, otherwise return 2432 // Array.from(addStyles(userStyles, new Set<CSSResult>())).reverse() 2433 const set = addStyles(userStyles, new Set()); 2434 const styles = []; 2435 set.forEach((v) => styles.unshift(v)); 2436 this._styles = styles; 2437 } 2438 else { 2439 this._styles = userStyles === undefined ? [] : [userStyles]; 2440 } 2441 // Ensure that there are no invalid CSSStyleSheet instances here. They are 2442 // invalid in two conditions. 2443 // (1) the sheet is non-constructible (`sheet` of a HTMLStyleElement), but 2444 // this is impossible to check except via .replaceSync or use 2445 // (2) the ShadyCSS polyfill is enabled (:. supportsAdoptingStyleSheets is 2446 // false) 2447 this._styles = this._styles.map((s) => { 2448 if (s instanceof CSSStyleSheet && !supportsAdoptingStyleSheets) { 2449 // Flatten the cssText from the passed constructible stylesheet (or 2450 // undetectable non-constructible stylesheet). The user might have 2451 // expected to update their stylesheets over time, but the alternative 2452 // is a crash. 2453 const cssText = Array.prototype.slice.call(s.cssRules) 2454 .reduce((css, rule) => css + rule.cssText, ''); 2455 return unsafeCSS(cssText); 2456 } 2457 return s; 2458 }); 2459 } 2460 /** 2461 * Performs element initialization. By default this calls 2462 * [[`createRenderRoot`]] to create the element [[`renderRoot`]] node and 2463 * captures any pre-set values for registered properties. 2464 */ 2465 initialize() { 2466 super.initialize(); 2467 this.constructor._getUniqueStyles(); 2468 this.renderRoot = this.createRenderRoot(); 2469 // Note, if renderRoot is not a shadowRoot, styles would/could apply to the 2470 // element's getRootNode(). While this could be done, we're choosing not to 2471 // support this now since it would require different logic around de-duping. 2472 if (window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot) { 2473 this.adoptStyles(); 2474 } 2475 } 2476 /** 2477 * Returns the node into which the element should render and by default 2478 * creates and returns an open shadowRoot. Implement to customize where the 2479 * element's DOM is rendered. For example, to render into the element's 2480 * childNodes, return `this`. 2481 * @returns {Element|DocumentFragment} Returns a node into which to render. 2482 */ 2483 createRenderRoot() { 2484 return this.attachShadow(this.constructor.shadowRootOptions); 2485 } 2486 /** 2487 * Applies styling to the element shadowRoot using the [[`styles`]] 2488 * property. Styling will apply using `shadowRoot.adoptedStyleSheets` where 2489 * available and will fallback otherwise. When Shadow DOM is polyfilled, 2490 * ShadyCSS scopes styles and adds them to the document. When Shadow DOM 2491 * is available but `adoptedStyleSheets` is not, styles are appended to the 2492 * end of the `shadowRoot` to [mimic spec 2493 * behavior](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets). 2494 */ 2495 adoptStyles() { 2496 const styles = this.constructor._styles; 2497 if (styles.length === 0) { 2498 return; 2499 } 2500 // There are three separate cases here based on Shadow DOM support. 2501 // (1) shadowRoot polyfilled: use ShadyCSS 2502 // (2) shadowRoot.adoptedStyleSheets available: use it 2503 // (3) shadowRoot.adoptedStyleSheets polyfilled: append styles after 2504 // rendering 2505 if (window.ShadyCSS !== undefined && !window.ShadyCSS.nativeShadow) { 2506 window.ShadyCSS.ScopingShim.prepareAdoptedCssText(styles.map((s) => s.cssText), this.localName); 2507 } 2508 else if (supportsAdoptingStyleSheets) { 2509 this.renderRoot.adoptedStyleSheets = 2510 styles.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet); 2511 } 2512 else { 2513 // This must be done after rendering so the actual style insertion is done 2514 // in `update`. 2515 this._needsShimAdoptedStyleSheets = true; 2516 } 2517 } 2518 connectedCallback() { 2519 super.connectedCallback(); 2520 // Note, first update/render handles styleElement so we only call this if 2521 // connected after first update. 2522 if (this.hasUpdated && window.ShadyCSS !== undefined) { 2523 window.ShadyCSS.styleElement(this); 2524 } 2525 } 2526 /** 2527 * Updates the element. This method reflects property values to attributes 2528 * and calls `render` to render DOM via lit-html. Setting properties inside 2529 * this method will *not* trigger another update. 2530 * @param _changedProperties Map of changed properties with old values 2531 */ 2532 update(changedProperties) { 2533 // Setting properties in `render` should not trigger an update. Since 2534 // updates are allowed after super.update, it's important to call `render` 2535 // before that. 2536 const templateResult = this.render(); 2537 super.update(changedProperties); 2538 // If render is not implemented by the component, don't call lit-html render 2539 if (templateResult !== renderNotImplemented) { 2540 this.constructor 2541 .render(templateResult, this.renderRoot, { scopeName: this.localName, eventContext: this }); 2542 }
2543 // When native Shadow DOM is used but adoptedStyles are not supported, 2544 // insert styling after rendering to ensure adoptedStyles have highest 2545 // priority. 2546 if (this._needsShimAdoptedStyleSheets) { 2547 this._needsShimAdoptedStyleSheets = false; 2548 this.constructor._styles.forEach((s) => { 2549 const style = document.createElement('style'); 2550 style.textContent = s.cssText; 2551 this.renderRoot.appendChild(style); 2552 }); 2553 } 2554 } 2555 /** 2556 * Invoked on each update to perform rendering tasks. This method may return 2557 * any value renderable by lit-html's `NodePart` - typically a 2558 * `TemplateResult`. Setting properties inside this method will *not* trigger 2559 * the element to update. 2560 */ 2561 render() { 2562 return renderNotImplemented; 2563 } 2564} 2565/** 2566 * Ensure this class is marked as `finalized` as an optimization ensuring 2567 * it will not needlessly try to `finalize`. 2568 * 2569 * Note this property name is a string to prevent breaking Closure JS Compiler 2570 * optimizations. See updating-element.ts for more information. 2571 */ 2572LitElement['finalized'] = true; 2573/** 2574 * Reference to the underlying library method used to render the element's 2575 * DOM. By default, points to the `render` method from lit-html's shady-render 2576 * module. 2577 * 2578 * **Most users will never need to touch this property.** 2579 * 2580 * This property should not be confused with the `render` instance method, 2581 * which should be overridden to define a template for the element. 2582 * 2583 * Advanced users creating a new base class based on LitElement can override 2584 * this property to point to a custom render method with a signature that 2585 * matches [shady-render's `render` 2586 * method](https://lit-html.polymer-project.org/api/modules/shady_render.html#render). 2587 * 2588 * @nocollapse 2589 */ 2590LitElement.render = render; 2591/** @nocollapse */ 2592LitElement.shadowRootOptions = { mode: 'open' };const JWPlayerStyle = css` 2593 atype-video-jwplayer { 2594 display: block; 2595 max-width: 100%; 2596 position: relative; 2597 aspect-ratio: var(--aspect-ratio, 16 / 9); 2598 } 2599 atype-video-jwplayer[with-title] { 2600 --titleHeight: var(--title-height, 3.5em); 2601 --primaryTextColor: var(--primary-text-color, #8a8a8a); 2602 --secondaryTextColor: var(--secondary-text-color, #141414); 2603 --backgroundColor: var(--title-background-color, #fefefe); 2604 --borderColor: var(--border-color, #cacaca); 2605 --titleFontSize: var(--video-title-font-size, 1.0em); 2606 2607 --primaryTitleFontSize: 1.0em; 2608 --titlePaddingBottom: 0.5em; 2609 --titleWrap: normal; 2610 } 2611 2612 @media(min-width: 426px){ 2613 atype-video-jwplayer[with-title] { 2614 --titleHeight: var(--title-height, 4em); 2615 --primaryTitleFontSize: 1.25em; 2616 --titlePaddingBottom: 0.25em; 2617 --titleWrap: nowrap; 2618 } 2619 } 2620 atype-video-jwplayer[with-title] .jwplayer { 2621 overflow: visible; 2622 margin-bottom: calc(var(--titleHeight) + 8px); 2623 font-size: var(--titleFontSize); 2624 } 2625 2626 atype-video-jwplayer[with-title] .jw-title { 2627 font-size: var(--titleFontSize); 2628 display: block; 2629 top: unset; 2630 bottom: 0; 2631 padding: 0; 2632 transform: translate(0, var(--titleHeight)); 2633 padding: .5em; 2634 height: var(--titleHeight); 2635 overflow: hidden; 2636 white-space: normal; 2637 background-color: var(--backgroundColor); 2638 border: 1px solid var(--borderColor); 2639 } 2640 2641 atype-video-jwplayer[with-title] .jw-title-primary { 2642 color: var(--primaryTextColor); 2643 font-size: var(--primaryTitleFontSize); 2644 line-height: 1.25em; 2645 padding: 0; 2646 padding-bottom: var(--titlePaddingBottom); 2647 white-space: var(--titleWrap); 2648 font-family: "Montserrat" !important; 2649 font-weight: 200; 2650 } 2651 atype-video-jwplayer[with-title] .jw-title-secondary { 2652 display: block; 2653 padding: 0; 2654 white-space: nowrap; 2655 font-family: "Montserrat" !important; 2656 color: var(--secondaryTextColor); 2657 } 2658 2659 .jw-flag-floating .jw-wrapper { 2660 background: transparent; 2661 } 2662 .jw-float-icon { 2663 display: none; 2664 position: absolute; 2665 left: 0rem; 2666 align-items: center; 2667 justify-content: center; 2668 width: auto; 2669 right: auto; 2670 top: 0; 2671 background-color: #0009; 2672 padding: 0.25rem; 2673 } 2674 .jw-float-icon:after { 2675 content: "Close"; 2676 color: #fff; 2677 } 2678 2679 .jw-ad-instream { 2680 z-index: 0; 2681 } 2682 2683 span.jw-text.jw-reset.jw-text-alt { 2684 position: absolute; 2685 bottom: 4px; 2686} 2687 2688 .jw-flag-floating .jw-wrapper { 2689 left: 1rem; 2690 right: auto; 2691 } 2692 @media(min-width: 481px) { 2693 .jw-flag-floating .jw-wrapper.jw-reset { 2694 bottom: initial !important; 2695 top: calc(var(--nav-height, 48px) + 12px) !important; 2696 animation-name: jw-float-to-top; 2697 } 2698 } 2699 @media(max-width: 480px){ 2700 .jw-flag-floating .jw-wrapper.jw-reset { 2701 max-width: 150px !important; 2702 right: 1rem !important; 2703 bottom: initial !important; 2704 margin: 0px !important; 2705 position: fixed; 2706 top: 48px !important; 2707 left: unset !important; 2708 } 2709 2710 .jw-flag-floating.jw-flag-touch .jw-slider-time::before { 2711 content: unset; 2712 } 2713 .jw-float-icon { 2714 display: none; 2715 position: absolute; 2716 left: 0rem; 2717 align-items: center; 2718 justify-content: center; 2719 width: auto; 2720 right: auto; 2721 top: 0; 2722 padding: 0.25rem; 2723 } 2724 2725 .jw-float-icon:after { 2726 font-size: 9px; 2727 } 2728 .jw-svg-icon { 2729 height: 14px; 2730 width: 14px; 2731 } 2732 .jw-icon { 2733 height: 24px; 2734 } 2735 span.jw-text.jw-reset.jw-text-alt { 2736 font-size: 8px; 2737 } 2738 } 2739 2740 2741 2742`;const PCT = [ 25, 50, 75 ]; 2743const CATEGORY = 'JW Player Video'; 2744class GAVideoTracker { 2745 2746 constructor(player) { 2747 2748 this.player = player; 2749 this.T = 0; 2750 this.registerEvents(); 2751 this.cancel = []; 2752 this.until = []; 2753 this.notice = []; 2754 2755 this.setGlobals(); 2756 this.registerEvents(); 2757 } 2758 2759 registerEvents() { 2760 2761 ['beforePlay', 'time', 'autostartNotAllowed', 'error', 'playAttemptFailed', 'setupError', 'warning', 'play', 'pause'] 2762 .forEach((item) => { 2763 this.player.on(item, this.handleEvent.bind(this)); 2764 }); 2765 } 2766 2767 handleEvent(event) { 2768 switch(event.type) { 2769 case 'beforePlay': 2770 this.beforePlay(event); 2771 this.player.off(event.type, this.onBeforePlay); 2772 break; 2773 case 'time': 2774 this.time(event); 2775 break; 2776 default: 2777 console.log('got event ', event.type); 2778 } 2779 } 2780 2781 setGlobals() { 2782 2783 window.gtag = function(hitType, eventType, eventModel) { 2784 ga('send', { 2785 hitType: hitType, 2786 eventCategory: eventModel.event_category, 2787 eventLabel: eventModel.event_label, 2788 eventAction: eventModel.event_action, 2789 nonInteraction: true 2790 }); 2791 2792 dataLayer.push(arguments); 2793 }; 2794 } 2795 2796 beforePlay(event) { 2797 let dur = this.player.getDuration()/100; 2798 this.duration = dur; 2799 this.title = this.player.getPlaylistItem().title; 2800 this.until = PCT.map( (pct) => [ pct, pct * dur ] ); 2801 console.log("marking percentiles at ", this.until); 2802 } 2803 2804 time(t) { 2805 2806 if(this.until.length <= 0) 2807 return 2808 let T= this.T = t.currentTime; 2809 let until = this.until[0]; 2810 let limit = until[1]; 2811 if (T < limit) 2812 return 2813 let pct = until[0]; 2814 this.until.shift(); 2815 this.sendEvent({ 2816 hitType: 'event', 2817 eventCategory: CATEGORY, 2818 eventAction: pct+'% Milestone Passed', 2819 eventLabel: this.title 2820 }); 2821 } 2822 2823 sendEvent(event) { 2824 if(window.ga) 2825 window.ga('send', event); 2826 } 2827}let INSTANCE_COUNT = 0; 2828 2829class JWPlayer extends LitElement { 2830 2831 static get properties() { 2832 return { 2833 playerId: String, 2834 playerUrl: String, 2835 playListId: String, 2836 playListUrl: String, 2837 aspectRatio: String, 2838 playList: Object, 2839 mediaId: String, 2840 autoStart: String, 2841 options: Object, 2842 playListId: String, 2843 videoId: String, 2844 mute: Boolean, 2845 title: String, 2846 initialized: Boolean,
2847 controls: Boolean, 2848 withTracking: { 2849 type: Boolean, 2850 reflect: true, 2851 attribute: 'with-tracking' 2852 }, 2853 nostick: Boolean, 2854 ads: Boolean, 2855 adUnit: String, 2856 custTags: String, 2857 custSection: String, 2858 } 2859 } 2860 2861 set options(value) { 2862 if(typeof value == 'string') 2863 value = JSON.parse(value.replace(/\n/g, '\\n')); 2864 this._options = value; 2865 } 2866 2867 get options() { 2868 return this._options 2869 } 2870 2871 set playList(value) { 2872 if(typeof value === 'string') 2873 try { 2874 value = JSON.parse(value.replace(/\n/g, '\\n')); 2875 } catch (ignore) { 2876 /* property playList is defined as Object type so hypothetically we should never have had to parse it */ 2877 } 2878 this.changePlayList(value); 2879 } 2880 2881 get playList() { 2882 return this._playList 2883 } 2884 2885 set playListUrl(value) { 2886 this.changePlayList(value); 2887 } 2888 2889 get playListUrl() { 2890 return ('string' == typeof this._playList) ?this._playList :false 2891 } 2892 2893 set playListId(value) { 2894 this.changePlayList('https://cdn.jwplayer.com/v2/playlists/'+value); 2895 } 2896 2897 get playListId() { 2898 if ('string' != typeof this._playList) 2899 return false 2900 return this._playList.match(/^http:\/\/cdn.jwplayer.com\/v2\/playlists\/(\w+)$/) && RegExp.$1 2901 } 2902 2903 set mediaId(value) { 2904 this.changePlayList(`http://content.jwplatform.com/feeds/${value}.json`); 2905 } 2906 2907 get mediaId() { 2908 if ('string' != typeof this._playList) 2909 return false 2910 return this._playList.match(/^http:\/\/content.jwplatform.com\/feeds\/(\w+).json$/) && RegExp.$1 2911 } 2912 2913 get videoId() { 2914 return this._videoId ? this._videoId : 2915 (this._videoId = this.playerId + '-' + Math.floor(Math.random() * 1e16).toString(32)) 2916 } 2917 2918 changePlayList(playlist) { 2919 var oldPlayList= JSON.stringify(playlist); 2920 if (this.oldPlayList == oldPlayList) { 2921 if (this.jwplayer) this.jwplayer.play(); 2922 } else { 2923 this.oldPlayList= oldPlayList; 2924 this._playList= playlist; 2925 if (this.jwplayer) { 2926 this.jwplayer.remove(); 2927 this.onJWLoaded({target:{onload:null}}); 2928 this.jwplayer.play(); 2929 } 2930 } 2931 } 2932 2933 get playerLib() { 2934 return this.playerUrl ? 2935 this.playerUrl : 2936 `https://cdn.jwplayer.com/libraries/${this.playerId}.js` 2937 } 2938 2939 // get mute() { 2940 // if(!this.player) 2941 // return this.hasAttribute('mute') 2942 // else 2943 // return this.player.getMute() 2944 // } 2945 2946 // set mute(value) { 2947 // if(value == null) 2948 // this.removeAttribute('mute') 2949 // else if(this.player) 2950 // this.player.setMute(value) 2951 // } 2952 2953 constructor() { 2954 super(); 2955 this.oldPlayList = false; 2956 this.options = {}; 2957 this.autoStart = 'viewable'; 2958 this.playerId = 'uD1427Ts'; 2959 this.playListUrl = 'https://cdn.jwplayer.com/v2/playlists/U09OFT24'; 2960 this.aspectRatio = '16 / 9'; 2961 this.controls = true; 2962 this.ads = true; 2963 } 2964 2965 render() { 2966 return html` 2967 <style>${JWPlayerStyle}</style> 2968 <div id=${this.videoId}></div> 2969 ` 2970 } 2971 2972 createRenderRoot() { 2973 return this 2974 } 2975 2976 firstUpdated() { 2977 //TODO: refactor to method and promisify 2978 this.instanceCount = ++INSTANCE_COUNT; 2979 let script = document.createElement('script'); 2980 script.src = this.playerLib; 2981 script.onload = script.onreadystatechange = this.onJWLoaded.bind(this); 2982 this.appendChild(script); 2983 this.style.setProperty('--aspect-ratio', this.aspectRatio); 2984 } 2985 2986 configureAdSchedule(config) { 2987 2988 if (!config.advertising) 2989 return config 2990 if (!config.advertising.schedule) 2991 return config 2992 config.advertising.schedule= config.advertising.schedule.map(sched => { 2993 if (!sched.tag) 2994 return sched 2995 2996 let url= new URL(sched.tag); 2997 url.searchParams.set('description_url', '__page-url__'); 2998 2999 if (this.adUnit) { 3000 url.searchParams.set('iu', this.adUnit); 3001 } 3002 let cust= new URLSearchParams(url.searchParams.get('cust_params')); 3003 if (this.custSection) { 3004 cust.set('Section', this.custSection); 3005 } 3006 cust.set('v_title', ''); 3007 3008 url.searchParams.set('cust_params', cust.toString()); 3009 3010 sched.tag= url.toString(); 3011 return sched 3012 }, this); 3013 return config 3014 } 3015 3016 onJWLoaded(event) { 3017 const ar = this.aspectRatio.replace(/\s*\/\s*/g, ':'); 3018 this.jwplayer= window.jwplayer(this.videoId); 3019 const baseConfig = { 3020 playlist: this.playList, 3021 aspectratio: ar, 3022 advertising: { 3023 maxRedirects: 6, 3024 }, 3025 autostart: this.instanceCount == 1 ? this.autoStart : 'false', 3026 mute: this.hasAttribute('mute'), 3027 title: this.title, 3028 controls: this.controls, 3029 ga: this.withTracking ? { label: 'title' }
3029 : null, 3030 ph: 2, 3031 floating: { 3032 dismissible: true 3033 } 3034 }; 3035 const configList = [ baseConfig, window.jwplayer.defaults||{}, this.options ]; 3036 const config = configList.reduce(this.md(), {}); 3037 if (!this.ads || 'false' == this.ads) 3038 config.advertising= {}; 3039 3040 this.config = this.configureAdSchedule(config); 3041 if(this.hasAttribute('nostick')) { 3042 delete this.config.floating; 3043 delete (window['jwDefaults'] || {}).floating; 3044 } 3045 3046 this.jwplayer.setup(this.config); 3047 event.target.onload = null; 3048 3049 if(this.autoStart && this.autoStart != 'false' && this.autoStart != 'viewable' && this.instanceCount == 1) 3050 this.forceAutoPlay(); 3051 3052 if(this.withTracking) 3053 try { 3054 this.tracker = new GAVideoTracker(this.jwplayer); 3055 } catch (e) { 3056 console.error('GA:',e); 3057 } 3058 } 3059 3060 forceAutoPlay() { 3061 3062 let playFn = () => this.jwplayer.getState() == 'idle' && this.jwplayer.play() && console.log('starting video'); 3063 this.jwplayer.once('breakpoint', playFn); 3064 } 3065 3066 md() { 3067 function isMergeable(y) { 3068 return Array.isArray(y) || ('object'===typeof y && !(y instanceof Date) && !(y instanceof RegExp)) 3069 } 3070 function mergeDefaults(dst = {}, src) { 3071 if (Array.isArray(dst) && Array.isArray(src)) { 3072 src.forEach(function(val, key) { 3073 if ('undefined'===typeof dst[key]) {dst[key]= val;} 3074 else if (isMergeable(val)) {dst[key]= mergeDefaults(dst[key], val);} 3075 else if (-1===dst.indexOf(val)) {dst.push(val);} 3076 }); 3077 } else if (isMergeable(dst)) { 3078 Object.keys(src).forEach(function (key) { 3079 if (!(key in dst)) { 3080 dst[key]= src[key]; 3081 } else if (isMergeable(dst[key])) { 3082 dst[key]= mergeDefaults((dst[key]||{}), src[key]); 3083 } 3084 }); 3085 } 3086 return dst 3087 } 3088 return function(d,s) {return mergeDefaults(d,s)} 3089 } 3090}customElements.define('atype-video-jwplayer', JWPlayer);//# sourceMappingURL=index.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.