govuk_publishing_components 35.15.0 → 35.15.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|