govuk_publishing_components 29.6.0 → 29.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics/explicit-cross-domain-links.js +72 -73
  3. data/app/assets/javascripts/govuk_publishing_components/analytics/page-content.js +11 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/gtm-click-tracking.js +49 -0
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  6. data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +0 -1
  7. data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/magna-charta.js +20 -6
  8. data/app/assets/javascripts/govuk_publishing_components/modules.js +0 -1
  9. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-measurer.js +37 -0
  10. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +166 -142
  11. data/app/assets/stylesheets/govuk_publishing_components/components/_cards.scss +11 -4
  12. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +1 -1
  13. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_attachment.scss +1 -1
  14. data/app/controllers/govuk_publishing_components/audit_controller.rb +1 -4
  15. data/app/models/govuk_publishing_components/audit_components.rb +61 -24
  16. data/app/views/govuk_publishing_components/audit/_applications.html.erb +8 -3
  17. data/app/views/govuk_publishing_components/audit/_component_contents.html.erb +82 -0
  18. data/app/views/govuk_publishing_components/audit/_components.html.erb +2 -47
  19. data/app/views/govuk_publishing_components/component_guide/index.html.erb +2 -2
  20. data/app/views/govuk_publishing_components/component_guide/show.html.erb +1 -1
  21. data/app/views/govuk_publishing_components/components/_attachment.html.erb +1 -1
  22. data/app/views/govuk_publishing_components/components/_attachment_link.html.erb +1 -1
  23. data/app/views/govuk_publishing_components/components/_cards.html.erb +13 -11
  24. data/app/views/govuk_publishing_components/components/_summary_list.html.erb +4 -5
  25. data/app/views/govuk_publishing_components/components/docs/contents_list.yml +1 -1
  26. data/app/views/govuk_publishing_components/components/docs/document_list.yml +4 -4
  27. data/app/views/govuk_publishing_components/components/docs/heading.yml +1 -1
  28. data/app/views/govuk_publishing_components/components/docs/image_card.yml +1 -1
  29. data/app/views/govuk_publishing_components/components/docs/meta_tags.yml +4 -4
  30. data/app/views/govuk_publishing_components/components/docs/metadata.yml +1 -1
  31. data/app/views/govuk_publishing_components/components/docs/share_links.yml +1 -1
  32. data/app/views/govuk_publishing_components/components/docs/subscription_links.yml +1 -1
  33. data/app/views/govuk_publishing_components/components/docs/translation_nav.yml +1 -2
  34. data/config/locales/en.yml +0 -2
  35. data/lib/generators/govuk_publishing_components/templates/_component.html.erb +1 -1
  36. data/lib/govuk_publishing_components/app_helpers/brand_helper.rb +1 -1
  37. data/lib/govuk_publishing_components/presenters/{attachment.rb → attachment_helper.rb} +1 -2
  38. data/lib/govuk_publishing_components/version.rb +1 -1
  39. data/lib/govuk_publishing_components.rb +1 -1
  40. metadata +7 -4
@@ -41,6 +41,7 @@
41
41
  maxErrors: getProperty(obj, "maxErrors", 5),
42
42
  maxMeasureTime: getProperty(obj, "maxMeasureTime", 60000),
43
43
  measureUntil: getProperty(obj, "measureUntil", "onload"),
44
+ minMeasureTime: getProperty(obj, "minMeasureTime", 0),
44
45
  samplerate: getProperty(obj, "samplerate", 100),
45
46
  sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", autoMode),
46
47
  trackErrors: getProperty(obj, "trackErrors", true),
@@ -63,6 +64,9 @@
63
64
  AddDataCalled: 6,
64
65
  SendCalled: 7,
65
66
  ForceSampleCalled: 8,
67
+ DataCollectionStart: 9,
68
+ UnloadHandlerTriggered: 10,
69
+ OnloadHandlerTriggered: 11,
66
70
  // Data collection events
67
71
  SessionIsSampled: 21,
68
72
  SessionIsNotSampled: 22,
@@ -70,6 +74,10 @@
70
74
  UserTimingBeaconSent: 24,
71
75
  InteractionBeaconSent: 25,
72
76
  CustomDataBeaconSent: 26,
77
+ // Metric information
78
+ NavigationStart: 41,
79
+ PerformanceEntryReceived: 42,
80
+ PerformanceEntryProcessed: 43,
73
81
  // Errors
74
82
  PerformanceObserverError: 51,
75
83
  InputEventPermissionError: 52,
@@ -82,7 +90,7 @@
82
90
  NavTimingNotSupported: 71,
83
91
  PaintTimingNotSupported: 72,
84
92
  };
85
- var Logger = (function () {
93
+ var Logger = /** @class */ (function () {
86
94
  function Logger() {
87
95
  this.events = [];
88
96
  }
@@ -90,7 +98,7 @@
90
98
  if (args === void 0) {
91
99
  args = [];
92
100
  }
93
- this.events.push([new Date(), event, args]);
101
+ this.events.push([now(), event, args]);
94
102
  };
95
103
  Logger.prototype.getEvents = function () {
96
104
  return this.events;
@@ -113,7 +121,34 @@
113
121
  return flags | flag;
114
122
  }
115
123
 
124
+ // If the various performance APIs aren't available, we export an empty object to
125
+ // prevent having to make regular typeof checks.
126
+ var performance = window.performance || {};
127
+ var timing = performance.timing || {};
128
+ /**
129
+ * Simple wrapper around performance.getEntriesByType to provide fallbacks for
130
+ * legacy browsers, and work around edge cases where undefined is returned instead
131
+ * of an empty PerformanceEntryList.
132
+ */
133
+ function getEntriesByType(type) {
134
+ if (typeof performance.getEntriesByType === "function") {
135
+ var entries = performance.getEntriesByType(type);
136
+ if (entries && entries.length) {
137
+ return entries;
138
+ }
139
+ } else if (typeof performance.webkitGetEntriesByType === "function") {
140
+ var entries = performance.webkitGetEntriesByType(type);
141
+ if (entries && entries.length) {
142
+ return entries;
143
+ }
144
+ }
145
+ return [];
146
+ }
147
+
116
148
  var LUX = window.LUX || {};
149
+ // Get a timestamp as close to navigationStart as possible.
150
+ var _navigationStart = LUX.ns ? LUX.ns : now();
151
+ var LUX_t_end = LUX_t_start;
117
152
  LUX = (function () {
118
153
  // -------------------------------------------------------------------------
119
154
  // Settings
@@ -124,7 +159,7 @@
124
159
  /// End
125
160
  // -------------------------------------------------------------------------
126
161
 
127
- var SCRIPT_VERSION = "300";
162
+ var SCRIPT_VERSION = "301";
128
163
  var logger = new Logger();
129
164
  var userConfig = fromObject(LUX);
130
165
  logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION]);
@@ -137,17 +172,13 @@
137
172
  nErrors++;
138
173
  if (
139
174
  e &&
140
- "undefined" !== typeof e.filename &&
141
- "undefined" !== typeof e.message
175
+ typeof e.filename !== "undefined" &&
176
+ typeof e.message !== "undefined"
142
177
  ) {
143
- // it is a valid error object
144
- if (
145
- -1 !== e.filename.indexOf("/lux.js?") ||
146
- -1 !== e.message.indexOf("LUX") || // Always send LUX errors.
147
- (nErrors <= userConfig.maxErrors &&
148
- "function" === typeof _sample &&
149
- _sample())
150
- ) {
178
+ // Always send LUX errors
179
+ var isLuxError =
180
+ e.filename.indexOf("/lux.js?") > -1 || e.message.indexOf("LUX") > -1;
181
+ if (isLuxError || (nErrors <= userConfig.maxErrors && _sample())) {
151
182
  // Sample & limit other errors.
152
183
  // Send the error beacon.
153
184
  new Image().src =
@@ -184,8 +215,8 @@
184
215
  var gaPerfEntries = gaSnippetLongTasks.slice(); // array of Long Tasks (prefer the array from the snippet)
185
216
  if (typeof PerformanceObserver === "function") {
186
217
  var perfObserver = new PerformanceObserver(function (list) {
187
- // Keep an array of perf objects to process later.
188
218
  list.getEntries().forEach(function (entry) {
219
+ logger.logEvent(LogEvent.PerformanceEntryReceived, [entry]);
189
220
  // Only record long tasks that weren't already recorded by the PerformanceObserver in the snippet
190
221
  if (
191
222
  entry.entryType !== "longtask" ||
@@ -237,24 +268,23 @@
237
268
  var gSyncId = createSyncId(); // if we send multiple beacons, use this to sync them (eg, LUX & IX) (also called "luxid")
238
269
  var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
239
270
  var gCustomerDataTimeout; // setTimeout timer for sending a Customer Data beacon after onload
240
- var perf = window.performance;
271
+ var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
241
272
  var gMaxQuerystring = 8190; // split the beacon querystring if it gets longer than this
242
273
  if (_sample()) {
243
274
  logger.logEvent(LogEvent.SessionIsSampled, [userConfig.samplerate]);
244
275
  } else {
245
276
  logger.logEvent(LogEvent.SessionIsNotSampled, [userConfig.samplerate]);
246
277
  }
247
- // Get a timestamp as close to navigationStart as possible.
248
- var _navigationStart = LUX.ns ? LUX.ns : now(); // create a _navigationStart
249
278
  var gLuxSnippetStart = 0;
250
- if (perf && perf.timing && perf.timing.navigationStart) {
251
- _navigationStart = perf.timing.navigationStart;
279
+ if (timing.navigationStart) {
280
+ _navigationStart = timing.navigationStart;
252
281
  // Record when the LUX snippet was evaluated relative to navigationStart.
253
282
  gLuxSnippetStart = LUX.ns ? LUX.ns - _navigationStart : 0;
254
283
  } else {
255
284
  logger.logEvent(LogEvent.NavTimingNotSupported);
256
285
  gFlags = addFlag(gFlags, Flags.NavTimingNotSupported);
257
286
  }
287
+ logger.logEvent(LogEvent.NavigationStart, [_navigationStart]);
258
288
  ////////////////////// FID BEGIN
259
289
  // FIRST INPUT DELAY (FID)
260
290
  // The basic idea behind FID is to attach various input event listeners and measure the time
@@ -351,16 +381,15 @@
351
381
  * in SPAs.
352
382
  */
353
383
  function _now(absolute) {
354
- var currentTimestamp = Date.now ? Date.now() : +new Date();
355
- var msSinceNavigationStart = currentTimestamp - _navigationStart;
384
+ var msSinceNavigationStart = now() - _navigationStart;
356
385
  var startMark = _getMark(gStartMark);
357
386
  // For SPA page views, we use our internal mark as a reference point
358
387
  if (startMark && !absolute) {
359
388
  return msSinceNavigationStart - startMark.startTime;
360
389
  }
361
390
  // For "regular" page views, we can use performance.now() if it's available...
362
- if (perf && perf.now) {
363
- return perf.now();
391
+ if (performance.now) {
392
+ return performance.now();
364
393
  }
365
394
  // ... or we can use navigationStart as a reference point
366
395
  return msSinceNavigationStart;
@@ -369,12 +398,10 @@
369
398
  // NOTE: It's possible to set multiple marks with the same name.
370
399
  function _mark(name) {
371
400
  logger.logEvent(LogEvent.MarkCalled, [name]);
372
- if (perf) {
373
- if (perf.mark) {
374
- return perf.mark(name);
375
- } else if (perf.webkitMark) {
376
- return perf.webkitMark(name);
377
- }
401
+ if (performance.mark) {
402
+ return performance.mark(name);
403
+ } else if (performance.webkitMark) {
404
+ return performance.webkitMark(name);
378
405
  }
379
406
  gFlags = addFlag(gFlags, Flags.UserTimingNotSupported);
380
407
  // Shim
@@ -395,26 +422,24 @@
395
422
  startMarkName,
396
423
  endMarkName,
397
424
  ]);
398
- if ("undefined" === typeof startMarkName && _getMark(gStartMark)) {
425
+ if (typeof startMarkName === "undefined" && _getMark(gStartMark)) {
399
426
  // If a start mark is not specified, but the user has called _init() to set a new start,
400
427
  // then use the new start base time (similar to navigationStart) as the start mark.
401
428
  startMarkName = gStartMark;
402
429
  }
403
- if (perf) {
404
- if (perf.measure) {
405
- // IE 11 does not handle null and undefined correctly
406
- if (startMarkName) {
407
- if (endMarkName) {
408
- return perf.measure(name, startMarkName, endMarkName);
409
- } else {
410
- return perf.measure(name, startMarkName);
411
- }
430
+ if (performance.measure) {
431
+ // IE 11 does not handle null and undefined correctly
432
+ if (startMarkName) {
433
+ if (endMarkName) {
434
+ return performance.measure(name, startMarkName, endMarkName);
412
435
  } else {
413
- return perf.measure(name);
436
+ return performance.measure(name, startMarkName);
414
437
  }
415
- } else if (perf.webkitMeasure) {
416
- return perf.webkitMeasure(name, startMarkName, endMarkName);
438
+ } else {
439
+ return performance.measure(name);
417
440
  }
441
+ } else if (performance.webkitMeasure) {
442
+ return performance.webkitMeasure(name, startMarkName, endMarkName);
418
443
  }
419
444
  // shim:
420
445
  var startTime = 0,
@@ -423,9 +448,9 @@
423
448
  var startMark = _getMark(startMarkName);
424
449
  if (startMark) {
425
450
  startTime = startMark.startTime;
426
- } else if (perf && perf.timing && perf.timing[startMarkName]) {
451
+ } else if (timing[startMarkName]) {
427
452
  // the mark name can also be a property from Navigation Timing
428
- startTime = perf.timing[startMarkName] - perf.timing.navigationStart;
453
+ startTime = timing[startMarkName] - timing.navigationStart;
429
454
  } else {
430
455
  throw new DOMException(
431
456
  "Failed to execute 'measure' on 'Performance': The mark '".concat(
@@ -439,9 +464,9 @@
439
464
  var endMark = _getMark(endMarkName);
440
465
  if (endMark) {
441
466
  endTime = endMark.startTime;
442
- } else if (perf && perf.timing && perf.timing[endMarkName]) {
467
+ } else if (timing[endMarkName]) {
443
468
  // the mark name can also be a property from Navigation Timing
444
- endTime = perf.timing[endMarkName] - perf.timing.navigationStart;
469
+ endTime = timing[endMarkName] - timing.navigationStart;
445
470
  } else {
446
471
  throw new DOMException(
447
472
  "Failed to execute 'measure' on 'Performance': The mark '".concat(
@@ -479,23 +504,17 @@
479
504
  }
480
505
  // Return an array of marks.
481
506
  function _getMarks() {
482
- if (perf) {
483
- if (perf.getEntriesByType) {
484
- return perf.getEntriesByType("mark");
485
- } else if (perf.webkitGetEntriesByType) {
486
- return perf.webkitGetEntriesByType("mark");
487
- }
507
+ var marks = getEntriesByType("mark");
508
+ if (marks.length) {
509
+ return marks;
488
510
  }
489
511
  return gaMarks;
490
512
  }
491
513
  // Return an array of measures.
492
514
  function _getMeasures() {
493
- if (perf) {
494
- if (perf.getEntriesByType) {
495
- return perf.getEntriesByType("measure");
496
- } else if (perf.webkitGetEntriesByType) {
497
- return perf.webkitGetEntriesByType("measure");
498
- }
515
+ var measures = getEntriesByType("measure");
516
+ if (measures.length) {
517
+ return measures;
499
518
  }
500
519
  return gaMeasures;
501
520
  }
@@ -568,6 +587,7 @@
568
587
  for (var i = 0; i < gaPerfEntries.length; i++) {
569
588
  var pe = gaPerfEntries[i];
570
589
  if ("element" === pe.entryType && pe.identifier && pe.startTime) {
590
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [pe]);
571
591
  aET.push(pe.identifier + "|" + Math.round(pe.startTime));
572
592
  }
573
593
  }
@@ -576,7 +596,7 @@
576
596
  }
577
597
  // Return a string of CPU times formatted for beacon querystring.
578
598
  function cpuTimes() {
579
- if ("function" !== typeof PerformanceLongTaskTiming) {
599
+ if (typeof PerformanceLongTaskTiming !== "function") {
580
600
  // Do not return any CPU metrics if Long Tasks API is not supported.
581
601
  return "";
582
602
  }
@@ -591,7 +611,7 @@
591
611
  var tZero = startMark ? startMark.startTime : 0;
592
612
  // Do not include Long Tasks that start after the page is done.
593
613
  // For full page loads, "done" is loadEventEnd.
594
- var tEnd = perf.timing.loadEventEnd - perf.timing.navigationStart;
614
+ var tEnd = timing.loadEventEnd - timing.navigationStart;
595
615
  if (startMark) {
596
616
  // For SPA page loads (determined by the presence of a start mark), "done" is gEndMark.
597
617
  var endMark = _getMark(gEndMark);
@@ -614,6 +634,7 @@
614
634
  // callback from setTimeout(200) happened. Do not include anything that started after tEnd.
615
635
  continue;
616
636
  }
637
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [p]);
617
638
  var type = p.attribution[0].name; // TODO - is there ever more than 1 attribution???
618
639
  if (!hCPU[type]) {
619
640
  // initialize this category
@@ -626,8 +647,8 @@
626
647
  }
627
648
  }
628
649
  // TODO - Add more types if/when they become available.
629
- var jsType = "undefined" !== typeof hCPU["script"] ? "script" : "unknown"; // spec changed from "script" to "unknown" Nov 2018
630
- if ("undefined" === typeof hCPU[jsType]) {
650
+ var jsType = typeof hCPU["script"] !== "undefined" ? "script" : "unknown"; // spec changed from "script" to "unknown" Nov 2018
651
+ if (typeof hCPU[jsType] === "undefined") {
631
652
  // Initialize default values for pages that have *no Long Tasks*.
632
653
  hCPU[jsType] = 0;
633
654
  hCPUDetails[jsType] = "";
@@ -681,7 +702,7 @@
681
702
  return { count: count, median: median, max: max, fci: fci };
682
703
  }
683
704
  function calculateDCLS() {
684
- if ("function" !== typeof LayoutShift) {
705
+ if (typeof LayoutShift !== "function") {
685
706
  return false;
686
707
  }
687
708
  var DCLS = 0;
@@ -690,6 +711,7 @@
690
711
  if ("layout-shift" !== p.entryType || p.hadRecentInput) {
691
712
  continue;
692
713
  }
714
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [p]);
693
715
  DCLS += p.value;
694
716
  }
695
717
  // The DCL column in Redshift is REAL (FLOAT4) which stores a maximum
@@ -716,11 +738,11 @@
716
738
  // Track how long it took lux.js to load via Resource Timing.
717
739
  function selfLoading() {
718
740
  var sLuxjs = "";
719
- if (perf && perf.getEntriesByName) {
741
+ if (performance.getEntriesByName) {
720
742
  // Get the lux script URL (including querystring params).
721
743
  var luxScript = getScriptElement("/js/lux.js");
722
744
  if (luxScript) {
723
- var aResources = perf.getEntriesByName(luxScript.src);
745
+ var aResources = performance.getEntriesByName(luxScript.src);
724
746
  if (aResources && aResources.length) {
725
747
  var r = aResources[0];
726
748
  // DO NOT USE DURATION!!!!!
@@ -799,8 +821,8 @@
799
821
  // Return true if beacons for this page should be sampled.
800
822
  function _sample() {
801
823
  if (
802
- "undefined" === typeof gUid ||
803
- "undefined" === typeof userConfig.samplerate
824
+ typeof gUid === "undefined" ||
825
+ typeof userConfig.samplerate === "undefined"
804
826
  ) {
805
827
  return false; // bail
806
828
  }
@@ -851,6 +873,8 @@
851
873
  gFlags = addFlag(gFlags, Flags.InitCalled);
852
874
  // Mark the "navigationStart" for this SPA page.
853
875
  _mark(gStartMark);
876
+ // Reset the maximum measure timeout
877
+ createMaxMeasureTimeout();
854
878
  }
855
879
  // Return the number of blocking (synchronous) external scripts in the page.
856
880
  function blockingScripts() {
@@ -891,7 +915,7 @@
891
915
  e.onloadcssdefined ||
892
916
  "print" === e.media ||
893
917
  "style" === e.as ||
894
- ("function" === typeof e.onload && "all" === e.media)
918
+ (typeof e.onload === "function" && e.media === "all")
895
919
  );
896
920
  else {
897
921
  nBlocking++;
@@ -971,9 +995,9 @@
971
995
  "le" +
972
996
  end +
973
997
  "";
974
- } else if (perf && perf.timing) {
998
+ } else if (performance.timing) {
975
999
  // Return the real Nav Timing metrics because this is the "main" page view (not a SPA)
976
- var t = perf.timing;
1000
+ var t = timing;
977
1001
  var startRender = getStartRender(); // first paint
978
1002
  var fcp = getFcp(); // first contentful paint
979
1003
  var lcp = getLcp(); // largest contentful paint
@@ -1024,20 +1048,11 @@
1024
1048
  }
1025
1049
  // Return First Contentful Paint or zero if not supported.
1026
1050
  function getFcp() {
1027
- if (
1028
- perf &&
1029
- perf.getEntriesByType &&
1030
- perf.getEntriesByType("paint").length
1031
- ) {
1032
- for (
1033
- var arr = perf.getEntriesByType("paint"), i = 0;
1034
- i < arr.length;
1035
- i++
1036
- ) {
1037
- var ppt = arr[i]; // PerformancePaintTiming object
1038
- if ("first-contentful-paint" === ppt.name) {
1039
- return Math.round(ppt.startTime);
1040
- }
1051
+ var paintEntries = getEntriesByType("paint");
1052
+ for (var i = 0; i < paintEntries.length; i++) {
1053
+ var entry = paintEntries[i];
1054
+ if (entry.name === "first-contentful-paint") {
1055
+ return Math.round(entry.startTime);
1041
1056
  }
1042
1057
  }
1043
1058
  return 0;
@@ -1049,6 +1064,7 @@
1049
1064
  for (var i = gaPerfEntries.length - 1; i >= 0; i--) {
1050
1065
  var pe = gaPerfEntries[i];
1051
1066
  if ("largest-contentful-paint" === pe.entryType) {
1067
+ logger.logEvent(LogEvent.PerformanceEntryProcessed, [pe]);
1052
1068
  return Math.round(pe.startTime);
1053
1069
  }
1054
1070
  }
@@ -1059,31 +1075,24 @@
1059
1075
  // Mostly works on just Chrome and IE.
1060
1076
  // Return null if not supported.
1061
1077
  function getStartRender() {
1062
- if (perf && perf.timing) {
1063
- var t = perf.timing;
1078
+ if (performance.timing) {
1079
+ var t = timing;
1064
1080
  var ns = t.navigationStart;
1065
1081
  var startRender = void 0;
1066
1082
  if (ns) {
1067
- if (
1068
- perf &&
1069
- perf.getEntriesByType &&
1070
- perf.getEntriesByType("paint").length
1071
- ) {
1083
+ var paintEntries = getEntriesByType("paint");
1084
+ if (paintEntries.length) {
1072
1085
  // If Paint Timing API is supported, use it.
1073
- for (
1074
- var arr = perf.getEntriesByType("paint"), i = 0;
1075
- i < arr.length;
1076
- i++
1077
- ) {
1078
- var ppt = arr[i]; // PerformancePaintTiming object
1079
- if ("first-paint" === ppt.name) {
1080
- startRender = Math.round(ppt.startTime);
1086
+ for (var i = 0; i < paintEntries.length; i++) {
1087
+ var entry = paintEntries[i];
1088
+ if (entry.name === "first-paint") {
1089
+ startRender = Math.round(entry.startTime);
1081
1090
  break;
1082
1091
  }
1083
1092
  }
1084
1093
  } else if (
1085
1094
  window.chrome &&
1086
- "function" === typeof window.chrome.loadTimes
1095
+ typeof window.chrome.loadTimes === "function"
1087
1096
  ) {
1088
1097
  // If chrome, get first paint time from `chrome.loadTimes`. Need extra error handling.
1089
1098
  var loadTimes = window.chrome.loadTimes();
@@ -1094,9 +1103,9 @@
1094
1103
  // If IE/Edge, use the prefixed `msFirstPaint` property (see http://msdn.microsoft.com/ff974719).
1095
1104
  startRender = Math.round(t.msFirstPaint - ns);
1096
1105
  }
1097
- if (startRender) {
1098
- return startRender;
1099
- }
1106
+ }
1107
+ if (startRender) {
1108
+ return startRender;
1100
1109
  }
1101
1110
  }
1102
1111
  logger.logEvent(LogEvent.PaintTimingNotSupported);
@@ -1185,15 +1194,9 @@
1185
1194
  }
1186
1195
  // Return the main HTML document transfer size (in bytes).
1187
1196
  function docSize() {
1188
- if (
1189
- perf &&
1190
- perf.getEntriesByType &&
1191
- perf.getEntriesByType("navigation").length
1192
- ) {
1193
- var aEntries = performance.getEntriesByType("navigation");
1194
- if (aEntries && aEntries.length > 0 && aEntries[0]["encodedBodySize"]) {
1195
- return aEntries[0]["encodedBodySize"];
1196
- }
1197
+ var aEntries = getEntriesByType("navigation");
1198
+ if (aEntries.length && aEntries[0]["encodedBodySize"]) {
1199
+ return aEntries[0]["encodedBodySize"];
1197
1200
  }
1198
1201
  return 0; // ERROR - NOT FOUND
1199
1202
  }
@@ -1201,11 +1204,10 @@
1201
1204
  // Return empty string if not available.
1202
1205
  function navigationType() {
1203
1206
  if (
1204
- perf &&
1205
- perf.navigation &&
1206
- "undefined" != typeof perf.navigation.type
1207
+ performance.navigation &&
1208
+ typeof performance.navigation.type !== "undefined"
1207
1209
  ) {
1208
- return perf.navigation.type;
1210
+ return performance.navigation.type;
1209
1211
  }
1210
1212
  return "";
1211
1213
  }
@@ -1310,19 +1312,31 @@
1310
1312
  function _markLoadTime() {
1311
1313
  _mark(gEndMark);
1312
1314
  }
1315
+ function createMaxMeasureTimeout() {
1316
+ clearMaxMeasureTimeout();
1317
+ gMaxMeasureTimeout = window.setTimeout(function () {
1318
+ gFlags = addFlag(gFlags, Flags.BeaconSentAfterTimeout);
1319
+ _sendLux();
1320
+ }, userConfig.maxMeasureTime - _now());
1321
+ }
1322
+ function clearMaxMeasureTimeout() {
1323
+ if (gMaxMeasureTimeout) {
1324
+ clearTimeout(gMaxMeasureTimeout);
1325
+ }
1326
+ }
1313
1327
  // Beacon back the LUX data.
1314
1328
  function _sendLux() {
1315
- logger.logEvent(LogEvent.SendCalled);
1329
+ clearMaxMeasureTimeout();
1316
1330
  var customerid = getCustomerId();
1317
1331
  if (
1318
1332
  !customerid ||
1319
1333
  !gSyncId ||
1320
- !validDomain() ||
1321
1334
  !_sample() || // OUTSIDE the sampled range
1322
1335
  gbLuxSent // LUX data already sent
1323
1336
  ) {
1324
1337
  return;
1325
1338
  }
1339
+ logger.logEvent(LogEvent.DataCollectionStart);
1326
1340
  var startMark = _getMark(gStartMark);
1327
1341
  var endMark = _getMark(gEndMark);
1328
1342
  if (!startMark || (endMark && endMark.startTime < startMark.startTime)) {
@@ -1472,7 +1486,6 @@
1472
1486
  if (
1473
1487
  !customerid ||
1474
1488
  !gSyncId ||
1475
- !validDomain() ||
1476
1489
  !_sample() || // OUTSIDE the sampled range
1477
1490
  gbIxSent || // IX data already sent
1478
1491
  !gbLuxSent // LUX has NOT been sent yet, so wait to include it there
@@ -1514,7 +1527,6 @@
1514
1527
  if (
1515
1528
  !customerid ||
1516
1529
  !gSyncId ||
1517
- !validDomain() ||
1518
1530
  !_sample() || // OUTSIDE the sampled range
1519
1531
  !gbLuxSent // LUX has NOT been sent yet, so wait to include it there
1520
1532
  ) {
@@ -1599,14 +1611,14 @@
1599
1611
  function _scrollHandler() {
1600
1612
  // Leave handlers IN PLACE so we can track which ID is clicked/keyed.
1601
1613
  // _removeIxHandlers();
1602
- if ("undefined" === typeof ghIx["s"]) {
1614
+ if (typeof ghIx["s"] === "undefined") {
1603
1615
  ghIx["s"] = Math.round(_now());
1604
1616
  // _sendIx(); // wait for key or click to send the IX beacon
1605
1617
  }
1606
1618
  }
1607
1619
  function _keyHandler(e) {
1608
1620
  _removeIxHandlers();
1609
- if ("undefined" === typeof ghIx["k"]) {
1621
+ if (typeof ghIx["k"] === "undefined") {
1610
1622
  ghIx["k"] = Math.round(_now());
1611
1623
  if (e && e.target) {
1612
1624
  var trackId = interactionAttributionForElement(e.target);
@@ -1619,7 +1631,7 @@
1619
1631
  }
1620
1632
  function _clickHandler(e) {
1621
1633
  _removeIxHandlers();
1622
- if ("undefined" === typeof ghIx["c"]) {
1634
+ if (typeof ghIx["c"] === "undefined") {
1623
1635
  ghIx["c"] = Math.round(_now());
1624
1636
  var target = null;
1625
1637
  try {
@@ -1672,6 +1684,7 @@
1672
1684
  function _addUnloadHandlers() {
1673
1685
  var onunload = function () {
1674
1686
  gFlags = addFlag(gFlags, Flags.BeaconSentFromUnloadHandler);
1687
+ logger.logEvent(LogEvent.UnloadHandlerTriggered);
1675
1688
  _sendLux();
1676
1689
  _sendIx();
1677
1690
  };
@@ -1775,14 +1788,6 @@
1775
1788
  gFlags = addFlag(gFlags, Flags.PageLabelFromDocumentTitle);
1776
1789
  return document.title;
1777
1790
  }
1778
- // Return true if the hostname of the current page is one of the listed domains.
1779
- function validDomain() {
1780
- // Our signup process is such that a customer almost always deploys lux.js BEFORE we
1781
- // enable LUX for their account. In which case, the list of domains is empty and no
1782
- // beacons will be sent. Further, that version of lux.js will be cached at the CDN
1783
- // and browser for a week. Instead, do the domain validation on the backend in VCL.
1784
- return true;
1785
- }
1786
1791
  function _getCookie(name) {
1787
1792
  try {
1788
1793
  // Seeing "Permission denied" errors, so do a simple try-catch.
@@ -1818,15 +1823,29 @@
1818
1823
  // Set "LUX.auto=false" to disable send results automatically and
1819
1824
  // instead you must call LUX.send() explicitly.
1820
1825
  if (userConfig.auto) {
1821
- if (document.readyState === "complete") {
1822
- // If onload has already passed, send the beacon now.
1823
- _sendLux();
1824
- } else {
1825
- // Ow, send the beacon slightly after window.onload.
1826
- addListener("load", function () {
1827
- setTimeout(_sendLux, 200);
1828
- });
1829
- }
1826
+ var sendBeaconAfterMinimumMeasureTime_1 = function () {
1827
+ var elapsedTime = _now();
1828
+ var timeRemaining = userConfig.minMeasureTime - elapsedTime;
1829
+ if (timeRemaining <= 0) {
1830
+ logger.logEvent(LogEvent.OnloadHandlerTriggered, [
1831
+ elapsedTime,
1832
+ userConfig.minMeasureTime,
1833
+ ]);
1834
+ if (document.readyState === "complete") {
1835
+ // If onload has already passed, send the beacon now.
1836
+ _sendLux();
1837
+ } else {
1838
+ // Ow, send the beacon slightly after window.onload.
1839
+ addListener("load", function () {
1840
+ setTimeout(_sendLux, 200);
1841
+ });
1842
+ }
1843
+ } else {
1844
+ // Try again after the minimum measurement time has elapsed
1845
+ setTimeout(sendBeaconAfterMinimumMeasureTime_1, timeRemaining);
1846
+ }
1847
+ };
1848
+ sendBeaconAfterMinimumMeasureTime_1();
1830
1849
  }
1831
1850
  // Add the unload handlers for auto mode, or when LUX.measureUntil is "pagehidden"
1832
1851
  if (userConfig.sendBeaconOnPageHidden) {
@@ -1834,6 +1853,8 @@
1834
1853
  }
1835
1854
  // Regardless of userConfig.auto, we need to register the IX handlers immediately.
1836
1855
  _addIxHandlers();
1856
+ // Set the maximum measurement timer
1857
+ createMaxMeasureTimeout();
1837
1858
  // This is the public API.
1838
1859
  var _LUX = userConfig;
1839
1860
  // Functions
@@ -1841,7 +1862,10 @@
1841
1862
  _LUX.measure = _measure;
1842
1863
  _LUX.init = _init;
1843
1864
  _LUX.markLoadTime = _markLoadTime;
1844
- _LUX.send = _sendLux;
1865
+ _LUX.send = function () {
1866
+ logger.logEvent(LogEvent.SendCalled);
1867
+ _sendLux();
1868
+ };
1845
1869
  _LUX.addData = _addData;
1846
1870
  _LUX.getSessionId = _getUniqueId; // so customers can do their own sampling
1847
1871
  _LUX.getDebug = function () {
@@ -1883,7 +1907,7 @@
1883
1907
  return _LUX;
1884
1908
  })();
1885
1909
  window.LUX = LUX;
1886
- var LUX_t_end = now();
1910
+ LUX_t_end = now();
1887
1911
 
1888
1912
  // ---------------------------------------------------------------------------
1889
1913
  // More settings