PageSourceSearch

https://revistaprogresiv.ro/sw.js

js revistaprogresiv.ro collected 2026-10-02 05:04:47 UTC 9,037 bytes, 264 lines download raw bytes

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.