import { elementFactory, requireFile, setButtonLabel } from "./dom.ts"; import { branch, leaf } from "./tree.ts"; import type { ViewerMount } from "../types.ts"; function label(node: Element): string { const attributes = [...node.attributes] .map((attribute) => `${attribute.name}="${attribute.value}"`) .join(" "); return `<${node.tagName}${attributes ? ` ${attributes}` : ""}>`; } function render(doc: Document, node: Element, depth: number): HTMLElement { const children = [...node.childNodes].filter( (child) => child.nodeType === 1 || (child.textContent ?? "").trim(), ); const elements = children.filter((child) => child.nodeType === 1) as Element[]; if (!elements.length) return leaf(doc, label(node), (node.textContent ?? "").trim(), "wrn-viewer-value"); return branch( doc, label(node), "wrn-viewer-key", (into) => { for (const child of children) into.append( child.nodeType === 1 ? render(doc, child as Element, depth + 1) : leaf(doc, "", (child.textContent ?? "").trim(), "wrn-viewer-value"), ); }, depth < 2, ); } export const mountXml: ViewerMount = async ({ parent, file, button, signal }) => { const doc = parent.ownerDocument; const view = doc.defaultView; const element = elementFactory(doc); const text = await requireFile(file).text(); if (signal.aborted) return; if (!view?.DOMParser) throw new Error("No XML parser available"); const parsed = new view.DOMParser().parseFromString(text, "application/xml"); const root = parsed.documentElement; // Malformed XML throws, and the ladder falls through to the source view. if (!root || parsed.getElementsByTagName("parsererror").length) throw new Error("Malformed XML document"); const tree = element("div"); tree.className = "wrn-viewer-tree"; tree.append(render(doc, root, 0)); const raw = element("pre", text); let showingRaw = false; const toggle = button("Raw", () => { showingRaw = !showingRaw; setButtonLabel(toggle, showingRaw ? "Tree" : "Raw"); toggle.setAttribute("aria-pressed", String(showingRaw)); parent.replaceChildren(showingRaw ? raw : tree); }); toggle.setAttribute("aria-pressed", "false"); parent.append(tree); };