@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 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,9 @@
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.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 ([bb11187](https://github.com/adobe/helix-rum-enhancer/commit/bb11187058b8cb521a270c316323b66414a8d817))
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 = (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
+ if (el.closest('form')) {
64
+ return `form${el.id ? `#${el.id}` : ''}`;
66
65
  }
67
- const block = element.closest('.block[data-block-name]');
66
+ const block = el.closest('.block[data-block-name]');
68
67
  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}`));
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(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') || ''}']`
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(element, isButton)) return 'button';
85
- return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase();
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(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]}`);
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 = (element) => {
93
+ export const sourceSelector = (el) => {
94
94
  try {
95
- if (!element || element === document.body || element === document.documentElement) {
95
+ if (!el || el === document.body || el === document.documentElement) {
96
96
  return undefined;
97
97
  }
98
- if (element.getAttribute('data-rum-source')) {
99
- return element.getAttribute('data-rum-source');
98
+ if (el.getAttribute('data-rum-source')) {
99
+ return el.getAttribute('data-rum-source');
100
100
  }
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)}"`;
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
- 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', () => {
@@ -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.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 beta",
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 = 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
+ if (el.closest('form')) {
84
+ return `form${el.id ? `#${el.id}` : ''}`;
85
85
  }
86
- const block = element.closest('.block[data-block-name]');
86
+ const block = el.closest('.block[data-block-name]');
87
87
  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}`));
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(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') || ''}']`
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(element, isButton)) return 'button';
103
- return element.tagName.toLowerCase().match(/^(a|img|video)$/) && element.tagName.toLowerCase();
103
+ if (walk(el, isButton)) return 'button';
104
+ return el.tagName.toLowerCase().match(/^(a|img|video)$/) && el.tagName.toLowerCase();
104
105
  }
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]}`);
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 = (element) => {
111
+ const sourceSelector = (el) => {
111
112
  try {
112
- if (!element || element === document.body || element === document.documentElement) {
113
+ if (!el || el === document.body || el === document.documentElement) {
113
114
  return undefined;
114
115
  }
115
- if (element.getAttribute('data-rum-source')) {
116
- return element.getAttribute('data-rum-source');
116
+ if (el.getAttribute('data-rum-source')) {
117
+ return el.getAttribute('data-rum-source');
117
118
  }
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)}"`;
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 consentMutationObserver;
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 (consentMutationObserver) {
145
- consentMutationObserver.disconnect();
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
- consentMutationObserver = window.MutationObserver
153
+ consentMO = window.MutationObserver
153
154
  ? new MutationObserver(trackShowConsent)
154
155
  : null;
155
- if (consentMutationObserver) {
156
- consentMutationObserver.observe(
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 formSubmitListener = (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) });
201
- const blocksMutationObserver = window.MutationObserver ? new MutationObserver(blocksMutationsCallback)
201
+ const blocksMO = window.MutationObserver ? new MutationObserver(blocksMCB)
202
202
  : {};
203
- const mediaMutationObserver = window.MutationObserver ? new MutationObserver(mediaMutationsCallback)
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((entry) => navigate(
296
+ .getEntries().map((e) => navigate(
297
297
  window.hlx.referrer || document.referrer,
298
- entry.type,
299
- entry.redirectCount,
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((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) });
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((entry) => entry.responseStatus === 404)
315
- .forEach((entry) => {
316
- sampleRUM('missingresource', { source: entry.name, target: entry.hostname });
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 activateBlocksMutationObserver() {
324
- if (!blocksMutationObserver || blocksMutationObserver.active) {
323
+ function activateBlocksMO() {
324
+ if (!blocksMO || blocksMO.active) {
325
325
  return;
326
326
  }
327
- blocksMutationObserver.active = true;
328
- blocksMutationObserver.observe(
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 activateMediaMutationObserver() {
334
- if (!mediaMutationObserver || mediaMutationObserver.active) {
333
+ function activateMediaMO() {
334
+ if (!mediaMO || mediaMO.active) {
335
335
  return;
336
336
  }
337
- mediaMutationObserver.active = true;
338
- mediaMutationObserver.observe(
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
- activateBlocksMutationObserver();
348
- activateMediaMutationObserver();
347
+ activateBlocksMO();
348
+ activateMediaMO();
349
349
  const observer = new IntersectionObserver((entries) => {
350
350
  try {
351
351
  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);
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
- activateBlocksMutationObserver();
385
- activateMediaMutationObserver();
384
+ activateBlocksMO();
385
+ activateMediaMO();
386
386
  parent.querySelectorAll('form').forEach((form) => {
387
- form.removeEventListener('submit', formSubmitListener);
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 blocksMutationsCallback(mutations) {
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 mediaMutationsCallback(mutations) {
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
- 81557c0399da9e24aeac8f384303248a
1
+ 8094422990fd7a9219bffb7309cb92a2