/**
* GeoGraSim Plattform: Live-Client
*
* Sendet Heartbeats an /api/live (Live-View für Lehrkräfte) und pollt das
* Pause-Flag aus /api/modules?student=1. Wird vom Sim-Wrapper als Plattform-
* Asset geladen — Sims selbst müssen nichts implementieren.
*
* Erwartet: window.__GGS__ = { sessionId, simId, baseUrl, basePath, apiUrl }.
*
* Sims können optional window.GGS_LIVE_STATE() zurückgeben, um einen kleinen
* State-Snapshot mitzusenden (max 50 KB). Ohne diese Funktion wird nur ein
* leerer State gemeldet — Lehrer sieht trotzdem WER online ist und WAS spielt.
*/
(function () {
'use strict';
if (window.__GGS_LIVE_INITIALIZED__) return;
window.__GGS_LIVE_INITIALIZED__ = true;
var ctx = window.__GGS__ || {};
// Ohne simId können wir nicht melden, welches Modul gespielt wird.
if (!ctx.simId) return;
// sessionId nicht vorab prüfen — der Browser sendet das ggs_session-Cookie
// automatisch mit den fetch-Calls (credentials:'same-origin'), und der
// Server entscheidet, ob es eine gültige Schüler-Session ist.
var apiBase = ctx.apiUrl || ((ctx.baseUrl || '') + '/php/api');
var simId = ctx.simId;
// --- Heartbeat ---
var heartbeatTimer = null;
function snapshotState() {
try {
if (typeof window.GGS_LIVE_STATE === 'function') {
var s = window.GGS_LIVE_STATE();
if (s && typeof s === 'object') return s;
}
} catch (_) {}
return {};
}
function sendHeartbeat() {
fetch(apiBase + '/live.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify({ action: 'heartbeat', module_id: simId, state: snapshotState() })
}).catch(function () {});
}
// --- Pause-Polling ---
var pauseTimer = null;
var pauseOverlay = null;
function ensureOverlay() {
if (pauseOverlay) return pauseOverlay;
var el = document.createElement('div');
el.id = 'ggs-pause-overlay';
el.style.cssText = 'position:fixed;inset:0;background:rgba(31,75,55,.78);color:#fff;z-index:99999;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:Inter,sans-serif;font-weight:700;text-align:center;padding:2rem;backdrop-filter:blur(3px)';
el.innerHTML = '
⏸
'
+ '
Auftrag pausiert
'
+ '
Deine Lehrperson hat das Tempo angehalten. Sobald sie freigibt, kannst du weitermachen.
';
document.body.appendChild(el);
pauseOverlay = el;
return el;
}
function showPause() { ensureOverlay().style.display = 'flex'; }
function hidePause() { if (pauseOverlay) pauseOverlay.style.display = 'none'; }
function checkPause() {
fetch(apiBase + '/modules.php?student=1&module_id=' + encodeURIComponent(simId), {
credentials: 'same-origin'
}).then(function (r) { return r.json(); })
.then(function (m) {
if (m && m.paused && m.mode === 'teacher_started') showPause();
else hidePause();
})
.catch(function () {});
}
// --- Spectator-Mode für Lehrkräfte ---
// ?view=teacher schaltet Input ab und blendet ein dezentes Banner ein.
function isSpectator() {
try { return new URLSearchParams(location.search).get('view') === 'teacher'; }
catch (_) { return false; }
}
function applySpectator() {
if (!isSpectator()) return;
var s = document.createElement('style');
s.textContent = 'html,body{pointer-events:none!important}'
+ '#ggs-spectator-banner{pointer-events:auto!important;position:fixed;top:0;left:0;right:0;background:#4a7c8a;color:#fff;padding:.5rem;text-align:center;font-family:Inter,sans-serif;font-size:.78rem;font-weight:700;z-index:99998}'
+ '#ggs-spectator-banner button{pointer-events:auto;margin-left:.6rem;padding:.2rem .6rem;border:none;border-radius:5px;background:#fff;color:#4a7c8a;font-weight:700;cursor:pointer}';
document.head.appendChild(s);
var bar = document.createElement('div');
bar.id = 'ggs-spectator-banner';
bar.innerHTML = '👁 Live-Ansicht (Lehrkraft) — Eingaben sind deaktiviert. ';
document.body.appendChild(bar);
}
// --- Lifecycle ---
function start() {
if (isSpectator()) { applySpectator(); return; } // Spectator nicht heartbeaten
sendHeartbeat();
heartbeatTimer = setInterval(sendHeartbeat, 4000);
checkPause();
pauseTimer = setInterval(checkPause, 5000);
// Bei Tab-Schließung sauberer „end"-Heartbeat (keepalive für unload)
window.addEventListener('beforeunload', function () {
try {
navigator.sendBeacon && navigator.sendBeacon(
apiBase + '/live.php',
new Blob([JSON.stringify({ action: 'end' })], { type: 'application/json' })
);
} catch (_) {}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
})();