@adobe/helix-rum-enhancer 2.26.0-beta.1 → 2.26.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/.releaserc.cjs CHANGED
@@ -29,5 +29,5 @@ module.exports = {
29
29
  slackChannel: "rum-explorers",
30
30
  }],
31
31
  ],
32
- branches: ['main', { name: 'beta', prerelease: true }],
32
+ branches: ['main'],
33
33
  };
package/CHANGELOG.md CHANGED
@@ -1,9 +1,16 @@
1
- # [2.26.0-beta.1](https://github.com/adobe/helix-rum-enhancer/compare/v2.25.0...v2.26.0-beta.1) (2024-10-09)
1
+ ## [2.26.1](https://github.com/adobe/helix-rum-enhancer/compare/v2.26.0...v2.26.1) (2024-12-10)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **redirects:** use underscore in param ([bada92e](https://github.com/adobe/helix-rum-enhancer/commit/bada92e69aaa8f560d61d6264f578eb2958934ec))
7
+
8
+ # [2.26.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.25.0...v2.26.0) (2024-10-16)
2
9
 
3
10
 
4
11
  ### Features
5
12
 
6
- * handle pre-rendering ([bb11187](https://github.com/adobe/helix-rum-enhancer/commit/bb11187058b8cb521a270c316323b66414a8d817))
13
+ * 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
14
 
8
15
  # [2.25.0](https://github.com/adobe/helix-rum-enhancer/compare/v2.24.0...v2.25.0) (2024-10-03)
9
16
 
package/modules/dom.js CHANGED
@@ -9,99 +9,100 @@
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 = (element) => element.getAttribute('data-rum-target') || element.getAttribute('href')
13
- || element.currentSrc || element.getAttribute('src') || element.dataset.action || element.action;
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 = (element) => {
15
+ export const targetSelector = (el) => {
16
16
  try {
17
- if (!element) return undefined;
18
- let value = getTargetValue(element);
19
- if (!value && element.tagName !== 'A' && element.closest('a')) {
20
- value = getTargetValue(element.closest('a'));
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 (value && !value.startsWith('https://')) {
22
+ if (v && !v.startsWith('https://')) {
23
23
  // resolve relative links
24
- value = new URL(value, window.location).href;
24
+ v = new URL(v, window.location).href;
25
25
  }
26
- return value;
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(element, checkFn) {
34
- if (!element || element === document.body || element === document.documentElement) {
33
+ function walk(el, checkFn) {
34
+ if (!el || el === document.body || el === document.documentElement) {
35
35
  return undefined;
36
36
  }
37
- const checkValue = checkFn(element);
38
- return checkValue || walk(element.parentElement, checkFn);
37
+ return checkFn(el) || walk(el.parentElement, checkFn);
39
38
  }
40
39
 
41
- function isDialog(element) {
40
+ function isDialog(el) {
42
41
  // doing it well
43
- if (element.tagName === 'DIALOG') return true;
42
+ if (el.tagName === 'DIALOG') return true;
44
43
  // making the best of it
45
- if (element.getAttribute('role') === 'dialog') return true;
46
- if (element.getAttribute('role') === 'alertdialog') return true;
47
- if (element.getAttribute('aria-modal') === 'true') return true;
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 computedStyle = window.getComputedStyle(element);
50
- return (computedStyle && computedStyle.position === 'fixed' && computedStyle.zIndex > 100);
48
+ const cs = window.getComputedStyle(el);
49
+ return (cs && cs.position === 'fixed' && cs.zIndex > 100);
51
50
  }
52
51
 
53
- function isButton(element) {
54
- if (element.tagName === 'BUTTON') return true;
55
- if (element.tagName === 'INPUT' && element.getAttribute('type') === 'button') return true;
56
- if (element.tagName === 'A') {
57
- const classes = Array.from(element.classList);
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 element.getAttribute('role') === 'button';
59
+ return el.getAttribute('role') === 'button';
61
60
  }
62
61
 
63
- function getSourceContext(element) {
64
- if (element.closest('form')) {
65
- return `form${element.id ? `#${element.id}` : ''}`;
62
+ function getSourceContext(el) {
63
+ const formEl = el.closest('form');
64
+ if (formEl) {
65
+ return `form${formEl.id ? `#${formEl.id}` : ''}`;
66
66
  }
67
- const block = element.closest('.block[data-block-name]');
67
+ const block = el.closest('.block[data-block-name]');
68
68
  if (block) return `.${block.getAttribute('data-block-name')}`;
69
- if (walk(element, isDialog)) return 'dialog';
70
- if (element.closest('nav')) return 'nav';
71
- if (element.closest('header')) return 'header';
72
- if (element.closest('footer')) return 'footer';
73
- if (element.closest('aside')) return 'aside';
74
- return (walk(element, (e) => e.id && `#${e.id}`));
69
+ if (walk(el, isDialog)) return 'dialog';
70
+ if (el.closest('nav')) return 'nav';
71
+ if (el.closest('header')) return 'header';
72
+ if (el.closest('footer')) return 'footer';
73
+ if (el.closest('aside')) return 'aside';
74
+ return (walk(el, (e) => e.id && `#${e.id}`));
75
75
  }
76
76
 
77
- function getSourceElement(element) {
78
- if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) {
79
- return (element.tagName.toLowerCase()
80
- + (['INPUT', 'BUTTON'].includes(element.tagName)
81
- ? `[type='${element.getAttribute('type') || ''}']`
77
+ function getSourceElement(el) {
78
+ const f = el.closest('form');
79
+ if (f && Array.from(f.elements).includes(el)) {
80
+ return (el.tagName.toLowerCase()
81
+ + (['INPUT', 'BUTTON'].includes(el.tagName)
82
+ ? `[type='${el.getAttribute('type') || ''}']`
82
83
  : ''));
83
84
  }
84
- if (walk(element, isButton)) return 'button';
85
- return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase();
85
+ if (walk(el, isButton)) return 'button';
86
+ return el.tagName.toLowerCase().match(/^(a|img|video)$/) && el.tagName.toLowerCase();
86
87
  }
87
88
 
88
- function getSourceIdentifier(element) {
89
- if (element.id) return `#${element.id}`;
90
- if (element.getAttribute('data-block-name')) return `.${element.getAttribute('data-block-name')}`;
91
- return (element.classList.length > 0 && `.${element.classList[0]}`);
89
+ function getSourceIdentifier(el) {
90
+ if (el.id) return `#${el.id}`;
91
+ if (el.getAttribute('data-block-name')) return `.${el.getAttribute('data-block-name')}`;
92
+ return (el.classList.length > 0 && `.${el.classList[0]}`);
92
93
  }
93
- export const sourceSelector = (element) => {
94
+ export const sourceSelector = (el) => {
94
95
  try {
95
- if (!element || element === document.body || element === document.documentElement) {
96
+ if (!el || el === document.body || el === document.documentElement) {
96
97
  return undefined;
97
98
  }
98
- if (element.getAttribute('data-rum-source')) {
99
- return element.getAttribute('data-rum-source');
99
+ if (el.getAttribute('data-rum-source')) {
100
+ return el.getAttribute('data-rum-source');
100
101
  }
101
- const context = getSourceContext(element.parentElement) || '';
102
- const elementName = getSourceElement(element) || '';
103
- const identifier = getSourceIdentifier(element) || '';
104
- return `${context} ${elementName}${identifier}`.trim() || `"${element.textContent.substring(0, 10)}"`;
102
+ const ctx = getSourceContext(el.parentElement) || '';
103
+ const name = getSourceElement(el) || '';
104
+ const id = getSourceIdentifier(el) || '';
105
+ return `${ctx} ${name}${id}`.trim() || `"${el.textContent.substring(0, 10)}"`;
105
106
  /* c8 ignore next 3 */
106
107
  } catch (error) {
107
108
  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
- const formSubmitListener = (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) });
29
-
28
+ // blocks mutation observer
30
29
  // eslint-disable-next-line no-use-before-define, max-len
31
- const blocksMutationObserver = window.MutationObserver ? new MutationObserver(blocksMutationsCallback)
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 mediaMutationObserver = window.MutationObserver ? new MutationObserver(mediaMutationsCallback)
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', () => {
@@ -136,7 +138,7 @@ function addNavigationTracking() {
136
138
  sampleRUM('enter', payload); // enter site
137
139
  }
138
140
  fflags.enabled('redirect', () => {
139
- const from = new URLSearchParams(window.location.search).get('redirect-from');
141
+ const from = new URLSearchParams(window.location.search).get('redirect_from');
140
142
  if (redirectCount || from) {
141
143
  sampleRUM('redirect', { source: from, target: redirectCount || 1 });
142
144
  }
@@ -144,10 +146,10 @@ function addNavigationTracking() {
144
146
  };
145
147
 
146
148
  new PerformanceObserver((list) => list
147
- .getEntries().map((entry) => navigate(
149
+ .getEntries().map((e) => navigate(
148
150
  window.hlx.referrer || document.referrer,
149
- entry.type,
150
- entry.redirectCount,
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((entry) => !entry.responseStatus || entry.responseStatus < 400)
160
- .filter((entry) => window.location.hostname === new URL(entry.name).hostname)
161
- .filter((entry) => new URL(entry.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
162
- .forEach((entry) => {
163
- sampleRUM('loadresource', { source: entry.name, target: Math.round(entry.duration) });
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((entry) => entry.responseStatus === 404)
167
- .forEach((entry) => {
168
- sampleRUM('missingresource', { source: entry.name, target: entry.hostname });
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
- function activateBlocksMutationObserver() {
179
- if (!blocksMutationObserver || blocksMutationObserver.active) {
180
+ // activate blocks mutation observer
181
+ function activateBlocksMO() {
182
+ if (!blocksMO || blocksMO.active) {
180
183
  return;
181
184
  }
182
- blocksMutationObserver.active = true;
183
- blocksMutationObserver.observe(
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
- function activateMediaMutationObserver() {
191
- if (!mediaMutationObserver || mediaMutationObserver.active) {
193
+ // activate media mutation observer
194
+ function activateMediaMO() {
195
+ if (!mediaMO || mediaMO.active) {
192
196
  return;
193
197
  }
194
- mediaMutationObserver.active = true;
195
- mediaMutationObserver.observe(
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
- activateBlocksMutationObserver();
208
- activateMediaMutationObserver();
211
+ activateBlocksMO();
212
+ activateMediaMO();
209
213
  const observer = new IntersectionObserver((entries) => {
210
214
  try {
211
215
  entries
212
- .filter((entry) => entry.isIntersecting)
213
- .forEach((entry) => {
214
- observer.unobserve(entry.target); // observe only once
215
- const target = targetSelector(entry.target);
216
- const source = sourceSelector(entry.target);
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
- activateBlocksMutationObserver();
249
- activateMediaMutationObserver();
252
+ activateBlocksMO();
253
+ activateMediaMO();
250
254
  parent.querySelectorAll('form').forEach((form) => {
251
- form.removeEventListener('submit', formSubmitListener); // listen only once
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
- function blocksMutationsCallback(mutations) {
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
- function mediaMutationsCallback(mutations) {
275
+ // media mutation observer callback
276
+ function mediaMCB(mutations) {
272
277
  // media mutations
273
278
  mutations
274
279
  .forEach((m) => {
@@ -19,7 +19,7 @@ export function addCookieConsentTracking(sampleRUM) {
19
19
  return;
20
20
  }
21
21
 
22
- let consentMutationObserver;
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 (consentMutationObserver) {
32
- consentMutationObserver.disconnect();
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
- consentMutationObserver = window.MutationObserver
41
+ consentMO = window.MutationObserver
42
42
  ? new MutationObserver(trackShowConsent)
43
43
  : /* c8 ignore next */ null;
44
- if (consentMutationObserver) {
45
- consentMutationObserver.observe(
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 = window.location.href) => new URL(url).toString(),
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 = window.location.href) => new URL(url).origin,
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 = window.location.href) => {
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-beta.1",
3
+ "version": "2.26.1",
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 beta",
17
+ "semantic-release-dry": "semantic-release --dry-run --branches $CI_BRANCH 1.x main",
18
18
  "prepare": "husky"
19
19
  },
20
20
  "repository": {
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "devDependencies": {
36
36
  "@adobe/eslint-config-helix": "2.0.8",
37
- "@adobe/helix-rum-js": "2.6.0",
37
+ "@adobe/helix-rum-js": "2.10.0",
38
38
  "@esm-bundle/chai": "4.3.4-fix.0",
39
39
  "@semantic-release/changelog": "6.0.3",
40
40
  "@semantic-release/git": "10.0.1",
@@ -48,19 +48,19 @@
48
48
  "eslint": "8.57.1",
49
49
  "eslint-plugin-header": "3.1.1",
50
50
  "eslint-plugin-import": "2.31.0",
51
- "husky": "9.1.6",
52
- "jsdoc-to-markdown": "9.0.2",
51
+ "husky": "9.1.7",
52
+ "jsdoc-to-markdown": "9.1.1",
53
53
  "junit-report-builder": "5.1.1",
54
54
  "lint-staged": "15.2.10",
55
- "mocha": "10.7.3",
55
+ "mocha": "11.0.1",
56
56
  "mocha-multi-reporters": "1.5.1",
57
- "rollup": "4.24.0",
57
+ "rollup": "4.28.1",
58
58
  "rollup-plugin-checksum": "1.0.1",
59
59
  "rollup-plugin-cleanup": "3.2.1",
60
60
  "rollup-plugin-eslint-bundle": "9.0.0",
61
- "semantic-release": "24.1.2",
61
+ "semantic-release": "24.2.0",
62
62
  "semantic-release-slack-bot": "^4.0.2",
63
- "web-vitals": "4.2.3"
63
+ "web-vitals": "4.2.4"
64
64
  },
65
65
  "lint-staged": {
66
66
  "*.js": "eslint",
package/src/index.js CHANGED
@@ -29,96 +29,98 @@
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 = window.location.href) => new URL(url).toString(),
34
- origin: (url = window.location.href) => new URL(url).origin,
35
- path: (url = window.location.href) => {
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 = (element) => element.getAttribute('data-rum-target') || element.getAttribute('href')
42
- || element.currentSrc || element.getAttribute('src') || element.dataset.action || element.action;
43
- const targetSelector = (element) => {
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 (!element) return undefined;
46
- let value = getTargetValue(element);
47
- if (!value && element.tagName !== 'A' && element.closest('a')) {
48
- value = getTargetValue(element.closest('a'));
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 (value && !value.startsWith('https://')) {
51
- value = new URL(value, window.location).href;
51
+ if (v && !v.startsWith('https://')) {
52
+ v = new URL(v, window.location).href;
52
53
  }
53
- return value;
54
+ return v;
54
55
  } catch (error) {
55
56
  return null;
56
57
  }
57
58
  };
58
- function walk(element, checkFn) {
59
- if (!element || element === document.body || element === document.documentElement) {
59
+ function walk(el, checkFn) {
60
+ if (!el || el === document.body || el === document.documentElement) {
60
61
  return undefined;
61
62
  }
62
- const checkValue = checkFn(element);
63
- return checkValue || walk(element.parentElement, checkFn);
63
+ return checkFn(el) || walk(el.parentElement, checkFn);
64
64
  }
65
- function isDialog(element) {
66
- if (element.tagName === 'DIALOG') return true;
67
- if (element.getAttribute('role') === 'dialog') return true;
68
- if (element.getAttribute('role') === 'alertdialog') return true;
69
- if (element.getAttribute('aria-modal') === 'true') return true;
70
- const computedStyle = window.getComputedStyle(element);
71
- return (computedStyle && computedStyle.position === 'fixed' && computedStyle.zIndex > 100);
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(element) {
74
- if (element.tagName === 'BUTTON') return true;
75
- if (element.tagName === 'INPUT' && element.getAttribute('type') === 'button') return true;
76
- if (element.tagName === 'A') {
77
- const classes = Array.from(element.classList);
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 element.getAttribute('role') === 'button';
80
+ return el.getAttribute('role') === 'button';
81
81
  }
82
- function getSourceContext(element) {
83
- if (element.closest('form')) {
84
- return `form${element.id ? `#${element.id}` : ''}`;
82
+ function getSourceContext(el) {
83
+ const formEl = el.closest('form');
84
+ if (formEl) {
85
+ return `form${formEl.id ? `#${formEl.id}` : ''}`;
85
86
  }
86
- const block = element.closest('.block[data-block-name]');
87
+ const block = el.closest('.block[data-block-name]');
87
88
  if (block) return `.${block.getAttribute('data-block-name')}`;
88
- if (walk(element, isDialog)) return 'dialog';
89
- if (element.closest('nav')) return 'nav';
90
- if (element.closest('header')) return 'header';
91
- if (element.closest('footer')) return 'footer';
92
- if (element.closest('aside')) return 'aside';
93
- return (walk(element, (e) => e.id && `#${e.id}`));
89
+ if (walk(el, isDialog)) return 'dialog';
90
+ if (el.closest('nav')) return 'nav';
91
+ if (el.closest('header')) return 'header';
92
+ if (el.closest('footer')) return 'footer';
93
+ if (el.closest('aside')) return 'aside';
94
+ return (walk(el, (e) => e.id && `#${e.id}`));
94
95
  }
95
- function getSourceElement(element) {
96
- if (element.closest('form') && Array.from(element.closest('form').elements).includes(element)) {
97
- return (element.tagName.toLowerCase()
98
- + (['INPUT', 'BUTTON'].includes(element.tagName)
99
- ? `[type='${element.getAttribute('type') || ''}']`
96
+ function getSourceElement(el) {
97
+ const f = el.closest('form');
98
+ if (f && Array.from(f.elements).includes(el)) {
99
+ return (el.tagName.toLowerCase()
100
+ + (['INPUT', 'BUTTON'].includes(el.tagName)
101
+ ? `[type='${el.getAttribute('type') || ''}']`
100
102
  : ''));
101
103
  }
102
- if (walk(element, isButton)) return 'button';
103
- return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase();
104
+ if (walk(el, isButton)) return 'button';
105
+ return el.tagName.toLowerCase().match(/^(a|img|video)$/) && el.tagName.toLowerCase();
104
106
  }
105
- function getSourceIdentifier(element) {
106
- if (element.id) return `#${element.id}`;
107
- if (element.getAttribute('data-block-name')) return `.${element.getAttribute('data-block-name')}`;
108
- return (element.classList.length > 0 && `.${element.classList[0]}`);
107
+ function getSourceIdentifier(el) {
108
+ if (el.id) return `#${el.id}`;
109
+ if (el.getAttribute('data-block-name')) return `.${el.getAttribute('data-block-name')}`;
110
+ return (el.classList.length > 0 && `.${el.classList[0]}`);
109
111
  }
110
- const sourceSelector = (element) => {
112
+ const sourceSelector = (el) => {
111
113
  try {
112
- if (!element || element === document.body || element === document.documentElement) {
114
+ if (!el || el === document.body || el === document.documentElement) {
113
115
  return undefined;
114
116
  }
115
- if (element.getAttribute('data-rum-source')) {
116
- return element.getAttribute('data-rum-source');
117
+ if (el.getAttribute('data-rum-source')) {
118
+ return el.getAttribute('data-rum-source');
117
119
  }
118
- const context = getSourceContext(element.parentElement) || '';
119
- const elementName = getSourceElement(element) || '';
120
- const identifier = getSourceIdentifier(element) || '';
121
- return `${context} ${elementName}${identifier}`.trim() || `"${element.textContent.substring(0, 10)}"`;
120
+ const ctx = getSourceContext(el.parentElement) || '';
121
+ const name = getSourceElement(el) || '';
122
+ const id = getSourceIdentifier(el) || '';
123
+ return `${ctx} ${name}${id}`.trim() || `"${el.textContent.substring(0, 10)}"`;
122
124
  } catch (error) {
123
125
  return null;
124
126
  }
@@ -132,7 +134,7 @@
132
134
  sampleRUM('consent', { source: 'onetrust', target: 'hidden' });
133
135
  return;
134
136
  }
135
- let consentMutationObserver;
137
+ let consentMO;
136
138
  const trackShowConsent = () => {
137
139
  const otsdk = document.querySelector('body > div#onetrust-consent-sdk');
138
140
  if (otsdk) {
@@ -141,19 +143,19 @@
141
143
  } else {
142
144
  sampleRUM('consent', { source: 'onetrust', target: 'show' });
143
145
  }
144
- if (consentMutationObserver) {
145
- consentMutationObserver.disconnect();
146
+ if (consentMO) {
147
+ consentMO.disconnect();
146
148
  }
147
149
  return true;
148
150
  }
149
151
  return false;
150
152
  };
151
153
  if (!trackShowConsent()) {
152
- consentMutationObserver = window.MutationObserver
154
+ consentMO = window.MutationObserver
153
155
  ? new MutationObserver(trackShowConsent)
154
156
  : null;
155
- if (consentMutationObserver) {
156
- consentMutationObserver.observe(
157
+ if (consentMO) {
158
+ consentMO.observe(
157
159
  document.body,
158
160
  { attributes: false, childList: true, subtree: false },
159
161
  );
@@ -197,10 +199,9 @@
197
199
 
198
200
  const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum
199
201
  : {};
200
- const formSubmitListener = (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) });
201
- const blocksMutationObserver = window.MutationObserver ? new MutationObserver(blocksMutationsCallback)
202
+ const blocksMO = window.MutationObserver ? new MutationObserver(blocksMCB)
202
203
  : {};
203
- const mediaMutationObserver = window.MutationObserver ? new MutationObserver(mediaMutationsCallback)
204
+ const mediaMO = window.MutationObserver ? new MutationObserver(mediaMCB)
204
205
  : {};
205
206
  function trackCheckpoint(checkpoint, data, t) {
206
207
  const { weight, id } = window.hlx.rum;
@@ -286,17 +287,17 @@
286
287
  sampleRUM('enter', payload);
287
288
  }
288
289
  fflags.enabled('redirect', () => {
289
- const from = new URLSearchParams(window.location.search).get('redirect-from');
290
+ const from = new URLSearchParams(window.location.search).get('redirect_from');
290
291
  if (redirectCount || from) {
291
292
  sampleRUM('redirect', { source: from, target: redirectCount || 1 });
292
293
  }
293
294
  });
294
295
  };
295
296
  new PerformanceObserver((list) => list
296
- .getEntries().map((entry) => navigate(
297
+ .getEntries().map((e) => navigate(
297
298
  window.hlx.referrer || document.referrer,
298
- entry.type,
299
- entry.redirectCount,
299
+ e.type,
300
+ e.redirectCount,
300
301
  )))
301
302
  .observe({ type: 'navigation', buffered: true });
302
303
  }
@@ -304,38 +305,38 @@
304
305
  const observer = new PerformanceObserver((list) => {
305
306
  try {
306
307
  list.getEntries()
307
- .filter((entry) => !entry.responseStatus || entry.responseStatus < 400)
308
- .filter((entry) => window.location.hostname === new URL(entry.name).hostname)
309
- .filter((entry) => new URL(entry.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
310
- .forEach((entry) => {
311
- sampleRUM('loadresource', { source: entry.name, target: Math.round(entry.duration) });
308
+ .filter((e) => !e.responseStatus || e.responseStatus < 400)
309
+ .filter((e) => window.location.hostname === new URL(e.name).hostname)
310
+ .filter((e) => new URL(e.name).pathname.match('.*(\\.plain\\.html$|\\.json|graphql|api)'))
311
+ .forEach((e) => {
312
+ sampleRUM('loadresource', { source: e.name, target: Math.round(e.duration) });
312
313
  });
313
314
  list.getEntries()
314
- .filter((entry) => entry.responseStatus === 404)
315
- .forEach((entry) => {
316
- sampleRUM('missingresource', { source: entry.name, target: entry.hostname });
315
+ .filter((e) => e.responseStatus === 404)
316
+ .forEach((e) => {
317
+ sampleRUM('missingresource', { source: e.name, target: e.hostname });
317
318
  });
318
319
  } catch (error) {
319
320
  }
320
321
  });
321
322
  observer.observe({ type: 'resource', buffered: true });
322
323
  }
323
- function activateBlocksMutationObserver() {
324
- if (!blocksMutationObserver || blocksMutationObserver.active) {
324
+ function activateBlocksMO() {
325
+ if (!blocksMO || blocksMO.active) {
325
326
  return;
326
327
  }
327
- blocksMutationObserver.active = true;
328
- blocksMutationObserver.observe(
328
+ blocksMO.active = true;
329
+ blocksMO.observe(
329
330
  document.body,
330
331
  { subtree: true, attributes: true, attributeFilter: ['data-block-status'] },
331
332
  );
332
333
  }
333
- function activateMediaMutationObserver() {
334
- if (!mediaMutationObserver || mediaMutationObserver.active) {
334
+ function activateMediaMO() {
335
+ if (!mediaMO || mediaMO.active) {
335
336
  return;
336
337
  }
337
- mediaMutationObserver.active = true;
338
- mediaMutationObserver.observe(
338
+ mediaMO.active = true;
339
+ mediaMO.observe(
339
340
  document.body,
340
341
  { subtree: true, attributes: false, childList: true },
341
342
  );
@@ -344,16 +345,16 @@
344
345
  if (!window.IntersectionObserver) {
345
346
  return null;
346
347
  }
347
- activateBlocksMutationObserver();
348
- activateMediaMutationObserver();
348
+ activateBlocksMO();
349
+ activateMediaMO();
349
350
  const observer = new IntersectionObserver((entries) => {
350
351
  try {
351
352
  entries
352
- .filter((entry) => entry.isIntersecting)
353
- .forEach((entry) => {
354
- observer.unobserve(entry.target);
355
- const target = targetSelector(entry.target);
356
- const source = sourceSelector(entry.target);
353
+ .filter((e) => e.isIntersecting)
354
+ .forEach((e) => {
355
+ observer.unobserve(e.target);
356
+ const target = targetSelector(e.target);
357
+ const source = sourceSelector(e.target);
357
358
  sampleRUM(checkpoint, { target, source });
358
359
  });
359
360
  } catch (error) {
@@ -381,17 +382,16 @@
381
382
  }
382
383
  }
383
384
  function addFormTracking(parent) {
384
- activateBlocksMutationObserver();
385
- activateMediaMutationObserver();
385
+ activateBlocksMO();
386
+ activateMediaMO();
386
387
  parent.querySelectorAll('form').forEach((form) => {
387
- form.removeEventListener('submit', formSubmitListener);
388
- form.addEventListener('submit', formSubmitListener);
388
+ form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true });
389
389
  });
390
390
  }
391
391
  function addObserver(ck, fn, block) {
392
392
  return DEFAULT_TRACKING_EVENTS.includes(ck) && fn(block);
393
393
  }
394
- function blocksMutationsCallback(mutations) {
394
+ function blocksMCB(mutations) {
395
395
  mutations
396
396
  .filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status')
397
397
  .filter((m) => m.target.dataset.blockStatus === 'loaded')
@@ -400,7 +400,7 @@
400
400
  addObserver('viewblock', addViewBlockTracking, m.target);
401
401
  });
402
402
  }
403
- function mediaMutationsCallback(mutations) {
403
+ function mediaMCB(mutations) {
404
404
  mutations
405
405
  .forEach((m) => {
406
406
  addObserver('viewmedia', addViewMediaTracking, m.target);
package/src/index.md5 CHANGED
@@ -1 +1 @@
1
- 81557c0399da9e24aeac8f384303248a
1
+ 9516743215eadcd19f5b5cf311883d16
@@ -23,6 +23,7 @@ export default {
23
23
  'test/fixtures/**',
24
24
  'node_modules/**',
25
25
  '.rum/**',
26
+ 'src/**',
26
27
  ],
27
28
  },
28
29
  files: [
@@ -43,9 +44,10 @@ export default {
43
44
  context.url = '/node_modules/@adobe/helix-rum-js/dist/rum-standalone.js';
44
45
  await next();
45
46
  context.body = context.body
46
- .replace(/const weight.*/, 'const weight = 1')
47
+ .replace(/const weight =/, 'const weight = 1 ||')
47
48
  .replace(/navigator\.sendBeacon/g, 'fakeSendBeacon')
48
- .replace('.rum/@adobe/helix-rum-enhancer@^2/src/index.js', 'src/index.map.js');
49
+ // eslint-disable-next-line no-template-curly-in-string
50
+ .replace('.rum/@adobe/helix-rum-enhancer@${enhancerVersion || \'^2\'}/src/index.js', 'src/index.map.js');
49
51
  return true;
50
52
  } else if (context.url.startsWith('/.rum/web-vitals')) {
51
53
  context.url = '/node_modules/web-vitals/dist/web-vitals.iife.js';