1// $Id: mobileApp.js 39996 2026-05-18 08:09:58Z brendon $ 2// Function definitions specific to the app environment/platform of a site. 3 4// Add to push notifications uniqueID array 5function addToUniqueResults (notifications) { 6 var uniqueID = new Array; 7 for (i = 0; i < notifications.length; i++) { 8 uniqueID.push(notifications[i].additionalData.uniqueID); 9 } 10 return uniqueID.toString(); 11} 12 13// If the search string exists, then build params from it 14function updateUrlParameter(uri, key, value) { 15 // remove the hash part before operating on the uri 16 var i = uri.indexOf('#'); 17 var hash = i === -1 ? '' : uri.substr(i); 18 uri = i === -1 ? uri : uri.substr(0, i); 19 20 var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"); 21 var separator = uri.indexOf('?') !== -1 ? "&" : "?"; 22 if (uri.match(re)) { 23 uri = uri.replace(re, '$1' + key + "=" + value + '$2'); 24 } else { 25 uri = uri + separator + key + "=" + value; 26 } 27 return uri + hash; // finally append the hash as well 28} 29 30// AJAX submission 31function sendAjaxRequest (data, url, async) { 32 if (typeof async == "undefined") { 33 async = true; 34 } 35 return $.ajax({ 36 type: "POST", 37 url: url, 38 async: async, 39 data: data 40 }); 41} 42 43function versionCompare (left, right) { 44 // source: https://gist.github.com/alexey-bass/1115557 45 // -1 = left is LOWER than right 46 // 0 = they are equal 47 // 1 = left is GREATER = right is LOWER 48 // And FALSE if one of input versions are not valid 49 if (typeof left + typeof right != 'stringstring') 50 return false; 51 52 var a = left.split('.'), 53 b = right.split('.'), 54 len = Math.max(a.length, b.length); 55 56 for (i = 0; i < len; i++) { 57 if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { 58 return 1; 59 } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { 60 return -1; 61 } 62 } 63 64 return 0; 65} 66 67// Extract a query parameter value 68function getParameterByName(parameter, url) { 69 if (!url) { 70 url = window.location.href; 71 } 72 parameter = parameter.replace(/[\[\]]/g, "\\$&"); 73 var regex = new RegExp("[?&]" + parameter + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); 74 if (!results) { 75 return null; 76 } 77 if (!results[2]) { 78 return ''; 79 } 80 return decodeURIComponent(results[2].replace(/\+/g, " ")); 81} 82 83// Pass a key name and value, will add that key to the storage, or update that key's value if it already exists 84function setLocalStorageItem (key, value) { 85 localStorage.setItem(key, value); 86} 87 88// Pass a key name, will return that key's value. 89function getLocalStorageItem (key) { 90 return localStorage.getItem(key); 91} 92 93// Pass a key name, will remove that key from the storage 94function removeLocalStorageItem (key) { 95 if (localStorage.getItem(key) !== null) { 96 localStorage.removeItem(key); 97 } 98} 99 100// Read a cookie value 101function readCookie (name) { 102 var nameEQ = name + "="; 103 var ca = document.cookie.split(';'); 104 for (var i = 0; i < ca.length; i++) { 105 var c = ca[i]; 106 while (c.charAt(0)==' ') c = c.substring(1,c.length); 107 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 108 } 109 return null; 110} 111 112// PWA - detect if app was launched in standalone mode 113function detectStandalone () { 114 var isInStandaloneMode = (window.matchMedia('(display-mode: standalone)').matches) || (window.navigator.standalone); 115 return (isInStandaloneMode); 116} 117 118// PWA - Display the native like notification 119function showUpdateBar () { 120 var toastBar = document.getElementById('backExitsApp'); 121 if (typeof toastBar != 'undefined' && toastBar != null) { 122 if (sessionStorage.getItem("showUpdateBar") != null) { 123 sessionStorage.removeItem('showUpdateBar'); 124 } 125 $(toastBar).show(); 126 setTimeout(function () { 127 $(toastBar).hide(); 128 }, 4000) 129 } 130} 131 132// PWA - check the state of a users permission 133function subscriptionStateChange() { 134 var deviceRegistered = JSON.parse(getLocalStorageItem('deviceRegistered')); 135 var isSubscribed = (Notification.permission == "granted") ? true : false; 136 if (deviceRegistered != null && (deviceRegistered.uuid != null && deviceRegistered.uuid != false)) { 137 // Update the users notification and permission state 138 var data = {"uuid": deviceRegistered.uuid, "subscribed": isSubscribed}; 139 sendAjaxRequest(data, app_pushPermission) 140 .done(function(response) { 141 setLocalStorageItem('deviceRegistered', JSON.stringify({"uuid": deviceRegistered.uuid, "provider": pushProvider})); 142 }); 143 } 144} 145 146// check if the Progressive Web App (PWA) is already installed on a user's device 147// Android only 148function detectPWAInstalled() { 149 var isAppInstalled = true; 150 return new Promise(function(resolve) { 151 // user can get an installation prompt meaning the app is not installed 152 if ("onbeforeinstallprompt" in window) { 153 window.addEventListener('beforeinstallprompt', function (e) { 154 isAppInstalled = false;
155 }, false); 156 } else { 157 isAppInstalled = false; 158 } 159 setTimeout(function() { 160 resolve(isAppInstalled); 161 }, 2000); 162 }); 163} 164 165// PWA - access the registration token 166function requestPermissionForToken() { 167 return detectPWAInstalled() 168 .then(function(isAppInstalled) { 169 if (sessionStatus != "guest") { 170 if (viaPWA) { 171 // ask the user for permission notification 172 return notificationRequestPermission() 173 .then(function(permissionGranted){ 174 var permission = (permissionGranted == "granted") ? true : false; 175 return permission; 176 }); 177 } 178 } 179 // returns the current permission 180 // must still handle foreground and background notifications 181 var permissionGranted = (isAppInstalled && Notification.permission == 'granted') ? true : false; 182 return permissionGranted; 183 }); 184} 185 186// Check whether notification permissions have been granted 187function notificationRequestPermission () { 188 // We need to ask the user for permission 189 return new Promise(function(resolve) { 190 if (Notification.permission === "default") { 191 Notification.requestPermission() 192 .then(function(permission){ 193 // granted: user has accepted the request 194 // default: user has dismissed the notification permission popup 195 // denied: user has denied the request 196 resolve(permission); 197 }); 198 } else { 199 resolve(Notification.permission); 200 } 201 }); 202} 203 204// PWA - send the registration token to the server 205function sendTokenToServer(token, appID) { 206 var deviceRegistered = JSON.parse(getLocalStorageItem('deviceRegistered')); 207 if (deviceRegistered == null || (deviceRegistered.uuid == null || deviceRegistered.uuid == false)) { 208 // if onesignal storage values exists, delete first 209 removeLocalStorageItem("pwaInstalled"); 210 } else { 211 if (deviceRegistered != null && deviceRegistered.provider != pushProvider) { 212 if ((deviceRegistered.provider != "huawei" && pushProvider == "huawei") || (deviceRegistered.provider == "huawei" && pushProvider == "firebase")) { 213 removeLocalStorageItem("pwaInstalled"); 214 removeLocalStorageItem("deviceRegistered"); 215 deviceRegistered = JSON.parse(getLocalStorageItem('deviceRegistered')); 216 } 217 } 218 } 219 // using localStorage to set a flag, which will determine if the app was installed or not 220 var pwaInstalled = JSON.parse(getLocalStorageItem('pwaInstalled')); 221 if ((token != null) && (pwaInstalled == null || pwaInstalled.senderid != token)) { 222 var data = {"senderid": token, "pwaPlatform": pwaPlatform, "appID": appID, "provider": pushProvider}; 223 if (deviceRegistered != null && (deviceRegistered.uuid != null && deviceRegistered.uuid != false)) { 224 data.uuid = deviceRegistered.uuid; 225 } else { 226 var cookieValue = getCookie("pwaInstalled"); 227 if (cookieValue != null) { 228 cookieValue = JSON.parse(cookieValue).uuid; 229 data.uuid = cookieValue; 230 } 231 } 232 var hasPushPermission = (Notification.permission == "granted") ? 1 : 0; 233 data.subscribed = hasPushPermission; 234 setLocalStorageItem('pwaInstalled', JSON.stringify({"senderid": token, "subscribed": Notification.permission})); 235 // Use this method to register the users device 236 sendAjaxRequest(data, app_pushToken) 237 .done(function(response) { 238 var responseData = jQuery.parseJSON(response); 239 if (responseData.deviceRegistered != false) { 240 setLocalStorageItem('deviceRegistered', JSON.stringify({"uuid": responseData.deviceRegistered, "provider": pushProvider})); 241 } 242 }); 243 } 244} 245 246// PWA - Detect if web app is running as PWA 247function handleAppEnvironment () { 248 // Detects if device is in standalone mode 249 if (detectStandalone()) { 250 // refresh the main window, once the popup window (WorldPay, Paypal) was closed 251 if (typeof openedWindow != 'undefined' && openedWindow != null) { 252 // in order to know when this popup window is closed, keep checking this with a loop 253 var timer = setInterval(function() { 254 if (openedWindow.closed) { 255 clearInterval(timer); 256 if (localStorage.getItem('subsURLParams')) { 257 localStorage.removeItem('subsURLParams'); 258 } 259 // reload the parent window after popup window closed 260 window.location.href = subscribe_status; 261 } 262 }, 1000); 263 } 264 265 // Check if PWA cookie is NOT set 266 if (readCookie("pwaPlatform") == null) { 267 var redirectURL = updateUrlParameter(appStartEndpoint, "viaPWA", "true"); 268 redirectURL = updateUrlParameter(redirectURL, "pagePath", encodeURIComponent(window.location.pathname)); 269 // retain urlChanged awareness 270 if (/[?&]urlChanged/.test(window.location.search)) { 271 redirectURL = updateUrlParameter(redirectURL, "urlChanged", "1"); 272 } 273 // no need to redirect, if user is already viewing the custom page 274 if (!window.location.pathname.match(/(urlChanged|pwaStart)/)) { 275 window.location.href = redirectURL; 276 } 277 } 278 } else { 279 // Check if PWA cookie is set 280 if (readCookie("pwaPlatform") != null) { 281 var redirectURL = updateUrlParameter(appStartEndpoint, "viaPWA", "false"); 282 redirectURL = updateUrlParameter(redirectURL, "pagePath", encodeURIComponent(window.location.pathname)); 283 if (!window.location.pathname.match(/(pwaStart)/)) { 284 window.location.href = redirectURL; 285 } 286 } 287 } 288} 289 290// PWA - Detect browser back button event 291function handleBackButton() { 292 var state = window.history.state; 293 var sessionFirstVisit = sessionStorage.getItem('firstPageVisited'); 294 if (state != null && state.firstPage) { 295 // when there's no browser history, pressing the back button immediately closes the app 296 showUpdateBar(); 297 } else { 298 if (state == null) { 299 // only executed on first page load 300 if (sessionFirstVisit == null) { 301 sessionStorage.setItem('firstPageVisited', true); 302 var deviceRegistered = JSON.parse(getLocalStorageItem('deviceRegistered')); 303 // Track login, this is currently bound to unique app launches 304 // as a remembered session is the default behaviour on the app. 305 if (currPage != "help_pwaUninstall") { 306 var pwaInstalled = JSON.parse(getLocalStorageItem('pwaInstalled')); 307 if (deviceRegistered != null && (deviceRegistered.uuid != null && deviceRegistered.uuid != false)) { 308 var hasPushPermission = (Notification.permission == "granted") ? 1 : 0; 309 if (Notification.permission == "default") { 310 hasPushPermission = (pwaInstalled.subscribed == "granted") ? 1 : 0; 311 } 312 var data = {"updateLastLogin": 1, "uuid": deviceRegistered.uuid, "subscribed": hasPushPermission}; 313 sendAjaxRequest(data, appApiEndpoint); 314 } 315 } 316 if ((!(isAppleMobileDevice && isMobileSafariBrowser))) { 317 // update the current history item and update the data on the session history when the app is first opens 318 if (currPage == "home" || sessionStatus == "guest") { 319 if (window.history.replaceState) { 320 window.history.replaceState({firstPage: true, currPage: currPage}, null, window.location.href); 321 } 322 } else { 323 // if app was opened from a push notification or shortcut 324 // if there is no user interaction, the history api event will not fire (i.e on page load
324) 325 if (window.history.replaceState) { 326 window.history.replaceState({firstPage: true, currPage: currPage}, null, stateHomeURL); 327 } 328 if (window.history.pushState) { 329 if (sessionStorage.getItem("doubleBackToExit") == null) { 330 sessionStorage.setItem('doubleBackToExit', true); 331 } 332 window.history.pushState({doubleBackToExit: true, currPage: currPage}, null, currLocation); 333 } 334 } 335 } 336 } else { 337 if (!(isAppleMobileDevice && isMobileSafariBrowser)) { 338 if (/[?&]pwaPlatform/.test(window.location.search)) { 339 if (currPage == "home") { 340 showUpdateBar(); 341 } else { 342 if (window.history.replaceState) { 343 window.history.replaceState({hasVisited: true, currPage: currPage}, null, window.location.href); 344 } 345 if (window.history.pushState) { 346 if (sessionStorage.getItem("doubleBackToExit") == null) { 347 sessionStorage.setItem('doubleBackToExit', true); 348 } 349 window.history.pushState({doubleBackToExit: true, currPage: currPage}, null, window.location.href); 350 } 351 } 352 } else { 353 if (sessionStorage.getItem("showUpdateBar") != null) { 354 showUpdateBar(); 355 } else { 356 if (window.history.replaceState) { 357 window.history.replaceState({hasVisited: true, currPage: currPage}, null, window.location.href); 358 } 359 if (window.history.pushState) { 360 if (sessionStorage.getItem("doubleBackToExit") == null) { 361 sessionStorage.setItem('doubleBackToExit', true); 362 } 363 window.history.pushState({doubleBackToExit: true, currPage: currPage}, null, window.location.href); 364 } 365 } 366 } 367 } 368 } 369 } else { 370 if (!(isAppleMobileDevice && isMobileSafariBrowser)) { 371 if (typeof state.modal == "undefined") { 372 if (state.hasVisited) { 373 window.history.pushState({}, null); 374 window.history.go(-(history.length - 1)); 375 } else if (state.doubleBackToExit) { 376 if (sessionStorage.getItem("doubleBackToExit") != null) { 377 sessionStorage.removeItem('doubleBackToExit'); 378 } 379 } 380 } else if (state.modal == "modalWrap") { 381 if (state.firstPage) { 382 if (state.currPage == "home") { 383 window.history.go(-1); 384 } 385 } else if (state.doubleBackToExit) { 386 if (sessionStorage.getItem("doubleBackToExit") != null) { 387 sessionStorage.removeItem('doubleBackToExit'); 388 } 389 window.history.go(-1); 390 } 391 } 392 } 393 } 394 } 395} 396 397// TDL Connect Firebase Functions 398// Check if push permission has been granted. 399function checkFCMNotificationPermission() { 400 return new Promise(function(resolve) { 401 window.FirebasePlugin.hasPermission(function (hasPushPermission) { 402 resolve(hasPushPermission); 403 }); 404 }); 405} 406 407// Get a Firebase token. 408function getFCMToken () { 409 return new Promise(function(resolve) { 410 window.FirebasePlugin.getToken(function(fcmPushToken) { 411 resolve(fcmPushToken); 412 }, function (error) { 413 resolve(false); 414 }); 415 }); 416} 417 418function addToHomeScreen (installPromptEvent) { 419 if (typeof installPromptEvent != 'undefined' && installPromptEvent != null) { 420 var a2hsBtn = document.getElementById("a2hs-prompt"); 421 // show the prompt 422 installPromptEvent.prompt(); 423 // wait for the user to respond to the prompt 424 installPromptEvent.userChoice 425 .then(function(choiceResult) { 426 if (choiceResult.outcome === 'accepted') { 427 // hide our user interface that shows our A2HS button 428 a2hsBtn.style.display = 'none'; 429 resizeStickyHeaderSpacer(); 430 } 431 installPromptEvent = null; 432 }); 433 } 434} 435 436function trackSmartBannerClick (memberID, type, pageID, platform, app, cookieExpiry) { 437 $.ajax({ 438 type: "POST", 439 url: trackingURL, 440 data: {action: "trackSmartBannerClick", memberID: memberID, type: type, pageID: pageID, platform: platform, app: app, cookieExpiry: cookieExpiry} 441 }); 442} 443 444// Auto adjust sticky header height, once banner is hidden 445function resizeStickyHeaderSpacer () { 446 var stickyHeaderSpacer = $(".stickyHeader").outerHeight(true); 447 cssMenus.stickyHeaderSpacer = stickyHeaderSpacer; 448} 449 450// set the cookie expiration date 451function setCookieExpiryDate (days) { 452 var expiry = new Date(new Date().getTime() + (days * 24 * 60 * 60 * 1000)); 453 var year = expiry.getFullYear();
454 var month = expiry.getMonth() + 1; 455 var day = expiry.getDate(); 456 var time = expiry.toLocaleTimeString(); 457 var expiryString = year + "-" + month + "-" + day + " " + time; 458 459 return expiryString; 460} 461 462function hideSmartBanner (bannerType, trackType, daysHide) { 463 if (displaySmartBanner) { 464 var appType = "native"; 465 } else if (displayPWASmartBanner) { 466 var appType = "pwa"; 467 // installPromptEvent = null; 468 } 469 var data = {type:bannerType, app:appType}; 470 var hideSmartBanner = createCookie("smartbanner", data, daysHide, true); 471 var expiryString = setCookieExpiryDate(daysHide); 472 var click = trackSmartBannerClick(userID, trackType, currPage, smartBannerPlatform, appType, expiryString); 473 resizeStickyHeaderSpacer(); 474} 475 476function createCookie (key, value, days, incDates) { 477 var cookieCreated = false; 478 var expiryDate = ""; 479 if (typeof days != "undefined" && days != "") { 480 var date = new Date(); 481 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 482 var expiry = date.toGMTString(); 483 expiryDate = "; expires=" + expiry; 484 if (typeof incDates != "undefined" && incDates) { 485 value["created"] = Math.floor(new Date().getTime() / 1000); 486 value["expires"] = Math.floor(new Date(expiry.toString()).getTime() / 1000); 487 } 488 document.cookie = key + "=" + encodeURIComponent(JSON.stringify(value)) + "; path=/" + expiryDate; 489 $(".smartbanner").hide(); 490 cookieCreated = true; 491 } 492 return cookieCreated; 493} 494 495// detect retina support on a device 496function detectRetinaDisplay () { 497 var retina = (window.retina || window.devicePixelRatio > 1); 498 if (retina) { 499 return true; 500 } 501 else { 502 return false; 503 } 504}
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.