Loading…
See More

: the image, then its caption in , // opening with the slide it came from. Promote that to a real

, with the // caption in a footer and the slide number in the plate's top edge. function plate(p) { const imgs = p.querySelectorAll("img"); if (!imgs.length) return; const fig = document.createElement("figure"); fig.className = "plate"; const art = document.createElement("div"); art.className = "plate-art"; // a figure can be a set of views: slide 13 and slide 19 each show two imgs.forEach((i) => art.appendChild(i)); fig.appendChild(art); const em = p.querySelector("em"); if (em) { const cap = document.createElement("figcaption"); const parts = em.innerHTML.match(/^\s*(Slides?[^—]*?)\s*—\s*([\s\S]+)$/); cap.innerHTML = parts ? parts[2] : em.innerHTML; if (parts) { const no = document.createElement("span"); no.className = "plate-no"; no.textContent = parts[1].replace(/\s+/g, " "); fig.insertBefore(no, art); } fig.appendChild(cap); } p.replaceWith(fig); } let marks = []; function buildOutline() { outline.textContent = ""; marks = [...doc.querySelectorAll("h2, h3, h4")].map((h) => { const a = document.createElement("a"); a.className = h.tagName.toLowerCase(); a.textContent = h.textContent.replace(/\s+/g, " ").trim(); a.href = `#${h.id}`; a.onclick = (e) => { e.preventDefault(); h.scrollIntoView({ behavior: "smooth" }); history.replaceState(null, "", docUrl(current, h.id)); }; outline.appendChild(a); return { h, a }; }); spy(); } // Highlight the heading the reader is currently under, and show progress. function spy() { let cur = marks[0]; for (const m of marks) { if (m.h.getBoundingClientRect().top <= 90) cur = m; else break; } for (const m of marks) m.a.classList.toggle("here", m === cur); const max = document.documentElement.scrollHeight - innerHeight; bar.style.width = `${max > 0 ? Math.min(100, (scrollY / max) * 100) : 0}%`; } let queued = false; addEventListener("scroll", () => { if (queued || !marks.length) return; queued = true; requestAnimationFrame(() => { queued = false; spy(); }); }, { passive: true }); async function load(path, section = "") { err.textContent = ""; current = path; docsNav.querySelectorAll("a").forEach((a) => a.toggleAttribute("aria-current", a.dataset.path === path)); try { const r = await fetch(`${path}?t=${Date.now()}`); if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); render(await r.text(), path.replace(/[^/]*$/, "")); } catch (e) { doc.textContent = ""; outline.textContent = ""; err.textContent = `Could not load ${path}\n${e.message}\n\n` + `Serve the repository root: python3 -m http.server`; return; } history.replaceState(null, "", docUrl(path, section)); // The content is injected after page load, so the browser cannot scroll to a // fragment on its own. const anchor = section && document.getElementById(section); if (anchor) anchor.scrollIntoView(); else scrollTo(0, 0); spy(); } for (const [label, path] of DOCS) { const a = document.createElement("a"); a.textContent = label; a.href = docUrl(path); a.dataset.path = path; a.onclick = (e) => { e.preventDefault(); load(path); }; docsNav.appendChild(a); } let startDoc = new URLSearchParams(location.search).get("doc"); let startSection = decodeURIComponent(location.hash.slice(1)); // Links written before the document moved into the query string put the // document path in the hash. Keep those working. if (!startDoc && isDoc(startSection)) { startDoc = startSection; startSection = ""; } load(isDoc(startDoc) ? startDoc : DOCS[0][1], startSection);