/* live.f12.rocks — live poll + lightbox gallery. No framework. */ (function () { "use strict"; var POLL_MS = 4000; function $(id) { return document.getElementById(id); } /* --- live reload while jobs run --- */ function fingerprint(payload) { if (!payload) return ""; if (payload.job_id) { return [ payload.status, payload.stage, payload.updated_at, payload.variant_count, (payload.intermediates || []).length, payload.error || "", ].join("|"); } var jobs = payload.jobs || []; return jobs .map(function (j) { return [j.job_id, j.status, j.updated_at, j.variant_count, j.stage].join(":"); }) .join(","); } function startLivePoll() { if (!document.body.hasAttribute("data-live")) return; var jobId = document.body.getAttribute("data-job-id"); var url = jobId ? "/api/jobs/" + encodeURIComponent(jobId) : "/api/jobs"; var last = ""; var first = true; function tick() { fetch(url, { headers: { Accept: "application/json" } }) .then(function (r) { if (!r.ok) throw new Error("poll " + r.status); return r.json(); }) .then(function (data) { var next = fingerprint(data); if (first) { last = next; first = false; return; } if (next !== last) { window.location.reload(); } }) .catch(function () { /* venue wifi: try again next tick */ }); } tick(); setInterval(tick, POLL_MS); } /* --- lightbox with prev/next --- */ function startLightbox() { var box = $("lightbox"); var img = $("lightbox-img"); var caption = $("lightbox-caption"); if (!box || !img) return; var items = []; var index = 0; function collect() { items = Array.prototype.slice .call(document.querySelectorAll("img.lightboxable")) .map(function (el) { return { src: el.getAttribute("data-full-src") || el.currentSrc || el.src, alt: el.alt || "", }; }); } function show(i) { if (!items.length) return; index = (i + items.length) % items.length; img.src = items[index].src; img.alt = items[index].alt; if (caption) caption.textContent = items[index].alt; box.hidden = false; document.body.classList.add("lightbox-open"); } function close() { box.hidden = true; img.removeAttribute("src"); document.body.classList.remove("lightbox-open"); } document.addEventListener("click", function (e) { var target = e.target; if (!(target instanceof HTMLImageElement)) return; if (!target.classList.contains("lightboxable")) return; e.preventDefault(); e.stopPropagation(); collect(); var full = target.getAttribute("data-full-src") || target.currentSrc || target.src; var found = items.findIndex(function (it) { return it.src === full; }); show(found >= 0 ? found : 0); }); box.addEventListener("click", function (e) { if (e.target === box || (e.target && e.target.classList.contains("lightbox-close"))) { close(); return; } if (e.target && e.target.classList.contains("lightbox-next")) { show(index + 1); } if (e.target && e.target.classList.contains("lightbox-prev")) { show(index - 1); } }); document.addEventListener("keydown", function (e) { if (box.hidden) return; if (e.key === "Escape") close(); if (e.key === "ArrowRight") show(index + 1); if (e.key === "ArrowLeft") show(index - 1); }); var startX = 0; box.addEventListener( "touchstart", function (e) { if (e.changedTouches && e.changedTouches[0]) startX = e.changedTouches[0].screenX; }, { passive: true } ); box.addEventListener( "touchend", function (e) { if (!e.changedTouches || !e.changedTouches[0]) return; var dx = e.changedTouches[0].screenX - startX; if (Math.abs(dx) < 40) return; show(index + (dx < 0 ? 1 : -1)); }, { passive: true } ); } startLivePoll(); startLightbox(); })();