// ophour.js: the two things the operations page's hour cannot do as HTML.
//
// The four tiles and the shared hour are both in the page, and its
// data-view attribute says which is shown. The control is a form button
// that reloads with ?hour=, so with no script it still works; here a
// press flips the attribute in place and corrects the address to match,
// so a reload or a shared link shows the same view. The second thing is
// reading a minute: pointing at, or dragging across, the shared hour
// draws one line through all four rows and puts that minute's four
// values in the readout under the title.
//
// Nothing here holds an element. The page streams: every few seconds the
// body is patched, so the listeners sit on the document and find their
// panel when the event happens, and the numbers a minute is read from
// travel in the panel's data-hover, worded by the server.
(function () {
"use strict";
// View. The button that was pressed leaves the screen with its view, so
// focus goes to the button that took its place.
document.addEventListener("click", function (e) {
var btn = e.target.closest && e.target.closest(".ops-hs-f button[name=hour]");
if (!btn || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button) return;
var live = btn.closest("[data-view]");
if (!live) return;
e.preventDefault();
var v = btn.value, open = v !== "0";
live.setAttribute("data-view", open ? "hour" : "tiles");
if (open && v !== "1") live.setAttribute("data-focus", v);
else live.removeAttribute("data-focus");
try {
var u = new URL(location.href);
if (open) u.searchParams.set("hour", v);
else u.searchParams.delete("hour");
history.replaceState(null, "", u.pathname + u.search + u.hash);
} catch (err) { /* a cosmetic address must not stop the toggle */ }
var next = live.querySelector(open ? "[data-hook=ops-compare-back]" : "[data-hook=ops-compare]");
if (next) next.focus({ preventScroll: true });
hide(live.querySelector(".ops-hr"));
});
// Reading a minute.
function dataOf(panel) {
try { return JSON.parse(panel.querySelector(".ops-hr-grid").getAttribute("data-hover")); } catch (e) { return null; }
}
function hide(panel) {
if (!panel) return;
var c = panel.querySelector(".ops-hr-cross");
if (c) { c.hidden = true; delete c.dataset.at; }
}
function show(panel, i) {
var d = dataOf(panel), c = panel.querySelector(".ops-hr-cross"), r = panel.querySelector(".ops-hr-read");
if (!d || !c || !r || !d.at.length) return;
var n = d.at.length;
i = Math.max(0, Math.min(n - 1, i));
c.style.left = ((i + 0.5) / n * 100) + "%";
c.hidden = false;
// The position lives on the crosshair, which patches leave alone; the
// panel itself is rewritten from the server every few seconds.
c.dataset.at = String(i);
r.textContent = d.at[i] + " · " + d.rows.map(function (row) { return row.name + " " + row.v[i]; }).join(" · ");
}
function slot(panel, x) {
var over = panel.querySelector(".ops-hr-over");
var d = dataOf(panel);
if (!over || !d) return -1;
var box = over.getBoundingClientRect();
if (x < box.left || x > box.right || box.width <= 0) return -1;
return Math.floor((x - box.left) / box.width * d.at.length);
}
var pointed = null;
function move(e) {
var grid = e.target.closest && e.target.closest(".ops-hr-grid");
if (!grid) return;
var panel = grid.closest(".ops-hr");
var i = slot(panel, e.clientX);
if (i < 0) { hide(panel); return; }
pointed = panel;
show(panel, i);
}
document.addEventListener("pointermove", move);
document.addEventListener("pointerdown", move);
// A mouse that leaves lets go; a finger that lifts keeps its minute on
// screen, which is the one it was reading.
document.addEventListener("pointerout", function (e) {
if (e.pointerType === "touch" || !pointed) return;
var grid = e.target.closest && e.target.closest(".ops-hr-grid");
if (grid && !grid.contains(e.relatedTarget)) { hide(pointed); pointed = null; }
});
document.addEventListener("keydown", function (e) {
var grid = e.target.closest && e.target.closest(".ops-hr-grid");
if (!grid || e.target !== grid) return;
var panel = grid.closest(".ops-hr"), d = dataOf(panel);
if (!d) return;
var cross = panel.querySelector(".ops-hr-cross");
var at = !cross || cross.dataset.at === undefined ? d.at.length : +cross.dataset.at;
var to = { ArrowLeft: at - 1, ArrowRight: at + 1, Home: 0, End: d.at.length - 1 }[e.key];
if (e.key === "Escape") { hide(panel); return; }
if (to === undefined) return;
e.preventDefault();
show(panel, to);
});
})();