/* Usage-Analytics (anonym, non-blocking). * window.smTrack('cat:name') sammelt Events und schickt sie GEBUENDELT per * navigator.sendBeacon('/track', ...) -> feuern-und-vergessen, blockiert NIE den * Render-/Interaktionspfad. Es wird NICHTS Personenbezogenes gesendet (kein Name, * keine ID, keine IP). Server haelt nur anonyme Event->Count je UTC-Tag. * Erlaubte Kategorien (muessen zum Server passen): * tab action stat lookup opt lb view sort filter gear info */ (function () { "use strict"; var QUEUE = []; var timer = null; var FLUSH_MS = 4000; // kurze Sammelphase, damit mehrere Klicks in EINEM Beacon landen var MAX_QUEUE = 40; // bei Flut sofort flushen (deckt sich mit dem Server-Batch-Deckel) function send(list) { if (!list || !list.length) return; var body = JSON.stringify({ e: list }); try { if (navigator.sendBeacon) { var blob = new Blob([body], { type: "application/json" }); if (navigator.sendBeacon("/track", blob)) return; } } catch (e) {} // Fallback: keepalive-fetch (stoert den Hauptpfad nicht, ueberlebt unload) try { fetch("/track", { method: "POST", body: body, keepalive: true, headers: { "Content-Type": "application/json" } }).catch(function () {}); } catch (e) {} } function flush() { if (timer) { clearTimeout(timer); timer = null; } if (!QUEUE.length) return; send(QUEUE.splice(0, QUEUE.length)); } window.smTrack = function (ev) { try { if (typeof ev !== "string" || !ev) return; if (ev.length > 60) ev = ev.slice(0, 60); QUEUE.push(ev); if (QUEUE.length >= MAX_QUEUE) { flush(); return; } if (!timer) timer = setTimeout(flush, FLUSH_MS); } catch (e) {} }; // Beim Verlassen/Verstecken alles rausschicken (sendBeacon ueberlebt unload) try { document.addEventListener("visibilitychange", function () { if (document.visibilityState === "hidden") flush(); }); window.addEventListener("pagehide", flush); } catch (e) {} // Erster Seitenaufruf try { window.smTrack("view:load"); } catch (e) {} })();