govuk_publishing_components 39.2.0 → 39.2.2

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.
@@ -22,6 +22,242 @@
22
22
  (function () {
23
23
  'use strict';
24
24
 
25
+ function now() {
26
+ return Date.now ? Date.now() : +new Date();
27
+ }
28
+
29
+ var LogEvent = {
30
+ // Internal events
31
+ EvaluationStart: 1,
32
+ EvaluationEnd: 2,
33
+ InitCalled: 3,
34
+ MarkCalled: 4,
35
+ MeasureCalled: 5,
36
+ AddDataCalled: 6,
37
+ SendCalled: 7,
38
+ ForceSampleCalled: 8,
39
+ DataCollectionStart: 9,
40
+ UnloadHandlerTriggered: 10,
41
+ OnloadHandlerTriggered: 11,
42
+ MarkLoadTimeCalled: 12,
43
+ SendCancelledPageHidden: 13,
44
+ // Data collection events
45
+ SessionIsSampled: 21,
46
+ SessionIsNotSampled: 22,
47
+ MainBeaconSent: 23,
48
+ UserTimingBeaconSent: 24,
49
+ InteractionBeaconSent: 25,
50
+ CustomDataBeaconSent: 26,
51
+ // Metric information
52
+ NavigationStart: 41,
53
+ PerformanceEntryReceived: 42,
54
+ PerformanceEntryProcessed: 43,
55
+ // Errors
56
+ PerformanceObserverError: 51,
57
+ InputEventPermissionError: 52,
58
+ InnerHtmlAccessError: 53,
59
+ EventTargetAccessError: 54,
60
+ CookieReadError: 55,
61
+ CookieSetError: 56,
62
+ PageLabelEvaluationError: 57,
63
+ // Browser support messages
64
+ NavTimingNotSupported: 71,
65
+ PaintTimingNotSupported: 72,
66
+ // POST beacon events
67
+ PostBeaconInitialised: 80,
68
+ PostBeaconSendCalled: 81,
69
+ PostBeaconTimeoutReached: 82,
70
+ PostBeaconSent: 83,
71
+ PostBeaconAlreadySent: 84,
72
+ PostBeaconCancelled: 85,
73
+ PostBeaconStopRecording: 86,
74
+ PostBeaconMetricRejected: 87,
75
+ PostBeaconDisabled: 88,
76
+ PostBeaconSendFailed: 89,
77
+ };
78
+ var Logger = /** @class */ (function () {
79
+ function Logger() {
80
+ this.events = [];
81
+ }
82
+ Logger.prototype.logEvent = function (event, args) {
83
+ if (args === void 0) { args = []; }
84
+ this.events.push([now(), event, args]);
85
+ };
86
+ Logger.prototype.getEvents = function () {
87
+ return this.events;
88
+ };
89
+ return Logger;
90
+ }());
91
+
92
+ var START_MARK = "LUX_start";
93
+ var END_MARK = "LUX_end";
94
+ var BOOLEAN_TRUE = "true";
95
+
96
+ function floor(x) {
97
+ return Math.floor(x);
98
+ }
99
+ var max = Math.max;
100
+ var round = Math.round;
101
+ /**
102
+ * Clamp a number so that it is never less than 0
103
+ */
104
+ function clamp(x) {
105
+ return max(0, x);
106
+ }
107
+ function sortNumeric(a, b) {
108
+ return a - b;
109
+ }
110
+
111
+ var scriptStartTime = now();
112
+
113
+ var _a;
114
+ // If the various performance APIs aren't available, we export an empty object to
115
+ // prevent having to make regular typeof checks.
116
+ var performance = window.performance || {};
117
+ var timing = performance.timing || {
118
+ activationStart: 0,
119
+ // If performance.timing isn't available, we attempt to polyfill the navigationStart value.
120
+ // Our first attempt is from LUX.ns, which is the time that the snippet execution began. If this
121
+ // is not available, we fall back to the time that the current script execution began.
122
+ navigationStart: ((_a = window.LUX) === null || _a === void 0 ? void 0 : _a.ns) || scriptStartTime,
123
+ };
124
+ function navigationType() {
125
+ if (performance.navigation && typeof performance.navigation.type !== "undefined") {
126
+ return performance.navigation.type;
127
+ }
128
+ return "";
129
+ }
130
+ function getNavigationEntry() {
131
+ var navEntries = getEntriesByType("navigation");
132
+ if (navEntries.length) {
133
+ var nativeEntry = navEntries[0];
134
+ var entry_1 = {
135
+ navigationStart: 0,
136
+ activationStart: 0,
137
+ };
138
+ for (var key in nativeEntry) {
139
+ entry_1[key] = nativeEntry[key];
140
+ }
141
+ return entry_1;
142
+ }
143
+ var navType = navigationType();
144
+ var entry = {
145
+ navigationStart: 0,
146
+ activationStart: 0,
147
+ startTime: 0,
148
+ type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
149
+ };
150
+ if (true) {
151
+ for (var key in timing) {
152
+ if (typeof timing[key] === "number" && key !== "navigationStart") {
153
+ entry[key] = floor(timing[key] - timing.navigationStart);
154
+ }
155
+ }
156
+ }
157
+ return entry;
158
+ }
159
+ /**
160
+ * Simple wrapper around performance.getEntriesByType to provide fallbacks for
161
+ * legacy browsers, and work around edge cases where undefined is returned instead
162
+ * of an empty PerformanceEntryList.
163
+ */
164
+ function getEntriesByType(type) {
165
+ if (typeof performance.getEntriesByType === "function") {
166
+ var entries = performance.getEntriesByType(type);
167
+ if (entries && entries.length) {
168
+ return entries;
169
+ }
170
+ }
171
+ return [];
172
+ }
173
+ /**
174
+ * Simple wrapper around performance.getEntriesByName to provide fallbacks for
175
+ * legacy browsers, and work around edge cases where undefined is returned instead
176
+ * of an empty PerformanceEntryList.
177
+ */
178
+ function getEntriesByName(type) {
179
+ if (typeof performance.getEntriesByName === "function") {
180
+ var entries = performance.getEntriesByName(type);
181
+ if (entries && entries.length) {
182
+ return entries;
183
+ }
184
+ }
185
+ return [];
186
+ }
187
+
188
+ /**
189
+ * Milliseconds since navigationStart representing when the page was restored from the bfcache
190
+ */
191
+ var pageRestoreTime;
192
+ function setPageRestoreTime(time) {
193
+ pageRestoreTime = time;
194
+ }
195
+ function getPageRestoreTime() {
196
+ return pageRestoreTime;
197
+ }
198
+ /**
199
+ * To measure the way a user experienced a metric, we measure metrics relative to the time the user
200
+ * started viewing the page. On prerendered pages, this is activationStart. On bfcache restores, this
201
+ * is the page restore time. On all other pages this value will be zero.
202
+ */
203
+ function getZeroTime() {
204
+ var _a;
205
+ return max(getPageRestoreTime() || 0, getNavigationEntry().activationStart, ((_a = getEntriesByName(START_MARK).pop()) === null || _a === void 0 ? void 0 : _a.startTime) || 0);
206
+ }
207
+ /**
208
+ * Most time-based metrics that LUX reports should be relative to the "zero" marker, rounded down
209
+ * to the nearest unit so as not to report times in the future, and clamped to zero.
210
+ */
211
+ function processTimeMetric(value) {
212
+ return clamp(floor(value - getZeroTime()));
213
+ }
214
+ /**
215
+ * Returns the number of milliseconds since navigationStart.
216
+ */
217
+ function msSinceNavigationStart() {
218
+ if (performance.now) {
219
+ return floor(performance.now());
220
+ }
221
+ return now() - timing.navigationStart;
222
+ }
223
+ /**
224
+ * Returns the number of milliseconds since the current page was initialized. For SPAs, this is the
225
+ * time since the last LUX.init() call.
226
+ */
227
+ function msSincePageInit() {
228
+ var sinceNavigationStart = msSinceNavigationStart();
229
+ var startMark = getEntriesByName(START_MARK).pop();
230
+ if (startMark) {
231
+ return floor(sinceNavigationStart - startMark.startTime);
232
+ }
233
+ return sinceNavigationStart;
234
+ }
235
+
236
+ function padStart(str, length, char) {
237
+ while (str.length < length) {
238
+ str = char + str;
239
+ }
240
+ return str;
241
+ }
242
+
243
+ var VERSION = "4.0.20";
244
+ /**
245
+ * Returns the version of the script as a float to be stored in legacy systems that do not support
246
+ * string versions.
247
+ */
248
+ function versionAsFloat(ver) {
249
+ if (ver === void 0) { ver = VERSION; }
250
+ var parts = ver.split(".");
251
+ return parseFloat(parts[0] + "." + padStart(parts[1], 2, "0") + padStart(parts[2], 2, "0"));
252
+ }
253
+
254
+ var sendBeaconFallback = function (url, data) {
255
+ var xhr = new XMLHttpRequest();
256
+ xhr.open("POST", url, true);
257
+ xhr.setRequestHeader("content-type", "application/json");
258
+ xhr.send(String(data));
259
+ };
260
+ var sendBeacon = "sendBeacon" in navigator ? navigator.sendBeacon.bind(navigator) : sendBeaconFallback;
25
261
  /**
26
262
  * Fit an array of user timing delimited strings into a URL and return both the entries that fit and
27
263
  * the remaining entries that didn't fit.
@@ -38,31 +274,162 @@
38
274
  }
39
275
  return [beaconUtValues, remainingUtValues];
40
276
  }
277
+ var Beacon = /** @class */ (function () {
278
+ function Beacon(opts) {
279
+ var _this = this;
280
+ this.isRecording = true;
281
+ this.isSent = false;
282
+ this.maxMeasureTimeout = 0;
283
+ this.onBeforeSendCbs = [];
284
+ this.startTime = opts.startTime || getZeroTime();
285
+ this.config = opts.config;
286
+ this.logger = opts.logger;
287
+ this.customerId = opts.customerId;
288
+ this.sessionId = opts.sessionId;
289
+ this.pageId = opts.pageId;
290
+ this.metricData = {};
291
+ this.maxMeasureTimeout = window.setTimeout(function () {
292
+ _this.logger.logEvent(LogEvent.PostBeaconTimeoutReached);
293
+ _this.stopRecording();
294
+ _this.send();
295
+ }, this.config.maxMeasureTime);
296
+ this.logger.logEvent(LogEvent.PostBeaconInitialised);
297
+ }
298
+ Beacon.prototype.isBeingSampled = function () {
299
+ var bucket = parseInt(String(this.sessionId).slice(-2));
300
+ return bucket < this.config.samplerate;
301
+ };
302
+ Beacon.prototype.stopRecording = function () {
303
+ this.isRecording = false;
304
+ this.logger.logEvent(LogEvent.PostBeaconStopRecording);
305
+ };
306
+ Beacon.prototype.setMetricData = function (metric, data) {
307
+ if (!this.isRecording) {
308
+ this.logger.logEvent(LogEvent.PostBeaconMetricRejected, [metric]);
309
+ return;
310
+ }
311
+ this.metricData[metric] = data;
312
+ };
313
+ Beacon.prototype.hasMetricData = function () {
314
+ return Object.keys(this.metricData).length > 0;
315
+ };
316
+ Beacon.prototype.beaconUrl = function () {
317
+ return this.config.beaconUrlV2;
318
+ };
319
+ Beacon.prototype.onBeforeSend = function (cb) {
320
+ this.onBeforeSendCbs.push(cb);
321
+ };
322
+ Beacon.prototype.send = function () {
323
+ this.logger.logEvent(LogEvent.PostBeaconSendCalled);
324
+ if (!this.config.enablePostBeacon) {
325
+ this.logger.logEvent(LogEvent.PostBeaconDisabled);
326
+ return;
327
+ }
328
+ for (var _i = 0, _a = this.onBeforeSendCbs; _i < _a.length; _i++) {
329
+ var cb = _a[_i];
330
+ cb();
331
+ }
332
+ if (!this.isBeingSampled()) {
333
+ return;
334
+ }
335
+ if (!this.hasMetricData() && !this.config.allowEmptyPostBeacon) {
336
+ // TODO: This is only required while the new beacon is supplementary. Once it's the primary
337
+ // beacon, we should send it regardless of how much metric data it has.
338
+ this.logger.logEvent(LogEvent.PostBeaconCancelled);
339
+ return;
340
+ }
341
+ if (this.isSent) {
342
+ this.logger.logEvent(LogEvent.PostBeaconAlreadySent);
343
+ return;
344
+ }
345
+ // Only clear the max measure timeout if there's data to send.
346
+ clearTimeout(this.maxMeasureTimeout);
347
+ var beaconUrl = this.beaconUrl();
348
+ var payload = Object.assign({
349
+ customerId: this.customerId,
350
+ measureDuration: msSincePageInit(),
351
+ pageId: this.pageId,
352
+ scriptVersion: VERSION,
353
+ sessionId: this.sessionId,
354
+ startTime: this.startTime,
355
+ }, this.metricData);
356
+ try {
357
+ sendBeacon(beaconUrl, JSON.stringify(payload));
358
+ this.isSent = true;
359
+ this.logger.logEvent(LogEvent.PostBeaconSent, [beaconUrl, payload]);
360
+ }
361
+ catch (e) {
362
+ this.logger.logEvent(LogEvent.PostBeaconSendFailed, [e]);
363
+ }
364
+ };
365
+ return Beacon;
366
+ }());
367
+
368
+ // Wrapper to support older browsers (<= IE8)
369
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
370
+ function addListener(type, callback, useCapture) {
371
+ if (useCapture === void 0) { useCapture = false; }
372
+ if (addEventListener) {
373
+ addEventListener(type, callback, useCapture);
374
+ }
375
+ else if (window.attachEvent && true) {
376
+ window.attachEvent("on" + type, callback);
377
+ }
378
+ }
379
+ // Wrapper to support older browsers (<= IE8)
380
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
381
+ function removeListener(type, callback, useCapture) {
382
+ if (useCapture === void 0) { useCapture = false; }
383
+ if (removeEventListener) {
384
+ removeEventListener(type, callback, useCapture);
385
+ }
386
+ else if (window.detachEvent && true) {
387
+ window.detachEvent("on" + type, callback);
388
+ }
389
+ }
390
+
391
+ function onPageLoad(callback) {
392
+ if (document.readyState === "complete") {
393
+ // The onload event has already fired
394
+ callback();
395
+ }
396
+ else {
397
+ // Listen for the onload event and run the callback after a short delay
398
+ addListener("load", function () {
399
+ setTimeout(callback, 200);
400
+ });
401
+ }
402
+ }
41
403
 
42
404
  var luxOrigin = "https://lux.speedcurve.com";
43
405
  function fromObject(obj) {
44
406
  var autoMode = getProperty(obj, "auto", true);
45
407
  return {
408
+ allowEmptyPostBeacon: getProperty(obj, "allowEmptyPostBeacon", false),
46
409
  auto: autoMode,
47
410
  beaconUrl: getProperty(obj, "beaconUrl", luxOrigin + "/lux/"),
411
+ beaconUrlV2: getProperty(obj, "beaconUrlV2", "https://beacon.speedcurve.com/store"),
48
412
  conversions: getProperty(obj, "conversions"),
49
413
  cookieDomain: getProperty(obj, "cookieDomain"),
50
414
  customerid: getProperty(obj, "customerid"),
415
+ enablePostBeacon: getProperty(obj, "enablePostBeacon", true),
51
416
  errorBeaconUrl: getProperty(obj, "errorBeaconUrl", luxOrigin + "/error/"),
417
+ interactionBeaconDelay: getProperty(obj, "interactionBeaconDelay", 200),
52
418
  jspagelabel: getProperty(obj, "jspagelabel"),
53
419
  label: getProperty(obj, "label"),
54
420
  maxBeaconUrlLength: getProperty(obj, "maxBeaconUrlLength", 8190),
55
421
  maxBeaconUTEntries: getProperty(obj, "maxBeaconUTEntries", 20),
56
422
  maxErrors: getProperty(obj, "maxErrors", 5),
57
423
  maxMeasureTime: getProperty(obj, "maxMeasureTime", 60000),
424
+ measureUntil: getProperty(obj, "measureUntil", "onload"),
58
425
  minMeasureTime: getProperty(obj, "minMeasureTime", 0),
59
426
  newBeaconOnPageShow: getProperty(obj, "newBeaconOnPageShow", false),
427
+ pagegroups: getProperty(obj, "pagegroups"),
60
428
  samplerate: getProperty(obj, "samplerate", 100),
61
429
  sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", autoMode),
62
430
  serverTiming: getProperty(obj, "serverTiming"),
63
431
  trackErrors: getProperty(obj, "trackErrors", true),
64
432
  trackHiddenPages: getProperty(obj, "trackHiddenPages", false),
65
- pagegroups: getProperty(obj, "pagegroups"),
66
433
  };
67
434
  }
68
435
  function getProperty(obj, key, defaultValue) {
@@ -72,9 +439,7 @@
72
439
  return defaultValue;
73
440
  }
74
441
 
75
- var START_MARK = "LUX_start";
76
- var END_MARK = "LUX_end";
77
- var BOOLEAN_TRUE = "true";
442
+ var SESSION_COOKIE_NAME = "lux_uid";
78
443
 
79
444
  var customDataValues = {};
80
445
  var updatedCustomData = {};
@@ -117,93 +482,6 @@
117
482
  return encodeURIComponent(strings.join(","));
118
483
  }
119
484
 
120
- function floor(x) {
121
- return Math.floor(x);
122
- }
123
- var max = Math.max;
124
- var round = Math.round;
125
- /**
126
- * Clamp a number so that it is never less than 0
127
- */
128
- function clamp(x) {
129
- return max(0, x);
130
- }
131
- function sortNumeric(a, b) {
132
- return a - b;
133
- }
134
-
135
- function now() {
136
- return Date.now ? Date.now() : +new Date();
137
- }
138
-
139
- var scriptStartTime = now();
140
-
141
- var _a;
142
- // If the various performance APIs aren't available, we export an empty object to
143
- // prevent having to make regular typeof checks.
144
- var performance = window.performance || {};
145
- var timing = performance.timing || {
146
- // If performance.timing isn't available, we attempt to polyfill the navigationStart value.
147
- // Our first attempt is from LUX.ns, which is the time that the snippet execution began. If this
148
- // is not available, we fall back to the time that the current script execution began.
149
- navigationStart: ((_a = window.LUX) === null || _a === void 0 ? void 0 : _a.ns) || scriptStartTime,
150
- };
151
- function msSinceNavigationStart() {
152
- if (performance.now) {
153
- return floor(performance.now());
154
- }
155
- return now() - timing.navigationStart;
156
- }
157
- function navigationType() {
158
- if (performance.navigation && typeof performance.navigation.type !== "undefined") {
159
- return performance.navigation.type;
160
- }
161
- return "";
162
- }
163
- function getNavigationEntry() {
164
- var navEntries = getEntriesByType("navigation");
165
- if (navEntries.length) {
166
- var nativeEntry = navEntries[0];
167
- var entry_1 = {
168
- navigationStart: 0,
169
- activationStart: 0,
170
- };
171
- for (var key in nativeEntry) {
172
- entry_1[key] = nativeEntry[key];
173
- }
174
- return entry_1;
175
- }
176
- var navType = navigationType();
177
- var entry = {
178
- navigationStart: 0,
179
- activationStart: 0,
180
- startTime: 0,
181
- type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
182
- };
183
- if (true) {
184
- for (var key in timing) {
185
- if (typeof timing[key] === "number" && key !== "navigationStart") {
186
- entry[key] = floor(timing[key] - timing.navigationStart);
187
- }
188
- }
189
- }
190
- return entry;
191
- }
192
- /**
193
- * Simple wrapper around performance.getEntriesByType to provide fallbacks for
194
- * legacy browsers, and work around edge cases where undefined is returned instead
195
- * of an empty PerformanceEntryList.
196
- */
197
- function getEntriesByType(type) {
198
- if (typeof performance.getEntriesByType === "function") {
199
- var entries = performance.getEntriesByType(type);
200
- if (entries && entries.length) {
201
- return entries;
202
- }
203
- }
204
- return [];
205
- }
206
-
207
485
  function isVisible() {
208
486
  if (document.visibilityState) {
209
487
  return document.visibilityState === "visible";
@@ -264,37 +542,40 @@
264
542
  }
265
543
  var MAX_SELECTOR_LENGTH = 100;
266
544
  function getNodeSelector(node, selector) {
267
- if (selector === void 0) {
268
- selector = "";
269
- }
270
- try {
271
- if (selector && (node.nodeType === 9 || selector.length > MAX_SELECTOR_LENGTH || !node.parentNode)) {
272
- // Final selector.
273
- return selector;
274
- }
275
- var el = node;
276
- // Our first preference is to use the data-sctrack attribute from anywhere in the tree
277
- var trackId = getClosestScTrackAttribute(el);
278
- if (trackId) {
279
- return trackId;
280
- }
281
- if (el.id) {
282
- // Once we've found an element with ID we return the selector.
283
- return "#" + el.id + (selector ? ">" + selector : "");
284
- }
285
- else {
286
- // Otherwise attempt to get parent elements recursively
287
- var name_1 = el.nodeType === 1 ? el.nodeName.toLowerCase() : el.nodeName.toUpperCase();
288
- var classes = el.className ? "." + el.className.replace(/\s+/g, ".") : "";
289
- var currentSelector = name_1 + classes + (selector ? ">" + selector : "");
290
- if (el.parentNode) {
291
- var selectorWithParent = getNodeSelector(el.parentNode, currentSelector);
292
- if (selectorWithParent.length < MAX_SELECTOR_LENGTH) {
293
- return selectorWithParent;
545
+ if (selector === void 0) { selector = ""; }
546
+ try {
547
+ if (selector &&
548
+ (node.nodeType === 9 || selector.length > MAX_SELECTOR_LENGTH || !node.parentNode)) {
549
+ // Final selector.
550
+ return selector;
551
+ }
552
+ var el = node;
553
+ // Our first preference is to use the data-sctrack attribute from anywhere in the tree
554
+ var trackId = getClosestScTrackAttribute(el);
555
+ if (trackId) {
556
+ return trackId;
557
+ }
558
+ if (el.id) {
559
+ // Once we've found an element with ID we return the selector.
560
+ return "#" + el.id + (selector ? ">" + selector : "");
561
+ }
562
+ else if (el) {
563
+ // Otherwise attempt to get parent elements recursively
564
+ var name_1 = el.nodeType === 1 ? el.nodeName.toLowerCase() : el.nodeName.toUpperCase();
565
+ var classes = el.className ? "." + el.className.replace(/\s+/g, ".") : "";
566
+ // Remove classes until the selector is short enough
567
+ while ((name_1 + classes).length > MAX_SELECTOR_LENGTH) {
568
+ classes = classes.split(".").slice(0, -1).join(".");
294
569
  }
570
+ var currentSelector = name_1 + classes + (selector ? ">" + selector : "");
571
+ if (el.parentNode) {
572
+ var selectorWithParent = getNodeSelector(el.parentNode, currentSelector);
573
+ if (selectorWithParent.length < MAX_SELECTOR_LENGTH) {
574
+ return selectorWithParent;
575
+ }
576
+ }
577
+ return currentSelector;
295
578
  }
296
- return currentSelector;
297
- }
298
579
  }
299
580
  catch (error) {
300
581
  // Do nothing.
@@ -320,72 +601,40 @@
320
601
  return flags | flag;
321
602
  }
322
603
 
323
- var LogEvent = {
324
- // Internal events
325
- EvaluationStart: 1,
326
- EvaluationEnd: 2,
327
- InitCalled: 3,
328
- MarkCalled: 4,
329
- MeasureCalled: 5,
330
- AddDataCalled: 6,
331
- SendCalled: 7,
332
- ForceSampleCalled: 8,
333
- DataCollectionStart: 9,
334
- UnloadHandlerTriggered: 10,
335
- OnloadHandlerTriggered: 11,
336
- MarkLoadTimeCalled: 12,
337
- SendCancelledPageHidden: 13,
338
- // Data collection events
339
- SessionIsSampled: 21,
340
- SessionIsNotSampled: 22,
341
- MainBeaconSent: 23,
342
- UserTimingBeaconSent: 24,
343
- InteractionBeaconSent: 25,
344
- CustomDataBeaconSent: 26,
345
- // Metric information
346
- NavigationStart: 41,
347
- PerformanceEntryReceived: 42,
348
- PerformanceEntryProcessed: 43,
349
- // Errors
350
- PerformanceObserverError: 51,
351
- InputEventPermissionError: 52,
352
- InnerHtmlAccessError: 53,
353
- EventTargetAccessError: 54,
354
- CookieReadError: 55,
355
- CookieSetError: 56,
356
- PageLabelEvaluationError: 57,
357
- // Browser support messages
358
- NavTimingNotSupported: 71,
359
- PaintTimingNotSupported: 72,
360
- };
361
- var Logger = /** @class */ (function () {
362
- function Logger() {
363
- this.events = [];
364
- }
365
- Logger.prototype.logEvent = function (event, args) {
366
- if (args === void 0) { args = []; }
367
- this.events.push([now(), event, args]);
368
- };
369
- Logger.prototype.getEvents = function () {
370
- return this.events;
371
- };
372
- return Logger;
373
- })();
374
-
375
604
  var sessionValue = 0;
376
605
  var sessionEntries = [];
606
+ var sessionAttributions = [];
607
+ var largestEntry;
377
608
  var maximumSessionValue = 0;
378
- function addEntry$2(entry) {
609
+ function processEntry$2(entry) {
379
610
  if (!entry.hadRecentInput) {
380
611
  var firstEntry = sessionEntries[0];
381
612
  var latestEntry = sessionEntries[sessionEntries.length - 1];
382
- if (sessionEntries.length && (entry.startTime - latestEntry.startTime >= 1000 || entry.startTime - firstEntry.startTime >= 5000)) {
383
- sessionValue = entry.value;
384
- sessionEntries = [entry];
613
+ var sources = entry.sources
614
+ ? entry.sources
615
+ .filter(function (source) { return source.node; })
616
+ .map(function (source) { return ({
617
+ value: entry.value,
618
+ startTime: processTimeMetric(entry.startTime),
619
+ elementSelector: getNodeSelector(source.node),
620
+ elementType: source.node.nodeName,
621
+ }); })
622
+ : [];
623
+ if (sessionEntries.length &&
624
+ (entry.startTime - latestEntry.startTime >= 1000 ||
625
+ entry.startTime - firstEntry.startTime >= 5000)) {
626
+ sessionValue = entry.value;
627
+ sessionEntries = [entry];
628
+ sessionAttributions = sources;
629
+ largestEntry = entry;
385
630
  }
386
631
  else {
387
632
  sessionValue += entry.value;
388
633
  sessionEntries.push(entry);
634
+ sessionAttributions = sessionAttributions.concat(sources);
635
+ if (!largestEntry || entry.value > largestEntry.value) {
636
+ largestEntry = entry;
637
+ }
389
638
  }
390
639
  maximumSessionValue = max(maximumSessionValue, sessionValue);
391
640
  }
@@ -394,9 +643,20 @@
394
643
  sessionValue = 0;
395
644
  sessionEntries = [];
396
645
  maximumSessionValue = 0;
646
+ largestEntry = undefined;
397
647
  }
398
- function getCLS() {
399
- return maximumSessionValue;
648
+ function getData$2() {
649
+ return {
650
+ value: maximumSessionValue,
651
+ startTime: sessionEntries[0] ? processTimeMetric(sessionEntries[0].startTime) : null,
652
+ largestEntry: largestEntry
653
+ ? {
654
+ value: largestEntry.value,
655
+ startTime: processTimeMetric(largestEntry.startTime),
656
+ }
657
+ : null,
658
+ sources: sessionAttributions.length ? sessionAttributions : null,
659
+ };
400
660
  }
401
661
 
402
662
  /**
@@ -416,23 +676,27 @@
416
676
  slowestEntries = [];
417
677
  slowestEntriesMap = {};
418
678
  }
419
- function addEntry$1(entry) {
679
+ function processEntry$1(entry) {
420
680
  if (entry.interactionId || (entry.entryType === "first-input" && !entryExists(entry))) {
421
- var duration = entry.duration,
422
- startTime = entry.startTime,
423
- interactionId = entry.interactionId,
424
- processingStart = entry.processingStart,
425
- processingEnd = entry.processingEnd,
426
- target = entry.target;
681
+ var duration = entry.duration, startTime = entry.startTime, interactionId = entry.interactionId, name_1 = entry.name, processingStart = entry.processingStart, processingEnd = entry.processingEnd, target = entry.target;
682
+ var processingTime = processingEnd - processingStart;
427
683
  var existingEntry = slowestEntriesMap[interactionId];
428
684
  var selector = target ? getNodeSelector(target) : null;
429
685
  if (existingEntry) {
430
- if (existingEntry.duration < duration) {
686
+ var longerDuration = duration > existingEntry.duration;
687
+ var sameWithLongerProcessingTime = duration === existingEntry.duration && processingTime > existingEntry.processingTime;
688
+ if (longerDuration || sameWithLongerProcessingTime) {
689
+ // Only replace an existing interation if the duration is longer, or if the duration is the
690
+ // same but the processing time is longer. The logic around this is that the interaction with
691
+ // longer processing time is likely to be the event that actually had a handler.
431
692
  existingEntry.duration = duration;
432
- existingEntry.startTime = startTime;
433
- existingEntry.processingStart = processingStart;
693
+ existingEntry.name = name_1;
434
694
  existingEntry.processingEnd = processingEnd;
695
+ existingEntry.processingStart = processingStart;
696
+ existingEntry.processingTime = processingTime;
435
697
  existingEntry.selector = selector;
698
+ existingEntry.startTime = startTime;
699
+ existingEntry.target = target;
436
700
  }
437
701
  }
438
702
  else {
@@ -440,10 +704,13 @@
440
704
  slowestEntriesMap[interactionId] = {
441
705
  duration: duration,
442
706
  interactionId: interactionId,
443
- startTime: startTime,
444
- processingStart: processingStart,
707
+ name: name_1,
445
708
  processingEnd: processingEnd,
709
+ processingStart: processingStart,
710
+ processingTime: processingTime,
446
711
  selector: selector,
712
+ startTime: startTime,
713
+ target: target,
447
714
  };
448
715
  slowestEntries.push(slowestEntriesMap[interactionId]);
449
716
  }
@@ -465,6 +732,29 @@
465
732
  var index = Math.min(slowestEntries.length - 1, Math.floor(getInteractionCount() / 50));
466
733
  return slowestEntries[index];
467
734
  }
735
+ function getData$1() {
736
+ var _a;
737
+ var interaction = getHighPercentileInteraction();
738
+ if (!interaction) {
739
+ return undefined;
740
+ }
741
+ return {
742
+ value: interaction.duration,
743
+ startTime: processTimeMetric(interaction.startTime),
744
+ subParts: {
745
+ inputDelay: clamp(floor(interaction.processingStart - interaction.startTime)),
746
+ processingTime: clamp(floor(interaction.processingTime)),
747
+ presentationDelay: clamp(floor(interaction.startTime + interaction.duration - interaction.processingEnd)),
748
+ },
749
+ attribution: interaction.selector
750
+ ? {
751
+ elementSelector: interaction.selector,
752
+ elementType: ((_a = interaction.target) === null || _a === void 0 ? void 0 : _a.nodeName) || "",
753
+ eventType: interaction.name,
754
+ }
755
+ : null,
756
+ };
757
+ }
468
758
  function getInteractionCount() {
469
759
  if ("interactionCount" in performance) {
470
760
  return performance.interactionCount;
@@ -472,6 +762,47 @@
472
762
  return interactionCountEstimate;
473
763
  }
474
764
 
765
+ var lcpEntry;
766
+ function processEntry(entry) {
767
+ if (!lcpEntry || entry.startTime > lcpEntry.startTime) {
768
+ lcpEntry = entry;
769
+ }
770
+ }
771
+ function getData() {
772
+ if (!lcpEntry) {
773
+ return undefined;
774
+ }
775
+ var subParts = null;
776
+ if (lcpEntry.url) {
777
+ var lcpResource = getEntriesByType("resource").find(function (resource) { return resource.name === lcpEntry.url; });
778
+ if (lcpResource) {
779
+ var navEntry = getNavigationEntry();
780
+ var responseStart = navEntry.responseStart || timing.responseStart;
781
+ var activationStart = navEntry.activationStart;
782
+ var ttfb = max(0, responseStart - activationStart);
783
+ var lcpStartTime = lcpResource.startTime;
784
+ var lcpRequestStart = (lcpResource.requestStart || lcpStartTime) - activationStart;
785
+ var lcpResponseEnd = max(lcpRequestStart, lcpResource.responseEnd - activationStart);
786
+ var lcpRenderTime = max(lcpResponseEnd, lcpStartTime - activationStart);
787
+ subParts = {
788
+ resourceLoadDelay: clamp(floor(lcpRequestStart - ttfb)),
789
+ resourceLoadTime: clamp(floor(lcpResponseEnd - lcpRequestStart)),
790
+ elementRenderDelay: clamp(floor(lcpRenderTime - lcpResponseEnd)),
791
+ };
792
+ }
793
+ }
794
+ return {
795
+ value: processTimeMetric(lcpEntry.startTime),
796
+ subParts: subParts,
797
+ attribution: lcpEntry.element
798
+ ? {
799
+ elementSelector: getNodeSelector(lcpEntry.element),
800
+ elementType: lcpEntry.element.nodeName,
801
+ }
802
+ : null,
803
+ };
804
+ }
805
+
475
806
  var ALL_ENTRIES = [];
476
807
  function observe(type, callback, options) {
477
808
  if (typeof PerformanceObserver === "function" &&
@@ -577,10 +908,9 @@
577
908
  // -------------------------------------------------------------------------
578
909
  /// End
579
910
  // -------------------------------------------------------------------------
580
- var SCRIPT_VERSION = "314";
581
911
  var logger = new Logger();
582
912
  var globalConfig = fromObject(LUX);
583
- logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION, globalConfig]);
913
+ logger.logEvent(LogEvent.EvaluationStart, [VERSION, JSON.stringify(globalConfig)]);
584
914
  // Variable aliases that allow the minifier to reduce file size.
585
915
  var document = window.document;
586
916
  var addEventListener = window.addEventListener;
@@ -605,7 +935,7 @@
605
935
  new Image().src =
606
936
  globalConfig.errorBeaconUrl +
607
937
  "?v=" +
608
- SCRIPT_VERSION +
938
+ versionAsFloat() +
609
939
  "&id=" +
610
940
  getCustomerId() +
611
941
  "&fn=" +
@@ -627,6 +957,30 @@
627
957
  }
628
958
  }
629
959
  addEventListener("error", errorHandler);
960
+ // Bitmask of flags for this session & page
961
+ var gFlags = 0;
962
+ var gaMarks = [];
963
+ var gaMeasures = [];
964
+ var ghIx = {}; // hash for Interaction Metrics (scroll, click, keyboard)
965
+ var gbLuxSent = 0; // have we sent the LUX data? (avoid sending twice in unload)
966
+ var gbNavSent = 0; // have we sent the Nav Timing beacon yet? (avoid sending twice for SPA)
967
+ var gbIxSent = 0; // have we sent the IX data? (avoid sending twice for SPA)
968
+ var gbFirstPV = 1; // this is the first page view (vs. a SPA "soft nav")
969
+ var gSessionTimeout = 30 * 60; // number of seconds after which we consider a session to have "timed out" (used for calculating bouncerate)
970
+ var gSyncId = createSyncId(); // if we send multiple beacons, use this to sync them (eg, LUX & IX) (also called "luxid")
971
+ var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
972
+ var gCustomDataTimeout; // setTimeout timer for sending a Custom data beacon after onload
973
+ var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
974
+ var initPostBeacon = function () {
975
+ return new Beacon({
976
+ config: globalConfig,
977
+ logger: logger,
978
+ customerId: getCustomerId(),
979
+ sessionId: gUid,
980
+ pageId: gSyncId,
981
+ });
982
+ };
983
+ var beacon = initPostBeacon();
630
984
  var logEntry = function (entry) {
631
985
  logger.logEvent(LogEvent.PerformanceEntryReceived, [entry]);
632
986
  };
@@ -635,74 +989,67 @@
635
989
  addEntry(entry);
636
990
  logEntry(entry);
637
991
  };
638
- // Before long tasks were buffered, we added a PerformanceObserver to the lux.js snippet to capture
639
- // any long tasks that occurred before the full script was loaded. To deal with this, we process
640
- // all of the snippet long tasks, and we check for double-ups in the new PerformanceObserver.
641
- var snippetLongTasks = typeof window.LUX_al === "object" ? window.LUX_al : [];
642
- snippetLongTasks.forEach(processAndLogEntry);
643
992
  try {
644
- observe("longtask", function (entry) {
645
- if (ALL_ENTRIES.indexOf(entry) === -1) {
646
- processAndLogEntry(entry);
647
- }
648
- });
993
+ observe("longtask", processAndLogEntry);
649
994
  observe("largest-contentful-paint", processAndLogEntry);
650
995
  observe("element", processAndLogEntry);
651
996
  observe("paint", processAndLogEntry);
997
+ observe("largest-contentful-paint", function (entry) {
998
+ // Process the LCP entry for the legacy beacon
999
+ processAndLogEntry(entry);
1000
+ // Process the LCP entry for the new beacon
1001
+ processEntry(entry);
1002
+ beacon.setMetricData("lcp", getData());
1003
+ });
652
1004
  observe("layout-shift", function (entry) {
653
- addEntry$2(entry);
1005
+ processEntry$2(entry);
1006
+ beacon.setMetricData("cls", getData$2());
654
1007
  logEntry(entry);
655
1008
  });
1009
+ var handleINPEntry_1 = function (entry) {
1010
+ processEntry$1(entry);
1011
+ var data = getData$1();
1012
+ if (data) {
1013
+ beacon.setMetricData("inp", data);
1014
+ }
1015
+ };
656
1016
  observe("first-input", function (entry) {
1017
+ logEntry(entry);
657
1018
  var entryTime = entry.processingStart - entry.startTime;
658
1019
  if (!gFirstInputDelay || gFirstInputDelay < entryTime) {
659
1020
  gFirstInputDelay = floor(entryTime);
660
1021
  }
661
1022
  // Allow first-input events to be considered for INP
662
- addEntry$1(entry);
1023
+ handleINPEntry_1(entry);
663
1024
  });
664
- // TODO: Add { durationThreshold: 40 } once performance.interactionCount is widely supported.
1025
+ // TODO: Set durationThreshold to 40 once performance.interactionCount is widely supported.
665
1026
  // Right now we have to count every event to get the total interaction count so that we can
666
1027
  // estimate a high percentile value for INP.
667
- observe("event", addEntry$1);
1028
+ observe("event", function (entry) {
1029
+ handleINPEntry_1(entry);
1030
+ // It's useful to log the interactionId, but it is not serialised by default. Annoyingly, we
1031
+ // need to manually serialize our own object with the keys we want.
1032
+ logEntry({
1033
+ interactionId: entry.interactionId,
1034
+ name: entry.name,
1035
+ entryType: entry.entryType,
1036
+ startTime: entry.startTime,
1037
+ duration: entry.duration,
1038
+ processingStart: entry.processingStart,
1039
+ processingEnd: entry.processingEnd,
1040
+ });
1041
+ }, { durationThreshold: 0 });
668
1042
  }
669
1043
  catch (e) {
670
1044
  logger.logEvent(LogEvent.PerformanceObserverError, [e]);
671
1045
  }
672
- // Bitmask of flags for this session & page
673
- var gFlags = 0;
674
- var gaMarks = [];
675
- var gaMeasures = [];
676
- var ghIx = {}; // hash for Interaction Metrics (scroll, click, keyboard)
677
- var gbLuxSent = 0; // have we sent the LUX data? (avoid sending twice in unload)
678
- var gbNavSent = 0; // have we sent the Nav Timing beacon yet? (avoid sending twice for SPA)
679
- var gbIxSent = 0; // have we sent the IX data? (avoid sending twice for SPA)
680
- var gbFirstPV = 1; // this is the first page view (vs. a SPA "soft nav")
681
- var gSessionTimeout = 30 * 60; // number of seconds after which we consider a session to have "timed out" (used for calculating bouncerate)
682
- var gSyncId = createSyncId(); // if we send multiple beacons, use this to sync them (eg, LUX & IX) (also called "luxid")
683
- var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
684
- var gCustomDataTimeout; // setTimeout timer for sending a Custom data beacon after onload
685
- var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
686
- var pageRestoreTime; // ms since navigationStart representing when the page was restored from the bfcache
687
- /**
688
- * To measure the way a user experienced a metric, we measure metrics relative to the time the user
689
- * started viewing the page. On prerendered pages, this is activationStart. On bfcache restores, this
690
- * is the page restore time. On all other pages this value will be zero.
691
- */
692
- var getZeroTime = function () {
693
- var _a;
694
- return max(pageRestoreTime || 0, getNavigationEntry().activationStart, ((_a = _getMark(START_MARK)) === null || _a === void 0 ? void 0 : _a.startTime) || 0);
695
- };
696
- /**
697
- * Most time-based metrics that LUX reports should be relative to the "zero" marker, rounded down
698
- * to the nearest unit so as not to report times in the future, and clamped to zero.
699
- */
700
- var processTimeMetric = function (value) { return clamp(floor(value - getZeroTime())); };
701
1046
  /**
702
1047
  * Some values should only be reported if they are non-zero. The exception to this is when the page
703
1048
  * was prerendered or restored from BF cache
704
1049
  */
705
- var shouldReportValue = function (value) { return value > 0 || pageRestoreTime || wasPrerendered(); };
1050
+ var shouldReportValue = function (value) {
1051
+ return value > 0 || getPageRestoreTime() || wasPrerendered();
1052
+ };
706
1053
  if (_sample()) {
707
1054
  logger.logEvent(LogEvent.SessionIsSampled, [globalConfig.samplerate]);
708
1055
  }
@@ -764,7 +1111,7 @@
764
1111
  return;
765
1112
  }
766
1113
  if (bCancelable) {
767
- var now_1 = _now(true);
1114
+ var now_1 = msSinceNavigationStart();
768
1115
  var eventTimeStamp = evt.timeStamp;
769
1116
  if (eventTimeStamp > 1520000000) {
770
1117
  // If the event timeStamp is an epoch time instead of a time relative to NavigationStart,
@@ -791,23 +1138,6 @@
791
1138
  addEventListener(eventType, onInput, ghListenerOptions);
792
1139
  });
793
1140
  ////////////////////// FID END
794
- /**
795
- * Returns the time elapsed (in ms) since navigationStart. For SPAs, returns
796
- * the time elapsed since the last LUX.init call.
797
- *
798
- * When `absolute = true` the time is always relative to navigationStart, even
799
- * in SPAs.
800
- */
801
- function _now(absolute) {
802
- var sinceNavigationStart = msSinceNavigationStart();
803
- var startMark = _getMark(START_MARK);
804
- // For SPA page views, we use our internal mark as a reference point
805
- if (startMark && !absolute) {
806
- return floor(sinceNavigationStart - startMark.startTime);
807
- }
808
- // For "regular" page views, we can use performance.now() if it's available...
809
- return sinceNavigationStart;
810
- }
811
1141
  // This is a wrapper around performance.mark that falls back to a polyfill when the User Timing
812
1142
  // API isn't supported.
813
1143
  function _mark() {
@@ -825,7 +1155,7 @@
825
1155
  if (true) {
826
1156
  var name_1 = args[0];
827
1157
  var detail = ((_a = args[1]) === null || _a === void 0 ? void 0 : _a.detail) || null;
828
- var startTime = ((_b = args[1]) === null || _b === void 0 ? void 0 : _b.startTime) || _now();
1158
+ var startTime = ((_b = args[1]) === null || _b === void 0 ? void 0 : _b.startTime) || msSincePageInit();
829
1159
  var entry = {
830
1160
  entryType: "mark",
831
1161
  duration: 0,
@@ -888,7 +1218,7 @@
888
1218
  if (true) {
889
1219
  var navEntry = getNavigationEntry();
890
1220
  var startTime = typeof startMarkName === "number" ? startMarkName : 0;
891
- var endTime = typeof endMarkName === "number" ? endMarkName : _now();
1221
+ var endTime = typeof endMarkName === "number" ? endMarkName : msSincePageInit();
892
1222
  var throwError = function (missingMark) {
893
1223
  throw new DOMException("Failed to execute 'measure' on 'Performance': The mark '" +
894
1224
  missingMark +
@@ -1132,13 +1462,12 @@
1132
1462
  var median = arrayMedian(aValues);
1133
1463
  return { count: count, median: median, max: max, fci: fci };
1134
1464
  }
1135
- function getCLS$1() {
1465
+ function getCLS() {
1136
1466
  if (!("LayoutShift" in self)) {
1137
1467
  return undefined;
1138
1468
  }
1139
- // The DCLS column in Redshift is REAL (FLOAT4) which stores a maximum
1140
- // of 6 significant digits.
1141
- return getCLS().toFixed(6);
1469
+ var clsData = getData$2();
1470
+ return clsData.value.toFixed(6);
1142
1471
  }
1143
1472
  // Return the median value from an array of integers.
1144
1473
  function arrayMedian(aValues) {
@@ -1276,6 +1605,7 @@
1276
1605
  reset();
1277
1606
  nErrors = 0;
1278
1607
  gFirstInputDelay = undefined;
1608
+ beacon = initPostBeacon();
1279
1609
  // Clear flags then set the flag that init was called (ie, this is a SPA).
1280
1610
  if (clearFlags) {
1281
1611
  gFlags = 0;
@@ -1298,10 +1628,13 @@
1298
1628
  var num = 0;
1299
1629
  for (var i = 0, len = aElems.length; i < len; i++) {
1300
1630
  var e = aElems[i];
1301
- if (e.src && !e.async && !e.defer && 0 !== (e.compareDocumentPosition(lastViewportElem) & 4)) {
1302
- // If the script has a SRC and async is false and it occurs BEFORE the last viewport element,
1303
- // then increment the counter.
1304
- num++;
1631
+ if (e.src &&
1632
+ !e.async &&
1633
+ !e.defer &&
1634
+ 0 !== (e.compareDocumentPosition(lastViewportElem) & 4)) {
1635
+ // If the script has a SRC and async is false and it occurs BEFORE the last viewport element,
1636
+ // then increment the counter.
1637
+ num++;
1305
1638
  }
1306
1639
  }
1307
1640
  return num;
@@ -1318,7 +1651,7 @@
1318
1651
  e.onloadcssdefined ||
1319
1652
  "print" === e.media ||
1320
1653
  "style" === e.as ||
1321
- (typeof e.onload === "function" && e.media === "all"));
1654
+ (typeof e.onload === "function" && e.media === "all")) ;
1322
1655
  else {
1323
1656
  nBlocking++;
1324
1657
  }
@@ -1384,9 +1717,9 @@
1384
1717
  var ns = timing.navigationStart;
1385
1718
  var startMark = _getMark(START_MARK);
1386
1719
  var endMark = _getMark(END_MARK);
1387
- if (startMark && endMark && !pageRestoreTime) {
1720
+ if (startMark && endMark && !getPageRestoreTime()) {
1388
1721
  // This is a SPA page view, so send the SPA marks & measures instead of Nav Timing.
1389
- // Note: pageRestoreTime indicates this was a bfcache restore, which we don't want to treat as a SPA.
1722
+ // Note: getPageRestoreTime() indicates this was a bfcache restore, which we don't want to treat as a SPA.
1390
1723
  var start = floor(startMark.startTime); // the start mark is "zero"
1391
1724
  ns += start; // "navigationStart" for a SPA is the real navigationStart plus the start mark
1392
1725
  var end = floor(endMark.startTime) - start; // delta from start mark
@@ -1423,7 +1756,7 @@
1423
1756
  };
1424
1757
  var loadEventStartStr = prefixNTValue("loadEventStart", "ls", true);
1425
1758
  var loadEventEndStr = prefixNTValue("loadEventEnd", "le", true);
1426
- if (pageRestoreTime && startMark && endMark) {
1759
+ if (getPageRestoreTime() && startMark && endMark) {
1427
1760
  // For bfcache restores, we set the load time to the time it took for the page to be restored.
1428
1761
  var loadTime = floor(endMark.startTime - startMark.startTime);
1429
1762
  loadEventStartStr = "ls" + loadTime;
@@ -1434,8 +1767,8 @@
1434
1767
  s = [
1435
1768
  ns,
1436
1769
  "as" + clamp(navEntry_1.activationStart),
1437
- redirect && !pageRestoreTime ? prefixNTValue("redirectStart", "rs") : "",
1438
- redirect && !pageRestoreTime ? prefixNTValue("redirectEnd", "re") : "",
1770
+ redirect && !getPageRestoreTime() ? prefixNTValue("redirectStart", "rs") : "",
1771
+ redirect && !getPageRestoreTime() ? prefixNTValue("redirectEnd", "re") : "",
1439
1772
  prefixNTValue("fetchStart", "fs"),
1440
1773
  prefixNTValue("domainLookupStart", "ds"),
1441
1774
  prefixNTValue("domainLookupEnd", "de"),
@@ -1542,9 +1875,9 @@
1542
1875
  return [
1543
1876
  "&INP=" + details.duration,
1544
1877
  details.selector ? "&INPs=" + encodeURIComponent(details.selector) : "",
1545
- "&INPt=" + clamp(floor(details.startTime)),
1878
+ "&INPt=" + floor(details.startTime),
1546
1879
  "&INPi=" + clamp(floor(details.processingStart - details.startTime)),
1547
- "&INPp=" + clamp(floor(details.processingEnd - details.processingStart)),
1880
+ "&INPp=" + clamp(floor(details.processingTime)),
1548
1881
  "&INPd=" + clamp(floor(details.startTime + details.duration - details.processingEnd)),
1549
1882
  ].join("");
1550
1883
  }
@@ -1657,7 +1990,12 @@
1657
1990
  var vw = document.documentElement.clientWidth;
1658
1991
  // Return true if the top-left corner is in the viewport and it has width & height.
1659
1992
  var lt = findPos(e);
1660
- return lt[0] >= 0 && lt[1] >= 0 && lt[0] < vw && lt[1] < vh && e.offsetWidth > 0 && e.offsetHeight > 0;
1993
+ return (lt[0] >= 0 &&
1994
+ lt[1] >= 0 &&
1995
+ lt[0] < vw &&
1996
+ lt[1] < vh &&
1997
+ e.offsetWidth > 0 &&
1998
+ e.offsetHeight > 0);
1661
1999
  }
1662
2000
  // Return an array containing the top & left coordinates of the element.
1663
2001
  // from http://www.quirksmode.org/js/findpos.html
@@ -1687,7 +2025,7 @@
1687
2025
  gMaxMeasureTimeout = setTimeout(function () {
1688
2026
  gFlags = addFlag(gFlags, Flags.BeaconSentAfterTimeout);
1689
2027
  _sendLux();
1690
- }, globalConfig.maxMeasureTime - _now());
2028
+ }, globalConfig.maxMeasureTime - msSincePageInit());
1691
2029
  }
1692
2030
  function clearMaxMeasureTimeout() {
1693
2031
  if (gMaxMeasureTimeout) {
@@ -1696,7 +2034,7 @@
1696
2034
  }
1697
2035
  function _getBeaconUrl(customData) {
1698
2036
  var queryParams = [
1699
- "v=" + SCRIPT_VERSION,
2037
+ "v=" + versionAsFloat(),
1700
2038
  "id=" + getCustomerId(),
1701
2039
  "sid=" + gSyncId,
1702
2040
  "uid=" + gUid,
@@ -1727,7 +2065,7 @@
1727
2065
  !gSyncId ||
1728
2066
  !_sample() || // OUTSIDE the sampled range
1729
2067
  gbLuxSent // LUX data already sent
1730
- ) {
2068
+ ) {
1731
2069
  return;
1732
2070
  }
1733
2071
  logger.logEvent(LogEvent.DataCollectionStart);
@@ -1752,7 +2090,7 @@
1752
2090
  }
1753
2091
  var sET = elementTimingValues(); // Element Timing data
1754
2092
  var sCPU = cpuTimes();
1755
- var CLS = getCLS$1();
2093
+ var CLS = getCLS();
1756
2094
  var sLuxjs = selfLoading();
1757
2095
  if (!isVisible()) {
1758
2096
  gFlags = addFlag(gFlags, Flags.VisibilityStateNotVisible);
@@ -1839,7 +2177,7 @@
1839
2177
  gbIxSent = sIx ? 1 : 0;
1840
2178
  // Send other beacons for JUST User Timing.
1841
2179
  while (remainingUtValues.length) {
1842
- (_a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl)), (beaconUtValues = _a[0]), (remainingUtValues = _a[1]);
2180
+ _a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl), beaconUtValues = _a[0], remainingUtValues = _a[1];
1843
2181
  var utBeaconUrl = baseUrl + "&UT=" + beaconUtValues.join(",");
1844
2182
  logger.logEvent(LogEvent.UserTimingBeaconSent, [utBeaconUrl]);
1845
2183
  _sendBeacon(utBeaconUrl);
@@ -1848,7 +2186,7 @@
1848
2186
  var ixTimerId;
1849
2187
  function _sendIxAfterDelay() {
1850
2188
  clearTimeout(ixTimerId);
1851
- ixTimerId = setTimeout(_sendIx, 100);
2189
+ ixTimerId = setTimeout(_sendIx, globalConfig.interactionBeaconDelay);
1852
2190
  }
1853
2191
  // Beacon back the IX data separately (need to sync with LUX beacon on the backend).
1854
2192
  function _sendIx() {
@@ -1906,7 +2244,7 @@
1906
2244
  // Note for scroll input we don't remove the handlers or send the IX beacon because we want to
1907
2245
  // capture click and key events as well, since these are typically more important than scrolls.
1908
2246
  if (typeof ghIx["s"] === "undefined") {
1909
- ghIx["s"] = _now();
2247
+ ghIx["s"] = msSincePageInit();
1910
2248
  }
1911
2249
  }
1912
2250
  function _keyHandler(e) {
@@ -1924,7 +2262,7 @@
1924
2262
  return;
1925
2263
  }
1926
2264
  if (typeof ghIx["k"] === "undefined") {
1927
- ghIx["k"] = _now();
2265
+ ghIx["k"] = msSincePageInit();
1928
2266
  if (e && e.target instanceof Element) {
1929
2267
  var trackId = getNodeSelector(e.target);
1930
2268
  if (trackId) {
@@ -1940,7 +2278,7 @@
1940
2278
  }
1941
2279
  function _clickHandler(e) {
1942
2280
  if (typeof ghIx["c"] === "undefined") {
1943
- ghIx["c"] = _now();
2281
+ ghIx["c"] = msSincePageInit();
1944
2282
  // Only one interaction type is recorded. Scrolls are considered less important, so delete
1945
2283
  // any scroll times if they exist.
1946
2284
  delete ghIx["s"];
@@ -1969,34 +2307,13 @@
1969
2307
  }
1970
2308
  _removeIxHandlers();
1971
2309
  }
1972
- // Wrapper to support older browsers (<= IE8)
1973
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1974
- function addListener(type, callback, useCapture) {
1975
- if (useCapture === void 0) { useCapture = false; }
1976
- if (addEventListener) {
1977
- addEventListener(type, callback, useCapture);
1978
- }
1979
- else if (window.attachEvent && true) {
1980
- window.attachEvent("on" + type, callback);
1981
- }
1982
- }
1983
- // Wrapper to support older browsers (<= IE8)
1984
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1985
- function removeListener(type, callback, useCapture) {
1986
- if (useCapture === void 0) { useCapture = false; }
1987
- if (removeEventListener) {
1988
- removeEventListener(type, callback, useCapture);
1989
- }
1990
- else if (window.detachEvent && true) {
1991
- window.detachEvent("on" + type, callback);
1992
- }
1993
- }
1994
2310
  function _addUnloadHandlers() {
1995
2311
  var onunload = function () {
1996
2312
  gFlags = addFlag(gFlags, Flags.BeaconSentFromUnloadHandler);
1997
2313
  logger.logEvent(LogEvent.UnloadHandlerTriggered);
1998
2314
  _sendLux();
1999
2315
  _sendIx();
2316
+ beacon.send();
2000
2317
  };
2001
2318
  // As well as visibilitychange, we also listen for pagehide. This is really only for browsers
2002
2319
  // with buggy visibilitychange implementations. For much older browsers that don't support
@@ -2033,14 +2350,14 @@
2033
2350
  // "00" matches all sample rates
2034
2351
  return Number(new Date()) + "00000";
2035
2352
  }
2036
- return Number(new Date()) + _padLeft(String(round(100000 * Math.random())), "00000");
2353
+ return Number(new Date()) + padStart(String(round(100000 * Math.random())), 5, "0");
2037
2354
  }
2038
2355
  // Unique ID (also known as Session ID)
2039
2356
  // We use this to track all the page views in a single user session.
2040
2357
  // If there is NOT a UID then set it to the new value (which is the same as the "sync ID" for this page).
2041
2358
  // Refresh its expiration date and return its value.
2042
2359
  function refreshUniqueId(newValue) {
2043
- var uid = _getCookie("lux_uid");
2360
+ var uid = _getCookie(SESSION_COOKIE_NAME);
2044
2361
  if (!uid || uid.length < 11) {
2045
2362
  uid = newValue;
2046
2363
  }
@@ -2058,7 +2375,7 @@
2058
2375
  return uid;
2059
2376
  }
2060
2377
  function setUniqueId(uid) {
2061
- _setCookie("lux_uid", uid, gSessionTimeout);
2378
+ _setCookie(SESSION_COOKIE_NAME, uid, gSessionTimeout);
2062
2379
  return uid;
2063
2380
  }
2064
2381
  // We use gUid (session ID) to do sampling. We make this available to customers so
@@ -2120,18 +2437,13 @@
2120
2437
  "=" +
2121
2438
  escape(value) +
2122
2439
  (seconds ? "; max-age=" + seconds : "") +
2123
- (globalConfig.cookieDomain ? "; domain=" +
2124
- globalConfig.cookieDomain : "") +
2440
+ (globalConfig.cookieDomain ? "; domain=" + globalConfig.cookieDomain : "") +
2125
2441
  "; path=/; SameSite=Lax";
2126
2442
  }
2127
2443
  catch (e) {
2128
2444
  logger.logEvent(LogEvent.CookieSetError);
2129
2445
  }
2130
2446
  }
2131
- // "padding" MUST be the length of the resulting string, eg, "0000" if you want a result of length 4.
2132
- function _padLeft(str, padding) {
2133
- return (padding + str).slice(-padding.length);
2134
- }
2135
2447
  // Set "LUX.auto=false" to disable send results automatically and
2136
2448
  // instead you must call LUX.send() explicitly.
2137
2449
  if (globalConfig.auto) {
@@ -2144,22 +2456,15 @@
2144
2456
  }
2145
2457
  };
2146
2458
  var sendBeaconAfterMinimumMeasureTime_1 = function () {
2147
- var elapsedTime = _now();
2459
+ var elapsedTime = msSincePageInit();
2148
2460
  var timeRemaining = globalConfig.minMeasureTime - elapsedTime;
2149
2461
  if (timeRemaining <= 0) {
2150
2462
  logger.logEvent(LogEvent.OnloadHandlerTriggered, [
2151
2463
  elapsedTime,
2152
2464
  globalConfig.minMeasureTime,
2153
2465
  ]);
2154
- if (document.readyState === "complete") {
2155
- // If onload has already passed, send the beacon now.
2156
- sendBeaconWhenVisible_1();
2157
- }
2158
- else {
2159
- // Ow, send the beacon slightly after window.onload.
2160
- addListener("load", function () {
2161
- setTimeout(sendBeaconWhenVisible_1, 200);
2162
- });
2466
+ if (globalConfig.measureUntil === "onload") {
2467
+ onPageLoad(sendBeaconWhenVisible_1);
2163
2468
  }
2164
2469
  }
2165
2470
  else {
@@ -2176,7 +2481,7 @@
2176
2481
  addEventListener("pageshow", function (event) {
2177
2482
  if (event.persisted) {
2178
2483
  // Record the timestamp of the bfcache restore
2179
- pageRestoreTime = event.timeStamp;
2484
+ setPageRestoreTime(event.timeStamp);
2180
2485
  // In Chromium, document.visibilityState is still "hidden" when pageshow fires after a bfcache
2181
2486
  // restore. Wrapping this in a setTimeout ensures the browser has enough time to update the
2182
2487
  // visibility.
@@ -2185,7 +2490,7 @@
2185
2490
  if (gbLuxSent) {
2186
2491
  // If the beacon was already sent for this page, we start a new page view and mark the
2187
2492
  // load time as the time it took to restore the page.
2188
- _init(pageRestoreTime, false);
2493
+ _init(getPageRestoreTime(), false);
2189
2494
  _markLoadTime();
2190
2495
  }
2191
2496
  // Flag the current page as a bfcache restore
@@ -2214,11 +2519,15 @@
2214
2519
  globalLux.markLoadTime = _markLoadTime;
2215
2520
  globalLux.send = function () {
2216
2521
  logger.logEvent(LogEvent.SendCalled);
2522
+ beacon.send();
2217
2523
  _sendLux();
2218
2524
  };
2219
2525
  globalLux.addData = _addData;
2220
2526
  globalLux.getSessionId = _getUniqueId; // so customers can do their own sampling
2221
- globalLux.getDebug = function () { return logger.getEvents(); };
2527
+ globalLux.getDebug = function () {
2528
+ console.log("SpeedCurve RUM debugging documentation: https://support.speedcurve.com/docs/rum-js-api#luxgetdebug");
2529
+ return logger.getEvents();
2530
+ };
2222
2531
  globalLux.forceSample = function () {
2223
2532
  logger.logEvent(LogEvent.ForceSampleCalled);
2224
2533
  setUniqueId(createSyncId(true));
@@ -2228,7 +2537,7 @@
2228
2537
  };
2229
2538
  globalLux.cmd = _runCommand;
2230
2539
  // Public properties
2231
- globalLux.version = SCRIPT_VERSION;
2540
+ globalLux.version = VERSION;
2232
2541
  /**
2233
2542
  * Run a command from the command queue
2234
2543
  */