1import { initializeApp } from "https://www.gstatic.com/firebasejs/10.13.0/firebase-app.js"; 2import { deleteToken, getMessaging, getToken, onMessage } from 'https://www.gstatic.com/firebasejs/10.13.0/firebase-messaging.js'; 3 4import { config, vapidKey } from "https://cdn.detik.net.id/libs/kayumanis/v1/haibunda.js"; 5import { idbSet, idbGet } from "https://cdn.detik.net.id/libs/kayumanis/v1/utils.js"; 6 7// Initialize Firebase 8initializeApp(config); 9const messaging = getMessaging(); 10 11onMessage(messaging, async (payload) => { 12 console.log('Message received.', payload); 13 14 if(payload.data.url.indexOf('haibunda.com') < 0) return; 15 if (Notification.permission !== 'granted') return; 16 17 const { title, content, icon, id, url, app } = payload.data; 18 19 const notification = new Notification(title, { 20 body: content, 21 icon 22 }); 23 24 const trackerUrl = payload.data?.tracker; 25 if (trackerUrl) { 26 fetch(trackerUrl, { 27 method: 'GET', 28 mode: 'no-cors', 29 keepalive: true, 30 credentials: 'omit' 31 }).catch(err => { 32 console.log('Tracker fetch failed, fallback to beacon:', err); 33 if (navigator.sendBeacon) { 34 navigator.sendBeacon(trackerUrl); 35 } 36 }); 37 } 38 39 40 let device = '3'; 41 let token; 42 43 try { 44 token = await getToken(messaging, { vapidKey }); 45 const deviceData = await new Promise((resolve) => { 46 const req = indexedDB.open('device-token-database', 1); 47 48 req.onsuccess = (event) => { 49 const db = event.target.result; 50 const getReq = db 51 .transaction('device-token-store', 'readonly') 52 .objectStore('device-token-store') 53 .get('device-token'); 54 55 getReq.onsuccess = (e) => resolve(e.target.result); 56 getReq.onerror = () => resolve(null); 57 }; 58 59 req.onerror = () => resolve(null); 60 }); 61 62 if (deviceData && deviceData.device) { 63 device = deviceData.device.toString(); 64 } else { 65 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i 66 .test(navigator.userAgent)) { 67 device = '4'; 68 } 69 70 saveDeviceTokenToIndexedDB(token, device); 71 } 72 73 const lastVerify = await idbGet('kayumanis', 'lastVerifyTokenAt'); 74 const nowVerify = Date.now(); 75 if (!lastVerify || nowVerify - lastVerify > 30000) { 76 await fetch('https://kayumanis.detik.com/api/verify/', { 77 method: 'POST', 78 headers: { 'Content-Type': 'application/json' }, 79 body: JSON.stringify({ 80 token, 81 application: config.application, 82 device, 83 notif_id: id 84 }) 85 }); 86 87 await idbSet('kayumanis', 'lastVerifyTokenAt', nowVerify); 88 } 89 } catch (e) { 90 console.error('foreground verify error:', e); 91 } 92 93 notification.onclick = async (event) => { 94 event.preventDefault(); 95 notification.close(); 96 window.open(url, '_blank'); 97 98 try { 99 const formData = new FormData(); 100 formData.append('id', id); 101 formData.append('token', token); 102 formData.append('app', app); 103 formData.append('platform', device); 104 105 await fetch('https://kayumanis.detik.com/api/app_open/', { 106 method: 'POST', 107 body: formData 108 }); 109 } catch (e) { 110 console.error('app_open error:', e); 111 } 112 }; 113}); 114 115function requestToken() { 116 getToken(messaging, {vapidKey: vapidKey}) 117 .then((currentToken) => { 118 if (currentToken) { 119 sendTokenToServer(currentToken); 120 } else { 121 // Show permission request. 122 console.log('No registration token available. Request permission to generate one.'); 123 setTokenSentToServer(false); 124 } 125 }).catch((err) => { 126 console.log('An error occurred while retrieving token. ', err); 127 setTokenSentToServer(false); 128 requestToken(); 129 }); 130} 131 132function sendTokenToServer(currentToken) { 133 if (!isTokenSentToServer()) {
134 console.log("Sending token to server..."); 135 sendTokenToAppsDev(currentToken); 136 setTokenSentToServer(true); 137 } else { 138 console.log("Token already sent to server so won't send it again " + "unless it changes"); 139 } 140} 141 142function sendTokenToAppsDev(currentToken) { 143 let url = "https://kayumanis.detik.com/api/media/"; 144 let method = "POST"; 145 146 let device = 3; 147 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 148 device = 4; 149 } 150 151 let postData = new FormData(); 152 postData.append("application", config.application); 153 postData.append("token", currentToken); 154 postData.append("device", device); 155 156 let uv = getCookieNotif("__dtma"); 157 if (uv !== "") { 158 postData.append("uv", uv); 159 } 160 161 let async = true; 162 let request = new XMLHttpRequest(); 163 request.withCredentials = true; 164 request.onload = function () { 165 let status = request.status; 166 let data = request.responseText; 167 }; 168 request.open(method, url, async); 169 request.send(postData); 170 171 setCookieNotif('__fcm_token', currentToken); 172 173 saveDeviceTokenToIndexedDB(currentToken, device); 174} 175 176function isTokenSentToServer() { 177 if (window.localStorage.getItem("sentToServer") == 1) { 178 return true; 179 } 180 return false; 181} 182 183function setTokenSentToServer(sent) { 184 window.localStorage.setItem("sentToServer", sent ? 1 : 0); 185 186 if (!sent) { 187 deleteDeviceFromIndexedDB(); 188 } 189} 190 191function requestPermission() { 192 console.log('Requesting permission...'); 193 Notification.requestPermission().then((permission) => { 194 if (permission === 'granted') { 195 requestToken(); 196 console.log("Notification permission granted."); 197 } else { 198 console.log('Unable to get permission to notify.'); 199 } 200 }); 201} 202 203function deleteTokenFromFirebase() { 204 getToken(messaging, {vapidKey: vapidKey}) 205 .then((currentToken) => { 206 deleteToken(messaging).then(() => { 207 console.log('Token deleted.', currentToken); 208 setTokenSentToServer(false); 209 }).catch((err) => { 210 console.log('Unable to delete token. ', err); 211 setTokenSentToServer(false); 212 }); 213 }).catch((err) => { 214 console.log("Error retrieving Instance ID token. ", err); 215 setTokenSentToServer(false); 216 }); 217} 218 219function getCookieNotif(cname) { 220 var name = cname + "="; 221 var decodedCookie = decodeURIComponent(document.cookie); 222 var ca = decodedCookie.split(";"); 223 for (var i = 0; i < ca.length; i++) { 224 var c = ca[i]; 225 while (c.charAt(0) == " ") { 226 c = c.substring(1); 227 } 228 if (c.indexOf(name) == 0) { 229 return c.substring(name.length, c.length); 230 } 231 } 232 return ""; 233} 234 235function setCookieNotif(name, value, days) { 236 var expires = ""; 237 if (days) { 238 var date = new Date(); 239 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 240 expires = "; expires=" + date.toUTCString(); 241 } 242 document.cookie = name + "=" + (value || "") + expires + "; path=/"; 243} 244 245function resendToken() { 246 const lastValidation = localStorage.getItem("lastTokenValidationAt"); 247 const now = Date.now(); 248 if (lastValidation && now - parseInt(lastValidation) < 86400000) { 249 return; 250 } 251 252 getToken(messaging, {vapidKey: vapidKey}) 253 .then(function (currentToken) { 254 if (!currentToken) { 255 setTokenSentToServer(false); 256 return; 257 } 258 259 var platform = 3; 260 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 261 platform = 4; 262 } 263 $.post("https://kayumanis.detik.com/api/validation/", { 264 token: currentToken, 265 device: platform, 266 application: config.application 267 }).done(function (msg, _, jqXHR) { 268 if (jqXHR.status === 202) { 269 localStorage.setItem("lastTokenValidationAt", Date.now().toString()); 270 } else { 271 deleteTokenFromFirebase(); 272 deleteDeviceFromIndexedDB(); 273 274 localStorage.removeItem("lastTokenValidationAt"); 275 } 276 }); 277 }); 278} 279 280 281function initNotif() { 282 if (isTokenSentToServer()) { 283 resendToken(); 284 } else { 285 requestPermission(); 286 } 287} 288 289initNotif(); 290 291function saveDeviceTokenToIndexedDB(currentToken, device) { 292 let requestIndexDb = indexedDB.open('device-token-database', 1); 293 294 try {
295 requestIndexDb.onupgradeneeded = function(event) { 296 let db = event.target.result; 297 db.createObjectStore('device-token-store', { keyPath: 'id' }); 298 } 299 300 requestIndexDb.onsuccess = function(event) { 301 let db = event.target.result; 302 let transaction = db.transaction('device-token-store', 'readwrite'); 303 let objectStore = transaction.objectStore('device-token-store'); 304 let key = "device-token"; 305 objectStore.add ({'id': key, token: currentToken, device: device}); 306 } 307 308 requestIndexDb.onerror = function(event) { 309 console.log("Error opening indexedDB: ", event); 310 } 311 } catch (e) { 312 console.log("Cannot save device to indexedDB", e); 313 } 314} 315 316function deleteDeviceFromIndexedDB() { 317 let requestIndexDb = indexedDB.open('device-token-database', 1); 318 try { 319 requestIndexDb.onupgradeneeded = function(event) { 320 let db = event.target.result; 321 db.createObjectStore('device-token-store', { keyPath: 'id' }); 322 } 323 324 requestIndexDb.onsuccess = function(event) { 325 let db = event.target.result; 326 let transaction = db.transaction('device-token-store', 'readwrite'); 327 let objectStore = transaction.objectStore('device-token-store'); 328 objectStore.delete("device-token"); 329 } 330 331 requestIndexDb.onerror = function(event) { 332 console.log("Error opening indexedDB: ", event); 333 } 334 } catch (e) { 335 console.log("Cannot delete device from indexedDB", e); 336 } 337} 338 339function isMobileDevice() { 340 return /Mobi|Android|iPad|iPhone|iPod|IEMobile|Opera Mini/i.test(navigator.userAgent); 341} 342 343if (isMobileDevice()) { 344document.querySelector('#btn_pnf').addEventListener('click', () => { // add a click event listener 345 requestPermission(); 346}); 347} else { 348document.querySelector('#yes-notif').addEventListener('click', () => { // add a click event listener 349 requestPermission(); 350}); 351}
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.