1/* 2 * Push-only service worker for listing Subscribe/Alerts notifications. 3 * Registered by /js/web-push-subscribe.js with scope /push-notifications/, so it 4 * never controls site pages and has no fetch handler. The legacy /sw.js and 5 * /service-worker.js files are self-unregistering kill switches; keep them separate. 6 */ 7self.addEventListener('install', function () { 8 self.skipWaiting(); 9}); 10 11self.addEventListener('activate', function (event) { 12 event.waitUntil(self.clients.claim()); 13}); 14 15// Site icon passed by web-push-subscribe.js as ?icon=/img/<site>/<file>; same-origin only. 16var SITE_ICON = (function () { 17 var value = new URL(self.location.href).searchParams.get('icon') || ''; 18 return /^\/img\/[A-Za-z0-9_\-]+\/[A-Za-z0-9_.\-]+$/.test(value) ? value : ''; 19})(); 20 21function sameOriginUrl(value) { 22 try { 23 var url = new URL(value || '/', self.location.origin); 24 return url.origin === self.location.origin ? url.href : self.location.origin + '/'; 25 } catch (e) { 26 return self.location.origin + '/'; 27 } 28} 29 30self.addEventListener('push', function (event) { 31 var data = {}; 32 if (event.data) { 33 try { 34 data = event.data.json(); 35 } catch (e) { 36 data = { body: event.data.text() }; 37 } 38 } 39 var options = { 40 body: data.body || '', 41 tag: data.tag || undefined, 42 renotify: !!data.tag, 43 data: { url: sameOriginUrl(data.url), click: typeof data.click === 'string' ? data.click : '' } 44 }; 45 if (SITE_ICON) { 46 options.icon = SITE_ICON; 47 } 48 event.waitUntil(self.registration.showNotification(data.title || self.location.hostname, options)); 49}); 50 51// Reports the click for attribution; failures are ignored so they never block navigation. 52function reportClick(token, landed) { 53 if (!token) { 54 return Promise.resolve(); 55 } 56 return fetch('/push/click', { 57 method: 'POST', 58 credentials: 'same-origin', 59 keepalive: true, 60 headers: { 'Content-Type': 'application/json' }, 61 body: JSON.stringify({ token: token, landed: landed }) 62 }).catch(function () {}); 63} 64 65self.addEventListener('notificationclick', function (event) { 66 event.notification.close(); 67 var data = event.notification.data || {}; 68 var target = data.url || self.location.origin + '/'; 69 event.waitUntil( 70 self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clients) { 71 for (var i = 0; i < clients.length; i++) { 72 if (clients[i].url === target && 'focus' in clients[i]) { 73 return clients[i].focus(); 74 } 75 } 76 return self.clients.openWindow ? self.clients.openWindow(target) : null; 77 }).then(function (client) { 78 return reportClick(data.click, !!client); 79 }, function () { 80 return reportClick(data.click, false); 81 }) 82 ); 83}); 84 85// The browser rotated or dropped the subscription; stop sending to the old endpoint. 86self.addEventListener('pushsubscriptionchange', function (event) { 87 var old = event.oldSubscription; 88 if (!old || !old.endpoint) { 89 return; 90 } 91 event.waitUntil( 92 fetch('/push/unsubscribe', { 93 method: 'POST', 94 credentials: 'same-origin', 95 headers: { 'Content-Type': 'application/json' }, 96 body: JSON.stringify({ endpoint: old.endpoint }) 97 }).catch(function () {}) 98 ); 99});
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.