93 lines
3.4 KiB
JavaScript
93 lines
3.4 KiB
JavaScript
|
|
(function(){
|
|
const THEME_KEY = 'force-monitor-theme';
|
|
function byId(id){ return id ? document.getElementById(id) : null; }
|
|
function setTheme(theme, opts){
|
|
opts = opts || {};
|
|
const t = theme === 'light' ? 'light' : 'dark';
|
|
document.body.setAttribute('data-theme', t);
|
|
try { localStorage.setItem(THEME_KEY, t); } catch (_) {}
|
|
const btn = byId(opts.buttonId || 'theme-toggle');
|
|
if (btn) btn.textContent = t === 'light' ? 'Dark theme' : 'Light theme';
|
|
if (typeof opts.onChange === 'function') opts.onChange(t);
|
|
return t;
|
|
}
|
|
function initTheme(opts){
|
|
opts = opts || {};
|
|
let theme = 'dark';
|
|
try {
|
|
const stored = localStorage.getItem(THEME_KEY);
|
|
if (stored === 'light' || stored === 'dark') theme = stored;
|
|
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) theme = 'light';
|
|
} catch (_) {}
|
|
setTheme(theme, opts);
|
|
const btn = byId(opts.buttonId || 'theme-toggle');
|
|
if (btn && !btn.dataset.themeBound) {
|
|
btn.dataset.themeBound = '1';
|
|
btn.addEventListener('click', function(){
|
|
setTheme(document.body.getAttribute('data-theme') === 'light' ? 'dark' : 'light', opts);
|
|
});
|
|
}
|
|
return theme;
|
|
}
|
|
function updateFullscreenButton(buttonId){
|
|
const btn = byId(buttonId || 'fullscreen-toggle');
|
|
if (btn) btn.textContent = document.fullscreenElement ? 'Exit fullscreen' : 'Enter fullscreen';
|
|
}
|
|
async function toggleFullscreen(buttonId){
|
|
try {
|
|
if (!document.fullscreenElement) await document.documentElement.requestFullscreen();
|
|
else await document.exitFullscreen();
|
|
} catch (err) {
|
|
console.warn('Fullscreen error:', err);
|
|
} finally {
|
|
updateFullscreenButton(buttonId || 'fullscreen-toggle');
|
|
}
|
|
}
|
|
function initFullscreen(opts){
|
|
opts = opts || {};
|
|
const buttonId = opts.buttonId || 'fullscreen-toggle';
|
|
const btn = byId(buttonId);
|
|
if (btn && !btn.dataset.fsBound) {
|
|
btn.dataset.fsBound = '1';
|
|
btn.addEventListener('click', function(){ toggleFullscreen(buttonId); });
|
|
}
|
|
if (!document.documentElement.dataset.fsListenerBound) {
|
|
document.documentElement.dataset.fsListenerBound = '1';
|
|
document.addEventListener('fullscreenchange', function(){
|
|
document.querySelectorAll('#fullscreen-toggle, #fullscreen-btn').forEach(function(el){
|
|
el.textContent = document.fullscreenElement ? 'Exit fullscreen' : 'Enter fullscreen';
|
|
});
|
|
});
|
|
}
|
|
updateFullscreenButton(buttonId);
|
|
}
|
|
async function fetchJson(url, opts){
|
|
opts = opts || {};
|
|
const controller = new AbortController();
|
|
const timeoutMs = opts.timeoutMs || 8000;
|
|
const timer = setTimeout(function(){ controller.abort(); }, timeoutMs);
|
|
try {
|
|
const res = await fetch(url, {
|
|
method: opts.method || 'GET',
|
|
headers: opts.headers || undefined,
|
|
body: opts.body,
|
|
cache: 'no-store',
|
|
signal: controller.signal
|
|
});
|
|
let data = null;
|
|
try { data = await res.json(); } catch (_) { data = null; }
|
|
if (!res.ok) {
|
|
const err = new Error(data && data.error ? data.error : ('HTTP ' + res.status));
|
|
err.response = res;
|
|
err.data = data;
|
|
throw err;
|
|
}
|
|
return data;
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|
|
window.AppUI = { setTheme, initTheme, updateFullscreenButton, toggleFullscreen, initFullscreen, fetchJson };
|
|
})();
|