govuk_publishing_components 35.15.0 → 35.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +11 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +1 -1
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +11 -9
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +331 -327
- data/app/assets/stylesheets/govuk_publishing_components/components/_accordion.scss +6 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_govspeak.scss +6 -0
- data/app/views/govuk_publishing_components/components/_attachment.html.erb +2 -1
- data/app/views/govuk_publishing_components/components/layout_for_public/_account-layout.html.erb +1 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e84a39ca858aeb5f82b99db7e36787fcb9de0789d216a01ae705edc8b6412e50
|
|
4
|
+
data.tar.gz: cd621c972ba4b393cd8ca2921a43e45101dcdd1330c5d1dc80eee74b11ac1556
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac457ab2307e9a0c5e38b494b54268c34113521a93e25d1d0bc99378b075b8ae8a9a3ba4477a6dc7249fc58e1d42be51600f3746019109eff8b31460d3ae8a6d
|
|
7
|
+
data.tar.gz: 8369bcf1d142762cd85e4c5e9a483560b1e904071dd73e500407c8abfffa692cc7f60745debae002861850a08a170a1fbc90c5b0ae7f72abe972e5ba089de898
|
|
@@ -205,6 +205,10 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
|
|
|
205
205
|
}
|
|
206
206
|
},
|
|
207
207
|
|
|
208
|
+
getPathname: function () {
|
|
209
|
+
return window.location.pathname
|
|
210
|
+
},
|
|
211
|
+
|
|
208
212
|
getProtocol: function () {
|
|
209
213
|
return window.location.protocol
|
|
210
214
|
},
|
|
@@ -252,6 +256,13 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
|
|
|
252
256
|
|
|
253
257
|
applyRedactionIfRequired: function (PIIRemover, element, data) {
|
|
254
258
|
return element.closest('[data-ga4-do-not-redact]') ? data : PIIRemover.stripPIIWithOverride(data, true, true)
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
appendPathToAnchorLinks: function (url) {
|
|
262
|
+
if (!this.stringStartsWith(url, '#') || this.getPathname() === '/') {
|
|
263
|
+
return url
|
|
264
|
+
}
|
|
265
|
+
return this.getPathname() + url
|
|
255
266
|
}
|
|
256
267
|
},
|
|
257
268
|
|
|
@@ -74,7 +74,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
|
|
|
74
74
|
if (aTag) {
|
|
75
75
|
var href = aTag.getAttribute('href')
|
|
76
76
|
if (href) {
|
|
77
|
-
schema.event_data.url = href
|
|
77
|
+
schema.event_data.url = window.GOVUK.analyticsGa4.core.trackFunctions.appendPathToAnchorLinks(href)
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -62,13 +62,13 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
|
|
|
62
62
|
|
|
63
63
|
Ga4LinkTracker.prototype.trackClick = function (event) {
|
|
64
64
|
var element = event.target
|
|
65
|
-
|
|
65
|
+
var trackFunctions = window.GOVUK.analyticsGa4.core.trackFunctions
|
|
66
66
|
// don't track this link if it's already being tracked by the ecommerce tracker
|
|
67
67
|
if (element.closest('[data-ga4-ecommerce-path]')) {
|
|
68
68
|
return
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
var target =
|
|
71
|
+
var target = trackFunctions.findTrackingAttributes(event.target, this.trackingTrigger)
|
|
72
72
|
if (target) {
|
|
73
73
|
try {
|
|
74
74
|
var data = target.getAttribute(this.trackingTrigger)
|
|
@@ -80,17 +80,19 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
var text = data.text || event.target.textContent
|
|
83
|
-
data.text =
|
|
84
|
-
data.text =
|
|
83
|
+
data.text = trackFunctions.removeLinesAndExtraSpaces(text)
|
|
84
|
+
data.text = trackFunctions.applyRedactionIfRequired(this.PIIRemover, element, data.text)
|
|
85
85
|
if (!data.text && (element.querySelector('img') || element.querySelector('svg') || element.tagName === 'IMG' || element.closest('svg'))) {
|
|
86
86
|
data.text = 'image'
|
|
87
87
|
}
|
|
88
88
|
var url = data.url || this.findLink(event.target).getAttribute('href')
|
|
89
|
-
data.url =
|
|
90
|
-
data.
|
|
91
|
-
data.
|
|
92
|
-
data.
|
|
93
|
-
data.
|
|
89
|
+
data.url = trackFunctions.removeCrossDomainParams(url)
|
|
90
|
+
data.url = trackFunctions.applyRedactionIfRequired(this.PIIRemover, element, data.url)
|
|
91
|
+
data.url = trackFunctions.appendPathToAnchorLinks(data.url)
|
|
92
|
+
data.link_domain = trackFunctions.populateLinkDomain(data.url)
|
|
93
|
+
data.link_path_parts = trackFunctions.populateLinkPathParts(data.url)
|
|
94
|
+
data.method = trackFunctions.getClickType(event)
|
|
95
|
+
data.external = trackFunctions.isExternalLink(data.url) ? 'true' : 'false'
|
|
94
96
|
data.index = this.setIndex(data.index, event.target)
|
|
95
97
|
|
|
96
98
|
if (data.type === 'smart answer' && data.action === 'change response') {
|
|
@@ -21,33 +21,34 @@
|
|
|
21
21
|
|
|
22
22
|
(function () {
|
|
23
23
|
'use strict';
|
|
24
|
+
|
|
24
25
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
* Fit an array of user timing delimited strings into a URL and return both the entries that fit and
|
|
27
|
+
* the remaining entries that didn't fit.
|
|
28
|
+
*/
|
|
28
29
|
function fitUserTimingEntries(utValues, config, url) {
|
|
29
30
|
// Start with the maximum allowed UT entries per beacon
|
|
30
31
|
var beaconUtValues = utValues.slice(0, config.maxBeaconUTEntries);
|
|
31
32
|
var remainingUtValues = utValues.slice(config.maxBeaconUTEntries);
|
|
32
33
|
// Trim UT entries until they fit within the maximum URL length, ensuring at least one UT entry
|
|
33
34
|
// is included.
|
|
34
|
-
while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength &&
|
|
35
|
-
beaconUtValues.length > 1) {
|
|
35
|
+
while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength && beaconUtValues.length > 1) {
|
|
36
36
|
remainingUtValues.unshift(beaconUtValues.pop());
|
|
37
37
|
}
|
|
38
38
|
return [beaconUtValues, remainingUtValues];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
var luxOrigin = "https://lux.speedcurve.com";
|
|
41
42
|
function fromObject(obj) {
|
|
42
43
|
var autoMode = getProperty(obj, "auto", true);
|
|
43
44
|
return {
|
|
44
45
|
auto: autoMode,
|
|
45
|
-
beaconUrl: getProperty(obj, "beaconUrl", "
|
|
46
|
-
conversions: getProperty(obj, "conversions"
|
|
47
|
-
customerid: getProperty(obj, "customerid"
|
|
48
|
-
errorBeaconUrl: getProperty(obj, "errorBeaconUrl", "
|
|
49
|
-
jspagelabel: getProperty(obj, "jspagelabel"
|
|
50
|
-
label: getProperty(obj, "label"
|
|
46
|
+
beaconUrl: getProperty(obj, "beaconUrl", luxOrigin + "/lux/"),
|
|
47
|
+
conversions: getProperty(obj, "conversions"),
|
|
48
|
+
customerid: getProperty(obj, "customerid"),
|
|
49
|
+
errorBeaconUrl: getProperty(obj, "errorBeaconUrl", luxOrigin + "/error/"),
|
|
50
|
+
jspagelabel: getProperty(obj, "jspagelabel"),
|
|
51
|
+
label: getProperty(obj, "label"),
|
|
51
52
|
maxBeaconUrlLength: getProperty(obj, "maxBeaconUrlLength", 8190),
|
|
52
53
|
maxBeaconUTEntries: getProperty(obj, "maxBeaconUTEntries", 20),
|
|
53
54
|
maxErrors: getProperty(obj, "maxErrors", 5),
|
|
@@ -56,10 +57,10 @@
|
|
|
56
57
|
newBeaconOnPageShow: getProperty(obj, "newBeaconOnPageShow", false),
|
|
57
58
|
samplerate: getProperty(obj, "samplerate", 100),
|
|
58
59
|
sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", autoMode),
|
|
59
|
-
serverTiming: getProperty(obj, "serverTiming"
|
|
60
|
+
serverTiming: getProperty(obj, "serverTiming"),
|
|
60
61
|
trackErrors: getProperty(obj, "trackErrors", true),
|
|
61
62
|
trackHiddenPages: getProperty(obj, "trackHiddenPages", false),
|
|
62
|
-
pagegroups: getProperty(obj, "pagegroups"
|
|
63
|
+
pagegroups: getProperty(obj, "pagegroups"),
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
function getProperty(obj, key, defaultValue) {
|
|
@@ -99,8 +100,8 @@
|
|
|
99
100
|
updatedCustomData = {};
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
* Convert a set of custom data values to the string format expected by the backend.
|
|
104
|
+
*/
|
|
104
105
|
function valuesToString(values) {
|
|
105
106
|
var strings = [];
|
|
106
107
|
for (var key in values) {
|
|
@@ -119,11 +120,14 @@
|
|
|
119
120
|
}
|
|
120
121
|
var max = Math.max;
|
|
121
122
|
/**
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
* Clamp a number so that it is never less than 0
|
|
124
|
+
*/
|
|
124
125
|
function clamp(x) {
|
|
125
126
|
return max(0, x);
|
|
126
127
|
}
|
|
128
|
+
function sortNumeric(a, b) {
|
|
129
|
+
return a - b;
|
|
130
|
+
}
|
|
127
131
|
|
|
128
132
|
function now() {
|
|
129
133
|
return Date.now ? Date.now() : +new Date();
|
|
@@ -173,7 +177,7 @@
|
|
|
173
177
|
startTime: 0,
|
|
174
178
|
type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
|
|
175
179
|
};
|
|
176
|
-
|
|
180
|
+
{
|
|
177
181
|
for (var key in timing) {
|
|
178
182
|
if (typeof timing[key] === "number" && key !== "navigationStart") {
|
|
179
183
|
entry[key] = floor(timing[key] - timing.navigationStart);
|
|
@@ -183,10 +187,10 @@
|
|
|
183
187
|
return entry;
|
|
184
188
|
}
|
|
185
189
|
/**
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
* Simple wrapper around performance.getEntriesByType to provide fallbacks for
|
|
191
|
+
* legacy browsers, and work around edge cases where undefined is returned instead
|
|
192
|
+
* of an empty PerformanceEntryList.
|
|
193
|
+
*/
|
|
190
194
|
function getEntriesByType(type) {
|
|
191
195
|
if (typeof performance.getEntriesByType === "function") {
|
|
192
196
|
var entries = performance.getEntriesByType(type);
|
|
@@ -245,7 +249,7 @@
|
|
|
245
249
|
PageLabelFromDocumentTitle: 1 << 6,
|
|
246
250
|
PageLabelFromLabelProp: 1 << 7,
|
|
247
251
|
PageLabelFromGlobalVariable: 1 << 8,
|
|
248
|
-
|
|
252
|
+
PageLabelFromUrlPattern: 1 << 9,
|
|
249
253
|
PageWasPrerendered: 1 << 10,
|
|
250
254
|
PageWasBfCacheRestored: 1 << 11,
|
|
251
255
|
};
|
|
@@ -261,8 +265,8 @@
|
|
|
261
265
|
}
|
|
262
266
|
|
|
263
267
|
/**
|
|
264
|
-
|
|
265
|
-
|
|
268
|
+
* Get the interaction attribution name for an element
|
|
269
|
+
*/
|
|
266
270
|
function interactionAttributionForElement(el) {
|
|
267
271
|
// Our first preference is to use the data-sctrack attribute from anywhere in the tree
|
|
268
272
|
var trackId = getClosestScTrackAttribute(el);
|
|
@@ -354,6 +358,7 @@
|
|
|
354
358
|
};
|
|
355
359
|
return Logger;
|
|
356
360
|
}());
|
|
361
|
+
|
|
357
362
|
var sessionValue = 0;
|
|
358
363
|
var sessionEntries = [];
|
|
359
364
|
var maximumSessionValue = 0;
|
|
@@ -363,9 +368,9 @@
|
|
|
363
368
|
var latestEntry = sessionEntries[sessionEntries.length - 1];
|
|
364
369
|
if (sessionEntries.length &&
|
|
365
370
|
(entry.startTime - latestEntry.startTime >= 1000 ||
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
371
|
+
entry.startTime - firstEntry.startTime >= 5000)) {
|
|
372
|
+
sessionValue = entry.value;
|
|
373
|
+
sessionEntries = [entry];
|
|
369
374
|
}
|
|
370
375
|
else {
|
|
371
376
|
sessionValue += entry.value;
|
|
@@ -384,9 +389,9 @@
|
|
|
384
389
|
}
|
|
385
390
|
|
|
386
391
|
/**
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
392
|
+
* This implementation is based on the web-vitals implementation, however it is stripped back to the
|
|
393
|
+
* bare minimum required to measure just the INP value and does not store the actual event entries.
|
|
394
|
+
*/
|
|
390
395
|
// The maximum number of interactions to store
|
|
391
396
|
var MAX_INTERACTIONS = 10;
|
|
392
397
|
// A list of the slowest interactions
|
|
@@ -423,9 +428,9 @@
|
|
|
423
428
|
return slowestEntries.some(function (e2) { return e1.startTime === e2.startTime && e1.duration === e2.duration; });
|
|
424
429
|
}
|
|
425
430
|
/**
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
431
|
+
* Returns an estimated high percentile INP value based on the total number of interactions on the
|
|
432
|
+
* current page.
|
|
433
|
+
*/
|
|
429
434
|
function getHighPercentileINP() {
|
|
430
435
|
var _a;
|
|
431
436
|
var index = Math.min(slowestEntries.length - 1, Math.floor(getInteractionCount() / 50));
|
|
@@ -440,8 +445,7 @@
|
|
|
440
445
|
|
|
441
446
|
var ALL_ENTRIES = [];
|
|
442
447
|
function observe(type, callback, options) {
|
|
443
|
-
if (typeof PerformanceObserver === "function" &&
|
|
444
|
-
PerformanceObserver.supportedEntryTypes.includes(type)) {
|
|
448
|
+
if (typeof PerformanceObserver === "function" && PerformanceObserver.supportedEntryTypes.includes(type)) {
|
|
445
449
|
var po = new PerformanceObserver(function (list) {
|
|
446
450
|
list.getEntries().forEach(function (entry) { return callback(entry); });
|
|
447
451
|
});
|
|
@@ -456,17 +460,14 @@
|
|
|
456
460
|
function addEntry(entry) {
|
|
457
461
|
ALL_ENTRIES.push(entry);
|
|
458
462
|
}
|
|
459
|
-
function clearEntries() {
|
|
460
|
-
ALL_ENTRIES.splice(0);
|
|
461
|
-
}
|
|
462
463
|
|
|
463
464
|
/**
|
|
464
|
-
|
|
465
|
-
|
|
465
|
+
* A server timing metric that has its value set to the duration field
|
|
466
|
+
*/
|
|
466
467
|
var TYPE_DURATION = "r";
|
|
467
468
|
/**
|
|
468
|
-
|
|
469
|
-
|
|
469
|
+
* When a description metric has no value, we consider it to be a boolean and set it to this value.
|
|
470
|
+
*/
|
|
470
471
|
var BOOLEAN_TRUE_VALUE = "true";
|
|
471
472
|
function getKeyValuePairs(config, serverTiming) {
|
|
472
473
|
var pairs = {};
|
|
@@ -500,7 +501,7 @@
|
|
|
500
501
|
if (Array.isArray(patterns)) {
|
|
501
502
|
for (var i in patterns) {
|
|
502
503
|
var pattern = patterns[i];
|
|
503
|
-
if (patternMatchesUrl(pattern, hostname, pathname)) {
|
|
504
|
+
if (typeof pattern === "string" && patternMatchesUrl(pattern, hostname, pathname)) {
|
|
504
505
|
if (firstOnly) {
|
|
505
506
|
return key;
|
|
506
507
|
}
|
|
@@ -537,15 +538,27 @@
|
|
|
537
538
|
// -------------------------------------------------------------------------
|
|
538
539
|
// Settings
|
|
539
540
|
// -------------------------------------------------------------------------
|
|
541
|
+
// This ID usually appended to the end of the lux.js as a query string when
|
|
542
|
+
// using the SpeedCurve hosted version - but we have to include it here as
|
|
543
|
+
// this is self hosted.
|
|
544
|
+
LUX.customerid = 47044334;
|
|
540
545
|
// Set the sample rate to 1% to avoid all events being sent.
|
|
541
546
|
LUX.samplerate = 1;
|
|
542
547
|
// -------------------------------------------------------------------------
|
|
543
548
|
/// End
|
|
544
549
|
// -------------------------------------------------------------------------
|
|
545
|
-
var SCRIPT_VERSION = "
|
|
550
|
+
var SCRIPT_VERSION = "311";
|
|
546
551
|
var logger = new Logger();
|
|
547
552
|
var globalConfig = fromObject(LUX);
|
|
548
553
|
logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION]);
|
|
554
|
+
// Variable aliases that allow the minifier to reduce file size.
|
|
555
|
+
var document = window.document;
|
|
556
|
+
var addEventListener = window.addEventListener;
|
|
557
|
+
var removeEventListener = window.removeEventListener;
|
|
558
|
+
var setTimeout = window.setTimeout;
|
|
559
|
+
var clearTimeout = window.clearTimeout;
|
|
560
|
+
var encodeURIComponent = window.encodeURIComponent;
|
|
561
|
+
var thisScript = document.currentScript || {};
|
|
549
562
|
// Log JS errors.
|
|
550
563
|
var nErrors = 0;
|
|
551
564
|
function errorHandler(e) {
|
|
@@ -560,30 +573,30 @@
|
|
|
560
573
|
// Sample & limit other errors.
|
|
561
574
|
// Send the error beacon.
|
|
562
575
|
new Image().src =
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
576
|
+
globalConfig.errorBeaconUrl +
|
|
577
|
+
"?v=" +
|
|
578
|
+
SCRIPT_VERSION +
|
|
579
|
+
"&id=" +
|
|
580
|
+
getCustomerId() +
|
|
581
|
+
"&fn=" +
|
|
582
|
+
encodeURIComponent(e.filename) +
|
|
583
|
+
"&ln=" +
|
|
584
|
+
e.lineno +
|
|
585
|
+
"&cn=" +
|
|
586
|
+
e.colno +
|
|
587
|
+
"&msg=" +
|
|
588
|
+
encodeURIComponent(e.message) +
|
|
589
|
+
"&l=" +
|
|
590
|
+
encodeURIComponent(_getPageLabel()) +
|
|
591
|
+
(connectionType() ? "&ct=" + connectionType() : "") +
|
|
592
|
+
"&HN=" +
|
|
593
|
+
encodeURIComponent(document.location.hostname) +
|
|
594
|
+
"&PN=" +
|
|
595
|
+
encodeURIComponent(document.location.pathname);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
addEventListener("error", errorHandler);
|
|
587
600
|
var logEntry = function (entry) {
|
|
588
601
|
logger.logEvent(LogEvent.PerformanceEntryReceived, [entry]);
|
|
589
602
|
};
|
|
@@ -611,9 +624,9 @@
|
|
|
611
624
|
logEntry(entry);
|
|
612
625
|
});
|
|
613
626
|
observe("first-input", function (entry) {
|
|
614
|
-
var
|
|
615
|
-
if (!gFirstInputDelay || gFirstInputDelay <
|
|
616
|
-
gFirstInputDelay = floor(
|
|
627
|
+
var entryTime = entry.processingStart - entry.startTime;
|
|
628
|
+
if (!gFirstInputDelay || gFirstInputDelay < entryTime) {
|
|
629
|
+
gFirstInputDelay = floor(entryTime);
|
|
617
630
|
}
|
|
618
631
|
// Allow first-input events to be considered for INP
|
|
619
632
|
addEntry$1(entry);
|
|
@@ -642,11 +655,24 @@
|
|
|
642
655
|
var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
|
|
643
656
|
var pageRestoreTime; // ms since navigationStart representing when the page was restored from the bfcache
|
|
644
657
|
/**
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
var getZeroTime = function () {
|
|
658
|
+
* To measure the way a user experienced a metric, we measure metrics relative to the time the user
|
|
659
|
+
* started viewing the page. On prerendered pages, this is activationStart. On bfcache restores, this
|
|
660
|
+
* is the page restore time. On all other pages this value will be zero.
|
|
661
|
+
*/
|
|
662
|
+
var getZeroTime = function () {
|
|
663
|
+
var _a;
|
|
664
|
+
return Math.max(pageRestoreTime || 0, getNavigationEntry().activationStart, ((_a = _getMark(START_MARK)) === null || _a === void 0 ? void 0 : _a.startTime) || 0);
|
|
665
|
+
};
|
|
666
|
+
/**
|
|
667
|
+
* Most time-based metrics that LUX reports should be relative to the "zero" marker, rounded down
|
|
668
|
+
* to the nearest unit so as not to report times in the future, and clamped to zero.
|
|
669
|
+
*/
|
|
670
|
+
var processTimeMetric = function (value) { return clamp(floor(value - getZeroTime())); };
|
|
671
|
+
/**
|
|
672
|
+
* Some values should only be reported if they are non-zero. The exception to this is when the page
|
|
673
|
+
* was prerendered or restored from BF cache
|
|
674
|
+
*/
|
|
675
|
+
var shouldReportValue = function (value) { return value > 0 || pageRestoreTime || wasPrerendered(); };
|
|
650
676
|
if (_sample()) {
|
|
651
677
|
logger.logEvent(LogEvent.SessionIsSampled, [globalConfig.samplerate]);
|
|
652
678
|
}
|
|
@@ -688,11 +714,11 @@
|
|
|
688
714
|
removeListeners();
|
|
689
715
|
}
|
|
690
716
|
function removeListeners() {
|
|
691
|
-
|
|
692
|
-
|
|
717
|
+
removeEventListener("pointerup", onPointerUp, ghListenerOptions);
|
|
718
|
+
removeEventListener("pointercancel", onPointerCancel, ghListenerOptions);
|
|
693
719
|
}
|
|
694
|
-
|
|
695
|
-
|
|
720
|
+
addEventListener("pointerup", onPointerUp, ghListenerOptions);
|
|
721
|
+
addEventListener("pointercancel", onPointerCancel, ghListenerOptions);
|
|
696
722
|
}
|
|
697
723
|
// Record FID as the delta between when the event happened and when the
|
|
698
724
|
// listener was able to execute.
|
|
@@ -732,16 +758,16 @@
|
|
|
732
758
|
}
|
|
733
759
|
// Attach event listener to input events.
|
|
734
760
|
gaEventTypes.forEach(function (eventType) {
|
|
735
|
-
|
|
761
|
+
addEventListener(eventType, onInput, ghListenerOptions);
|
|
736
762
|
});
|
|
737
763
|
////////////////////// FID END
|
|
738
764
|
/**
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
765
|
+
* Returns the time elapsed (in ms) since navigationStart. For SPAs, returns
|
|
766
|
+
* the time elapsed since the last LUX.init call.
|
|
767
|
+
*
|
|
768
|
+
* When `absolute = true` the time is always relative to navigationStart, even
|
|
769
|
+
* in SPAs.
|
|
770
|
+
*/
|
|
745
771
|
function _now(absolute) {
|
|
746
772
|
var sinceNavigationStart = msSinceNavigationStart();
|
|
747
773
|
var startMark = _getMark(START_MARK);
|
|
@@ -766,7 +792,7 @@
|
|
|
766
792
|
return performance.mark.apply(performance, args);
|
|
767
793
|
}
|
|
768
794
|
// ...Otherwise provide a polyfill
|
|
769
|
-
|
|
795
|
+
{
|
|
770
796
|
var name_1 = args[0];
|
|
771
797
|
var detail = ((_a = args[1]) === null || _a === void 0 ? void 0 : _a.detail) || null;
|
|
772
798
|
var startTime = ((_b = args[1]) === null || _b === void 0 ? void 0 : _b.startTime) || _now();
|
|
@@ -829,14 +855,14 @@
|
|
|
829
855
|
return performance.measure.apply(performance, args);
|
|
830
856
|
}
|
|
831
857
|
// ...Otherwise provide a polyfill
|
|
832
|
-
|
|
858
|
+
{
|
|
833
859
|
var navEntry = getNavigationEntry();
|
|
834
860
|
var startTime = typeof startMarkName === "number" ? startMarkName : 0;
|
|
835
861
|
var endTime = typeof endMarkName === "number" ? endMarkName : _now();
|
|
836
862
|
var throwError = function (missingMark) {
|
|
837
863
|
throw new DOMException("Failed to execute 'measure' on 'Performance': The mark '" +
|
|
838
|
-
|
|
839
|
-
|
|
864
|
+
missingMark +
|
|
865
|
+
"' does not exist");
|
|
840
866
|
};
|
|
841
867
|
if (typeof startMarkName === "string") {
|
|
842
868
|
var startMark = _getMark(startMarkName);
|
|
@@ -924,7 +950,7 @@
|
|
|
924
950
|
var startMark = _getMark(START_MARK);
|
|
925
951
|
// For user timing values taken in a SPA page load, we need to adjust them
|
|
926
952
|
// so that they're zeroed against the last LUX.init() call.
|
|
927
|
-
var tZero =
|
|
953
|
+
var tZero = getZeroTime();
|
|
928
954
|
// marks
|
|
929
955
|
_getMarks().forEach(function (mark) {
|
|
930
956
|
var name = mark.name;
|
|
@@ -973,12 +999,13 @@
|
|
|
973
999
|
// Return a string of Element Timing Metrics formatted for beacon querystring.
|
|
974
1000
|
function elementTimingValues() {
|
|
975
1001
|
var aET = [];
|
|
976
|
-
var startMark = _getMark(START_MARK);
|
|
977
|
-
var tZero = startMark ? startMark.startTime : getZeroTime();
|
|
978
1002
|
getEntries("element").forEach(function (entry) {
|
|
979
1003
|
if (entry.identifier && entry.startTime) {
|
|
980
|
-
|
|
981
|
-
|
|
1004
|
+
var value = processTimeMetric(entry.startTime);
|
|
1005
|
+
if (shouldReportValue(value)) {
|
|
1006
|
+
logger.logEvent(LogEvent.PerformanceEntryProcessed, [entry]);
|
|
1007
|
+
aET.push(entry.identifier + "|" + value);
|
|
1008
|
+
}
|
|
982
1009
|
}
|
|
983
1010
|
});
|
|
984
1011
|
return aET.join(",");
|
|
@@ -995,20 +1022,7 @@
|
|
|
995
1022
|
var longTaskEntries = getEntries("longtask");
|
|
996
1023
|
// Add up totals for each "type" of long task
|
|
997
1024
|
if (longTaskEntries.length) {
|
|
998
|
-
|
|
999
|
-
// But if it is a SPA then the relative start time is gStartMark.
|
|
1000
|
-
var startMark = _getMark(START_MARK);
|
|
1001
|
-
var tZero_1 = startMark ? startMark.startTime : 0;
|
|
1002
|
-
// Do not include Long Tasks that start after the page is done.
|
|
1003
|
-
// For full page loads, "done" is loadEventEnd.
|
|
1004
|
-
var tEnd_1 = timing.loadEventEnd - timing.navigationStart;
|
|
1005
|
-
if (startMark) {
|
|
1006
|
-
// For SPA page loads (determined by the presence of a start mark), "done" is gEndMark.
|
|
1007
|
-
var endMark = _getMark(END_MARK);
|
|
1008
|
-
if (endMark) {
|
|
1009
|
-
tEnd_1 = endMark.startTime;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1025
|
+
var tZero_1 = getZeroTime();
|
|
1012
1026
|
longTaskEntries.forEach(function (entry) {
|
|
1013
1027
|
var dur = floor(entry.duration);
|
|
1014
1028
|
if (entry.startTime < tZero_1) {
|
|
@@ -1016,21 +1030,18 @@
|
|
|
1016
1030
|
// LUX.init() was called. If so, only include the duration after tZero.
|
|
1017
1031
|
dur -= tZero_1 - entry.startTime;
|
|
1018
1032
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
hCPUDetails[type]
|
|
1033
|
+
// Only process entries that we calculated to have a valid duration
|
|
1034
|
+
if (dur > 0) {
|
|
1035
|
+
logger.logEvent(LogEvent.PerformanceEntryProcessed, [entry]);
|
|
1036
|
+
var type = entry.attribution[0].name;
|
|
1037
|
+
if (!hCPU[type]) {
|
|
1038
|
+
hCPU[type] = 0;
|
|
1039
|
+
hCPUDetails[type] = "";
|
|
1040
|
+
}
|
|
1041
|
+
hCPU[type] += dur;
|
|
1042
|
+
// Send back the raw startTime and duration, as well as the adjusted duration.
|
|
1043
|
+
hCPUDetails[type] += "," + floor(entry.startTime) + "|" + dur;
|
|
1030
1044
|
}
|
|
1031
|
-
hCPU[type] += dur;
|
|
1032
|
-
// Send back the raw startTime and duration, as well as the adjusted duration.
|
|
1033
|
-
hCPUDetails[type] += "," + floor(entry.startTime) + "|" + dur;
|
|
1034
1045
|
});
|
|
1035
1046
|
}
|
|
1036
1047
|
// TODO - Add more types if/when they become available.
|
|
@@ -1042,12 +1053,12 @@
|
|
|
1042
1053
|
}
|
|
1043
1054
|
var hStats = cpuStats(hCPUDetails[jsType]);
|
|
1044
1055
|
var sStats = ",n|" +
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1056
|
+
hStats.count +
|
|
1057
|
+
",d|" +
|
|
1058
|
+
hStats.median +
|
|
1059
|
+
",x|" +
|
|
1060
|
+
hStats.max +
|
|
1061
|
+
(typeof hStats.fci === "undefined" ? "" : ",i|" + hStats.fci);
|
|
1051
1062
|
sCPU += "s|" + hCPU[jsType] + sStats + hCPUDetails[jsType];
|
|
1052
1063
|
return sCPU;
|
|
1053
1064
|
}
|
|
@@ -1055,11 +1066,11 @@
|
|
|
1055
1066
|
function cpuStats(sDetails) {
|
|
1056
1067
|
// tuples of starttime|duration, eg: ,456|250,789|250,1012|250
|
|
1057
1068
|
var max = 0;
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
//
|
|
1062
|
-
var bFoundFci =
|
|
1069
|
+
// FCI is beginning of 5 second window of no Long Tasks _after_ first contentful paint
|
|
1070
|
+
var fcp = getFcp();
|
|
1071
|
+
var fci = fcp || 0;
|
|
1072
|
+
// If FCP is not supported, we can't calculate a valid FCI.
|
|
1073
|
+
var bFoundFci = typeof fcp === "undefined";
|
|
1063
1074
|
var aValues = [];
|
|
1064
1075
|
var aTuples = sDetails.split(",");
|
|
1065
1076
|
for (var i = 0; i < aTuples.length; i++) {
|
|
@@ -1079,7 +1090,10 @@
|
|
|
1079
1090
|
}
|
|
1080
1091
|
else {
|
|
1081
1092
|
// Less than 5 seconds of inactivity
|
|
1082
|
-
|
|
1093
|
+
var val = processTimeMetric(start + dur);
|
|
1094
|
+
if (shouldReportValue(val)) {
|
|
1095
|
+
fci = val; // FCI is now the end of this Long Task
|
|
1096
|
+
}
|
|
1083
1097
|
}
|
|
1084
1098
|
}
|
|
1085
1099
|
}
|
|
@@ -1102,9 +1116,7 @@
|
|
|
1102
1116
|
return 0;
|
|
1103
1117
|
}
|
|
1104
1118
|
var half = floor(aValues.length / 2);
|
|
1105
|
-
aValues.sort(
|
|
1106
|
-
return a - b;
|
|
1107
|
-
});
|
|
1119
|
+
aValues.sort(sortNumeric);
|
|
1108
1120
|
if (aValues.length % 2) {
|
|
1109
1121
|
// Return the middle value.
|
|
1110
1122
|
return aValues[half];
|
|
@@ -1119,42 +1131,39 @@
|
|
|
1119
1131
|
var sLuxjs = "";
|
|
1120
1132
|
if (performance.getEntriesByName) {
|
|
1121
1133
|
// Get the lux script URL (including querystring params).
|
|
1122
|
-
var
|
|
1123
|
-
if (
|
|
1124
|
-
var
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
(scriptStartTime - timing.navigationStart) + // when lux.js started getting evaluated relative to navigationStart
|
|
1156
|
-
"";
|
|
1157
|
-
}
|
|
1134
|
+
var aResources = performance.getEntriesByName(thisScript.src);
|
|
1135
|
+
if (aResources && aResources.length) {
|
|
1136
|
+
var r = aResources[0];
|
|
1137
|
+
// DO NOT USE DURATION!!!!!
|
|
1138
|
+
// See https://www.stevesouders.com/blog/2014/11/25/serious-confusion-with-resource-timing/
|
|
1139
|
+
var dns = floor(r.domainLookupEnd - r.domainLookupStart);
|
|
1140
|
+
var tcp = floor(r.connectEnd - r.connectStart);
|
|
1141
|
+
var fb = floor(r.responseStart - r.requestStart);
|
|
1142
|
+
var content = floor(r.responseEnd - r.responseStart);
|
|
1143
|
+
var networkDuration = dns + tcp + fb + content;
|
|
1144
|
+
var parseEval = scriptEndTime - scriptStartTime;
|
|
1145
|
+
var transferSize = r.encodedBodySize ? r.encodedBodySize : 0;
|
|
1146
|
+
// Instead of a delimiter use a 1-letter abbreviation as a separator.
|
|
1147
|
+
sLuxjs =
|
|
1148
|
+
"d" +
|
|
1149
|
+
dns +
|
|
1150
|
+
"t" +
|
|
1151
|
+
tcp +
|
|
1152
|
+
"f" +
|
|
1153
|
+
fb +
|
|
1154
|
+
"c" +
|
|
1155
|
+
content +
|
|
1156
|
+
"n" +
|
|
1157
|
+
networkDuration +
|
|
1158
|
+
"e" +
|
|
1159
|
+
parseEval +
|
|
1160
|
+
"r" +
|
|
1161
|
+
globalConfig.samplerate + // sample rate
|
|
1162
|
+
(typeof transferSize === "number" ? "x" + transferSize : "") +
|
|
1163
|
+
(typeof gLuxSnippetStart === "number" ? "l" + gLuxSnippetStart : "") +
|
|
1164
|
+
"s" +
|
|
1165
|
+
(scriptStartTime - timing.navigationStart) + // when lux.js started getting evaluated relative to navigationStart
|
|
1166
|
+
"";
|
|
1158
1167
|
}
|
|
1159
1168
|
}
|
|
1160
1169
|
return sLuxjs;
|
|
@@ -1185,9 +1194,9 @@
|
|
|
1185
1194
|
if (gCustomerDataTimeout) {
|
|
1186
1195
|
// Cancel the timer for any previous beacons so that if they have not
|
|
1187
1196
|
// yet been sent we can combine all the data in a new beacon.
|
|
1188
|
-
|
|
1197
|
+
clearTimeout(gCustomerDataTimeout);
|
|
1189
1198
|
}
|
|
1190
|
-
gCustomerDataTimeout =
|
|
1199
|
+
gCustomerDataTimeout = setTimeout(_sendCustomerData, 100);
|
|
1191
1200
|
}
|
|
1192
1201
|
}
|
|
1193
1202
|
// _sample()
|
|
@@ -1200,9 +1209,9 @@
|
|
|
1200
1209
|
return parseInt(nThis) < globalConfig.samplerate;
|
|
1201
1210
|
}
|
|
1202
1211
|
/**
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1212
|
+
* Re-initialize lux.js to start a new "page". This is typically called within a SPA at the
|
|
1213
|
+
* beginning of a page transition, but is also called internally when the BF cache is restored.
|
|
1214
|
+
*/
|
|
1206
1215
|
function _init(startTime) {
|
|
1207
1216
|
// Some customers (incorrectly) call LUX.init on the very first page load of a SPA. This would
|
|
1208
1217
|
// cause some first-page-only data (like paint metrics) to be lost. To prevent this, we silently
|
|
@@ -1232,7 +1241,6 @@
|
|
|
1232
1241
|
gbFirstPV = 0;
|
|
1233
1242
|
gSyncId = createSyncId();
|
|
1234
1243
|
gUid = refreshUniqueId(gSyncId);
|
|
1235
|
-
clearEntries();
|
|
1236
1244
|
reset$1();
|
|
1237
1245
|
reset();
|
|
1238
1246
|
nErrors = 0;
|
|
@@ -1353,14 +1361,17 @@
|
|
|
1353
1361
|
ns += start; // "navigationStart" for a SPA is the real navigationStart plus the start mark
|
|
1354
1362
|
var end = floor(endMark.startTime) - start; // delta from start mark
|
|
1355
1363
|
s =
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
+
ns +
|
|
1365
|
+
// fetchStart and activationStart are the same as navigationStart for a SPA
|
|
1366
|
+
"as" +
|
|
1367
|
+
0 +
|
|
1368
|
+
"fs" +
|
|
1369
|
+
0 +
|
|
1370
|
+
"ls" +
|
|
1371
|
+
end +
|
|
1372
|
+
"le" +
|
|
1373
|
+
end +
|
|
1374
|
+
"";
|
|
1364
1375
|
}
|
|
1365
1376
|
else if (performance.timing) {
|
|
1366
1377
|
// Return the real Nav Timing metrics because this is the "main" page view (not a SPA)
|
|
@@ -1368,17 +1379,20 @@
|
|
|
1368
1379
|
var startRender = getStartRender();
|
|
1369
1380
|
var fcp = getFcp();
|
|
1370
1381
|
var lcp = getLcp();
|
|
1371
|
-
var prefixNTValue = function (key, prefix) {
|
|
1372
|
-
// activationStart is always absolute. Other values are relative to activationStart.
|
|
1373
|
-
var zero = key === "activationStart" ? 0 : getZeroTime();
|
|
1382
|
+
var prefixNTValue = function (key, prefix, ignoreZero) {
|
|
1374
1383
|
if (typeof navEntry_1[key] === "number") {
|
|
1375
|
-
var value =
|
|
1376
|
-
|
|
1384
|
+
var value = navEntry_1[key];
|
|
1385
|
+
// We allow zero values for most navigation timing metrics, but for some metrics we want
|
|
1386
|
+
// to ignore zeroes. The exceptions are that all metrics can be zero if the page was either
|
|
1387
|
+
// prerendered or restored from the BF cache.
|
|
1388
|
+
if (shouldReportValue(value) || !ignoreZero) {
|
|
1389
|
+
return prefix + processTimeMetric(value);
|
|
1390
|
+
}
|
|
1377
1391
|
}
|
|
1378
1392
|
return "";
|
|
1379
1393
|
};
|
|
1380
|
-
var loadEventStartStr = prefixNTValue("loadEventStart", "ls");
|
|
1381
|
-
var loadEventEndStr = prefixNTValue("loadEventEnd", "le");
|
|
1394
|
+
var loadEventStartStr = prefixNTValue("loadEventStart", "ls", true);
|
|
1395
|
+
var loadEventEndStr = prefixNTValue("loadEventEnd", "le", true);
|
|
1382
1396
|
if (pageRestoreTime && startMark && endMark) {
|
|
1383
1397
|
// For bfcache restores, we set the load time to the time it took for the page to be restored.
|
|
1384
1398
|
var loadTime = floor(endMark.startTime - startMark.startTime);
|
|
@@ -1386,43 +1400,44 @@
|
|
|
1386
1400
|
loadEventEndStr = "le" + loadTime;
|
|
1387
1401
|
}
|
|
1388
1402
|
var redirect = wasRedirected();
|
|
1403
|
+
var isSecure = document.location.protocol === "https:";
|
|
1389
1404
|
s = [
|
|
1390
1405
|
ns,
|
|
1391
|
-
|
|
1392
|
-
redirect ? prefixNTValue("redirectStart", "rs") : "",
|
|
1393
|
-
redirect ? prefixNTValue("redirectEnd", "re") : "",
|
|
1406
|
+
"as" + clamp(navEntry_1.activationStart),
|
|
1407
|
+
redirect && !pageRestoreTime ? prefixNTValue("redirectStart", "rs") : "",
|
|
1408
|
+
redirect && !pageRestoreTime ? prefixNTValue("redirectEnd", "re") : "",
|
|
1394
1409
|
prefixNTValue("fetchStart", "fs"),
|
|
1395
1410
|
prefixNTValue("domainLookupStart", "ds"),
|
|
1396
1411
|
prefixNTValue("domainLookupEnd", "de"),
|
|
1397
1412
|
prefixNTValue("connectStart", "cs"),
|
|
1398
|
-
prefixNTValue("secureConnectionStart", "sc"),
|
|
1413
|
+
isSecure ? prefixNTValue("secureConnectionStart", "sc") : "",
|
|
1399
1414
|
prefixNTValue("connectEnd", "ce"),
|
|
1400
1415
|
prefixNTValue("requestStart", "qs"),
|
|
1401
1416
|
prefixNTValue("responseStart", "bs"),
|
|
1402
1417
|
prefixNTValue("responseEnd", "be"),
|
|
1403
|
-
prefixNTValue("domInteractive", "oi"),
|
|
1404
|
-
prefixNTValue("domContentLoadedEventStart", "os"),
|
|
1405
|
-
prefixNTValue("domContentLoadedEventEnd", "oe"),
|
|
1406
|
-
prefixNTValue("domComplete", "oc"),
|
|
1418
|
+
prefixNTValue("domInteractive", "oi", true),
|
|
1419
|
+
prefixNTValue("domContentLoadedEventStart", "os", true),
|
|
1420
|
+
prefixNTValue("domContentLoadedEventEnd", "oe", true),
|
|
1421
|
+
prefixNTValue("domComplete", "oc", true),
|
|
1407
1422
|
loadEventStartStr,
|
|
1408
1423
|
loadEventEndStr,
|
|
1409
|
-
typeof startRender !== "undefined" ? "sr" +
|
|
1410
|
-
typeof fcp !== "undefined" ? "fc" +
|
|
1411
|
-
typeof lcp !== "undefined" ? "lc" +
|
|
1424
|
+
typeof startRender !== "undefined" ? "sr" + startRender : "",
|
|
1425
|
+
typeof fcp !== "undefined" ? "fc" + fcp : "",
|
|
1426
|
+
typeof lcp !== "undefined" ? "lc" + lcp : "",
|
|
1412
1427
|
].join("");
|
|
1413
1428
|
}
|
|
1414
1429
|
else if (endMark) {
|
|
1415
1430
|
// This is a "main" page view that does NOT support Navigation Timing - strange.
|
|
1416
1431
|
var end = floor(endMark.startTime);
|
|
1417
1432
|
s =
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1433
|
+
ns +
|
|
1434
|
+
"fs" +
|
|
1435
|
+
0 + // fetchStart is the same as navigationStart
|
|
1436
|
+
"ls" +
|
|
1437
|
+
end +
|
|
1438
|
+
"le" +
|
|
1439
|
+
end +
|
|
1440
|
+
"";
|
|
1426
1441
|
}
|
|
1427
1442
|
return s;
|
|
1428
1443
|
}
|
|
@@ -1432,7 +1447,10 @@
|
|
|
1432
1447
|
for (var i = 0; i < paintEntries.length; i++) {
|
|
1433
1448
|
var entry = paintEntries[i];
|
|
1434
1449
|
if (entry.name === "first-contentful-paint") {
|
|
1435
|
-
|
|
1450
|
+
var value = processTimeMetric(entry.startTime);
|
|
1451
|
+
if (shouldReportValue(value)) {
|
|
1452
|
+
return value;
|
|
1453
|
+
}
|
|
1436
1454
|
}
|
|
1437
1455
|
}
|
|
1438
1456
|
return undefined;
|
|
@@ -1442,8 +1460,11 @@
|
|
|
1442
1460
|
var lcpEntries = getEntries("largest-contentful-paint");
|
|
1443
1461
|
if (lcpEntries.length) {
|
|
1444
1462
|
var lastEntry = lcpEntries[lcpEntries.length - 1];
|
|
1445
|
-
|
|
1446
|
-
|
|
1463
|
+
var value = processTimeMetric(lastEntry.startTime);
|
|
1464
|
+
if (shouldReportValue(value)) {
|
|
1465
|
+
logger.logEvent(LogEvent.PerformanceEntryProcessed, [lastEntry]);
|
|
1466
|
+
return value;
|
|
1467
|
+
}
|
|
1447
1468
|
}
|
|
1448
1469
|
return undefined;
|
|
1449
1470
|
}
|
|
@@ -1454,12 +1475,17 @@
|
|
|
1454
1475
|
if ("PerformancePaintTiming" in self) {
|
|
1455
1476
|
var paintEntries = getEntriesByType("paint");
|
|
1456
1477
|
if (paintEntries.length) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1478
|
+
var paintValues = paintEntries.map(function (entry) { return entry.startTime; }).sort(sortNumeric);
|
|
1479
|
+
// Use the earliest valid paint entry as the start render time.
|
|
1480
|
+
for (var i = 0; i < paintValues.length; i++) {
|
|
1481
|
+
var value = processTimeMetric(paintValues[i]);
|
|
1482
|
+
if (shouldReportValue(value)) {
|
|
1483
|
+
return value;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1460
1486
|
}
|
|
1461
1487
|
}
|
|
1462
|
-
if (performance.timing && timing.msFirstPaint &&
|
|
1488
|
+
if (performance.timing && timing.msFirstPaint && true) {
|
|
1463
1489
|
// If IE/Edge, use the prefixed `msFirstPaint` property (see http://msdn.microsoft.com/ff974719).
|
|
1464
1490
|
return floor(timing.msFirstPaint - timing.navigationStart);
|
|
1465
1491
|
}
|
|
@@ -1472,41 +1498,11 @@
|
|
|
1472
1498
|
}
|
|
1473
1499
|
return getHighPercentileINP();
|
|
1474
1500
|
}
|
|
1501
|
+
// function simplified for our use, original would get the customerid from the script src URL
|
|
1502
|
+
// but we set it inside the code in this file, so this function just returns that
|
|
1475
1503
|
function getCustomerId() {
|
|
1476
|
-
if (typeof LUX.customerid === "undefined") {
|
|
1477
|
-
// Extract the id of the lux.js script element.
|
|
1478
|
-
var luxScript = getScriptElement("/js/lux.js");
|
|
1479
|
-
if (luxScript) {
|
|
1480
|
-
LUX.customerid = getQuerystringParam(luxScript.src, "id");
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
1504
|
return LUX.customerid || "";
|
|
1484
1505
|
}
|
|
1485
|
-
// Return the SCRIPT DOM element whose SRC contains the URL snippet.
|
|
1486
|
-
// This is used to find the LUX script element.
|
|
1487
|
-
function getScriptElement(urlsnippet) {
|
|
1488
|
-
var aScripts = document.getElementsByTagName("script");
|
|
1489
|
-
for (var i = 0, len = aScripts.length; i < len; i++) {
|
|
1490
|
-
var script = aScripts[i];
|
|
1491
|
-
if (script.src && -1 !== script.src.indexOf(urlsnippet)) {
|
|
1492
|
-
return script;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
return undefined;
|
|
1496
|
-
}
|
|
1497
|
-
function getQuerystringParam(url, name) {
|
|
1498
|
-
var qs = url.split("?")[1];
|
|
1499
|
-
var aTuples = qs.split("&");
|
|
1500
|
-
for (var i = 0, len = aTuples.length; i < len; i++) {
|
|
1501
|
-
var tuple = aTuples[i];
|
|
1502
|
-
var aTuple = tuple.split("=");
|
|
1503
|
-
var key = aTuple[0];
|
|
1504
|
-
if (name === key) {
|
|
1505
|
-
return aTuple[1];
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
return undefined;
|
|
1509
|
-
}
|
|
1510
1506
|
function avgDomDepth() {
|
|
1511
1507
|
var aElems = document.getElementsByTagName("*");
|
|
1512
1508
|
var i = aElems.length;
|
|
@@ -1645,14 +1641,14 @@
|
|
|
1645
1641
|
}
|
|
1646
1642
|
function createMaxMeasureTimeout() {
|
|
1647
1643
|
clearMaxMeasureTimeout();
|
|
1648
|
-
gMaxMeasureTimeout =
|
|
1644
|
+
gMaxMeasureTimeout = setTimeout(function () {
|
|
1649
1645
|
gFlags = addFlag(gFlags, Flags.BeaconSentAfterTimeout);
|
|
1650
1646
|
_sendLux();
|
|
1651
1647
|
}, globalConfig.maxMeasureTime - _now());
|
|
1652
1648
|
}
|
|
1653
1649
|
function clearMaxMeasureTimeout() {
|
|
1654
1650
|
if (gMaxMeasureTimeout) {
|
|
1655
|
-
|
|
1651
|
+
clearTimeout(gMaxMeasureTimeout);
|
|
1656
1652
|
}
|
|
1657
1653
|
}
|
|
1658
1654
|
function _getBeaconUrl(customData) {
|
|
@@ -1688,7 +1684,7 @@
|
|
|
1688
1684
|
!gSyncId ||
|
|
1689
1685
|
!_sample() || // OUTSIDE the sampled range
|
|
1690
1686
|
gbLuxSent // LUX data already sent
|
|
1691
|
-
|
|
1687
|
+
) {
|
|
1692
1688
|
return;
|
|
1693
1689
|
}
|
|
1694
1690
|
logger.logEvent(LogEvent.DataCollectionStart);
|
|
@@ -1741,49 +1737,49 @@
|
|
|
1741
1737
|
var is = inlineTagSize("script");
|
|
1742
1738
|
var ic = inlineTagSize("style");
|
|
1743
1739
|
var metricsQueryString =
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1740
|
+
// only send Nav Timing and lux.js metrics on initial pageload (not for SPA page views)
|
|
1741
|
+
(gbNavSent ? "" : "&NT=" + getNavTiming()) +
|
|
1742
|
+
(gbFirstPV ? "&LJS=" + sLuxjs : "") +
|
|
1743
|
+
// Page Stats
|
|
1744
|
+
"&PS=ns" +
|
|
1745
|
+
numScripts() +
|
|
1746
|
+
"bs" +
|
|
1747
|
+
blockingScripts() +
|
|
1748
|
+
(is > -1 ? "is" + is : "") +
|
|
1749
|
+
"ss" +
|
|
1750
|
+
numStylesheets() +
|
|
1751
|
+
"bc" +
|
|
1752
|
+
blockingStylesheets() +
|
|
1753
|
+
(ic > -1 ? "ic" + ic : "") +
|
|
1754
|
+
"ia" +
|
|
1755
|
+
imagesATF().length +
|
|
1756
|
+
"it" +
|
|
1757
|
+
document.getElementsByTagName("img").length + // total number of images
|
|
1758
|
+
"dd" +
|
|
1759
|
+
avgDomDepth() +
|
|
1760
|
+
"nd" +
|
|
1761
|
+
document.getElementsByTagName("*").length + // numdomelements
|
|
1762
|
+
"vh" +
|
|
1763
|
+
document.documentElement.clientHeight + // see http://www.quirksmode.org/mobile/viewports.html
|
|
1764
|
+
"vw" +
|
|
1765
|
+
document.documentElement.clientWidth +
|
|
1766
|
+
"dh" +
|
|
1767
|
+
docHeight(document) +
|
|
1768
|
+
"dw" +
|
|
1769
|
+
docWidth(document) +
|
|
1770
|
+
(docSize() ? "ds" + docSize() : "") + // document HTTP transfer size (bytes)
|
|
1771
|
+
(connectionType() ? "ct" + connectionType() + "_" : "") + // delimit with "_" since values can be non-numeric so need a way to extract with regex in VCL
|
|
1772
|
+
"er" +
|
|
1773
|
+
nErrors +
|
|
1774
|
+
"nt" +
|
|
1775
|
+
navigationType() +
|
|
1776
|
+
(navigator.deviceMemory ? "dm" + Math.round(navigator.deviceMemory) : "") + // device memory (GB)
|
|
1777
|
+
(sIx ? "&IX=" + sIx : "") +
|
|
1778
|
+
(typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
|
|
1779
|
+
(sCPU ? "&CPU=" + sCPU : "") +
|
|
1780
|
+
(sET ? "&ET=" + sET : "") + // element timing
|
|
1781
|
+
(typeof CLS !== "undefined" ? "&CLS=" + CLS : "") +
|
|
1782
|
+
(typeof INP !== "undefined" ? "&INP=" + INP : "");
|
|
1787
1783
|
// We add the user timing entries last so that we can split them to reduce the URL size if necessary.
|
|
1788
1784
|
var utValues = userTimingValues();
|
|
1789
1785
|
var _b = fitUserTimingEntries(utValues, globalConfig, baseUrl + metricsQueryString), beaconUtValues = _b[0], remainingUtValues = _b[1];
|
|
@@ -1807,8 +1803,8 @@
|
|
|
1807
1803
|
}
|
|
1808
1804
|
var ixTimerId;
|
|
1809
1805
|
function _sendIxAfterDelay() {
|
|
1810
|
-
|
|
1811
|
-
ixTimerId =
|
|
1806
|
+
clearTimeout(ixTimerId);
|
|
1807
|
+
ixTimerId = setTimeout(_sendIx, 100);
|
|
1812
1808
|
}
|
|
1813
1809
|
// Beacon back the IX data separately (need to sync with LUX beacon on the backend).
|
|
1814
1810
|
function _sendIx() {
|
|
@@ -1842,7 +1838,7 @@
|
|
|
1842
1838
|
!gSyncId ||
|
|
1843
1839
|
!_sample() || // OUTSIDE the sampled range
|
|
1844
1840
|
!gbLuxSent // LUX has NOT been sent yet, so wait to include it there
|
|
1845
|
-
|
|
1841
|
+
) {
|
|
1846
1842
|
return;
|
|
1847
1843
|
}
|
|
1848
1844
|
var sCustomerData = valuesToString(getUpdatedCustomData());
|
|
@@ -1870,6 +1866,19 @@
|
|
|
1870
1866
|
}
|
|
1871
1867
|
}
|
|
1872
1868
|
function _keyHandler(e) {
|
|
1869
|
+
var keyCode = e.keyCode;
|
|
1870
|
+
/**
|
|
1871
|
+
* Ignore modifier keys
|
|
1872
|
+
*
|
|
1873
|
+
* 16 = Shift
|
|
1874
|
+
* 17 = Control
|
|
1875
|
+
* 18 = Alt
|
|
1876
|
+
* 20 = Caps Lock
|
|
1877
|
+
* 224 = Meta/Command
|
|
1878
|
+
*/
|
|
1879
|
+
if (keyCode === 16 || keyCode === 17 || keyCode === 18 || keyCode === 20 || keyCode === 224) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1873
1882
|
_removeIxHandlers();
|
|
1874
1883
|
if (typeof ghIx["k"] === "undefined") {
|
|
1875
1884
|
ghIx["k"] = _now();
|
|
@@ -1914,10 +1923,10 @@
|
|
|
1914
1923
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1915
1924
|
function addListener(type, callback, useCapture) {
|
|
1916
1925
|
if (useCapture === void 0) { useCapture = false; }
|
|
1917
|
-
if (
|
|
1918
|
-
|
|
1926
|
+
if (addEventListener) {
|
|
1927
|
+
addEventListener(type, callback, useCapture);
|
|
1919
1928
|
}
|
|
1920
|
-
else if (window.attachEvent &&
|
|
1929
|
+
else if (window.attachEvent && true) {
|
|
1921
1930
|
window.attachEvent("on" + type, callback);
|
|
1922
1931
|
}
|
|
1923
1932
|
}
|
|
@@ -1925,10 +1934,10 @@
|
|
|
1925
1934
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1926
1935
|
function removeListener(type, callback, useCapture) {
|
|
1927
1936
|
if (useCapture === void 0) { useCapture = false; }
|
|
1928
|
-
if (
|
|
1929
|
-
|
|
1937
|
+
if (removeEventListener) {
|
|
1938
|
+
removeEventListener(type, callback, useCapture);
|
|
1930
1939
|
}
|
|
1931
|
-
else if (window.detachEvent &&
|
|
1940
|
+
else if (window.detachEvent && true) {
|
|
1932
1941
|
window.detachEvent("on" + type, callback);
|
|
1933
1942
|
}
|
|
1934
1943
|
}
|
|
@@ -2016,7 +2025,7 @@
|
|
|
2016
2025
|
if (typeof LUX.pagegroups !== "undefined") {
|
|
2017
2026
|
var label = getMatchesFromPatternMap(LUX.pagegroups, location.hostname, location.pathname, true);
|
|
2018
2027
|
if (label) {
|
|
2019
|
-
gFlags = addFlag(gFlags, Flags.
|
|
2028
|
+
gFlags = addFlag(gFlags, Flags.PageLabelFromUrlPattern);
|
|
2020
2029
|
return label;
|
|
2021
2030
|
}
|
|
2022
2031
|
}
|
|
@@ -2057,11 +2066,11 @@
|
|
|
2057
2066
|
function _setCookie(name, value, seconds) {
|
|
2058
2067
|
try {
|
|
2059
2068
|
document.cookie =
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2069
|
+
name +
|
|
2070
|
+
"=" +
|
|
2071
|
+
escape(value) +
|
|
2072
|
+
(seconds ? "; max-age=" + seconds : "") +
|
|
2073
|
+
"; path=/; SameSite=Lax";
|
|
2065
2074
|
}
|
|
2066
2075
|
catch (e) {
|
|
2067
2076
|
logger.logEvent(LogEvent.CookieSetError);
|
|
@@ -2111,7 +2120,7 @@
|
|
|
2111
2120
|
// bfcache. Since we have no "onload" event to hook into after a bfcache restore, we rely on the
|
|
2112
2121
|
// unload and maxMeasureTime handlers to send the beacon.
|
|
2113
2122
|
if (globalConfig.newBeaconOnPageShow) {
|
|
2114
|
-
|
|
2123
|
+
addEventListener("pageshow", function (event) {
|
|
2115
2124
|
if (event.persisted) {
|
|
2116
2125
|
// Record the timestamp of the bfcache restore
|
|
2117
2126
|
pageRestoreTime = event.timeStamp;
|
|
@@ -2141,9 +2150,9 @@
|
|
|
2141
2150
|
// Set the maximum measurement timer
|
|
2142
2151
|
createMaxMeasureTimeout();
|
|
2143
2152
|
/**
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2153
|
+
* LUX functions and properties must be attached to the existing global object to ensure that
|
|
2154
|
+
* changes made to the global object are reflected in the "internal" LUX object, and vice versa.
|
|
2155
|
+
*/
|
|
2147
2156
|
var globalLux = globalConfig;
|
|
2148
2157
|
// Functions
|
|
2149
2158
|
globalLux.mark = _mark;
|
|
@@ -2168,8 +2177,8 @@
|
|
|
2168
2177
|
// Public properties
|
|
2169
2178
|
globalLux.version = SCRIPT_VERSION;
|
|
2170
2179
|
/**
|
|
2171
|
-
|
|
2172
|
-
|
|
2180
|
+
* Run a command from the command queue
|
|
2181
|
+
*/
|
|
2173
2182
|
function _runCommand(_a) {
|
|
2174
2183
|
var fn = _a[0], args = _a.slice(1);
|
|
2175
2184
|
if (typeof globalLux[fn] === "function") {
|
|
@@ -2194,11 +2203,6 @@
|
|
|
2194
2203
|
// More settings
|
|
2195
2204
|
// ---------------------------------------------------------------------------
|
|
2196
2205
|
//
|
|
2197
|
-
// This ID usually appended to the end of the lux.js as a query string when
|
|
2198
|
-
// using the SpeedCurve hosted version - but we have to include it here as
|
|
2199
|
-
// this is self hosted.
|
|
2200
|
-
LUX.customerid = 47044334;
|
|
2201
|
-
|
|
2202
2206
|
// Setting debug to `true` shows what happening as it happens. Running
|
|
2203
2207
|
// `LUX.getDebug()` in the browser's console will show the history of what's
|
|
2204
2208
|
// happened.
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
display: block !important; // stylelint-disable-line declaration-no-important
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
.js-enabled .govuk-accordion__section-content[hidden] {
|
|
21
|
+
@supports (content-visibility: hidden) {
|
|
22
|
+
content-visibility: auto;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
// Change the colour from the blue link colour to black.
|
|
21
27
|
.govuk-accordion__section-button {
|
|
22
28
|
color: govuk-colour("black") !important; // stylelint-disable-line declaration-no-important
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
// stylelint-disable max-nesting-depth
|
|
35
35
|
|
|
36
36
|
.gem-c-govspeak {
|
|
37
|
+
a[href^="/"]:after,a[href^="http://"]:after,a[href^="https://"]:after {
|
|
38
|
+
content: " (" attr(href) ")";
|
|
39
|
+
font-size: 90%;
|
|
40
|
+
word-wrap: break-word;
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
.media-player {
|
|
38
44
|
display: none;
|
|
39
45
|
}
|
|
@@ -54,8 +54,9 @@
|
|
|
54
54
|
)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
data_attributes[:ga4_link] = ga4_link
|
|
57
58
|
%>
|
|
58
|
-
<%= tag.section class: class_names(container_class_names), data: { module: "ga4-link-tracker"
|
|
59
|
+
<%= tag.section class: class_names(container_class_names), data: { module: "ga4-link-tracker" } do %>
|
|
59
60
|
<%= tag.div class: "gem-c-attachment__thumbnail" do %>
|
|
60
61
|
<%= link_to attachment.url,
|
|
61
62
|
class: "govuk-link",
|
data/app/views/govuk_publishing_components/components/layout_for_public/_account-layout.html.erb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div class="govuk-width-container">
|
|
2
2
|
<%= render "govuk_publishing_components/components/phase_banner", {
|
|
3
3
|
phase: "beta",
|
|
4
|
+
ga4_tracking: true,
|
|
4
5
|
message: sanitize(t("components.layout_for_public.account_layout.feedback.banners.phase_banner_html"))
|
|
5
6
|
} unless omit_account_phase_banner %>
|
|
6
7
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: govuk_publishing_components
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 35.15.
|
|
4
|
+
version: 35.15.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GOV.UK Dev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-09-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: govuk_app_config
|