Initial Awawawa
This commit is contained in:
71
web/patchbay_web/static/common.js
Normal file
71
web/patchbay_web/static/common.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/* 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 };
|
||||
})();
|
||||
Reference in New Issue
Block a user