1/** 2 * Service Worker pre Multiobec PWA 3 * 4 * Stratégie: 5 * - IDSK assety (CSS/JS/fonty): Cache First â dlhodobo stabilné 6 * - Tenant ikony a obrázky: Cache First s network fallback 7 * - HTML stránky: Network First s cache fallback (offline podpora) 8 * - API/dynamické: Network Only 9 * 10 * Cache sa automaticky Äistà pri zmene verzie (CACHE_VERSION). 11 * Verzia sa bumpne pri deploji alebo pri tenant refresh. 12 */ 13 14var CACHE_VERSION = 'v2'; 15var STATIC_CACHE = 'multiobec-static-' + CACHE_VERSION; 16var PAGES_CACHE = 'multiobec-pages-' + CACHE_VERSION; 17var IMAGES_CACHE = 'multiobec-images-' + CACHE_VERSION; 18 19// Statické assety â precachujú sa pri inÅ¡talácii 20var PRECACHE_URLS = [ 21 '/', 22 '/offline', 23 '/assets/idsk/idsk-frontend.css', 24 '/assets/idsk/idsk-frontend.js', 25 '/assets/idsk/fonts/SourceSansPro-Regular.woff', 26 '/assets/idsk/fonts/SourceSansPro-Bold.woff', 27 '/assets/idsk/fonts/SourceSansPro-Semibold.woff' 28]; 29 30// ââ INSTALL ââââââââââââââââââââââââââââââââââââââââââââââââââ 31self.addEventListener('install', function(event) { 32 event.waitUntil( 33 caches.open(STATIC_CACHE).then(function(cache) { 34 return cache.addAll(PRECACHE_URLS); 35 }).then(function() { 36 // Okamžite aktivuj â neÄakaj na zatvorenie starých tabov 37 return self.skipWaiting(); 38 }) 39 ); 40}); 41 42// ââ ACTIVATE âââââââââââââââââââââââââââââââââââââââââââââââââ 43self.addEventListener('activate', function(event) { 44 // Vymaž staré cache (iná verzia) 45 event.waitUntil( 46 caches.keys().then(function(cacheNames) { 47 return Promise.all( 48 cacheNames.filter(function(name) { 49 return name.startsWith('multiobec-') && 50 name !== STATIC_CACHE && 51 name !== PAGES_CACHE && 52 name !== IMAGES_CACHE; 53 }).map(function(name) { 54 return caches.delete(name); 55 }) 56 ); 57 }).then(function() { 58 // Prevezmeme kontrolu nad vÅ¡etkými otvorenými tabmi 59 return self.clients.claim(); 60 }) 61 ); 62}); 63 64// ââ FETCH ââââââââââââââââââââââââââââââââââââââââââââââââââââ 65self.addEventListener('fetch', function(event) { 66 var request = event.request; 67 68 // Len GET requesty 69 if (request.method !== 'GET') { 70 return; 71 } 72 73 var url = new URL(request.url); 74 75 // Ignoruj externé URL (fonty z CDN atÄ. â nemali by byÅ¥, ale pre istotu) 76 if (url.origin !== location.origin) { 77 return; 78 } 79 80 // ââ Stratégia podľa typu requestu ââ 81 82 // 1. Statické assety â Cache First 83 if (isStaticAsset(url.pathname)) { 84 event.respondWith(cacheFirst(request, STATIC_CACHE)); 85 return; 86 } 87 88 // 2. Obrázky â Cache First 89 if (isImage(url.pathname)) { 90 event.respondWith(cacheFirst(request, IMAGES_CACHE)); 91 return; 92 } 93 94 // 3. Manifest â vždy network, necachujeme cez SW (PHP+Redis+ETag staÄÃ) 95 if (url.pathname === '/manifest' || url.pathname === '/manifest.json') { 96 return; // necháme browser fetchnúť normálne 97 } 98 99 // 4. HTML stránky â Network First s offline fallback 100 if (request.headers.get('Accept') && request.headers.get('Accept').indexOf('text/html') !== -1) { 101 event.respondWith(networkFirstWithOffline(request)); 102 return; 103 } 104}); 105 106// ââ Cache stratégie ââââââââââââââââââââââââââââââââââââââââââ 107 108/** 109 * Cache First â najprv cache, ak nie je â network â uložà do cache 110 */ 111function cacheFirst(request, cacheName) { 112 return caches.match(request).then(function(cached) { 113 if (cached) { 114 return cached; 115 } 116 return fetch(request).then(function(response) { 117 if (response.ok) { 118 var clone = response.clone(); 119 caches.open(cacheName).then(function(cache) { 120 cache.put(request, clone); 121 }); 122 } 123 return response; 124 }); 125 }); 126} 127 128/** 129 * Network First â najprv sieÅ¥, ak zlyhá â cache 130 */ 131function networkFirst(request, cacheName) { 132 return fetch(request).then(function(response) { 133 if (response.ok) { 134 var clone = response.clone(); 135 caches.open(cacheName).then(function(cache) { 136 cache.put(request, clone); 137 }); 138 } 139 return response; 140 }).catch(function() { 141 return caches.match(request); 142 }); 143} 144 145/** 146 * Network First s offline fallback â pre HTML stránky 147 * Ak sieÅ¥ zlyhá a stránka nie je v cache â zobrazà /offline 148 */ 149function networkFirstWithOffline(request) { 150 return fetch(request).then(function(response) { 151 if (response.ok) { 152 var clone = response.clone(); 153 caches.open(PAGES_CACHE).then(function(cache) { 154 cache.put(request, clone); 155 }); 156 } 157 return response; 158 }).catch(function() { 159 return caches.match(request).then(function(cached) { 160 if (cached) { 161 return cached; 162 } 163 // Offline fallback stránka 164 return caches.match('/offline'); 165 }); 166 }); 167} 168 169// ââ Helpers ââââââââââââââââââââââââââââââââââââââââââââââââââ 170 171function isStaticAsset(pathname) { 172 return /\.(css|js|woff|woff2|ttf|eot)$/.test(pathname) || 173 pathname.startsWith('/assets/'); 174} 175 176function isImage(pathname) { 177 return /\.(png|jpg|jpeg|gif|webp|svg|ico)$/.test(pathname) || 178 pathname.startsWith('/icons/') || 179 pathname.startsWith('/uploads/'); 180} 181 182// ââ Push notifikácie (Phase 2 â pripravené pre budúce použitie) ââ 183 184self.addEventListener('push', function(event) { 185 if (!event.data) return; 186 187 var data = {}; 188 try { 189 data = event.data.json(); 190 } catch (e) { 191 data = { title: 'Nová správa', body: event.data.text() }; 192 } 193 194 var options = { 195 body: data.body || '', 196 icon: data.icon || '/icons/icon-192x192.png', 197 badge: data.badge || '/icons/icon-72x72.png', 198 tag: data.tag || 'multiobec-notification', 199 data: { 200 url: data.url || '/' 201 } 202 }; 203 204 event.waitUntil( 205 self.registration.showNotification(data.title || 'Oznámenie', options) 206 ); 207}); 208 209self.addEventListener('notificationclick', function(event) { 210 event.notification.close(); 211 212 var url = event.notification.data && event.notification.data.url 213 ? event.notification.data.url 214 : '/'; 215 216 event.waitUntil( 217 self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function(clients) { 218 // Ak je už otvorený tab s touto stránkou, focusni ho 219 for (var i = 0; i < clients.length; i++) {
220 if (clients[i].url === url && 'focus' in clients[i]) { 221 return clients[i].focus(); 222 } 223 } 224 // Inak otvor nový tab 225 if (self.clients.openWindow) { 226 return self.clients.openWindow(url); 227 } 228 }) 229 ); 230});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.