/* Small JSON cache on top of localStorage (all calls are failure-safe: the cache is only an optimisation). */ (function () { const cfg = window.ICV_CONFIG, P = "icv:" + cfg.CACHE_VERSION + ":"; const hash = (s) => { let h = 2166136261; for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); } return (h >>> 0).toString(36) + s.length.toString(36); }; const get = (key) => { try { const v = JSON.parse(localStorage.getItem(P + key)); return v ? v.d : null; } catch (e) { return null; } }; function set(key, data) { try { localStorage.setItem(P + key, JSON.stringify({ t: Date.now(), d: data })); const keys = Object.keys(localStorage).filter((k) => k.startsWith(P + "r:")); if (keys.length > cfg.CACHE_MAX_ENTRIES) { // drop the oldest results keys.map((k) => [k, (JSON.parse(localStorage.getItem(k)) || {}).t || 0]).sort((a, b) => a[1] - b[1]) .slice(0, keys.length - cfg.CACHE_MAX_ENTRIES).forEach(([k]) => localStorage.removeItem(k)); } } catch (e) { /* quota or private mode */ } } function purgeOld() { try { Object.keys(localStorage).filter((k) => k.startsWith("icv:") && !k.startsWith(P)).forEach((k) => localStorage.removeItem(k)); } catch (e) {} } async function json(url) { // stale-while-revalidate for small JSON files such as the demo examples const cached = get("j:" + url); const fresh = fetch(url).then((r) => r.json()).then((d) => { set("j:" + url, d); return d; }); if (cached) { fresh.catch(() => {}); return cached; } return fresh; } purgeOld(); window.icvCache = { hash, get, set, json }; })();