// JGA Prag 2026 — Service Worker // Cache-Strategie: // • Eigene HTML/JS/CSS → network-first (immer frisch laden, Fallback Cache offline) // • Map-Tiles (OSM) → cache-first (für Offline-Karten) // • Externe APIs (OSRM) → network-only (kein Cache) const CACHE_VERSION = 'jga-2026-v4'; const TILE_CACHE = 'jga-2026-tiles-v4'; const PAGE_CACHE = 'jga-2026-pages-v4'; // Nur Map-Tiles werden offline gecached — KEINE eigenen HTML-Files const PRECACHE_URLS = [ './manifest.json' ]; // Install: nur manifest precachen, KEINE HTML-Files self.addEventListener('install', (event) => { event.waitUntil( caches.open(PAGE_CACHE) .then(cache => cache.addAll(PRECACHE_URLS)) .then(() => self.skipWaiting()) ); }); // Activate: alle alten Caches (inkl. v3, v2, v1) löschen self.addEventListener('activate', (event) => { event.waitUntil( caches.keys() .then(keys => Promise.all( keys .filter(k => k !== CACHE_VERSION && k !== TILE_CACHE && k !== PAGE_CACHE) .map(k => caches.delete(k)) )) .then(async () => { // Auch alte Tile- und Page-Caches aufräumen const old = await caches.keys(); await Promise.all( old .filter(k => k.startsWith('jga-2026-') && k !== CACHE_VERSION && k !== TILE_CACHE && k !== PAGE_CACHE) .map(k => caches.delete(k)) ); await self.clients.claim(); }) ); }); // Fetch-Handler self.addEventListener('fetch', (event) => { const req = event.request; if (req.method !== 'GET') return; const url = new URL(req.url); // ─── Externe APIs (OSRM, Open-Meteo) → network-only, kein Cache ─── if (url.origin !== self.location.origin) { // Map-Tiles (OSM / Carto / unpkg Leaflet) → cache-first if ( url.hostname.includes('openstreetmap') || url.hostname.includes('cartocdn') || url.hostname.includes('tile.openstreetmap') ) { event.respondWith(cacheFirst(req, TILE_CACHE)); return; } // Alles andere extern → einfach durch, kein Caching return; // default browser behavior } // ─── Eigene Files (HTML/JS/CSS/JSON) → network-first ─── // Immer versuchen frisch zu laden, nur bei Offline-Fallback aus Cache event.respondWith(networkFirst(req, PAGE_CACHE)); }); // Network-first: frisch vom Netz, Fallback auf Cache (offline) async function networkFirst(request, cacheName) { const cache = await caches.open(cacheName); try { const response = await fetch(request, { cache: 'no-store' }); if (response.ok) { cache.put(request, response.clone()); } return response; } catch (err) { const cached = await cache.match(request); if (cached) return cached; // Letzter Fallback: index.html (für SPA-ähnliches Verhalten) if (request.mode === 'navigate') { const fallback = await cache.match('./index.html'); if (fallback) return fallback; } throw err; } } // Cache-first: aus Cache, Fallback aufs Netz (für Tiles) async function cacheFirst(request, cacheName) { const cache = await caches.open(cacheName); const cached = await cache.match(request); if (cached) return cached; try { const response = await fetch(request); if (response.ok) { cache.put(request, response.clone()); } return response; } catch (err) { // Tile offline + nicht im Cache → leere 503 return new Response('', { status: 503, statusText: 'Tile offline' }); } } // Clients über neues Update benachrichtigen self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } });