The page's script
One script for every page, sent as a shared file. It runs the mode switch and each code run's fold, brings back the scroll position after a live reload, and marks the section being read.
It's a classic script, deferred, so it runs once the page is parsed. Each part sits in its own block, since classic scripts on one page share their top-level names. Browser storage can be unavailable, so it only ever tries it.
#The mode and the code runs
Prose & Code shows every code run open, and Prose only shows every one folded. Opening
or closing a run marks it as an exception to the mode, and switching the mode clears the
exceptions. The mode is remembered across pages. mode.js applies it before the first paint.
{
const root = document.documentElement;
const runs = [...document.querySelectorAll(".code")];
const isOpen = (run) =>
root.classList.contains("prose-only")
? run.classList.contains("opened")
: !run.classList.contains("closed");
const sync = () => {
const proseOnly = root.classList.contains("prose-only");
for (const b of document.querySelectorAll("[data-mode]")) {
b.setAttribute("aria-pressed", String((b.dataset.mode === "prose") === proseOnly));
}
for (const run of runs) {
run.querySelector(".code-head").setAttribute("aria-expanded", String(isOpen(run)));
}
};
for (const b of document.querySelectorAll("[data-mode]")) {
b.addEventListener("click", () => {
const proseOnly = b.dataset.mode === "prose";
root.classList.toggle("prose-only", proseOnly);
for (const run of runs) run.classList.remove("opened", "closed");
try {
localStorage.setItem("prose:mode", proseOnly ? "prose" : "code");
} catch {}
sync();
});
}
for (const run of runs) {
run.querySelector(".code-head").addEventListener("click", () => {
run.classList.toggle(root.classList.contains("prose-only") ? "opened" : "closed");
sync();
});
}
sync();
}
// `live.js` saves the scroll position under this key before it reloads the page.
{
const key = "prose:scroll:" + location.pathname;
try {
const y = sessionStorage.getItem(key);
if (y !== null) {
sessionStorage.removeItem(key);
scrollTo(0, Number(y));
}
} catch {}
}#The section being read
The contents mark the section being read as the page scrolls. That's the last heading whose top has passed a line a little under the bar. Choosing a heading from the folded contents folds them again, so the text it goes to isn't pushed down.
{
const links = [...document.querySelectorAll(".toc a")];
const idOf = (a) => decodeURIComponent(a.hash.slice(1));
const targets = [...new Set(links.map(idOf))]
.map((id) => document.getElementById(id))
.filter(Boolean);
let frame = 0;
const mark = () => {
frame = 0;
const line = document.querySelector(".bar").offsetHeight + innerHeight * 0.15;
let current = targets[0]?.id;
for (const t of targets) {
if (t.getBoundingClientRect().top > line) break;
current = t.id;
}
for (const a of links) a.classList.toggle("here", idOf(a) === current);
};
if (targets.length) {
addEventListener(
"scroll",
() => {
frame ||= requestAnimationFrame(mark);
},
{ passive: true },
);
mark();
}
for (const a of document.querySelectorAll(".toc-top a")) {
a.addEventListener("click", () => {
a.closest("details").open = false;
});
}
}