1// ---- Config --------------------------------------------------------------- 2const CACHE_VERSION = 'v46'; // bump ca sÄ forÈezi update-ul SW 3const STATIC_CACHE = `rp-static-${CACHE_VERSION}`; 4const PAGES_CACHE = `rp-pages-${CACHE_VERSION}`; 5const IMG_CACHE = `rp-img-${CACHE_VERSION}`; 6const FONT_CACHE = `rp-font-${CACHE_VERSION}`; 7const OFFLINE_URL = 'https://revistaprogresiv.ro/offline/index.html'; // fÄ o paginÄ simplÄ la /offline/ 8 9// Precache strict (doar asseturi stabile) 10const PRECACHE_URLS = [ 11 'https://static.revistaprogresiv.ro/fnt/Montserrat-Regular.woff2', 12 'https://static.revistaprogresiv.ro/fnt/Montserrat-SemiBold.woff2', 13 'https://static.revistaprogresiv.ro/fnt/bitter-bold-webfont.woff2', 14 OFFLINE_URL 15]; 16 17// Domenii/URL-uri de evitat (analytics & XML) 18const BYPASS_URL_PREFIXES = [ 19 'https://revistaprogresiv.ro/ana/collect.php', 20 'https://www.googletagmanager.com/', 21 'https://clarity.ms/', 22]; 23const XML_PREFIXES = [ 24 'https://revistaprogresiv.ro/sitemap/', 25 'https://revistaprogresiv.ro/rss/', 26]; 27 28// ---- Install: precache + navigation preload -------------------------------- 29self.addEventListener('install', (event) => { 30 event.waitUntil( 31 caches.open(STATIC_CACHE).then((c) => c.addAll(PRECACHE_URLS)) 32 ); 33 self.skipWaiting(); 34}); 35 36self.addEventListener('activate', (event) => { 37 // Enable Navigation Preload (reduce lag la primele navigÄri) 38 event.waitUntil((async () => { 39 if (self.registration.navigationPreload) { 40 await self.registration.navigationPreload.enable(); 41 } 42 const keys = await caches.keys(); 43 await Promise.all( 44 keys.map((k) => { 45 if (![STATIC_CACHE, PAGES_CACHE, IMG_CACHE, FONT_CACHE].includes(k)) { 46 return caches.delete(k); 47 } 48 }) 49 ); 50 await self.clients.claim(); 51 })()); 52}); 53 54// ---- Helpers --------------------------------------------------------------- 55const isGet = (req) => req.method === 'GET'; 56const isNav = (req) => req.mode === 'navigate' || (req.destination === 'document'); 57const urlStartsWith = (url, prefix) => url.indexOf(prefix) === 0; 58const shouldBypass = (url) => BYPASS_URL_PREFIXES.some(p => urlStartsWith(url, p)); 59const isXml = (url) => 60 (XML_PREFIXES.some(p => urlStartsWith(url, p)) && url.endsWith('.xml')); 61 62const putInCache = async (cacheName, req, res) => { 63 try { 64 const cache = await caches.open(cacheName); 65 // Clone doar rÄspunsurile âbasic/opaqueâ OK 66 if (res && res.status === 200 && (res.type === 'basic' || res.type === 'opaque')) { 67 cache.put(req, res.clone()); 68 } 69 } catch {} 70 return res; 71}; 72 73// ---- Fetch: strategii pe tipuri -------------------------------------------- 74// 1) NavigaÈii HTML: network-first + fallback offline + navigation preload 75// 2) Imagini: stale-while-revalidate (servim din cache, actualizÄm dupÄ) 76// 3) Fonturi: cache-first (rareori se schimbÄ) 77// 4) Restul staticelor: cache-first 78self.addEventListener('fetch', (event) => { 79 const { request } = event; 80 const url = request.url; 81 82 if (!isGet(request)) { 83 // LÄsÄm în pace POST/PUT etc (ex: /ana/collect.php) 84 return; 85 } 86 if (shouldBypass(url) || isXml(url)) { 87 // FÄrÄ cache pentru analytics & XML 88 return; 89 } 90 91 // NavigaÈii (HTML) 92 if (isNav(request)) { 93 event.respondWith((async () => { 94 try { 95 // FoloseÈte rÄspunsul preloaded dacÄ existÄ (porneÈte în paralel cu SW boot) 96 const preload = await event.preloadResponse; 97 const networkRes = preload || await fetch(request); 98 await putInCache(PAGES_CACHE, request, networkRes.clone()); 99 return networkRes; 100 } catch { 101 // DacÄ e offline, încearcÄ din cache, altfel pagina OFFLINE 102 const cached = await caches.match(request); 103 return cached || (await caches.match(OFFLINE_URL)); 104 } 105 })()); 106 return; 107 } 108 109 // Imagini 110 if (request.destination === 'image') { 111 event.respondWith((async () => { 112 const cached = await caches.match(request); 113 const networkPromise = fetch(request) 114 .then((res) => putInCache(IMG_CACHE, request, res.clone())) 115 .catch(() => null); 116 return cached || networkPromise || (await caches.match(OFFLINE_URL)); 117 })()); 118 return; 119 } 120 121 // Fonturi 122 if (request.destination === 'font') { 123 event.respondWith((async () => { 124 const cached = await caches.match(request); 125 if (cached) return cached;
126 try { 127 const res = await fetch(request); 128 return await putInCache(FONT_CACHE, request, res.clone()); 129 } catch { 130 return cached || Response.error(); 131 } 132 })()); 133 return; 134 } 135 136 // Alte resurse statice (CSS/JS/etc.) â cache-first 137 event.respondWith((async () => { 138 const cached = await caches.match(request); 139 if (cached) return cached; 140 try { 141 const res = await fetch(request); 142 return await putInCache(STATIC_CACHE, request, res.clone()); 143 } catch { 144 return cached || Response.error(); 145 } 146 })()); 147}); 148 149// ---- Messaging: update SW fÄrÄ aÈteptare ----------------------------------- 150self.addEventListener('message', (event) => { 151 if (event.data && event.data.action === 'skipWaiting') { 152 self.skipWaiting(); 153 } 154}); 155 156// ---- Push: suport payload JSON + IMAGINE + deeplink + acÈiuni -------------- 157// Trimite din backend: { title, body, url, image, icon?, badge? } 158// ---- Push: robust cu log + prefetch + tag unic + fallback ------------------ 159self.addEventListener('push', (event) => { 160 event.waitUntil((async () => { 161 let data = {}; 162 try { 163 data = event.data ? event.data.json() : {}; 164 } catch { 165 try { data = { body: event.data && event.data.text() }; } catch {} 166 } 167 168 // Log în consola SW ca sÄ vedem exact ce vine 169 //try { console.log('[SW] push payload:', data); } catch {} 170 171 const title = data.title || 'Revista Progresiv'; 172 173 // Tag unic implicit ca sÄ nu înlocuim notificÄri anterioare (Èi sÄ nu pierdem layoutul cu imagine) 174 const uniqueTag = data.tag || ('rp-news-' + Date.now()); 175 176 // Baza opÈiunilor 177 const base = { 178 body: data.body || '', 179 icon: data.icon || '/images/icon-192.png', 180 badge: data.badge || '/images/icon-192.png', 181 tag: uniqueTag, 182 data: { url: data.url || '/' } 183 }; 184 185 const img = data.image; 186 187 // DacÄ avem imagine, încercÄm întâi fÄrÄ actions (pe Windows creÈte Èansa de âbig pictureâ) 188 if (img) { 189 try { 190 // Pre-touch: tolerÄm rÄspuns opaque; important e sÄ nu arunce 191 await fetch(img, { mode: 'no-cors', cache: 'no-cache' }); 192 const optsWithImage = { ...base, image: img }; 193 await self.registration.showNotification(title, optsWithImage); 194 return; // succes, am afiÈat cu imagine 195 } catch (e) { 196 //try { console.warn('[SW] image prefetch/show failed, fallback w/o image:', e); } catch {} 197 // cÄdem mai jos, vom afiÈa fÄrÄ imagine 198 } 199 } 200 201 // Fallback fÄrÄ imagine sau când nu existÄ `image` 202 // (dacÄ vrei actions, aici e locul â dupÄ ce am încercat deja âbig imageâ) 203 const fallback = { 204 ...base, 205 actions: data.actions || [ 206 { action: 'open', title: 'Deschide' } 207 ] 208 }; 209 await self.registration.showNotification(title, fallback); 210 })()); 211}); 212 213 214 215/* 216self.addEventListener('push', (event) => { 217 let data = {}; 218 try { data = event.data ? event.data.json() : {}; } 219 catch { data = { body: event.data?.text() }; } 220 221 const title = data.title || 'Revista Progresiv'; 222 const options = { 223 body: data.body || '', 224 icon: data.icon || '/android-chrome-192x192.png', 225 badge: data.badge || '/android-chrome-192x192.png', 226 image: data.image || undefined, // â imagine mare în notificare (Chrome/Android) 227 tag: data.tag || 'rp-news', 228 data: { url: data.url || '/' }, 229 actions: data.actions || [ 230 { action: 'open', title: 'Deschide' }, 231 { action: 'save', title: 'CiteÈte mai târziu' } 232 ] 233 }; 234 235 // DacÄ imaginea cauzeazÄ o eroare, reafiÈÄm fÄrÄ `image` 236 event.waitUntil((async () => { 237 try { 238 await self.registration.showNotification(title, options); 239 } catch (e) { 240 const fallback = { ...options }; 241 delete fallback.image; 242 await self.registration.showNotification(title, fallback); 243 } 244 })()); 245}); 246*/ 247 248// Deschidere la click + eventualÄ acÈiune 249self.addEventListener('notificationclick', (event) => { 250 event.notification.close(); 251 const url = event.notification.data && event.notification.data.url ? event.notification.data.url : '/'; 252 event.waitUntil((async () => { 253 const allClients = await clients.matchAll({ type: 'window', includeUncontrolled: true }); 254 // FocalizeazÄ un tab existent dacÄ deschis deja 255 for (const client of allClients) { 256 if (client.url && url.startsWith(new URL(client.url, self.location.origin).origin)) { 257 await client.focus(); 258 await client.navigate(url); 259 return; 260 } 261 } 262 await clients.openWindow(url); 263 })()); 264});
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.