Files
WRNexusJS/packages/viewer/src/renderers/xml.ts
T

63 lines
2.2 KiB
TypeScript

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);
};