/* Click-to-zoom lightbox for docs diagrams. Dependency-free. * * Sphinx renders graphviz SVG as , which swallows clicks and * can't be lightboxed. We replace each with an (crisp vector, * clickable), then a click opens a fullscreen overlay with * scroll-to-zoom + drag-to-pan so dense diagrams can be read in detail. * Esc / double-click / background-click closes. Degrades gracefully: * if JS is off, the inline image still renders. */ (function () { function ready(fn) { if (document.readyState !== "loading") fn(); else document.addEventListener("DOMContentLoaded", fn); } ready(function () { // 1. graphviz SVG -> clickable document .querySelectorAll('.graphviz object[type="image/svg+xml"]') .forEach(function (obj) { var img = document.createElement("img"); img.src = obj.getAttribute("data"); img.className = "zoomable"; img.alt = "diagram — click to zoom"; obj.parentNode.replaceChild(img, obj); }); // 2. overlay scaffold var ov = document.createElement("div"); ov.id = "arb-zoom-overlay"; var big = document.createElement("img"); ov.appendChild(big); var hint = document.createElement("div"); hint.id = "arb-zoom-hint"; hint.textContent = "scroll = zoom · drag = pan · Esc / dbl-click = close"; document.body.appendChild(ov); document.body.appendChild(hint); var scale = 1, tx = 0, ty = 0; var dragging = false, moved = false, sx = 0, sy = 0; function apply() { big.style.transform = "translate(" + tx + "px," + ty + "px) scale(" + scale + ")"; } function open(src) { ov.classList.add("open"); big.onload = function () { var s = Math.min( window.innerWidth / big.naturalWidth, window.innerHeight / big.naturalHeight ) * 0.95; scale = s > 0 ? s : 1; tx = (window.innerWidth - big.naturalWidth * scale) / 2; ty = (window.innerHeight - big.naturalHeight * scale) / 2; apply(); }; big.src = src; } function close() { ov.classList.remove("open"); big.src = ""; } document.body.addEventListener("click", function (e) { var t = e.target; if (t && t.classList && t.classList.contains("zoomable")) open(t.src); }); ov.addEventListener( "wheel", function (e) { e.preventDefault(); var f = e.deltaY < 0 ? 1.15 : 1 / 1.15; // zoom toward the cursor tx = e.clientX - (e.clientX - tx) * f; ty = e.clientY - (e.clientY - ty) * f; scale *= f; apply(); }, { passive: false } ); ov.addEventListener("mousedown", function (e) { dragging = true; moved = false; sx = e.clientX - tx; sy = e.clientY - ty; ov.classList.add("grabbing"); }); window.addEventListener("mousemove", function (e) { if (!dragging) return; moved = true; tx = e.clientX - sx; ty = e.clientY - sy; apply(); }); window.addEventListener("mouseup", function () { dragging = false; ov.classList.remove("grabbing"); }); // background click (no drag) closes; dbl-click + Esc close ov.addEventListener("click", function (e) { if (e.target === ov && !moved) close(); }); ov.addEventListener("dblclick", close); document.addEventListener("keydown", function (e) { if (e.key === "Escape") close(); }); }); })();