diff --git a/assets/js/main.js b/assets/js/main.js index bf863f4..9c9bdc5 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -104,6 +104,19 @@ document.querySelectorAll('.card').forEach(card => { return String(url).replace(/\/+$/, ''); }; + const parsePossiblyUtf16Json = (text) => { + if (!text) return null; + + // If server accidentally delivered UTF-16LE bytes, the browser may decode it with lots of \u0000. + // Remove NULs and common BOM remnants. + const cleaned = String(text) + .replace(/^\uFEFF/, '') + .replace(/\u0000/g, '') + .trim(); + + return JSON.parse(cleaned); + }; + const applyPayload = (payload) => { const byUrl = payload && payload.byUrl ? payload.byUrl : {}; const byUrlNormalized = payload && payload.byUrlNormalized ? payload.byUrlNormalized : {}; @@ -136,15 +149,19 @@ document.querySelectorAll('.card').forEach(card => { const fetchAndApply = async () => { const res = await fetch('/server_status.php', { cache: 'no-store' }); if (!res.ok) throw new Error('status_fetch_failed'); - const payload = await res.json(); + + // Use text() + robust JSON parse to survive wrong encoding (UTF-16/NULs). + const text = await res.text(); + const payload = parsePossiblyUtf16Json(text); + if (!payload) throw new Error('status_json_empty'); + applyPayload(payload); }; - // Ziel: nach ~1s sichtbar springen (nicht sofort beim ersten Paint). - // Falls der Endpoint kurz hängt, probieren wir 1-2x nochmal. - const attempts = [1000, 2000, 3500]; - for (let i = 0; i < attempts.length; i++) { - await new Promise(r => setTimeout(r, attempts[i])); + // Nach ~1s einmal aktualisieren, dann (falls nötig) 2 schnelle Retries. + const delays = [1000, 1000, 1500]; + for (const d of delays) { + await new Promise(r => setTimeout(r, d)); try { await fetchAndApply(); break;