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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/govuk_publishing_components/analytics/explicit-cross-domain-links.js +72 -73
- data/app/assets/javascripts/govuk_publishing_components/analytics/page-content.js +11 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/gtm-click-tracking.js +49 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
- data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +0 -1
- data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/magna-charta.js +20 -6
- data/app/assets/javascripts/govuk_publishing_components/modules.js +0 -1
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-measurer.js +37 -0
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +166 -142
- data/app/assets/stylesheets/govuk_publishing_components/components/_cards.scss +11 -4
- data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +1 -1
- data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_attachment.scss +1 -1
- data/app/controllers/govuk_publishing_components/audit_controller.rb +1 -4
- data/app/models/govuk_publishing_components/audit_components.rb +61 -24
- data/app/views/govuk_publishing_components/audit/_applications.html.erb +8 -3
- data/app/views/govuk_publishing_components/audit/_component_contents.html.erb +82 -0
- data/app/views/govuk_publishing_components/audit/_components.html.erb +2 -47
- data/app/views/govuk_publishing_components/component_guide/index.html.erb +2 -2
- data/app/views/govuk_publishing_components/component_guide/show.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/_attachment.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/_attachment_link.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/_cards.html.erb +13 -11
- data/app/views/govuk_publishing_components/components/_summary_list.html.erb +4 -5
- data/app/views/govuk_publishing_components/components/docs/contents_list.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/document_list.yml +4 -4
- data/app/views/govuk_publishing_components/components/docs/heading.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/image_card.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/meta_tags.yml +4 -4
- data/app/views/govuk_publishing_components/components/docs/metadata.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/share_links.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/subscription_links.yml +1 -1
- data/app/views/govuk_publishing_components/components/docs/translation_nav.yml +1 -2
- data/config/locales/en.yml +0 -2
- data/lib/generators/govuk_publishing_components/templates/_component.html.erb +1 -1
- data/lib/govuk_publishing_components/app_helpers/brand_helper.rb +1 -1
- data/lib/govuk_publishing_components/presenters/{attachment.rb → attachment_helper.rb} +1 -2
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/lib/govuk_publishing_components.rb +1 -1
- 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([
|
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 = "
|
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
|
-
|
141
|
-
|
175
|
+
typeof e.filename !== "undefined" &&
|
176
|
+
typeof e.message !== "undefined"
|
142
177
|
) {
|
143
|
-
//
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
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 (
|
251
|
-
_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
|
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 (
|
363
|
-
return
|
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 (
|
373
|
-
|
374
|
-
|
375
|
-
|
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"
|
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 (
|
404
|
-
|
405
|
-
|
406
|
-
if (
|
407
|
-
|
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
|
436
|
+
return performance.measure(name, startMarkName);
|
414
437
|
}
|
415
|
-
} else
|
416
|
-
return
|
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 (
|
451
|
+
} else if (timing[startMarkName]) {
|
427
452
|
// the mark name can also be a property from Navigation Timing
|
428
|
-
startTime =
|
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 (
|
467
|
+
} else if (timing[endMarkName]) {
|
443
468
|
// the mark name can also be a property from Navigation Timing
|
444
|
-
endTime =
|
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
|
-
|
483
|
-
|
484
|
-
|
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
|
-
|
494
|
-
|
495
|
-
|
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"
|
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 =
|
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 =
|
630
|
-
if (
|
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"
|
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 (
|
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 =
|
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"
|
803
|
-
|
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
|
-
(
|
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 (
|
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 =
|
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
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
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 (
|
1063
|
-
var t =
|
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
|
-
|
1068
|
-
|
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
|
1075
|
-
|
1076
|
-
|
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
|
-
|
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
|
-
|
1098
|
-
|
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
|
-
|
1189
|
-
|
1190
|
-
|
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
|
-
|
1205
|
-
|
1206
|
-
"undefined" != typeof perf.navigation.type
|
1207
|
+
performance.navigation &&
|
1208
|
+
typeof performance.navigation.type !== "undefined"
|
1207
1209
|
) {
|
1208
|
-
return
|
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
|
-
|
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 (
|
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 (
|
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 (
|
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
|
-
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
|
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 =
|
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
|
-
|
1910
|
+
LUX_t_end = now();
|
1887
1911
|
|
1888
1912
|
// ---------------------------------------------------------------------------
|
1889
1913
|
// More settings
|