14
14
import { KNOWN_PROPERTIES , DEFAULT_TRACKING_EVENTS } from './defaults.js' ;
15
15
import { urlSanitizers } from './utils.js' ;
16
16
import { targetSelector , sourceSelector } from './dom.js' ;
17
- import {
18
- addAdsParametersTracking ,
19
- addCookieConsentTracking ,
20
- addEmailParameterTracking ,
21
- addUTMParametersTracking ,
22
- } from './martech.js' ;
23
17
import { fflags } from './fflags.js' ;
24
18
25
19
const { sampleRUM, queue, isSelected } = ( window . hlx && window . hlx . rum ) ? window . hlx . rum
26
20
/* c8 ignore next */ : { } ;
27
21
28
- // blocks mutation observer
29
- // eslint-disable-next-line no-use-before-define, max-len
30
- const blocksMO = window . MutationObserver ? new MutationObserver ( blocksMCB )
31
- /* c8 ignore next */ : { } ;
22
+ const createMO = ( cb ) => ( window . MutationObserver ? new MutationObserver ( cb )
23
+ /* c8 ignore next */ : { } ) ;
32
24
33
- // media mutation observer
34
- // eslint-disable-next-line no-use-before-define, max-len
35
- const mediaMO = window . MutationObserver ? new MutationObserver ( mediaMCB )
36
- /* c8 ignore next */ : { } ;
25
+ // blocks & media mutation observer
26
+ // eslint-disable-next-line no-use-before-define
27
+ const [ blocksMO , mediaMO ] = [ blocksMCB , mediaMCB ] . map ( createMO ) ;
28
+
29
+ // Check for the presence of a given cookie
30
+ const hasCookieKey = ( key ) => ( ) => document . cookie . split ( ';' ) . some ( ( c ) => c . trim ( ) . startsWith ( `${ key } =` ) ) ;
31
+
32
+ // Set the base path for the plugins
33
+ const pluginBasePath = new URL ( document . currentScript . src ) . href . replace ( / i n d e x ( \. m a p ) ? \. j s / , 'plugins' ) ;
34
+
35
+ const PLUGINS = {
36
+ cwv : `${ pluginBasePath } /cwv.js` ,
37
+ // Interactive elements
38
+ form : { url : `${ pluginBasePath } /form.js` , when : ( ) => document . querySelector ( 'form' ) , isBlockDependent : true } ,
39
+ video : { url : `${ pluginBasePath } /video.js` , when : ( ) => document . querySelector ( 'video' ) , isBlockDependent : true } ,
40
+ // Martech
41
+ martech : { url : `${ pluginBasePath } /martech.js` , when : ( { urlParameters } ) => urlParameters . size > 0 } ,
42
+ onetrust : { url : `${ pluginBasePath } /onetrust.js` , when : ( ) => ( document . querySelector ( '#onetrust-consent-sdk' ) || hasCookieKey ( 'OptanonAlertBoxClosed' ) ) , isBlockDependent : true } ,
43
+ // test: broken-plugin
44
+ } ;
45
+
46
+ const PLUGIN_PARAMETERS = {
47
+ context : document . body ,
48
+ fflags,
49
+ sampleRUM,
50
+ sourceSelector,
51
+ targetSelector,
52
+ } ;
53
+
54
+ const pluginCache = new Map ( ) ;
55
+
56
+ function loadPlugin ( key , params ) {
57
+ const plugin = PLUGINS [ key ] ;
58
+ const usp = new URLSearchParams ( window . location . search ) ;
59
+ if ( ! pluginCache . has ( key ) && plugin . when && ! plugin . when ( { urlParameters : usp } ) ) {
60
+ return null ;
61
+ }
62
+ if ( ! pluginCache . has ( key ) ) {
63
+ pluginCache . set ( key , import ( `${ plugin . url || plugin } ` ) ) ;
64
+ }
65
+ const pluginLoadPromise = pluginCache . get ( key ) ;
66
+ return pluginLoadPromise
67
+ . then ( ( p ) => ( p . default && p . default ( params ) ) || ( typeof p === 'function' && p ( params ) ) )
68
+ . catch ( ( ) => { /* silent plugin error catching */ } ) ;
69
+ }
70
+
71
+ function loadPlugins ( filter = ( ) => true , params = PLUGIN_PARAMETERS ) {
72
+ Object . entries ( PLUGINS )
73
+ . filter ( ( [ , plugin ] ) => filter ( plugin ) )
74
+ . map ( ( [ key ] ) => loadPlugin ( key , params ) ) ;
75
+ }
37
76
/**
38
77
* Maximum number of events. The first call will be made by rum-js,
39
78
* leaving 1023 events for the enhancer to track
@@ -70,55 +109,9 @@ function processQueue() {
70
109
}
71
110
}
72
111
73
- function addCWVTracking ( ) {
74
- setTimeout ( ( ) => {
75
- try {
76
- const cwvScript = new URL ( '.rum/web-vitals/dist/web-vitals.iife.js' , sampleRUM . baseURL ) . href ;
77
- if ( document . querySelector ( `script[src="${ cwvScript } "]` ) ) {
78
- // web vitals script has been loaded already
79
- return ;
80
- }
81
- const script = document . createElement ( 'script' ) ;
82
- script . src = cwvScript ;
83
- script . onload = ( ) => {
84
- const storeCWV = ( measurement ) => {
85
- const data = { cwv : { } } ;
86
- data . cwv [ measurement . name ] = measurement . value ;
87
- if ( measurement . name === 'LCP' && measurement . entries . length > 0 ) {
88
- const { element } = measurement . entries . pop ( ) ;
89
- data . target = targetSelector ( element ) ;
90
- data . source = sourceSelector ( element ) || ( element && element . outerHTML . slice ( 0 , 30 ) ) ;
91
- }
92
- sampleRUM ( 'cwv' , data ) ;
93
- } ;
94
-
95
- const isEager = ( metric ) => [ 'CLS' , 'LCP' ] . includes ( metric ) ;
96
-
97
- // When loading `web-vitals` using a classic script, all the public
98
- // methods can be found on the `webVitals` global namespace.
99
- [ 'INP' , 'TTFB' , 'CLS' , 'LCP' ] . forEach ( ( metric ) => {
100
- const metricFn = window . webVitals [ `on${ metric } ` ] ;
101
- if ( typeof metricFn === 'function' ) {
102
- let opts = { } ;
103
- fflags . enabled ( 'eagercwv' , ( ) => {
104
- opts = { reportAllChanges : isEager ( metric ) } ;
105
- } ) ;
106
- metricFn ( storeCWV , opts ) ;
107
- }
108
- } ) ;
109
- } ;
110
- document . head . appendChild ( script ) ;
111
- /* c8 ignore next 3 */
112
- } catch ( error ) {
113
- // something went wrong
114
- }
115
- } , 2000 ) ; // wait for delayed
116
- }
117
-
118
112
function addNavigationTracking ( ) {
119
113
// enter checkpoint when referrer is not the current page url
120
114
const navigate = ( source , type , redirectCount ) => {
121
- // target can be 'visible', 'hidden' (background tab) or 'prerendered' (speculation rules)
122
115
const payload = { source, target : document . visibilityState } ;
123
116
/* c8 ignore next 13 */
124
117
// prerendering cannot be tested yet with headless browsers
@@ -218,8 +211,6 @@ function getIntersectionObsever(checkpoint) {
218
211
if ( ! window . IntersectionObserver ) {
219
212
return null ;
220
213
}
221
- activateBlocksMO ( ) ;
222
- activateMediaMO ( ) ;
223
214
const observer = new IntersectionObserver ( ( entries ) => {
224
215
try {
225
216
entries
@@ -258,30 +249,6 @@ function addViewMediaTracking(parent) {
258
249
}
259
250
}
260
251
261
- function addFormTracking ( parent ) {
262
- activateBlocksMO ( ) ;
263
- activateMediaMO ( ) ;
264
- parent . querySelectorAll ( 'form' ) . forEach ( ( form ) => {
265
- form . addEventListener ( 'submit' , ( e ) => sampleRUM ( 'formsubmit' , { target : targetSelector ( e . target ) , source : sourceSelector ( e . target ) } ) , { once : true } ) ;
266
- let lastSource ;
267
- form . addEventListener ( 'change' , ( e ) => {
268
- if ( e . target . checkVisibility ( ) ) {
269
- const source = sourceSelector ( e . target ) ;
270
- if ( source !== lastSource ) {
271
- sampleRUM ( 'fill' , { source } ) ;
272
- lastSource = source ;
273
- }
274
- }
275
- } ) ;
276
- form . addEventListener ( 'focusin' , ( e ) => {
277
- if ( [ 'INPUT' , 'TEXTAREA' , 'SELECT' , 'BUTTON' ] . includes ( e . target . tagName )
278
- || e . target . getAttribute ( 'contenteditable' ) === 'true' ) {
279
- sampleRUM ( 'click' , { source : sourceSelector ( e . target ) } ) ;
280
- }
281
- } ) ;
282
- } ) ;
283
- }
284
-
285
252
function addObserver ( ck , fn , block ) {
286
253
return DEFAULT_TRACKING_EVENTS . includes ( ck ) && fn ( block ) ;
287
254
}
@@ -293,7 +260,7 @@ function blocksMCB(mutations) {
293
260
. filter ( ( m ) => m . type === 'attributes' && m . attributeName === 'data-block-status' )
294
261
. filter ( ( m ) => m . target . dataset . blockStatus === 'loaded' )
295
262
. forEach ( ( m ) => {
296
- addObserver ( 'form' , addFormTracking , m . target ) ;
263
+ addObserver ( 'form' , ( el ) => loadPlugins ( ( p ) => p . isBlockDependent , { ... PLUGIN_PARAMETERS , context : el } ) , m . target ) ;
297
264
addObserver ( 'viewblock' , addViewBlockTracking , m . target ) ;
298
265
} ) ;
299
266
}
@@ -308,20 +275,22 @@ function mediaMCB(mutations) {
308
275
}
309
276
310
277
function addTrackingFromConfig ( ) {
278
+ activateBlocksMO ( ) ;
279
+ activateMediaMO ( ) ;
280
+
311
281
document . addEventListener ( 'click' , ( event ) => {
312
282
sampleRUM ( 'click' , { target : targetSelector ( event . target ) , source : sourceSelector ( event . target ) } ) ;
313
283
} ) ;
314
284
315
- addCWVTracking ( ) ;
316
- addFormTracking ( window . document . body ) ;
285
+ // Core tracking
317
286
addNavigationTracking ( ) ;
318
287
addLoadResourceTracking ( ) ;
319
- addUTMParametersTracking ( sampleRUM ) ;
320
- addViewBlockTracking ( window . document . body ) ;
321
- addViewMediaTracking ( window . document . body ) ;
322
- addCookieConsentTracking ( sampleRUM ) ;
323
- addAdsParametersTracking ( sampleRUM ) ;
324
- addEmailParameterTracking ( sampleRUM ) ;
288
+ addViewBlockTracking ( document . body ) ;
289
+ addViewMediaTracking ( document . body ) ;
290
+
291
+ // Tracking extensions
292
+ loadPlugins ( ) ;
293
+
325
294
fflags . enabled ( 'language' , ( ) => {
326
295
const target = navigator . language ;
327
296
const source = document . documentElement . lang ;
0 commit comments