class TramLite {
    static version = "5.3.0";
    static installed = !0;
    // extending HTMLElement so that we can attach shadow root processors
    //   without interfering with the global HTMLElement prototype
    //   (this is most important in the import/export case)
    static ComponentInterface = class extends HTMLElement {};
    /**
	 * utility function to build the component class from the template string
	 * (this is an underlying utility for the define function)
	 */
    static makeComponentClass(t, ...e) {
        var o = document.createElement("template"), a = e.map(t => `tl:${t}:`);
        // tag our templateVariables, so we know how to look for them in the dom
        o.innerHTML = String.raw({
            raw: t
        }, ...a);
        const r = o.content.firstElementChild, i = {}, n = ([ ...r.attributes ].forEach(t => {
            i[t.name] = t.value;
        }), null !== r.querySelector("script[src]"));
        // any attributes on the root are considered default values
        r.querySelectorAll("script[tl-effect]:not([tl-hold])").forEach(t => {
            t.setAttribute("tl-hold", n ? "external-script" : "component-mount");
        });
        // Custom element class with tram-lite template support.
        class s extends TramLite.ComponentInterface {
            static tramLiteVersion = "5.3.0";
            static tagName = r.tagName.toLowerCase();
            static get observedAttributes() {
                // all of the template variables are attributes that we'll update on
                return e;
            }
            constructor() {
                super(), 
                // list of attribute and text nodes that have a template value
                // these are scanned through when templated attributes are updated
                this.templateValuesAttrNodes = [], this.templateValuesTextNodes = [];
                // Create a shadow root
                // and append our HTML to it
                var t = this.attachShadow({
                    mode: "open",
                    delegatesFocus: !0
                });
                t.append(...r.cloneNode(!0).childNodes), 
                // save the original template in templateValuesTextNodes
                ComponentDefinition.getTextNodesWithTramLiteValues(t).forEach(t => {
                    this.templateValuesTextNodes.push({
                        textNode: t,
                        originalTemplate: t.textContent
                    });
                }), 
                // save the original attribute in the templateValuesAttributes
                ComponentDefinition.getElementsWithTramLiteValuesInAttributes(t).forEach(e => {
                    [ ...e.attributes ].forEach(t => {
                        t.value.match(/tl:(.+?):/) && this.templateValuesAttrNodes.push({
                            attrNode: t,
                            element: e,
                            originalTemplate: t.value
                        });
                    });
                });
            }
            connectedCallback() {
                // set all attribute values
                // - the first default value is whatever is set on DOM creation
                // - next, we check if there are default values that were part of the define
                // - lastly, we'll set it to an empty string.
                var t = Object.keys(i);
                [ ...e, ...t ].forEach(t => {
                    null === this.getAttribute(t) && this.setAttribute(t, i[t] || "");
                }), 
                // an initial call to set the default attributes
                this.attributeChangedCallback(), 
                // if there were any scripts that were waiting to be triggered on component mount, trigger them now
                this.shadowRoot.querySelectorAll('script[tl-hold="component-mount"]').forEach(t => {
                    t.removeAttribute("tl-hold");
                });
            }
            attributeChangedCallback(t, e, o) {
                // scan through all text nodes and attributes with template values, and update them
                this.updateTextNodeTemplates(), this.updateAttrNodeTemplates();
            }
            getUpdatedTemplate(t) {
                let o = t;
                return e.forEach(t => {
                    // fallback on the default values or an empty string if there is no value for this attribute yet
                    var e = this.getAttribute(t) || i[t] || "";
                    o = o.replaceAll(`tl:${t}:`, e);
                }), o;
            }
            updateTextNodeTemplates() {
                // go through each text node that has a template variable, and update them
                this.templateValuesTextNodes.forEach(({
                    textNode: t,
                    originalTemplate: e
                }) => {
                    t.textContent = this.getUpdatedTemplate(e);
                });
            }
            updateAttrNodeTemplates() {
                // go through each element with an attribute that has a template variable, and update those attribute values
                this.templateValuesAttrNodes.forEach(({
                    attrNode: t,
                    element: e,
                    originalTemplate: o
                }) => {
                    // set the attribute value to the new value (updated with all template variables)
                    t.value = this.getUpdatedTemplate(o), 
                    // these attributes are special, in order to update the live value (after a user has interacted with them),
                    // they need to be set on the element as well
                    [ "value", "checked", "selected" ].includes(t.name) && (e[t.name] = this.getUpdatedTemplate(o));
                });
            }
        }
        return s;
    }
    /**
	 * a template tag function used to create new web-components.
	 * {@link https://tram-one.io/tram-lite/#define Read the full docs here.}
	 */
    static define(t, ...e) {
        // build the new component class from the template
        t = TramLite.makeComponentClass(t, ...e);
        // register this as a new element as a native web-component
        return customElements.define(t.tagName, t), t;
    }
    /**
	 * function to set up a callback for when an element's attribute changes
	 * {@link https://tram-one.io/tram-lite/#addAttributeListener Read the full docs here.}
	 * @param {Element} targetElement - The DOM element to observe.
	 * @param {string[]} attributeNames - The name of the attribute (or list of attributes) to observe for changes.
	 * @param {function(MutationRecord):void} callback - The function to call when the observed attribute changes.
	 *    This function takes one argument: the MutationRecord representing the change.
	 */
    static addAttributeListener(e, t, o) {
        new MutationObserver(t => {
            t.forEach(t => {
                // only call the mutation if an attribute changed
                t.oldValue !== e.getAttribute(t.attributeName) && o(t);
            });
        }).observe(e, {
            attributes: !0,
            attributeFilter: t,
            attributeOldValue: !0
        });
    }
    /**
	 * function to dispatch events up or down from a host component.
	 * {@link https://tram-one.io/tram-lite/#broadcastEvent Read the full docs here.}
	 * @param {HTMLElement} element - element to associate with the event, and to dispatch from
	 * @param {string} eventName - event name, can be listened for from other elements
	 * @param {'up' | 'down'} eventDirection - dictates which elements should receive the event, parents ('up') or children ('down')
	 */
    static broadcastEvent(t, e, o) {
        var a, r = {
            originalElement: t
        };
        if ("up" === o && (a = new CustomEvent(e, {
            bubbles: !0,
            composed: !0,
            detail: r
        }), t.dispatchEvent(a)), "down" === o) {
            // if we are dispatching an event to child elements, query all child elements,
            //   and dispatch on each one individually
            const i = new CustomEvent(e, {
                bubbles: !1,
                composed: !1,
                detail: r
            });
            [ ...t.shadowRoot.querySelectorAll("*"), ...t.querySelectorAll("*") ].forEach(t => t.dispatchEvent(i));
        }
    }
    /**
	 * function to append new behaviors to elements that are attached to the shadowDOM.
	 * Can be used for Tram-Lite component definitions, and generic Web-Component classes.
	 * {@link https://tram-one.io/tram-lite/#appendShadowRootProcessor Read the full docs here.}
	 * @param {string} matcher - css selectors to match on new elements that are appended to shadowDOM.
	 * @param {{ connect: function }} processorClass - class with a static connect function, which are associated with newly attached nodes.
	 * @param {typeof HTMLElement} [componentInterface=TramLite.ComponentInterface] - Web Component class that we should add this shadowRootProcessor on
	 */
    static appendShadowRootProcessor(o, a, t = TramLite.ComponentInterface) {
        // override attachShadow so that we can add shadowRootProcessors
        const r = t.prototype.attachShadow;
        t.prototype.attachShadow = function(...t) {
            t = r.call(this, ...t);
            // save the original version of shadowRoot.append
            const e = t.append;
            return t.append = function(...t) {
                e.call(this, ...t), 
                // if any element in this shadowRoot matches our matcher,
                //   run the `connect` function from this class
                this.querySelectorAll(o).forEach(t => {
                    t.getRootNode().host && a.connect(t);
                });
            }, t;
        };
    }
}

/**
 * AttrBroadcaster is a class that extends the tl-broadcast custom element, which allows developers
 * to emit events when a component's dependencies update.
 *
 * {@link https://tram-one.io/tram-lite/#tl-broadcast}
 */
class AttrBroadcaster {
    /**
	 * connect function for AttrBroadcaster - when this is attached to an element, we
	 *   set up a listener to for its attributes and send them up or down the DOM
	 * @param {HTMLElement} newNode
	 */
    static connect(t) {
        // attributes that control the behavior of the attr emitter
        var e = t.getAttribute("tl-dependencies").split(" ");
        const o = t.getAttribute("tl-eventname"), a = (t.getAttribute("tl-direction") || "up").split(" "), r = t.getRootNode().host;
        // emit an event whenever these attributes change
        TramLite.addAttributeListener(r, e, () => {
            // add a new attribute listener for each event direction
            a.forEach(t => {
                TramLite.broadcastEvent(r, o, t);
            });
        });
    }
}
// setup shadow root processor so that tl-broadcast that are added are processed correctly

TramLite.appendShadowRootProcessor("tl-broadcast", AttrBroadcaster);

/**
 * ComponentEffect is a class that can extend the script element, that allows developers
 * to build side-effects for web-components.
 *
 * {@link https://tram-one.io/tram-lite/#tl-effect}
 */
class ComponentEffect {
    /**
	 * function to trigger the javascript in a script tag.
	 * This does not handle the src attribute, only inline javascript.
	 * The `this` keyword will reference the host parent node of this script tag.
	 * @param {HTMLScriptElement} scriptTag
	 */
    static processScriptTag(t) {
        // don't do this if we have a hold on the script tag
        if (!t.hasAttribute("tl-hold")) {
            const e = t.getRootNode().host;
            // provide a scoped evaluation of the script tags in this element
            t = t.innerHTML, Function("document", "window", "TramLite", t).bind(e)(e.shadowRoot, window, TramLite);
        }
    }
    /**
	 * connect function for ComponentEffect - when this is run on a script,
	 *   we trigger that script with the host element as context, and set up an
	 *   observer if a set of dependencies are defined and ever update
	 * @param {HTMLScriptElement} newNode
	 */
    static connect(t) {
        var e, o = t.getRootNode().host;
        // run an initial run of the script
        // (this won't happen if there is a tl-hold on the script)
        ComponentEffect.processScriptTag(t), 
        // if we have any dependencies, add a listener to trigger them
        t.hasAttribute("tl-dependencies") && !0 !== t.hasSetupListener && (e = t.getAttribute("tl-dependencies").split(" "), 
        TramLite.addAttributeListener(o, e, () => {
            // check if the inline script is being held
            ComponentEffect.processScriptTag(t);
        }), t.hasSetupListener = !0), 
        // if we ever set (or remove) the hold on this, trigger the inline script
        // (this allows developers to delay triggering inline scripts)
        TramLite.addAttributeListener(t, [ "tl-hold" ], () => {
            ComponentEffect.processScriptTag(t);
        });
    }
}
// setup shadow root processor so that tl-effects that are added are processed correctly

TramLite.appendShadowRootProcessor("[tl-effect]", ComponentEffect);

/**
 * ContextConsumer is a class that extends the tl-context custom element, which allows developers
 * to share state across multiple components under a tl-provider element.
 *
 * {@link https://tram-one.io/tram-lite/#tl-context}
 */
class ContextConsumer {
    /**
	 * connect function for ContextConsumer - when this is attached to an element, we
	 *   set up a binding of attributes on the Provider and the host web-component.
	 * @param {HTMLElement} newNode
	 */
    static connect(t) {
        // attributes that control the behavior of the context consumer
        // tl-name determines the provider we should look up
        const e = t.getAttribute("tl-name"), o = t.getAttribute("tl-attrmap"), i = t.getRootNode().host;
        // tl-attrmap is a mapping of the provider's attributes to this one
        // (by default, this will be a mapping of all the attributes in the provider as the same name)
        // set up the event listener on the top-most context - set it to only trigger once
        window.addEventListener("context-search", 
        // search up the tree for a context provider
        // we do this by attaching an event listener to the top-level html element,
        //   emitting an event from our host element, and then checking the composed path
        //   for the first provider that matches our tl-name
        function(t) {
            const r = t.composedPath().find(t => t.matches && t.matches(`tl-provider[tl-name="${e}"]`));
            // if we can't find a provider, bail
            void 0 !== r && (t = [ ...r.attributes ].filter(t => !t.name.match(/tl-/)).map(t => t.name + ":" + t.name).join(" "), 
            (o || t).split(" ").forEach(t => {
                const [ e, o ] = t.split(":");
                // note the type of the attribute we are tracking
                // (if it is a boolean, we'll just check if this has an attribute)
                const a = "boolean" == typeof r[e];
                // set the value of the host based on the provider
                i.setAttribute(o, a ? r.hasAttribute(e) : r.getAttribute(e)), 
                // update the host whenever the provider attribute updates
                TramLite.addAttributeListener(r, [ e ], () => {
                    i.setAttribute(o, a ? r.hasAttribute(e) : r.getAttribute(e));
                }), 
                // update the provider whenever the host attribute updates
                TramLite.addAttributeListener(i, [ o ], () => {
                    r.setAttribute(e, a ? i.hasAttribute(o) : i.getAttribute(o));
                });
            }));
            // build a default attribute map based on all the attributes on the provider
        }, {
            once: !0
        });
        // dispatch the event from this host all the way up
        t = new CustomEvent("context-search", {
            bubbles: !0,
            composed: !0
        });
        i.dispatchEvent(t);
    }
}
// setup shadow root processor so that tl-context that are added are processed correctly

TramLite.appendShadowRootProcessor("tl-context", ContextConsumer);

/**
 * ControlledInput is a class that can extend the input or script element, that allows developers
 * to build 2-way data-binding for web-components.
 *
 * {@link https://tram-one.io/tram-lite/#tl-controlled}
 */
class ControlledInput {
    /**
	 * connect function for ControlledInput - when this is run on an input (or other similar control),
	 *   we set up a 2-way data binding from the input to the host element.
	 * @param {HTMLElement} newNode
	 */
    static connect(i) {
        const n = (i.getAttribute("tl-trigger") || "change").split(" ");
        (i.getAttribute("tl-attrmap") || "value:value").split(" ").forEach(t => {
            const [ o, a ] = t.split(":");
            // note the type of the attribute we are tracking
            // (if it is a boolean, we'll just check if this has an attribute)
            const r = "boolean" == typeof i[o], e = i.getRootNode().host;
            // set the value of this input based on the host element
            i[o] = r ? e.hasAttribute(a) : e.getAttribute(a), 
            // update this input whenever the host attribute updates
            TramLite.addAttributeListener(e, [ a ], () => {
                i[o] = r ? e.hasAttribute(a) : e.getAttribute(a);
            }), 
            // update the root component attribute whenever the value changes for this node updates
            n.forEach(t => {
                i.addEventListener(t, t => {
                    var e = t.target.getRootNode().host;
                    t.target[o] ? e.setAttribute(a, r ? "" : t.target[o]) : e.removeAttribute(a);
                });
            });
        });
    }
}
// setup shadow root processor so that tl-controlled that are added are processed correctly

TramLite.appendShadowRootProcessor("[tl-controlled]", ControlledInput);

/**
 * EventRebroadcaster is a class that extends other elements with the tl-rebroadcast attribute,
 *  which allows developers to emit events when another event happens
 *
 * {@link https://tram-one.io/tram-lite/#tl-rebroadcast}
 */
class EventRebroadcaster {
    /**
	 * connect function for EventRebroadcaster - when this is attached to an element, we
	 *   set up a listener to for events and rebroadcast them up or down the DOM
	 * @param {HTMLElement} newNode
	 */
    static connect(a) {
        // attributes that control the behavior of the event rebroadcaster
        var t = (a.getAttribute("tl-eventmap") || "change:change").split(" ");
        const r = (a.getAttribute("tl-direction") || "up").split(" "), i = a.getRootNode().host;
        t.forEach(t => {
            const [ e, o ] = t.split(":");
            a.addEventListener(e, () => {
                r.forEach(t => {
                    TramLite.broadcastEvent(i, o, t);
                });
            });
        });
    }
}
// setup shadow root processor so that tl-rebroadcast that are added are processed correctly

TramLite.appendShadowRootProcessor("[tl-rebroadcast]", EventRebroadcaster, TramLite.ComponentInterface);

/**
 * ExternalScript is a class that can extend the script element, that allows developers
 * to load external dependencies
 *
 * Special processing is required here, since all tags in Tram-Lite definitions are copied
 * using innerHTML (which disables script tags). We also do special processing to make sure
 * scripts are available for tl-effects.
 */
class ExternalScript {
    /**
	 * connect function for ExternalScript - this loads all the scripts (with src)
	 *   on the component, and saves the results to be used in other component effects
	 * @param {HTMLScriptElement} newNode
	 */
    static connect(t) {
        const e = t.getRootNode().host;
        if (!0 !== t.hasExecuted) {
            // clone the script tag
            const o = document.createElement("script");
            [ ...t.attributes ].forEach(t => o.setAttribute(t.name, t.value)), 
            // set up load event listener (which will remove the tl-hold for tl-effects)
            o.addEventListener("load", () => {
                o.hasExecuted = !0, [ ...e.shadowRoot.querySelectorAll("script[src]") ].every(t => t.hasExecuted) && e.shadowRoot.querySelectorAll('[tl-hold="external-script"]').forEach(t => {
                    t.removeAttribute("tl-hold");
                });
            }, {
                once: !0
            }), 
            // replace the original script tag with this new one (which will cause it to trigger)
            t.parentNode.replaceChild(o, t);
        }
    }
}
// setup shadow root processor so that tl-effects that are added are processed correctly

TramLite.appendShadowRootProcessor("script[src]", ExternalScript);

/**
 * ComponentDefinition is a custom element that extends the template element, that allows developers
 * to build new web-components, using Tram-Lite, all in their HTML templates!
 *
 * {@link https://tram-one.io/tram-lite/#tl-definition}
 */
class ComponentDefinition {
    // regex for finding attributes that have been templated in
    static templateVariableRegex = /tl:(.+?):/;
    /**
	 * function to test if node has an attribute value with a template variable
	 * e.g. \<custom-element style="color: ${'color'}"\>
	 */
    static nodeHasTramLiteAttr = t => [ ...t.attributes ].some(t => t.value.match(ComponentDefinition.templateVariableRegex)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
    /**
	 * function to test if node has an TEXT node with a template variable
	 * e.g. \<custom-element\>Hello ${'name'}\</custom-element\>
	 */
    static nodeHasTextElementWithTramLiteAttr = t => t.textContent.match(ComponentDefinition.templateVariableRegex) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
    /**
	 * generic function to build a tree walker, and use the filter + tram-lite matcher.
	 * this should return all elements that match the criteria
	 */
    static buildTreeWalkerTramLiteMatcher(t, e, o) {
        for (var a, r = [], i = document.createTreeWalker(t, e, {
            acceptNode: o
        })
        // build a tree walker that goes through each element, and each attribute
        ; a = i.nextNode(); ) r.push(a);
        return r;
    }
    // Returns elements with attributes containing tram-lite template variables.
    static getElementsWithTramLiteValuesInAttributes(t) {
        return ComponentDefinition.buildTreeWalkerTramLiteMatcher(t, NodeFilter.SHOW_ELEMENT, ComponentDefinition.nodeHasTramLiteAttr);
    }
    // Returns text nodes containing tram-lite template variables.
    static getTextNodesWithTramLiteValues(t) {
        return ComponentDefinition.buildTreeWalkerTramLiteMatcher(t, NodeFilter.SHOW_TEXT, ComponentDefinition.nodeHasTextElementWithTramLiteAttr);
    }
    /**
	 * static function to process template tags and define components
	 * @param {HTMLTemplateElement} templateTag
	 */
    static processTemplateDefinition(t) {
        [ ...t.content.children ].forEach(t => {
            var t = t.outerHTML, [ t, e ] = ComponentDefinition.extractTemplateVariables(t);
            TramLite.define(t, ...e);
        });
    }
    /**
	 * utility function to extract js template strings, so that they can be passed into a template tag function
	 */
    static extractTemplateVariables(t) {
        // we expect template variables to be in the following pattern, matching "${'...'}"
        t = t.split(/\$\{\'(.*?)\'\}/);
        // Split the string by the above pattern, which lets us get an alternating list of strings and variables
        return [ t.filter((t, e) => e % 2 == 0), t.filter((t, e) => e % 2 != 0) ];
    }
    /**
	 * function to query and process any templates that already exist in the root document
	 */
    static processExistingTemplates() {
        [ ...document.querySelectorAll("template[tl-definition]") ].forEach(t => {
            ComponentDefinition.processTemplateDefinition(t);
        });
    }
    /**
	 * function to set up an observer to watch for when new templates are added,
	 *   and process all the definitions in them
	 */
    static setupMutationObserverForTemplates() {
        new MutationObserver(t => {
            t.forEach(t => {
                t.addedNodes.forEach(t => {
                    // check if the previous element is a definition template
                    // we wait until we are in the next element (most likely a #text node)
                    // because that will confirm that the element has been completely parsed
                    t.previousSibling?.matches?.("[tl-definition]") && ComponentDefinition.processTemplateDefinition(t.previousSibling);
                });
            });
        }).observe(document, {
            subtree: !0,
            childList: !0
        });
    }
}

// process any existing template definitions (if this was added after-the-fact)
ComponentDefinition.processExistingTemplates(), 
// setup mutation observer so that template elements created will automatically be defined
ComponentDefinition.setupMutationObserverForTemplates();