govuk_publishing_components 38.1.1 → 38.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/images/govuk_publishing_components/crests/no10_crest_18px_x2.png +0 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +14 -7
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-measurer.js +1 -1
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +143 -84
- data/app/assets/stylesheets/govuk_publishing_components/components/_organisation-logo.scss +4 -0
- data/app/views/govuk_publishing_components/components/_layout_for_public.html.erb +5 -0
- data/app/views/govuk_publishing_components/components/docs/layout_for_public.yml +16 -0
- data/app/views/govuk_publishing_components/components/docs/organisation_logo.yml +9 -2
- data/lib/govuk_publishing_components/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0386f2759bb8457478930df490b0744905dd8b044daffbdc9f4074e44c11f7a2'
|
4
|
+
data.tar.gz: b2892a04b8af279fc386deace44cde4adeea7119cd53d6276ca62b13bcdf01e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 790aa1333fddc398174f120f056b8b3860e1eed354478b09dcae609c67330f0eecc567d39e94b12cdb349575360ccd0b8e535c53e26c54436057715615febb8c
|
7
|
+
data.tar.gz: 3b3759b84cb3b936f169494694fd4cd47db0ab834e60168dabfb286b3dae8e9139ff4d8cbe81aa9449ada1857943e9022ffc9025044dda3ca1ac84b20f0c0917
|
@@ -371,11 +371,6 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
|
|
371
371
|
}
|
372
372
|
} else {
|
373
373
|
for (var i = 0; i < items.length; i++) {
|
374
|
-
// GA4 limits us to 200 items, so we should limit the array to this size.
|
375
|
-
if (i === 200) {
|
376
|
-
break
|
377
|
-
}
|
378
|
-
|
379
374
|
var item = items[i]
|
380
375
|
var path = item.getAttribute('data-ga4-ecommerce-path')
|
381
376
|
|
@@ -385,16 +380,28 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
|
|
385
380
|
item.setAttribute('data-ga4-ecommerce-index', i + 1)
|
386
381
|
}
|
387
382
|
|
388
|
-
|
383
|
+
var nextItem = {
|
389
384
|
item_id: path,
|
390
385
|
item_content_id: item.getAttribute('data-ga4-ecommerce-content-id') || undefined,
|
391
386
|
item_list_name: listTitle,
|
392
387
|
index: window.GOVUK.analyticsGa4.core.ecommerceHelperFunctions.getIndex(item, startPosition)
|
393
|
-
}
|
388
|
+
}
|
389
|
+
|
390
|
+
// GA4 has a max payload, so ensure this array doesn't exceed 15,000 UTF-16 code units.
|
391
|
+
if ((this.getArraySize(ecommerceSchema.search_results.ecommerce.items) + this.getArraySize(nextItem)) >= 15000) {
|
392
|
+
break
|
393
|
+
}
|
394
|
+
|
395
|
+
ecommerceSchema.search_results.ecommerce.items.push(nextItem)
|
394
396
|
}
|
395
397
|
}
|
396
398
|
|
397
399
|
return ecommerceSchema
|
400
|
+
},
|
401
|
+
|
402
|
+
getArraySize: function (array) {
|
403
|
+
// .length represents the number of UTF-16 code units in a string, so we can stringify the array then grab the length of the string to get an 'accurate enough' number representing the size.
|
404
|
+
return JSON.stringify(array).length
|
398
405
|
}
|
399
406
|
}
|
400
407
|
}
|
@@ -46,6 +46,7 @@
|
|
46
46
|
auto: autoMode,
|
47
47
|
beaconUrl: getProperty(obj, "beaconUrl", luxOrigin + "/lux/"),
|
48
48
|
conversions: getProperty(obj, "conversions"),
|
49
|
+
cookieDomain: getProperty(obj, "cookieDomain"),
|
49
50
|
customerid: getProperty(obj, "customerid"),
|
50
51
|
errorBeaconUrl: getProperty(obj, "errorBeaconUrl", luxOrigin + "/error/"),
|
51
52
|
jspagelabel: getProperty(obj, "jspagelabel"),
|
@@ -120,6 +121,7 @@
|
|
120
121
|
return Math.floor(x);
|
121
122
|
}
|
122
123
|
var max = Math.max;
|
124
|
+
var round = Math.round;
|
123
125
|
/**
|
124
126
|
* Clamp a number so that it is never less than 0
|
125
127
|
*/
|
@@ -178,7 +180,7 @@
|
|
178
180
|
startTime: 0,
|
179
181
|
type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
|
180
182
|
};
|
181
|
-
{
|
183
|
+
if (true) {
|
182
184
|
for (var key in timing) {
|
183
185
|
if (typeof timing[key] === "number" && key !== "navigationStart") {
|
184
186
|
entry[key] = floor(timing[key] - timing.navigationStart);
|
@@ -240,6 +242,66 @@
|
|
240
242
|
return getNavigationEntry().redirectCount > 0 || timing.redirectEnd > 0;
|
241
243
|
}
|
242
244
|
|
245
|
+
function getClosestScTrackAttribute(el) {
|
246
|
+
var _a;
|
247
|
+
if (el.hasAttribute("data-sctrack")) {
|
248
|
+
var trackId = (_a = el.getAttribute("data-sctrack")) === null || _a === void 0 ? void 0 : _a.trim();
|
249
|
+
if (trackId) {
|
250
|
+
return trackId;
|
251
|
+
}
|
252
|
+
}
|
253
|
+
if (hasParentNode(el)) {
|
254
|
+
return getClosestScTrackAttribute(el.parentNode);
|
255
|
+
}
|
256
|
+
return null;
|
257
|
+
}
|
258
|
+
|
259
|
+
function hasParentNode(el) {
|
260
|
+
if (el.parentNode && el.parentNode.tagName) {
|
261
|
+
return true;
|
262
|
+
}
|
263
|
+
return false;
|
264
|
+
}
|
265
|
+
var MAX_SELECTOR_LENGTH = 100;
|
266
|
+
function getNodeSelector(node, selector) {
|
267
|
+
if (selector === void 0) {
|
268
|
+
selector = "";
|
269
|
+
}
|
270
|
+
try {
|
271
|
+
if (selector && (node.nodeType === 9 || selector.length > MAX_SELECTOR_LENGTH || !node.parentNode)) {
|
272
|
+
// Final selector.
|
273
|
+
return selector;
|
274
|
+
}
|
275
|
+
var el = node;
|
276
|
+
// Our first preference is to use the data-sctrack attribute from anywhere in the tree
|
277
|
+
var trackId = getClosestScTrackAttribute(el);
|
278
|
+
if (trackId) {
|
279
|
+
return trackId;
|
280
|
+
}
|
281
|
+
if (el.id) {
|
282
|
+
// Once we've found an element with ID we return the selector.
|
283
|
+
return "#" + el.id + (selector ? ">" + selector : "");
|
284
|
+
}
|
285
|
+
else {
|
286
|
+
// Otherwise attempt to get parent elements recursively
|
287
|
+
var name_1 = el.nodeType === 1 ? el.nodeName.toLowerCase() : el.nodeName.toUpperCase();
|
288
|
+
var classes = el.className ? "." + el.className.replace(/\s+/g, ".") : "";
|
289
|
+
var currentSelector = name_1 + classes + (selector ? ">" + selector : "");
|
290
|
+
if (el.parentNode) {
|
291
|
+
var selectorWithParent = getNodeSelector(el.parentNode, currentSelector);
|
292
|
+
if (selectorWithParent.length < MAX_SELECTOR_LENGTH) {
|
293
|
+
return selectorWithParent;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
return currentSelector;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
catch (error) {
|
300
|
+
// Do nothing.
|
301
|
+
}
|
302
|
+
return selector;
|
303
|
+
}
|
304
|
+
|
243
305
|
var Flags = {
|
244
306
|
InitCalled: 1 << 0,
|
245
307
|
NavTimingNotSupported: 1 << 1,
|
@@ -258,56 +320,6 @@
|
|
258
320
|
return flags | flag;
|
259
321
|
}
|
260
322
|
|
261
|
-
function hasParentNode(el) {
|
262
|
-
if (el.parentNode && el.parentNode.tagName) {
|
263
|
-
return true;
|
264
|
-
}
|
265
|
-
return false;
|
266
|
-
}
|
267
|
-
|
268
|
-
/**
|
269
|
-
* Get the interaction attribution name for an element
|
270
|
-
*/
|
271
|
-
function interactionAttributionForElement(el) {
|
272
|
-
// Our first preference is to use the data-sctrack attribute from anywhere in the tree
|
273
|
-
var trackId = getClosestScTrackAttribute(el);
|
274
|
-
if (trackId) {
|
275
|
-
return trackId;
|
276
|
-
}
|
277
|
-
// The second preference is to use the element's ID
|
278
|
-
if (el.id) {
|
279
|
-
return el.id;
|
280
|
-
}
|
281
|
-
// The third preference is to use the text content of a button or link
|
282
|
-
var isSubmitInput = el.tagName === "INPUT" && el.type === "submit";
|
283
|
-
var isButton = el.tagName === "BUTTON";
|
284
|
-
var isLink = el.tagName === "A";
|
285
|
-
if (isSubmitInput && el.value) {
|
286
|
-
return el.value;
|
287
|
-
}
|
288
|
-
if ((isButton || isLink) && el.innerText) {
|
289
|
-
return el.innerText;
|
290
|
-
}
|
291
|
-
if (hasParentNode(el)) {
|
292
|
-
return interactionAttributionForElement(el.parentNode);
|
293
|
-
}
|
294
|
-
// No suitable attribute was found
|
295
|
-
return "";
|
296
|
-
}
|
297
|
-
function getClosestScTrackAttribute(el) {
|
298
|
-
var _a;
|
299
|
-
if (el.hasAttribute("data-sctrack")) {
|
300
|
-
var trackId = (_a = el.getAttribute("data-sctrack")) === null || _a === void 0 ? void 0 : _a.trim();
|
301
|
-
if (trackId) {
|
302
|
-
return trackId;
|
303
|
-
}
|
304
|
-
}
|
305
|
-
if (hasParentNode(el)) {
|
306
|
-
return getClosestScTrackAttribute(el.parentNode);
|
307
|
-
}
|
308
|
-
return null;
|
309
|
-
}
|
310
|
-
|
311
323
|
var LogEvent = {
|
312
324
|
// Internal events
|
313
325
|
EvaluationStart: 1,
|
@@ -358,7 +370,7 @@
|
|
358
370
|
return this.events;
|
359
371
|
};
|
360
372
|
return Logger;
|
361
|
-
}()
|
373
|
+
})();
|
362
374
|
|
363
375
|
var sessionValue = 0;
|
364
376
|
var sessionEntries = [];
|
@@ -406,14 +418,33 @@
|
|
406
418
|
}
|
407
419
|
function addEntry$1(entry) {
|
408
420
|
if (entry.interactionId || (entry.entryType === "first-input" && !entryExists(entry))) {
|
409
|
-
var duration = entry.duration,
|
421
|
+
var duration = entry.duration,
|
422
|
+
startTime = entry.startTime,
|
423
|
+
interactionId = entry.interactionId,
|
424
|
+
processingStart = entry.processingStart,
|
425
|
+
processingEnd = entry.processingEnd,
|
426
|
+
target = entry.target;
|
410
427
|
var existingEntry = slowestEntriesMap[interactionId];
|
428
|
+
var selector = target ? getNodeSelector(target) : null;
|
411
429
|
if (existingEntry) {
|
412
|
-
existingEntry.duration
|
430
|
+
if (existingEntry.duration < duration) {
|
431
|
+
existingEntry.duration = duration;
|
432
|
+
existingEntry.startTime = startTime;
|
433
|
+
existingEntry.processingStart = processingStart;
|
434
|
+
existingEntry.processingEnd = processingEnd;
|
435
|
+
existingEntry.selector = selector;
|
436
|
+
}
|
413
437
|
}
|
414
438
|
else {
|
415
439
|
interactionCountEstimate++;
|
416
|
-
slowestEntriesMap[interactionId] = {
|
440
|
+
slowestEntriesMap[interactionId] = {
|
441
|
+
duration: duration,
|
442
|
+
interactionId: interactionId,
|
443
|
+
startTime: startTime,
|
444
|
+
processingStart: processingStart,
|
445
|
+
processingEnd: processingEnd,
|
446
|
+
selector: selector,
|
447
|
+
};
|
417
448
|
slowestEntries.push(slowestEntriesMap[interactionId]);
|
418
449
|
}
|
419
450
|
// Only store the longest <MAX_INTERACTIONS> interactions
|
@@ -430,10 +461,9 @@
|
|
430
461
|
* Returns an estimated high percentile INP value based on the total number of interactions on the
|
431
462
|
* current page.
|
432
463
|
*/
|
433
|
-
function
|
434
|
-
var _a;
|
464
|
+
function getHighPercentileInteraction() {
|
435
465
|
var index = Math.min(slowestEntries.length - 1, Math.floor(getInteractionCount() / 50));
|
436
|
-
return
|
466
|
+
return slowestEntries[index];
|
437
467
|
}
|
438
468
|
function getInteractionCount() {
|
439
469
|
if ("interactionCount" in performance) {
|
@@ -547,7 +577,7 @@
|
|
547
577
|
// -------------------------------------------------------------------------
|
548
578
|
/// End
|
549
579
|
// -------------------------------------------------------------------------
|
550
|
-
var SCRIPT_VERSION = "
|
580
|
+
var SCRIPT_VERSION = "314";
|
551
581
|
var logger = new Logger();
|
552
582
|
var globalConfig = fromObject(LUX);
|
553
583
|
logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION, globalConfig]);
|
@@ -661,7 +691,7 @@
|
|
661
691
|
*/
|
662
692
|
var getZeroTime = function () {
|
663
693
|
var _a;
|
664
|
-
return
|
694
|
+
return max(pageRestoreTime || 0, getNavigationEntry().activationStart, ((_a = _getMark(START_MARK)) === null || _a === void 0 ? void 0 : _a.startTime) || 0);
|
665
695
|
};
|
666
696
|
/**
|
667
697
|
* Most time-based metrics that LUX reports should be relative to the "zero" marker, rounded down
|
@@ -792,7 +822,7 @@
|
|
792
822
|
return performance.mark.apply(performance, args);
|
793
823
|
}
|
794
824
|
// ...Otherwise provide a polyfill
|
795
|
-
{
|
825
|
+
if (true) {
|
796
826
|
var name_1 = args[0];
|
797
827
|
var detail = ((_a = args[1]) === null || _a === void 0 ? void 0 : _a.detail) || null;
|
798
828
|
var startTime = ((_b = args[1]) === null || _b === void 0 ? void 0 : _b.startTime) || _now();
|
@@ -855,7 +885,7 @@
|
|
855
885
|
return performance.measure.apply(performance, args);
|
856
886
|
}
|
857
887
|
// ...Otherwise provide a polyfill
|
858
|
-
{
|
888
|
+
if (true) {
|
859
889
|
var navEntry = getNavigationEntry();
|
860
890
|
var startTime = typeof startMarkName === "number" ? startMarkName : 0;
|
861
891
|
var endTime = typeof endMarkName === "number" ? endMarkName : _now();
|
@@ -1123,7 +1153,7 @@
|
|
1123
1153
|
}
|
1124
1154
|
else {
|
1125
1155
|
// Return the average of the two middle values.
|
1126
|
-
return
|
1156
|
+
return round((aValues[half - 1] + aValues[half]) / 2.0);
|
1127
1157
|
}
|
1128
1158
|
}
|
1129
1159
|
// Track how long it took lux.js to load via Resource Timing.
|
@@ -1176,7 +1206,7 @@
|
|
1176
1206
|
function ixValues() {
|
1177
1207
|
var aIx = [];
|
1178
1208
|
for (var key in ghIx) {
|
1179
|
-
aIx.push(key + "|" + ghIx[key]);
|
1209
|
+
aIx.push(key + "|" + encodeURIComponent(ghIx[key]));
|
1180
1210
|
}
|
1181
1211
|
return aIx.join(",");
|
1182
1212
|
}
|
@@ -1288,7 +1318,7 @@
|
|
1288
1318
|
e.onloadcssdefined ||
|
1289
1319
|
"print" === e.media ||
|
1290
1320
|
"style" === e.as ||
|
1291
|
-
(typeof e.onload === "function" && e.media === "all"))
|
1321
|
+
(typeof e.onload === "function" && e.media === "all"));
|
1292
1322
|
else {
|
1293
1323
|
nBlocking++;
|
1294
1324
|
}
|
@@ -1492,11 +1522,31 @@
|
|
1492
1522
|
logger.logEvent(LogEvent.PaintTimingNotSupported);
|
1493
1523
|
return undefined;
|
1494
1524
|
}
|
1495
|
-
function
|
1525
|
+
function getINPDetails() {
|
1496
1526
|
if (!("PerformanceEventTiming" in self)) {
|
1497
1527
|
return undefined;
|
1498
1528
|
}
|
1499
|
-
return
|
1529
|
+
return getHighPercentileInteraction();
|
1530
|
+
}
|
1531
|
+
/**
|
1532
|
+
* Build the query string for the INP parameters:
|
1533
|
+
*
|
1534
|
+
* - INP: The duration of the P98 interaction
|
1535
|
+
* - INPs: The selector of the P98 interaction element
|
1536
|
+
* - INPt: The timestamp of the P98 interaction start time
|
1537
|
+
* - INPi: The input delay subpart of the P98 interaction
|
1538
|
+
* - INPp: The processing time subpart of the P98 interaction
|
1539
|
+
* - INPd: The presentation delay subpart of the P98 interaction
|
1540
|
+
*/
|
1541
|
+
function getINPString(details) {
|
1542
|
+
return [
|
1543
|
+
"&INP=" + details.duration,
|
1544
|
+
details.selector ? "&INPs=" + encodeURIComponent(details.selector) : "",
|
1545
|
+
"&INPt=" + clamp(floor(details.startTime)),
|
1546
|
+
"&INPi=" + clamp(floor(details.processingStart - details.startTime)),
|
1547
|
+
"&INPp=" + clamp(floor(details.processingEnd - details.processingStart)),
|
1548
|
+
"&INPd=" + clamp(floor(details.startTime + details.duration - details.processingEnd)),
|
1549
|
+
].join("");
|
1500
1550
|
}
|
1501
1551
|
function getCustomerId() {
|
1502
1552
|
return LUX.customerid || "";
|
@@ -1508,7 +1558,7 @@
|
|
1508
1558
|
while (i--) {
|
1509
1559
|
totalParents += numParents(aElems[i]);
|
1510
1560
|
}
|
1511
|
-
var average =
|
1561
|
+
var average = round(totalParents / aElems.length);
|
1512
1562
|
return average;
|
1513
1563
|
}
|
1514
1564
|
function numParents(elem) {
|
@@ -1607,7 +1657,7 @@
|
|
1607
1657
|
var vw = document.documentElement.clientWidth;
|
1608
1658
|
// Return true if the top-left corner is in the viewport and it has width & height.
|
1609
1659
|
var lt = findPos(e);
|
1610
|
-
return
|
1660
|
+
return lt[0] >= 0 && lt[1] >= 0 && lt[0] < vw && lt[1] < vh && e.offsetWidth > 0 && e.offsetHeight > 0;
|
1611
1661
|
}
|
1612
1662
|
// Return an array containing the top & left coordinates of the element.
|
1613
1663
|
// from http://www.quirksmode.org/js/findpos.html
|
@@ -1689,14 +1739,14 @@
|
|
1689
1739
|
_markLoadTime();
|
1690
1740
|
}
|
1691
1741
|
var sIx = "";
|
1692
|
-
var INP =
|
1693
|
-
//
|
1694
|
-
//
|
1695
|
-
// has not been sent.
|
1742
|
+
var INP = getINPDetails();
|
1743
|
+
// If we haven't already sent an interaction beacon, check for interaction metrics and include
|
1744
|
+
// them in the main beacon.
|
1696
1745
|
if (!gbIxSent) {
|
1697
1746
|
sIx = ixValues();
|
1698
1747
|
if (sIx === "") {
|
1699
|
-
// If there are no interaction metrics, we
|
1748
|
+
// If there are no interaction metrics, we wait to send INP with the IX beacon to increase
|
1749
|
+
// the chance that we capture a valid INP.
|
1700
1750
|
INP = undefined;
|
1701
1751
|
}
|
1702
1752
|
}
|
@@ -1766,13 +1816,14 @@
|
|
1766
1816
|
nErrors +
|
1767
1817
|
"nt" +
|
1768
1818
|
navigationType() +
|
1769
|
-
(navigator.deviceMemory ? "dm" +
|
1819
|
+
(navigator.deviceMemory ? "dm" + round(navigator.deviceMemory) : "") + // device memory (GB)
|
1770
1820
|
(sIx ? "&IX=" + sIx : "") +
|
1771
1821
|
(typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
|
1772
1822
|
(sCPU ? "&CPU=" + sCPU : "") +
|
1773
1823
|
(sET ? "&ET=" + sET : "") + // element timing
|
1774
1824
|
(typeof CLS !== "undefined" ? "&CLS=" + CLS : "") +
|
1775
|
-
|
1825
|
+
// INP and sub-parts
|
1826
|
+
(typeof INP !== "undefined" ? getINPString(INP) : "");
|
1776
1827
|
// We add the user timing entries last so that we can split them to reduce the URL size if necessary.
|
1777
1828
|
var utValues = userTimingValues();
|
1778
1829
|
var _b = fitUserTimingEntries(utValues, globalConfig, baseUrl + metricsQueryString), beaconUtValues = _b[0], remainingUtValues = _b[1];
|
@@ -1788,7 +1839,7 @@
|
|
1788
1839
|
gbIxSent = sIx ? 1 : 0;
|
1789
1840
|
// Send other beacons for JUST User Timing.
|
1790
1841
|
while (remainingUtValues.length) {
|
1791
|
-
_a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl), beaconUtValues = _a[0], remainingUtValues = _a[1];
|
1842
|
+
(_a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl)), (beaconUtValues = _a[0]), (remainingUtValues = _a[1]);
|
1792
1843
|
var utBeaconUrl = baseUrl + "&UT=" + beaconUtValues.join(",");
|
1793
1844
|
logger.logEvent(LogEvent.UserTimingBeaconSent, [utBeaconUrl]);
|
1794
1845
|
_sendBeacon(utBeaconUrl);
|
@@ -1811,13 +1862,13 @@
|
|
1811
1862
|
return;
|
1812
1863
|
}
|
1813
1864
|
var sIx = ixValues(); // Interaction Metrics
|
1814
|
-
var INP =
|
1865
|
+
var INP = getINPDetails();
|
1815
1866
|
if (sIx) {
|
1816
1867
|
var beaconUrl = _getBeaconUrl(getUpdatedCustomData()) +
|
1817
1868
|
"&IX=" +
|
1818
1869
|
sIx +
|
1819
1870
|
(typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
|
1820
|
-
(typeof INP !== "undefined" ?
|
1871
|
+
(typeof INP !== "undefined" ? getINPString(INP) : "");
|
1821
1872
|
logger.logEvent(LogEvent.InteractionBeaconSent, [beaconUrl]);
|
1822
1873
|
_sendBeacon(beaconUrl);
|
1823
1874
|
gbIxSent = 1;
|
@@ -1872,22 +1923,27 @@
|
|
1872
1923
|
if (keyCode === 16 || keyCode === 17 || keyCode === 18 || keyCode === 20 || keyCode === 224) {
|
1873
1924
|
return;
|
1874
1925
|
}
|
1875
|
-
_removeIxHandlers();
|
1876
1926
|
if (typeof ghIx["k"] === "undefined") {
|
1877
1927
|
ghIx["k"] = _now();
|
1878
1928
|
if (e && e.target instanceof Element) {
|
1879
|
-
var trackId =
|
1929
|
+
var trackId = getNodeSelector(e.target);
|
1880
1930
|
if (trackId) {
|
1881
1931
|
ghIx["ki"] = trackId;
|
1882
1932
|
}
|
1883
1933
|
}
|
1934
|
+
// Only one interaction type is recorded. Scrolls are considered less important, so delete
|
1935
|
+
// any scroll times if they exist.
|
1936
|
+
delete ghIx["s"];
|
1884
1937
|
_sendIxAfterDelay();
|
1885
1938
|
}
|
1939
|
+
_removeIxHandlers();
|
1886
1940
|
}
|
1887
1941
|
function _clickHandler(e) {
|
1888
|
-
_removeIxHandlers();
|
1889
1942
|
if (typeof ghIx["c"] === "undefined") {
|
1890
1943
|
ghIx["c"] = _now();
|
1944
|
+
// Only one interaction type is recorded. Scrolls are considered less important, so delete
|
1945
|
+
// any scroll times if they exist.
|
1946
|
+
delete ghIx["s"];
|
1891
1947
|
var target = void 0;
|
1892
1948
|
try {
|
1893
1949
|
// Seeing "Permission denied" errors, so do a simple try-catch.
|
@@ -1904,13 +1960,14 @@
|
|
1904
1960
|
ghIx["cx"] = e.clientX;
|
1905
1961
|
ghIx["cy"] = e.clientY;
|
1906
1962
|
}
|
1907
|
-
var trackId =
|
1963
|
+
var trackId = getNodeSelector(target);
|
1908
1964
|
if (trackId) {
|
1909
1965
|
ghIx["ci"] = trackId;
|
1910
1966
|
}
|
1911
1967
|
}
|
1912
1968
|
_sendIxAfterDelay();
|
1913
1969
|
}
|
1970
|
+
_removeIxHandlers();
|
1914
1971
|
}
|
1915
1972
|
// Wrapper to support older browsers (<= IE8)
|
1916
1973
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
@@ -1976,7 +2033,7 @@
|
|
1976
2033
|
// "00" matches all sample rates
|
1977
2034
|
return Number(new Date()) + "00000";
|
1978
2035
|
}
|
1979
|
-
return Number(new Date()) + _padLeft(String(
|
2036
|
+
return Number(new Date()) + _padLeft(String(round(100000 * Math.random())), "00000");
|
1980
2037
|
}
|
1981
2038
|
// Unique ID (also known as Session ID)
|
1982
2039
|
// We use this to track all the page views in a single user session.
|
@@ -2063,6 +2120,8 @@
|
|
2063
2120
|
"=" +
|
2064
2121
|
escape(value) +
|
2065
2122
|
(seconds ? "; max-age=" + seconds : "") +
|
2123
|
+
(globalConfig.cookieDomain ? "; domain=" +
|
2124
|
+
globalConfig.cookieDomain : "") +
|
2066
2125
|
"; path=/; SameSite=Lax";
|
2067
2126
|
}
|
2068
2127
|
catch (e) {
|
@@ -104,6 +104,10 @@
|
|
104
104
|
@include tall-crest;
|
105
105
|
}
|
106
106
|
|
107
|
+
.gem-c-organisation-logo__crest--no10 {
|
108
|
+
@include crest("no10_crest", $xpos: 8px);
|
109
|
+
}
|
110
|
+
|
107
111
|
.gem-c-organisation-logo__crest--single-identity,
|
108
112
|
.gem-c-organisation-logo__crest--eo,
|
109
113
|
.gem-c-organisation-logo__crest--org {
|
@@ -17,6 +17,7 @@
|
|
17
17
|
omit_footer_navigation ||= false
|
18
18
|
omit_footer_border ||= false
|
19
19
|
omit_header ||= false
|
20
|
+
custom_layout ||= false
|
20
21
|
product_name ||= nil
|
21
22
|
show_explore_header ||= false
|
22
23
|
show_cross_service_header ||= false
|
@@ -120,6 +121,8 @@
|
|
120
121
|
service_navigation_items: service_navigation_items,
|
121
122
|
product_name: product_name,
|
122
123
|
} %>
|
124
|
+
<% elsif content_for?(:custom_header) %>
|
125
|
+
<%= yield :custom_header %>
|
123
126
|
<% else %>
|
124
127
|
<%= render "govuk_publishing_components/components/layout_header", {
|
125
128
|
search: show_search,
|
@@ -159,6 +162,8 @@
|
|
159
162
|
<%= yield :before_content %>
|
160
163
|
<%= yield %>
|
161
164
|
<% end %>
|
165
|
+
<% elsif custom_layout %>
|
166
|
+
<%= yield %>
|
162
167
|
<% else %>
|
163
168
|
<div id="wrapper" class="<%= "govuk-width-container" unless full_width %>">
|
164
169
|
<%= yield :before_content %>
|
@@ -123,3 +123,19 @@ examples:
|
|
123
123
|
cookie_preferences:
|
124
124
|
text: How GOV.UK accounts use cookies
|
125
125
|
href: https://www.gov.uk/government/publications/govuk-accounts-trial-full-privacy-notice-and-accessibility-statement
|
126
|
+
with_custom_layout:
|
127
|
+
description: Yields a custom layout for the content.
|
128
|
+
data:
|
129
|
+
custom_layout: true
|
130
|
+
block: |
|
131
|
+
<main id="custom-layout">
|
132
|
+
<h1>This is a custom layout</h1>
|
133
|
+
</main>
|
134
|
+
with_custom_header:
|
135
|
+
description: Allows the header to be replaced with HTML injected by the calling application in a `content_for` tag named `:custom_header`.
|
136
|
+
embed: |
|
137
|
+
<% content_for(:custom_header) do %>
|
138
|
+
<header id="custom-header">I'm a custom header</header>
|
139
|
+
<% end %>
|
140
|
+
<%= render "govuk_publishing_components/components/layout_for_public", {
|
141
|
+
} %>
|
@@ -51,8 +51,8 @@ examples:
|
|
51
51
|
executive_office:
|
52
52
|
data:
|
53
53
|
organisation:
|
54
|
-
name:
|
55
|
-
url: '/government/organisations/
|
54
|
+
name: Example
|
55
|
+
url: '/government/organisations/'
|
56
56
|
brand: 'executive-office'
|
57
57
|
crest: 'eo'
|
58
58
|
home_office:
|
@@ -69,6 +69,13 @@ examples:
|
|
69
69
|
url: '/government/organisations/ministry-of-defence'
|
70
70
|
brand: 'ministry-of-defence'
|
71
71
|
crest: 'mod'
|
72
|
+
prime_ministers_office_10_downing_street:
|
73
|
+
data:
|
74
|
+
organisation:
|
75
|
+
name: "Prime Minister's Office,<br>10 Downing Street"
|
76
|
+
url: '/government/organisations/prime-ministers-office-10-downing-street'
|
77
|
+
brand: 'prime-ministers-office-10-downing-street'
|
78
|
+
crest: 'no10'
|
72
79
|
office_of_the_advocate_general_for_scotland:
|
73
80
|
data:
|
74
81
|
organisation:
|
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: 38.
|
4
|
+
version: 38.2.0
|
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: 2024-
|
11
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: govuk_app_config
|
@@ -373,6 +373,7 @@ files:
|
|
373
373
|
- app/assets/images/govuk_publishing_components/crests/hmrc_crest_18px_x2.png
|
374
374
|
- app/assets/images/govuk_publishing_components/crests/ho_crest_18px_x2.png
|
375
375
|
- app/assets/images/govuk_publishing_components/crests/mod_crest_18px_x2.png
|
376
|
+
- app/assets/images/govuk_publishing_components/crests/no10_crest_18px_x2.png
|
376
377
|
- app/assets/images/govuk_publishing_components/crests/org_crest_18px_x2.png
|
377
378
|
- app/assets/images/govuk_publishing_components/crests/portcullis_18px_x2.png
|
378
379
|
- app/assets/images/govuk_publishing_components/crests/so_crest_18px_x2.png
|
@@ -1599,7 +1600,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1599
1600
|
- !ruby/object:Gem::Version
|
1600
1601
|
version: '0'
|
1601
1602
|
requirements: []
|
1602
|
-
rubygems_version: 3.5.
|
1603
|
+
rubygems_version: 3.5.10
|
1603
1604
|
signing_key:
|
1604
1605
|
specification_version: 4
|
1605
1606
|
summary: A gem to document components in GOV.UK frontend applications
|