1/* var $zoho=$zoho || {}; 2$zoho.salesiq = $zoho.salesiq || {widgetcode:"013cd641be142742b2e56300a053cd36d2fcbb2f2e0660142780e5a1b9a70066", values:{},ready:function(){}}; */ 3/* s.type="text/javascript";s.id="zsiqscript";s.defer=true;s.src="https://salesiq.zoho.com/widget";t.parentNode.insertBefore(s,t);d.write("<div id='zsiqwidget'></div>"); */ 4let API_ENV = '' 5 6let boostSubDomain = '' 7switch (window.location.host.split('.')[0]) { 8 case 'myboost': 9 case 'www': 10 case '20221117-staging': 11 API_ENV = '/master/' 12 boostSubDomain = 'boost' 13 break 14 case 'test': 15 boostSubDomain = 'staging' 16 API_ENV = '/develop/' 17 break 18} 19 20const boostBaseUrl = 'https://' + boostSubDomain + '.myboost.co.nz' 21 22const dashboardUrl = boostBaseUrl + '/profile/dashboard' 23const profileUrl = boostBaseUrl + '/profile' 24const offersUrl = boostBaseUrl + '/offers' 25const changePasswordUrl = boostBaseUrl + '/profile/change-password' 26const preferencesUrl = boostBaseUrl + '/profile/preferences' 27 28// eslint-disable-next-line no-unused-vars 29const exports = {} 30const apiScript = document.createElement('script') 31apiScript.src = 'https://boost-js-lib.s3-ap-southeast-2.amazonaws.com' + API_ENV + 'api.js' 32const firstScriptNode = document.getElementsByTagName('script')[0] 33firstScriptNode.parentNode.insertBefore(apiScript, firstScriptNode) 34 35let userIsLoggedIn = false 36 37// eslint-disable-next-line no-unused-vars 38function consoleLog(info) { 39 if (API_ENV === '/develop/') { 40 if (typeof info === 'object') { 41 info.forEach(function (a) { 42 console.log(a) 43 }) 44 } else { 45 console.log('Log: ' + info) 46 } 47 } else { 48 return false 49 } 50} 51 52(function () { 53 const currUrl = window.location.href 54 // eslint-disable-next-line no-undef 55 let boostApi = null 56 57 const setBoostCookie = cookie => { 58 document.cookie = 'boost=' + JSON.stringify(cookie) + ';path=/;domain=myboost.co.nz' 59 } 60 61 const getBoostCookie = _ => { 62 const cookieVal = document.cookie.match('(^|[^;]+)\\s*boost\\s*=\\s*([^;]+)') 63 return cookieVal ? cookieVal.pop() : '' 64 } 65 66 const deleteLocalStorage = function () { // Delete cookie when logging out 67 document.cookie = 'boost= ; expires = Thu, 01 Jan 1970 00:00:00 GMT;path =/;domain=.myboost.co.nz;' 68 localStorage.removeItem('boost') 69 } 70 71 const handleLoginFail = function () { 72 73 } 74 75 const getUserRoles = function (roles) { 76 // roles is an array of roles which will contain 'myboost.co.nz BP' and/or 'myboost.co.nz BP' (or be empty) 77 if (!Array.isArray(roles)) { 78 roles = [] 79 } 80 81 return { 82 bp: roles.indexOf('myboost.co.nz BP') !== -1, 83 su: roles.indexOf('myboost.co.nz SU') !== -1 84 } 85 } 86 87 const dropdownCheckRole = function (roles) { 88 const roleMap = getUserRoles(roles) 89 90 if (!roleMap.bp) { // if np BP role, hide dashboard 91 document.querySelectorAll('.user-menu-dashboard').forEach(el => { 92 const menuContainer = el.parentNode.parentNode 93 menuContainer.removeChild(el.parentNode) 94 95 // the next link now gets the dashboard style 96 const newFirstLink = menuContainer.querySelector('a') 97 newFirstLink.parentNode.classList.add('dashboard-container') 98 newFirstLink.classList.add('btn') 99 newFirstLink.classList.add('btn-primary') 100 newFirstLink.classList.add('btn-sm') 101 }) 102 } 103 if (!roleMap.su) { // if no su rule, hide offers 104 document.querySelectorAll('.user-menu-offers').forEach(el => { 105 el.parentNode.parentNode.removeChild(el.parentNode) 106 }) 107 } 108 } 109 110 const handleRedirect = function (profile) { 111 const roles = profile.roles 112 const isSignIn = currUrl.indexOf('/sign-in') !== -1 113 const isReferrer = currUrl.indexOf('referrer=') !== -1 114 115 if (isSignIn && isReferrer) { 116 const ref = currUrl.split('?referrer=/')[1] 117 const redirect = 'https://' + boostSubDomain + '.myboost.co.nz/' + ref 118 window.location.replace(redirect) 119 } else { 120 const roleMap = getUserRoles(roles) 121 122 if (roleMap.bp) { 123 window.location.replace(dashboardUrl) 124 } else { 125 window.location.replace(offersUrl) 126 } 127 } 128 } 129 130 const updateApiVersionDisplay = version => { 131 document.getElementById('boost-api-code-display').textContent = version 132 } 133 134 const activateCardContainers = [ 135 document.getElementById('nav-activate-container') 136 ] 137 138 const anonymousNavPanels = [ 139 document.getElementById('nav-user-anonymous-panel'), 140 ] 141 142 const mobileNavUserPanelId = 'mobile-nav-user-authenticated-panel' 143 144 const mobileNavUserPanel = document.getElementById(mobileNavUserPanelId) 145 146 const authenticatedNavPanels = [ 147 document.getElementById('nav-user-authenticated-panel'), 148 mobileNavUserPanel 149 ] 150 151 const firstNameContainers = [ 152 document.getElementById('nav-user-name'), 153 document.getElementById('mobile-nav-user-name') 154 ] 155 156 const nameDisplays = firstNameContainers.map(c => c.querySelector('.name-display')) 157 158 const hideElement = el => el.style.display = 'none' 159 const showElement = el => el.style.display = 'block' 160 161 const hideElements = els =>
161 els.forEach(hideElement) 162 const showElements = els => els.forEach(showElement) 163 164 const disableUserPanels = panelEls => { 165 panelEls.forEach(panelEl => { 166 panelEl.classList.remove('nav-user-panel') 167 panelEl.style.display = 'none' 168 }) 169 } 170 171 const enableUserPanels = panelEls => { 172 panelEls.forEach(panelEl => { 173 if(panelEl.id !== mobileNavUserPanelId) { 174 panelEl.classList.add('nav-user-panel') 175 } 176 }) 177 } 178 179 // shortcuts to generate the right CSS display style for showing/hiding for roles 180 const showForBp = roleMap => roleMap.bp ? 'block' : 'none' 181 const showForSu = roleMap => roleMap.su ? 'block' : 'none' 182 183 const setUpAuthenticatedNavPanelForRole = roleMap => { 184 showElements([mobileNavUserPanel]) 185 // hide and show links based on the user role, and update their hrefs to the right domain 186 authenticatedNavPanels.forEach(authenticatedNavPanel => { 187 const dashboardLink = authenticatedNavPanel.querySelector('.user-menu-dashboard') 188 dashboardLink.parentElement.style.display = showForBp(roleMap) 189 dashboardLink.setAttribute('href', dashboardUrl) 190 191 const profileLink = authenticatedNavPanel.querySelector('.user-menu-profile') 192 profileLink.setAttribute('href', profileUrl) 193 194 const offersLink = authenticatedNavPanel.querySelector('.user-menu-offers') 195 offersLink.parentElement.style.display = showForSu(roleMap) 196 offersLink.setAttribute('href', offersUrl) 197 198 const changePasswordLink = authenticatedNavPanel.querySelector('.user-menu-change-password') 199 changePasswordLink.setAttribute('href', changePasswordLink) 200 201 const preferencesLink = authenticatedNavPanel.querySelector('.user-menu-preferences') 202 preferencesLink.setAttribute('href', preferencesUrl) 203 }) 204 } 205 206 const setUpLoggedInState = (userFirstName, roleMap) => { 207 hideElements(activateCardContainers) 208 disableUserPanels(anonymousNavPanels) 209 enableUserPanels(authenticatedNavPanels) 210 211 document.querySelectorAll('.signed-in-hidden').forEach(hideElement) 212 213 showElements(firstNameContainers) 214 215 let nameContent 216 217 if (userFirstName !== '' && userFirstName != null) { 218 // 'Hi' is already in the element, so this will make 'Hi, <name>' 219 nameContent = ', ' + userFirstName 220 } else { 221 nameContent = '' 222 } 223 224 nameDisplays.forEach(nameDisplay => { 225 nameDisplay.textContent = nameContent 226 }) 227 228 setUpAuthenticatedNavPanelForRole(roleMap) 229 230 const desktopNavMenu = document.querySelector('.nav-menu') 231 const mobileNavMenu = document.querySelector('.mobile-nav-menu') 232 233 const menus = [desktopNavMenu, mobileNavMenu] 234 235 menus.forEach(menu => { 236 const offersLink = menu.querySelector('a[href*="/offers"]') 237 offersLink.setAttribute('href', offersUrl) 238 if (roleMap.bp) { 239 offersLink.parentNode.parentNode.removeChild(offersLink.parentNode) 240 } 241 }) 242 243 userIsLoggedIn = true 244 } 245 246 const getToken = _ => { 247 const serializedToken = getBoostCookie().split(',path')[0] // Check if user is logged in 248 if (serializedToken == null || serializedToken === '' || serializedToken === ' ') { 249 return null 250 } 251 252 return JSON.parse(serializedToken) 253 } 254 255 const fetchProfileAndSetUpLoggedInState = (accessToken, doRedirect) => { 256 document.querySelector('.nav-menu a[href*="/offers"]').setAttribute('href', offersUrl) // point offers link to logged in offers 257 258 boostApi.profile.get(accessToken).then(profile => { 259 const roles = profile.roles 260 const firstName = profile.firstName 261 262 setUpLoggedInState(firstName, getUserRoles(roles)) 263 264 dropdownCheckRole(roles) 265 if (currUrl.indexOf('myboost.co.nz/sign-in') !== -1 || doRedirect) 266 handleRedirect(profile) 267 }) 268 } 269 270 const handleValidate = function () { 271 const token = getToken() 272 if (token == null || token.access_token == null) { 273 handleLoginFail() 274 return false 275 } 276 277 const accessToken = token.access_token 278 const expiresIn = token.expires_in 279 const refreshToken = token.refresh_token 280 const tokenTime = token.time 281 const currentTime = Math.floor(Date.now() / 1000) 282 boostApi.auth.validate(accessToken).then(function (res) { 283 updateApiVersionDisplay(!res.apiVersion || res.apiVersion === '' ? 'none' : res.apiVersion) 284 if (currentTime - tokenTime >
284 expiresIn) { // if current time minus time is greater than expiry then request refreshToken 285 boostApi.auth.refresh({ 286 token: ''.concat(refreshToken), 287 scope: 'website', 288 device: '' 289 }).then(function (res) { // get new accessToken and refreshToken 290 if (res && res.access_token) { 291 const newToken = token 292 293 newToken.access_token = res.access_token 294 newToken.refresh_token = res.refresh_token 295 setBoostCookie(newToken) 296 fetchProfileAndSetUpLoggedInState(newToken.access_token, false) 297 } else { 298 handleLoginFail() 299 deleteLocalStorage() 300 } 301 }) 302 } else { 303 if (res && res.data.email) { 304 fetchProfileAndSetUpLoggedInState(accessToken, false) 305 306 } else { 307 handleLoginFail() 308 deleteLocalStorage() 309 } 310 } 311 }) 312 } 313 314 const doLogin = (form, failureCallback) => { 315 if(userIsLoggedIn) { 316 // password managers (like DashLane?) might be submitting the login form when the user is already logged in 317 return; 318 } 319 const emailField = form.querySelector("input[type='text']") 320 const passwordField = form.querySelector("input[type='password']") 321 322 const email = emailField.value 323 324 if (email === '' || email === null) { 325 alert('Please enter your email address.') 326 return 327 } 328 329 const password = passwordField.value 330 331 if (password === '' || password == null) { 332 alert('Please enter your password.') 333 return 334 } 335 336 const credentials = { 337 Username: email, 338 Password: password, 339 Scope: 'website' 340 } 341 342 boostApi.auth.login(credentials).then(res => { 343 if (!res || res.code || !res.access_token || !res.refresh_token) { 344 alert('Login failed. ' + res.message) 345 if (failureCallback) { 346 failureCallback() 347 } 348 return 349 } 350 351 res.time = Math.floor(Date.now() / 1000) 352 setBoostCookie(res) 353 354 fetchProfileAndSetUpLoggedInState(res.access_token, true); 355 }) 356 } 357 358 const doLogout = _ => { 359 if (!confirm('Are you sure you want to sign out?')) { 360 return 361 } 362 363 boostApi.auth.logout(token).then(function (res) { 364 deleteLocalStorage() 365 userIsLoggedIn = false 366 location.reload() 367 }) 368 } 369 370 const setUpListeners = function () { 371 // set up the javascript event handlers that override default functionality 372 const signInForms = document.querySelectorAll('.nav-sign-in-form') 373 signInForms.forEach(signInForm => { 374 signInForm.addEventListener('submit', e => { 375 e.preventDefault() 376 doLogin(signInForm) 377 return false 378 }) 379 }) 380 381 authenticatedNavPanels.forEach(authenticatedNavPanel => { 382 const signOutButton = authenticatedNavPanel.querySelector('.user-menu-signout') 383 signOutButton.addEventListener('click', e => { 384 e.preventDefault() 385 doLogout() 386 return false; 387 }) 388 }) 389 390 try { 391 let ci = 0; 392 let kc = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13]; 393 394 document.addEventListener('keyup', e => { 395 if (e.which === kc[ci++]) { 396 if (ci === kc.length) { 397 document.querySelectorAll('.nav-logo img').forEach(logo => logo.style.transform = 'rotate(180deg)') 398 ci = 0 399 } 400 } else { 401 ci = 0 402 } 403 }); 404 } catch (e) {} 405 } 406 407 const setUp = () => { 408 setUpListeners() 409 handleValidate() 410 } 411 412 let apiCheckCount = 0 413 414 const apiCheck = setInterval(() => { 415 // The Boost API client is included in a different JS file which loads at the same time as this file, so 416 // window.api might not be ready in time. This loops until it's loaded (or errors if it takes too long). 417 if (window.api) {
418 boostApi = window.api 419 clearInterval(apiCheck) 420 setUp() 421 return 422 } 423 424 if (apiCheckCount++ === 100) { 425 clearInterval(apiCheck) 426 console.error('Loading API timed out') 427 } 428 429 }, 100) 430 431 window.doBoostLogin = doLogin 432})() 433 434const regexCount = (expression, s) => { 435 return ((s || '').match(expression) || []).length 436} 437 438 439window.boostValidatePassword = password => { 440 password = password || '' 441 const lowerCaseCount = regexCount(/[a-z]/g, password) 442 const upperCaseCount = regexCount(/[A-Z]/g, password) 443 const numberCount = regexCount(/[0-9]/g, password) 444 445 // all other characters must be symbols 446 const symbolCount = password.length - lowerCaseCount - upperCaseCount - numberCount 447 448 const score = (lowerCaseCount >= 1) + (upperCaseCount >= 1) + (numberCount >= 1) + (symbolCount >= 1) 449 450 const valid = score >= 2 && password.length >= 8 451 452 if (!valid) 453 alert( 454 'Passwords must have at least 8 characters and contain at least two of each of the following: ' + 455 'uppercase letters, lowercase letters, numbers, and symbols.' 456 ); 457 458 return valid 459} 460 461window.setFullscreenSpinnerVisible = isVisible => { 462 if (window.boostOverlay) { 463 if (isVisible) { 464 // should already be visible so do nothing 465 return 466 } 467 468 // remove it 469 window.boostOverlay.parentNode.removeChild(window.boostOverlay); 470 window.boostOverlay = null 471 return 472 } 473 474 if (!isVisible) { 475 return // no window.boostOverlay and we don't want it visible so return 476 } 477 478 const overlay = document.createElement('div'); 479 overlay.classList.add('spinner-overlay') 480 481 overlay.innerHTML = '<div class="lds-ripple"><div></div><div></div></div>' 482 483 document.body.appendChild(overlay); 484 window.boostOverlay = overlay 485}
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.