"use strict"; function req(path, options) { const headers = { "Content-Type": "application/json" }; options = Object.assign({ method: "GET", headers }, options || {}); if (options.body && typeof options.body === "object") { options.body = JSON.stringify(options.body); } return fetch(path, options).then(function (r) { return r.json().then(function (data) { if (!r.ok) throw new Error(data.error || "request failed"); return data; }); }); } function postForm(path, form) { const data = {}; for (const [k, v] of new FormData(form)) { if (v) data[k] = v; } return req(path, { method: "POST", body: JSON.stringify(data) }); } function patchJson(path, body) { return req(path, { method: "PATCH", body: body }); } function delReq(path) { return req(path, { method: "DELETE" }); } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function uuid() { if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID(); const a = new Uint8Array(16); (crypto.getRandomValues || function () { for (let i = 0; i < a.length; i++) a[i] = Math.floor(Math.random() * 256); })(a); a[6] = (a[6] & 0x0f) | 0x40; a[8] = (a[8] & 0x3f) | 0x80; const hex = Array.from(a, b => b.toString(16).padStart(2, "0")).join(""); return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20); } function appendMsg(el, message, isError) { el.textContent = message || ""; el.classList.toggle("hidden", !message); el.style.color = isError ? "#dc2626" : "#047857"; }