Files
mueller_minki 8e83a98ef4 Initial Awawawa
2026-10-04 09:08:27 +02:00

72 lines
2.3 KiB
JavaScript

/* 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 };
})();