@adobe/helix-rum-enhancer 2.36.5 → 2.37.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/CHANGELOG.md +21 -0
- package/eslint.config.js +34 -0
- package/modules/fflags.js +1 -0
- package/modules/index.js +43 -25
- package/package.json +14 -12
- package/plugins/a11y.js +231 -0
- package/plugins/onetrust.js +12 -18
- package/plugins/trustarc.js +38 -0
- package/plugins/usercentrics.js +35 -0
- package/src/index.js +49 -35
- 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/onetrust.js +15 -30
- package/src/plugins/onetrust.map.js +16 -31
- package/src/plugins/onetrust.md5 +1 -1
- package/src/plugins/trustarc.js +37 -0
- package/src/plugins/trustarc.map.js +38 -0
- package/src/plugins/trustarc.md5 +1 -0
- package/src/plugins/usercentrics.js +39 -0
- package/src/plugins/usercentrics.map.js +40 -0
- package/src/plugins/usercentrics.md5 +1 -0
- package/src/plugins/video.map.js +1 -1
- package/src/plugins/webcomponent.map.js +1 -1
- package/src/plugins/webcomponent.md5 +1 -1
- package/.eslintrc.cjs +0 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [2.37.1](https://github.com/adobe/helix-rum-enhancer/compare/v2.37.0...v2.37.1) (2025-07-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **a11y:** audience is flagged as using accessibility too often ([4449970](https://github.com/adobe/helix-rum-enhancer/commit/444997021c49b45d9162a4a09035e6b75c0360fc))
|
|
7
|
+
* **a11y:** audience is flagged as using accessibility too often ([03e9f1b](https://github.com/adobe/helix-rum-enhancer/commit/03e9f1b185574649741ca4e3a50ecce13165819d))
|
|
8
|
+
|
|
9
|
+
# [2.37.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.36.5...v2.37.0) (2025-07-08)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* audience reporting ([9f02657](https://github.com/adobe/helix-rum-enhancer/commit/9f0265703aba8ce01fa2850dadc36f993e9e2496))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **a11y:** add a new accessibility plugin to categorize audience and detect common bugs ([033b425](https://github.com/adobe/helix-rum-enhancer/commit/033b4250ddfd3a3a8887b6fbebd18b92543c70a5))
|
|
20
|
+
* **a11y:** add a new accessibility plugin to flag accessible audience and detect common bugs ([5a39aff](https://github.com/adobe/helix-rum-enhancer/commit/5a39aff315213187ce2b35928f72ad44fcd0e869))
|
|
21
|
+
|
|
1
22
|
## [2.36.5](https://github.com/adobe/helix-rum-enhancer/compare/v2.36.4...v2.36.5) (2025-06-20)
|
|
2
23
|
|
|
3
24
|
|
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
|
@@ -32,8 +32,26 @@ const hasCookieKey = (key) => document.cookie.split(';').some((c) => c.trim().st
|
|
|
32
32
|
// Set the base path for the plugins
|
|
33
33
|
const pluginBasePath = new URL(document.currentScript.src).href.replace(/index(\.map)?\.js/, 'plugins');
|
|
34
34
|
|
|
35
|
+
const CONSENT_PROVIDERS = [
|
|
36
|
+
{
|
|
37
|
+
name: 'onetrust',
|
|
38
|
+
detect: () => hasCookieKey('OptanonAlertBoxClosed') || document.querySelector('#onetrust-banner-sdk, #onetrust-pc-sdk'),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'trustarc',
|
|
42
|
+
detect: () => ['notice_gdpr_prefs', 'notice_preferences'].some(hasCookieKey) || document.querySelector('#truste-consent-track, #consent_blackbar'),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'usercentrics',
|
|
46
|
+
detect: () => window.localStorage.getItem('uc_gcm') || document.querySelector('#usercentrics-root'),
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const getConsentProvider = () => CONSENT_PROVIDERS.find(({ detect }) => detect());
|
|
51
|
+
|
|
35
52
|
const PLUGINS = {
|
|
36
53
|
cwv: `${pluginBasePath}/cwv.js`,
|
|
54
|
+
a11y: `${pluginBasePath}/a11y.js`,
|
|
37
55
|
// Interactive elements
|
|
38
56
|
form: { url: `${pluginBasePath}/form.js`, when: () => document.querySelector('form'), isBlockDependent: true },
|
|
39
57
|
video: { url: `${pluginBasePath}/video.js`, when: () => document.querySelector('video'), isBlockDependent: true },
|
|
@@ -44,9 +62,8 @@ const PLUGINS = {
|
|
|
44
62
|
},
|
|
45
63
|
// Martech
|
|
46
64
|
martech: { url: `${pluginBasePath}/martech.js`, when: ({ urlParameters }) => urlParameters.size > 0 },
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
when: () => (hasCookieKey('OptanonAlertBoxClosed') || document.querySelector('#onetrust-consent-sdk')),
|
|
65
|
+
consent: {
|
|
66
|
+
when: () => getConsentProvider(),
|
|
50
67
|
isBlockDependent: true,
|
|
51
68
|
mutationObserverParams: {
|
|
52
69
|
target: document.body,
|
|
@@ -102,12 +119,15 @@ function loadPlugin(key, params) {
|
|
|
102
119
|
return null;
|
|
103
120
|
}
|
|
104
121
|
|
|
122
|
+
if (key === 'consent') {
|
|
123
|
+
plugin.url = `${pluginBasePath}/${getConsentProvider().name}.js`;
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
if (!pluginCache.has(key)) {
|
|
106
127
|
pluginCache.set(key, import(`${plugin.url || plugin}`));
|
|
107
128
|
}
|
|
108
129
|
|
|
109
|
-
|
|
110
|
-
return pluginLoadPromise
|
|
130
|
+
return pluginCache.get(key)
|
|
111
131
|
.then((p) => (p.default && p.default(params)) || (typeof p === 'function' && p(params)))
|
|
112
132
|
.catch(() => { /* silent plugin error catching */ });
|
|
113
133
|
}
|
|
@@ -147,22 +167,19 @@ function trackCheckpoint(checkpoint, data, t) {
|
|
|
147
167
|
const { weight, id } = window.hlx.rum;
|
|
148
168
|
if (isSelected && maxEvents) {
|
|
149
169
|
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);
|
|
170
|
+
// eslint-disable-next-line object-curly-newline, max-len
|
|
171
|
+
const body = JSON.stringify({ weight, id, referer: urlSanitizers[window.hlx.RUM_MASK_URL || 'path'](), checkpoint, t, ...data }, KNOWN_PROPERTIES);
|
|
172
|
+
const urlParams = window.RUM_PARAMS ? `?${new URLSearchParams(window.RUM_PARAMS).toString()}` : '';
|
|
173
|
+
const { href: url, origin } = new URL(`.rum/${weight}${urlParams}`, sampleRUM.collectBaseURL);
|
|
174
|
+
if (window.location.origin === origin) {
|
|
175
|
+
const headers = { type: 'application/json' };
|
|
176
|
+
navigator.sendBeacon(url, new Blob([body], headers));
|
|
177
|
+
/* c8 ignore next 3 */
|
|
178
|
+
} else {
|
|
179
|
+
navigator.sendBeacon(url, body);
|
|
180
|
+
}
|
|
181
|
+
// eslint-disable-next-line no-console
|
|
182
|
+
console.debug(`ping:${checkpoint}`, data);
|
|
166
183
|
}
|
|
167
184
|
}
|
|
168
185
|
|
|
@@ -223,14 +240,15 @@ function addNavigationTracking() {
|
|
|
223
240
|
function addLoadResourceTracking() {
|
|
224
241
|
const observer = new PerformanceObserver((list) => {
|
|
225
242
|
try {
|
|
226
|
-
list.getEntries()
|
|
243
|
+
const entries = list.getEntries();
|
|
244
|
+
entries
|
|
227
245
|
.filter((e) => !e.responseStatus || e.responseStatus < 400)
|
|
228
246
|
.filter((e) => window.location.hostname === new URL(e.name).hostname || fflags.has('allresources'))
|
|
229
247
|
.filter((e) => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
|
|
230
248
|
.forEach((e) => {
|
|
231
249
|
sampleRUM('loadresource', { source: e.name, target: Math.round(e.duration) });
|
|
232
250
|
});
|
|
233
|
-
|
|
251
|
+
entries
|
|
234
252
|
.filter((e) => e.responseStatus >= 400)
|
|
235
253
|
.filter((e) => !(new URL(e.name).pathname.match('.*(/\\.rum/1[0-9]{0,3})')))
|
|
236
254
|
.forEach((e) => {
|
|
@@ -343,7 +361,7 @@ function addTrackingFromConfig() {
|
|
|
343
361
|
});
|
|
344
362
|
}
|
|
345
363
|
|
|
346
|
-
function
|
|
364
|
+
function init() {
|
|
347
365
|
try {
|
|
348
366
|
if (sampleRUM) {
|
|
349
367
|
addTrackingFromConfig();
|
|
@@ -356,4 +374,4 @@ function initEnhancer() {
|
|
|
356
374
|
}
|
|
357
375
|
}
|
|
358
376
|
|
|
359
|
-
|
|
377
|
+
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.1",
|
|
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.8",
|
|
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.45.0",
|
|
62
64
|
"rollup-plugin-eslint-bundle": "9.0.0",
|
|
63
|
-
"semantic-release": "24.2.
|
|
65
|
+
"semantic-release": "24.2.7",
|
|
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,231 @@
|
|
|
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-reduced-transparency: reduce)',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const KEYBOARD_RELEVANT_KEYS = [
|
|
22
|
+
'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter', ' ', 'Escape',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const HIGH_ZOOM_LEVEL = 200;
|
|
26
|
+
const MEDIUM_ZOOM_LEVEL = 125;
|
|
27
|
+
|
|
28
|
+
const BEHAVIORAL_MIN_EVENTS = 10;
|
|
29
|
+
|
|
30
|
+
const FOCUSTRAP_MAX_HISTORY = 25;
|
|
31
|
+
const FOCUS_TRAP_MIN_HISTORY = 4;
|
|
32
|
+
const FOCUS_TRAP_PING_PONG_DURATION = 3000;
|
|
33
|
+
const FOCUS_TRAP_MIN_REPEATS = 3;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Detects approximate zoom level using the Visual Viewport API.
|
|
37
|
+
* This is a pure utility function.
|
|
38
|
+
* @returns {Number} Estimated zoom percentage.
|
|
39
|
+
*/
|
|
40
|
+
function getZoom() {
|
|
41
|
+
if (window.visualViewport && window.visualViewport.width > 0) {
|
|
42
|
+
return Math.round((window.innerWidth / window.visualViewport.width) * 100);
|
|
43
|
+
}
|
|
44
|
+
/* c8 ignore next 1 */
|
|
45
|
+
return 100;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Minimal accessibility audience detection plugin for helix-rum-enhancer.
|
|
50
|
+
* @param {Object} params Plugin parameters from enhancer.
|
|
51
|
+
* @param {Function} params.sampleRUM RUM tracking function.
|
|
52
|
+
* @param {Function} params.sourceSelector Function to generate a selector for an element.
|
|
53
|
+
*/
|
|
54
|
+
export default function addAccessibilityAudienceTracking({ sampleRUM, sourceSelector }) {
|
|
55
|
+
const focusHistory = [];
|
|
56
|
+
let score = 0;
|
|
57
|
+
let reported = false;
|
|
58
|
+
let focusTrapDetected = false;
|
|
59
|
+
let keyboardCount = 0;
|
|
60
|
+
let totalCount = 0;
|
|
61
|
+
let lastFocusTime = 0;
|
|
62
|
+
let hadMeaningfulFocus = false;
|
|
63
|
+
|
|
64
|
+
const reportAudience = () => {
|
|
65
|
+
/* c8 ignore next 3 */
|
|
66
|
+
if (reported) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
reported = true;
|
|
70
|
+
const audience = score >= 1 ? 'on' : 'off';
|
|
71
|
+
sampleRUM('a11y', { source: audience, target: 'on:off' });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const reportFocusTrap = (trapType, elements) => {
|
|
75
|
+
/* c8 ignore next 3 */
|
|
76
|
+
if (focusTrapDetected) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
focusTrapDetected = true;
|
|
80
|
+
const elementDescriptions = elements.slice(0, 3).map(sourceSelector);
|
|
81
|
+
sampleRUM('error', {
|
|
82
|
+
source: `focus-trap:${trapType}`,
|
|
83
|
+
target: elementDescriptions.join(', '),
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const detectRepeatingSequence = (elements, seqLength) => {
|
|
88
|
+
const totalLength = seqLength * FOCUS_TRAP_MIN_REPEATS;
|
|
89
|
+
/* c8 ignore next 3 */
|
|
90
|
+
if (elements.length < totalLength) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const recent = elements.slice(-totalLength);
|
|
94
|
+
const baseSequence = recent.slice(0, seqLength);
|
|
95
|
+
for (let i = 1; i < FOCUS_TRAP_MIN_REPEATS; i += 1) {
|
|
96
|
+
const nextSequence = recent.slice(i * seqLength, (i + 1) * seqLength);
|
|
97
|
+
if (nextSequence.length !== seqLength
|
|
98
|
+
|| !nextSequence.every((el, j) => el === baseSequence[j])) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
reportFocusTrap('sequence', baseSequence);
|
|
103
|
+
return true;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const detectFocusTrap = () => {
|
|
107
|
+
if (focusHistory.length < FOCUS_TRAP_MIN_HISTORY || focusTrapDetected) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const elements = focusHistory.map((h) => h.element);
|
|
111
|
+
const timestamps = focusHistory.map((h) => h.timestamp);
|
|
112
|
+
|
|
113
|
+
if (elements.slice(-3).every((el) => el === elements[elements.length - 1])) {
|
|
114
|
+
reportFocusTrap('single', [elements[elements.length - 1]]);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const recent4 = elements.slice(-4);
|
|
119
|
+
const unique = [...new Set(recent4)];
|
|
120
|
+
const timeDiff = timestamps[timestamps.length - 1] - timestamps[timestamps.length - 4];
|
|
121
|
+
if (unique.length === 2 && timeDiff < FOCUS_TRAP_PING_PONG_DURATION) {
|
|
122
|
+
reportFocusTrap('ping-pong', unique);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const minSeqLength = 2 * FOCUS_TRAP_MIN_HISTORY + 1;
|
|
127
|
+
if (focusHistory.length >= minSeqLength) {
|
|
128
|
+
for (let seqLength = 2; seqLength <= 4; seqLength += 1) {
|
|
129
|
+
if (detectRepeatingSequence(elements, seqLength)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const trackFocusForTrap = (event) => {
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
const { activeElement } = document;
|
|
139
|
+
/* c8 ignore next 3 */
|
|
140
|
+
if (!activeElement || activeElement === document.body) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
focusHistory.push({
|
|
144
|
+
element: activeElement,
|
|
145
|
+
timestamp: performance.now(),
|
|
146
|
+
shiftKey: event.shiftKey,
|
|
147
|
+
});
|
|
148
|
+
/* c8 ignore next 3 */
|
|
149
|
+
if (focusHistory.length > FOCUSTRAP_MAX_HISTORY) {
|
|
150
|
+
focusHistory.shift();
|
|
151
|
+
}
|
|
152
|
+
detectFocusTrap();
|
|
153
|
+
}, 0);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const checkBehavioralScore = () => {
|
|
157
|
+
if (totalCount >= BEHAVIORAL_MIN_EVENTS && !reported) {
|
|
158
|
+
if (keyboardCount / totalCount > 0.7) {
|
|
159
|
+
score += 2;
|
|
160
|
+
}
|
|
161
|
+
reportAudience();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// --- Static Scoring ---
|
|
166
|
+
score += MEDIA_QUERY_PREFERENCES.filter((q) => window.matchMedia(q).matches).length;
|
|
167
|
+
|
|
168
|
+
const zoom = getZoom();
|
|
169
|
+
if (zoom >= HIGH_ZOOM_LEVEL) {
|
|
170
|
+
score += 3;
|
|
171
|
+
} else if (zoom >= MEDIUM_ZOOM_LEVEL) {
|
|
172
|
+
score += 2;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (navigator.maxTouchPoints === 0 && window.matchMedia('(pointer: coarse)').matches) {
|
|
176
|
+
score += 2;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// --- Event Listeners & Observers ---
|
|
180
|
+
document.addEventListener('keydown', (event) => {
|
|
181
|
+
if (KEYBOARD_RELEVANT_KEYS.includes(event.key)) {
|
|
182
|
+
keyboardCount += 1;
|
|
183
|
+
totalCount += 1;
|
|
184
|
+
if (event.key === 'Tab') {
|
|
185
|
+
trackFocusForTrap(event);
|
|
186
|
+
}
|
|
187
|
+
checkBehavioralScore();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
document.addEventListener('click', () => {
|
|
192
|
+
totalCount += 1;
|
|
193
|
+
checkBehavioralScore();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
document.addEventListener('focusin', (event) => {
|
|
197
|
+
if (event.target !== document.body) {
|
|
198
|
+
lastFocusTime = performance.now();
|
|
199
|
+
hadMeaningfulFocus = true;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const focusLossObserver = new MutationObserver((mutations) => {
|
|
204
|
+
if (hadMeaningfulFocus
|
|
205
|
+
&& document.activeElement === document.body
|
|
206
|
+
&& (performance.now() - lastFocusTime > 100)) {
|
|
207
|
+
/* c8 ignore next 9 */
|
|
208
|
+
const mutation = mutations.find((m) => (
|
|
209
|
+
m.type === 'childList' && (m.addedNodes.length > 0 || m.removedNodes.length > 0)
|
|
210
|
+
));
|
|
211
|
+
sampleRUM('error', {
|
|
212
|
+
source: 'focus-loss',
|
|
213
|
+
target: sourceSelector(mutation?.target),
|
|
214
|
+
});
|
|
215
|
+
hadMeaningfulFocus = false; // Prevent spam
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
focusLossObserver.observe(document, { childList: true, subtree: true });
|
|
220
|
+
|
|
221
|
+
// --- Final Reporting ---
|
|
222
|
+
if (score >= 1) {
|
|
223
|
+
reportAudience();
|
|
224
|
+
} else {
|
|
225
|
+
setTimeout(
|
|
226
|
+
() => reportAudience(),
|
|
227
|
+
/* c8 ignore next 1 */
|
|
228
|
+
window.hlx.A11Y_REPORT_DELAY || 10_000,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
package/plugins/onetrust.js
CHANGED
|
@@ -9,36 +9,30 @@
|
|
|
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
|
-
|
|
13
|
-
export default function addCookieConsentTracking({ sampleRUM }) {
|
|
14
|
-
const sampleRUMOnce = (...args) => {
|
|
15
|
-
if (!hasSentData) {
|
|
16
|
-
sampleRUM(...args);
|
|
17
|
-
hasSentData = true;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
12
|
+
function trackConsent() {
|
|
20
13
|
const cmpCookie = document.cookie.split(';')
|
|
21
14
|
.map((c) => c.trim())
|
|
22
15
|
.find((cookie) => cookie.startsWith('OptanonAlertBoxClosed='));
|
|
23
16
|
|
|
24
17
|
if (cmpCookie) {
|
|
25
|
-
|
|
26
|
-
return;
|
|
18
|
+
return 'hidden';
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
const
|
|
21
|
+
const banner = document.querySelector('#onetrust-banner-sdk') || document.querySelector('#onetrust-pc-sdk');
|
|
30
22
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
return;
|
|
23
|
+
if (banner && banner.offsetHeight > 0) {
|
|
24
|
+
return 'show';
|
|
34
25
|
}
|
|
35
26
|
|
|
36
|
-
|
|
27
|
+
return 'suppressed';
|
|
28
|
+
}
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
let hasSentData = false;
|
|
31
|
+
export default function addCookieConsentTracking({ sampleRUM }) {
|
|
32
|
+
if (hasSentData) {
|
|
40
33
|
return;
|
|
41
34
|
}
|
|
42
35
|
|
|
43
|
-
|
|
36
|
+
sampleRUM('consent', { source: 'onetrust', target: trackConsent() });
|
|
37
|
+
hasSentData = true;
|
|
44
38
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
function trackConsent() {
|
|
13
|
+
const cookies = document.cookie.split(';')
|
|
14
|
+
.map((c) => c.trim())
|
|
15
|
+
.filter((cookie) => (cookie.startsWith('notice_gdpr_prefs=') || cookie.startsWith('notice_preferences=')));
|
|
16
|
+
|
|
17
|
+
if (cookies.length === 2) {
|
|
18
|
+
return 'hidden';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const banner = document.querySelector('#truste-consent-track') || document.querySelector('#truste-consent-content');
|
|
22
|
+
|
|
23
|
+
if (banner && banner.offsetHeight > 0) {
|
|
24
|
+
return 'show';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return 'suppressed';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let hasSentData = false;
|
|
31
|
+
export default function addCookieConsentTracking({ sampleRUM }) {
|
|
32
|
+
if (hasSentData) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
sampleRUM('consent', { source: 'trustarc', target: trackConsent() });
|
|
37
|
+
hasSentData = true;
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
function trackConsent() {
|
|
13
|
+
if (localStorage.getItem('uc_gcm')) {
|
|
14
|
+
return 'hidden';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { shadowRoot } = document.querySelector('#usercentrics-root');
|
|
18
|
+
const banner = shadowRoot.querySelector('#uc-center-container') || shadowRoot.querySelector('#uc-fading-wrapper');
|
|
19
|
+
|
|
20
|
+
if (banner && banner.offsetHeight > 0) {
|
|
21
|
+
return 'show';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return 'suppressed';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let hasSentData = false;
|
|
28
|
+
export default function addCookieConsentTracking({ sampleRUM }) {
|
|
29
|
+
if (hasSentData) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
sampleRUM('consent', { source: 'usercentrics', target: trackConsent() });
|
|
34
|
+
hasSentData = true;
|
|
35
|
+
}
|