<!DOCTYPE html>
<html lang="de">
<head>
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Kostenloser Stadtführer für Weimar: Touren, Sehenswürdigkeiten, Karte, SprachGuide und individuelle Routen.">
<meta name="theme-color" content="#E03868">
<meta property="og:title" content="MeinWeimar TOURGUIDE">
<meta property="og:description" content="Weimar entdecken: kostenlose Touren, Orte, Karte und individueller Tourplaner.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://mwguideapp.meinweimar.de/">
<meta property="og:image" content="https://mwguideapp.meinweimar.de/icons/Icon-512.png">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="MeinWeimar TOURGUIDE">
<link rel="icon" type="image/png" href="favicon.png">
<title>MeinWeimar TOURGUIDE</title>
<link rel="manifest" href="manifest.json">
<style>
#mw-startup-debug {
display:none; position:fixed; inset:12px; z-index:2147483647;
background:rgba(20,20,22,.96); color:#fff; padding:14px;
border-radius:14px; font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;
overflow:auto; box-shadow:0 8px 30px rgba(0,0,0,.35);
}
#mw-startup-debug strong { font:600 15px/1.4 system-ui,sans-serif; }
#mw-startup-debug button { margin:10px 8px 0 0; padding:9px 12px; border:0; border-radius:9px; }
#mw-startup-log { white-space:pre-wrap; margin-top:10px; }
#mw-startup-error {
display:none; position:fixed; inset:0; z-index:2147483646;
background:#fff; color:#171717; font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
align-items:center; justify-content:center; padding:28px; text-align:center;
}
#mw-startup-error .box { max-width:440px; }
#mw-startup-error h1 { font-size:24px; margin:0 0 12px; }
#mw-startup-error p { font-size:16px; line-height:1.5; margin:0 0 20px; color:#555; }
#mw-startup-error button { border:0; border-radius:999px; padding:12px 20px; font-weight:700; font-size:16px; background:#171717; color:#fff; }
</style>
<script>
(() => {
const params = new URLSearchParams(location.search);
// Production diagnostics are NEVER shown automatically. They are only
// enabled through an explicitly requested ?mwdebug=1 URL.
const debugEnabled = params.get('mwdebug') === '1';
const logLines = [];
let runtimeErrorSeen = false;
let flutterStarted = false;
let recoveryStarted = false;
const log = (message) => {
const stamp = new Date().toISOString().slice(11, 23);
logLines.push(stamp + ' ' + message);
console.log('[MeinWeimar startup]', message);
if (debugEnabled) renderDebug();
};
window.mwStartupLog = log;
const ensureDebugPanel = () => {
if (!debugEnabled) return null;
let panel = document.getElementById('mw-startup-debug');
if (panel) return panel;
panel = document.createElement('div');
panel.id = 'mw-startup-debug';
panel.innerHTML = '<strong>MeinWeimar Startdiagnose</strong>' +
'<div id="mw-startup-log"></div>' +
'<button id="mw-reload">Neu laden</button>' +
'<button id="mw-repair">Nur MeinWeimar-Startdaten reparieren</button>';
document.documentElement.appendChild(panel);
panel.querySelector('#mw-reload').onclick = () => location.reload();
panel.querySelector('#mw-repair').onclick = async () => {
await cleanupMeinWeimarStorage();
const u = new URL(location.href);
u.searchParams.set('mwdebug', '1');
u.searchParams.set('mwrecovered', Date.now().toString());
location.replace(u.toString());
};
return panel;
};
const renderDebug = () => {
if (!debugEnabled) return;
const panel = ensureDebugPanel();
if (!panel) return;
panel.style.display = 'block';
const target = panel.querySelector('#mw-startup-log');
if (target) target.textContent = logLines.join('\n');
};
const showPublicError = () => {
if (debugEnabled) {
renderDebug();
return;
}
let panel = document.getElementById('mw-startup-error');
if (!panel) {
panel = document.createElement('div');
panel.id = 'mw-startup-error';
panel.innerHTML = '<div class="box"><h1>MeinWeimar konnte nicht geladen werden.</h1>' +
'<p>Bitte aktualisiere die Seite. Deine persönlichen Browserdaten werden dabei nicht gelöscht.</p>' +
'<button type="button">Neu laden</button></div>';
document.documentElement.appendChild(panel);
panel.querySelector('button').onclick = () => location.reload();
}
panel.style.display = 'flex';
};
const cleanupMeinWeimarStorage = async () => {
// Delete only data owned by MeinWeimar. Never clear global browser data.
try {
const ownedPrefixes = [
'mw_web_loaded_version',
'flutter.mw_api_cache_v1_',
'flutter.mw_analytics_pending_v1'
];
const removals = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && ownedPrefixes.some(prefix => key === prefix || key.startsWith(prefix))) {
removals.push(key);
}
}
removals.forEach(key => localStorage.removeItem(key));
} catch (e) {
log('LocalStorage cleanup skipped: ' + e);
}
try {
if ('serviceWorker' in navigator) {
const regs = await navigator.serviceWorker.getRegistrations();
await Promise.all(regs.map(reg => reg.unregister()));
}
} catch (e) {
log('Service-worker cleanup skipped: ' + e);
}
try {
if ('caches' in window) {
const keys = await caches.keys();
await Promise.all(keys.map(key => caches.delete(key)));
}
} catch (e) {
log('Cache Storage cleanup skipped: ' + e);
}
};
const hasFlutterSurface = () => {
return !!document.querySelector('flt-glass-pane, flutter-view, flt-scene-host, canvas');
};
const markFlutterStartedIfPresent = () => {
if (flutterStarted || !hasFlutterSurface()) return false;
flutterStarted = true;
log('Flutter surface detected');
const publicError = document.getElementById('mw-startup-error');
if (publicError) publicError.style.display = 'none';
return true;
};
const recoverOnce = async (reason) => {
if (recoveryStarted || flutterStarted) return;
recoveryStarted = true;
log('Production recovery requested: ' + reason);
// Debug mode is observational: never auto-reload while diagnosing.
if (debugEnabled) {
renderDebug();
return;
}
const u = new URL(location.href);
if (u.searchParams.has('mwrecovered')) {
log('Recovery already attempted; showing public error');
showPublicError();
return;
}
await cleanupMeinWeimarStorage();
u.searchParams.delete('mwdebug');
u.searchParams.set('mwrecovered', Date.now().toString());
location.replace(u.toString());
};
window.addEventListener('error', (event) => {
runtimeErrorSeen = true;
log('WINDOW ERROR: ' + (event.message || event.error || 'unknown'));
// Never expose developer diagnostics in production.
if (debugEnabled) renderDebug();
});
window.addEventListener('unhandledrejection', (event) => {
runtimeErrorSeen = true;
log('UNHANDLED PROMISE: ' + String(event.reason || 'unknown'));
if (debugEnabled) renderDebug();
});
// Observe the DOM so we know when Flutter has really mounted. This avoids
// treating harmless JavaScript warnings as fatal startup errors.
const observer = new MutationObserver(() => markFlutterStartedIfPresent());
observer.observe(document.documentElement, {childList:true, subtree:true});
log('HTML shell loaded; href=' + location.href);
window.addEventListener('DOMContentLoaded', async () => {
const host = window.location.hostname;
const isLocalDev = host === 'localhost' || host === '127.0.0.1' || host === '::1';
log('DOMContentLoaded; mode=' + (isLocalDev ? 'local-dev' : 'production'));
if (isLocalDev) {
const script = document.createElement('script');
script.src = 'flutter_bootstrap.js';
script.async = true;
script.dataset.meinweimarMode = 'flutter-dev';
script.onload = () => log('Local flutter_bootstrap.js loaded');
script.onerror = () => {
log('ERROR loading local flutter_bootstrap.js');
if (debugEnabled) renderDebug(); else showPublicError();
};
document.head.appendChild(script);
return;
}
try {
log('Unregistering legacy service workers');
if ('serviceWorker' in navigator) {
const regs = await navigator.serviceWorker.getRegistrations();
await Promise.all(regs.map(reg => reg.unregister()));
}
log('Service-worker cleanup done');
log('Clearing Cache Storage');
if ('caches' in window) {
const keys = await caches.keys();
await Promise.all(keys.map(key => caches.delete(key)));
}
log('Cache Storage cleanup done');
log('Fetching current release metadata');
let response;
try {
response = await fetch('version.php?cacheBust=' + Date.now(), {
cache: 'no-store', headers: {'Cache-Control':'no-cache'}
});
} catch (_) {
response = null;
}
if (!response || !response.ok) {
response = await fetch('version.json?cacheBust=' + Date.now(), {
cache: 'no-store', headers: {'Cache-Control':'no-cache'}
});
}
log('Release metadata HTTP ' + response.status);
if (!response.ok) throw new Error('release metadata HTTP ' + response.status);
const info = await response.json();
const version = String(info.version || '');
const releasePath = String(info.release_path || '');
log('Release metadata: version=' + version + ' path=' + releasePath);
if (!version || !releasePath) throw new Error('Incomplete release metadata');
try {
localStorage.setItem('mw_web_loaded_version', version);
log('LocalStorage write OK');
} catch (e) {
// Storage failure must never block boot.
log('LocalStorage unavailable: ' + e);
}
const url = new URL(window.location.href);
if (url.searchParams.get('mwv') !== version) {
url.searchParams.set('mwv', version);
history.replaceState(null, '', url.toString());
}
const normalized = releasePath.endsWith('/') ? releasePath : releasePath + '/';
const bootstrapUrl = normalized + 'flutter_bootstrap.js?v=' + encodeURIComponent(version);
log('Loading ' + bootstrapUrl);
const script = document.createElement('script');
script.src = bootstrapUrl;
script.async = true;
script.dataset.meinweimarRelease = version;
script.onload = () => {
log('Release flutter_bootstrap.js loaded');
// Allow Flutter enough time to mount its first surface. Only if no
// surface exists after the deadline do we treat startup as failed.
setTimeout(() => {
if (markFlutterStartedIfPresent()) return;
if (!flutterStarted) {
recoverOnce(runtimeErrorSeen ? 'runtime error before first frame' : 'first-frame timeout');
}
}, 12000);
};
script.onerror = () => {
log('ERROR loading ' + bootstrapUrl);
recoverOnce('bootstrap script load failure');
};
document.body.appendChild(script);
} catch (error) {
log('BOOTSTRAP FAILED: ' + error);
recoverOnce('launcher exception');
}
});
if (debugEnabled) renderDebug();
})();
</script>
</head>
<body></body>
</html>

