fix(cache): SW network-first für eigene Files
- Eigene HTML/JS/CSS werden IMMER frisch vom Netz geladen (kein Cache-First mehr) - Map-Tiles (OSM/cartocdn) bleiben für Offline im Cache - Cache-Version v3 → v4 (alte Bug-Caches werden in activate() gelöscht) - Update-Hook: Browser fragt nach Reload wenn neuer SW aktiviert wird - Fix: Updates an GitHub Pages sind sofort für alle sichtbar
This commit is contained in:
+17
-3
@@ -398,12 +398,26 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// PWA SW
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
navigator.serviceWorker.register('./sw.js')
|
navigator.serviceWorker.register('./sw.js')
|
||||||
.then(function(r){ console.log('[PWA] SW registered:', r.scope); })
|
.then(function(reg) {
|
||||||
.catch(function(e){ console.warn('[PWA] SW failed:', e); });
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
reg.addEventListener('updatefound', function() {
|
||||||
|
var newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', function() {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert');
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) { console.warn('[PWA] SW failed:', err); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+18
-2
@@ -886,10 +886,26 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ===== PWA Service Worker ===== */
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('./sw.js').catch(() => {});
|
navigator.serviceWorker.register('./sw.js')
|
||||||
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert');
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+24
-3
@@ -1219,9 +1219,30 @@ if (!state.setupDone) {
|
|||||||
}
|
}
|
||||||
checkStorage();
|
checkStorage();
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
navigator.serviceWorker.register('sw.js').catch(() => {});
|
if ('serviceWorker' in navigator) {
|
||||||
}
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('./sw.js')
|
||||||
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+18
-2
@@ -381,11 +381,27 @@
|
|||||||
updateProgress();
|
updateProgress();
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// PWA Service Worker registrieren
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('./sw.js')
|
navigator.serviceWorker.register('./sw.js')
|
||||||
.then(reg => console.log('[PWA] SW registered:', reg.scope))
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-3
@@ -782,9 +782,30 @@ renderPool();
|
|||||||
renderStats();
|
renderStats();
|
||||||
renderHistory();
|
renderHistory();
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
navigator.serviceWorker.register('sw.js').catch(() => {});
|
if ('serviceWorker' in navigator) {
|
||||||
}
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('./sw.js')
|
||||||
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,74 +1,116 @@
|
|||||||
// JGA Prag 2026 — Service Worker
|
// JGA Prag 2026 — Service Worker
|
||||||
// Cache-Strategie: cache-first für HTML/Assets, network-first für externe APIs (OSRM)
|
// Cache-Strategie:
|
||||||
const CACHE_VERSION = 'jga-2026-v3';
|
// • Eigene HTML/JS/CSS → network-first (immer frisch laden, Fallback Cache offline)
|
||||||
const CORE_ASSETS = [
|
// • Map-Tiles (OSM) → cache-first (für Offline-Karten)
|
||||||
'./',
|
// • Externe APIs (OSRM) → network-only (kein Cache)
|
||||||
'./index.html',
|
const CACHE_VERSION = 'jga-2026-v4';
|
||||||
'./tourenplan.html',
|
const TILE_CACHE = 'jga-2026-tiles-v4';
|
||||||
'./tourenplan-mit-tram.html',
|
const PAGE_CACHE = 'jga-2026-pages-v4';
|
||||||
'./wetter.html',
|
|
||||||
'./packliste.html',
|
// Nur Map-Tiles werden offline gecached — KEINE eigenen HTML-Files
|
||||||
'./countdown.html',
|
const PRECACHE_URLS = [
|
||||||
'./trinkspiel.html',
|
|
||||||
'./strafen.html',
|
|
||||||
'./memory.html',
|
|
||||||
'./manifest.json'
|
'./manifest.json'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Install: alle Core-Files cachen
|
// Install: nur manifest precachen, KEINE HTML-Files
|
||||||
self.addEventListener('install', (event) => {
|
self.addEventListener('install', (event) => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
caches.open(CACHE_VERSION)
|
caches.open(PAGE_CACHE)
|
||||||
.then(cache => cache.addAll(CORE_ASSETS))
|
.then(cache => cache.addAll(PRECACHE_URLS))
|
||||||
.then(() => self.skipWaiting())
|
.then(() => self.skipWaiting())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Activate: alte Caches aufräumen
|
// Activate: alle alten Caches (inkl. v3, v2, v1) löschen
|
||||||
self.addEventListener('activate', (event) => {
|
self.addEventListener('activate', (event) => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
caches.keys()
|
caches.keys()
|
||||||
.then(keys => Promise.all(
|
.then(keys => Promise.all(
|
||||||
keys.filter(k => k !== CACHE_VERSION).map(k => caches.delete(k))
|
keys
|
||||||
|
.filter(k => k !== CACHE_VERSION && k !== TILE_CACHE && k !== PAGE_CACHE)
|
||||||
|
.map(k => caches.delete(k))
|
||||||
))
|
))
|
||||||
.then(() => self.clients.claim())
|
.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: cache-first für eigene Files, network-first für externe APIs
|
// Fetch-Handler
|
||||||
self.addEventListener('fetch', (event) => {
|
self.addEventListener('fetch', (event) => {
|
||||||
const url = new URL(event.request.url);
|
const req = event.request;
|
||||||
|
if (req.method !== 'GET') return;
|
||||||
|
|
||||||
// Externe APIs (OSRM, OpenStreetMap, Wetter) → network-first
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
// ─── Externe APIs (OSRM, Open-Meteo) → network-only, kein Cache ───
|
||||||
if (url.origin !== self.location.origin) {
|
if (url.origin !== self.location.origin) {
|
||||||
event.respondWith(
|
// Map-Tiles (OSM / Carto / unpkg Leaflet) → cache-first
|
||||||
fetch(event.request)
|
if (
|
||||||
.then(response => {
|
url.hostname.includes('openstreetmap') ||
|
||||||
// Erfolgreich: clone & cache für offline
|
|
||||||
if (response.ok && (url.hostname.includes('openstreetmap') ||
|
|
||||||
url.hostname.includes('cartocdn') ||
|
url.hostname.includes('cartocdn') ||
|
||||||
url.hostname.includes('unpkg'))) {
|
url.hostname.includes('tile.openstreetmap')
|
||||||
const clone = response.clone();
|
) {
|
||||||
caches.open(CACHE_VERSION).then(c => c.put(event.request, clone));
|
event.respondWith(cacheFirst(req, TILE_CACHE));
|
||||||
}
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(() => caches.match(event.request))
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Alles andere extern → einfach durch, kein Caching
|
||||||
|
return; // default browser behavior
|
||||||
|
}
|
||||||
|
|
||||||
// Eigene Files → cache-first
|
// ─── Eigene Files (HTML/JS/CSS/JSON) → network-first ───
|
||||||
event.respondWith(
|
// Immer versuchen frisch zu laden, nur bei Offline-Fallback aus Cache
|
||||||
caches.match(event.request)
|
event.respondWith(networkFirst(req, PAGE_CACHE));
|
||||||
.then(cached => cached || fetch(event.request)
|
});
|
||||||
.then(response => {
|
|
||||||
|
// 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) {
|
if (response.ok) {
|
||||||
const clone = response.clone();
|
cache.put(request, response.clone());
|
||||||
caches.open(CACHE_VERSION).then(c => c.put(event.request, clone));
|
|
||||||
}
|
}
|
||||||
return response;
|
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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@@ -1148,11 +1148,27 @@ document.addEventListener('keydown', (e) => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// PWA Service Worker registrieren
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('./sw.js')
|
navigator.serviceWorker.register('./sw.js')
|
||||||
.then(reg => console.log('[PWA] SW registered:', reg.scope))
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-2
@@ -664,11 +664,27 @@ legend.addTo(map);
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// PWA Service Worker registrieren
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('./sw.js')
|
navigator.serviceWorker.register('./sw.js')
|
||||||
.then(reg => console.log('[PWA] SW registered:', reg.scope))
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-3
@@ -1178,9 +1178,30 @@ renderGameInfo();
|
|||||||
document.getElementById('drawStatus').textContent = `Karten im Deck: ${currentDeck.length}`;
|
document.getElementById('drawStatus').textContent = `Karten im Deck: ${currentDeck.length}`;
|
||||||
|
|
||||||
/* ===== Service Worker ===== */
|
/* ===== Service Worker ===== */
|
||||||
if ('serviceWorker' in navigator) {
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
navigator.serviceWorker.register('sw.js').catch(() => {});
|
if ('serviceWorker' in navigator) {
|
||||||
}
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('./sw.js')
|
||||||
|
.then(reg => {
|
||||||
|
console.log('[PWA] SW registered:', reg.scope);
|
||||||
|
// Update-Hook: wenn ein neuer SW installiert wurde, kurz reloaden
|
||||||
|
reg.addEventListener('updatefound', () => {
|
||||||
|
const newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener('statechange', () => {
|
||||||
|
if (newSw.state === 'activated' && navigator.serviceWorker.controller) {
|
||||||
|
console.log('[PWA] Neue Version aktiviert — lade neu');
|
||||||
|
// Sanfter Reload ohne Datenverlust, Cache-Bust mit Timestamp
|
||||||
|
if (window.confirm('🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => console.warn('[PWA] SW registration failed:', err));
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+17
-2
@@ -187,11 +187,26 @@ h1 { font-size:42px; margin:0 0 8px 0; font-weight:700; letter-spacing:-1px; col
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
|
// PWA Service Worker — Network-First Strategie (Updates kommen sofort durch)
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
navigator.serviceWorker.register("./sw.js")
|
navigator.serviceWorker.register("./sw.js")
|
||||||
.then(function(r){ console.log("[PWA] SW registered:", r.scope); })
|
.then(function(reg) {
|
||||||
.catch(function(e){ console.warn("[PWA] SW registration failed:", e); });
|
console.log("[PWA] SW registered:", reg.scope);
|
||||||
|
reg.addEventListener("updatefound", function() {
|
||||||
|
var newSw = reg.installing;
|
||||||
|
if (!newSw) return;
|
||||||
|
newSw.addEventListener("statechange", function() {
|
||||||
|
if (newSw.state === "activated" && navigator.serviceWorker.controller) {
|
||||||
|
console.log("[PWA] Neue Version aktiviert");
|
||||||
|
if (window.confirm("🔄 Neue Version der JGA-Seite verfügbar — jetzt neu laden?")) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) { console.warn("[PWA] SW registration failed:", err); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user