/** * Kiosk template loader + renderer (display-only). * Espone window.kiosk con: loadTemplate, applyInline, patchBox, addBox, removeBox, updateValue. */ (function () { const COLS = 24, ROWS = 18; const canvasEl = document.getElementById('canvas'); const chip = document.getElementById('statusChip'); const state = { template: null, boxes: [], // array con .el attaccato byPath: new Map(), // path → Set sensorCode: null, sensorName: null, apiUrl: null, }; function setStatus(msg, err) { chip.textContent = msg; chip.classList.toggle('err', !!err); } function indexPaths() { state.byPath.clear(); for (const b of state.boxes) { if (!b.path) continue; if (!state.byPath.has(b.path)) state.byPath.set(b.path, new Set()); state.byPath.get(b.path).add(b); } } function renderBox(b) { const uw = canvasEl.clientWidth / COLS; const uh = canvasEl.clientHeight / ROWS; b.el.style.left = (b.x * uw) + 'px'; b.el.style.top = (b.y * uh) + 'px'; b.el.style.width = (b.w * uw) + 'px'; b.el.style.height = (b.h * uh) + 'px'; b.el.style.background = b.color || '#1e293b'; b.el.style.color = b.textColor || '#fff'; const titleEl = b.el.querySelector('.title'); const valEl = b.el.querySelector('.val'); titleEl.textContent = b.title || (b.path ? b.path.split('.').pop() : ''); // adatta font-size al box valEl.style.fontSize = Math.min(b.w * uw, b.h * uh) * 0.35 + 'px'; if (b._lastVal !== undefined) renderValue(b, b._lastVal); else valEl.innerHTML = ''; } function renderValue(b, value) { const valEl = b.el.querySelector('.val'); if (value == null || (typeof value === 'object' && !('longitude' in value))) { valEl.innerHTML = ''; return; } let v = value; if (typeof v === 'number') { const mul = typeof b.multiplier === 'number' ? b.multiplier : 1; const dec = typeof b.decimals === 'number' ? b.decimals : 1; v = (v * mul).toFixed(dec); } else if (v && typeof v === 'object' && 'longitude' in v) { v = v.latitude.toFixed(3) + ', ' + v.longitude.toFixed(3); } valEl.innerHTML = String(v) + (b.unit ? `${b.unit}` : ''); } function createBoxEl() { const el = document.createElement('div'); el.className = 'box'; el.innerHTML = '
'; canvasEl.appendChild(el); return el; } function clearAll() { for (const b of state.boxes) b.el.remove(); state.boxes = []; state.byPath.clear(); } function applyContent(content) { clearAll(); if (content?.background) document.body.style.background = content.background; for (const raw of content?.boxes || []) { const b = { ...raw, el: createBoxEl() }; state.boxes.push(b); renderBox(b); } indexPaths(); } async function loadTemplate(templateId) { try { const url = templateId ? `${state.apiUrl}/kiosk/templates/${templateId}` : `${state.apiUrl}/kiosk/template/active`; const r = await fetch(url); if (!r.ok) { setStatus('no template (' + r.status + ')', true); return null; } const tpl = await r.json(); state.template = tpl; applyContent(tpl.content); setStatus('template ' + tpl.name); return tpl; } catch (e) { setStatus('fetch error', true); return null; } } function patchBox(boxId, patch) { const b = state.boxes.find(x => x.id === boxId); if (!b) return false; Object.assign(b, patch); indexPaths(); renderBox(b); return true; } function addBox(boxDef) { const b = { ...boxDef, el: createBoxEl() }; state.boxes.push(b); renderBox(b); indexPaths(); return true; } function removeBox(boxId) { const i = state.boxes.findIndex(b => b.id === boxId); if (i < 0) return false; state.boxes[i].el.remove(); state.boxes.splice(i, 1); indexPaths(); return true; } function applyInline(content) { applyContent(content); return true; } function updateValue(path, value) { const set = state.byPath.get(path); if (!set) return; for (const b of set) { b._lastVal = value; renderValue(b, value); } } function init({ apiUrl, sensorCode, sensorName }) { state.apiUrl = apiUrl; state.sensorCode = sensorCode; state.sensorName = sensorName; } function currentTemplateId() { return state.template?.id || null; } let resizeRaf; window.addEventListener('resize', () => { cancelAnimationFrame(resizeRaf); resizeRaf = requestAnimationFrame(() => state.boxes.forEach(renderBox)); }); window.kiosk = { init, loadTemplate, patchBox, addBox, removeBox, applyInline, updateValue, currentTemplateId, setStatus }; })();