PageSourceSearch

https://documentconverter.com/sharedfiles-atom/js/eventTracking.js

js documentconverter.com collected 2026-10-02 01:27:31 UTC 14,136 bytes, 373 lines download raw bytes

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.