(function () { "use strict"; var slides = Array.prototype.slice.call(document.querySelectorAll("section.slide")); var total = slides.length; var progressEl = document.querySelector(".progress"); var baseTitle = document.title; var renderedMermaid = {}; var fragmentsCache = {}; var current = 1; var fragmentIndex = 0; var gridMode = false; function clamp(n) { if (n < 1) return 1; if (n > total) return total; return n; } // fragmentsFor returns the "fragment" elements of the given 1-based slide // index, in document order, computed and cached once per slide. A slide // marked with data-incremental="true" gets its top-level list items // auto-tagged as fragments in addition to any explicitly marked via raw // HTML (class="fragment"). function fragmentsFor(index) { if (fragmentsCache[index]) return fragmentsCache[index]; var slide = slides[index - 1]; if (!slide) return []; if (slide.dataset.incremental === "true") { var autoItems = slide.querySelectorAll(":scope > ul > li, :scope > ol > li"); Array.prototype.forEach.call(autoItems, function (li) { li.classList.add("fragment"); }); } var frags = Array.prototype.slice.call(slide.querySelectorAll(".fragment")); frags.forEach(function (el, i) { el.setAttribute("data-fragment-index", String(i + 1)); }); fragmentsCache[index] = frags; return frags; } function applyFragments(index, count) { fragmentsFor(index).forEach(function (el, i) { el.classList.toggle("fragment-visible", i < count); }); } function parseHash() { var raw = location.hash.replace("#", ""); var parts = raw.split("."); var slide = parseInt(parts[0], 10); var fragment = parseInt(parts[1], 10); return { slide: isNaN(slide) ? 1 : clamp(slide), fragment: isNaN(fragment) ? 0 : Math.max(0, fragment) }; } function renderMermaid(index) { if (renderedMermaid[index]) return; var slide = slides[index - 1]; if (!slide) return; var nodes = slide.querySelectorAll("pre.mermaid"); if (nodes.length === 0) return; renderedMermaid[index] = true; mermaid.run({ nodes: Array.prototype.slice.call(nodes) }); } function show(index, fragment, updateHash) { current = clamp(index); var frags = fragmentsFor(current); fragmentIndex = typeof fragment === "number" ? Math.min(Math.max(fragment, 0), frags.length) : 0; slides.forEach(function (slide, i) { slide.classList.toggle("active", i === current - 1); }); applyFragments(current, fragmentIndex); if (progressEl) { progressEl.textContent = current + " / " + total; } var slideTitle = slides[current - 1].dataset.title; document.title = slideTitle ? slideTitle + " ยท " + baseTitle : baseTitle; if (updateHash !== false) { location.hash = fragmentIndex > 0 ? current + "." + fragmentIndex : String(current); } renderMermaid(current); } function next() { var frags = fragmentsFor(current); if (fragmentIndex < frags.length) { show(current, fragmentIndex + 1); return; } show(current + 1, 0); } function prev() { if (fragmentIndex > 0) { show(current, fragmentIndex - 1); return; } var target = current - 1; if (target < 1) return; show(target, fragmentsFor(target).length); } function toggleGrid() { gridMode = !gridMode; document.body.classList.toggle("grid-mode", gridMode); } function exitGrid() { if (!gridMode) return; gridMode = false; document.body.classList.remove("grid-mode"); } window.addEventListener("hashchange", function () { var h = parseHash(); show(h.slide, h.fragment, false); }); document.addEventListener("keydown", function (e) { if (e.key === "o" || e.key === "O") { e.preventDefault(); toggleGrid(); return; } if (gridMode) { if (e.key === "Escape") { e.preventDefault(); exitGrid(); } return; } switch (e.key) { case "ArrowRight": case " ": case "PageDown": e.preventDefault(); next(); break; case "ArrowLeft": case "PageUp": e.preventDefault(); prev(); break; case "Home": e.preventDefault(); show(1, 0); break; case "End": e.preventDefault(); show(total, 0); break; } }); document.querySelectorAll(".nav-zone.prev, .nav-button.prev").forEach(function (el) { el.addEventListener("click", prev); }); document.querySelectorAll(".nav-zone.next, .nav-button.next").forEach(function (el) { el.addEventListener("click", next); }); slides.forEach(function (slide, i) { slide.addEventListener("click", function () { if (!gridMode) return; exitGrid(); show(i + 1, 0); }); }); var initial = parseHash(); show(initial.slide, initial.fragment, true); })();