@adobe/helix-rum-enhancer 2.13.0 → 2.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2023 Adobe. All rights reserved.
2
+ * Copyright 2024 Adobe. All rights reserved.
3
3
  * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License. You may obtain a copy
5
5
  * of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -9,406 +9,405 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- /* eslint-env browser */
13
- const KNOWN_PROPERTIES = ['weight', 'id', 'referer', 'checkpoint', 't', 'source', 'target', 'cwv', 'CLS', 'FID', 'LCP', 'INP', 'TTFB'];
14
- const DEFAULT_TRACKING_EVENTS = ['click', 'cwv', 'form', 'enterleave', 'viewblock', 'viewmedia', 'loadresource', 'utm'];
15
- const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum : {};
16
-
17
- const fflags = {
18
- has: (flag) => fflags[flag].indexOf(Array.from(window.origin)
19
- .map((a) => a.charCodeAt(0))
20
- .reduce((a, b) => a + b, 1) % 1371) !== -1,
21
- enabled: (flag, callback) => fflags.has(flag) && callback(),
22
- disabled: (flag, callback) => !fflags.has(flag) && callback(),
23
- onetrust: [543, 770, 1136],
24
- ads: [1139, 543, 770, 984],
25
- email: [1139, 543, 770, 984],
26
- };
27
-
28
- const urlSanitizers = {
29
- full: () => window.location.href,
30
- origin: () => window.location.origin,
31
- path: () => window.location.href.replace(/\?.*$/, ''),
32
- };
33
-
34
- const getTargetValue = (element) => element.getAttribute('data-rum-target') || element.getAttribute('href')
35
- || element.currentSrc || element.getAttribute('src') || element.dataset.action || element.action;
36
-
37
- const targetselector = (element) => {
38
- try {
39
- if (!element) return undefined;
40
- let value = getTargetValue(element);
41
- if (!value && element.tagName !== 'A' && element.closest('a')) {
42
- value = getTargetValue(element.closest('a'));
43
- }
44
- if (value && !value.startsWith('https://')) {
12
+
13
+ /* eslint-disable max-classes-per-file, wrap-iife */
14
+ // eslint-disable-next-line func-names
15
+ (function () {
16
+ 'use strict';
17
+
18
+ const KNOWN_PROPERTIES = ['weight', 'id', 'referer', 'checkpoint', 't', 'source', 'target', 'cwv', 'CLS', 'FID', 'LCP', 'INP', 'TTFB'];
19
+ const DEFAULT_TRACKING_EVENTS = ['click', 'cwv', 'form', 'enterleave', 'viewblock', 'viewmedia', 'loadresource', 'utm'];
20
+
21
+ const fflags = {
22
+ has: (flag) => fflags[flag].indexOf(Array.from(window.origin)
23
+ .map((a) => a.charCodeAt(0))
24
+ .reduce((a, b) => a + b, 1) % 1371) !== -1,
25
+ enabled: (flag, callback) => fflags.has(flag) && callback(),
26
+ disabled: (flag, callback) => !fflags.has(flag) && callback(),
27
+ onetrust: [543, 770, 1136],
28
+ ads: [1139, 543],
29
+ email: [1139, 543],
30
+ };
31
+
32
+ const urlSanitizers = {
33
+ full: (url = window.location.href) => new URL(url).toString(),
34
+ origin: (url = window.location.href) => new URL(url).origin,
35
+ path: (url = window.location.href) => new URL(url).pathname,
36
+ };
37
+
38
+ const getTargetValue = (element) => element.getAttribute('data-rum-target') || element.getAttribute('href')
39
+ || element.currentSrc || element.getAttribute('src') || element.dataset.action || element.action;
40
+
41
+ const targetSelector = (element) => {
42
+ try {
43
+ if (!element) return undefined;
44
+ let value = getTargetValue(element);
45
+ if (!value && element.tagName !== 'A' && element.closest('a')) {
46
+ value = getTargetValue(element.closest('a'));
47
+ }
48
+ if (value && !value.startsWith('https://')) {
45
49
  // resolve relative links
46
- value = new URL(value, window.location).href;
50
+ value = new URL(value, window.location).href;
51
+ }
52
+ return value;
53
+ } catch (error) {
54
+ return null;
47
55
  }
48
- return value;
49
- } catch (error) {
50
- // something went wrong
51
- return null;
52
- }
53
- };
56
+ };
54
57
 
55
- const sourceselector = (element) => {
56
- try {
57
- if (element === document.body || element === document.documentElement || !element) {
58
- return undefined;
59
- }
60
- if (element.getAttribute('data-rum-source')) {
61
- return element.getAttribute('data-rum-source');
62
- }
63
- const form = element.closest('form');
64
- let formElementSelector = '';
65
- if (form && Array.from(form.elements).includes(element)) {
66
- formElementSelector = element.tagName === 'INPUT' ? `form input[type='${element.getAttribute('type')}']` : `form ${element.tagName.toLowerCase()}`;
67
- }
58
+ const sourceSelector = (element) => {
59
+ try {
60
+ if (!element || element === document.body || element === document.documentElement) {
61
+ return undefined;
62
+ }
63
+ if (element.getAttribute('data-rum-source')) {
64
+ return element.getAttribute('data-rum-source');
65
+ }
66
+ const form = element.closest('form');
67
+ let formElementSelector = '';
68
+ if (form && Array.from(form.elements).includes(element)) {
69
+ formElementSelector = element.tagName === 'INPUT' ? `form input[type='${element.getAttribute('type')}']` : `form ${element.tagName.toLowerCase()}`;
70
+ }
71
+
72
+ const blockName = element.closest('.block') ? element.closest('.block').getAttribute('data-block-name') : '';
73
+ if (element.id || formElementSelector) {
74
+ const id = element.id ? `#${element.id}` : '';
75
+ return blockName ? `.${blockName} ${formElementSelector}${id}` : `${formElementSelector}${id}`;
76
+ }
77
+
78
+ if (element.getAttribute('data-block-name')) {
79
+ return `.${element.getAttribute('data-block-name')}`;
80
+ }
81
+
82
+ if (Array.from(element.classList).some((className) => className.match(/button|cta/))) {
83
+ return blockName ? `.${blockName} .button` : '.button';
84
+ }
68
85
 
69
- const blockName = element.closest('.block') ? element.closest('.block').getAttribute('data-block-name') : '';
70
- if (element.id || formElementSelector) {
71
- const id = element.id ? `#${element.id}` : '';
72
- return blockName ? `.${blockName} ${formElementSelector}${id}` : `${formElementSelector}${id}`;
86
+ return sourceSelector(element.parentElement);
87
+ } catch (error) {
88
+ return null;
73
89
  }
90
+ };
74
91
 
75
- if (element.getAttribute('data-block-name')) {
76
- return `.${element.getAttribute('data-block-name')}`;
92
+ const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum : {};
93
+
94
+ const formSubmitListener = (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) });
95
+ // eslint-disable-next-line no-use-before-define
96
+ const mutationObserver = window.MutationObserver ? new MutationObserver(mutationsCallback) : null;
97
+ // Gets configured collection from the config service for the current domain
98
+ function getCollectionConfig() {
99
+ // eslint-disable-next-line max-len
100
+ fflags.enabled('onetrust', () => DEFAULT_TRACKING_EVENTS.push('consent'));
101
+ fflags.enabled('ads', () => DEFAULT_TRACKING_EVENTS.push('paid'));
102
+ fflags.enabled('email', () => DEFAULT_TRACKING_EVENTS.push('email'));
103
+ return DEFAULT_TRACKING_EVENTS;
104
+ }
105
+
106
+ function trackCheckpoint(checkpoint, data, t) {
107
+ const { weight, id } = window.hlx.rum;
108
+ if (isSelected) {
109
+ const sendPing = (pdata = data) => {
110
+ // eslint-disable-next-line object-curly-newline, max-len
111
+ const body = JSON.stringify({ weight, id, referer: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'](), checkpoint, t, ...data }, KNOWN_PROPERTIES);
112
+ const { href: url, origin } = new URL(`.rum/${weight}`, sampleRUM.collectBaseURL || sampleRUM.baseURL);
113
+ if (window.location.origin === origin) {
114
+ const headers = { type: 'application/json' };
115
+ navigator.sendBeacon(url, new Blob([body], headers));
116
+ } else {
117
+ navigator.sendBeacon(url, body);
118
+ }
119
+ // eslint-disable-next-line no-console
120
+ console.debug(`ping:${checkpoint}`, pdata);
121
+ };
122
+ sendPing(data);
77
123
  }
124
+ }
78
125
 
79
- if (Array.from(element.classList).some((className) => className.match(/button|cta/))) {
80
- return blockName ? `.${blockName} .button` : '.button';
126
+ function processQueue() {
127
+ while (queue.length) {
128
+ const ck = queue.shift();
129
+ trackCheckpoint(...ck);
81
130
  }
131
+ }
82
132
 
83
- return sourceselector(element.parentElement);
84
- } catch (error) {
85
- return null;
133
+ function addCWVTracking() {
134
+ setTimeout(() => {
135
+ try {
136
+ const cwvScript = new URL('.rum/web-vitals/dist/web-vitals.iife.js', sampleRUM.baseURL).href;
137
+ if (document.querySelector(`script[src="${cwvScript}"]`)) {
138
+ // web vitals script has been loaded already
139
+ return;
140
+ }
141
+ const script = document.createElement('script');
142
+ script.src = cwvScript;
143
+ script.onload = () => {
144
+ const storeCWV = (measurement) => {
145
+ const data = { cwv: {} };
146
+ data.cwv[measurement.name] = measurement.value;
147
+ if (measurement.name === 'LCP' && measurement.entries.length > 0) {
148
+ const { element } = measurement.entries.pop();
149
+ data.target = targetSelector(element);
150
+ data.source = sourceSelector(element) || (element && element.outerHTML.slice(0, 30));
151
+ }
152
+ sampleRUM('cwv', data);
153
+ };
154
+
155
+ const featureToggle = () => window.location.hostname === 'blog.adobe.com';
156
+ const isEager = (metric) => ['CLS', 'LCP'].includes(metric);
157
+
158
+ // When loading `web-vitals` using a classic script, all the public
159
+ // methods can be found on the `webVitals` global namespace.
160
+ ['FID', 'INP', 'TTFB', 'CLS', 'LCP'].forEach((metric) => {
161
+ const metricFn = window.webVitals[`on${metric}`];
162
+ if (typeof metricFn === 'function') {
163
+ const opts = isEager(metric) ? { reportAllChanges: featureToggle() } : undefined;
164
+ metricFn(storeCWV, opts);
165
+ }
166
+ });
167
+ };
168
+ document.head.appendChild(script);
169
+ } catch (error) {
170
+ // something went wrong
171
+ }
172
+ }, 2000); // wait for delayed
86
173
  }
87
- };
88
-
89
- const formSubmitListener = (e) => sampleRUM('formsubmit', { target: targetselector(e.target), source: sourceselector(e.target) });
90
- // eslint-disable-next-line no-use-before-define
91
- const mutationObserver = window.MutationObserver ? new MutationObserver(mutationsCallback) : null;
92
-
93
- // eslint-disable-next-line no-unused-vars
94
- function optedIn(checkpoint, data) {
95
- // TODO: check config service to know if
96
- return true;
97
- }
98
- // Gets configured collection from the config service for the current domain
99
- function getCollectionConfig() {
100
- // eslint-disable-next-line max-len
101
- fflags.enabled('onetrust', () => DEFAULT_TRACKING_EVENTS.push('consent'));
102
- fflags.enabled('ads', () => DEFAULT_TRACKING_EVENTS.push('paid'));
103
- fflags.enabled('email', () => DEFAULT_TRACKING_EVENTS.push('email'));
104
- return DEFAULT_TRACKING_EVENTS;
105
- }
106
-
107
- function trackCheckpoint(checkpoint, data, t) {
108
- const { weight, id } = window.hlx.rum;
109
- if (optedIn(checkpoint, data) && isSelected) {
110
- const sendPing = (pdata = data) => {
111
- // eslint-disable-next-line object-curly-newline, max-len
112
- const body = JSON.stringify({ weight, id, referer: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'](), checkpoint, t, ...data }, KNOWN_PROPERTIES);
113
- const { href: url, origin } = new URL(`.rum/${weight}`, sampleRUM.collectBaseURL || sampleRUM.baseURL);
114
- if (window.location.origin === origin) {
115
- const headers = { type: 'application/json' };
116
- navigator.sendBeacon(url, new Blob([body], headers));
174
+
175
+ function addEnterLeaveTracking() {
176
+ // enter checkpoint when referrer is not the current page url
177
+ const navigate = (source, type) => {
178
+ const payload = { source, target: document.visibilityState };
179
+ // reload: same page, navigate: same origin, enter: everything else
180
+ if (type === 'reload' || source === window.location.href) {
181
+ sampleRUM('reload', payload);
182
+ } else if (type && type !== 'navigate') {
183
+ sampleRUM(type, payload); // back, forward, prerender, etc.
184
+ } else if (source && window.location.origin === new URL(source).origin) {
185
+ sampleRUM('navigate', payload); // internal navigation
117
186
  } else {
118
- navigator.sendBeacon(url, body);
187
+ sampleRUM('enter', payload); // enter site
119
188
  }
120
- // eslint-disable-next-line no-console
121
- console.debug(`ping:${checkpoint}`, pdata);
122
189
  };
123
- sendPing(data);
124
- }
125
- }
126
190
 
127
- function processQueue() {
128
- while (queue.length) {
129
- const ck = queue.shift();
130
- trackCheckpoint(...ck);
191
+ new PerformanceObserver((list) => list
192
+ .getEntries().map((entry) => navigate(document.referrer, entry.type)))
193
+ .observe({ type: 'navigation', buffered: true });
194
+
195
+ const leave = ((event) => {
196
+ try {
197
+ if (leave.left || (event.type === 'visibilitychange' && document.visibilityState !== 'hidden')) {
198
+ return;
199
+ }
200
+ leave.left = true;
201
+ sampleRUM('leave');
202
+ } catch (error) {
203
+ // something went wrong
204
+ }
205
+ });
206
+ window.addEventListener('visibilitychange', ((event) => leave(event)));
207
+ window.addEventListener('pagehide', ((event) => leave(event)));
131
208
  }
132
- }
133
209
 
134
- function addCWVTracking() {
135
- setTimeout(() => {
136
- try {
137
- const cwvScript = new URL('.rum/web-vitals/dist/web-vitals.iife.js', sampleRUM.baseURL).href;
138
- if (document.querySelector(`script[src="${cwvScript}"]`)) {
139
- // web vitals script has been loaded already
140
- return;
210
+ function addLoadResourceTracking() {
211
+ const observer = new PerformanceObserver((list) => {
212
+ try {
213
+ list.getEntries()
214
+ .filter((entry) => !entry.responseStatus || entry.responseStatus < 400)
215
+ .filter((entry) => window.location.hostname === new URL(entry.name).hostname)
216
+ .filter((entry) => new URL(entry.name).pathname.match('.*(\\.plain\\.html|\\.json)$'))
217
+ .forEach((entry) => {
218
+ sampleRUM('loadresource', { source: entry.name, target: Math.round(entry.duration) });
219
+ });
220
+ list.getEntries()
221
+ .filter((entry) => entry.responseStatus === 404)
222
+ .forEach((entry) => {
223
+ sampleRUM('missingresource', { source: entry.name, target: entry.hostname });
224
+ });
225
+ } catch (error) {
226
+ // something went wrong
141
227
  }
142
- const script = document.createElement('script');
143
- script.src = cwvScript;
144
- script.onload = () => {
145
- const storeCWV = (measurement) => {
146
- const data = { cwv: {} };
147
- data.cwv[measurement.name] = measurement.value;
148
- if (measurement.name === 'LCP' && measurement.entries.length > 0) {
149
- const { element } = measurement.entries.pop();
150
- data.target = targetselector(element);
151
- data.source = sourceselector(element) || (element && element.outerHTML.slice(0, 30));
152
- }
153
- sampleRUM('cwv', data);
154
- };
228
+ });
229
+ observer.observe({ type: 'resource', buffered: true });
230
+ }
155
231
 
156
- const featureToggle = () => window.location.hostname === 'blog.adobe.com';
157
- const isEager = (metric) => ['CLS', 'LCP'].includes(metric);
158
-
159
- // When loading `web-vitals` using a classic script, all the public
160
- // methods can be found on the `webVitals` global namespace.
161
- ['FID', 'INP', 'TTFB', 'CLS', 'LCP'].forEach((metric) => {
162
- const metricFn = window.webVitals[`on${metric}`];
163
- if (typeof metricFn === 'function') {
164
- const opts = isEager(metric) ? { reportAllChanges: featureToggle() } : undefined;
165
- metricFn(storeCWV, opts);
166
- }
167
- });
168
- };
169
- document.head.appendChild(script);
170
- } catch (error) {
171
- // something went wrong
172
- }
173
- }, 2000); // wait for delayed
174
- }
175
-
176
- function addEnterLeaveTracking() {
177
- // enter checkpoint when referrer is not the current page url
178
- const navigate = (source, type) => {
179
- const payload = { source, target: document.visibilityState };
180
- // reload: same page, navigate: same origin, enter: everything else
181
- if (type === 'reload' || source === window.location.href) {
182
- sampleRUM('reload', payload);
183
- } else if (type && type !== 'navigate') {
184
- sampleRUM(type, payload); // back, forward, prerender, etc.
185
- } else if (source && window.location.origin === new URL(source).origin) {
186
- sampleRUM('navigate', payload); // internal navigation
187
- } else {
188
- sampleRUM('enter', payload); // enter site
232
+ function activateBlocksMutationObserver() {
233
+ if (!mutationObserver || mutationObserver.active) {
234
+ return;
189
235
  }
190
- };
191
-
192
- new PerformanceObserver((list) => list
193
- .getEntries().map((entry) => navigate(document.referrer, entry.type)))
194
- .observe({ type: 'navigation', buffered: true });
236
+ mutationObserver.active = true;
237
+ mutationObserver.observe(
238
+ document.body,
239
+ // eslint-disable-next-line object-curly-newline
240
+ { subtree: true, attributes: true, attributeFilter: ['data-block-status'] },
241
+ );
242
+ }
195
243
 
196
- const leave = ((event) => {
197
- try {
198
- if (leave.left || (event.type === 'visibilitychange' && document.visibilityState !== 'hidden')) {
199
- return;
244
+ function getIntersectionObsever(checkpoint) {
245
+ if (!window.IntersectionObserver) {
246
+ return null;
247
+ }
248
+ activateBlocksMutationObserver();
249
+ const observer = new IntersectionObserver((entries) => {
250
+ try {
251
+ entries
252
+ .filter((entry) => entry.isIntersecting)
253
+ .forEach((entry) => {
254
+ observer.unobserve(entry.target); // observe only once
255
+ const target = targetSelector(entry.target);
256
+ const source = sourceSelector(entry.target);
257
+ sampleRUM(checkpoint, { target, source });
258
+ });
259
+ } catch (error) {
260
+ // something went wrong
200
261
  }
201
- leave.left = true;
202
- sampleRUM('leave');
203
- } catch (error) {
204
- // something went wrong
262
+ }, { threshold: 0.25 });
263
+ return observer;
264
+ }
265
+ function addViewBlockTracking(element) {
266
+ const blockobserver = getIntersectionObsever('viewblock');
267
+ if (blockobserver) {
268
+ const blocks = element.getAttribute('data-block-status') ? [element] : element.querySelectorAll('div[data-block-status="loaded"]');
269
+ blocks.forEach((b) => blockobserver.observe(b));
205
270
  }
206
- });
207
- window.addEventListener('visibilitychange', ((event) => leave(event)));
208
- window.addEventListener('pagehide', ((event) => leave(event)));
209
- }
271
+ }
210
272
 
211
- function addLoadResourceTracking() {
212
- const observer = new PerformanceObserver((list) => {
213
- try {
214
- list.getEntries()
215
- .filter((entry) => !entry.responseStatus || entry.responseStatus < 400)
216
- .filter((entry) => window.location.hostname === new URL(entry.name).hostname)
217
- .filter((entry) => new URL(entry.name).pathname.match('.*(\\.plain\\.html|\\.json)$'))
218
- .forEach((entry) => {
219
- sampleRUM('loadresource', { source: entry.name, target: Math.round(entry.duration) });
220
- });
221
- list.getEntries()
222
- .filter((entry) => entry.responseStatus === 404)
223
- .forEach((entry) => {
224
- sampleRUM('missingresource', { source: entry.name, target: entry.hostname });
225
- });
226
- } catch (error) {
227
- // something went wrong
273
+ function addViewMediaTracking(parent) {
274
+ const mediaobserver = getIntersectionObsever('viewmedia');
275
+ if (mediaobserver) {
276
+ parent.querySelectorAll('img, video, audio, iframe').forEach((m) => {
277
+ if (!m.closest('div .block') || m.closest('div[data-block-status="loaded"]')) {
278
+ mediaobserver.observe(m);
279
+ }
280
+ });
228
281
  }
229
- });
230
- observer.observe({ type: 'resource', buffered: true });
231
- }
282
+ }
232
283
 
233
- function activateBlocksMutationObserver() {
234
- if (!mutationObserver || mutationObserver.active) {
235
- return;
284
+ function addUTMParametersTracking() {
285
+ const usp = new URLSearchParams(window.location.search);
286
+ [...usp.entries()]
287
+ .filter(([key]) => key.startsWith('utm_'))
288
+ // exclude keys that may leak PII
289
+ .filter(([key]) => key !== 'utm_id')
290
+ .filter(([key]) => key !== 'utm_term')
291
+ .forEach(([source, target]) => sampleRUM('utm', { source, target }));
236
292
  }
237
- mutationObserver.active = true;
238
- mutationObserver.observe(
239
- document.body,
240
- // eslint-disable-next-line object-curly-newline
241
- { subtree: true, attributes: true, attributeFilter: ['data-block-status'] },
242
- );
243
- }
244
-
245
- function getIntersectionObsever(checkpoint) {
246
- if (!window.IntersectionObserver) {
247
- return null;
293
+
294
+ function addAdsParametersTracking() {
295
+ const networks = {
296
+ google: /gclid|gclsrc|wbraid|gbraid/,
297
+ doubleclick: /dclid/,
298
+ microsoft: /msclkid/,
299
+ facebook: /fb(cl|ad_|pxl_)id/,
300
+ twitter: /tw(clid|src|term)/,
301
+ linkedin: /li_fat_id/,
302
+ pinterest: /epik/,
303
+ tiktok: /ttclid/,
304
+ };
305
+ const params = Array.from(new URLSearchParams(window.location.search).keys());
306
+ Object.entries(networks).forEach(([network, regex]) => {
307
+ params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('paid', { source: network, target: param }));
308
+ });
248
309
  }
249
- activateBlocksMutationObserver();
250
- const observer = new IntersectionObserver((entries) => {
251
- try {
252
- entries
253
- .filter((entry) => entry.isIntersecting)
254
- .forEach((entry) => {
255
- observer.unobserve(entry.target); // observe only once
256
- const target = targetselector(entry.target);
257
- const source = sourceselector(entry.target);
258
- sampleRUM(checkpoint, { target, source });
259
- });
260
- } catch (error) {
261
- // something went wrong
262
- }
263
- }, { threshold: 0.25 });
264
- return observer;
265
- }
266
- function addViewBlockTracking(element) {
267
- const blockobserver = getIntersectionObsever('viewblock');
268
- if (blockobserver) {
269
- const blocks = element.getAttribute('data-block-status') ? [element] : element.querySelectorAll('div[data-block-status="loaded"]');
270
- blocks.forEach((b) => blockobserver.observe(b));
310
+
311
+ function addEmailParameterTracking() {
312
+ const networks = {
313
+ mailchimp: /mc_(c|e)id/,
314
+ marketo: /mkt_tok/,
315
+
316
+ };
317
+ const params = Array.from(new URLSearchParams(window.location.search).keys());
318
+ Object.entries(networks).forEach(([network, regex]) => {
319
+ params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('email', { source: network, target: param }));
320
+ });
271
321
  }
272
- }
273
-
274
- function addViewMediaTracking(parent) {
275
- const mediaobserver = getIntersectionObsever('viewmedia');
276
- if (mediaobserver) {
277
- parent.querySelectorAll('picture > img, video, audio, iframe').forEach((m) => {
278
- if (!m.closest('div .block') || m.closest('div[data-block-status="loaded"]')) {
279
- mediaobserver.observe(m);
280
- }
322
+
323
+ function addFormTracking(parent) {
324
+ activateBlocksMutationObserver();
325
+ parent.querySelectorAll('form').forEach((form) => {
326
+ form.removeEventListener('submit', formSubmitListener); // listen only once
327
+ form.addEventListener('submit', formSubmitListener);
281
328
  });
282
329
  }
283
- }
284
-
285
- function addUTMParametersTracking() {
286
- const usp = new URLSearchParams(window.location.search);
287
- [...usp.entries()]
288
- .filter(([key]) => key.startsWith('utm_'))
289
- // exclude keys that may leak PII
290
- .filter(([key]) => key !== 'utm_id')
291
- .filter(([key]) => key !== 'utm_term')
292
- .forEach(([source, target]) => sampleRUM('utm', { source, target }));
293
- }
294
-
295
- function addAdsParametersTracking() {
296
- const networks = {
297
- google: /gclid|gclsrc|wbraid|gbraid/,
298
- doubleclick: /dclid/,
299
- microsoft: /msclkid/,
300
- facebook: /fb(cl|ad_|pxl_)id/,
301
- twitter: /tw(clid|src|term)/,
302
- linkedin: /li_fat_id/,
303
- pinterest: /epik/,
304
- tiktok: /ttclid/,
305
- };
306
- const params = Array.from(new URLSearchParams(window.location.search).keys());
307
- Object.entries(networks).forEach(([network, regex]) => {
308
- params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('paid', { source: network, target: param }));
309
- });
310
- }
311
330
 
312
- function addEmailParameterTracking() {
313
- const networks = {
314
- mailchimp: /mc_(c|e)id/,
315
- marketo: /mkt_tok/,
331
+ function addCookieConsentTracking() {
332
+ const cmpCookie = document.cookie.split(';')
333
+ .map((c) => c.trim())
334
+ .find((cookie) => cookie.startsWith('OptanonAlertBoxClosed='));
316
335
 
317
- };
318
- const params = Array.from(new URLSearchParams(window.location.search).keys());
319
- Object.entries(networks).forEach(([network, regex]) => {
320
- params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('email', { source: network, target: param }));
321
- });
322
- }
323
-
324
- function addFormTracking(parent) {
325
- activateBlocksMutationObserver();
326
- parent.querySelectorAll('form').forEach((form) => {
327
- form.removeEventListener('submit', formSubmitListener); // listen only once
328
- form.addEventListener('submit', formSubmitListener);
329
- });
330
- }
331
-
332
- function addCookieConsentTracking() {
333
- const cmpCookie = document.cookie.split(';')
334
- .map((c) => c.trim())
335
- .find((cookie) => cookie.startsWith('OptanonAlertBoxClosed='));
336
-
337
- if (cmpCookie) {
338
- sampleRUM('consent', { source: 'onetrust', target: 'hidden' });
339
- return;
340
- }
336
+ if (cmpCookie) {
337
+ sampleRUM('consent', { source: 'onetrust', target: 'hidden' });
338
+ return;
339
+ }
341
340
 
342
- let consentMutationObserver;
343
- const trackShowConsent = () => {
344
- if (document.querySelector('body > div#onetrust-consent-sdk')) {
345
- sampleRUM('consent', { source: 'onetrust', target: 'show' });
346
- if (consentMutationObserver) {
347
- consentMutationObserver.disconnect();
341
+ let consentMutationObserver;
342
+ const trackShowConsent = () => {
343
+ if (document.querySelector('body > div#onetrust-consent-sdk')) {
344
+ sampleRUM('consent', { source: 'onetrust', target: 'show' });
345
+ if (consentMutationObserver) {
346
+ consentMutationObserver.disconnect();
347
+ }
348
+ return true;
348
349
  }
349
- return true;
350
- }
351
- return false;
352
- };
350
+ return false;
351
+ };
353
352
 
354
- if (!trackShowConsent()) {
355
- // eslint-disable-next-line max-len
356
- consentMutationObserver = window.MutationObserver ? new MutationObserver(trackShowConsent) : null;
357
- if (consentMutationObserver) {
358
- consentMutationObserver.observe(
359
- document.body,
360
- // eslint-disable-next-line object-curly-newline
361
- { attributes: false, childList: true, subtree: false },
362
- );
353
+ if (!trackShowConsent()) {
354
+ // eslint-disable-next-line max-len
355
+ consentMutationObserver = window.MutationObserver ? new MutationObserver(trackShowConsent) : null;
356
+ if (consentMutationObserver) {
357
+ consentMutationObserver.observe(
358
+ document.body,
359
+ // eslint-disable-next-line object-curly-newline
360
+ { attributes: false, childList: true, subtree: false },
361
+ );
362
+ }
363
363
  }
364
364
  }
365
- }
366
-
367
- const addObserver = (ck, fn, block) => getCollectionConfig().includes(ck) && fn(block);
368
- function mutationsCallback(mutations) {
369
- mutations.filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status')
370
- .filter((m) => m.target.dataset.blockStatus === 'loaded')
371
- .forEach((m) => {
372
- addObserver('form', addFormTracking, m.target);
373
- addObserver('viewblock', addViewBlockTracking, m.target);
374
- addObserver('viewmedia', addViewMediaTracking, m.target);
375
- });
376
- }
377
365
 
378
- function addTrackingFromConfig() {
379
- const trackingFunctions = {
380
- click: () => {
381
- document.addEventListener('click', (event) => {
382
- sampleRUM('click', { target: targetselector(event.target), source: sourceselector(event.target) });
366
+ const addObserver = (ck, fn, block) => getCollectionConfig().includes(ck) && fn(block);
367
+ function mutationsCallback(mutations) {
368
+ mutations.filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status')
369
+ .filter((m) => m.target.dataset.blockStatus === 'loaded')
370
+ .forEach((m) => {
371
+ addObserver('form', addFormTracking, m.target);
372
+ addObserver('viewblock', addViewBlockTracking, m.target);
373
+ addObserver('viewmedia', addViewMediaTracking, m.target);
383
374
  });
384
- },
385
- cwv: () => addCWVTracking(),
386
- form: () => addFormTracking(window.document.body),
387
- enterleave: () => addEnterLeaveTracking(),
388
- loadresource: () => addLoadResourceTracking(),
389
- utm: () => addUTMParametersTracking(),
390
- viewblock: () => addViewBlockTracking(window.document.body),
391
- viewmedia: () => addViewMediaTracking(window.document.body),
392
- consent: () => addCookieConsentTracking(),
393
- paid: () => addAdsParametersTracking(),
394
- email: () => addEmailParameterTracking(),
395
- };
375
+ }
376
+
377
+ function addTrackingFromConfig() {
378
+ const trackingFunctions = {
379
+ click: () => {
380
+ document.addEventListener('click', (event) => {
381
+ sampleRUM('click', { target: targetSelector(event.target), source: sourceSelector(event.target) });
382
+ });
383
+ },
384
+ cwv: () => addCWVTracking(),
385
+ form: () => addFormTracking(window.document.body),
386
+ enterleave: () => addEnterLeaveTracking(),
387
+ loadresource: () => addLoadResourceTracking(),
388
+ utm: () => addUTMParametersTracking(),
389
+ viewblock: () => addViewBlockTracking(window.document.body),
390
+ viewmedia: () => addViewMediaTracking(window.document.body),
391
+ consent: () => addCookieConsentTracking(),
392
+ paid: () => addAdsParametersTracking(),
393
+ email: () => addEmailParameterTracking(),
394
+ };
396
395
 
397
- getCollectionConfig().filter((ck) => trackingFunctions[ck])
398
- .forEach((ck) => trackingFunctions[ck]());
399
- }
396
+ getCollectionConfig().filter((ck) => trackingFunctions[ck])
397
+ .forEach((ck) => trackingFunctions[ck]());
398
+ }
400
399
 
401
- function initEnhancer() {
402
- try {
403
- if (sampleRUM) {
404
- addTrackingFromConfig();
405
- window.hlx.rum.collector = trackCheckpoint;
406
- processQueue();
400
+ function initEnhancer() {
401
+ try {
402
+ if (sampleRUM) {
403
+ addTrackingFromConfig();
404
+ window.hlx.rum.collector = trackCheckpoint;
405
+ processQueue();
406
+ }
407
+ } catch (error) {
408
+ // something went wrong
407
409
  }
408
- } catch (error) {
409
- // something went wrong
410
410
  }
411
- }
412
411
 
413
- // eslint-disable-next-line padded-blocks
414
- initEnhancer();
412
+ initEnhancer();
413
+ })();