/* Shared helpers: CSRF-aware fetch, confirm dialogs, formatting. */ "use strict"; const PB = (() => { const csrf = document.querySelector('meta[name="csrf-token"]').content; async function api(method, url, body) { const opts = { method, headers: { "X-CSRF-Token": csrf }, credentials: "same-origin" }; if (body !== undefined) { opts.headers["Content-Type"] = "application/json"; opts.body = JSON.stringify(body); } const res = await fetch(url, opts); if (res.status === 401) { location.href = "/login"; throw new Error("not logged in"); } let data = {}; try { data = await res.json(); } catch (e) { /* non-JSON error page */ } if (!res.ok) throw new Error(data.error || res.statusText); return data; } function bytes(n) { const units = ["B", "KB", "MB", "GB", "TB", "PB"]; let i = 0; n = Number(n) || 0; while (Math.abs(n) >= 1000 && i < units.length - 1) { n /= 1000; i++; } return (i === 0 ? n.toFixed(0) : n.toFixed(n < 10 ? 2 : n < 100 ? 1 : 0)) + " " + units[i]; } function rate(bps) { return bytes(bps) + "/s"; } function ago(ts) { if (!ts) return "never"; const s = Math.max(0, Date.now() / 1000 - ts); if (s < 60) return "just now"; if (s < 3600) return Math.floor(s / 60) + " min ago"; if (s < 86400) return Math.floor(s / 3600) + " h ago"; return new Date(ts * 1000).toLocaleString(); } function el(tag, attrs, ...children) { const e = document.createElement(tag); for (const [k, v] of Object.entries(attrs || {})) { if (k === "class") e.className = v; else if (k === "text") e.textContent = v; else e.setAttribute(k, v); } for (const c of children) e.append(c); return e; } // Inline handlers are blocked by the CSP, so confirmations are wired here. document.addEventListener("submit", (ev) => { const msg = ev.target.dataset && ev.target.dataset.confirm; if (msg && !window.confirm(msg)) ev.preventDefault(); }); function fillTimestamps() { document.querySelectorAll("[data-ts]").forEach((n) => { const t = n.querySelector(".ts"); if (t) t.textContent = ago(Number(n.dataset.ts)); }); } document.addEventListener("DOMContentLoaded", fillTimestamps); return { api, bytes, rate, ago, el, csrf }; })();