Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/strata/assets/diagram.js

Raw
(() => {
	const MAX_SOURCE_LENGTH = 12000;
	const MAX_HEIGHT = 4000;
	const expectedOrigin = document.documentElement.dataset.parentOrigin;
	const root = document.querySelector("#diagram");
	let generation = 0;
	let running = false;
	let pending = null;

	function send(message) {
		window.parent.postMessage(message, expectedOrigin);
	}

	function reportSize() {
		const height = Math.max(
			80,
			Math.min(MAX_HEIGHT, Math.ceil(document.documentElement.scrollHeight)),
		);
		send({ type: "strata.diagram.size", height });
	}

	function sanitize(svg) {
		const documentNode = new DOMParser().parseFromString(svg, "image/svg+xml");
		const svgNode = documentNode.documentElement;
		if (
			svgNode.localName !== "svg" ||
			documentNode.querySelector("parsererror")
		)
			return null;
		for (const node of svgNode.querySelectorAll(
			"script, foreignObject, iframe, object, embed, audio, video, image, animate, animateMotion, animateTransform, set, a",
		))
			node.remove();
		for (const element of [svgNode, ...svgNode.querySelectorAll("*")]) {
			if (element.namespaceURI !== "http://www.w3.org/2000/svg") return null;
			if (
				element.localName === "style" &&
				/@(?!keyframes\b)|url\s*\(|expression\s*\(|\\/iu.test(
					element.textContent,
				)
			)
				return null;
			for (const attribute of [...element.attributes]) {
				const name = attribute.name.toLowerCase();
				const value = attribute.value.trim().toLowerCase();
				if (
					name.startsWith("on") ||
					name === "xml:base" ||
					(name.endsWith("href") && !/^#[\w.-]+$/u.test(value))
				)
					return null;
				const withoutLocalReferences = value.replace(
					/url\(\s*["']?#[\w.-]+["']?\s*\)/giu,
					"",
				);
				if (/url\s*\(|expression\s*\(|\\/iu.test(withoutLocalReferences))
					return null;
			}
		}
		return svgNode.outerHTML;
	}

	async function render(source, dark, requestGeneration) {
		try {
			mermaid.initialize({
				startOnLoad: false,
				securityLevel: "strict",
				htmlLabels: false,
				maxTextSize: MAX_SOURCE_LENGTH,
				maxEdges: 500,
				theme: dark ? "dark" : "default",
			});
			const rendered = await mermaid.render(
				`strata-diagram-${requestGeneration}`,
				source,
			);
			if (requestGeneration !== generation) return;
			const svg = sanitize(rendered.svg);
			if (!svg) throw new Error("unsafe diagram output");
			root.innerHTML = svg;
			reportSize();
		} catch {
			if (requestGeneration !== generation) return;
			root.textContent = "Diagram unavailable.";
			send({ type: "strata.diagram.error" });
			reportSize();
		}
	}

	async function drain() {
		if (running) return;
		running = true;
		try {
			while (pending) {
				const next = pending;
				pending = null;
				await render(next.source, next.dark, next.generation);
			}
		} finally {
			running = false;
		}
	}

	window.addEventListener("message", (event) => {
		if (event.source !== window.parent || event.origin !== expectedOrigin)
			return;
		const data = event.data;
		if (
			!data ||
			typeof data !== "object" ||
			data.type !== "strata.diagram.render" ||
			typeof data.source !== "string" ||
			typeof data.dark !== "boolean" ||
			Object.keys(data).length !== 3
		)
			return;
		try {
			const source = validateDiagram(data.source);
			generation += 1;
			pending = { source, dark: data.dark, generation };
			void drain();
		} catch {
			generation += 1;
			pending = null;
			root.textContent = "Diagram unavailable.";
			send({ type: "strata.diagram.error" });
			reportSize();
		}
	});

	send({ type: "strata.diagram.ready" });
})();