donau2space-dev/js/utils.js

84 lines
3.2 KiB
JavaScript

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// DONAU2SPACE // DEV ENTITY — Utilities
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
export const $ = (sel, ctx = document) => ctx.querySelector(sel);
export const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];
export const sleep = ms => new Promise(r => setTimeout(r, ms));
export const random = (min, max) => Math.random() * (max - min) + min;
export const randInt = (min, max) => (random(min, max) | 0);
export const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
export const lerp = (a, b, t) => a + (b - a) * t;
export const pick = arr => arr[(Math.random() * arr.length) | 0];
export function esc(s) {
const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' };
return (s ?? '').replace(/[&<>"']/g, m => map[m]);
}
export function nowISO() {
return new Date().toISOString().replace('T', ' ').replace('Z', ' UTC');
}
export function timeGreeting() {
const h = new Date().getHours();
if (h < 5) return 'Deep night. Perfect time for dev.';
if (h < 8) return 'Early bird mode. Coffee recommended.';
if (h < 12) return 'Morning session. Systems nominal.';
if (h < 14) return 'Midday. Hunger may affect judgment.';
if (h < 18) return 'Afternoon cycle. Productivity variable.';
if (h < 22) return 'Evening mode. Creative peak for some.';
return 'Late night. Here be dragons.';
}
// ── Event Bus ──────────────────────────────────
const listeners = {};
export const bus = {
on(event, fn) {
(listeners[event] = listeners[event] || []).push(fn);
return () => this.off(event, fn);
},
off(event, fn) {
listeners[event] = (listeners[event] || []).filter(f => f !== fn);
},
emit(event, data) {
(listeners[event] || []).forEach(fn => {
try { fn(data); } catch (e) { console.warn(`[bus:${event}]`, e); }
});
}
};
// ── Storage helpers ────────────────────────────
const PREFIX = 'd2s_';
export const store = {
get(key, fallback = null) {
try {
const raw = localStorage.getItem(PREFIX + key);
return raw !== null ? JSON.parse(raw) : fallback;
} catch { return fallback; }
},
set(key, val) {
try { localStorage.setItem(PREFIX + key, JSON.stringify(val)); } catch {}
},
remove(key) {
try { localStorage.removeItem(PREFIX + key); } catch {}
}
};
// ── DOM helper ─────────────────────────────────
export function el(tag, attrs = {}, children = []) {
const node = document.createElement(tag);
for (const [k, v] of Object.entries(attrs)) {
if (k === 'className') node.className = v;
else if (k === 'textContent') node.textContent = v;
else if (k.startsWith('on')) node.addEventListener(k.slice(2).toLowerCase(), v);
else node.setAttribute(k, v);
}
for (const c of children) {
node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
}
return node;
}