(function () { 'use strict'; // --- theme (dark/light), persisted per browser ------------------------- var THEME_KEY = 'ts3monolith-theme'; var root = document.documentElement; var toggle = document.querySelector('.theme-toggle'); var graphs = document.querySelectorAll('.js-live-graph'); var openers = document.querySelectorAll('.js-graph-open'); function currentTheme() { return root.getAttribute('data-theme') === 'light' ? 'light' : 'dark'; } // RRD graphs are static PNGs rendered server-side in both a dark and a // light palette (see internal/rrd/rrd.go); swap to the variant matching // the active theme so panel graphs never clash with the page around them. function applyGraphTheme(theme) { var stamp = Date.now(); graphs.forEach(function (img) { var src = theme === 'light' ? img.dataset.srcLight : img.dataset.src; if (src) img.src = src + '?v=' + stamp; }); openers.forEach(function (button) { var full = theme === 'light' ? button.getAttribute('data-full-light') : button.getAttribute('data-full'); if (full) button.setAttribute('data-full-active', full); }); } function applyTheme(mode) { if (mode === 'light') root.setAttribute('data-theme', 'light'); else root.removeAttribute('data-theme'); applyGraphTheme(mode === 'light' ? 'light' : 'dark'); } try { var saved = window.localStorage.getItem(THEME_KEY); if (saved) applyTheme(saved); else applyGraphTheme(currentTheme()); } catch (e) { applyGraphTheme(currentTheme()); } if (toggle) { toggle.addEventListener('click', function () { var next = currentTheme() === 'light' ? 'dark' : 'light'; applyTheme(next); try { window.localStorage.setItem(THEME_KEY, next); } catch (e) { /* ignore */ } }); } // --- live stats + graph refresh ----------------------------------------- var count = document.getElementById('online-count'); var status = document.getElementById('refresh-status'); var progress = document.getElementById('route-progress'); function setBusy(busy) { if (progress) progress.hidden = !busy; } function refresh() { setBusy(true); if (status) status.textContent = 'Odświeżanie…'; var done = Promise.resolve(); if (count) { done = fetch('/stats/live', { cache: 'no-store' }) .then(function (response) { if (!response.ok) throw new Error('stats'); return response.json(); }) .then(function (data) { if (typeof data.current === 'number') count.textContent = String(data.current); if (status) status.textContent = 'Dane aktualne'; }) .catch(function () { if (status) status.textContent = 'Nie udało się odświeżyć'; }); } applyGraphTheme(currentTheme()); done.then(function () { setBusy(false); }); } if (count || graphs.length) { window.setInterval(refresh, 60000); } // --- graph lightbox ------------------------------------------------------- var lightbox = document.getElementById('graph-lightbox'); var lightboxImage = document.getElementById('lightbox-image'); var openers = document.querySelectorAll('.js-graph-open'); var closeButtons = document.querySelectorAll('[data-lightbox-close]'); function openLightbox(src) { if (!lightbox || !lightboxImage) return; lightboxImage.src = src + (src.indexOf('?') === -1 ? '?' : '&') + 'v=' + Date.now(); lightbox.hidden = false; lightbox.setAttribute('aria-hidden', 'false'); document.body.classList.add('lightbox-open'); } function closeLightbox() { if (!lightbox || !lightboxImage) return; lightbox.hidden = true; lightbox.setAttribute('aria-hidden', 'true'); lightboxImage.src = ''; document.body.classList.remove('lightbox-open'); } openers.forEach(function (button) { button.addEventListener('click', function () { var src = button.getAttribute('data-full-active') || button.getAttribute('data-full'); if (src) openLightbox(src); }); }); closeButtons.forEach(function (button) { button.addEventListener('click', closeLightbox); }); document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && lightbox && !lightbox.hidden) closeLightbox(); }); })();