| (async function () { |
| "use strict"; |
| const manifest = await fetch("logbook.json", { cache: "no-store" }).then(r => r.json()); |
| const nodes = [manifest.root].concat(manifest.root.children || []); |
| const nav = document.getElementById("nav"); |
| const content = document.getElementById("content"); |
|
|
| function inline(text) { |
| return text |
| .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") |
| .replace(/`([^`]+)`/g, "<code>$1</code>") |
| .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>"); |
| } |
|
|
| function markdown(source) { |
| const lines = source.split("\n"); |
| let html = "", paragraph = [], i = 0; |
| const flush = () => { if (paragraph.length) { html += `<p>${inline(paragraph.join(" "))}</p>`; paragraph = []; } }; |
| while (i < lines.length) { |
| const line = lines[i], t = line.trim(); |
| if (!t) { flush(); i++; continue; } |
| if (t.startsWith("```")) { |
| flush(); const body = []; i++; |
| while (i < lines.length && !lines[i].trim().startsWith("```")) body.push(lines[i++]); |
| html += `<pre><code>${inline(body.join("\n"))}</code></pre>`; i++; continue; |
| } |
| const h = t.match(/^(#{1,3})\s+(.*)$/); |
| if (h) { flush(); html += `<h${h[1].length}>${inline(h[2])}</h${h[1].length}>`; i++; continue; } |
| if (t.startsWith("> ")) { flush(); html += `<blockquote>${inline(t.slice(2))}</blockquote>`; i++; continue; } |
| if (t.startsWith("- ")) { |
| flush(); const items = []; |
| while (i < lines.length && lines[i].trim().startsWith("- ")) items.push(`<li>${inline(lines[i++].trim().slice(2))}</li>`); |
| html += `<ul>${items.join("")}</ul>`; continue; |
| } |
| if (t.startsWith("|") && i + 1 < lines.length && lines[i + 1].includes("---")) { |
| flush(); const rows = []; |
| while (i < lines.length && lines[i].trim().startsWith("|")) rows.push(lines[i++].trim().slice(1, -1).split("|").map(x => x.trim())); |
| const head = rows[0], body = rows.slice(2); |
| html += `<table><thead><tr>${head.map(x => `<th>${inline(x)}</th>`).join("")}</tr></thead><tbody>${body.map(row => `<tr>${row.map(x => `<td>${inline(x)}</td>`).join("")}</tr>`).join("")}</tbody></table>`; continue; |
| } |
| paragraph.push(t); i++; |
| } |
| flush(); return html; |
| } |
|
|
| async function show(node) { |
| document.querySelectorAll("nav a").forEach(a => a.classList.toggle("active", a.dataset.slug === node.slug)); |
| const text = await fetch(node.file, { cache: "no-store" }).then(r => r.text()); |
| content.innerHTML = markdown(text); |
| history.replaceState(null, "", `#${node.slug}`); |
| window.scrollTo(0, 0); |
| } |
|
|
| nodes.forEach(node => { |
| const link = document.createElement("a"); |
| link.href = `#${node.slug}`; link.dataset.slug = node.slug; link.textContent = node.title; |
| link.onclick = event => { event.preventDefault(); show(node); }; |
| nav.appendChild(link); |
| }); |
| const initial = nodes.find(node => `#${node.slug}` === location.hash) || manifest.root; |
| await show(initial); |
| })(); |
|
|