export const $ = (sel, root = document) => root.querySelector(sel); export const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); export function el(tag, attrs = {}, children = []) { const node = document.createElement(tag); Object.entries(attrs).forEach(([k, v]) => { if (k === "class") node.className = v; else if (k.startsWith("on") && typeof v === "function") node.addEventListener(k.slice(2), v); else if (v !== undefined && v !== null) node.setAttribute(k, v); }); for (const ch of Array.isArray(children) ? children : [children]) { if (typeof ch === "string") node.appendChild(document.createTextNode(ch)); else if (ch) node.appendChild(ch); } return node; } export function formToJSON(form) { const data = {}; const coerceNumberIfNeeded = (key, value) => { const field = form.elements.namedItem(key); let isNumber = false; let integerOnly = false; const inspect = (el) => { if (!el) return; if (el.getAttribute && el.getAttribute("data-type") === "number") { isNumber = true; } if (el.getAttribute && el.getAttribute("data-integer") === "true") { isNumber = true; integerOnly = true; } if (el.tagName === "INPUT") { const type = (el.type || "").toLowerCase(); if (type === "number") { isNumber = true; const step = el.step; if ( step && (step === "1" || (!step.includes(".") && step !== "any")) ) { integerOnly = true; } } } }; if ( field && typeof RadioNodeList !== "undefined" && field instanceof RadioNodeList ) { inspect(field[0]); } else { inspect(field); } if (isNumber) { if (value === "" || value === null) return null; const n = Number(value); if (Number.isNaN(n)) return null; return integerOnly ? Math.trunc(n) : n; } return value; }; new FormData(form).forEach((v, k) => { const val = coerceNumberIfNeeded(k, v); if (data[k] !== undefined) { if (!Array.isArray(data[k])) data[k] = [data[k]]; data[k].push(val); } else data[k] = val; }); return data; } export function saveSession(session) { localStorage.setItem("srab.session", JSON.stringify(session)); } export function loadSession() { try { return JSON.parse(localStorage.getItem("srab.session")) || null; } catch { return null; } } export function clearSession() { localStorage.removeItem("srab.session"); } export function setTopUser(text) { $("#top-user").textContent = text || ""; } export function setLogoutVisible(vis) { $("#btn-logout").hidden = !vis; } export function setAvg(text) { const n = $("#top-avg"); n.textContent = text || ""; n.className = "avg badge ok"; } export function fmtDateInput(d = new Date()) { const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); return `${yyyy}-${mm}-${dd}`; }