/* Utility condivise tra le pagine (galassia e classifiche) */ "use strict"; /* ---------------- utility base ---------------- */ function esc(s) { return String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); } function num(v) { return (v === null || v === undefined) ? "—" : Number(v).toLocaleString("it"); } function hashStr(s) { let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0; return Math.abs(h); } async function api(path, opts) { const r = await fetch(path, opts); let data = null; try { data = await r.json(); } catch (_) { /* noop */ } if (!r.ok) throw new Error((data && data.error) || ("Errore HTTP " + r.status)); return data; } /* ---------------- icone SVG (window.ICONS da icons.js) ---------------- */ function icon(name, size = 16) { const it = (window.ICONS || {})[name]; if (!it) return ""; return ``; } /* ---------------- avatar pianeti/lune (SVG) ---------------- */ let _svgSeq = 0; const PLANET_PAL = [ ["#ffd9a0", "#e0762c"], ["#f2b3a0", "#b3422f"], ["#ffb1c0", "#a0275a"], ["#9fd8ff", "#2a6fc4"], ["#8fe8c0", "#1e7a5f"], ["#d3b8ff", "#6d3fc4"], ["#f5e3a0", "#c2a22e"], ["#c3d0e2", "#5c6f8c"], ]; const LUNA_PAL = [["#e6e6e6", "#9a9a9a"]]; function artSVG(seedKey, size, moon = false) { const seed = hashStr(seedKey) || 1; const r = 15; // viewBox 32 const pal = (moon ? LUNA_PAL : PLANET_PAL)[seed % (moon ? 1 : PLANET_PAL.length)]; const id = "g" + (++_svgSeq); let extras = ""; if (moon) { for (let i = 0; i < 4; i++) { const cx = 7 + ((seed >> (i * 3)) % 18); const cy = 7 + ((seed >> (i * 5 + 2)) % 18); const cr = 2 + ((seed >> (i * 2 + 1)) % 3); extras += `` + ``; } } else { for (let i = 0; i < 3; i++) { const cx = 8 + ((seed >> (i * 4)) % 14); const cy = 6 + ((seed >> (i * 3 + 1)) % 14); extras += ``; } } return `` + `` + `` + `` + `` + extras + `` + `` + ``; } /* ---------------- sfondo stellare ---------------- */ function initStars() { const cv = document.getElementById("space"); if (!cv) return; let ctx = null; try { ctx = cv.getContext("2d"); } catch (_) { /* noop */ } if (!ctx) return; // mai bloccare il resto dell'app let stars = [], tw = []; function resize() { cv.width = innerWidth; cv.height = innerHeight; stars = []; tw = []; const n = Math.floor((cv.width * cv.height) / 6500); for (let i = 0; i < n; i++) { stars.push({ x: Math.random() * cv.width, y: Math.random() * cv.height, r: Math.random() * 1.1 + .25, a: Math.random() * .5 + .2 }); } for (let i = 0; i < 26; i++) { tw.push({ x: Math.random() * cv.width, y: Math.random() * cv.height, r: Math.random() * 1.2 + .4, p: Math.random() * Math.PI * 2, s: .8 + Math.random() }); } } resize(); addEventListener("resize", resize); (function draw(t) { ctx.clearRect(0, 0, cv.width, cv.height); for (const s of stars) { ctx.globalAlpha = s.a; ctx.fillStyle = "#cfe0ff"; ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, 7); ctx.fill(); } const k = t / 1000; for (const s of tw) { ctx.globalAlpha = .25 + .6 * Math.abs(Math.sin(k * s.s + s.p)); ctx.fillStyle = "#fff"; ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, 7); ctx.fill(); } ctx.globalAlpha = 1; requestAnimationFrame(draw); })(performance.now()); }