@adobe/helix-rum-enhancer 2.26.0-beta.1 → 2.26.0
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/.releaserc.cjs +1 -1
- package/CHANGELOG.md +2 -2
- package/modules/dom.js +56 -56
- package/modules/index.js +41 -36
- package/modules/martech.js +6 -6
- package/modules/utils.js +5 -3
- package/package.json +2 -2
- package/src/index.js +99 -100
- package/src/index.md5 +1 -1
package/.releaserc.cjs
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# [2.26.0
|
|
1
|
+
# [2.26.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.25.0...v2.26.0) (2024-10-16)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Features
|
|
5
5
|
|
|
6
|
-
* handle pre-rendering ([
|
|
6
|
+
* handle pre-speculation rules and rendering ([#301](https://github.com/adobe/helix-rum-enhancer/issues/301)) ([196d404](https://github.com/adobe/helix-rum-enhancer/commit/196d404f3f90a247a223e8ddc7c667592a87319f))
|
|
7
7
|
|
|
8
8
|
# [2.25.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.24.0...v2.25.0) (2024-10-03)
|
|
9
9
|
|
package/modules/dom.js
CHANGED
|
@@ -9,99 +9,99 @@
|
|
|
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
|
-
export const getTargetValue = (
|
|
13
|
-
||
|
|
12
|
+
export const getTargetValue = (el) => el.getAttribute('data-rum-target') || el.getAttribute('href')
|
|
13
|
+
|| el.currentSrc || el.getAttribute('src') || el.dataset.action || el.action;
|
|
14
14
|
|
|
15
|
-
export const targetSelector = (
|
|
15
|
+
export const targetSelector = (el) => {
|
|
16
16
|
try {
|
|
17
|
-
if (!
|
|
18
|
-
let
|
|
19
|
-
if (!
|
|
20
|
-
|
|
17
|
+
if (!el) return undefined;
|
|
18
|
+
let v = getTargetValue(el);
|
|
19
|
+
if (!v && el.tagName !== 'A' && el.closest('a')) {
|
|
20
|
+
v = getTargetValue(el.closest('a'));
|
|
21
21
|
}
|
|
22
|
-
if (
|
|
22
|
+
if (v && !v.startsWith('https://')) {
|
|
23
23
|
// resolve relative links
|
|
24
|
-
|
|
24
|
+
v = new URL(v, window.location).href;
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return v;
|
|
27
27
|
/* c8 ignore next 3 */
|
|
28
28
|
} catch (error) {
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
function walk(
|
|
34
|
-
if (!
|
|
33
|
+
function walk(el, checkFn) {
|
|
34
|
+
if (!el || el === document.body || el === document.documentElement) {
|
|
35
35
|
return undefined;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
return checkValue || walk(element.parentElement, checkFn);
|
|
37
|
+
return checkFn(el) || walk(el.parentElement, checkFn);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
function isDialog(
|
|
40
|
+
function isDialog(el) {
|
|
42
41
|
// doing it well
|
|
43
|
-
if (
|
|
42
|
+
if (el.tagName === 'DIALOG') return true;
|
|
44
43
|
// making the best of it
|
|
45
|
-
if (
|
|
46
|
-
if (
|
|
47
|
-
if (
|
|
44
|
+
if (el.getAttribute('role') === 'dialog') return true;
|
|
45
|
+
if (el.getAttribute('role') === 'alertdialog') return true;
|
|
46
|
+
if (el.getAttribute('aria-modal') === 'true') return true;
|
|
48
47
|
// doing it wrong
|
|
49
|
-
const
|
|
50
|
-
return (
|
|
48
|
+
const cs = window.getComputedStyle(el);
|
|
49
|
+
return (cs && cs.position === 'fixed' && cs.zIndex > 100);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
function isButton(
|
|
54
|
-
if (
|
|
55
|
-
if (
|
|
56
|
-
if (
|
|
57
|
-
const classes = Array.from(
|
|
52
|
+
function isButton(el) {
|
|
53
|
+
if (el.tagName === 'BUTTON') return true;
|
|
54
|
+
if (el.tagName === 'INPUT' && el.getAttribute('type') === 'button') return true;
|
|
55
|
+
if (el.tagName === 'A') {
|
|
56
|
+
const classes = Array.from(el.classList);
|
|
58
57
|
return classes.some((className) => className.match(/button|cta/));
|
|
59
58
|
}
|
|
60
|
-
return
|
|
59
|
+
return el.getAttribute('role') === 'button';
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
function getSourceContext(
|
|
64
|
-
if (
|
|
65
|
-
return `form${
|
|
62
|
+
function getSourceContext(el) {
|
|
63
|
+
if (el.closest('form')) {
|
|
64
|
+
return `form${el.id ? `#${el.id}` : ''}`;
|
|
66
65
|
}
|
|
67
|
-
const block =
|
|
66
|
+
const block = el.closest('.block[data-block-name]');
|
|
68
67
|
if (block) return `.${block.getAttribute('data-block-name')}`;
|
|
69
|
-
if (walk(
|
|
70
|
-
if (
|
|
71
|
-
if (
|
|
72
|
-
if (
|
|
73
|
-
if (
|
|
74
|
-
return (walk(
|
|
68
|
+
if (walk(el, isDialog)) return 'dialog';
|
|
69
|
+
if (el.closest('nav')) return 'nav';
|
|
70
|
+
if (el.closest('header')) return 'header';
|
|
71
|
+
if (el.closest('footer')) return 'footer';
|
|
72
|
+
if (el.closest('aside')) return 'aside';
|
|
73
|
+
return (walk(el, (e) => e.id && `#${e.id}`));
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
function getSourceElement(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
function getSourceElement(el) {
|
|
77
|
+
const f = el.closest('form');
|
|
78
|
+
if (f && Array.from(f.elements).includes(el)) {
|
|
79
|
+
return (el.tagName.toLowerCase()
|
|
80
|
+
+ (['INPUT', 'BUTTON'].includes(el.tagName)
|
|
81
|
+
? `[type='${el.getAttribute('type') || ''}']`
|
|
82
82
|
: ''));
|
|
83
83
|
}
|
|
84
|
-
if (walk(
|
|
85
|
-
return
|
|
84
|
+
if (walk(el, isButton)) return 'button';
|
|
85
|
+
return el.tagName.toLowerCase().match(/^(a|img|video)$/) && el.tagName.toLowerCase();
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function getSourceIdentifier(
|
|
89
|
-
if (
|
|
90
|
-
if (
|
|
91
|
-
return (
|
|
88
|
+
function getSourceIdentifier(el) {
|
|
89
|
+
if (el.id) return `#${el.id}`;
|
|
90
|
+
if (el.getAttribute('data-block-name')) return `.${el.getAttribute('data-block-name')}`;
|
|
91
|
+
return (el.classList.length > 0 && `.${el.classList[0]}`);
|
|
92
92
|
}
|
|
93
|
-
export const sourceSelector = (
|
|
93
|
+
export const sourceSelector = (el) => {
|
|
94
94
|
try {
|
|
95
|
-
if (!
|
|
95
|
+
if (!el || el === document.body || el === document.documentElement) {
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
|
-
if (
|
|
99
|
-
return
|
|
98
|
+
if (el.getAttribute('data-rum-source')) {
|
|
99
|
+
return el.getAttribute('data-rum-source');
|
|
100
100
|
}
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
return `${
|
|
101
|
+
const ctx = getSourceContext(el.parentElement) || '';
|
|
102
|
+
const name = getSourceElement(el) || '';
|
|
103
|
+
const id = getSourceIdentifier(el) || '';
|
|
104
|
+
return `${ctx} ${name}${id}`.trim() || `"${el.textContent.substring(0, 10)}"`;
|
|
105
105
|
/* c8 ignore next 3 */
|
|
106
106
|
} catch (error) {
|
|
107
107
|
return null;
|
package/modules/index.js
CHANGED
|
@@ -25,14 +25,14 @@ import { fflags } from './fflags.js';
|
|
|
25
25
|
const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum
|
|
26
26
|
/* c8 ignore next */ : {};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
// blocks mutation observer
|
|
30
29
|
// eslint-disable-next-line no-use-before-define, max-len
|
|
31
|
-
const
|
|
30
|
+
const blocksMO = window.MutationObserver ? new MutationObserver(blocksMCB)
|
|
32
31
|
/* c8 ignore next */ : {};
|
|
33
32
|
|
|
33
|
+
// media mutation observer
|
|
34
34
|
// eslint-disable-next-line no-use-before-define, max-len
|
|
35
|
-
const
|
|
35
|
+
const mediaMO = window.MutationObserver ? new MutationObserver(mediaMCB)
|
|
36
36
|
/* c8 ignore next */ : {};
|
|
37
37
|
|
|
38
38
|
function trackCheckpoint(checkpoint, data, t) {
|
|
@@ -114,6 +114,8 @@ function addNavigationTracking() {
|
|
|
114
114
|
const navigate = (source, type, redirectCount) => {
|
|
115
115
|
// target can be 'visible', 'hidden' (background tab) or 'prerendered' (speculation rules)
|
|
116
116
|
const payload = { source, target: document.visibilityState };
|
|
117
|
+
/* c8 ignore next 13 */
|
|
118
|
+
// prerendering cannot be tested yet with headless browsers
|
|
117
119
|
if (document.prerendering) {
|
|
118
120
|
// listen for "activation" of the current pre-rendered page
|
|
119
121
|
document.addEventListener('prerenderingchange', () => {
|
|
@@ -144,10 +146,10 @@ function addNavigationTracking() {
|
|
|
144
146
|
};
|
|
145
147
|
|
|
146
148
|
new PerformanceObserver((list) => list
|
|
147
|
-
.getEntries().map((
|
|
149
|
+
.getEntries().map((e) => navigate(
|
|
148
150
|
window.hlx.referrer || document.referrer,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
e.type,
|
|
152
|
+
e.redirectCount,
|
|
151
153
|
)))
|
|
152
154
|
.observe({ type: 'navigation', buffered: true });
|
|
153
155
|
}
|
|
@@ -156,16 +158,16 @@ function addLoadResourceTracking() {
|
|
|
156
158
|
const observer = new PerformanceObserver((list) => {
|
|
157
159
|
try {
|
|
158
160
|
list.getEntries()
|
|
159
|
-
.filter((
|
|
160
|
-
.filter((
|
|
161
|
-
.filter((
|
|
162
|
-
.forEach((
|
|
163
|
-
sampleRUM('loadresource', { source:
|
|
161
|
+
.filter((e) => !e.responseStatus || e.responseStatus < 400)
|
|
162
|
+
.filter((e) => window.location.hostname === new URL(e.name).hostname)
|
|
163
|
+
.filter((e) => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
|
|
164
|
+
.forEach((e) => {
|
|
165
|
+
sampleRUM('loadresource', { source: e.name, target: Math.round(e.duration) });
|
|
164
166
|
});
|
|
165
167
|
list.getEntries()
|
|
166
|
-
.filter((
|
|
167
|
-
.forEach((
|
|
168
|
-
sampleRUM('missingresource', { source:
|
|
168
|
+
.filter((e) => e.responseStatus === 404)
|
|
169
|
+
.forEach((e) => {
|
|
170
|
+
sampleRUM('missingresource', { source: e.name, target: e.hostname });
|
|
169
171
|
});
|
|
170
172
|
/* c8 ignore next 3 */
|
|
171
173
|
} catch (error) {
|
|
@@ -175,24 +177,26 @@ function addLoadResourceTracking() {
|
|
|
175
177
|
observer.observe({ type: 'resource', buffered: true });
|
|
176
178
|
}
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
// activate blocks mutation observer
|
|
181
|
+
function activateBlocksMO() {
|
|
182
|
+
if (!blocksMO || blocksMO.active) {
|
|
180
183
|
return;
|
|
181
184
|
}
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
blocksMO.active = true;
|
|
186
|
+
blocksMO.observe(
|
|
184
187
|
document.body,
|
|
185
188
|
// eslint-disable-next-line object-curly-newline
|
|
186
189
|
{ subtree: true, attributes: true, attributeFilter: ['data-block-status'] },
|
|
187
190
|
);
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
// activate media mutation observer
|
|
194
|
+
function activateMediaMO() {
|
|
195
|
+
if (!mediaMO || mediaMO.active) {
|
|
192
196
|
return;
|
|
193
197
|
}
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
mediaMO.active = true;
|
|
199
|
+
mediaMO.observe(
|
|
196
200
|
document.body,
|
|
197
201
|
// eslint-disable-next-line object-curly-newline
|
|
198
202
|
{ subtree: true, attributes: false, childList: true },
|
|
@@ -204,16 +208,16 @@ function getIntersectionObsever(checkpoint) {
|
|
|
204
208
|
if (!window.IntersectionObserver) {
|
|
205
209
|
return null;
|
|
206
210
|
}
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
activateBlocksMO();
|
|
212
|
+
activateMediaMO();
|
|
209
213
|
const observer = new IntersectionObserver((entries) => {
|
|
210
214
|
try {
|
|
211
215
|
entries
|
|
212
|
-
.filter((
|
|
213
|
-
.forEach((
|
|
214
|
-
observer.unobserve(
|
|
215
|
-
const target = targetSelector(
|
|
216
|
-
const source = sourceSelector(
|
|
216
|
+
.filter((e) => e.isIntersecting)
|
|
217
|
+
.forEach((e) => {
|
|
218
|
+
observer.unobserve(e.target); // observe only once
|
|
219
|
+
const target = targetSelector(e.target);
|
|
220
|
+
const source = sourceSelector(e.target);
|
|
217
221
|
sampleRUM(checkpoint, { target, source });
|
|
218
222
|
});
|
|
219
223
|
/* c8 ignore next 3 */
|
|
@@ -245,11 +249,10 @@ function addViewMediaTracking(parent) {
|
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
function addFormTracking(parent) {
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
activateBlocksMO();
|
|
253
|
+
activateMediaMO();
|
|
250
254
|
parent.querySelectorAll('form').forEach((form) => {
|
|
251
|
-
form.
|
|
252
|
-
form.addEventListener('submit', formSubmitListener);
|
|
255
|
+
form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true });
|
|
253
256
|
});
|
|
254
257
|
}
|
|
255
258
|
|
|
@@ -257,7 +260,8 @@ function addObserver(ck, fn, block) {
|
|
|
257
260
|
return DEFAULT_TRACKING_EVENTS.includes(ck) && fn(block);
|
|
258
261
|
}
|
|
259
262
|
|
|
260
|
-
|
|
263
|
+
// blocks mutation observer callback
|
|
264
|
+
function blocksMCB(mutations) {
|
|
261
265
|
// block specific mutations
|
|
262
266
|
mutations
|
|
263
267
|
.filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status')
|
|
@@ -268,7 +272,8 @@ function blocksMutationsCallback(mutations) {
|
|
|
268
272
|
});
|
|
269
273
|
}
|
|
270
274
|
|
|
271
|
-
|
|
275
|
+
// media mutation observer callback
|
|
276
|
+
function mediaMCB(mutations) {
|
|
272
277
|
// media mutations
|
|
273
278
|
mutations
|
|
274
279
|
.forEach((m) => {
|
package/modules/martech.js
CHANGED
|
@@ -19,7 +19,7 @@ export function addCookieConsentTracking(sampleRUM) {
|
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
let
|
|
22
|
+
let consentMO; // consent mutation observer
|
|
23
23
|
const trackShowConsent = () => {
|
|
24
24
|
const otsdk = document.querySelector('body > div#onetrust-consent-sdk');
|
|
25
25
|
if (otsdk) {
|
|
@@ -28,8 +28,8 @@ export function addCookieConsentTracking(sampleRUM) {
|
|
|
28
28
|
} else {
|
|
29
29
|
sampleRUM('consent', { source: 'onetrust', target: 'show' });
|
|
30
30
|
}
|
|
31
|
-
if (
|
|
32
|
-
|
|
31
|
+
if (consentMO) {
|
|
32
|
+
consentMO.disconnect();
|
|
33
33
|
}
|
|
34
34
|
return true;
|
|
35
35
|
}
|
|
@@ -38,11 +38,11 @@ export function addCookieConsentTracking(sampleRUM) {
|
|
|
38
38
|
|
|
39
39
|
if (!trackShowConsent()) {
|
|
40
40
|
// eslint-disable-next-line max-len
|
|
41
|
-
|
|
41
|
+
consentMO = window.MutationObserver
|
|
42
42
|
? new MutationObserver(trackShowConsent)
|
|
43
43
|
: /* c8 ignore next */ null;
|
|
44
|
-
if (
|
|
45
|
-
|
|
44
|
+
if (consentMO) {
|
|
45
|
+
consentMO.observe(
|
|
46
46
|
document.body,
|
|
47
47
|
// eslint-disable-next-line object-curly-newline
|
|
48
48
|
{ attributes: false, childList: true, subtree: false },
|
package/modules/utils.js
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
const { href } = window.location;
|
|
14
|
+
|
|
13
15
|
export const urlSanitizers = {
|
|
14
16
|
/**
|
|
15
17
|
* Returns the full url.
|
|
@@ -17,21 +19,21 @@ export const urlSanitizers = {
|
|
|
17
19
|
* @param {string} url (default: window.location.href) The url to sanitize
|
|
18
20
|
* @returns {string} The sanitized url
|
|
19
21
|
*/
|
|
20
|
-
full: (url =
|
|
22
|
+
full: (url = href) => new URL(url).toString(),
|
|
21
23
|
/**
|
|
22
24
|
* Returns the origin of the provided url.
|
|
23
25
|
* If no url is provided, it defaults to window.location.href.
|
|
24
26
|
* @param {string} url (default: window.location.href) The url to sanitize
|
|
25
27
|
* @returns {string} The sanitized url
|
|
26
28
|
*/
|
|
27
|
-
origin: (url =
|
|
29
|
+
origin: (url = href) => new URL(url).origin,
|
|
28
30
|
/**
|
|
29
31
|
* Returns the sanitized url: the origin and the path (no query params or hash)
|
|
30
32
|
* If no url is provided, it defaults to window.location.href.
|
|
31
33
|
* @param {string} url (default: window.location.href) The url to sanitize
|
|
32
34
|
* @returns {string} The sanitized url
|
|
33
35
|
*/
|
|
34
|
-
path: (url =
|
|
36
|
+
path: (url = href) => {
|
|
35
37
|
const u = new URL(url);
|
|
36
38
|
return `${u.origin}${u.pathname}`;
|
|
37
39
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-rum-enhancer",
|
|
3
|
-
"version": "2.26.0
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Helix RUM Enhancer",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build-bundle": "rollup --config",
|
|
15
15
|
"prepack": "npm run build-bundle",
|
|
16
16
|
"semantic-release": "semantic-release",
|
|
17
|
-
"semantic-release-dry": "semantic-release --dry-run --branches $CI_BRANCH 1.x main
|
|
17
|
+
"semantic-release-dry": "semantic-release --dry-run --branches $CI_BRANCH 1.x main",
|
|
18
18
|
"prepare": "husky"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
package/src/index.js
CHANGED
|
@@ -29,96 +29,97 @@
|
|
|
29
29
|
const DEFAULT_TRACKING_EVENTS = ['click', 'cwv', 'form', 'viewblock', 'viewmedia', 'loadresource', 'utm', 'paid', 'email', 'consent'];
|
|
30
30
|
fflags.enabled('example', () => DEFAULT_TRACKING_EVENTS.push('example'));
|
|
31
31
|
|
|
32
|
+
const { href } = window.location;
|
|
32
33
|
const urlSanitizers = {
|
|
33
|
-
full: (url =
|
|
34
|
-
origin: (url =
|
|
35
|
-
path: (url =
|
|
34
|
+
full: (url = href) => new URL(url).toString(),
|
|
35
|
+
origin: (url = href) => new URL(url).origin,
|
|
36
|
+
path: (url = href) => {
|
|
36
37
|
const u = new URL(url);
|
|
37
38
|
return `${u.origin}${u.pathname}`;
|
|
38
39
|
},
|
|
39
40
|
};
|
|
40
41
|
|
|
41
|
-
const getTargetValue = (
|
|
42
|
-
||
|
|
43
|
-
const targetSelector = (
|
|
42
|
+
const getTargetValue = (el) => el.getAttribute('data-rum-target') || el.getAttribute('href')
|
|
43
|
+
|| el.currentSrc || el.getAttribute('src') || el.dataset.action || el.action;
|
|
44
|
+
const targetSelector = (el) => {
|
|
44
45
|
try {
|
|
45
|
-
if (!
|
|
46
|
-
let
|
|
47
|
-
if (!
|
|
48
|
-
|
|
46
|
+
if (!el) return undefined;
|
|
47
|
+
let v = getTargetValue(el);
|
|
48
|
+
if (!v && el.tagName !== 'A' && el.closest('a')) {
|
|
49
|
+
v = getTargetValue(el.closest('a'));
|
|
49
50
|
}
|
|
50
|
-
if (
|
|
51
|
-
|
|
51
|
+
if (v && !v.startsWith('https://')) {
|
|
52
|
+
v = new URL(v, window.location).href;
|
|
52
53
|
}
|
|
53
|
-
return
|
|
54
|
+
return v;
|
|
54
55
|
} catch (error) {
|
|
55
56
|
return null;
|
|
56
57
|
}
|
|
57
58
|
};
|
|
58
|
-
function walk(
|
|
59
|
-
if (!
|
|
59
|
+
function walk(el, checkFn) {
|
|
60
|
+
if (!el || el === document.body || el === document.documentElement) {
|
|
60
61
|
return undefined;
|
|
61
62
|
}
|
|
62
|
-
|
|
63
|
-
return checkValue || walk(element.parentElement, checkFn);
|
|
63
|
+
return checkFn(el) || walk(el.parentElement, checkFn);
|
|
64
64
|
}
|
|
65
|
-
function isDialog(
|
|
66
|
-
if (
|
|
67
|
-
if (
|
|
68
|
-
if (
|
|
69
|
-
if (
|
|
70
|
-
const
|
|
71
|
-
return (
|
|
65
|
+
function isDialog(el) {
|
|
66
|
+
if (el.tagName === 'DIALOG') return true;
|
|
67
|
+
if (el.getAttribute('role') === 'dialog') return true;
|
|
68
|
+
if (el.getAttribute('role') === 'alertdialog') return true;
|
|
69
|
+
if (el.getAttribute('aria-modal') === 'true') return true;
|
|
70
|
+
const cs = window.getComputedStyle(el);
|
|
71
|
+
return (cs && cs.position === 'fixed' && cs.zIndex > 100);
|
|
72
72
|
}
|
|
73
|
-
function isButton(
|
|
74
|
-
if (
|
|
75
|
-
if (
|
|
76
|
-
if (
|
|
77
|
-
const classes = Array.from(
|
|
73
|
+
function isButton(el) {
|
|
74
|
+
if (el.tagName === 'BUTTON') return true;
|
|
75
|
+
if (el.tagName === 'INPUT' && el.getAttribute('type') === 'button') return true;
|
|
76
|
+
if (el.tagName === 'A') {
|
|
77
|
+
const classes = Array.from(el.classList);
|
|
78
78
|
return classes.some((className) => className.match(/button|cta/));
|
|
79
79
|
}
|
|
80
|
-
return
|
|
80
|
+
return el.getAttribute('role') === 'button';
|
|
81
81
|
}
|
|
82
|
-
function getSourceContext(
|
|
83
|
-
if (
|
|
84
|
-
return `form${
|
|
82
|
+
function getSourceContext(el) {
|
|
83
|
+
if (el.closest('form')) {
|
|
84
|
+
return `form${el.id ? `#${el.id}` : ''}`;
|
|
85
85
|
}
|
|
86
|
-
const block =
|
|
86
|
+
const block = el.closest('.block[data-block-name]');
|
|
87
87
|
if (block) return `.${block.getAttribute('data-block-name')}`;
|
|
88
|
-
if (walk(
|
|
89
|
-
if (
|
|
90
|
-
if (
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
93
|
-
return (walk(
|
|
88
|
+
if (walk(el, isDialog)) return 'dialog';
|
|
89
|
+
if (el.closest('nav')) return 'nav';
|
|
90
|
+
if (el.closest('header')) return 'header';
|
|
91
|
+
if (el.closest('footer')) return 'footer';
|
|
92
|
+
if (el.closest('aside')) return 'aside';
|
|
93
|
+
return (walk(el, (e) => e.id && `#${e.id}`));
|
|
94
94
|
}
|
|
95
|
-
function getSourceElement(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
function getSourceElement(el) {
|
|
96
|
+
const f = el.closest('form');
|
|
97
|
+
if (f && Array.from(f.elements).includes(el)) {
|
|
98
|
+
return (el.tagName.toLowerCase()
|
|
99
|
+
+ (['INPUT', 'BUTTON'].includes(el.tagName)
|
|
100
|
+
? `[type='${el.getAttribute('type') || ''}']`
|
|
100
101
|
: ''));
|
|
101
102
|
}
|
|
102
|
-
if (walk(
|
|
103
|
-
return
|
|
103
|
+
if (walk(el, isButton)) return 'button';
|
|
104
|
+
return el.tagName.toLowerCase().match(/^(a|img|video)$/) && el.tagName.toLowerCase();
|
|
104
105
|
}
|
|
105
|
-
function getSourceIdentifier(
|
|
106
|
-
if (
|
|
107
|
-
if (
|
|
108
|
-
return (
|
|
106
|
+
function getSourceIdentifier(el) {
|
|
107
|
+
if (el.id) return `#${el.id}`;
|
|
108
|
+
if (el.getAttribute('data-block-name')) return `.${el.getAttribute('data-block-name')}`;
|
|
109
|
+
return (el.classList.length > 0 && `.${el.classList[0]}`);
|
|
109
110
|
}
|
|
110
|
-
const sourceSelector = (
|
|
111
|
+
const sourceSelector = (el) => {
|
|
111
112
|
try {
|
|
112
|
-
if (!
|
|
113
|
+
if (!el || el === document.body || el === document.documentElement) {
|
|
113
114
|
return undefined;
|
|
114
115
|
}
|
|
115
|
-
if (
|
|
116
|
-
return
|
|
116
|
+
if (el.getAttribute('data-rum-source')) {
|
|
117
|
+
return el.getAttribute('data-rum-source');
|
|
117
118
|
}
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
return `${
|
|
119
|
+
const ctx = getSourceContext(el.parentElement) || '';
|
|
120
|
+
const name = getSourceElement(el) || '';
|
|
121
|
+
const id = getSourceIdentifier(el) || '';
|
|
122
|
+
return `${ctx} ${name}${id}`.trim() || `"${el.textContent.substring(0, 10)}"`;
|
|
122
123
|
} catch (error) {
|
|
123
124
|
return null;
|
|
124
125
|
}
|
|
@@ -132,7 +133,7 @@
|
|
|
132
133
|
sampleRUM('consent', { source: 'onetrust', target: 'hidden' });
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
|
-
let
|
|
136
|
+
let consentMO;
|
|
136
137
|
const trackShowConsent = () => {
|
|
137
138
|
const otsdk = document.querySelector('body > div#onetrust-consent-sdk');
|
|
138
139
|
if (otsdk) {
|
|
@@ -141,19 +142,19 @@
|
|
|
141
142
|
} else {
|
|
142
143
|
sampleRUM('consent', { source: 'onetrust', target: 'show' });
|
|
143
144
|
}
|
|
144
|
-
if (
|
|
145
|
-
|
|
145
|
+
if (consentMO) {
|
|
146
|
+
consentMO.disconnect();
|
|
146
147
|
}
|
|
147
148
|
return true;
|
|
148
149
|
}
|
|
149
150
|
return false;
|
|
150
151
|
};
|
|
151
152
|
if (!trackShowConsent()) {
|
|
152
|
-
|
|
153
|
+
consentMO = window.MutationObserver
|
|
153
154
|
? new MutationObserver(trackShowConsent)
|
|
154
155
|
: null;
|
|
155
|
-
if (
|
|
156
|
-
|
|
156
|
+
if (consentMO) {
|
|
157
|
+
consentMO.observe(
|
|
157
158
|
document.body,
|
|
158
159
|
{ attributes: false, childList: true, subtree: false },
|
|
159
160
|
);
|
|
@@ -197,10 +198,9 @@
|
|
|
197
198
|
|
|
198
199
|
const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum
|
|
199
200
|
: {};
|
|
200
|
-
const
|
|
201
|
-
const blocksMutationObserver = window.MutationObserver ? new MutationObserver(blocksMutationsCallback)
|
|
201
|
+
const blocksMO = window.MutationObserver ? new MutationObserver(blocksMCB)
|
|
202
202
|
: {};
|
|
203
|
-
const
|
|
203
|
+
const mediaMO = window.MutationObserver ? new MutationObserver(mediaMCB)
|
|
204
204
|
: {};
|
|
205
205
|
function trackCheckpoint(checkpoint, data, t) {
|
|
206
206
|
const { weight, id } = window.hlx.rum;
|
|
@@ -293,10 +293,10 @@
|
|
|
293
293
|
});
|
|
294
294
|
};
|
|
295
295
|
new PerformanceObserver((list) => list
|
|
296
|
-
.getEntries().map((
|
|
296
|
+
.getEntries().map((e) => navigate(
|
|
297
297
|
window.hlx.referrer || document.referrer,
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
e.type,
|
|
299
|
+
e.redirectCount,
|
|
300
300
|
)))
|
|
301
301
|
.observe({ type: 'navigation', buffered: true });
|
|
302
302
|
}
|
|
@@ -304,38 +304,38 @@
|
|
|
304
304
|
const observer = new PerformanceObserver((list) => {
|
|
305
305
|
try {
|
|
306
306
|
list.getEntries()
|
|
307
|
-
.filter((
|
|
308
|
-
.filter((
|
|
309
|
-
.filter((
|
|
310
|
-
.forEach((
|
|
311
|
-
sampleRUM('loadresource', { source:
|
|
307
|
+
.filter((e) => !e.responseStatus || e.responseStatus < 400)
|
|
308
|
+
.filter((e) => window.location.hostname === new URL(e.name).hostname)
|
|
309
|
+
.filter((e) => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
|
|
310
|
+
.forEach((e) => {
|
|
311
|
+
sampleRUM('loadresource', { source: e.name, target: Math.round(e.duration) });
|
|
312
312
|
});
|
|
313
313
|
list.getEntries()
|
|
314
|
-
.filter((
|
|
315
|
-
.forEach((
|
|
316
|
-
sampleRUM('missingresource', { source:
|
|
314
|
+
.filter((e) => e.responseStatus === 404)
|
|
315
|
+
.forEach((e) => {
|
|
316
|
+
sampleRUM('missingresource', { source: e.name, target: e.hostname });
|
|
317
317
|
});
|
|
318
318
|
} catch (error) {
|
|
319
319
|
}
|
|
320
320
|
});
|
|
321
321
|
observer.observe({ type: 'resource', buffered: true });
|
|
322
322
|
}
|
|
323
|
-
function
|
|
324
|
-
if (!
|
|
323
|
+
function activateBlocksMO() {
|
|
324
|
+
if (!blocksMO || blocksMO.active) {
|
|
325
325
|
return;
|
|
326
326
|
}
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
blocksMO.active = true;
|
|
328
|
+
blocksMO.observe(
|
|
329
329
|
document.body,
|
|
330
330
|
{ subtree: true, attributes: true, attributeFilter: ['data-block-status'] },
|
|
331
331
|
);
|
|
332
332
|
}
|
|
333
|
-
function
|
|
334
|
-
if (!
|
|
333
|
+
function activateMediaMO() {
|
|
334
|
+
if (!mediaMO || mediaMO.active) {
|
|
335
335
|
return;
|
|
336
336
|
}
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
mediaMO.active = true;
|
|
338
|
+
mediaMO.observe(
|
|
339
339
|
document.body,
|
|
340
340
|
{ subtree: true, attributes: false, childList: true },
|
|
341
341
|
);
|
|
@@ -344,16 +344,16 @@
|
|
|
344
344
|
if (!window.IntersectionObserver) {
|
|
345
345
|
return null;
|
|
346
346
|
}
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
activateBlocksMO();
|
|
348
|
+
activateMediaMO();
|
|
349
349
|
const observer = new IntersectionObserver((entries) => {
|
|
350
350
|
try {
|
|
351
351
|
entries
|
|
352
|
-
.filter((
|
|
353
|
-
.forEach((
|
|
354
|
-
observer.unobserve(
|
|
355
|
-
const target = targetSelector(
|
|
356
|
-
const source = sourceSelector(
|
|
352
|
+
.filter((e) => e.isIntersecting)
|
|
353
|
+
.forEach((e) => {
|
|
354
|
+
observer.unobserve(e.target);
|
|
355
|
+
const target = targetSelector(e.target);
|
|
356
|
+
const source = sourceSelector(e.target);
|
|
357
357
|
sampleRUM(checkpoint, { target, source });
|
|
358
358
|
});
|
|
359
359
|
} catch (error) {
|
|
@@ -381,17 +381,16 @@
|
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
383
|
function addFormTracking(parent) {
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
activateBlocksMO();
|
|
385
|
+
activateMediaMO();
|
|
386
386
|
parent.querySelectorAll('form').forEach((form) => {
|
|
387
|
-
form.
|
|
388
|
-
form.addEventListener('submit', formSubmitListener);
|
|
387
|
+
form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true });
|
|
389
388
|
});
|
|
390
389
|
}
|
|
391
390
|
function addObserver(ck, fn, block) {
|
|
392
391
|
return DEFAULT_TRACKING_EVENTS.includes(ck) && fn(block);
|
|
393
392
|
}
|
|
394
|
-
function
|
|
393
|
+
function blocksMCB(mutations) {
|
|
395
394
|
mutations
|
|
396
395
|
.filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status')
|
|
397
396
|
.filter((m) => m.target.dataset.blockStatus === 'loaded')
|
|
@@ -400,7 +399,7 @@
|
|
|
400
399
|
addObserver('viewblock', addViewBlockTracking, m.target);
|
|
401
400
|
});
|
|
402
401
|
}
|
|
403
|
-
function
|
|
402
|
+
function mediaMCB(mutations) {
|
|
404
403
|
mutations
|
|
405
404
|
.forEach((m) => {
|
|
406
405
|
addObserver('viewmedia', addViewMediaTracking, m.target);
|
package/src/index.md5
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
8094422990fd7a9219bffb7309cb92a2
|