govuk_publishing_components 35.1.1 → 35.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/application.js +1 -0
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +34 -4
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +20 -2
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +5 -0
  6. data/app/assets/javascripts/govuk_publishing_components/components/intervention.js +69 -0
  7. data/app/assets/javascripts/govuk_publishing_components/components/step-by-step-nav.js +5 -2
  8. data/app/assets/javascripts/govuk_publishing_components/lib/cookie-functions.js +1 -0
  9. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +430 -313
  10. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +2 -3
  11. data/app/controllers/govuk_publishing_components/component_guide_controller.rb +1 -1
  12. data/app/models/govuk_publishing_components/audit_applications.rb +5 -1
  13. data/app/models/govuk_publishing_components/audit_comparer.rb +4 -4
  14. data/app/views/govuk_publishing_components/audit/_applications.html.erb +22 -4
  15. data/app/views/govuk_publishing_components/components/_accordion.html.erb +25 -1
  16. data/app/views/govuk_publishing_components/components/_intervention.html.erb +15 -7
  17. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +19 -5
  18. data/app/views/govuk_publishing_components/components/_step_by_step_nav.html.erb +23 -3
  19. data/app/views/govuk_publishing_components/components/_step_by_step_nav_related.html.erb +24 -4
  20. data/app/views/govuk_publishing_components/components/docs/intervention.yml +11 -6
  21. data/lib/govuk_publishing_components/presenters/component_wrapper_helper.rb +35 -12
  22. data/lib/govuk_publishing_components/presenters/intervention_helper.rb +16 -5
  23. data/lib/govuk_publishing_components/presenters/step_by_step_nav_helper.rb +0 -16
  24. data/lib/govuk_publishing_components/version.rb +1 -1
  25. metadata +5 -4
@@ -21,13 +21,23 @@
21
21
 
22
22
  (function () {
23
23
  'use strict';
24
-
25
- function now() {
26
- return Date.now ? Date.now() : +new Date();
24
+ /**
25
+ * Fit an array of user timing delimited strings into a URL and return both the entries that fit and
26
+ * the remaining entries that didn't fit.
27
+ */
28
+ function fitUserTimingEntries(utValues, config, url) {
29
+ // Start with the maximum allowed UT entries per beacon
30
+ var beaconUtValues = utValues.slice(0, config.maxBeaconUTEntries);
31
+ var remainingUtValues = utValues.slice(config.maxBeaconUTEntries);
32
+ // Trim UT entries until they fit within the maximum URL length, ensuring at least one UT entry
33
+ // is included.
34
+ while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength &&
35
+ beaconUtValues.length > 1) {
36
+ remainingUtValues.unshift(beaconUtValues.pop());
37
+ }
38
+ return [beaconUtValues, remainingUtValues];
27
39
  }
28
40
 
29
- var scriptStartTime = now();
30
-
31
41
  function fromObject(obj) {
32
42
  var autoMode = getProperty(obj, "auto", true);
33
43
  return {
@@ -57,60 +67,50 @@
57
67
  return defaultValue;
58
68
  }
59
69
 
60
- var LogEvent = {
61
- // Internal events
62
- EvaluationStart: 1,
63
- EvaluationEnd: 2,
64
- InitCalled: 3,
65
- MarkCalled: 4,
66
- MeasureCalled: 5,
67
- AddDataCalled: 6,
68
- SendCalled: 7,
69
- ForceSampleCalled: 8,
70
- DataCollectionStart: 9,
71
- UnloadHandlerTriggered: 10,
72
- OnloadHandlerTriggered: 11,
73
- MarkLoadTimeCalled: 12,
74
- // Data collection events
75
- SessionIsSampled: 21,
76
- SessionIsNotSampled: 22,
77
- MainBeaconSent: 23,
78
- UserTimingBeaconSent: 24,
79
- InteractionBeaconSent: 25,
80
- CustomDataBeaconSent: 26,
81
- // Metric information
82
- NavigationStart: 41,
83
- PerformanceEntryReceived: 42,
84
- PerformanceEntryProcessed: 43,
85
- // Errors
86
- PerformanceObserverError: 51,
87
- InputEventPermissionError: 52,
88
- InnerHtmlAccessError: 53,
89
- EventTargetAccessError: 54,
90
- CookieReadError: 55,
91
- CookieSetError: 56,
92
- PageLabelEvaluationError: 57,
93
- // Browser support messages
94
- NavTimingNotSupported: 71,
95
- PaintTimingNotSupported: 72,
96
- };
97
- var Logger = /** @class */ (function () {
98
- function Logger() {
99
- this.events = [];
100
- }
101
- Logger.prototype.logEvent = function (event, args) {
102
- if (args === void 0) { args = []; }
103
- this.events.push([now(), event, args]);
104
- };
105
- Logger.prototype.getEvents = function () {
106
- return this.events;
107
- };
108
- return Logger;
109
- }());
110
-
111
70
  var START_MARK = "LUX_start";
112
71
  var END_MARK = "LUX_end";
113
72
 
73
+ var customDataValues = {};
74
+ var updatedCustomData = {};
75
+ function addCustomDataValue(name, value) {
76
+ var typeV = typeof value;
77
+ if (customDataValues[name] !== value) {
78
+ // If the value is new or different to the previous value, record it so that later we can send
79
+ // only the values that have changed.
80
+ updatedCustomData[name] = value;
81
+ }
82
+ if (typeV === "string" || typeV === "number" || typeV === "boolean") {
83
+ customDataValues[name] = value;
84
+ }
85
+ if (typeV === "undefined" || value === null) {
86
+ delete customDataValues[name];
87
+ }
88
+ }
89
+ function getAllCustomData() {
90
+ return customDataValues;
91
+ }
92
+ function getUpdatedCustomData() {
93
+ return updatedCustomData;
94
+ }
95
+ function clearUpdateCustomData() {
96
+ updatedCustomData = {};
97
+ }
98
+ /**
99
+ * Convert a set of custom data values to the string format expected by the backend.
100
+ */
101
+ function valuesToString(values) {
102
+ var strings = [];
103
+ for (var key in values) {
104
+ // Convert all values to strings
105
+ var value = "" + values[key];
106
+ // Strip out reserved characters (, and | are used as delimiters)
107
+ key = key.replace(/,/g, "").replace(/\|/g, "");
108
+ value = value.replace(/,/g, "").replace(/\|/g, "");
109
+ strings.push(key + "|" + value);
110
+ }
111
+ return encodeURIComponent(strings.join(","));
112
+ }
113
+
114
114
  var Flags = {
115
115
  InitCalled: 1 << 0,
116
116
  NavTimingNotSupported: 1 << 1,
@@ -177,6 +177,85 @@
177
177
  return null;
178
178
  }
179
179
 
180
+ function now() {
181
+ return Date.now ? Date.now() : +new Date();
182
+ }
183
+
184
+ var LogEvent = {
185
+ // Internal events
186
+ EvaluationStart: 1,
187
+ EvaluationEnd: 2,
188
+ InitCalled: 3,
189
+ MarkCalled: 4,
190
+ MeasureCalled: 5,
191
+ AddDataCalled: 6,
192
+ SendCalled: 7,
193
+ ForceSampleCalled: 8,
194
+ DataCollectionStart: 9,
195
+ UnloadHandlerTriggered: 10,
196
+ OnloadHandlerTriggered: 11,
197
+ MarkLoadTimeCalled: 12,
198
+ // Data collection events
199
+ SessionIsSampled: 21,
200
+ SessionIsNotSampled: 22,
201
+ MainBeaconSent: 23,
202
+ UserTimingBeaconSent: 24,
203
+ InteractionBeaconSent: 25,
204
+ CustomDataBeaconSent: 26,
205
+ // Metric information
206
+ NavigationStart: 41,
207
+ PerformanceEntryReceived: 42,
208
+ PerformanceEntryProcessed: 43,
209
+ // Errors
210
+ PerformanceObserverError: 51,
211
+ InputEventPermissionError: 52,
212
+ InnerHtmlAccessError: 53,
213
+ EventTargetAccessError: 54,
214
+ CookieReadError: 55,
215
+ CookieSetError: 56,
216
+ PageLabelEvaluationError: 57,
217
+ // Browser support messages
218
+ NavTimingNotSupported: 71,
219
+ PaintTimingNotSupported: 72,
220
+ };
221
+ var Logger = /** @class */ (function () {
222
+ function Logger() {
223
+ this.events = [];
224
+ }
225
+ Logger.prototype.logEvent = function (event, args) {
226
+ if (args === void 0) { args = []; }
227
+ this.events.push([now(), event, args]);
228
+ };
229
+ Logger.prototype.getEvents = function () {
230
+ return this.events;
231
+ };
232
+ return Logger;
233
+ }());
234
+ var sessionValue = 0;
235
+ var sessionEntries = [];
236
+ function addEntry$2(entry) {
237
+ if (!entry.hadRecentInput) {
238
+ var firstEntry = sessionEntries[0];
239
+ var latestEntry = sessionEntries[sessionEntries.length - 1];
240
+ if (sessionEntries.length &&
241
+ (entry.startTime - latestEntry.startTime >= 1000 ||
242
+ entry.startTime - firstEntry.startTime >= 5000)) {
243
+ reset$1();
244
+ }
245
+ sessionValue += entry.value;
246
+ sessionEntries.push(entry);
247
+ }
248
+ }
249
+ function reset$1() {
250
+ sessionValue = 0;
251
+ sessionEntries = [];
252
+ }
253
+ function getCLS() {
254
+ return sessionValue;
255
+ }
256
+
257
+ var scriptStartTime = now();
258
+
180
259
  var _a;
181
260
  // If the various performance APIs aren't available, we export an empty object to
182
261
  // prevent having to make regular typeof checks.
@@ -193,6 +272,32 @@
193
272
  }
194
273
  return now() - timing.navigationStart;
195
274
  }
275
+ function navigationType() {
276
+ if (performance.navigation && typeof performance.navigation.type !== "undefined") {
277
+ return performance.navigation.type;
278
+ }
279
+ return "";
280
+ }
281
+ function getNavigationEntry() {
282
+ var navEntries = getEntriesByType("navigation");
283
+ if (navEntries.length) {
284
+ return navEntries[0];
285
+ }
286
+ var navType = navigationType();
287
+ var entry = {
288
+ activationStart: 0,
289
+ startTime: 0,
290
+ type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
291
+ };
292
+ if (__ENABLE_POLYFILLS) {
293
+ for (var key in timing) {
294
+ if (typeof timing[key] === "number" && key !== "navigationStart") {
295
+ entry[key] = Math.max(0, timing[key] - timing.navigationStart);
296
+ }
297
+ }
298
+ }
299
+ return entry;
300
+ }
196
301
  /**
197
302
  * Simple wrapper around performance.getEntriesByType to provide fallbacks for
198
303
  * legacy browsers, and work around edge cases where undefined is returned instead
@@ -208,91 +313,124 @@
208
313
  return [];
209
314
  }
210
315
 
211
- var Matching = /** @class */ (function () {
212
- function Matching() {
213
- }
214
- Matching.isMatching = function (pattern, url) {
215
- var regexp = Matching.createRegexpFromPattern(pattern);
216
- return url.match(regexp) ? true : false;
217
- };
218
- /**
219
- * Converts string pattern to RegExp object
220
- * @return RegExp
221
- */
222
- Matching.createRegexpFromPattern = function (pattern) {
223
- var regexp;
224
- if (pattern == "/") {
225
- regexp = this.getRegexpForHostnameRoot();
226
- }
227
- else if (!pattern.includes(Matching.wildcard)) {
228
- regexp = this.getRegexpForExactString(pattern);
316
+ /**
317
+ * This implementation is based on the web-vitals implementation, however it is stripped back to the
318
+ * bare minimum required to measure just the INP value and does not store the actual event entries.
319
+ */
320
+ // The maximum number of interactions to store
321
+ var MAX_INTERACTIONS = 10;
322
+ // A list of the slowest interactions
323
+ var slowestEntries = [];
324
+ // A map of the slowest interactions by ID
325
+ var slowestEntriesMap = {};
326
+ // The total number of interactions recorded on the page
327
+ var interactionCountEstimate = 0;
328
+ function reset() {
329
+ interactionCountEstimate = 0;
330
+ slowestEntries = [];
331
+ slowestEntriesMap = {};
332
+ }
333
+ function addEntry$1(entry) {
334
+ if (entry.interactionId || (entry.entryType === "first-input" && !entryExists(entry))) {
335
+ var duration = entry.duration, startTime = entry.startTime, interactionId = entry.interactionId;
336
+ var existingEntry = slowestEntriesMap[interactionId];
337
+ if (existingEntry) {
338
+ existingEntry.duration = Math.max(duration, existingEntry.duration);
229
339
  }
230
340
  else {
231
- regexp = this.createRegexpFromPathname(pattern);
341
+ interactionCountEstimate++;
342
+ slowestEntriesMap[interactionId] = { duration: duration, interactionId: interactionId, startTime: startTime };
343
+ slowestEntries.push(slowestEntriesMap[interactionId]);
344
+ }
345
+ // Only store the longest <MAX_INTERACTIONS> interactions
346
+ slowestEntries.sort(function (a, b) { return b.duration - a.duration; });
347
+ slowestEntries.splice(MAX_INTERACTIONS).forEach(function (entry) {
348
+ delete slowestEntriesMap[entry.interactionId];
349
+ });
350
+ }
351
+ }
352
+ function entryExists(e1) {
353
+ return slowestEntries.some(function (e2) { return e1.startTime === e2.startTime && e1.duration === e2.duration; });
354
+ }
355
+ /**
356
+ * Returns an estimated high percentile INP value based on the total number of interactions on the
357
+ * current page.
358
+ */
359
+ function getHighPercentileINP() {
360
+ var _a;
361
+ var index = Math.min(slowestEntries.length - 1, Math.floor(getInteractionCount() / 50));
362
+ return (_a = slowestEntries[index]) === null || _a === void 0 ? void 0 : _a.duration;
363
+ }
364
+ function getInteractionCount() {
365
+ if ("interactionCount" in performance) {
366
+ return performance.interactionCount;
367
+ }
368
+ return interactionCountEstimate;
369
+ }
370
+
371
+ /******************************************************************************
372
+ Copyright (c) Microsoft Corporation.
373
+
374
+ Permission to use, copy, modify, and/or distribute this software for any
375
+ purpose with or without fee is hereby granted.
376
+
377
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
378
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
379
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
380
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
381
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
382
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
383
+ PERFORMANCE OF THIS SOFTWARE.
384
+ ***************************************************************************** */
385
+
386
+ var __assign = function() {
387
+ __assign = Object.assign || function __assign(t) {
388
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
389
+ s = arguments[i];
390
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
232
391
  }
233
- return regexp;
392
+ return t;
234
393
  };
235
- /**
236
- * Converts URL pathname string pattern to RegExp object
237
- * Multile wildcards (*) are supported
238
- * @return RegExp
239
- */
240
- Matching.createRegexpFromPathname = function (pattern) {
241
- var anyDomain = pattern.charAt(0) == "/";
242
- pattern = this.escapeStringForRegexp(pattern);
243
- var expression = "^" +
244
- (anyDomain ? Matching.domainExpression : "") +
245
- pattern.replaceAll(Matching.wildcard, ".*?") +
246
- "$";
247
- return new RegExp(expression, "i");
248
- };
249
- /**
250
- * Matches hostname root (e.g. "/", "somedomain.com/", "www.somedomain.co.nz/")
251
- * Trailing slash is mandatory
252
- * @return RegExp
253
- */
254
- Matching.getRegexpForHostnameRoot = function () {
255
- return new RegExp("^" + Matching.domainExpression + "/$", "i");
256
- };
257
- /**
258
- * Matches exact string (no wildcard provided)
259
- * @return RegExp
260
- */
261
- Matching.getRegexpForExactString = function (string) {
262
- var anyDomain = string.charAt(0) == "/";
263
- return new RegExp("^" +
264
- (anyDomain ? Matching.domainExpression : "") +
265
- this.escapeStringForRegexp(string) +
266
- "/?$", "i");
267
- };
268
- /**
269
- * Escape special symbols in regexp string
270
- * @param string
271
- */
272
- Matching.escapeStringForRegexp = function (string) {
273
- // we don't escape * because it's our own special symbol!
274
- return string.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
275
- };
276
- Matching.wildcard = "*";
277
- Matching.domainExpression = "[a-zA-Z0-9-.]{1,61}[a-zA-Z0-9]\\.[a-zA-Z]{2,}";
278
- return Matching;
279
- }());
394
+ return __assign.apply(this, arguments);
395
+ };
280
396
 
281
- /**
282
- * Fit an array of user timing delimited strings into a URL and return both the entries that fit and
283
- * the remaining entries that didn't fit.
284
- */
285
- function fitUserTimingEntries(utValues, config, url) {
286
- // Start with the maximum allowed UT entries per beacon
287
- var beaconUtValues = utValues.slice(0, config.maxBeaconUTEntries);
288
- var remainingUtValues = utValues.slice(config.maxBeaconUTEntries);
289
- // Trim UT entries until they fit within the maximum URL length, ensuring at least one UT entry
290
- // is included.
291
- while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength &&
292
- beaconUtValues.length > 1) {
293
- remainingUtValues.unshift(beaconUtValues.pop());
294
- }
295
- return [beaconUtValues, remainingUtValues];
397
+ var ALL_ENTRIES = [];
398
+ function observe(type, callback, options) {
399
+ if (typeof PerformanceObserver === "function" &&
400
+ PerformanceObserver.supportedEntryTypes.includes(type)) {
401
+ var po = new PerformanceObserver(function (list) {
402
+ list.getEntries().forEach(function (entry) { return callback(entry); });
403
+ });
404
+ po.observe(__assign({ type: type, buffered: true }, options));
405
+ return po;
406
+ }
407
+ return undefined;
408
+ }
409
+ function getEntries(type) {
410
+ return ALL_ENTRIES.filter(function (entry) { return entry.entryType === type; });
411
+ }
412
+ function addEntry(entry) {
413
+ ALL_ENTRIES.push(entry);
414
+ }
415
+ function clearEntries() {
416
+ ALL_ENTRIES.splice(0);
417
+ }
418
+
419
+ function patternMatchesUrl(pattern, hostname, pathname) {
420
+ var regex = createRegExpFromPattern(pattern);
421
+ if (pattern.charAt(0) === "/") {
422
+ // Rule is a pathname only
423
+ return regex.test(pathname);
424
+ }
425
+ // Rule is a hostname and pathname
426
+ return regex.test(hostname + pathname);
427
+ }
428
+ function createRegExpFromPattern(pattern) {
429
+ return new RegExp("^" + escapeStringForRegExp(pattern).replaceAll("*", ".*") + "$", "i");
430
+ }
431
+ function escapeStringForRegExp(str) {
432
+ // Note: we don't escape * because it's our own special symbol!
433
+ return str.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
296
434
  }
297
435
 
298
436
  var LUX = window.LUX || {};
@@ -306,8 +444,7 @@
306
444
  // -------------------------------------------------------------------------
307
445
  /// End
308
446
  // -------------------------------------------------------------------------
309
-
310
- var SCRIPT_VERSION = "305";
447
+ var SCRIPT_VERSION = "307";
311
448
  var logger = new Logger();
312
449
  var globalConfig = fromObject(LUX);
313
450
  logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION]);
@@ -349,49 +486,53 @@
349
486
  }
350
487
  }
351
488
  window.addEventListener("error", errorHandler);
352
- // Initialize performance observer
353
- // Note: This code was later added to the LUX snippet. In the snippet we ONLY collect
354
- // Long Task entries because that is the only entry type that can not be buffered.
355
- // We _copy_ any Long Tasks collected by the snippet and ignore it after that.
356
- var gaSnippetLongTasks = typeof window.LUX_al === "object" ? window.LUX_al : [];
357
- var gaPerfEntries = gaSnippetLongTasks.slice(); // array of Long Tasks (prefer the array from the snippet)
358
- if (typeof PerformanceObserver === "function") {
359
- var perfObserver = new PerformanceObserver(function (list) {
360
- list.getEntries().forEach(function (entry) {
361
- logger.logEvent(LogEvent.PerformanceEntryReceived, [entry]);
362
- // Only record long tasks that weren't already recorded by the PerformanceObserver in the snippet
363
- if (entry.entryType !== "longtask" || gaPerfEntries.indexOf(entry) === -1) {
364
- gaPerfEntries.push(entry);
365
- }
366
- });
367
- });
368
- try {
369
- if (typeof PerformanceLongTaskTiming === "function") {
370
- perfObserver.observe({ type: "longtask", buffered: true });
371
- }
372
- if (typeof LargestContentfulPaint === "function") {
373
- perfObserver.observe({ type: "largest-contentful-paint", buffered: true });
374
- }
375
- if (typeof PerformanceElementTiming === "function") {
376
- perfObserver.observe({ type: "element", buffered: true });
377
- }
378
- if (typeof PerformancePaintTiming === "function") {
379
- perfObserver.observe({ type: "paint", buffered: true });
489
+ var logEntry = function (entry) {
490
+ logger.logEvent(LogEvent.PerformanceEntryReceived, [entry]);
491
+ };
492
+ // Most PerformanceEntry types we log an event for and add it to the global entry store.
493
+ var processAndLogEntry = function (entry) {
494
+ addEntry(entry);
495
+ logEntry(entry);
496
+ };
497
+ // Before long tasks were buffered, we added a PerformanceObserver to the lux.js snippet to capture
498
+ // any long tasks that occurred before the full script was loaded. To deal with this, we process
499
+ // all of the snippet long tasks, and we check for double-ups in the new PerformanceObserver.
500
+ var snippetLongTasks = typeof window.LUX_al === "object" ? window.LUX_al : [];
501
+ snippetLongTasks.forEach(processAndLogEntry);
502
+ try {
503
+ observe("longtask", function (entry) {
504
+ if (ALL_ENTRIES.indexOf(entry) === -1) {
505
+ processAndLogEntry(entry);
380
506
  }
381
- if (typeof LayoutShift === "function") {
382
- perfObserver.observe({ type: "layout-shift", buffered: true });
507
+ });
508
+ observe("largest-contentful-paint", processAndLogEntry);
509
+ observe("element", processAndLogEntry);
510
+ observe("paint", processAndLogEntry);
511
+ observe("layout-shift", function (entry) {
512
+ addEntry$2(entry);
513
+ logEntry(entry);
514
+ });
515
+ observe("first-input", function (entry) {
516
+ var fid = entry.processingStart - entry.startTime;
517
+ if (!gFirstInputDelay || gFirstInputDelay < fid) {
518
+ gFirstInputDelay = fid;
383
519
  }
384
- }
385
- catch (e) {
386
- logger.logEvent(LogEvent.PerformanceObserverError, [e]);
387
- }
520
+ // Allow first-input events to be considered for INP
521
+ addEntry$1(entry);
522
+ });
523
+ // TODO: Add { durationThreshold: 40 } once performance.interactionCount is widely supported.
524
+ // Right now we have to count every event to get the total interaction count so that we can
525
+ // estimate a high percentile value for INP.
526
+ observe("event", addEntry$1);
527
+ }
528
+ catch (e) {
529
+ logger.logEvent(LogEvent.PerformanceObserverError, [e]);
388
530
  }
389
531
  // Bitmask of flags for this session & page
390
532
  var gFlags = 0;
391
533
  var gaMarks = [];
392
534
  var gaMeasures = [];
393
535
  var ghIx = {}; // hash for Interaction Metrics (scroll, click, keyboard)
394
- var ghData = {}; // hash for data that is specific to the customer (eg, userid, conversion info)
395
536
  var gbLuxSent = 0; // have we sent the LUX data? (avoid sending twice in unload)
396
537
  var gbNavSent = 0; // have we sent the Nav Timing beacon yet? (avoid sending twice for SPA)
397
538
  var gbIxSent = 0; // have we sent the IX data? (avoid sending twice for SPA)
@@ -417,7 +558,7 @@
417
558
  // FIRST INPUT DELAY (FID)
418
559
  // The basic idea behind FID is to attach various input event listeners and measure the time
419
560
  // between when the event happens and when the handler executes. That is FID.
420
- var gFirstInputDelay; // this is FID
561
+ var gFirstInputDelay;
421
562
  var gaEventTypes = ["click", "mousedown", "keydown", "touchstart", "pointerdown"]; // NOTE: does NOT include scroll!
422
563
  var ghListenerOptions = { passive: true, capture: true };
423
564
  // Record the FIRST input delay.
@@ -587,7 +728,9 @@
587
728
  var startTime = typeof startMarkName === "number" ? startMarkName : 0;
588
729
  var endTime = typeof endMarkName === "number" ? endMarkName : _now();
589
730
  var throwError = function (missingMark) {
590
- throw new DOMException("Failed to execute 'measure' on 'Performance': The mark '".concat(missingMark, "' does not exist"));
731
+ throw new DOMException("Failed to execute 'measure' on 'Performance': The mark '" +
732
+ missingMark +
733
+ "' does not exist");
591
734
  };
592
735
  if (typeof startMarkName === "string") {
593
736
  var startMark = _getMark(startMarkName);
@@ -724,60 +867,56 @@
724
867
  // Return a string of Element Timing Metrics formatted for beacon querystring.
725
868
  function elementTimingValues() {
726
869
  var aET = [];
727
- if (gaPerfEntries.length) {
728
- for (var i = 0; i < gaPerfEntries.length; i++) {
729
- var pe = gaPerfEntries[i];
730
- if ("element" === pe.entryType && pe.identifier && pe.startTime) {
731
- logger.logEvent(LogEvent.PerformanceEntryProcessed, [pe]);
732
- aET.push(pe.identifier + "|" + Math.round(pe.startTime));
733
- }
870
+ var startMark = _getMark(START_MARK);
871
+ var tZero = startMark ? startMark.startTime : 0;
872
+ getEntries("element").forEach(function (entry) {
873
+ if (entry.identifier && entry.startTime) {
874
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [entry]);
875
+ aET.push(entry.identifier + "|" + Math.round(entry.startTime - tZero));
734
876
  }
735
- }
877
+ });
736
878
  return aET.join(",");
737
879
  }
738
880
  // Return a string of CPU times formatted for beacon querystring.
739
881
  function cpuTimes() {
740
- if (typeof PerformanceLongTaskTiming !== "function") {
882
+ if (!("PerformanceLongTaskTiming" in self)) {
741
883
  // Do not return any CPU metrics if Long Tasks API is not supported.
742
884
  return "";
743
885
  }
744
886
  var sCPU = "";
745
887
  var hCPU = {};
746
888
  var hCPUDetails = {}; // TODO - Could remove this later after large totals go away.
889
+ var longTaskEntries = getEntries("longtask");
747
890
  // Add up totals for each "type" of long task
748
- if (gaPerfEntries.length) {
891
+ if (longTaskEntries.length) {
749
892
  // Long Task start times are relative to NavigationStart which is "0".
750
893
  // But if it is a SPA then the relative start time is gStartMark.
751
894
  var startMark = _getMark(START_MARK);
752
- var tZero = startMark ? startMark.startTime : 0;
895
+ var tZero_1 = startMark ? startMark.startTime : 0;
753
896
  // Do not include Long Tasks that start after the page is done.
754
897
  // For full page loads, "done" is loadEventEnd.
755
- var tEnd = timing.loadEventEnd - timing.navigationStart;
898
+ var tEnd_1 = timing.loadEventEnd - timing.navigationStart;
756
899
  if (startMark) {
757
900
  // For SPA page loads (determined by the presence of a start mark), "done" is gEndMark.
758
901
  var endMark = _getMark(END_MARK);
759
902
  if (endMark) {
760
- tEnd = endMark.startTime;
903
+ tEnd_1 = endMark.startTime;
761
904
  }
762
905
  }
763
- for (var i = 0; i < gaPerfEntries.length; i++) {
764
- var p = gaPerfEntries[i];
765
- if ("longtask" !== p.entryType) {
766
- continue;
767
- }
768
- var dur = Math.round(p.duration);
769
- if (p.startTime < tZero) {
906
+ longTaskEntries.forEach(function (entry) {
907
+ var dur = Math.round(entry.duration);
908
+ if (entry.startTime < tZero_1) {
770
909
  // In a SPA it is possible that we were in the middle of a Long Task when
771
910
  // LUX.init() was called. If so, only include the duration after tZero.
772
- dur -= tZero - p.startTime;
911
+ dur -= tZero_1 - entry.startTime;
773
912
  }
774
- else if (p.startTime >= tEnd) {
913
+ else if (entry.startTime >= tEnd_1) {
775
914
  // In a SPA it is possible that a Long Task started after loadEventEnd but before our
776
915
  // callback from setTimeout(200) happened. Do not include anything that started after tEnd.
777
- continue;
916
+ return;
778
917
  }
779
- logger.logEvent(LogEvent.PerformanceEntryProcessed, [p]);
780
- var type = p.attribution[0].name; // TODO - is there ever more than 1 attribution???
918
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [entry]);
919
+ var type = entry.attribution[0].name; // TODO - is there ever more than 1 attribution???
781
920
  if (!hCPU[type]) {
782
921
  // initialize this category
783
922
  hCPU[type] = 0;
@@ -785,8 +924,8 @@
785
924
  }
786
925
  hCPU[type] += dur;
787
926
  // Send back the raw startTime and duration, as well as the adjusted duration.
788
- hCPUDetails[type] += "," + Math.round(p.startTime) + "|" + dur;
789
- }
927
+ hCPUDetails[type] += "," + Math.round(entry.startTime) + "|" + dur;
928
+ });
790
929
  }
791
930
  // TODO - Add more types if/when they become available.
792
931
  var jsType = typeof hCPU["script"] !== "undefined" ? "script" : "unknown"; // spec changed from "script" to "unknown" Nov 2018
@@ -803,14 +942,14 @@
803
942
  ",x|" +
804
943
  hStats["max"] +
805
944
  (0 === hStats["fci"] ? "" : ",i|" + hStats["fci"]); // only add FCI if it is non-zero
806
- sCPU += "s|" + hCPU[jsType] + sStats + hCPUDetails[jsType];
945
+ sCPU += "s|" + hCPU[jsType] + sStats + hCPUDetails[jsType];
807
946
  return sCPU;
808
947
  }
809
948
  // Return a hash of "stats" about the CPU details incl. count, max, and median.
810
949
  function cpuStats(sDetails) {
811
950
  // tuples of starttime|duration, eg: ,456|250,789|250,1012|250
812
951
  var max = 0;
813
- var fci = getFcp(); // FCI is beginning of 5 second window of no Long Tasks _after_ first contentful paint
952
+ var fci = getFcp() || 0; // FCI is beginning of 5 second window of no Long Tasks _after_ first contentful paint
814
953
  // If FCP is 0 then that means FCP is not supported.
815
954
  // If FCP is not supported then we can NOT calculate a valid FCI.
816
955
  // Thus, leave FCI = 0 and exclude it from the beacon above.
@@ -843,22 +982,13 @@
843
982
  var median = arrayMedian(aValues);
844
983
  return { count: count, median: median, max: max, fci: fci };
845
984
  }
846
- function calculateDCLS() {
847
- if (typeof LayoutShift !== "function") {
848
- return false;
985
+ function getCLS$1() {
986
+ if (!("LayoutShift" in self)) {
987
+ return undefined;
849
988
  }
850
- var DCLS = 0;
851
- for (var i = 0; i < gaPerfEntries.length; i++) {
852
- var p = gaPerfEntries[i];
853
- if ("layout-shift" !== p.entryType || p.hadRecentInput) {
854
- continue;
855
- }
856
- logger.logEvent(LogEvent.PerformanceEntryProcessed, [p]);
857
- DCLS += p.value;
858
- }
859
- // The DCL column in Redshift is REAL (FLOAT4) which stores a maximum
989
+ // The DCLS column in Redshift is REAL (FLOAT4) which stores a maximum
860
990
  // of 6 significant digits.
861
- return DCLS.toFixed(6);
991
+ return getCLS().toFixed(6);
862
992
  }
863
993
  // Return the median value from an array of integers.
864
994
  function arrayMedian(aValues) {
@@ -935,17 +1065,10 @@
935
1065
  }
936
1066
  return aIx.join(",");
937
1067
  }
938
- // _addData()
939
1068
  function _addData(name, value) {
940
1069
  logger.logEvent(LogEvent.AddDataCalled, [name, value]);
941
- var typeV = typeof value;
942
1070
  if (typeof name === "string") {
943
- if (typeV === "string" || typeV === "number" || typeV === "boolean") {
944
- ghData[name] = value;
945
- }
946
- if (typeV === "undefined" || value === null) {
947
- delete ghData[name];
948
- }
1071
+ addCustomDataValue(name, value);
949
1072
  }
950
1073
  if (gbLuxSent) {
951
1074
  // This is special: We want to allow customers to call LUX.addData()
@@ -956,7 +1079,7 @@
956
1079
  if (gCustomerDataTimeout) {
957
1080
  // Cancel the timer for any previous beacons so that if they have not
958
1081
  // yet been sent we can combine all the data in a new beacon.
959
- clearTimeout(gCustomerDataTimeout);
1082
+ window.clearTimeout(gCustomerDataTimeout);
960
1083
  }
961
1084
  gCustomerDataTimeout = window.setTimeout(_sendCustomerData, 100);
962
1085
  }
@@ -970,18 +1093,6 @@
970
1093
  var nThis = ("" + gUid).substr(-2); // number for THIS page - from 00 to 99
971
1094
  return parseInt(nThis) < globalConfig.samplerate;
972
1095
  }
973
- // Return a string of Customer Data formatted for beacon querystring.
974
- function customerDataValues() {
975
- var aData = [];
976
- for (var key in ghData) {
977
- var value = "" + ghData[key]; // convert to string (eg for ints and booleans)
978
- // strip delimiters (instead of escaping)
979
- key = key.replace(/,/g, "").replace(/\|/g, "");
980
- value = value.replace(/,/g, "").replace(/\|/g, "");
981
- aData.push(key + "|" + value);
982
- }
983
- return encodeURIComponent(aData.join(","));
984
- }
985
1096
  // _init()
986
1097
  // Use this function in Single Page Apps to reset things.
987
1098
  // This function should ONLY be called within a SPA!
@@ -1007,8 +1118,11 @@
1007
1118
  gbFirstPV = 0;
1008
1119
  gSyncId = createSyncId();
1009
1120
  gUid = refreshUniqueId(gSyncId);
1010
- gaPerfEntries.splice(0); // clear out the array of performance entries (do NOT redefine gaPerfEntries!)
1121
+ clearEntries();
1122
+ reset$1();
1123
+ reset();
1011
1124
  nErrors = 0;
1125
+ gFirstInputDelay = undefined;
1012
1126
  // Clear flags then set the flag that init was called (ie, this is a SPA).
1013
1127
  gFlags = 0;
1014
1128
  gFlags = addFlag(gFlags, Flags.InitCalled);
@@ -1031,9 +1145,12 @@
1031
1145
  var num = 0;
1032
1146
  for (var i = 0, len = aElems.length; i < len; i++) {
1033
1147
  var e = aElems[i];
1034
- if (e.src && !e.async && !e.defer && 0 !== (e.compareDocumentPosition(lastViewportElem) & 4)) {
1035
- // If the script has a SRC and async is false and it occurs BEFORE the last viewport element,
1036
- // then increment the counter.
1148
+ if (e.src &&
1149
+ !e.async &&
1150
+ !e.defer &&
1151
+ 0 !== (e.compareDocumentPosition(lastViewportElem) & 4)) {
1152
+ // If the script has a SRC and async is false and it occurs BEFORE the last viewport element,
1153
+ // then increment the counter.
1037
1154
  num++;
1038
1155
  }
1039
1156
  }
@@ -1158,9 +1275,9 @@
1158
1275
  (t.domComplete ? "oc" + (t.domComplete - ns) : "") +
1159
1276
  (t.loadEventStart ? "ls" + (t.loadEventStart - ns) : "") +
1160
1277
  (t.loadEventEnd ? "le" + (t.loadEventEnd - ns) : "") +
1161
- (startRender ? "sr" + startRender : "") +
1162
- (fcp ? "fc" + fcp : "") +
1163
- (lcp ? "lc" + lcp : "") +
1278
+ (typeof startRender !== "undefined" ? "sr" + startRender : "") +
1279
+ (typeof fcp !== "undefined" ? "fc" + fcp : "") +
1280
+ (typeof lcp !== "undefined" ? "lc" + lcp : "") +
1164
1281
  "";
1165
1282
  }
1166
1283
  else if (endMark) {
@@ -1178,7 +1295,7 @@
1178
1295
  }
1179
1296
  return s;
1180
1297
  }
1181
- // Return First Contentful Paint or zero if not supported.
1298
+ // Return First Contentful Paint or undefined if not supported.
1182
1299
  function getFcp() {
1183
1300
  var paintEntries = getEntriesByType("paint");
1184
1301
  for (var i = 0; i < paintEntries.length; i++) {
@@ -1187,25 +1304,21 @@
1187
1304
  return Math.round(entry.startTime);
1188
1305
  }
1189
1306
  }
1190
- return 0;
1307
+ return undefined;
1191
1308
  }
1192
- // Return Largest Contentful Paint or zero if not supported.
1309
+ // Return Largest Contentful Paint or undefined if not supported.
1193
1310
  function getLcp() {
1194
- if (gaPerfEntries.length) {
1195
- // Find the *LAST* LCP per https://web.dev/largest-contentful-paint
1196
- for (var i = gaPerfEntries.length - 1; i >= 0; i--) {
1197
- var pe = gaPerfEntries[i];
1198
- if ("largest-contentful-paint" === pe.entryType) {
1199
- logger.logEvent(LogEvent.PerformanceEntryProcessed, [pe]);
1200
- return Math.round(pe.startTime);
1201
- }
1202
- }
1311
+ var lcpEntries = getEntries("largest-contentful-paint");
1312
+ if (lcpEntries.length) {
1313
+ var lastEntry = lcpEntries[lcpEntries.length - 1];
1314
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [lastEntry]);
1315
+ return Math.max(0, Math.round(lastEntry.startTime - getNavigationEntry().activationStart));
1203
1316
  }
1204
- return 0;
1317
+ return undefined;
1205
1318
  }
1206
1319
  // Return best guess at Start Render time (in ms).
1207
1320
  // Mostly works on just Chrome and IE.
1208
- // Return null if not supported.
1321
+ // Return undefined if not supported.
1209
1322
  function getStartRender() {
1210
1323
  if (performance.timing) {
1211
1324
  var paintEntries = getEntriesByType("paint");
@@ -1224,20 +1337,23 @@
1224
1337
  }
1225
1338
  }
1226
1339
  logger.logEvent(LogEvent.PaintTimingNotSupported);
1227
- return null;
1340
+ return undefined;
1228
1341
  }
1229
- function getCustomerId() {
1230
- if (typeof LUX.customerid !== "undefined") {
1231
- // Return the id explicitly set in the JavaScript variable.
1232
- return LUX.customerid;
1342
+ function getINP() {
1343
+ if (!("PerformanceEventTiming" in self)) {
1344
+ return undefined;
1233
1345
  }
1234
- // Extract the id of the lux.js script element.
1235
- var luxScript = getScriptElement("/js/lux.js");
1236
- if (luxScript) {
1237
- LUX.customerid = getQuerystringParam(luxScript.src, "id");
1238
- return LUX.customerid;
1346
+ return getHighPercentileINP();
1347
+ }
1348
+ function getCustomerId() {
1349
+ if (typeof LUX.customerid === "undefined") {
1350
+ // Extract the id of the lux.js script element.
1351
+ var luxScript = getScriptElement("/js/lux.js");
1352
+ if (luxScript) {
1353
+ LUX.customerid = getQuerystringParam(luxScript.src, "id");
1354
+ }
1239
1355
  }
1240
- return "";
1356
+ return LUX.customerid || "";
1241
1357
  }
1242
1358
  // Return the SCRIPT DOM element whose SRC contains the URL snippet.
1243
1359
  // This is used to find the LUX script element.
@@ -1249,7 +1365,7 @@
1249
1365
  return script;
1250
1366
  }
1251
1367
  }
1252
- return null;
1368
+ return undefined;
1253
1369
  }
1254
1370
  function getQuerystringParam(url, name) {
1255
1371
  var qs = url.split("?")[1];
@@ -1295,19 +1411,7 @@
1295
1411
  }
1296
1412
  // Return the main HTML document transfer size (in bytes).
1297
1413
  function docSize() {
1298
- var aEntries = getEntriesByType("navigation");
1299
- if (aEntries.length && aEntries[0]["encodedBodySize"]) {
1300
- return aEntries[0]["encodedBodySize"];
1301
- }
1302
- return 0; // ERROR - NOT FOUND
1303
- }
1304
- // Return the navigation type. 0 = normal, 1 = reload, etc.
1305
- // Return empty string if not available.
1306
- function navigationType() {
1307
- if (performance.navigation && typeof performance.navigation.type !== "undefined") {
1308
- return performance.navigation.type;
1309
- }
1310
- return "";
1414
+ return getNavigationEntry().encodedBodySize || 0;
1311
1415
  }
1312
1416
  // Return the connection type based on Network Information API.
1313
1417
  // Note this API is in flux.
@@ -1421,10 +1525,10 @@
1421
1525
  }
1422
1526
  function clearMaxMeasureTimeout() {
1423
1527
  if (gMaxMeasureTimeout) {
1424
- clearTimeout(gMaxMeasureTimeout);
1528
+ window.clearTimeout(gMaxMeasureTimeout);
1425
1529
  }
1426
1530
  }
1427
- function _getBeaconUrl() {
1531
+ function _getBeaconUrl(customData) {
1428
1532
  var queryParams = [
1429
1533
  "v=" + SCRIPT_VERSION,
1430
1534
  "id=" + getCustomerId(),
@@ -1437,9 +1541,10 @@
1437
1541
  if (gFlags) {
1438
1542
  queryParams.push("fl=" + gFlags);
1439
1543
  }
1440
- var customerData = customerDataValues();
1544
+ var customerData = valuesToString(customData);
1441
1545
  if (customerData) {
1442
1546
  queryParams.push("CD=" + customerData);
1547
+ clearUpdateCustomData();
1443
1548
  }
1444
1549
  return globalConfig.beaconUrl + "?" + queryParams.join("&");
1445
1550
  }
@@ -1463,22 +1568,28 @@
1463
1568
  // with LUX.markLoadTime()
1464
1569
  _markLoadTime();
1465
1570
  }
1466
- var sET = elementTimingValues(); // Element Timing data
1467
- var sIx = ""; // Interaction Metrics
1571
+ var sIx = "";
1572
+ var INP = getINP();
1573
+ // It's possible that the interaction beacon has been sent before the main beacon. We don't want
1574
+ // to send the interaction metrics twice, so we only include them here if the interaction beacon
1575
+ // has not been sent.
1468
1576
  if (!gbIxSent) {
1469
- // It is possible for the IX beacon to be sent BEFORE the "main" window.onload LUX beacon.
1470
- // Make sure we do not send the IX data twice.
1471
1577
  sIx = ixValues();
1578
+ if (sIx === "") {
1579
+ // If there are no interaction metrics, we
1580
+ INP = undefined;
1581
+ }
1472
1582
  }
1583
+ var sET = elementTimingValues(); // Element Timing data
1473
1584
  var sCPU = cpuTimes();
1474
- var DCLS = calculateDCLS();
1585
+ var CLS = getCLS$1();
1475
1586
  var sLuxjs = selfLoading();
1476
1587
  if (document.visibilityState && "visible" !== document.visibilityState) {
1477
1588
  gFlags = addFlag(gFlags, Flags.VisibilityStateNotVisible);
1478
1589
  }
1479
1590
  // We want ALL beacons to have ALL the data used for query filters (geo, pagelabel, browser, & customerdata).
1480
1591
  // So we create a base URL that has all the necessary information:
1481
- var baseUrl = _getBeaconUrl();
1592
+ var baseUrl = _getBeaconUrl(getAllCustomData());
1482
1593
  var is = inlineTagSize("script");
1483
1594
  var ic = inlineTagSize("style");
1484
1595
  var metricsQueryString =
@@ -1517,13 +1628,14 @@
1517
1628
  "er" +
1518
1629
  nErrors +
1519
1630
  "nt" +
1520
- navigationType() + // reload
1631
+ navigationType() +
1521
1632
  (navigator.deviceMemory ? "dm" + Math.round(navigator.deviceMemory) : "") + // device memory (GB)
1522
1633
  (sIx ? "&IX=" + sIx : "") +
1523
1634
  (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
1524
1635
  (sCPU ? "&CPU=" + sCPU : "") +
1525
1636
  (sET ? "&ET=" + sET : "") + // element timing
1526
- (DCLS !== false ? "&CLS=" + DCLS : "");
1637
+ (typeof CLS !== "undefined" ? "&CLS=" + CLS : "") +
1638
+ (typeof INP !== "undefined" ? "&INP=" + INP : "");
1527
1639
  // We add the user timing entries last so that we can split them to reduce the URL size if necessary.
1528
1640
  var utValues = userTimingValues();
1529
1641
  var _b = fitUserTimingEntries(utValues, globalConfig, baseUrl + metricsQueryString), beaconUtValues = _b[0], remainingUtValues = _b[1];
@@ -1545,6 +1657,11 @@
1545
1657
  _sendBeacon(utBeaconUrl);
1546
1658
  }
1547
1659
  }
1660
+ var ixTimerId;
1661
+ function _sendIxAfterDelay() {
1662
+ window.clearTimeout(ixTimerId);
1663
+ ixTimerId = window.setTimeout(_sendIx, 100);
1664
+ }
1548
1665
  // Beacon back the IX data separately (need to sync with LUX beacon on the backend).
1549
1666
  function _sendIx() {
1550
1667
  var customerid = getCustomerId();
@@ -1557,11 +1674,13 @@
1557
1674
  return;
1558
1675
  }
1559
1676
  var sIx = ixValues(); // Interaction Metrics
1677
+ var INP = getINP();
1560
1678
  if (sIx) {
1561
- var beaconUrl = _getBeaconUrl() +
1679
+ var beaconUrl = _getBeaconUrl(getUpdatedCustomData()) +
1562
1680
  "&IX=" +
1563
1681
  sIx +
1564
- (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "");
1682
+ (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
1683
+ (typeof INP !== "undefined" ? "&INP=" + INP : "");
1565
1684
  logger.logEvent(LogEvent.InteractionBeaconSent, [beaconUrl]);
1566
1685
  _sendBeacon(beaconUrl);
1567
1686
  gbIxSent = 1;
@@ -1578,9 +1697,9 @@
1578
1697
  ) {
1579
1698
  return;
1580
1699
  }
1581
- var sCustomerData = customerDataValues(); // customer data
1700
+ var sCustomerData = valuesToString(getUpdatedCustomData());
1582
1701
  if (sCustomerData) {
1583
- var beaconUrl = _getBeaconUrl();
1702
+ var beaconUrl = _getBeaconUrl(getUpdatedCustomData());
1584
1703
  logger.logEvent(LogEvent.CustomDataBeaconSent, [beaconUrl]);
1585
1704
  _sendBeacon(beaconUrl);
1586
1705
  }
@@ -1596,11 +1715,10 @@
1596
1715
  // Most of the time, however, IX happens *after* LUX, so we send a separate IX beacon but
1597
1716
  // only beacon back the first interaction that happens.
1598
1717
  function _scrollHandler() {
1599
- // Leave handlers IN PLACE so we can track which ID is clicked/keyed.
1600
- // _removeIxHandlers();
1718
+ // Note for scroll input we don't remove the handlers or send the IX beacon because we want to
1719
+ // capture click and key events as well, since these are typically more important than scrolls.
1601
1720
  if (typeof ghIx["s"] === "undefined") {
1602
1721
  ghIx["s"] = Math.round(_now());
1603
- // _sendIx(); // wait for key or click to send the IX beacon
1604
1722
  }
1605
1723
  }
1606
1724
  function _keyHandler(e) {
@@ -1613,7 +1731,7 @@
1613
1731
  ghIx["ki"] = trackId;
1614
1732
  }
1615
1733
  }
1616
- _sendIx();
1734
+ _sendIxAfterDelay();
1617
1735
  }
1618
1736
  }
1619
1737
  function _clickHandler(e) {
@@ -1641,7 +1759,7 @@
1641
1759
  ghIx["ci"] = trackId;
1642
1760
  }
1643
1761
  }
1644
- _sendIx();
1762
+ _sendIxAfterDelay();
1645
1763
  }
1646
1764
  }
1647
1765
  // Wrapper to support older browsers (<= IE8)
@@ -1706,9 +1824,9 @@
1706
1824
  if (inSampleBucket === void 0) { inSampleBucket = false; }
1707
1825
  if (inSampleBucket) {
1708
1826
  // "00" matches all sample rates
1709
- return "".concat(Number(new Date()), "00000");
1827
+ return Number(new Date()) + "00000";
1710
1828
  }
1711
- return "".concat(Number(new Date())).concat(_padLeft(String(Math.round(100000 * Math.random())), "00000"));
1829
+ return Number(new Date()) + _padLeft(String(Math.round(100000 * Math.random())), "00000");
1712
1830
  }
1713
1831
  // Unique ID (also known as Session ID)
1714
1832
  // We use this to track all the page views in a single user session.
@@ -1749,13 +1867,12 @@
1749
1867
  }
1750
1868
  else if (typeof LUX.pagegroups !== "undefined") {
1751
1869
  var pagegroups = LUX.pagegroups;
1752
- var url_1 = "".concat(document.location.hostname).concat(document.location.pathname);
1753
1870
  var label_1 = "";
1754
1871
  var _loop_1 = function (pagegroup) {
1755
1872
  var rules = pagegroups[pagegroup];
1756
1873
  if (Array.isArray(rules)) {
1757
1874
  rules.every(function (rule) {
1758
- if (Matching.isMatching(rule, url_1)) {
1875
+ if (patternMatchesUrl(rule, document.location.hostname, document.location.pathname)) {
1759
1876
  label_1 = pagegroup;
1760
1877
  return false; // stop when first match is found
1761
1878
  }
@@ -1763,7 +1880,7 @@
1763
1880
  });
1764
1881
  }
1765
1882
  // exits loop when first match is found
1766
- if (label_1.length) {
1883
+ if (label_1) {
1767
1884
  gFlags = addFlag(gFlags, Flags.PageLabelFromPagegroup);
1768
1885
  return { value: label_1 };
1769
1886
  }
@@ -1771,11 +1888,11 @@
1771
1888
  for (var pagegroup in pagegroups) {
1772
1889
  var state_1 = _loop_1(pagegroup);
1773
1890
  if (typeof state_1 === "object")
1774
- return state_1.value;
1891
+ return state_1.value;
1775
1892
  }
1776
1893
  }
1777
1894
  if (typeof LUX.jspagelabel !== "undefined") {
1778
- var evaluateJsPageLabel = Function("\"use strict\"; return ".concat(LUX.jspagelabel));
1895
+ var evaluateJsPageLabel = Function('"use strict"; return ' + LUX.jspagelabel);
1779
1896
  try {
1780
1897
  var label = evaluateJsPageLabel();
1781
1898
  if (label) {
@@ -1912,7 +2029,6 @@
1912
2029
  })();
1913
2030
  window.LUX = LUX;
1914
2031
  scriptEndTime = now();
1915
-
1916
2032
  // ---------------------------------------------------------------------------
1917
2033
  // More settings
1918
2034
  // ---------------------------------------------------------------------------
@@ -1934,3 +2050,4 @@
1934
2050
  // End of more settings
1935
2051
  // ---------------------------------------------------------------------------
1936
2052
  })();
2053
+ //# sourceMappingURL=lux.js.map