PageSourceSearch

https://www.mitalee.com.my/pwabuilder-sw.js?v=2025-11-06-02

js mitalee.com.my collected 2026-10-06 11:00:05 UTC 5,748 bytes, 183 lines download raw bytes

1/* ==========================================================
2   SERVICE WORKER – Mobile Safe & Integrity Compatible
3   (fetch handler corrected)
4   ========================================================== */
5
6// 🔸 Bump this when you deploy updates
7const CACHE_VERSION = 'v7';
8const CACHE_NAME = `npcdn-cache-${CACHE_VERSION}`;
9const OFFLINE_URL = 'ex_offline.html';
10
11/* ========================
12   🧱 INSTALL EVENT
13   ======================== */
14self.addEventListener('install', (event) => {
15  console.log('[SW] Installing version', CACHE_VERSION);
16
17  event.waitUntil(
18    (async () => {
19      const cache = await caches.open(CACHE_NAME);
20      try {
21        await cache.add(new Request(OFFLINE_URL, { cache: 'reload' }));
22      } catch (e) {
23        console.warn('[SW] Could not cache offline page:', e);
24      }
25    })()
26  );
27
28  self.skipWaiting();
29});
30
31/* ========================
32   🚀 ACTIVATE EVENT
33   ======================== */
34self.addEventListener('activate', (event) => {
35  console.log('[SW] Activating and cleaning old caches...');
36
37  event.waitUntil(
38    (async () => {
39      const keys = await caches.keys();
40      await Promise.all(
41        keys.map((key) => {
42          if (key !== CACHE_NAME) {
43            console.log('[SW] Deleting old cache:', key);
44            return caches.delete(key);
45          }
46        })
47      );
48
49      if ('navigationPreload' in self.registration) {
50        await self.registration.navigationPreload.enable();
51      }
52
53      await self.clients.claim();
54    })()
55  );
56});
57
58/* ========================
59   🌐 FETCH EVENT  (corrected)
60   ========================
61   THE FIX:
62   - HTML navigations: network-first with offline fallback (unchanged behaviour).
63   - CSS / JS / images / fonts: NOT intercepted. We let the browser fetch them
64     natively. That preserves Subresource Integrity, uses the normal HTTP cache
65     (so most assets on refresh come from disk and never hit the network or the
66     WAF), and removes the `cache:'reload'` burst that was forcing every asset
67     through the "Checking your browser" challenge on shared-IP networks (Maxis).
68   ======================== */
69self.addEventListener('fetch', (event) => {
70  const req = event.request;
71
72  // Only touch GET navigations. Everything else falls through
73  // to the browser's default handling (no event.respondWith).
74  if (req.method !== 'GET') return;
75
76  if (req.mode === 'navigate') {
77    event.respondWith(
78      (async () => {
79        try {
80          const preloadResponse = await event.preloadResponse;
81          if (preloadResponse) return preloadResponse;
82          return await fetch(req);
83        } catch (error) {
84          console.warn('[SW] Navigation fetch failed; serving offline page.', error);
85          const cache = await caches.open(CACHE_NAME);
86          return (await cache.match(OFFLINE_URL)) || Response.error();
87        }
88      })()
89    );
90    return;
91  }
92
93  // No branch for CSS/JS/images: intentionally left to the browser.
94});
95
96/* ========================
97   🔔 PUSH NOTIFICATION HANDLING
98   ======================== */
99self.addEventListener('push', (event) => {
100  if (!(self.Notification && self.Notification.permission === 'granted')) return;
101
102  const sendNotification = (payload) => {
103    const title = payload.title || 'Notification';
104    const body = payload.body || '';
105    const icon = payload.icon || '/icon.png';
106    const data = { url: payload.url || '/' };
107
108    return self.registration.showNotification(title, {
109      body,
110      icon,
111      data,
112    });
113  };
114
115  if (event.data) {
116    const payload = event.data.json();
117    event.waitUntil(sendNotification(payload));
118  }
119});
120
121/* ========================
122   🔄 PUSH SUBSCRIPTION CHANGE
123   ======================== */
124self.addEventListener('pushsubscriptionchange', (event) => {
125  event.waitUntil(
126    self.registration.pushManager
127      .subscribe({
128        userVisibleOnly: true,
129        applicationServerKey: urlB64ToUint8Array(
130          'BHjtiK75djCrpfhTHanqvpvjvyfk6f2g_uPQHTSQgdI9EtiKIwIHfrVQXuT63RaeQSWun5L0ggcrWIbI3axDUv8'
131        ),
132      })
133      .then((newSub) => {
134        self.clients.matchAll().then((clients) => {
135          clients.forEach((client) =>
136            client.postMessage({ type: 'new-subscription', subscription: newSub })
137          );
138        });
139      })
140      .catch((err) => console.error('[SW] Failed to resubscribe:', err))
141  );
142});
143
144/* ========================
145   🖱️ NOTIFICATION CLICK
146   ======================== */
147self.addEventListener('notificationclick', (event) => {
148  event.notification.close();
149  const targetUrl = event.notification.data.url;
150
151  event.waitUntil(
152    clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => {
153      for (const client of windowClients) {
154        if (client.url === targetUrl && 'focus' in client) {
155          return client.focus();
156        }
157      }
158      return clients.openWindow(targetUrl);
159    })
160  );
161});
162
163/* ========================
164   🧹 MANUAL CACHE CLEAR MESSAGE
165   ======================== */
166self.addEventListener('message', (event) => {
167  if (event.data && event.data.type === 'CLEAR_ALL_CACHE') {
168    caches.keys().then((keys) => keys.forEach((k) => caches.delete(k)));
169    console.log('[SW] Manual cache clear requested.');
170  }
171});
172
173/* ========================
174   ⚙️ HELPER: VAPID key conversion
175   ======================== */
176function urlB64ToUint8Array(base64String) {
177  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
178  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
179  const rawData = atob(base64);
180  const output = new Uint8Array(rawData.length);
181  for (let i = 0; i < rawData.length; ++i) output[i] = rawData.charCodeAt(i);
182  return output;
183}

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.