import { ComponentBase, ComponentState } from "./componentBase"; export type HtmlState = ComponentState & { _type_: "Html-builtin"; html?: string; enable_pointer_events?: boolean; }; export class HtmlComponent extends ComponentBase { declare state: Required; private isInitialized = false; createElement(): HTMLElement { let element = document.createElement("div"); element.classList.add("rio-html"); return element; } runScriptsInElement(): void { for (let oldScriptElement of this.element.querySelectorAll("script")) { // Create a new script element const newScriptElement = document.createElement("script"); // Copy over all attributes for (let i = 0; i < oldScriptElement.attributes.length; i++) { const attr = oldScriptElement.attributes[i]; newScriptElement.setAttribute(attr.name, attr.value); } // And the source itself newScriptElement.appendChild( document.createTextNode(oldScriptElement.innerHTML) ); // Finally replace the old script element with the new one so // the browser executes it oldScriptElement.parentNode!.replaceChild( newScriptElement, oldScriptElement ); } } updateElement( deltaState: HtmlState, latentComponents: Set ): void { super.updateElement(deltaState, latentComponents); if (deltaState.html !== undefined) { // If the HTML hasn't actually changed from last time, don't do // anything. This is important so scripts don't get re-executed each // time the component is updated. if (deltaState.html === this.state.html && this.isInitialized) { return; } if (requiresIframe(deltaState.html)) { this.element.innerHTML = ""; let iframe = document.createElement("iframe"); iframe.srcdoc = deltaState.html; this.element.appendChild(iframe); } else { // Load the HTML this.element.innerHTML = deltaState.html; // Just setting the innerHTML doesn't run scripts. Do that manually. this.runScriptsInElement(); } this.isInitialized = true; } if (deltaState.enable_pointer_events !== undefined) { this.element.style.pointerEvents = deltaState.enable_pointer_events ? "auto" : "none"; } } } function requiresIframe(html: string): boolean { return html.match(/^\s*(])/i) !== null; }