1//Shared resource 2(function (f, b) { 3 if (!b.__SV) { 4 var e, g, i, h; 5 window.mixpanel = b; 6 b._i = []; 7 b.init = function (e, f, c) { 8 function g(a, d) { 9 var b = d.split('.'); 10 2 == b.length && ((a = a[b[0]]), (d = b[1])); 11 a[d] = function () { 12 a.push([d].concat(Array.prototype.slice.call(arguments, 0))); 13 }; 14 } 15 var a = b; 16 'undefined' !== typeof c ? (a = b[c] = []) : (c = 'mixpanel'); 17 a.people = a.people || []; 18 a.toString = function (a) { 19 var d = 'mixpanel'; 20 'mixpanel' !== c && (d += '.' + c); 21 a || (d += ' (stub)'); 22 return d; 23 }; 24 a.people.toString = function () { 25 return a.toString(1) + '.people (stub)'; 26 }; 27 i = 28 'disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove'.split( 29 ' ' 30 ); 31 for (h = 0; h < i.length; h++) g(a, i[h]); 32 var j = 'set set_once union unset remove delete'.split(' '); 33 a.get_group = function () { 34 function b(c) { 35 d[c] = function () { 36 call2_args = arguments; 37 call2 = [c].concat(Array.prototype.slice.call(call2_args, 0)); 38 a.push([e, call2]); 39 }; 40 } 41 for (var d = {}, e = ['get_group'].concat(Array.prototype.slice.call(arguments, 0)), c = 0; c < j.length; c++) b(j[c]); 42 return d; 43 }; 44 b._i.push([e, f, c]); 45 }; 46 b.__SV = 1.2; 47 e = f.createElement('script'); 48 e.type = 'text/javascript'; 49 e.async = !0; 50 e.src = 51 'undefined' !== typeof MIXPANEL_CUSTOM_LIB_URL 52 ? MIXPANEL_CUSTOM_LIB_URL 53 : 'file:' === f.location.protocol && '//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js'.match(/^\/\//) 54 ? 'https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js' 55 : '//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js'; 56 g = f.getElementsByTagName('script')[0]; 57 g.parentNode.insertBefore(e, g); 58 } 59})(document, window.mixpanel || []); 60 61mixpanel.init('7317283d1b73a1bf35908d0b6c2192d3', { 62 debug: true, 63 track_pageview: false, 64 persistence: 'localStorage', 65 api_host: MIXPANEL_API_HOST, 66 ignore_dnt: true, 67}); 68 69let blockedMixpanelEvents = [ 70 'Overlay1Display', 71 'Overlay2Display', 72 'Overlay3Display', 73 'sourceLocationList', 74 'destLocationList', 75 'sourceLocationSelected', 76 'destLocationSelected', 77]; 78let blockedUrls = []; 79 80var curExperimentName = ''; 81var curVariant = ''; 82 83 84if ( 85 window['convert'] && 86 window['convert']['data'] && 87 window['convert']['data']['experiments'] && 88 window['convert']['currentData'] && 89 window['convert']['currentData']['experiments'] 90) { 91 var refObject = window['convert']['data']['experiments']; 92 93 for (var key in window['convert']['currentData']['experiments']) { 94 if (!window['convert']['currentData']['experiments'].hasOwnProperty(key)) { 95 continue; 96 } 97 98 var currentExperiment = window['convert']['currentData']['experiments'][key]; 99 curExperimentName = refObject[key] && refObject[key].n ? refObject[key].n : 'unknown experiment name'; 100 curExperimentName = curExperimentName.replace('Test #', ''); 101 curVariant = currentExperiment['variation_name'] ? currentExperiment['variation_name'] : 'unknown variant'; 102 curVariant = curVariant.replace('Var #', ''); 103 } 104} 105 106let convertTrackParams = { 107 convert_experience_id: curExperimentName, 108 convert_variation_id: curVariant, 109}; 110 111// Determine pathname early so it can drive the param mapping selection 112let fullpathname = window.location.pathname; 113let pathname = fullpathname.substring(1); 114if (pathname === '' || pathname === '/') { 115 pathname = 'index.html'; 116} 117 118// Default URL param â internal param mapping 119// ETL layer: t1=utm_campaign, t2=utm_custom1, t3=utm_term, t4=utm_content, 120// t5=utm_custom2, t6=utm_custom3, t7=utm_custom4, t8=utm_source, t9=utm_custom5 121const defaultUrlParamKeyValues = { 122 utm_campaign: 'utm_campaign', // t1 123 utm_custom1: 'utm_custom1', // t2 124 utm_term: 'utm_term', // t3 125 utm_content: 'utm_content', // t4 126 utm_custom2: 'utm_custom2', // t5 127 utm_custom3: 'utm_custom3', // t6 128 utm_custom4: 'utm_custom4', // t7 129 utm_source: 'utm_source', // t8 130 utm_custom5: 'utm_custom5', // t9 131}; 132 133// sa- lander specific mapping (AdRoll campaign URL structure) 134// Sample URL: /sa-pcbto01?utm_source=adroll&utm_medium=prospecting_web&...&CLICK_ID=[ADROLL:CLICK_ID] 135const saUrlParamKeyValues = { 136 utm_campaign: 'utm_campaign', // t1: utm_campaign â utm_campaign (same) 137 utm_custom1: 'utm_custom1', // t2: utm_custom1 â utm_custom1 (same) 138 utm_term: 'utm_term', // t3: utm_term â utm_term (same) 139 utm_content: 'utm_content', // t4: utm_content â utm_content (same) 140 utm_custom2: 'utm_custom2', // t5: utm_custom2 â utm_custom2 (same) 141 utm_custom3: 'utm_custom3', // t6: utm_custom3 â utm_custom3 (same) 142 utm_custom4: 'utm_custom4', // t7: utm_custom4 â utm_custom4 (same) 143 utm_source: 'utm_source', // t8: utm_source â utm_source (same) 144 utm_medium: 'utm_custom5', // t9: utm_medium â utm_custom5 145 CLICK_ID: 'gclid', // CLICK_ID â gclid 146}; 147 148// mt- lander specific mapping (Meta/Facebook campaign URL structure) 149// Sample URL: /mt-bwmm5?utm_campaign=...&utm_source=...&utm_content=...&utm_term=...&utm_medium=...&fbclid=... 150const mtUrlParamKeyValues = { 151 utm_campaign: 'utm_campaign', // t1: utm_campaign â utm_campaign (same) 152 utm_custom1: 'utm_custom1', // t2: utm_custom1 â utm_custom1 (same) 153 utm_term: 'utm_term', // t3: utm_term â utm_term (same) 154 utm_content: 'utm_content', // t4: utm_content â utm_content (same) 155 utm_custom2: 'utm_custom2', // t5: utm_custom2 â utm_custom2 (same) 156 utm_custom3: 'utm_custom3', // t6: utm_custom3 â utm_custom3 (same) 157 utm_custom4: 'utm_custom4', // t7: utm_custom4 â utm_custom4 (same) 158 utm_source: 'utm_source', // t8: utm_source â utm_source (same) 159 utm_medium: 'utm_custom5', // t9: utm_medium â utm_custom5 160 fbclid: 'gclid', // fbclid â gclid 161}; 162 163const lowerPathname = pathname.toLowerCase(); 164const urlParamKeyValues = lowerPathname.startsWith('mt-') ? mtUrlParamKeyValues 165 : lowerPathname.startsWith('sa-') ? saUrlParamKeyValues 166 : defaultUrlParamKeyValues; 167const validKeys = new Set(Object.keys(urlParamKeyValues)); 168 169const currentURL = new URL(window.location.href); 170const urlSearchParams = currentURL.searchParams; 171const queryParams = {}; 172for (const key of Object.keys(urlParamKeyValues)) { 173 queryParams[urlParamKeyValues[key]] = ''; 174} 175 176if (!convertTrackParams.convert_experience_id && urlSearchParams.get('ad1')) { 177 // If convert track params are not extracted from window object, try to extract from url query params. Else empty string. 178 convertTrackParams.convert_experience_id = urlSearchParams.get('ad1'); 179} 180if (!convertTrackParams.convert_variation_id && urlSearchParams.get('ad2')) { 181 convertTrackParams.convert_variation_id = urlSearchParams.get('ad2'); 182} 183 184for (const [key, value] of urlSearchParams) { 185 if (validKeys.has(key)) { 186 const matchValue = urlParamKeyValues[key]; 187 if (matchValue.includes('-') && value.includes('-')) { 188 const [firstPart, secondPart] = matchValue.split('-'); 189 const [firstValue, secondValue] = value.split('-'); 190 queryParams[firstPart] = firstValue; 191 queryParams[secondPart] = secondValue; 192 } else { 193 queryParams[matchValue] = value; 194 } 195 } else { 196 // Any URL param not covered by the explicit mapping is tracked as-is (key â key) 197 queryParams[key] = value; 198 } 199} 200 201// List of valid landing page prefixes 202const landingPagePrefixes = ['aw-', 'fm-', 'sa-', 'mt-', 'pr-', 'our-product', 'product_']; 203const isLandingPage = lowerPathname === 'index.html' || 204 landingPagePrefixes.some(prefix => lowerPathname.startsWith(prefix)); 205 206if (isLandingPage) { 207 const constants = (typeof specificConstants !== 'undefined' && specificConstants) || {}; 208 console.log('constants in eventTracking.js ', constants); 209 mixpanel.register({landingurlregex: constants.finalRegex || pathname}); 210 mixpanel.register({templateRegex: constants.template || pathname}); 211 mixpanel.register({parentRegex: constants.landingRegex || pathname}); 212 mixpanel.register(queryParams); 213 // For any URL param that is remapped to a different internal key (e.g. utm_medium â 214 // utm_custom5, fbclid â gclid), Mixpanel's own init auto-captures the original param 215 // name as a super property independently of our mapping. Sync those source keys back 216 // to their mapped values so they never ghost a prior visit's value. 217 const remappedSourceKeys = Object.fromEntries( 218 Object.entries(urlParamKeyValues) 219 .filter(([src, dst]) => src !== dst) 220 .map(([src, dst]) => [src, queryParams[dst] || '']) 221 ); 222 if (Object.keys(remappedSourceKeys).length > 0) { 223 mixpanel.register(remappedSourceKeys); 224 } 225 mixpanel.register(convertTrackParams); 226} 227 228 229function hasCookiebotConsent() { 230 return typeof Cookiebot !== 'undefined' && Cookiebot.consent; 231} 232 233function hasStatisticsConsent() { 234 return !hasCookiebotConsent() || Cookiebot.consent.statistics; 235} 236 237function hasMarketingConsent() { 238 return !hasCookiebotConsent() || Cookiebot.consent.marketing; 239} 240 241function syncTrackingWithCookiebotConsent() { 242 if (!hasCookiebotConsent()) { 243 return; 244 } 245 if (!Cookiebot.consent.statistics) { 246 if (typeof mixpanel !== 'undefined' && typeof mixpanel.opt_out_tracking === 'function') { 247 mixpanel.opt_out_tracking(); 248 } 249 if (typeof Sentry !== 'undefined' && typeof Sentry.close === 'function') { 250 Sentry.close(); 251 } 252 } else if (typeof mixpanel !== 'undefined' && typeof mixpanel.opt_in_tracking === 'function') { 253 mixpanel.opt_in_tracking(); 254 } 255} 256 257if (typeof window !== 'undefined') { 258 window.addEventListener('CookiebotOnConsentReady', syncTrackingWithCookiebotConsent, false); 259 window.addEventListener('CookiebotOnConsentChanged', syncTrackingWithCookiebotConsent, false); 260} 261 262// Recover from stale Mixpanel opt-out for non-GDPR visitors. 263// `mixpanel.opt_out_tracking()` is persisted in localStorage, so a visitor
264// who declined statistics consent on a uk-cp (or any GDPR-gated) lander 265// stays opted out forever â even after switching to a non-GDPR region where 266// Cookiebot no longer loads. We treat "no Cookiebot loader on this page" as 267// "GDPR gating is not in effect here" and clear any inherited opt-out so 268// tracking resumes for legitimate non-GDPR traffic. 269function recoverFromStaleMixpanelOptOut() { 270 if (typeof document === 'undefined' || document.getElementById('Cookiebot')) { 271 return; 272 } 273 if (typeof mixpanel !== 'undefined' && typeof mixpanel.opt_in_tracking === 'function') { 274 mixpanel.opt_in_tracking(); 275 } 276} 277 278if (typeof window !== 'undefined') { 279 recoverFromStaleMixpanelOptOut(); 280} 281 282// ---- Incognito / private-window detection (Firefox only) ---------------- 283// We only detect private mode on Firefox. In a Firefox private window the 284// Service Worker API is unavailable (navigator.serviceWorker === undefined), 285// which is a reliable signal. For every other browser we do NOT attempt 286// detection and report is_incognito = false. 287// The result is registered as a Mixpanel super property so it rides on every 288// event (UserLanderVisit and downstream CtaClick, install_click, etc.) and is 289// also attached to the GTM dataLayer push in gtmTrack. 290function checkFirefoxPrivateMode() { 291 // First, verify the user is actually on Firefox. `InstallTrigger` is a legacy 292 // Firefox global; the userAgent check covers modern Firefox where it's removed. 293 const isFirefox = 294 typeof InstallTrigger !== 'undefined' || 295 navigator.userAgent.toLowerCase().includes('firefox'); 296 if (isFirefox) { 297 if (navigator.serviceWorker === undefined) { 298 console.log('User is in Firefox Private Browsing Mode'); 299 return true; 300 } 301 console.log('User is in Normal Firefox Mode'); 302 return false; 303 } 304 return false; 305} 306 307// Detection is synchronous, so compute once at load and register immediately. 308// mixpanel.init(...) has already run above, so mixpanel.register is available. 309var isIncognito = checkFirefoxPrivateMode(); 310if (typeof mixpanel !== 'undefined' && typeof mixpanel.register === 'function') { 311 mixpanel.register({ is_incognito: isIncognito }); 312} 313 314function gtmTrack(eventName) { 315 if (!hasMarketingConsent()) { 316 return; 317 } 318 // `gtm` is declared inside the marketing-gated inline script in spa.ejs, 319 // so it may be undeclared when Cookiebot consent hasn't loaded yet. 320 // Also require `window.dataLayer` to exist before pushing. 321 if (typeof gtm !== 'undefined' && gtm && Array.isArray(window.dataLayer)) { 322 const eventDict = { 323 event: eventName, 324 browser: jscd.browser, 325 OS: jscd.os + ' ' + jscd.osVersion, 326 is_incognito: isIncognito, 327 }; 328 const gtmCombinedProperties = Object.assign({}, queryParams, eventDict); 329 window.dataLayer.push(gtmCombinedProperties); 330 } 331} 332 333async function mixpanelTrack(mixpanelEventName, eventAttributes = {}) { 334 if (!hasStatisticsConsent()) { 335 return; 336 } 337 if(typeof specificConstants !== 'undefined' && specificConstants && specificConstants.param1 && specificConstants.param2) { 338 mixpanelCustomProperties['param1'] = specificConstants.param1; 339 mixpanelCustomProperties['param2'] = specificConstants.param2; 340 } 341 342 if (!conditionChecksForTrackingEvent(mixpanelEventName)) { 343 return; 344 } 345 346 347 if (Object.keys(eventAttributes).length > 0) { // Register event attributes in Mixpanel if they have properties 348 mixpanel.register(eventAttributes); 349 } 350 351 mixpanel.track(mixpanelEventName, mixpanelCustomProperties); 352} 353 354 355function conditionChecksForTrackingEvent(mixpanelEventName) { 356 if (!urlTrackable() || (blockedMixpanelEvents.includes(mixpanelEventName) && !urlSearchParams.has('debug'))) { 357 return false; 358 } 359 360 if (urlSearchParams.has('test')) { 361 return false; 362 } 363 364 if (mixpanelEventName === 'UserLanderVisit' && !isLandingPage) { 365 return false; 366 } 367 368 return true; 369} 370 371function urlTrackable() { 372 return !blockedUrls.some(blockedUrl => currentURL.hostname.includes(blockedUrl)) || urlSearchParams.has('trackStaging'); 373}
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.