@adobe/helix-rum-enhancer 2.36.4 → 2.37.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 +20 -0
- package/eslint.config.js +34 -0
- package/modules/fflags.js +1 -0
- package/modules/index.js +20 -22
- package/package.json +14 -12
- package/plugins/a11y.js +232 -0
- package/src/index.js +31 -32
- package/src/index.md5 +1 -1
- package/src/index.sri +1 -1
- package/src/plugins/a11y.js +181 -0
- package/src/plugins/a11y.map.js +182 -0
- package/src/plugins/a11y.md5 +1 -0
- package/src/plugins/cwv.js +3 -1
- package/src/plugins/cwv.map.js +4 -2
- package/src/plugins/cwv.md5 +1 -1
- package/src/plugins/form.map.js +1 -1
- package/src/plugins/martech.map.js +1 -1
- package/src/plugins/martech.md5 +1 -1
- package/src/plugins/onetrust.map.js +1 -1
- package/src/plugins/video.map.js +1 -1
- package/src/plugins/video.md5 +1 -1
- package/src/plugins/webcomponent.map.js +1 -1
- package/src/plugins/webcomponent.md5 +1 -1
- package/.eslintrc.cjs +0 -32
package/.releaserc.cjs
CHANGED
|
@@ -27,7 +27,7 @@ module.exports = {
|
|
|
27
27
|
]
|
|
28
28
|
}],
|
|
29
29
|
["@semantic-release/exec", {
|
|
30
|
-
"publishCmd": "./tagger.sh ${nextRelease.version} src/index.js src/plugins/cwv.js src/plugins/form.js src/plugins/martech.js src/plugins/onetrust.js src/plugins/video.js --push"
|
|
30
|
+
"publishCmd": "./tagger.sh ${nextRelease.version} src/index.js src/plugins/cwv.js src/plugins/form.js src/plugins/martech.js src/plugins/onetrust.js src/plugins/video.js src/plugins/webcomponent.js --push"
|
|
31
31
|
}],
|
|
32
32
|
["semantic-release-slack-bot", {
|
|
33
33
|
notifyOnSuccess: true,
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
# [2.37.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.36.5...v2.37.0) (2025-07-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* audience reporting ([9f02657](https://github.com/adobe/helix-rum-enhancer/commit/9f0265703aba8ce01fa2850dadc36f993e9e2496))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **a11y:** add a new accessibility plugin to categorize audience and detect common bugs ([033b425](https://github.com/adobe/helix-rum-enhancer/commit/033b4250ddfd3a3a8887b6fbebd18b92543c70a5))
|
|
12
|
+
* **a11y:** add a new accessibility plugin to flag accessible audience and detect common bugs ([5a39aff](https://github.com/adobe/helix-rum-enhancer/commit/5a39aff315213187ce2b35928f72ad44fcd0e869))
|
|
13
|
+
|
|
14
|
+
## [2.36.5](https://github.com/adobe/helix-rum-enhancer/compare/v2.36.4...v2.36.5) (2025-06-20)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **release:** Add webcomponent.js to release tagging ([0953552](https://github.com/adobe/helix-rum-enhancer/commit/0953552f424fba66f1e26b2bd32bebeff86015fe))
|
|
20
|
+
|
|
1
21
|
## [2.36.4](https://github.com/adobe/helix-rum-enhancer/compare/v2.36.3...v2.36.4) (2025-06-18)
|
|
2
22
|
|
|
3
23
|
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from '@eslint/config-helpers'
|
|
2
|
+
import { recommended, source, test } from '@adobe/eslint-config-helix';
|
|
3
|
+
import globals from "globals";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
globalIgnores([
|
|
7
|
+
'.vscode/*',
|
|
8
|
+
'coverage/*',
|
|
9
|
+
'src/*',
|
|
10
|
+
]), {
|
|
11
|
+
languageOptions: {
|
|
12
|
+
globals: {
|
|
13
|
+
...globals.browser,
|
|
14
|
+
...globals.mocha,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
rules: {
|
|
18
|
+
'import/no-extraneous-dependencies': ['error', {
|
|
19
|
+
devDependencies: [
|
|
20
|
+
'**/test/**',
|
|
21
|
+
'rollup.config.js',
|
|
22
|
+
'web-test-runner.config.js',
|
|
23
|
+
'eslint.config.js',
|
|
24
|
+
],
|
|
25
|
+
}],
|
|
26
|
+
},
|
|
27
|
+
plugins: {
|
|
28
|
+
import: recommended.plugins.import,
|
|
29
|
+
},
|
|
30
|
+
extends: [recommended],
|
|
31
|
+
},
|
|
32
|
+
source,
|
|
33
|
+
test,
|
|
34
|
+
]);
|
package/modules/fflags.js
CHANGED
package/modules/index.js
CHANGED
|
@@ -34,6 +34,7 @@ const pluginBasePath = new URL(document.currentScript.src).href.replace(/index(\
|
|
|
34
34
|
|
|
35
35
|
const PLUGINS = {
|
|
36
36
|
cwv: `${pluginBasePath}/cwv.js`,
|
|
37
|
+
a11y: `${pluginBasePath}/a11y.js`,
|
|
37
38
|
// Interactive elements
|
|
38
39
|
form: { url: `${pluginBasePath}/form.js`, when: () => document.querySelector('form'), isBlockDependent: true },
|
|
39
40
|
video: { url: `${pluginBasePath}/video.js`, when: () => document.querySelector('video'), isBlockDependent: true },
|
|
@@ -106,8 +107,7 @@ function loadPlugin(key, params) {
|
|
|
106
107
|
pluginCache.set(key, import(`${plugin.url || plugin}`));
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
return pluginLoadPromise
|
|
110
|
+
return pluginCache.get(key)
|
|
111
111
|
.then((p) => (p.default && p.default(params)) || (typeof p === 'function' && p(params)))
|
|
112
112
|
.catch(() => { /* silent plugin error catching */ });
|
|
113
113
|
}
|
|
@@ -147,22 +147,19 @@ function trackCheckpoint(checkpoint, data, t) {
|
|
|
147
147
|
const { weight, id } = window.hlx.rum;
|
|
148
148
|
if (isSelected && maxEvents) {
|
|
149
149
|
maxEvents -= 1;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
console.debug(`ping:${checkpoint}`, pdata);
|
|
164
|
-
};
|
|
165
|
-
sendPing(data);
|
|
150
|
+
// eslint-disable-next-line object-curly-newline, max-len
|
|
151
|
+
const body = JSON.stringify({ weight, id, referer: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'](), checkpoint, t, ...data }, KNOWN_PROPERTIES);
|
|
152
|
+
const urlParams = window.RUM_PARAMS ? `?${new URLSearchParams(window.RUM_PARAMS).toString()}` : '';
|
|
153
|
+
const { href: url, origin } = new URL(`.rum/${weight}${urlParams}`, sampleRUM.collectBaseURL);
|
|
154
|
+
if (window.location.origin === origin) {
|
|
155
|
+
const headers = { type: 'application/json' };
|
|
156
|
+
navigator.sendBeacon(url, new Blob([body], headers));
|
|
157
|
+
/* c8 ignore next 3 */
|
|
158
|
+
} else {
|
|
159
|
+
navigator.sendBeacon(url, body);
|
|
160
|
+
}
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.debug(`ping:${checkpoint}`, data);
|
|
166
163
|
}
|
|
167
164
|
}
|
|
168
165
|
|
|
@@ -223,14 +220,15 @@ function addNavigationTracking() {
|
|
|
223
220
|
function addLoadResourceTracking() {
|
|
224
221
|
const observer = new PerformanceObserver((list) => {
|
|
225
222
|
try {
|
|
226
|
-
list.getEntries()
|
|
223
|
+
const entries = list.getEntries();
|
|
224
|
+
entries
|
|
227
225
|
.filter((e) => !e.responseStatus || e.responseStatus < 400)
|
|
228
226
|
.filter((e) => window.location.hostname === new URL(e.name).hostname || fflags.has('allresources'))
|
|
229
227
|
.filter((e) => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
|
|
230
228
|
.forEach((e) => {
|
|
231
229
|
sampleRUM('loadresource', { source: e.name, target: Math.round(e.duration) });
|
|
232
230
|
});
|
|
233
|
-
|
|
231
|
+
entries
|
|
234
232
|
.filter((e) => e.responseStatus >= 400)
|
|
235
233
|
.filter((e) => !(new URL(e.name).pathname.match('.*(/\\.rum/1[0-9]{0,3})')))
|
|
236
234
|
.forEach((e) => {
|
|
@@ -343,7 +341,7 @@ function addTrackingFromConfig() {
|
|
|
343
341
|
});
|
|
344
342
|
}
|
|
345
343
|
|
|
346
|
-
function
|
|
344
|
+
function init() {
|
|
347
345
|
try {
|
|
348
346
|
if (sampleRUM) {
|
|
349
347
|
addTrackingFromConfig();
|
|
@@ -356,4 +354,4 @@ function initEnhancer() {
|
|
|
356
354
|
}
|
|
357
355
|
}
|
|
358
356
|
|
|
359
|
-
|
|
357
|
+
init();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-rum-enhancer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.37.0",
|
|
4
4
|
"description": "Helix RUM Enhancer",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -33,36 +33,38 @@
|
|
|
33
33
|
"reporter-options": "configFile=.mocha-multi.json"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@adobe/eslint-config-helix": "
|
|
36
|
+
"@adobe/eslint-config-helix": "3.0.7",
|
|
37
37
|
"@adobe/helix-rum-js": "2.12.1",
|
|
38
|
+
"@adobe/rollup-plugin-checksum": "1.2.0",
|
|
39
|
+
"@eslint/config-helpers": "0.3.0",
|
|
38
40
|
"@esm-bundle/chai": "4.3.4-fix.0",
|
|
39
41
|
"@rollup/plugin-babel": "6.0.4",
|
|
40
42
|
"@semantic-release/changelog": "6.0.3",
|
|
41
43
|
"@semantic-release/exec": "7.1.0",
|
|
42
44
|
"@semantic-release/git": "10.0.1",
|
|
43
|
-
"@semantic-release/npm": "12.0.
|
|
45
|
+
"@semantic-release/npm": "12.0.2",
|
|
44
46
|
"@web/test-runner": "0.20.2",
|
|
45
47
|
"@web/test-runner-commands": "0.9.0",
|
|
46
48
|
"@web/test-runner-junit-reporter": "0.8.0",
|
|
47
49
|
"@web/test-runner-mocha": "0.9.0",
|
|
48
|
-
"@web/test-runner-playwright": "0.11.
|
|
50
|
+
"@web/test-runner-playwright": "0.11.1",
|
|
49
51
|
"c8": "10.1.3",
|
|
50
52
|
"codecov": "3.8.3",
|
|
51
|
-
"eslint": "
|
|
53
|
+
"eslint": "9.30.1",
|
|
52
54
|
"eslint-plugin-header": "3.1.1",
|
|
53
|
-
"eslint-plugin-import": "2.
|
|
55
|
+
"eslint-plugin-import": "2.32.0",
|
|
56
|
+
"globals": "16.3.0",
|
|
54
57
|
"husky": "9.1.7",
|
|
55
58
|
"jsdoc-to-markdown": "9.1.1",
|
|
56
59
|
"junit-report-builder": "5.1.1",
|
|
57
|
-
"lint-staged": "16.1.
|
|
58
|
-
"mocha": "11.
|
|
60
|
+
"lint-staged": "16.1.2",
|
|
61
|
+
"mocha": "11.7.1",
|
|
59
62
|
"mocha-multi-reporters": "1.5.1",
|
|
60
|
-
"rollup": "4.
|
|
61
|
-
"@adobe/rollup-plugin-checksum": "1.2.0",
|
|
63
|
+
"rollup": "4.44.2",
|
|
62
64
|
"rollup-plugin-eslint-bundle": "9.0.0",
|
|
63
|
-
"semantic-release": "24.2.
|
|
65
|
+
"semantic-release": "24.2.6",
|
|
64
66
|
"semantic-release-slack-bot": "^4.0.2",
|
|
65
|
-
"web-vitals": "5.0.
|
|
67
|
+
"web-vitals": "5.0.3"
|
|
66
68
|
},
|
|
67
69
|
"lint-staged": {
|
|
68
70
|
"*.js": "eslint",
|
package/plugins/a11y.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const MEDIA_QUERY_PREFERENCES = [
|
|
14
|
+
'(prefers-reduced-motion: reduce)',
|
|
15
|
+
'(prefers-contrast: high)',
|
|
16
|
+
'(prefers-contrast: more)',
|
|
17
|
+
'(forced-colors: active)',
|
|
18
|
+
'(prefers-color-scheme: dark)',
|
|
19
|
+
'(prefers-reduced-transparency: reduce)',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const KEYBOARD_RELEVANT_KEYS = [
|
|
23
|
+
'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', ' ', 'Escape',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const HIGH_ZOOM_LEVEL = 200;
|
|
27
|
+
const MEDIUM_ZOOM_LEVEL = 125;
|
|
28
|
+
|
|
29
|
+
const BEHAVIORAL_MIN_EVENTS = 10;
|
|
30
|
+
|
|
31
|
+
const FOCUSTRAP_MAX_HISTORY = 25;
|
|
32
|
+
const FOCUS_TRAP_MIN_HISTORY = 4;
|
|
33
|
+
const FOCUS_TRAP_PING_PONG_DURATION = 3000;
|
|
34
|
+
const FOCUS_TRAP_MIN_REPEATS = 3;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Detects approximate zoom level using the Visual Viewport API.
|
|
38
|
+
* This is a pure utility function.
|
|
39
|
+
* @returns {Number} Estimated zoom percentage.
|
|
40
|
+
*/
|
|
41
|
+
function getZoom() {
|
|
42
|
+
if (window.visualViewport && window.visualViewport.width > 0) {
|
|
43
|
+
return Math.round((window.innerWidth / window.visualViewport.width) * 100);
|
|
44
|
+
}
|
|
45
|
+
/* c8 ignore next 1 */
|
|
46
|
+
return 100;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Minimal accessibility audience detection plugin for helix-rum-enhancer.
|
|
51
|
+
* @param {Object} params Plugin parameters from enhancer.
|
|
52
|
+
* @param {Function} params.sampleRUM RUM tracking function.
|
|
53
|
+
* @param {Function} params.sourceSelector Function to generate a selector for an element.
|
|
54
|
+
*/
|
|
55
|
+
export default function addAccessibilityAudienceTracking({ sampleRUM, sourceSelector }) {
|
|
56
|
+
const focusHistory = [];
|
|
57
|
+
let score = 0;
|
|
58
|
+
let reported = false;
|
|
59
|
+
let focusTrapDetected = false;
|
|
60
|
+
let keyboardCount = 0;
|
|
61
|
+
let totalCount = 0;
|
|
62
|
+
let lastFocusTime = 0;
|
|
63
|
+
let hadMeaningfulFocus = false;
|
|
64
|
+
|
|
65
|
+
const reportAudience = () => {
|
|
66
|
+
/* c8 ignore next 3 */
|
|
67
|
+
if (reported) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
reported = true;
|
|
71
|
+
const audience = score >= 1 ? 'on' : 'off';
|
|
72
|
+
sampleRUM('a11y', { source: audience, target: 'on:off' });
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const reportFocusTrap = (trapType, elements) => {
|
|
76
|
+
/* c8 ignore next 3 */
|
|
77
|
+
if (focusTrapDetected) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
focusTrapDetected = true;
|
|
81
|
+
const elementDescriptions = elements.slice(0, 3).map(sourceSelector);
|
|
82
|
+
sampleRUM('error', {
|
|
83
|
+
source: `focus-trap:${trapType}`,
|
|
84
|
+
target: elementDescriptions.join(', '),
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const detectRepeatingSequence = (elements, seqLength) => {
|
|
89
|
+
const totalLength = seqLength * FOCUS_TRAP_MIN_REPEATS;
|
|
90
|
+
/* c8 ignore next 3 */
|
|
91
|
+
if (elements.length < totalLength) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const recent = elements.slice(-totalLength);
|
|
95
|
+
const baseSequence = recent.slice(0, seqLength);
|
|
96
|
+
for (let i = 1; i < FOCUS_TRAP_MIN_REPEATS; i += 1) {
|
|
97
|
+
const nextSequence = recent.slice(i * seqLength, (i + 1) * seqLength);
|
|
98
|
+
if (nextSequence.length !== seqLength
|
|
99
|
+
|| !nextSequence.every((el, j) => el === baseSequence[j])) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
reportFocusTrap('sequence', baseSequence);
|
|
104
|
+
return true;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const detectFocusTrap = () => {
|
|
108
|
+
if (focusHistory.length < FOCUS_TRAP_MIN_HISTORY || focusTrapDetected) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const elements = focusHistory.map((h) => h.element);
|
|
112
|
+
const timestamps = focusHistory.map((h) => h.timestamp);
|
|
113
|
+
|
|
114
|
+
if (elements.slice(-3).every((el) => el === elements[elements.length - 1])) {
|
|
115
|
+
reportFocusTrap('single', [elements[elements.length - 1]]);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const recent4 = elements.slice(-4);
|
|
120
|
+
const unique = [...new Set(recent4)];
|
|
121
|
+
const timeDiff = timestamps[timestamps.length - 1] - timestamps[timestamps.length - 4];
|
|
122
|
+
if (unique.length === 2 && timeDiff < FOCUS_TRAP_PING_PONG_DURATION) {
|
|
123
|
+
reportFocusTrap('ping-pong', unique);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const minSeqLength = 2 * FOCUS_TRAP_MIN_HISTORY + 1;
|
|
128
|
+
if (focusHistory.length >= minSeqLength) {
|
|
129
|
+
for (let seqLength = 2; seqLength <= 4; seqLength += 1) {
|
|
130
|
+
if (detectRepeatingSequence(elements, seqLength)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const trackFocusForTrap = (event) => {
|
|
138
|
+
setTimeout(() => {
|
|
139
|
+
const { activeElement } = document;
|
|
140
|
+
/* c8 ignore next 3 */
|
|
141
|
+
if (!activeElement || activeElement === document.body) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
focusHistory.push({
|
|
145
|
+
element: activeElement,
|
|
146
|
+
timestamp: performance.now(),
|
|
147
|
+
shiftKey: event.shiftKey,
|
|
148
|
+
});
|
|
149
|
+
/* c8 ignore next 3 */
|
|
150
|
+
if (focusHistory.length > FOCUSTRAP_MAX_HISTORY) {
|
|
151
|
+
focusHistory.shift();
|
|
152
|
+
}
|
|
153
|
+
detectFocusTrap();
|
|
154
|
+
}, 0);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const checkBehavioralScore = () => {
|
|
158
|
+
if (totalCount >= BEHAVIORAL_MIN_EVENTS && !reported) {
|
|
159
|
+
if (keyboardCount / totalCount > 0.7) {
|
|
160
|
+
score += 2;
|
|
161
|
+
}
|
|
162
|
+
reportAudience();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// --- Static Scoring ---
|
|
167
|
+
score += MEDIA_QUERY_PREFERENCES.filter((q) => window.matchMedia(q).matches).length;
|
|
168
|
+
|
|
169
|
+
const zoom = getZoom();
|
|
170
|
+
if (zoom >= HIGH_ZOOM_LEVEL) {
|
|
171
|
+
score += 3;
|
|
172
|
+
} else if (zoom >= MEDIUM_ZOOM_LEVEL) {
|
|
173
|
+
score += 2;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (navigator.maxTouchPoints === 0 && !window.matchMedia('(pointer: coarse)').matches) {
|
|
177
|
+
score += 2;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// --- Event Listeners & Observers ---
|
|
181
|
+
document.addEventListener('keydown', (event) => {
|
|
182
|
+
if (KEYBOARD_RELEVANT_KEYS.includes(event.key)) {
|
|
183
|
+
keyboardCount += 1;
|
|
184
|
+
totalCount += 1;
|
|
185
|
+
if (event.key === 'Tab') {
|
|
186
|
+
trackFocusForTrap(event);
|
|
187
|
+
}
|
|
188
|
+
checkBehavioralScore();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
document.addEventListener('click', () => {
|
|
193
|
+
totalCount += 1;
|
|
194
|
+
checkBehavioralScore();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
document.addEventListener('focusin', (event) => {
|
|
198
|
+
if (event.target !== document.body) {
|
|
199
|
+
lastFocusTime = performance.now();
|
|
200
|
+
hadMeaningfulFocus = true;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const focusLossObserver = new MutationObserver((mutations) => {
|
|
205
|
+
if (hadMeaningfulFocus
|
|
206
|
+
&& document.activeElement === document.body
|
|
207
|
+
&& (performance.now() - lastFocusTime > 100)) {
|
|
208
|
+
/* c8 ignore next 9 */
|
|
209
|
+
const mutation = mutations.find((m) => (
|
|
210
|
+
m.type === 'childList' && (m.addedNodes.length > 0 || m.removedNodes.length > 0)
|
|
211
|
+
));
|
|
212
|
+
sampleRUM('error', {
|
|
213
|
+
source: 'focus-loss',
|
|
214
|
+
target: sourceSelector(mutation?.target),
|
|
215
|
+
});
|
|
216
|
+
hadMeaningfulFocus = false; // Prevent spam
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
focusLossObserver.observe(document, { childList: true, subtree: true });
|
|
221
|
+
|
|
222
|
+
// --- Final Reporting ---
|
|
223
|
+
if (score >= 1) {
|
|
224
|
+
reportAudience();
|
|
225
|
+
} else {
|
|
226
|
+
setTimeout(
|
|
227
|
+
() => reportAudience(),
|
|
228
|
+
/* c8 ignore next 1 */
|
|
229
|
+
window.hlx.A11Y_REPORT_DELAY || 10_000,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
package/src/index.js
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
redirect: [620, 1139],
|
|
22
22
|
example: [543, 770, 1136],
|
|
23
23
|
language: [543, 959, 1139, 620],
|
|
24
|
-
allresources: [1139]
|
|
24
|
+
allresources: [1139],
|
|
25
|
+
a11y: [557, 781, 897, 955, 959]
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const KNOWN_PROPERTIES = ['weight', 'id', 'referer', 'checkpoint', 't', 'source', 'target', 'cwv', 'CLS', 'FID', 'LCP', 'INP', 'TTFB'];
|
|
@@ -129,6 +130,7 @@
|
|
|
129
130
|
const pluginBasePath = new URL(document.currentScript.src).href.replace(/index(\.map)?\.js/, 'plugins');
|
|
130
131
|
const PLUGINS = {
|
|
131
132
|
cwv: `${pluginBasePath}/cwv.js`,
|
|
133
|
+
a11y: `${pluginBasePath}/a11y.js`,
|
|
132
134
|
form: {
|
|
133
135
|
url: `${pluginBasePath}/form.js`,
|
|
134
136
|
when: () => document.querySelector('form'),
|
|
@@ -207,8 +209,7 @@
|
|
|
207
209
|
if (!pluginCache.has(key)) {
|
|
208
210
|
pluginCache.set(key, import(`${plugin.url || plugin}`));
|
|
209
211
|
}
|
|
210
|
-
|
|
211
|
-
return pluginLoadPromise.then(p => p.default && p.default(params) || typeof p === 'function' && p(params)).catch(() => {});
|
|
212
|
+
return pluginCache.get(key).then(p => p.default && p.default(params) || typeof p === 'function' && p(params)).catch(() => {});
|
|
212
213
|
}
|
|
213
214
|
function loadPlugins(filter = () => true, params = PLUGIN_PARAMETERS) {
|
|
214
215
|
Object.entries(PLUGINS).filter(([, plugin]) => filter(plugin)).map(([key]) => loadPlugin(key, params));
|
|
@@ -237,31 +238,28 @@
|
|
|
237
238
|
} = window.hlx.rum;
|
|
238
239
|
if (isSelected && maxEvents) {
|
|
239
240
|
maxEvents -= 1;
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
console.debug(`ping:${checkpoint}`, pdata);
|
|
263
|
-
};
|
|
264
|
-
sendPing(data);
|
|
241
|
+
const body = JSON.stringify({
|
|
242
|
+
weight,
|
|
243
|
+
id,
|
|
244
|
+
referer: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'](),
|
|
245
|
+
checkpoint,
|
|
246
|
+
t,
|
|
247
|
+
...data
|
|
248
|
+
}, KNOWN_PROPERTIES);
|
|
249
|
+
const urlParams = window.RUM_PARAMS ? `?${new URLSearchParams(window.RUM_PARAMS).toString()}` : '';
|
|
250
|
+
const {
|
|
251
|
+
href: url,
|
|
252
|
+
origin
|
|
253
|
+
} = new URL(`.rum/${weight}${urlParams}`, sampleRUM.collectBaseURL);
|
|
254
|
+
if (window.location.origin === origin) {
|
|
255
|
+
const headers = {
|
|
256
|
+
type: 'application/json'
|
|
257
|
+
};
|
|
258
|
+
navigator.sendBeacon(url, new Blob([body], headers));
|
|
259
|
+
} else {
|
|
260
|
+
navigator.sendBeacon(url, body);
|
|
261
|
+
}
|
|
262
|
+
console.debug(`ping:${checkpoint}`, data);
|
|
265
263
|
}
|
|
266
264
|
}
|
|
267
265
|
function processQueue() {
|
|
@@ -316,13 +314,14 @@
|
|
|
316
314
|
function addLoadResourceTracking() {
|
|
317
315
|
const observer = new PerformanceObserver(list => {
|
|
318
316
|
try {
|
|
319
|
-
|
|
317
|
+
const entries = list.getEntries();
|
|
318
|
+
entries.filter(e => !e.responseStatus || e.responseStatus < 400).filter(e => window.location.hostname === new URL(e.name).hostname || fflags.has('allresources')).filter(e => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)')).forEach(e => {
|
|
320
319
|
sampleRUM('loadresource', {
|
|
321
320
|
source: e.name,
|
|
322
321
|
target: Math.round(e.duration)
|
|
323
322
|
});
|
|
324
323
|
});
|
|
325
|
-
|
|
324
|
+
entries.filter(e => e.responseStatus >= 400).filter(e => !new URL(e.name).pathname.match('.*(/\\.rum/1[0-9]{0,3})')).forEach(e => {
|
|
326
325
|
sampleRUM('missingresource', {
|
|
327
326
|
source: e.name,
|
|
328
327
|
target: e.responseStatus
|
|
@@ -419,7 +418,7 @@
|
|
|
419
418
|
});
|
|
420
419
|
});
|
|
421
420
|
}
|
|
422
|
-
function
|
|
421
|
+
function init() {
|
|
423
422
|
try {
|
|
424
423
|
if (sampleRUM) {
|
|
425
424
|
addTrackingFromConfig();
|
|
@@ -428,6 +427,6 @@
|
|
|
428
427
|
}
|
|
429
428
|
} catch (error) {}
|
|
430
429
|
}
|
|
431
|
-
|
|
430
|
+
init();
|
|
432
431
|
|
|
433
432
|
})();
|
package/src/index.md5
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
9ba349a918f32b3e7ab28953b112e1e3
|
package/src/index.sri
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
sha384-
|
|
1
|
+
sha384-aqtOVXFtjtaolB7x98Nxy+5pQMlMsfcgRSYyTEkx+BucLpCtD+Zor5pu0VVOXSkP
|