` element.
445 | */
446 | getHTML() {
447 | const l = this.strings.length - 1;
448 | let html = '';
449 | let isCommentBinding = false;
450 | for (let i = 0; i < l; i++) {
451 | const s = this.strings[i];
452 | // For each binding we want to determine the kind of marker to insert
453 | // into the template source before it's parsed by the browser's HTML
454 | // parser. The marker type is based on whether the expression is in an
455 | // attribute, text, or comment poisition.
456 | // * For node-position bindings we insert a comment with the marker
457 | // sentinel as its text content, like .
458 | // * For attribute bindings we insert just the marker sentinel for the
459 | // first binding, so that we support unquoted attribute bindings.
460 | // Subsequent bindings can use a comment marker because multi-binding
461 | // attributes must be quoted.
462 | // * For comment bindings we insert just the marker sentinel so we don't
463 | // close the comment.
464 | //
465 | // The following code scans the template source, but is *not* an HTML
466 | // parser. We don't need to track the tree structure of the HTML, only
467 | // whether a binding is inside a comment, and if not, if it appears to be
468 | // the first binding in an attribute.
469 | const commentOpen = s.lastIndexOf('', commentOpen + 1) === -1;
475 | // Check to see if we have an attribute-like sequence preceeding the
476 | // expression. This can match "name=value" like structures in text,
477 | // comments, and attribute values, so there can be false-positives.
478 | const attributeMatch = lastAttributeNameRegex.exec(s);
479 | if (attributeMatch === null) {
480 | // We're only in this branch if we don't have a attribute-like
481 | // preceeding sequence. For comments, this guards against unusual
482 | // attribute values like . Cases like
483 | // are handled correctly in the attribute branch
484 | // below.
485 | html += s + (isCommentBinding ? commentMarker : nodeMarker);
486 | }
487 | else {
488 | // For attributes we use just a marker sentinel, and also append a
489 | // $lit$ suffix to the name to opt-out of attribute-specific parsing
490 | // that IE and Edge do for style and certain SVG attributes.
491 | html += s.substr(0, attributeMatch.index) + attributeMatch[1] +
492 | attributeMatch[2] + boundAttributeSuffix + attributeMatch[3] +
493 | marker;
494 | }
495 | }
496 | html += this.strings[l];
497 | return html;
498 | }
499 | getTemplateElement() {
500 | const template = document.createElement('template');
501 | template.innerHTML = this.getHTML();
502 | return template;
503 | }
504 | }
505 |
506 | /**
507 | * @license
508 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
509 | * This code may only be used under the BSD style license found at
510 | * http://polymer.github.io/LICENSE.txt
511 | * The complete set of authors may be found at
512 | * http://polymer.github.io/AUTHORS.txt
513 | * The complete set of contributors may be found at
514 | * http://polymer.github.io/CONTRIBUTORS.txt
515 | * Code distributed by Google as part of the polymer project is also
516 | * subject to an additional IP rights grant found at
517 | * http://polymer.github.io/PATENTS.txt
518 | */
519 | const isPrimitive = (value) => {
520 | return (value === null ||
521 | !(typeof value === 'object' || typeof value === 'function'));
522 | };
523 | const isIterable = (value) => {
524 | return Array.isArray(value) ||
525 | // tslint:disable-next-line:no-any
526 | !!(value && value[Symbol.iterator]);
527 | };
528 | /**
529 | * Writes attribute values to the DOM for a group of AttributeParts bound to a
530 | * single attibute. The value is only set once even if there are multiple parts
531 | * for an attribute.
532 | */
533 | class AttributeCommitter {
534 | constructor(element, name, strings) {
535 | this.dirty = true;
536 | this.element = element;
537 | this.name = name;
538 | this.strings = strings;
539 | this.parts = [];
540 | for (let i = 0; i < strings.length - 1; i++) {
541 | this.parts[i] = this._createPart();
542 | }
543 | }
544 | /**
545 | * Creates a single part. Override this to create a differnt type of part.
546 | */
547 | _createPart() {
548 | return new AttributePart(this);
549 | }
550 | _getValue() {
551 | const strings = this.strings;
552 | const l = strings.length - 1;
553 | let text = '';
554 | for (let i = 0; i < l; i++) {
555 | text += strings[i];
556 | const part = this.parts[i];
557 | if (part !== undefined) {
558 | const v = part.value;
559 | if (isPrimitive(v) || !isIterable(v)) {
560 | text += typeof v === 'string' ? v : String(v);
561 | }
562 | else {
563 | for (const t of v) {
564 | text += typeof t === 'string' ? t : String(t);
565 | }
566 | }
567 | }
568 | }
569 | text += strings[l];
570 | return text;
571 | }
572 | commit() {
573 | if (this.dirty) {
574 | this.dirty = false;
575 | this.element.setAttribute(this.name, this._getValue());
576 | }
577 | }
578 | }
579 | /**
580 | * A Part that controls all or part of an attribute value.
581 | */
582 | class AttributePart {
583 | constructor(committer) {
584 | this.value = undefined;
585 | this.committer = committer;
586 | }
587 | setValue(value) {
588 | if (value !== noChange && (!isPrimitive(value) || value !== this.value)) {
589 | this.value = value;
590 | // If the value is a not a directive, dirty the committer so that it'll
591 | // call setAttribute. If the value is a directive, it'll dirty the
592 | // committer if it calls setValue().
593 | if (!isDirective(value)) {
594 | this.committer.dirty = true;
595 | }
596 | }
597 | }
598 | commit() {
599 | while (isDirective(this.value)) {
600 | const directive = this.value;
601 | this.value = noChange;
602 | directive(this);
603 | }
604 | if (this.value === noChange) {
605 | return;
606 | }
607 | this.committer.commit();
608 | }
609 | }
610 | /**
611 | * A Part that controls a location within a Node tree. Like a Range, NodePart
612 | * has start and end locations and can set and update the Nodes between those
613 | * locations.
614 | *
615 | * NodeParts support several value types: primitives, Nodes, TemplateResults,
616 | * as well as arrays and iterables of those types.
617 | */
618 | class NodePart {
619 | constructor(options) {
620 | this.value = undefined;
621 | this.__pendingValue = undefined;
622 | this.options = options;
623 | }
624 | /**
625 | * Appends this part into a container.
626 | *
627 | * This part must be empty, as its contents are not automatically moved.
628 | */
629 | appendInto(container) {
630 | this.startNode = container.appendChild(createMarker());
631 | this.endNode = container.appendChild(createMarker());
632 | }
633 | /**
634 | * Inserts this part after the `ref` node (between `ref` and `ref`'s next
635 | * sibling). Both `ref` and its next sibling must be static, unchanging nodes
636 | * such as those that appear in a literal section of a template.
637 | *
638 | * This part must be empty, as its contents are not automatically moved.
639 | */
640 | insertAfterNode(ref) {
641 | this.startNode = ref;
642 | this.endNode = ref.nextSibling;
643 | }
644 | /**
645 | * Appends this part into a parent part.
646 | *
647 | * This part must be empty, as its contents are not automatically moved.
648 | */
649 | appendIntoPart(part) {
650 | part.__insert(this.startNode = createMarker());
651 | part.__insert(this.endNode = createMarker());
652 | }
653 | /**
654 | * Inserts this part after the `ref` part.
655 | *
656 | * This part must be empty, as its contents are not automatically moved.
657 | */
658 | insertAfterPart(ref) {
659 | ref.__insert(this.startNode = createMarker());
660 | this.endNode = ref.endNode;
661 | ref.endNode = this.startNode;
662 | }
663 | setValue(value) {
664 | this.__pendingValue = value;
665 | }
666 | commit() {
667 | while (isDirective(this.__pendingValue)) {
668 | const directive = this.__pendingValue;
669 | this.__pendingValue = noChange;
670 | directive(this);
671 | }
672 | const value = this.__pendingValue;
673 | if (value === noChange) {
674 | return;
675 | }
676 | if (isPrimitive(value)) {
677 | if (value !== this.value) {
678 | this.__commitText(value);
679 | }
680 | }
681 | else if (value instanceof TemplateResult) {
682 | this.__commitTemplateResult(value);
683 | }
684 | else if (value instanceof Node) {
685 | this.__commitNode(value);
686 | }
687 | else if (isIterable(value)) {
688 | this.__commitIterable(value);
689 | }
690 | else if (value === nothing) {
691 | this.value = nothing;
692 | this.clear();
693 | }
694 | else {
695 | // Fallback, will render the string representation
696 | this.__commitText(value);
697 | }
698 | }
699 | __insert(node) {
700 | this.endNode.parentNode.insertBefore(node, this.endNode);
701 | }
702 | __commitNode(value) {
703 | if (this.value === value) {
704 | return;
705 | }
706 | this.clear();
707 | this.__insert(value);
708 | this.value = value;
709 | }
710 | __commitText(value) {
711 | const node = this.startNode.nextSibling;
712 | value = value == null ? '' : value;
713 | // If `value` isn't already a string, we explicitly convert it here in case
714 | // it can't be implicitly converted - i.e. it's a symbol.
715 | const valueAsString = typeof value === 'string' ? value : String(value);
716 | if (node === this.endNode.previousSibling &&
717 | node.nodeType === 3 /* Node.TEXT_NODE */) {
718 | // If we only have a single text node between the markers, we can just
719 | // set its value, rather than replacing it.
720 | // TODO(justinfagnani): Can we just check if this.value is primitive?
721 | node.data = valueAsString;
722 | }
723 | else {
724 | this.__commitNode(document.createTextNode(valueAsString));
725 | }
726 | this.value = value;
727 | }
728 | __commitTemplateResult(value) {
729 | const template = this.options.templateFactory(value);
730 | if (this.value instanceof TemplateInstance &&
731 | this.value.template === template) {
732 | this.value.update(value.values);
733 | }
734 | else {
735 | // Make sure we propagate the template processor from the TemplateResult
736 | // so that we use its syntax extension, etc. The template factory comes
737 | // from the render function options so that it can control template
738 | // caching and preprocessing.
739 | const instance = new TemplateInstance(template, value.processor, this.options);
740 | const fragment = instance._clone();
741 | instance.update(value.values);
742 | this.__commitNode(fragment);
743 | this.value = instance;
744 | }
745 | }
746 | __commitIterable(value) {
747 | // For an Iterable, we create a new InstancePart per item, then set its
748 | // value to the item. This is a little bit of overhead for every item in
749 | // an Iterable, but it lets us recurse easily and efficiently update Arrays
750 | // of TemplateResults that will be commonly returned from expressions like:
751 | // array.map((i) => html`${i}`), by reusing existing TemplateInstances.
752 | // If _value is an array, then the previous render was of an
753 | // iterable and _value will contain the NodeParts from the previous
754 | // render. If _value is not an array, clear this part and make a new
755 | // array for NodeParts.
756 | if (!Array.isArray(this.value)) {
757 | this.value = [];
758 | this.clear();
759 | }
760 | // Lets us keep track of how many items we stamped so we can clear leftover
761 | // items from a previous render
762 | const itemParts = this.value;
763 | let partIndex = 0;
764 | let itemPart;
765 | for (const item of value) {
766 | // Try to reuse an existing part
767 | itemPart = itemParts[partIndex];
768 | // If no existing part, create a new one
769 | if (itemPart === undefined) {
770 | itemPart = new NodePart(this.options);
771 | itemParts.push(itemPart);
772 | if (partIndex === 0) {
773 | itemPart.appendIntoPart(this);
774 | }
775 | else {
776 | itemPart.insertAfterPart(itemParts[partIndex - 1]);
777 | }
778 | }
779 | itemPart.setValue(item);
780 | itemPart.commit();
781 | partIndex++;
782 | }
783 | if (partIndex < itemParts.length) {
784 | // Truncate the parts array so _value reflects the current state
785 | itemParts.length = partIndex;
786 | this.clear(itemPart && itemPart.endNode);
787 | }
788 | }
789 | clear(startNode = this.startNode) {
790 | removeNodes(this.startNode.parentNode, startNode.nextSibling, this.endNode);
791 | }
792 | }
793 | /**
794 | * Implements a boolean attribute, roughly as defined in the HTML
795 | * specification.
796 | *
797 | * If the value is truthy, then the attribute is present with a value of
798 | * ''. If the value is falsey, the attribute is removed.
799 | */
800 | class BooleanAttributePart {
801 | constructor(element, name, strings) {
802 | this.value = undefined;
803 | this.__pendingValue = undefined;
804 | if (strings.length !== 2 || strings[0] !== '' || strings[1] !== '') {
805 | throw new Error('Boolean attributes can only contain a single expression');
806 | }
807 | this.element = element;
808 | this.name = name;
809 | this.strings = strings;
810 | }
811 | setValue(value) {
812 | this.__pendingValue = value;
813 | }
814 | commit() {
815 | while (isDirective(this.__pendingValue)) {
816 | const directive = this.__pendingValue;
817 | this.__pendingValue = noChange;
818 | directive(this);
819 | }
820 | if (this.__pendingValue === noChange) {
821 | return;
822 | }
823 | const value = !!this.__pendingValue;
824 | if (this.value !== value) {
825 | if (value) {
826 | this.element.setAttribute(this.name, '');
827 | }
828 | else {
829 | this.element.removeAttribute(this.name);
830 | }
831 | this.value = value;
832 | }
833 | this.__pendingValue = noChange;
834 | }
835 | }
836 | /**
837 | * Sets attribute values for PropertyParts, so that the value is only set once
838 | * even if there are multiple parts for a property.
839 | *
840 | * If an expression controls the whole property value, then the value is simply
841 | * assigned to the property under control. If there are string literals or
842 | * multiple expressions, then the strings are expressions are interpolated into
843 | * a string first.
844 | */
845 | class PropertyCommitter extends AttributeCommitter {
846 | constructor(element, name, strings) {
847 | super(element, name, strings);
848 | this.single =
849 | (strings.length === 2 && strings[0] === '' && strings[1] === '');
850 | }
851 | _createPart() {
852 | return new PropertyPart(this);
853 | }
854 | _getValue() {
855 | if (this.single) {
856 | return this.parts[0].value;
857 | }
858 | return super._getValue();
859 | }
860 | commit() {
861 | if (this.dirty) {
862 | this.dirty = false;
863 | // tslint:disable-next-line:no-any
864 | this.element[this.name] = this._getValue();
865 | }
866 | }
867 | }
868 | class PropertyPart extends AttributePart {
869 | }
870 | // Detect event listener options support. If the `capture` property is read
871 | // from the options object, then options are supported. If not, then the thrid
872 | // argument to add/removeEventListener is interpreted as the boolean capture
873 | // value so we should only pass the `capture` property.
874 | let eventOptionsSupported = false;
875 | try {
876 | const options = {
877 | get capture() {
878 | eventOptionsSupported = true;
879 | return false;
880 | }
881 | };
882 | // tslint:disable-next-line:no-any
883 | window.addEventListener('test', options, options);
884 | // tslint:disable-next-line:no-any
885 | window.removeEventListener('test', options, options);
886 | }
887 | catch (_e) {
888 | }
889 | class EventPart {
890 | constructor(element, eventName, eventContext) {
891 | this.value = undefined;
892 | this.__pendingValue = undefined;
893 | this.element = element;
894 | this.eventName = eventName;
895 | this.eventContext = eventContext;
896 | this.__boundHandleEvent = (e) => this.handleEvent(e);
897 | }
898 | setValue(value) {
899 | this.__pendingValue = value;
900 | }
901 | commit() {
902 | while (isDirective(this.__pendingValue)) {
903 | const directive = this.__pendingValue;
904 | this.__pendingValue = noChange;
905 | directive(this);
906 | }
907 | if (this.__pendingValue === noChange) {
908 | return;
909 | }
910 | const newListener = this.__pendingValue;
911 | const oldListener = this.value;
912 | const shouldRemoveListener = newListener == null ||
913 | oldListener != null &&
914 | (newListener.capture !== oldListener.capture ||
915 | newListener.once !== oldListener.once ||
916 | newListener.passive !== oldListener.passive);
917 | const shouldAddListener = newListener != null && (oldListener == null || shouldRemoveListener);
918 | if (shouldRemoveListener) {
919 | this.element.removeEventListener(this.eventName, this.__boundHandleEvent, this.__options);
920 | }
921 | if (shouldAddListener) {
922 | this.__options = getOptions(newListener);
923 | this.element.addEventListener(this.eventName, this.__boundHandleEvent, this.__options);
924 | }
925 | this.value = newListener;
926 | this.__pendingValue = noChange;
927 | }
928 | handleEvent(event) {
929 | if (typeof this.value === 'function') {
930 | this.value.call(this.eventContext || this.element, event);
931 | }
932 | else {
933 | this.value.handleEvent(event);
934 | }
935 | }
936 | }
937 | // We copy options because of the inconsistent behavior of browsers when reading
938 | // the third argument of add/removeEventListener. IE11 doesn't support options
939 | // at all. Chrome 41 only reads `capture` if the argument is an object.
940 | const getOptions = (o) => o &&
941 | (eventOptionsSupported ?
942 | { capture: o.capture, passive: o.passive, once: o.once } :
943 | o.capture);
944 |
945 | /**
946 | * @license
947 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
948 | * This code may only be used under the BSD style license found at
949 | * http://polymer.github.io/LICENSE.txt
950 | * The complete set of authors may be found at
951 | * http://polymer.github.io/AUTHORS.txt
952 | * The complete set of contributors may be found at
953 | * http://polymer.github.io/CONTRIBUTORS.txt
954 | * Code distributed by Google as part of the polymer project is also
955 | * subject to an additional IP rights grant found at
956 | * http://polymer.github.io/PATENTS.txt
957 | */
958 | /**
959 | * Creates Parts when a template is instantiated.
960 | */
961 | class DefaultTemplateProcessor {
962 | /**
963 | * Create parts for an attribute-position binding, given the event, attribute
964 | * name, and string literals.
965 | *
966 | * @param element The element containing the binding
967 | * @param name The attribute name
968 | * @param strings The string literals. There are always at least two strings,
969 | * event for fully-controlled bindings with a single expression.
970 | */
971 | handleAttributeExpressions(element, name, strings, options) {
972 | const prefix = name[0];
973 | if (prefix === '.') {
974 | const committer = new PropertyCommitter(element, name.slice(1), strings);
975 | return committer.parts;
976 | }
977 | if (prefix === '@') {
978 | return [new EventPart(element, name.slice(1), options.eventContext)];
979 | }
980 | if (prefix === '?') {
981 | return [new BooleanAttributePart(element, name.slice(1), strings)];
982 | }
983 | const committer = new AttributeCommitter(element, name, strings);
984 | return committer.parts;
985 | }
986 | /**
987 | * Create parts for a text-position binding.
988 | * @param templateFactory
989 | */
990 | handleTextExpression(options) {
991 | return new NodePart(options);
992 | }
993 | }
994 | const defaultTemplateProcessor = new DefaultTemplateProcessor();
995 |
996 | /**
997 | * @license
998 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
999 | * This code may only be used under the BSD style license found at
1000 | * http://polymer.github.io/LICENSE.txt
1001 | * The complete set of authors may be found at
1002 | * http://polymer.github.io/AUTHORS.txt
1003 | * The complete set of contributors may be found at
1004 | * http://polymer.github.io/CONTRIBUTORS.txt
1005 | * Code distributed by Google as part of the polymer project is also
1006 | * subject to an additional IP rights grant found at
1007 | * http://polymer.github.io/PATENTS.txt
1008 | */
1009 | /**
1010 | * The default TemplateFactory which caches Templates keyed on
1011 | * result.type and result.strings.
1012 | */
1013 | function templateFactory(result) {
1014 | let templateCache = templateCaches.get(result.type);
1015 | if (templateCache === undefined) {
1016 | templateCache = {
1017 | stringsArray: new WeakMap(),
1018 | keyString: new Map()
1019 | };
1020 | templateCaches.set(result.type, templateCache);
1021 | }
1022 | let template = templateCache.stringsArray.get(result.strings);
1023 | if (template !== undefined) {
1024 | return template;
1025 | }
1026 | // If the TemplateStringsArray is new, generate a key from the strings
1027 | // This key is shared between all templates with identical content
1028 | const key = result.strings.join(marker);
1029 | // Check if we already have a Template for this key
1030 | template = templateCache.keyString.get(key);
1031 | if (template === undefined) {
1032 | // If we have not seen this key before, create a new Template
1033 | template = new Template(result, result.getTemplateElement());
1034 | // Cache the Template for this key
1035 | templateCache.keyString.set(key, template);
1036 | }
1037 | // Cache all future queries for this TemplateStringsArray
1038 | templateCache.stringsArray.set(result.strings, template);
1039 | return template;
1040 | }
1041 | const templateCaches = new Map();
1042 |
1043 | /**
1044 | * @license
1045 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
1046 | * This code may only be used under the BSD style license found at
1047 | * http://polymer.github.io/LICENSE.txt
1048 | * The complete set of authors may be found at
1049 | * http://polymer.github.io/AUTHORS.txt
1050 | * The complete set of contributors may be found at
1051 | * http://polymer.github.io/CONTRIBUTORS.txt
1052 | * Code distributed by Google as part of the polymer project is also
1053 | * subject to an additional IP rights grant found at
1054 | * http://polymer.github.io/PATENTS.txt
1055 | */
1056 | const parts = new WeakMap();
1057 | /**
1058 | * Renders a template result or other value to a container.
1059 | *
1060 | * To update a container with new values, reevaluate the template literal and
1061 | * call `render` with the new result.
1062 | *
1063 | * @param result Any value renderable by NodePart - typically a TemplateResult
1064 | * created by evaluating a template tag like `html` or `svg`.
1065 | * @param container A DOM parent to render to. The entire contents are either
1066 | * replaced, or efficiently updated if the same result type was previous
1067 | * rendered there.
1068 | * @param options RenderOptions for the entire render tree rendered to this
1069 | * container. Render options must *not* change between renders to the same
1070 | * container, as those changes will not effect previously rendered DOM.
1071 | */
1072 | const render = (result, container, options) => {
1073 | let part = parts.get(container);
1074 | if (part === undefined) {
1075 | removeNodes(container, container.firstChild);
1076 | parts.set(container, part = new NodePart(Object.assign({ templateFactory }, options)));
1077 | part.appendInto(container);
1078 | }
1079 | part.setValue(result);
1080 | part.commit();
1081 | };
1082 |
1083 | /**
1084 | * @license
1085 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
1086 | * This code may only be used under the BSD style license found at
1087 | * http://polymer.github.io/LICENSE.txt
1088 | * The complete set of authors may be found at
1089 | * http://polymer.github.io/AUTHORS.txt
1090 | * The complete set of contributors may be found at
1091 | * http://polymer.github.io/CONTRIBUTORS.txt
1092 | * Code distributed by Google as part of the polymer project is also
1093 | * subject to an additional IP rights grant found at
1094 | * http://polymer.github.io/PATENTS.txt
1095 | */
1096 | // IMPORTANT: do not change the property name or the assignment expression.
1097 | // This line will be used in regexes to search for lit-html usage.
1098 | // TODO(justinfagnani): inject version number at build time
1099 | (window['litHtmlVersions'] || (window['litHtmlVersions'] = [])).push('1.1.2');
1100 | /**
1101 | * Interprets a template literal as an HTML template that can efficiently
1102 | * render to and update a container.
1103 | */
1104 | const html = (strings, ...values) => new TemplateResult(strings, values, 'html', defaultTemplateProcessor);
1105 |
1106 | /**
1107 | * @license
1108 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
1109 | * This code may only be used under the BSD style license found at
1110 | * http://polymer.github.io/LICENSE.txt
1111 | * The complete set of authors may be found at
1112 | * http://polymer.github.io/AUTHORS.txt
1113 | * The complete set of contributors may be found at
1114 | * http://polymer.github.io/CONTRIBUTORS.txt
1115 | * Code distributed by Google as part of the polymer project is also
1116 | * subject to an additional IP rights grant found at
1117 | * http://polymer.github.io/PATENTS.txt
1118 | */
1119 | const walkerNodeFilter = 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */;
1120 | /**
1121 | * Removes the list of nodes from a Template safely. In addition to removing
1122 | * nodes from the Template, the Template part indices are updated to match
1123 | * the mutated Template DOM.
1124 | *
1125 | * As the template is walked the removal state is tracked and
1126 | * part indices are adjusted as needed.
1127 | *
1128 | * div
1129 | * div#1 (remove) <-- start removing (removing node is div#1)
1130 | * div
1131 | * div#2 (remove) <-- continue removing (removing node is still div#1)
1132 | * div
1133 | * div <-- stop removing since previous sibling is the removing node (div#1,
1134 | * removed 4 nodes)
1135 | */
1136 | function removeNodesFromTemplate(template, nodesToRemove) {
1137 | const { element: { content }, parts } = template;
1138 | const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
1139 | let partIndex = nextActiveIndexInTemplateParts(parts);
1140 | let part = parts[partIndex];
1141 | let nodeIndex = -1;
1142 | let removeCount = 0;
1143 | const nodesToRemoveInTemplate = [];
1144 | let currentRemovingNode = null;
1145 | while (walker.nextNode()) {
1146 | nodeIndex++;
1147 | const node = walker.currentNode;
1148 | // End removal if stepped past the removing node
1149 | if (node.previousSibling === currentRemovingNode) {
1150 | currentRemovingNode = null;
1151 | }
1152 | // A node to remove was found in the template
1153 | if (nodesToRemove.has(node)) {
1154 | nodesToRemoveInTemplate.push(node);
1155 | // Track node we're removing
1156 | if (currentRemovingNode === null) {
1157 | currentRemovingNode = node;
1158 | }
1159 | }
1160 | // When removing, increment count by which to adjust subsequent part indices
1161 | if (currentRemovingNode !== null) {
1162 | removeCount++;
1163 | }
1164 | while (part !== undefined && part.index === nodeIndex) {
1165 | // If part is in a removed node deactivate it by setting index to -1 or
1166 | // adjust the index as needed.
1167 | part.index = currentRemovingNode !== null ? -1 : part.index - removeCount;
1168 | // go to the next active part.
1169 | partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
1170 | part = parts[partIndex];
1171 | }
1172 | }
1173 | nodesToRemoveInTemplate.forEach((n) => n.parentNode.removeChild(n));
1174 | }
1175 | const countNodes = (node) => {
1176 | let count = (node.nodeType === 11 /* Node.DOCUMENT_FRAGMENT_NODE */) ? 0 : 1;
1177 | const walker = document.createTreeWalker(node, walkerNodeFilter, null, false);
1178 | while (walker.nextNode()) {
1179 | count++;
1180 | }
1181 | return count;
1182 | };
1183 | const nextActiveIndexInTemplateParts = (parts, startIndex = -1) => {
1184 | for (let i = startIndex + 1; i < parts.length; i++) {
1185 | const part = parts[i];
1186 | if (isTemplatePartActive(part)) {
1187 | return i;
1188 | }
1189 | }
1190 | return -1;
1191 | };
1192 | /**
1193 | * Inserts the given node into the Template, optionally before the given
1194 | * refNode. In addition to inserting the node into the Template, the Template
1195 | * part indices are updated to match the mutated Template DOM.
1196 | */
1197 | function insertNodeIntoTemplate(template, node, refNode = null) {
1198 | const { element: { content }, parts } = template;
1199 | // If there's no refNode, then put node at end of template.
1200 | // No part indices need to be shifted in this case.
1201 | if (refNode === null || refNode === undefined) {
1202 | content.appendChild(node);
1203 | return;
1204 | }
1205 | const walker = document.createTreeWalker(content, walkerNodeFilter, null, false);
1206 | let partIndex = nextActiveIndexInTemplateParts(parts);
1207 | let insertCount = 0;
1208 | let walkerIndex = -1;
1209 | while (walker.nextNode()) {
1210 | walkerIndex++;
1211 | const walkerNode = walker.currentNode;
1212 | if (walkerNode === refNode) {
1213 | insertCount = countNodes(node);
1214 | refNode.parentNode.insertBefore(node, refNode);
1215 | }
1216 | while (partIndex !== -1 && parts[partIndex].index === walkerIndex) {
1217 | // If we've inserted the node, simply adjust all subsequent parts
1218 | if (insertCount > 0) {
1219 | while (partIndex !== -1) {
1220 | parts[partIndex].index += insertCount;
1221 | partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
1222 | }
1223 | return;
1224 | }
1225 | partIndex = nextActiveIndexInTemplateParts(parts, partIndex);
1226 | }
1227 | }
1228 | }
1229 |
1230 | /**
1231 | * @license
1232 | * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
1233 | * This code may only be used under the BSD style license found at
1234 | * http://polymer.github.io/LICENSE.txt
1235 | * The complete set of authors may be found at
1236 | * http://polymer.github.io/AUTHORS.txt
1237 | * The complete set of contributors may be found at
1238 | * http://polymer.github.io/CONTRIBUTORS.txt
1239 | * Code distributed by Google as part of the polymer project is also
1240 | * subject to an additional IP rights grant found at
1241 | * http://polymer.github.io/PATENTS.txt
1242 | */
1243 | // Get a key to lookup in `templateCaches`.
1244 | const getTemplateCacheKey = (type, scopeName) => `${type}--${scopeName}`;
1245 | let compatibleShadyCSSVersion = true;
1246 | if (typeof window.ShadyCSS === 'undefined') {
1247 | compatibleShadyCSSVersion = false;
1248 | }
1249 | else if (typeof window.ShadyCSS.prepareTemplateDom === 'undefined') {
1250 | console.warn(`Incompatible ShadyCSS version detected. ` +
1251 | `Please update to at least @webcomponents/webcomponentsjs@2.0.2 and ` +
1252 | `@webcomponents/shadycss@1.3.1.`);
1253 | compatibleShadyCSSVersion = false;
1254 | }
1255 | /**
1256 | * Template factory which scopes template DOM using ShadyCSS.
1257 | * @param scopeName {string}
1258 | */
1259 | const shadyTemplateFactory = (scopeName) => (result) => {
1260 | const cacheKey = getTemplateCacheKey(result.type, scopeName);
1261 | let templateCache = templateCaches.get(cacheKey);
1262 | if (templateCache === undefined) {
1263 | templateCache = {
1264 | stringsArray: new WeakMap(),
1265 | keyString: new Map()
1266 | };
1267 | templateCaches.set(cacheKey, templateCache);
1268 | }
1269 | let template = templateCache.stringsArray.get(result.strings);
1270 | if (template !== undefined) {
1271 | return template;
1272 | }
1273 | const key = result.strings.join(marker);
1274 | template = templateCache.keyString.get(key);
1275 | if (template === undefined) {
1276 | const element = result.getTemplateElement();
1277 | if (compatibleShadyCSSVersion) {
1278 | window.ShadyCSS.prepareTemplateDom(element, scopeName);
1279 | }
1280 | template = new Template(result, element);
1281 | templateCache.keyString.set(key, template);
1282 | }
1283 | templateCache.stringsArray.set(result.strings, template);
1284 | return template;
1285 | };
1286 | const TEMPLATE_TYPES = ['html', 'svg'];
1287 | /**
1288 | * Removes all style elements from Templates for the given scopeName.
1289 | */
1290 | const removeStylesFromLitTemplates = (scopeName) => {
1291 | TEMPLATE_TYPES.forEach((type) => {
1292 | const templates = templateCaches.get(getTemplateCacheKey(type, scopeName));
1293 | if (templates !== undefined) {
1294 | templates.keyString.forEach((template) => {
1295 | const { element: { content } } = template;
1296 | // IE 11 doesn't support the iterable param Set constructor
1297 | const styles = new Set();
1298 | Array.from(content.querySelectorAll('style')).forEach((s) => {
1299 | styles.add(s);
1300 | });
1301 | removeNodesFromTemplate(template, styles);
1302 | });
1303 | }
1304 | });
1305 | };
1306 | const shadyRenderSet = new Set();
1307 | /**
1308 | * For the given scope name, ensures that ShadyCSS style scoping is performed.
1309 | * This is done just once per scope name so the fragment and template cannot
1310 | * be modified.
1311 | * (1) extracts styles from the rendered fragment and hands them to ShadyCSS
1312 | * to be scoped and appended to the document
1313 | * (2) removes style elements from all lit-html Templates for this scope name.
1314 | *
1315 | * Note,