1'use strict'; 2 3const CACHE_VERSION = 'v1.0.1'; 4const CACHE_NAME = 'non14-cache-' + CACHE_VERSION; 5const RUNTIME_CACHE = 'non14-runtime-' + CACHE_VERSION; 6 7const CACHE_URLS = [ 8 '/', 9 '/offline.html', 10 '/assets/non/style/style.css', 11 '/assets/non/icons/android-icon-192x192.png', 12 '/assets/non/icons/favicon-96x96.png', 13 'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.rtl.min.css', 14 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', 15 'https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;500;600;700;800;900&display=swap' 16]; 17 18self.addEventListener('install', function (event) { 19 event.waitUntil( 20 caches.open(CACHE_NAME) 21 .then(function (cache) { 22 return cache.addAll(CACHE_URLS); 23 }) 24 .then(function () { 25 return self.skipWaiting(); 26 }) 27 .catch(function (error) { 28 console.error('Cache install failed:', error); 29 }) 30 ); 31}); 32 33self.addEventListener('activate', function (event) { 34 event.waitUntil( 35 caches.keys().then(function (cacheNames) { 36 return Promise.all( 37 cacheNames.map(function (cacheName) { 38 if (cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE) { 39 return caches.delete(cacheName); 40 } 41 }) 42 ); 43 }).then(function () { 44 return self.clients.claim(); 45 }) 46 ); 47}); 48 49self.addEventListener('fetch', function (event) { 50 if (event.request.method !== 'GET') { 51 return; 52 } 53 54 const url = new URL(event.request.url); 55 56 // Don't cache HTML pages or API calls - always fetch fresh 57 if (url.origin === location.origin && 58 (event.request.mode === 'navigate' || 59 url.pathname.startsWith('/api/') || 60 url.pathname === '/' || 61 url.pathname.match(/\/\d+$/))) { 62 return; 63 } 64 65 if (url.origin !== location.origin && !url.href.includes('cdn.')) { 66 return; 67 } 68 69 event.respondWith( 70 caches.match(event.request) 71 .then(function (response) { 72 if (response) { 73 return response; 74 } 75 76 return fetch(event.request) 77 .then(function (response) { 78 if (!response || response.status !== 200 || response.type === 'error') { 79 return response; 80 } 81 82 const shouldCache = 83 url.pathname.endsWith('.css') || 84 url.pathname.endsWith('.js') || 85 url.pathname.endsWith('.png') || 86 url.pathname.endsWith('.jpg') || 87 url.pathname.endsWith('.jpeg') || 88 url.pathname.endsWith('.svg') || 89 url.pathname.endsWith('.woff') || 90 url.pathname.endsWith('.woff2') || 91 url.href.includes('cdn.'); 92 93 if (shouldCache) { 94 const responseToCache = response.clone(); 95 caches.open(RUNTIME_CACHE).then(function (cache) { 96 cache.put(event.request, responseToCache); 97 }); 98 } 99 100 return response; 101 }) 102 .catch(function () { 103 if (event.request.mode === 'navigate') { 104 return caches.match('/offline.html'); 105 } 106 return new Response('Network error', { 107 status: 408, 108 headers: { 'Content-Type': 'text/plain' } 109 }); 110 }); 111 }) 112 ); 113}); 114 115self.addEventListener('push', function (event) { 116 if (!event.data) { 117 return; 118 } 119 120 let payload; 121 122 try { 123 payload = event.data.json(); 124 } catch (error) { 125 payload = { 126 title: 'New notification', 127 body: event.data.text() 128 }; 129 } 130 131 const title = payload.title || 'New notification'; 132 const options = { 133 body: payload.body || '', 134 icon: payload.icon || '/assets/non/icons/android-icon-192x192.png', 135 badge: payload.badge || '/assets/non/icons/favicon-96x96.png', 136 image: payload.image, 137 vibrate: payload.vibrate || [200, 100, 200], 138 requireInteraction: Boolean(payload.requireInteraction), 139 data: payload.data || {}, 140 actions: payload.actions || [] 141 }; 142 143 if (payload.tag) { 144 options.tag = payload.tag; 145 options.renotify = Boolean(payload.renotify); 146 } 147 148 event.waitUntil(self.registration.showNotification(title, options)); 149}); 150 151self.addEventListener('notificationclick', function (event) { 152 event.notification.close(); 153 154 const notificationData = event.notification.data || {}; 155 const targetUrl = notificationData.url || notificationData.link || notificationData.navigateTo || '/'; 156 157 event.waitUntil( 158 self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) { 159 for (let i = 0; i < clientList.length; i += 1) { 160 const client = clientList[i];
161 if (client.url === targetUrl && 'focus' in client) { 162 return client.focus(); 163 } 164 } 165 166 if (self.clients.openWindow) { 167 return self.clients.openWindow(targetUrl); 168 } 169 170 return undefined; 171 }) 172 ); 173}); 174 175self.addEventListener('notificationclose', function (event) { 176 if (!event.notification || !event.notification.data) { 177 return; 178 } 179 180 const closeEndpoint = event.notification.data.onCloseUrl; 181 if (!closeEndpoint) { 182 return; 183 } 184 185 event.waitUntil( 186 fetch(closeEndpoint, { 187 method: 'POST', 188 credentials: 'same-origin' 189 }).catch(function () { 190 return undefined; 191 }) 192 ); 193});
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.