pboProperty Ontology
javascript87 lines2.4 KB
RawDownload
1/* Lazily render Mermaid diagrams in Markdown pages as they scroll into view.
2 Loaded only on pages that contain at least one ```mermaid fence. */
3import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
4
5const isDark = () => {
6 const t = document.documentElement.dataset.theme;
7 if (t) return t === 'dark';
8 return window.matchMedia('(prefers-color-scheme: dark)').matches;
9};
10
11function configure() {
12 mermaid.initialize({
13 startOnLoad: false,
14 securityLevel: 'strict',
15 theme: isDark() ? 'dark' : 'neutral',
16 themeVariables: { fontFamily: 'Inter, Segoe UI, system-ui, sans-serif' },
17 flowchart: { useMaxWidth: true, htmlLabels: true },
18 maxTextSize: 200000,
19 });
20}
21configure();
22
23const blocks = Array.from(document.querySelectorAll('pre.mermaid'));
24let counter = 0;
25
26async function renderBlock(pre) {
27 if (pre.dataset.processed) return;
28 pre.dataset.processed = 'rendering';
29 const src = pre.dataset.src || pre.textContent;
30 const id = `mmd-${++counter}`;
31 try {
32 const { svg, bindFunctions } = await mermaid.render(id, src);
33 pre.innerHTML = svg;
34 if (bindFunctions) bindFunctions(pre);
35 pre.dataset.processed = 'done';
36 } catch (err) {
37 pre.dataset.processed = 'error';
38 const msg = document.createElement('div');
39 msg.className = 'mermaid-error';
40 msg.textContent = `Diagram failed to render: ${err && err.message ? err.message : err}`;
41 pre.textContent = src;
42 pre.parentElement.insertBefore(msg, pre);
43 // mermaid leaves a stray element behind on error
44 const stray = document.getElementById('d' + id);
45 if (stray) stray.remove();
46 }
47}
48
49const queue = [];
50let running = false;
51async function pump() {
52 if (running) return;
53 running = true;
54 while (queue.length) {
55 const pre = queue.shift();
56 // eslint-disable-next-line no-await-in-loop
57 await renderBlock(pre);
58 }
59 running = false;
60}
61
62const io = new IntersectionObserver(
63 (entries) => {
64 for (const e of entries) {
65 if (e.isIntersecting) {
66 io.unobserve(e.target);
67 queue.push(e.target);
68 }
69 }
70 pump();
71 },
72 { rootMargin: '600px 0px' },
73);
74blocks.forEach((b) => io.observe(b));
75
76// Re-render everything already drawn when the theme flips.
77document.addEventListener('themechange', () => {
78 configure();
79 counter += 1000;
80 blocks
81 .filter((b) => b.dataset.processed === 'done')
82 .forEach((b) => {
83 delete b.dataset.processed;
84 queue.push(b);
85 });
86 pump();
87});