// ====================================================
// ⚡ SERVICE WORKER — Ultra Elite Dashboard
// Ermöglicht Offline-Nutzung auf allen Geräten
// ====================================================
const CACHE_NAME = 'elite-dashboard-v6';
const OFFLINE_URL = './index.html';
// Dateien die IMMER gecacht werden (Offline verfügbar)
const PRECACHE_ASSETS = [
'./',
'./index.html',
'./manifest.json',
'./icon-192.png',
'./icon-512.png',
// Externe Bibliotheken
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap',
'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js',
'https://unpkg.com/docx@8.2.3/build/index.js',
'https://unpkg.com/file-saver@2.0.5/dist/FileSaver.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js',
'https://cdn.jsdelivr.net/npm/pannellum@2.5.7/build/pannellum.js',
'https://cdn.jsdelivr.net/npm/pannellum@2.5.7/build/pannellum.css',
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'
];
// ===== INSTALL: Dateien vorab cachen =====
self.addEventListener('install', event => {
console.log('🔧 Service Worker: Installing...');
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('📦 Caching core assets...');
// Cache each individually to avoid one failure blocking all
return Promise.allSettled(
PRECACHE_ASSETS.map(url =>
cache.add(url).catch(err =>
console.warn('⚠️ Failed to cache:', url, err)
)
)
);
})
.then(() => self.skipWaiting()) // Sofort aktivieren
);
});
// ===== ACTIVATE: Alte Caches löschen =====
self.addEventListener('activate', event => {
console.log('✅ Service Worker: Activated!');
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(name => name !== CACHE_NAME)
.map(name => {
console.log('🗑️ Deleting old cache:', name);
return caches.delete(name);
})
);
}).then(() => self.clients.claim()) // Alle Tabs übernehmen
);
});
// ===== FETCH: Anfragen abfangen & aus Cache bedienen =====
self.addEventListener('fetch', event => {
const request = event.request;
// Nur GET-Anfragen cachen
if (request.method !== 'GET') return;
// API-Anfragen: Network First (z.B. Gebetszeiten, Währung)
if (request.url.includes('api.') ||
request.url.includes('aladhan.com') ||
request.url.includes('frankfurter.app') ||
request.url.includes('mymemory.translated') ||
request.url.includes('nominatim.openstreetmap') ||
request.url.includes('openai.com')) {
event.respondWith(
fetch(request)
.then(response => {
// API-Antwort auch cachen für Offline-Zugriff
const responseClone = response.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(request, responseClone);
});
return response;
})
.catch(() => {
// Offline: Aus Cache holen falls vorhanden
return caches.match(request);
})
);
return;
}
// Alles andere: Cache First, Network Fallback
event.respondWith(
caches.match(request)
.then(cachedResponse => {
if (cachedResponse) {
// Im Hintergrund aktualisieren (Stale While Revalidate)
fetch(request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
caches.open(CACHE_NAME).then(cache => {
cache.put(request, networkResponse);
});
}
}).catch(() => {}); // Netzwerkfehler ignorieren
return cachedResponse;
}
// Nicht im Cache → Aus Netzwerk holen & cachen
return fetch(request)
.then(networkResponse => {
if (!networkResponse || networkResponse.status !== 200) {
return networkResponse;
}
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(request, responseClone);
});
return networkResponse;
})
.catch(() => {
// Komplett Offline: Fallback zur Hauptseite
if (request.destination === 'document') {
return caches.match(OFFLINE_URL);
}
// Für Bilder: leeres transparentes Pixel
if (request.destination === 'image') {
return new Response(
'',
{ headers: { 'Content-Type': 'image/svg+xml' } }
);
}
});
})
);
});
// ===== BACKGROUND SYNC (optional) =====
self.addEventListener('sync', event => {
if (event.tag === 'sync-data') {
console.log('🔄 Background sync triggered');
}
});
// ===== PUSH NOTIFICATIONS (optional) =====
self.addEventListener('push', event => {
const data = event.data ? event.data.json() : {};
self.registration.showNotification(data.title || 'Dashboard', {
body: data.body || 'Neue Benachrichtigung',
icon: './icon-192.png',
badge: './icon-192.png'
});
});