Files
olivv-cs-ComfyUI-FunPack/app/ui/composer/internals/el.js
T

67 lines
2.3 KiB
JavaScript

// The only document.createElement in the app.
//
// Everything the kit renders comes through here, which is what makes the
// no-styling rule checkable: a module has no route to a raw element, and the
// scanner only has one construction site to watch.
import { asText } from "./text.js";
const UNSAFE_ATTRS = new Set(["style", "class", "srcdoc", "onload", "onerror"]);
/**
* el("div", { cls, text, attrs, on, children })
*
* `cls` takes kit class names only -- callers inside the kit. `text` goes
* through asText. `attrs` refuses style/class and any inline handler, so no
* amount of prop-forwarding can turn data into presentation or script.
*/
export function el(tag, opts = {}) {
const node = document.createElement(tag);
const { cls, text, attrs, on, children } = opts;
if (cls) node.className = Array.isArray(cls) ? cls.filter(Boolean).join(" ") : cls;
if (text !== undefined) node.appendChild(asText(text));
if (attrs) {
for (const [name, value] of Object.entries(attrs)) {
if (value == null || value === false) continue;
const lower = name.toLowerCase();
if (UNSAFE_ATTRS.has(lower) || lower.startsWith("on")) {
throw new TypeError(`Refusing to set "${name}": presentation and handlers are the kit's, not a caller's.`);
}
node.setAttribute(name, value === true ? "" : String(value));
}
}
if (on) for (const [event, handler] of Object.entries(on)) node.addEventListener(event, handler);
if (children) for (const child of [].concat(children)) if (child) node.appendChild(child);
return node;
}
const SVG_NS = "http://www.w3.org/2000/svg";
/**
* An SVG element. Separate because SVG needs createElementNS -- createElement
* would produce an HTMLUnknownElement that renders as nothing at all.
* Attributes only: SVG carries no module content, so there is no text path here.
*/
export function svg(tag, attrs = {}) {
const node = document.createElementNS(SVG_NS, tag);
for (const [name, value] of Object.entries(attrs)) {
if (value == null || value === false) continue;
node.setAttribute(name, String(value));
}
return node;
}
/** A detached fragment. Panels build into one so a half-built tree never lands. */
export function frag() {
return document.createDocumentFragment();
}
export function clear(node) {
while (node.firstChild) node.removeChild(node.firstChild);
return node;
}