govuk_publishing_components 60.2.1 → 61.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +704 -594
- data/app/assets/stylesheets/govuk_publishing_components/component_support.scss +0 -2
- data/app/assets/stylesheets/govuk_publishing_components/components/_cards.scss +1 -1
- data/app/assets/stylesheets/govuk_publishing_components/components/_contents-list.scss +0 -4
- data/app/assets/stylesheets/govuk_publishing_components/components/_cross-service-header.scss +4 -4
- data/app/assets/stylesheets/govuk_publishing_components/components/_govspeak.scss +1 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +51 -40
- data/app/assets/stylesheets/govuk_publishing_components/components/_related-navigation.scss +0 -11
- data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +1 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_select-with-search.scss +1 -1
- data/app/assets/stylesheets/govuk_publishing_components/components/_share-links.scss +0 -1
- data/app/assets/stylesheets/govuk_publishing_components/components/_subscription-links.scss +1 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_translation-nav.scss +0 -5
- data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_attachment.scss +2 -1
- data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_charts.scss +9 -8
- data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_content-block.scss +27 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_css3.scss +6 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_margins.scss +0 -13
- data/app/assets/stylesheets/govuk_publishing_components/govuk_frontend_support.scss +0 -3
- data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +222 -237
- data/app/views/govuk_publishing_components/components/docs/govspeak.yml +69 -0
- data/app/views/govuk_publishing_components/components/docs/translation_nav.yml +0 -11
- data/lib/govuk_publishing_components/presenters/translation_nav_helper.rb +0 -6
- data/lib/govuk_publishing_components/version.rb +1 -1
- metadata +2 -4
- data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_govuk-template-link-focus-override.scss +0 -14
- data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_media-down.scss +0 -22
- data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_prefixed-transform.scss +0 -5
@@ -22,9 +22,9 @@
|
|
22
22
|
(function () {
|
23
23
|
'use strict';
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
var max = Math.max;
|
26
|
+
var floor = Math.floor;
|
27
|
+
var round = Math.round;
|
28
28
|
/**
|
29
29
|
* Clamp a number so that it is never less than 0
|
30
30
|
*/
|
@@ -41,15 +41,16 @@
|
|
41
41
|
|
42
42
|
var scriptStartTime = now();
|
43
43
|
|
44
|
+
var _a;
|
44
45
|
// If the various performance APIs aren't available, we export an empty object to
|
45
46
|
// prevent having to make regular typeof checks.
|
46
|
-
|
47
|
-
|
47
|
+
var performance = window.performance || {};
|
48
|
+
var timing = performance.timing || {
|
48
49
|
activationStart: 0,
|
49
50
|
// If performance.timing isn't available, we attempt to polyfill the navigationStart value.
|
50
51
|
// Our first attempt is from LUX.ns, which is the time that the snippet execution began. If this
|
51
52
|
// is not available, we fall back to the time that the current script execution began.
|
52
|
-
navigationStart: window.LUX
|
53
|
+
navigationStart: ((_a = window.LUX) === null || _a === void 0 ? void 0 : _a.ns) || scriptStartTime,
|
53
54
|
};
|
54
55
|
function navigationType() {
|
55
56
|
if (performance.navigation && typeof performance.navigation.type !== "undefined") {
|
@@ -66,34 +67,34 @@
|
|
66
67
|
* @see https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-deliverytype
|
67
68
|
*/
|
68
69
|
function deliveryType() {
|
69
|
-
|
70
|
+
var navEntry = getNavigationEntry();
|
70
71
|
if ("deliveryType" in navEntry) {
|
71
72
|
return navEntry.deliveryType || "(empty string)";
|
72
73
|
}
|
73
74
|
return undefined;
|
74
75
|
}
|
75
76
|
function getNavigationEntry() {
|
76
|
-
|
77
|
+
var navEntries = getEntriesByType("navigation");
|
77
78
|
if (navEntries.length) {
|
78
|
-
|
79
|
-
|
79
|
+
var nativeEntry = navEntries[0];
|
80
|
+
var entry_1 = {
|
80
81
|
navigationStart: 0,
|
81
82
|
activationStart: 0,
|
82
83
|
};
|
83
|
-
for (
|
84
|
-
|
84
|
+
for (var key in nativeEntry) {
|
85
|
+
entry_1[key] = nativeEntry[key];
|
85
86
|
}
|
86
|
-
return
|
87
|
+
return entry_1;
|
87
88
|
}
|
88
|
-
|
89
|
-
|
89
|
+
var navType = navigationType();
|
90
|
+
var entry = {
|
90
91
|
navigationStart: 0,
|
91
92
|
activationStart: 0,
|
92
93
|
startTime: 0,
|
93
94
|
type: navType == 2 ? "back_forward" : navType === 1 ? "reload" : "navigate",
|
94
95
|
};
|
95
|
-
|
96
|
-
for (
|
96
|
+
{
|
97
|
+
for (var key in timing) {
|
97
98
|
if (typeof timing[key] === "number" && key !== "navigationStart") {
|
98
99
|
entry[key] = floor(timing[key] - timing.navigationStart);
|
99
100
|
}
|
@@ -108,7 +109,7 @@
|
|
108
109
|
*/
|
109
110
|
function getEntriesByType(type) {
|
110
111
|
if (typeof performance.getEntriesByType === "function") {
|
111
|
-
|
112
|
+
var entries = performance.getEntriesByType(type);
|
112
113
|
if (entries && entries.length) {
|
113
114
|
return entries;
|
114
115
|
}
|
@@ -122,7 +123,7 @@
|
|
122
123
|
*/
|
123
124
|
function getEntriesByName(type) {
|
124
125
|
if (typeof performance.getEntriesByName === "function") {
|
125
|
-
|
126
|
+
var entries = performance.getEntriesByName(type);
|
126
127
|
if (entries && entries.length) {
|
127
128
|
return entries;
|
128
129
|
}
|
@@ -138,18 +139,18 @@
|
|
138
139
|
return true;
|
139
140
|
}
|
140
141
|
function onVisible(cb) {
|
141
|
-
afterPrerender(()
|
142
|
+
afterPrerender(function () {
|
142
143
|
if (isVisible()) {
|
143
144
|
cb();
|
144
145
|
}
|
145
146
|
else {
|
146
|
-
|
147
|
+
var onVisibleCallback_1 = function () {
|
147
148
|
if (isVisible()) {
|
148
149
|
cb();
|
149
|
-
removeEventListener("visibilitychange",
|
150
|
+
removeEventListener("visibilitychange", onVisibleCallback_1);
|
150
151
|
}
|
151
152
|
};
|
152
|
-
addEventListener("visibilitychange",
|
153
|
+
addEventListener("visibilitychange", onVisibleCallback_1, true);
|
153
154
|
}
|
154
155
|
});
|
155
156
|
}
|
@@ -168,13 +169,34 @@
|
|
168
169
|
return getNavigationEntry().redirectCount > 0 || timing.redirectEnd > 0;
|
169
170
|
}
|
170
171
|
|
172
|
+
var subscribers = {};
|
173
|
+
var eventData = {};
|
174
|
+
function subscribe(event, callback) {
|
175
|
+
if (!subscribers[event]) {
|
176
|
+
subscribers[event] = [];
|
177
|
+
}
|
178
|
+
subscribers[event].push(callback);
|
179
|
+
// Ensure previous event data is available to new subscribers
|
180
|
+
if (eventData[event] !== undefined) {
|
181
|
+
callback(eventData[event]);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
function emit(event, data) {
|
185
|
+
eventData[event] = data;
|
186
|
+
if (!subscribers[event]) {
|
187
|
+
return;
|
188
|
+
}
|
189
|
+
subscribers[event].forEach(function (callback) { return callback(data); });
|
190
|
+
}
|
191
|
+
|
171
192
|
function addFlag(flags, flag) {
|
172
193
|
return flags | flag;
|
173
194
|
}
|
174
195
|
|
175
196
|
// Wrapper to support older browsers (<= IE8)
|
176
197
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
177
|
-
function addListener(type, callback, useCapture
|
198
|
+
function addListener(type, callback, useCapture) {
|
199
|
+
if (useCapture === void 0) { useCapture = false; }
|
178
200
|
if (addEventListener) {
|
179
201
|
addEventListener(type, callback, useCapture);
|
180
202
|
}
|
@@ -184,7 +206,8 @@
|
|
184
206
|
}
|
185
207
|
// Wrapper to support older browsers (<= IE8)
|
186
208
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
187
|
-
function removeListener(type, callback, useCapture
|
209
|
+
function removeListener(type, callback, useCapture) {
|
210
|
+
if (useCapture === void 0) { useCapture = false; }
|
188
211
|
if (removeEventListener) {
|
189
212
|
removeEventListener(type, callback, useCapture);
|
190
213
|
}
|
@@ -193,14 +216,14 @@
|
|
193
216
|
}
|
194
217
|
}
|
195
218
|
|
196
|
-
|
197
|
-
|
198
|
-
|
219
|
+
var START_MARK = "LUX_start";
|
220
|
+
var END_MARK = "LUX_end";
|
221
|
+
var BOOLEAN_TRUE = "true";
|
199
222
|
|
200
223
|
/**
|
201
224
|
* Milliseconds since navigationStart representing when the page was restored from the bfcache
|
202
225
|
*/
|
203
|
-
|
226
|
+
var pageRestoreTime;
|
204
227
|
function setPageRestoreTime(time) {
|
205
228
|
pageRestoreTime = time;
|
206
229
|
}
|
@@ -213,7 +236,8 @@
|
|
213
236
|
* is the page restore time. On all other pages this value will be zero.
|
214
237
|
*/
|
215
238
|
function getZeroTime() {
|
216
|
-
|
239
|
+
var _a;
|
240
|
+
return max(getPageRestoreTime() || 0, getNavigationEntry().activationStart, ((_a = getEntriesByName(START_MARK).pop()) === null || _a === void 0 ? void 0 : _a.startTime) || 0);
|
217
241
|
}
|
218
242
|
/**
|
219
243
|
* Most time-based metrics that LUX reports should be relative to the "zero" marker, rounded down
|
@@ -236,14 +260,16 @@
|
|
236
260
|
* time since the last LUX.init() call.
|
237
261
|
*/
|
238
262
|
function msSincePageInit() {
|
239
|
-
|
240
|
-
|
263
|
+
var sinceNavigationStart = msSinceNavigationStart();
|
264
|
+
var startMark = getEntriesByName(START_MARK).pop();
|
241
265
|
if (startMark) {
|
242
266
|
return floor(sinceNavigationStart - startMark.startTime);
|
243
267
|
}
|
244
268
|
return sinceNavigationStart;
|
245
269
|
}
|
246
270
|
|
271
|
+
var version = "4.3.2";
|
272
|
+
|
247
273
|
function padStart(str, length, char) {
|
248
274
|
while (str.length < length) {
|
249
275
|
str = char + str;
|
@@ -251,24 +277,25 @@
|
|
251
277
|
return str;
|
252
278
|
}
|
253
279
|
|
254
|
-
|
280
|
+
var VERSION = version;
|
255
281
|
/**
|
256
282
|
* Returns the version of the script as a float to be stored in legacy systems that do not support
|
257
283
|
* string versions.
|
258
284
|
*/
|
259
|
-
function versionAsFloat(ver
|
260
|
-
|
285
|
+
function versionAsFloat(ver) {
|
286
|
+
if (ver === void 0) { ver = VERSION; }
|
287
|
+
var parts = ver.split(".");
|
261
288
|
return parseFloat(parts[0] + "." + padStart(parts[1], 2, "0") + padStart(parts[2], 2, "0"));
|
262
289
|
}
|
263
290
|
|
264
|
-
|
265
|
-
|
291
|
+
var sendBeaconFallback = function (url, data) {
|
292
|
+
var xhr = new XMLHttpRequest();
|
266
293
|
xhr.open("POST", url, true);
|
267
294
|
xhr.setRequestHeader("content-type", "application/json");
|
268
295
|
xhr.send(String(data));
|
269
296
|
return true;
|
270
297
|
};
|
271
|
-
|
298
|
+
var sendBeacon = "sendBeacon" in navigator ? navigator.sendBeacon.bind(navigator) : sendBeaconFallback;
|
272
299
|
/**
|
273
300
|
* Some values should only be reported if they are non-zero. The exception to this is when the page
|
274
301
|
* was prerendered or restored from BF cache
|
@@ -285,8 +312,8 @@
|
|
285
312
|
*/
|
286
313
|
function fitUserTimingEntries(utValues, config, url) {
|
287
314
|
// Start with the maximum allowed UT entries per beacon
|
288
|
-
|
289
|
-
|
315
|
+
var beaconUtValues = utValues.slice(0, config.maxBeaconUTEntries);
|
316
|
+
var remainingUtValues = utValues.slice(config.maxBeaconUTEntries);
|
290
317
|
// Trim UT entries until they fit within the maximum URL length, ensuring at least one UT entry
|
291
318
|
// is included.
|
292
319
|
while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength &&
|
@@ -295,93 +322,92 @@
|
|
295
322
|
}
|
296
323
|
return [beaconUtValues, remainingUtValues];
|
297
324
|
}
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
flags = 0;
|
309
|
-
startTime;
|
310
|
-
metricCollectors = {};
|
311
|
-
onBeforeSendCbs = [];
|
312
|
-
constructor(opts) {
|
325
|
+
var Beacon = /** @class */ (function () {
|
326
|
+
function Beacon(opts) {
|
327
|
+
var _this = this;
|
328
|
+
this.isRecording = true;
|
329
|
+
this.isSent = false;
|
330
|
+
this.sendRetries = 0;
|
331
|
+
this.maxMeasureTimeout = 0;
|
332
|
+
this.flags = 0;
|
333
|
+
this.metricCollectors = {};
|
334
|
+
this.onBeforeSendCbs = [];
|
313
335
|
this.startTime = opts.startTime || getZeroTime();
|
314
336
|
this.config = opts.config;
|
315
337
|
this.logger = opts.logger;
|
316
338
|
this.customerId = opts.customerId;
|
317
339
|
this.sessionId = opts.sessionId;
|
318
340
|
this.pageId = opts.pageId;
|
319
|
-
this.maxMeasureTimeout = window.setTimeout(()
|
320
|
-
|
321
|
-
|
322
|
-
|
341
|
+
this.maxMeasureTimeout = window.setTimeout(function () {
|
342
|
+
_this.logger.logEvent(82 /* LogEvent.PostBeaconTimeoutReached */);
|
343
|
+
_this.stopRecording();
|
344
|
+
_this.send();
|
323
345
|
}, this.config.maxMeasureTime - msSincePageInit());
|
324
|
-
addListener("securitypolicyviolation", (e)
|
325
|
-
if (e.disposition !== "report" && e.blockedURI ===
|
346
|
+
addListener("securitypolicyviolation", function (e) {
|
347
|
+
if (e.disposition !== "report" && e.blockedURI === _this.config.beaconUrlV2 && "URL" in self) {
|
326
348
|
// Some websites might have CSP rules that allow the GET beacon, but not the POST beacon.
|
327
349
|
// We can detect this here and attempt to send the beacon to a fallback endpoint.
|
328
350
|
//
|
329
351
|
// If the fallback endpoint has not been provided in the config, we will fall back to using
|
330
352
|
// the POST beacon pathname on the GET beacon origin.
|
331
|
-
if (!
|
332
|
-
|
333
|
-
|
334
|
-
|
353
|
+
if (!_this.config.beaconUrlFallback) {
|
354
|
+
var getOrigin = new URL(_this.config.beaconUrl).origin;
|
355
|
+
var postPathname = new URL(_this.config.beaconUrlV2).pathname;
|
356
|
+
_this.config.beaconUrlFallback = getOrigin + postPathname;
|
335
357
|
}
|
336
358
|
// Update the V2 beacon URL
|
337
|
-
|
338
|
-
|
339
|
-
|
359
|
+
_this.config.beaconUrlV2 = _this.config.beaconUrlFallback;
|
360
|
+
_this.logger.logEvent(90 /* LogEvent.PostBeaconCSPViolation */, [_this.config.beaconUrlV2]);
|
361
|
+
_this.addFlag(4096 /* Flags.BeaconBlockedByCsp */);
|
340
362
|
// Not all browsers return false if sendBeacon fails. In this case, `this.isSent` will be
|
341
363
|
// true, even though the beacon wasn't sent. We need to reset this flag to ensure we can
|
342
364
|
// retry sending the beacon.
|
343
|
-
|
365
|
+
_this.isSent = false;
|
344
366
|
// Try to send the beacon again
|
345
|
-
if (
|
346
|
-
|
347
|
-
|
367
|
+
if (_this.sendRetries < 1) {
|
368
|
+
_this.sendRetries++;
|
369
|
+
_this.send();
|
348
370
|
}
|
349
371
|
}
|
350
372
|
});
|
351
373
|
this.logger.logEvent(80 /* LogEvent.PostBeaconInitialised */);
|
352
374
|
}
|
353
|
-
isBeingSampled() {
|
354
|
-
|
375
|
+
Beacon.prototype.isBeingSampled = function () {
|
376
|
+
var bucket = parseInt(String(this.sessionId).slice(-2));
|
355
377
|
return bucket < this.config.samplerate;
|
356
|
-
}
|
357
|
-
stopRecording() {
|
378
|
+
};
|
379
|
+
Beacon.prototype.stopRecording = function () {
|
358
380
|
this.isRecording = false;
|
359
381
|
this.logger.logEvent(86 /* LogEvent.PostBeaconStopRecording */);
|
360
|
-
}
|
361
|
-
addCollector(metric, collector) {
|
382
|
+
};
|
383
|
+
Beacon.prototype.addCollector = function (metric, collector) {
|
362
384
|
this.metricCollectors[metric] = collector;
|
363
|
-
}
|
364
|
-
addFlag(flag) {
|
385
|
+
};
|
386
|
+
Beacon.prototype.addFlag = function (flag) {
|
365
387
|
this.flags = addFlag(this.flags, flag);
|
366
|
-
}
|
367
|
-
beaconUrl() {
|
388
|
+
};
|
389
|
+
Beacon.prototype.beaconUrl = function () {
|
368
390
|
return this.config.beaconUrlV2;
|
369
|
-
}
|
370
|
-
onBeforeSend(cb) {
|
391
|
+
};
|
392
|
+
Beacon.prototype.onBeforeSend = function (cb) {
|
371
393
|
this.onBeforeSendCbs.push(cb);
|
372
|
-
}
|
373
|
-
send() {
|
394
|
+
};
|
395
|
+
Beacon.prototype.send = function () {
|
396
|
+
if (this.isSent) {
|
397
|
+
return;
|
398
|
+
}
|
374
399
|
this.logger.logEvent(81 /* LogEvent.PostBeaconSendCalled */);
|
375
|
-
for (
|
400
|
+
for (var _i = 0, _a = this.onBeforeSendCbs; _i < _a.length; _i++) {
|
401
|
+
var cb = _a[_i];
|
376
402
|
cb();
|
377
403
|
}
|
378
404
|
if (!this.isBeingSampled()) {
|
379
405
|
return;
|
380
406
|
}
|
381
|
-
|
382
|
-
|
383
|
-
for (
|
384
|
-
|
407
|
+
var collectionStart = now();
|
408
|
+
var metricData = {};
|
409
|
+
for (var metric in this.metricCollectors) {
|
410
|
+
var data = this.metricCollectors[metric](this.config);
|
385
411
|
this.logger.logEvent(91 /* LogEvent.PostBeaconCollector */, [metric, !!data]);
|
386
412
|
if (data) {
|
387
413
|
metricData[metric] = data;
|
@@ -393,20 +419,17 @@
|
|
393
419
|
this.logger.logEvent(85 /* LogEvent.PostBeaconCancelled */);
|
394
420
|
return;
|
395
421
|
}
|
396
|
-
if (this.isSent) {
|
397
|
-
this.logger.logEvent(84 /* LogEvent.PostBeaconAlreadySent */);
|
398
|
-
return;
|
399
|
-
}
|
400
422
|
// Only clear the max measure timeout if there's data to send.
|
401
423
|
clearTimeout(this.maxMeasureTimeout);
|
402
|
-
|
403
|
-
|
424
|
+
var beaconUrl = this.beaconUrl();
|
425
|
+
var payload = Object.assign({
|
404
426
|
customerId: this.customerId,
|
405
427
|
flags: this.flags,
|
406
428
|
measureDuration: msSincePageInit(),
|
407
429
|
collectionDuration: now() - collectionStart,
|
408
430
|
pageId: this.pageId,
|
409
431
|
scriptVersion: VERSION,
|
432
|
+
snippetVersion: this.config.snippetVersion,
|
410
433
|
sessionId: this.sessionId,
|
411
434
|
startTime: this.startTime,
|
412
435
|
}, metricData);
|
@@ -414,6 +437,7 @@
|
|
414
437
|
if (sendBeacon(beaconUrl, JSON.stringify(payload))) {
|
415
438
|
this.isSent = true;
|
416
439
|
this.logger.logEvent(83 /* LogEvent.PostBeaconSent */, [beaconUrl, payload]);
|
440
|
+
emit("beacon", payload);
|
417
441
|
}
|
418
442
|
}
|
419
443
|
catch (e) {
|
@@ -422,8 +446,9 @@
|
|
422
446
|
if (!this.isSent) {
|
423
447
|
this.logger.logEvent(89 /* LogEvent.PostBeaconSendFailed */, [beaconUrl, payload]);
|
424
448
|
}
|
425
|
-
}
|
426
|
-
|
449
|
+
};
|
450
|
+
return Beacon;
|
451
|
+
}());
|
427
452
|
var BeaconMetricKey;
|
428
453
|
(function (BeaconMetricKey) {
|
429
454
|
BeaconMetricKey["CLS"] = "cls";
|
@@ -440,15 +465,16 @@
|
|
440
465
|
}
|
441
466
|
else {
|
442
467
|
// Listen for the onload event and run the callback after a short delay
|
443
|
-
addListener("load", ()
|
468
|
+
addListener("load", function () {
|
444
469
|
setTimeout(callback, 200);
|
445
470
|
});
|
446
471
|
}
|
447
472
|
}
|
448
473
|
|
449
|
-
|
474
|
+
var luxOrigin = "https://lux.speedcurve.com";
|
450
475
|
function fromObject(obj) {
|
451
|
-
|
476
|
+
var spaMode = getProperty(obj, "spaMode", false);
|
477
|
+
var autoMode = spaMode ? false : getProperty(obj, "auto", true);
|
452
478
|
return {
|
453
479
|
allowEmptyPostBeacon: getProperty(obj, "allowEmptyPostBeacon", false),
|
454
480
|
auto: autoMode,
|
@@ -466,14 +492,15 @@
|
|
466
492
|
maxBeaconUrlLength: getProperty(obj, "maxBeaconUrlLength", 8190),
|
467
493
|
maxBeaconUTEntries: getProperty(obj, "maxBeaconUTEntries", 20),
|
468
494
|
maxErrors: getProperty(obj, "maxErrors", 5),
|
469
|
-
maxMeasureTime: getProperty(obj, "maxMeasureTime",
|
470
|
-
measureUntil: getProperty(obj, "measureUntil", "onload"),
|
495
|
+
maxMeasureTime: getProperty(obj, "maxMeasureTime", 60000),
|
496
|
+
measureUntil: getProperty(obj, "measureUntil", spaMode ? "pagehidden" : "onload"),
|
471
497
|
minMeasureTime: getProperty(obj, "minMeasureTime", 0),
|
472
498
|
newBeaconOnPageShow: getProperty(obj, "newBeaconOnPageShow", false),
|
473
499
|
pagegroups: getProperty(obj, "pagegroups"),
|
474
500
|
samplerate: getProperty(obj, "samplerate", 100),
|
475
|
-
sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", autoMode),
|
501
|
+
sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", spaMode || autoMode),
|
476
502
|
serverTiming: getProperty(obj, "serverTiming"),
|
503
|
+
spaMode: spaMode,
|
477
504
|
trackErrors: getProperty(obj, "trackErrors", true),
|
478
505
|
trackHiddenPages: getProperty(obj, "trackHiddenPages", false),
|
479
506
|
};
|
@@ -485,13 +512,13 @@
|
|
485
512
|
return defaultValue;
|
486
513
|
}
|
487
514
|
|
488
|
-
|
515
|
+
var SESSION_COOKIE_NAME = "lux_uid";
|
489
516
|
|
490
|
-
|
491
|
-
|
517
|
+
var customDataValues = {};
|
518
|
+
var updatedCustomData = {};
|
492
519
|
function addCustomDataValue(name, value) {
|
493
|
-
|
494
|
-
|
520
|
+
var typeV = typeof value;
|
521
|
+
var valueIsEmpty = typeV === "undefined" || value === null;
|
495
522
|
if (!valueIsEmpty && customDataValues[name] !== value) {
|
496
523
|
// If the value is new or different to the previous value, record it so that later we can send
|
497
524
|
// only the values that have changed.
|
@@ -517,10 +544,10 @@
|
|
517
544
|
* Convert a set of custom data values to the string format expected by the backend.
|
518
545
|
*/
|
519
546
|
function valuesToString(values) {
|
520
|
-
|
521
|
-
for (
|
547
|
+
var strings = [];
|
548
|
+
for (var key in values) {
|
522
549
|
// Convert all values to strings
|
523
|
-
|
550
|
+
var value = "" + values[key];
|
524
551
|
// Strip out reserved characters (, and | are used as delimiters)
|
525
552
|
key = key.replace(/,/g, "").replace(/\|/g, "");
|
526
553
|
value = value.replace(/,/g, "").replace(/\|/g, "");
|
@@ -530,8 +557,9 @@
|
|
530
557
|
}
|
531
558
|
|
532
559
|
function getClosestScTrackAttribute(el) {
|
560
|
+
var _a;
|
533
561
|
if (el.hasAttribute("data-sctrack")) {
|
534
|
-
|
562
|
+
var trackId = (_a = el.getAttribute("data-sctrack")) === null || _a === void 0 ? void 0 : _a.trim();
|
535
563
|
if (trackId) {
|
536
564
|
return trackId;
|
537
565
|
}
|
@@ -548,20 +576,22 @@
|
|
548
576
|
}
|
549
577
|
return false;
|
550
578
|
}
|
551
|
-
|
552
|
-
function getNodeSelector(node, selector
|
579
|
+
var MAX_SELECTOR_LENGTH = 100;
|
580
|
+
function getNodeSelector(node, selector) {
|
581
|
+
if (selector === void 0) { selector = ""; }
|
553
582
|
return _getNodeSelector(node, selector).slice(0, MAX_SELECTOR_LENGTH);
|
554
583
|
}
|
555
|
-
function _getNodeSelector(node, selector
|
584
|
+
function _getNodeSelector(node, selector) {
|
585
|
+
if (selector === void 0) { selector = ""; }
|
556
586
|
try {
|
557
587
|
if (selector &&
|
558
588
|
(node.nodeType === 9 || selector.length > MAX_SELECTOR_LENGTH || !node.parentNode)) {
|
559
589
|
// Final selector.
|
560
590
|
return selector;
|
561
591
|
}
|
562
|
-
|
592
|
+
var el = node;
|
563
593
|
// Our first preference is to use the data-sctrack attribute from anywhere in the tree
|
564
|
-
|
594
|
+
var trackId = getClosestScTrackAttribute(el);
|
565
595
|
if (trackId) {
|
566
596
|
return trackId;
|
567
597
|
}
|
@@ -571,15 +601,15 @@
|
|
571
601
|
}
|
572
602
|
else if (el) {
|
573
603
|
// Otherwise attempt to get parent elements recursively
|
574
|
-
|
575
|
-
|
604
|
+
var name_1 = el.nodeType === 1 ? el.nodeName.toLowerCase() : el.nodeName.toUpperCase();
|
605
|
+
var classes = el.className ? "." + el.className.replace(/\s+/g, ".") : "";
|
576
606
|
// Remove classes until the selector is short enough
|
577
|
-
while ((
|
607
|
+
while ((name_1 + classes).length > MAX_SELECTOR_LENGTH) {
|
578
608
|
classes = classes.split(".").slice(0, -1).join(".");
|
579
609
|
}
|
580
|
-
|
610
|
+
var currentSelector = name_1 + classes + (selector ? ">" + selector : "");
|
581
611
|
if (el.parentNode) {
|
582
|
-
|
612
|
+
var selectorWithParent = getNodeSelector(el.parentNode, currentSelector);
|
583
613
|
if (selectorWithParent.length < MAX_SELECTOR_LENGTH) {
|
584
614
|
return selectorWithParent;
|
585
615
|
}
|
@@ -593,27 +623,7 @@
|
|
593
623
|
return selector;
|
594
624
|
}
|
595
625
|
|
596
|
-
|
597
|
-
const eventData = {};
|
598
|
-
function subscribe(event, callback) {
|
599
|
-
if (!subscribers[event]) {
|
600
|
-
subscribers[event] = [];
|
601
|
-
}
|
602
|
-
subscribers[event].push(callback);
|
603
|
-
// Ensure previous event data is available to new subscribers
|
604
|
-
if (eventData[event] !== undefined) {
|
605
|
-
callback(eventData[event]);
|
606
|
-
}
|
607
|
-
}
|
608
|
-
function emit(event, data) {
|
609
|
-
eventData[event] = data;
|
610
|
-
if (!subscribers[event]) {
|
611
|
-
return;
|
612
|
-
}
|
613
|
-
subscribers[event].forEach((callback) => callback(data));
|
614
|
-
}
|
615
|
-
|
616
|
-
const KNOWN_TRACKING_PARAMS = [
|
626
|
+
var KNOWN_TRACKING_PARAMS = [
|
617
627
|
"utm_source",
|
618
628
|
"utm_campaign",
|
619
629
|
"utm_medium",
|
@@ -624,11 +634,12 @@
|
|
624
634
|
* Add known tracking parameters to the custom data storage.
|
625
635
|
*/
|
626
636
|
function getTrackingParams() {
|
627
|
-
|
637
|
+
var trackingParams = {};
|
628
638
|
if (location.search && typeof URLSearchParams === "function") {
|
629
|
-
|
630
|
-
for (
|
631
|
-
|
639
|
+
var p = new URLSearchParams(location.search);
|
640
|
+
for (var _i = 0, KNOWN_TRACKING_PARAMS_1 = KNOWN_TRACKING_PARAMS; _i < KNOWN_TRACKING_PARAMS_1.length; _i++) {
|
641
|
+
var key = KNOWN_TRACKING_PARAMS_1[_i];
|
642
|
+
var value = p.get(key);
|
632
643
|
if (value) {
|
633
644
|
trackingParams[key] = value;
|
634
645
|
}
|
@@ -637,34 +648,38 @@
|
|
637
648
|
return trackingParams;
|
638
649
|
}
|
639
650
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
this.events.push([now(), event, args]);
|
651
|
+
var Logger = /** @class */ (function () {
|
652
|
+
function Logger() {
|
653
|
+
this.events = [];
|
644
654
|
}
|
645
|
-
|
655
|
+
Logger.prototype.logEvent = function (event, args) {
|
656
|
+
if (args === void 0) { args = []; }
|
657
|
+
this.events.push([now(), event, args]);
|
658
|
+
};
|
659
|
+
Logger.prototype.getEvents = function () {
|
646
660
|
return this.events;
|
647
|
-
}
|
648
|
-
|
661
|
+
};
|
662
|
+
return Logger;
|
663
|
+
}());
|
649
664
|
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
665
|
+
var sessionValue = 0;
|
666
|
+
var sessionEntries = [];
|
667
|
+
var sessionAttributions = [];
|
668
|
+
var largestEntry;
|
669
|
+
var maximumSessionValue = 0;
|
655
670
|
function processEntry$3(entry) {
|
656
671
|
if (!entry.hadRecentInput) {
|
657
|
-
|
658
|
-
|
659
|
-
|
672
|
+
var firstEntry = sessionEntries[0];
|
673
|
+
var latestEntry = sessionEntries[sessionEntries.length - 1];
|
674
|
+
var sources = entry.sources
|
660
675
|
? entry.sources
|
661
|
-
.filter((source)
|
662
|
-
.map((source)
|
676
|
+
.filter(function (source) { return source.node; })
|
677
|
+
.map(function (source) { return ({
|
663
678
|
value: entry.value,
|
664
679
|
startTime: processTimeMetric(entry.startTime),
|
665
680
|
elementSelector: getNodeSelector(source.node),
|
666
681
|
elementType: source.node.nodeName,
|
667
|
-
}))
|
682
|
+
}); })
|
668
683
|
: [];
|
669
684
|
if (sessionEntries.length &&
|
670
685
|
(entry.startTime - latestEntry.startTime >= 1000 ||
|
@@ -710,7 +725,40 @@
|
|
710
725
|
};
|
711
726
|
}
|
712
727
|
|
713
|
-
|
728
|
+
/******************************************************************************
|
729
|
+
Copyright (c) Microsoft Corporation.
|
730
|
+
|
731
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
732
|
+
purpose with or without fee is hereby granted.
|
733
|
+
|
734
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
735
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
736
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
737
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
738
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
739
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
740
|
+
PERFORMANCE OF THIS SOFTWARE.
|
741
|
+
***************************************************************************** */
|
742
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
743
|
+
|
744
|
+
|
745
|
+
var __assign = function() {
|
746
|
+
__assign = Object.assign || function __assign(t) {
|
747
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
748
|
+
s = arguments[i];
|
749
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
750
|
+
}
|
751
|
+
return t;
|
752
|
+
};
|
753
|
+
return __assign.apply(this, arguments);
|
754
|
+
};
|
755
|
+
|
756
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
757
|
+
var e = new Error(message);
|
758
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
759
|
+
};
|
760
|
+
|
761
|
+
var entries = [];
|
714
762
|
function processEntry$2(entry) {
|
715
763
|
entries.push(entry);
|
716
764
|
}
|
@@ -721,13 +769,13 @@
|
|
721
769
|
return entries;
|
722
770
|
}
|
723
771
|
function getData$2(config) {
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
entries.forEach((entry)
|
730
|
-
|
772
|
+
var summarizedEntries = [];
|
773
|
+
var totalDuration = 0;
|
774
|
+
var totalBlockingDuration = 0;
|
775
|
+
var totalStyleAndLayoutDuration = 0;
|
776
|
+
var totalWorkDuration = 0;
|
777
|
+
entries.forEach(function (entry) {
|
778
|
+
var startTime = entry.startTime, blockingDuration = entry.blockingDuration, duration = entry.duration, renderStart = entry.renderStart, styleAndLayoutStart = entry.styleAndLayoutStart;
|
731
779
|
totalDuration += duration;
|
732
780
|
totalBlockingDuration += blockingDuration;
|
733
781
|
totalStyleAndLayoutDuration += styleAndLayoutStart
|
@@ -748,18 +796,18 @@
|
|
748
796
|
totalEntries: entries.length,
|
749
797
|
totalStyleAndLayoutDuration: floor(totalStyleAndLayoutDuration),
|
750
798
|
totalWorkDuration: floor(totalWorkDuration),
|
751
|
-
scripts: summarizeLoAFScripts(entries.flatMap((entry)
|
799
|
+
scripts: summarizeLoAFScripts(entries.flatMap(function (entry) { return entry.scripts; }), config),
|
752
800
|
// Only keep the slowest LoAF entries
|
753
801
|
entries: summarizedEntries
|
754
|
-
.sort((a, b)
|
802
|
+
.sort(function (a, b) { return b.duration - a.duration; })
|
755
803
|
.slice(0, config.maxAttributionEntries)
|
756
|
-
.sort((a, b)
|
804
|
+
.sort(function (a, b) { return a.startTime - b.startTime; }),
|
757
805
|
};
|
758
806
|
}
|
759
807
|
function summarizeLoAFScripts(scripts, config) {
|
760
|
-
|
761
|
-
scripts.forEach((script)
|
762
|
-
|
808
|
+
var summary = {};
|
809
|
+
scripts.forEach(function (script) {
|
810
|
+
var key = script.sourceURL;
|
763
811
|
if (!summary[key]) {
|
764
812
|
summary[key] = {
|
765
813
|
sourceUrl: script.sourceURL,
|
@@ -782,13 +830,8 @@
|
|
782
830
|
summary[key].timings.push([floor(script.startTime), floor(script.duration)]);
|
783
831
|
});
|
784
832
|
return Object.values(summary)
|
785
|
-
.map((script)
|
786
|
-
|
787
|
-
totalDuration: floor(script.totalDuration),
|
788
|
-
totalPauseDuration: floor(script.totalPauseDuration),
|
789
|
-
totalForcedStyleAndLayoutDuration: floor(script.totalForcedStyleAndLayoutDuration),
|
790
|
-
}))
|
791
|
-
.sort((a, b) => b.totalDuration - a.totalDuration)
|
833
|
+
.map(function (script) { return (__assign(__assign({}, script), { totalDuration: floor(script.totalDuration), totalPauseDuration: floor(script.totalPauseDuration), totalForcedStyleAndLayoutDuration: floor(script.totalForcedStyleAndLayoutDuration) })); })
|
834
|
+
.sort(function (a, b) { return b.totalDuration - a.totalDuration; })
|
792
835
|
.slice(0, config.maxAttributionEntries);
|
793
836
|
}
|
794
837
|
|
@@ -797,7 +840,7 @@
|
|
797
840
|
* bare minimum required to measure just the INP value and does not store the actual event entries.
|
798
841
|
*/
|
799
842
|
// The maximum number of interactions to store
|
800
|
-
|
843
|
+
var MAX_INTERACTIONS = 10;
|
801
844
|
var INPPhase;
|
802
845
|
(function (INPPhase) {
|
803
846
|
INPPhase["InputDelay"] = "ID";
|
@@ -805,11 +848,11 @@
|
|
805
848
|
INPPhase["PresentationDelay"] = "PD";
|
806
849
|
})(INPPhase || (INPPhase = {}));
|
807
850
|
// A list of the slowest interactions
|
808
|
-
|
851
|
+
var slowestEntries = [];
|
809
852
|
// A map of the slowest interactions by ID
|
810
|
-
|
853
|
+
var slowestEntriesMap = {};
|
811
854
|
// The total number of interactions recorded on the page
|
812
|
-
|
855
|
+
var interactionCountEstimate = 0;
|
813
856
|
function reset$1() {
|
814
857
|
interactionCountEstimate = 0;
|
815
858
|
slowestEntries = [];
|
@@ -817,22 +860,22 @@
|
|
817
860
|
}
|
818
861
|
function processEntry$1(entry) {
|
819
862
|
if (entry.interactionId || (entry.entryType === "first-input" && !entryExists(entry))) {
|
820
|
-
|
863
|
+
var duration = entry.duration, startTime = entry.startTime, interactionId = entry.interactionId, name_1 = entry.name, processingStart = entry.processingStart, processingEnd = entry.processingEnd, target = entry.target;
|
821
864
|
if (duration < 0) {
|
822
865
|
return;
|
823
866
|
}
|
824
|
-
|
825
|
-
|
826
|
-
|
867
|
+
var processingTime = processingEnd - processingStart;
|
868
|
+
var existingEntry = slowestEntriesMap[interactionId];
|
869
|
+
var selector = target ? getNodeSelector(target) : null;
|
827
870
|
if (existingEntry) {
|
828
|
-
|
829
|
-
|
871
|
+
var longerDuration = duration > existingEntry.duration;
|
872
|
+
var sameWithLongerProcessingTime = duration === existingEntry.duration && processingTime > existingEntry.processingTime;
|
830
873
|
if (longerDuration || sameWithLongerProcessingTime) {
|
831
874
|
// Only replace an existing interation if the duration is longer, or if the duration is the
|
832
875
|
// same but the processing time is longer. The logic around this is that the interaction with
|
833
876
|
// longer processing time is likely to be the event that actually had a handler.
|
834
877
|
existingEntry.duration = duration;
|
835
|
-
existingEntry.name =
|
878
|
+
existingEntry.name = name_1;
|
836
879
|
existingEntry.processingEnd = processingEnd;
|
837
880
|
existingEntry.processingStart = processingStart;
|
838
881
|
existingEntry.processingTime = processingTime;
|
@@ -844,54 +887,57 @@
|
|
844
887
|
else {
|
845
888
|
interactionCountEstimate++;
|
846
889
|
slowestEntriesMap[interactionId] = {
|
847
|
-
duration,
|
848
|
-
interactionId,
|
849
|
-
name,
|
850
|
-
processingEnd,
|
851
|
-
processingStart,
|
852
|
-
processingTime,
|
853
|
-
selector,
|
854
|
-
startTime,
|
855
|
-
target,
|
890
|
+
duration: duration,
|
891
|
+
interactionId: interactionId,
|
892
|
+
name: name_1,
|
893
|
+
processingEnd: processingEnd,
|
894
|
+
processingStart: processingStart,
|
895
|
+
processingTime: processingTime,
|
896
|
+
selector: selector,
|
897
|
+
startTime: startTime,
|
898
|
+
target: target,
|
856
899
|
};
|
857
900
|
slowestEntries.push(slowestEntriesMap[interactionId]);
|
858
901
|
}
|
859
902
|
// Only store the longest <MAX_INTERACTIONS> interactions
|
860
|
-
slowestEntries.sort((a, b)
|
861
|
-
slowestEntries.splice(MAX_INTERACTIONS).forEach((entry)
|
903
|
+
slowestEntries.sort(function (a, b) { return b.duration - a.duration; });
|
904
|
+
slowestEntries.splice(MAX_INTERACTIONS).forEach(function (entry) {
|
862
905
|
delete slowestEntriesMap[entry.interactionId];
|
863
906
|
});
|
864
907
|
}
|
865
908
|
}
|
866
909
|
function entryExists(e1) {
|
867
|
-
return slowestEntries.some((e2)
|
910
|
+
return slowestEntries.some(function (e2) { return e1.startTime === e2.startTime && e1.duration === e2.duration; });
|
868
911
|
}
|
869
912
|
/**
|
870
913
|
* Returns an estimated high percentile INP value based on the total number of interactions on the
|
871
914
|
* current page.
|
872
915
|
*/
|
873
916
|
function getHighPercentileInteraction() {
|
874
|
-
|
917
|
+
var index = Math.min(slowestEntries.length - 1, Math.floor(getInteractionCount() / 50));
|
875
918
|
return slowestEntries[index];
|
876
919
|
}
|
877
920
|
function getData$1(config) {
|
878
|
-
|
921
|
+
var _a;
|
922
|
+
var interaction = getHighPercentileInteraction();
|
879
923
|
if (!interaction) {
|
880
924
|
return undefined;
|
881
925
|
}
|
882
|
-
|
883
|
-
|
884
|
-
.flatMap((entry)
|
926
|
+
var duration = interaction.duration, startTime = interaction.startTime, processingStart = interaction.processingStart;
|
927
|
+
var inpScripts = getEntries$1()
|
928
|
+
.flatMap(function (entry) { return entry.scripts; })
|
885
929
|
// Only include scripts that started during the interaction
|
886
|
-
.filter((script)
|
887
|
-
|
888
|
-
|
930
|
+
.filter(function (script) {
|
931
|
+
return script.startTime + script.duration >= startTime && script.startTime <= startTime + duration;
|
932
|
+
})
|
933
|
+
.map(function (_script) {
|
934
|
+
var script = JSON.parse(JSON.stringify(_script));
|
889
935
|
// Clamp the script duration to the time of the interaction
|
890
936
|
script.duration = script.startTime + script.duration - max(startTime, script.startTime);
|
891
937
|
script.inpPhase = getINPPhase(script, interaction);
|
892
938
|
return script;
|
893
939
|
});
|
894
|
-
|
940
|
+
var loafScripts = summarizeLoAFScripts(inpScripts, config);
|
895
941
|
return {
|
896
942
|
value: interaction.duration,
|
897
943
|
startTime: processTimeMetric(startTime),
|
@@ -906,14 +952,14 @@
|
|
906
952
|
attribution: {
|
907
953
|
eventType: interaction.name,
|
908
954
|
elementSelector: interaction.selector || null,
|
909
|
-
elementType: interaction.target
|
910
|
-
loafScripts,
|
955
|
+
elementType: ((_a = interaction.target) === null || _a === void 0 ? void 0 : _a.nodeName) || null,
|
956
|
+
loafScripts: loafScripts,
|
911
957
|
},
|
912
958
|
};
|
913
959
|
}
|
914
960
|
function getINPPhase(script, interaction) {
|
915
|
-
|
916
|
-
|
961
|
+
var processingStart = interaction.processingStart, processingTime = interaction.processingTime, startTime = interaction.startTime;
|
962
|
+
var inputDelay = processingStart - startTime;
|
917
963
|
if (script.startTime < startTime + inputDelay) {
|
918
964
|
return INPPhase.InputDelay;
|
919
965
|
}
|
@@ -929,7 +975,7 @@
|
|
929
975
|
return interactionCountEstimate;
|
930
976
|
}
|
931
977
|
|
932
|
-
|
978
|
+
var lcpEntry;
|
933
979
|
function processEntry(entry) {
|
934
980
|
if (!lcpEntry || entry.startTime > lcpEntry.startTime) {
|
935
981
|
lcpEntry = entry;
|
@@ -942,18 +988,18 @@
|
|
942
988
|
if (!lcpEntry) {
|
943
989
|
return undefined;
|
944
990
|
}
|
945
|
-
|
991
|
+
var subParts = null;
|
946
992
|
if (lcpEntry.url) {
|
947
|
-
|
993
|
+
var lcpResource = getEntriesByType("resource").find(function (resource) { return resource.name === lcpEntry.url; });
|
948
994
|
if (lcpResource) {
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
995
|
+
var navEntry = getNavigationEntry();
|
996
|
+
var responseStart = navEntry.responseStart || timing.responseStart;
|
997
|
+
var activationStart = navEntry.activationStart;
|
998
|
+
var ttfb = max(0, responseStart - activationStart);
|
999
|
+
var lcpStartTime = lcpResource.startTime;
|
1000
|
+
var lcpRequestStart = (lcpResource.requestStart || lcpStartTime) - activationStart;
|
1001
|
+
var lcpResponseEnd = max(lcpRequestStart, lcpResource.responseEnd - activationStart);
|
1002
|
+
var lcpRenderTime = max(lcpResponseEnd, lcpStartTime - activationStart);
|
957
1003
|
subParts = {
|
958
1004
|
resourceLoadDelay: clamp(floor(lcpRequestStart - ttfb)),
|
959
1005
|
resourceLoadTime: clamp(floor(lcpResponseEnd - lcpRequestStart)),
|
@@ -961,7 +1007,7 @@
|
|
961
1007
|
};
|
962
1008
|
}
|
963
1009
|
}
|
964
|
-
|
1010
|
+
var value = lcpEntry.startTime;
|
965
1011
|
if (!shouldReportValue(value)) {
|
966
1012
|
// It's possible the LCP entry we have occurred before the current page was initialised. In
|
967
1013
|
// this case, we don't want to report the LCP value.
|
@@ -969,7 +1015,7 @@
|
|
969
1015
|
}
|
970
1016
|
return {
|
971
1017
|
value: processTimeMetric(value),
|
972
|
-
subParts,
|
1018
|
+
subParts: subParts,
|
973
1019
|
attribution: lcpEntry.element
|
974
1020
|
? {
|
975
1021
|
elementSelector: getNodeSelector(lcpEntry.element),
|
@@ -979,20 +1025,20 @@
|
|
979
1025
|
};
|
980
1026
|
}
|
981
1027
|
|
982
|
-
|
1028
|
+
var ALL_ENTRIES = [];
|
983
1029
|
function observe(type, callback, options) {
|
984
1030
|
if (typeof PerformanceObserver === "function" &&
|
985
1031
|
PerformanceObserver.supportedEntryTypes.includes(type)) {
|
986
|
-
|
987
|
-
list.getEntries().forEach((entry)
|
1032
|
+
var po = new PerformanceObserver(function (list) {
|
1033
|
+
list.getEntries().forEach(function (entry) { return callback(entry); });
|
988
1034
|
});
|
989
|
-
po.observe(Object.assign({ type, buffered: true }, { options }));
|
1035
|
+
po.observe(Object.assign({ type: type, buffered: true }, { options: options }));
|
990
1036
|
return po;
|
991
1037
|
}
|
992
1038
|
return undefined;
|
993
1039
|
}
|
994
1040
|
function getEntries(type) {
|
995
|
-
return ALL_ENTRIES.filter((entry)
|
1041
|
+
return ALL_ENTRIES.filter(function (entry) { return entry.entryType === type; });
|
996
1042
|
}
|
997
1043
|
function addEntry(entry) {
|
998
1044
|
ALL_ENTRIES.push(entry);
|
@@ -1001,24 +1047,24 @@
|
|
1001
1047
|
/**
|
1002
1048
|
* A server timing metric that has its value set to the duration field
|
1003
1049
|
*/
|
1004
|
-
|
1050
|
+
var TYPE_DURATION = "r";
|
1005
1051
|
/**
|
1006
1052
|
* When a description metric has no value, we consider it to be a boolean and set it to this value.
|
1007
1053
|
*/
|
1008
|
-
|
1054
|
+
var BOOLEAN_TRUE_VALUE = "true";
|
1009
1055
|
function getKeyValuePairs(config, serverTiming) {
|
1010
|
-
|
1011
|
-
serverTiming.forEach((stEntry)
|
1012
|
-
|
1013
|
-
|
1056
|
+
var pairs = {};
|
1057
|
+
serverTiming.forEach(function (stEntry) {
|
1058
|
+
var name = stEntry.name;
|
1059
|
+
var description = stEntry.description;
|
1014
1060
|
if (name in config) {
|
1015
|
-
|
1016
|
-
|
1061
|
+
var spec = config[name];
|
1062
|
+
var multiplier = spec[1];
|
1017
1063
|
if (spec[0] === TYPE_DURATION) {
|
1018
1064
|
pairs[name] = stEntry.duration * (multiplier || 1);
|
1019
1065
|
}
|
1020
1066
|
else if (description && multiplier) {
|
1021
|
-
|
1067
|
+
var numericValue = parseFloat(description);
|
1022
1068
|
if (!isNaN(numericValue)) {
|
1023
1069
|
pairs[name] = numericValue * multiplier;
|
1024
1070
|
}
|
@@ -1032,12 +1078,12 @@
|
|
1032
1078
|
}
|
1033
1079
|
|
1034
1080
|
function getMatchesFromPatternMap(patternMap, hostname, pathname, firstOnly) {
|
1035
|
-
|
1036
|
-
for (
|
1037
|
-
|
1081
|
+
var matches = [];
|
1082
|
+
for (var key in patternMap) {
|
1083
|
+
var patterns = patternMap[key];
|
1038
1084
|
if (Array.isArray(patterns)) {
|
1039
|
-
for (
|
1040
|
-
|
1085
|
+
for (var i in patterns) {
|
1086
|
+
var pattern = patterns[i];
|
1041
1087
|
if (typeof pattern === "string" && patternMatchesUrl(pattern, hostname, pathname)) {
|
1042
1088
|
if (firstOnly) {
|
1043
1089
|
return key;
|
@@ -1053,7 +1099,7 @@
|
|
1053
1099
|
return matches;
|
1054
1100
|
}
|
1055
1101
|
function patternMatchesUrl(pattern, hostname, pathname) {
|
1056
|
-
|
1102
|
+
var regex = createRegExpFromPattern(pattern);
|
1057
1103
|
if (pattern.charAt(0) === "/") {
|
1058
1104
|
// Rule is a pathname only
|
1059
1105
|
return regex.test(pathname);
|
@@ -1069,8 +1115,8 @@
|
|
1069
1115
|
return str.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
|
1070
1116
|
}
|
1071
1117
|
|
1072
|
-
|
1073
|
-
|
1118
|
+
var LUX = window.LUX || {};
|
1119
|
+
var scriptEndTime = scriptStartTime;
|
1074
1120
|
LUX = (function () {
|
1075
1121
|
// -------------------------------------------------------------------------
|
1076
1122
|
// Settings
|
@@ -1084,19 +1130,19 @@
|
|
1084
1130
|
// -------------------------------------------------------------------------
|
1085
1131
|
/// End
|
1086
1132
|
// -------------------------------------------------------------------------
|
1087
|
-
|
1088
|
-
|
1133
|
+
var logger = new Logger();
|
1134
|
+
var globalConfig = fromObject(LUX);
|
1089
1135
|
logger.logEvent(1 /* LogEvent.EvaluationStart */, [VERSION, JSON.stringify(globalConfig)]);
|
1090
1136
|
// Variable aliases that allow the minifier to reduce file size.
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1137
|
+
var document = window.document;
|
1138
|
+
var addEventListener = window.addEventListener;
|
1139
|
+
var removeEventListener = window.removeEventListener;
|
1140
|
+
var setTimeout = window.setTimeout;
|
1141
|
+
var clearTimeout = window.clearTimeout;
|
1142
|
+
var encodeURIComponent = window.encodeURIComponent;
|
1143
|
+
var thisScript = document.currentScript || {};
|
1098
1144
|
// Log JS errors.
|
1099
|
-
|
1145
|
+
var nErrors = 0;
|
1100
1146
|
function errorHandler(e) {
|
1101
1147
|
if (!globalConfig.trackErrors) {
|
1102
1148
|
return;
|
@@ -1104,7 +1150,7 @@
|
|
1104
1150
|
nErrors++;
|
1105
1151
|
if (e && typeof e.filename !== "undefined" && typeof e.message !== "undefined") {
|
1106
1152
|
// Always send LUX errors
|
1107
|
-
|
1153
|
+
var isLuxError = e.filename.indexOf("/lux.js?") > -1 || e.message.indexOf("LUX") > -1;
|
1108
1154
|
if (isLuxError || (nErrors <= globalConfig.maxErrors && _sample())) {
|
1109
1155
|
// Sample & limit other errors.
|
1110
1156
|
// Send the error beacon.
|
@@ -1134,28 +1180,28 @@
|
|
1134
1180
|
}
|
1135
1181
|
addEventListener("error", errorHandler);
|
1136
1182
|
// Bitmask of flags for this session & page
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1183
|
+
var gFlags = 0;
|
1184
|
+
var gaMarks = [];
|
1185
|
+
var gaMeasures = [];
|
1186
|
+
var ghIx = {}; // hash for Interaction Metrics (scroll, click, keyboard)
|
1187
|
+
var gbLuxSent = 0; // have we sent the LUX data? (avoid sending twice in unload)
|
1188
|
+
var gbNavSent = 0; // have we sent the Nav Timing beacon yet? (avoid sending twice for SPA)
|
1189
|
+
var gbIxSent = 0; // have we sent the IX data? (avoid sending twice for SPA)
|
1190
|
+
var gbFirstPV = 1; // this is the first page view (vs. a SPA "soft nav")
|
1191
|
+
var gSessionTimeout = 30 * 60; // number of seconds after which we consider a session to have "timed out" (used for calculating bouncerate)
|
1192
|
+
var gSyncId = createSyncId(); // if we send multiple beacons, use this to sync them (eg, LUX & IX) (also called "luxid")
|
1193
|
+
var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
|
1194
|
+
var gCustomDataTimeout; // setTimeout timer for sending a Custom data beacon after onload
|
1195
|
+
var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
|
1150
1196
|
// Storing the customer ID in a local variable makes it possible to run multiple instances of lux.js
|
1151
1197
|
// on the same page.
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1198
|
+
var _thisCustomerId = LUX.customerid;
|
1199
|
+
var beaconCollectors = [];
|
1200
|
+
var logEntry = function (entry) {
|
1155
1201
|
logger.logEvent(42 /* LogEvent.PerformanceEntryReceived */, [entry]);
|
1156
1202
|
};
|
1157
1203
|
// Most PerformanceEntry types we log an event for and add it to the global entry store.
|
1158
|
-
|
1204
|
+
var processAndLogEntry = function (entry) {
|
1159
1205
|
addEntry(entry);
|
1160
1206
|
logEntry(entry);
|
1161
1207
|
};
|
@@ -1163,7 +1209,7 @@
|
|
1163
1209
|
observe("longtask", processAndLogEntry);
|
1164
1210
|
observe("element", processAndLogEntry);
|
1165
1211
|
observe("paint", processAndLogEntry);
|
1166
|
-
if (observe("largest-contentful-paint", (entry)
|
1212
|
+
if (observe("largest-contentful-paint", function (entry) {
|
1167
1213
|
// Process the LCP entry for the legacy beacon
|
1168
1214
|
processAndLogEntry(entry);
|
1169
1215
|
// Process the LCP entry for the new beacon
|
@@ -1171,36 +1217,36 @@
|
|
1171
1217
|
})) {
|
1172
1218
|
beaconCollectors.push([BeaconMetricKey.LCP, getData]);
|
1173
1219
|
}
|
1174
|
-
if (observe("layout-shift", (entry)
|
1220
|
+
if (observe("layout-shift", function (entry) {
|
1175
1221
|
processEntry$3(entry);
|
1176
1222
|
logEntry(entry);
|
1177
1223
|
})) {
|
1178
1224
|
beaconCollectors.push([BeaconMetricKey.CLS, getData$3]);
|
1179
1225
|
}
|
1180
|
-
if (observe("long-animation-frame", (entry)
|
1226
|
+
if (observe("long-animation-frame", function (entry) {
|
1181
1227
|
processEntry$2(entry);
|
1182
1228
|
logEntry(entry);
|
1183
1229
|
})) {
|
1184
1230
|
beaconCollectors.push([BeaconMetricKey.LoAF, getData$2]);
|
1185
1231
|
}
|
1186
|
-
|
1232
|
+
var handleINPEntry_1 = function (entry) {
|
1187
1233
|
processEntry$1(entry);
|
1188
1234
|
logEntry(entry);
|
1189
1235
|
};
|
1190
|
-
observe("first-input", (entry)
|
1236
|
+
observe("first-input", function (entry) {
|
1191
1237
|
logEntry(entry);
|
1192
|
-
|
1238
|
+
var entryTime = entry.processingStart - entry.startTime;
|
1193
1239
|
if (!gFirstInputDelay || gFirstInputDelay < entryTime) {
|
1194
1240
|
gFirstInputDelay = floor(entryTime);
|
1195
1241
|
}
|
1196
1242
|
// Allow first-input events to be considered for INP
|
1197
|
-
|
1243
|
+
handleINPEntry_1(entry);
|
1198
1244
|
});
|
1199
1245
|
// TODO: Set durationThreshold to 40 once performance.interactionCount is widely supported.
|
1200
1246
|
// Right now we have to count every event to get the total interaction count so that we can
|
1201
1247
|
// estimate a high percentile value for INP.
|
1202
|
-
if (observe("event", (entry)
|
1203
|
-
|
1248
|
+
if (observe("event", function (entry) {
|
1249
|
+
handleINPEntry_1(entry);
|
1204
1250
|
// It's useful to log the interactionId, but it is not serialised by default. Annoyingly, we
|
1205
1251
|
// need to manually serialize our own object with the keys we want.
|
1206
1252
|
logEntry({
|
@@ -1219,27 +1265,28 @@
|
|
1219
1265
|
catch (e) {
|
1220
1266
|
logger.logEvent(51 /* LogEvent.PerformanceObserverError */, [e]);
|
1221
1267
|
}
|
1222
|
-
|
1223
|
-
|
1268
|
+
var initPostBeacon = function () {
|
1269
|
+
var b = new Beacon({
|
1224
1270
|
config: globalConfig,
|
1225
|
-
logger,
|
1271
|
+
logger: logger,
|
1226
1272
|
customerId: getCustomerId(),
|
1227
1273
|
sessionId: gUid,
|
1228
1274
|
pageId: gSyncId,
|
1229
1275
|
});
|
1230
|
-
beaconCollectors.forEach((
|
1276
|
+
beaconCollectors.forEach(function (_a) {
|
1277
|
+
var metric = _a[0], collector = _a[1];
|
1231
1278
|
b.addCollector(metric, collector);
|
1232
1279
|
});
|
1233
1280
|
return b;
|
1234
1281
|
};
|
1235
|
-
|
1282
|
+
var beacon = initPostBeacon();
|
1236
1283
|
if (_sample()) {
|
1237
1284
|
logger.logEvent(21 /* LogEvent.SessionIsSampled */, [globalConfig.samplerate]);
|
1238
1285
|
}
|
1239
1286
|
else {
|
1240
1287
|
logger.logEvent(22 /* LogEvent.SessionIsNotSampled */, [globalConfig.samplerate]);
|
1241
1288
|
}
|
1242
|
-
|
1289
|
+
var gLuxSnippetStart = LUX.ns ? LUX.ns - timing.navigationStart : 0;
|
1243
1290
|
if (!performance.timing) {
|
1244
1291
|
logger.logEvent(71 /* LogEvent.NavTimingNotSupported */);
|
1245
1292
|
gFlags = addFlag(gFlags, 2 /* Flags.NavTimingNotSupported */);
|
@@ -1250,9 +1297,9 @@
|
|
1250
1297
|
// FIRST INPUT DELAY (FID)
|
1251
1298
|
// The basic idea behind FID is to attach various input event listeners and measure the time
|
1252
1299
|
// between when the event happens and when the handler executes. That is FID.
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1300
|
+
var gFirstInputDelay;
|
1301
|
+
var gaEventTypes = ["click", "mousedown", "keydown", "touchstart", "pointerdown"]; // NOTE: does NOT include scroll!
|
1302
|
+
var ghListenerOptions = { passive: true, capture: true };
|
1256
1303
|
// Record the FIRST input delay.
|
1257
1304
|
function recordDelay(delay) {
|
1258
1305
|
if (!gFirstInputDelay) {
|
@@ -1284,7 +1331,7 @@
|
|
1284
1331
|
// Record FID as the delta between when the event happened and when the
|
1285
1332
|
// listener was able to execute.
|
1286
1333
|
function onInput(evt) {
|
1287
|
-
|
1334
|
+
var bCancelable = false;
|
1288
1335
|
try {
|
1289
1336
|
// Seeing "Permission denied" errors, so do a simple try-catch.
|
1290
1337
|
bCancelable = evt.cancelable;
|
@@ -1295,19 +1342,19 @@
|
|
1295
1342
|
return;
|
1296
1343
|
}
|
1297
1344
|
if (bCancelable) {
|
1298
|
-
|
1299
|
-
|
1345
|
+
var now_1 = msSinceNavigationStart();
|
1346
|
+
var eventTimeStamp = evt.timeStamp;
|
1300
1347
|
if (eventTimeStamp > 1520000000) {
|
1301
1348
|
// If the event timeStamp is an epoch time instead of a time relative to NavigationStart,
|
1302
1349
|
// then compare it to Date.now() instead of performance.now().
|
1303
|
-
|
1350
|
+
now_1 = Number(new Date());
|
1304
1351
|
}
|
1305
|
-
if (eventTimeStamp >
|
1352
|
+
if (eventTimeStamp > now_1) {
|
1306
1353
|
// If there is a race condition and eventTimeStamp happened after
|
1307
1354
|
// this code was executed, something is wrong. Bail.
|
1308
1355
|
return;
|
1309
1356
|
}
|
1310
|
-
|
1357
|
+
var delay = now_1 - eventTimeStamp;
|
1311
1358
|
if (evt.type === "pointerdown") {
|
1312
1359
|
// special case
|
1313
1360
|
onPointerDown(delay);
|
@@ -1324,23 +1371,28 @@
|
|
1324
1371
|
////////////////////// FID END
|
1325
1372
|
// This is a wrapper around performance.mark that falls back to a polyfill when the User Timing
|
1326
1373
|
// API isn't supported.
|
1327
|
-
function _mark(
|
1374
|
+
function _mark() {
|
1375
|
+
var _a, _b;
|
1376
|
+
var args = [];
|
1377
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
1378
|
+
args[_i] = arguments[_i];
|
1379
|
+
}
|
1328
1380
|
logger.logEvent(4 /* LogEvent.MarkCalled */, args);
|
1329
1381
|
if (performance.mark) {
|
1330
1382
|
// Use the native performance.mark where possible...
|
1331
|
-
return performance.mark(
|
1383
|
+
return performance.mark.apply(performance, args);
|
1332
1384
|
}
|
1333
1385
|
// ...Otherwise provide a polyfill
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1386
|
+
{
|
1387
|
+
var name_1 = args[0];
|
1388
|
+
var detail = ((_a = args[1]) === null || _a === void 0 ? void 0 : _a.detail) || null;
|
1389
|
+
var startTime = ((_b = args[1]) === null || _b === void 0 ? void 0 : _b.startTime) || msSincePageInit();
|
1390
|
+
var entry = {
|
1339
1391
|
entryType: "mark",
|
1340
1392
|
duration: 0,
|
1341
|
-
name,
|
1342
|
-
detail,
|
1343
|
-
startTime,
|
1393
|
+
name: name_1,
|
1394
|
+
detail: detail,
|
1395
|
+
startTime: startTime,
|
1344
1396
|
};
|
1345
1397
|
gaMarks.push(entry);
|
1346
1398
|
gFlags = addFlag(gFlags, 4 /* Flags.UserTimingNotSupported */);
|
@@ -1350,12 +1402,16 @@
|
|
1350
1402
|
}
|
1351
1403
|
// This is a wrapper around performance.measure that falls back to a polyfill when the User Timing
|
1352
1404
|
// API isn't supported.
|
1353
|
-
function _measure(
|
1405
|
+
function _measure() {
|
1406
|
+
var args = [];
|
1407
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
1408
|
+
args[_i] = arguments[_i];
|
1409
|
+
}
|
1354
1410
|
logger.logEvent(5 /* LogEvent.MeasureCalled */, args);
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1411
|
+
var name = args[0];
|
1412
|
+
var startMarkName = args[1];
|
1413
|
+
var endMarkName = args[2];
|
1414
|
+
var options;
|
1359
1415
|
if (typeof startMarkName === "object") {
|
1360
1416
|
options = args[1];
|
1361
1417
|
startMarkName = options.start;
|
@@ -1388,20 +1444,20 @@
|
|
1388
1444
|
}
|
1389
1445
|
if (performance.measure) {
|
1390
1446
|
// Use the native performance.measure where possible...
|
1391
|
-
return performance.measure(
|
1447
|
+
return performance.measure.apply(performance, args);
|
1392
1448
|
}
|
1393
1449
|
// ...Otherwise provide a polyfill
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1450
|
+
{
|
1451
|
+
var navEntry = getNavigationEntry();
|
1452
|
+
var startTime = typeof startMarkName === "number" ? startMarkName : 0;
|
1453
|
+
var endTime = typeof endMarkName === "number" ? endMarkName : msSincePageInit();
|
1454
|
+
var throwError = function (missingMark) {
|
1399
1455
|
throw new DOMException("Failed to execute 'measure' on 'Performance': The mark '" +
|
1400
1456
|
missingMark +
|
1401
1457
|
"' does not exist");
|
1402
1458
|
};
|
1403
1459
|
if (typeof startMarkName === "string") {
|
1404
|
-
|
1460
|
+
var startMark = _getMark(startMarkName);
|
1405
1461
|
if (startMark) {
|
1406
1462
|
startTime = startMark.startTime;
|
1407
1463
|
}
|
@@ -1414,7 +1470,7 @@
|
|
1414
1470
|
}
|
1415
1471
|
}
|
1416
1472
|
if (typeof endMarkName === "string") {
|
1417
|
-
|
1473
|
+
var endMark = _getMark(endMarkName);
|
1418
1474
|
if (endMark) {
|
1419
1475
|
endTime = endMark.startTime;
|
1420
1476
|
}
|
@@ -1426,20 +1482,20 @@
|
|
1426
1482
|
throwError(endMarkName);
|
1427
1483
|
}
|
1428
1484
|
}
|
1429
|
-
|
1430
|
-
|
1485
|
+
var duration = endTime - startTime;
|
1486
|
+
var detail = null;
|
1431
1487
|
if (options) {
|
1432
1488
|
if (options.duration) {
|
1433
1489
|
duration = options.duration;
|
1434
1490
|
}
|
1435
1491
|
detail = options.detail;
|
1436
1492
|
}
|
1437
|
-
|
1493
|
+
var entry = {
|
1438
1494
|
entryType: "measure",
|
1439
|
-
name,
|
1440
|
-
detail,
|
1441
|
-
startTime,
|
1442
|
-
duration,
|
1495
|
+
name: name,
|
1496
|
+
detail: detail,
|
1497
|
+
startTime: startTime,
|
1498
|
+
duration: duration,
|
1443
1499
|
};
|
1444
1500
|
gaMeasures.push(entry);
|
1445
1501
|
gFlags = addFlag(gFlags, 4 /* Flags.UserTimingNotSupported */);
|
@@ -1453,8 +1509,8 @@
|
|
1453
1509
|
}
|
1454
1510
|
function _getM(name, aItems) {
|
1455
1511
|
if (aItems) {
|
1456
|
-
for (
|
1457
|
-
|
1512
|
+
for (var i = aItems.length - 1; i >= 0; i--) {
|
1513
|
+
var m = aItems[i];
|
1458
1514
|
if (name === m.name) {
|
1459
1515
|
return m;
|
1460
1516
|
}
|
@@ -1464,7 +1520,7 @@
|
|
1464
1520
|
}
|
1465
1521
|
// Return an array of marks.
|
1466
1522
|
function _getMarks() {
|
1467
|
-
|
1523
|
+
var marks = getEntriesByType("mark");
|
1468
1524
|
if (marks.length) {
|
1469
1525
|
return marks;
|
1470
1526
|
}
|
@@ -1472,7 +1528,7 @@
|
|
1472
1528
|
}
|
1473
1529
|
// Return an array of measures.
|
1474
1530
|
function _getMeasures() {
|
1475
|
-
|
1531
|
+
var measures = getEntriesByType("measure");
|
1476
1532
|
if (measures.length) {
|
1477
1533
|
return measures;
|
1478
1534
|
}
|
@@ -1483,49 +1539,49 @@
|
|
1483
1539
|
// The User Timing spec allows for there to be multiple marks with the same name,
|
1484
1540
|
// and multiple measures with the same name. But we can only send back one value
|
1485
1541
|
// for a name, so we always take the maximum value.
|
1486
|
-
|
1487
|
-
|
1542
|
+
var hUT = {};
|
1543
|
+
var startMark = _getMark(START_MARK);
|
1488
1544
|
// For user timing values taken in a SPA page load, we need to adjust them
|
1489
1545
|
// so that they're zeroed against the last LUX.init() call.
|
1490
|
-
|
1546
|
+
var tZero = getZeroTime();
|
1491
1547
|
// marks
|
1492
|
-
_getMarks().forEach((mark)
|
1493
|
-
|
1548
|
+
_getMarks().forEach(function (mark) {
|
1549
|
+
var name = mark.name;
|
1494
1550
|
if (name === START_MARK || name === END_MARK) {
|
1495
1551
|
// Don't include the internal marks in the beacon
|
1496
1552
|
return;
|
1497
1553
|
}
|
1498
|
-
|
1554
|
+
var startTime = floor(mark.startTime - tZero);
|
1499
1555
|
if (startTime < 0) {
|
1500
1556
|
// Exclude marks that were taken before the current SPA page view
|
1501
1557
|
return;
|
1502
1558
|
}
|
1503
1559
|
if (typeof hUT[name] === "undefined") {
|
1504
|
-
hUT[name] = { startTime };
|
1560
|
+
hUT[name] = { startTime: startTime };
|
1505
1561
|
}
|
1506
1562
|
else {
|
1507
1563
|
hUT[name].startTime = max(startTime, hUT[name].startTime);
|
1508
1564
|
}
|
1509
1565
|
});
|
1510
1566
|
// measures
|
1511
|
-
_getMeasures().forEach((measure)
|
1567
|
+
_getMeasures().forEach(function (measure) {
|
1512
1568
|
if (startMark && measure.startTime < startMark.startTime) {
|
1513
1569
|
// Exclude measures that were taken before the current SPA page view
|
1514
1570
|
return;
|
1515
1571
|
}
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1572
|
+
var name = measure.name;
|
1573
|
+
var startTime = floor(measure.startTime - tZero);
|
1574
|
+
var duration = floor(measure.duration);
|
1519
1575
|
if (typeof hUT[name] === "undefined" || startTime > hUT[name].startTime) {
|
1520
|
-
hUT[name] = { startTime, duration };
|
1576
|
+
hUT[name] = { startTime: startTime, duration: duration };
|
1521
1577
|
}
|
1522
1578
|
});
|
1523
1579
|
// Convert the user timing values into a delimited string. This string takes the format
|
1524
1580
|
// markName|startTime,measureName|startTime|duration,[markName...]
|
1525
|
-
|
1526
|
-
for (
|
1527
|
-
|
1528
|
-
|
1581
|
+
var aUT = [];
|
1582
|
+
for (var utName in hUT) {
|
1583
|
+
var _a = hUT[utName], startTime = _a.startTime, duration = _a.duration;
|
1584
|
+
var utParts = [utName, startTime];
|
1529
1585
|
if (typeof duration !== "undefined") {
|
1530
1586
|
utParts.push(duration);
|
1531
1587
|
}
|
@@ -1535,10 +1591,10 @@
|
|
1535
1591
|
}
|
1536
1592
|
// Return a string of Element Timing Metrics formatted for beacon querystring.
|
1537
1593
|
function elementTimingValues() {
|
1538
|
-
|
1539
|
-
getEntries("element").forEach((entry)
|
1594
|
+
var aET = [];
|
1595
|
+
getEntries("element").forEach(function (entry) {
|
1540
1596
|
if (entry.identifier && entry.startTime) {
|
1541
|
-
|
1597
|
+
var value = processTimeMetric(entry.startTime);
|
1542
1598
|
if (shouldReportValue(value)) {
|
1543
1599
|
logger.logEvent(43 /* LogEvent.PerformanceEntryProcessed */, [entry]);
|
1544
1600
|
aET.push(entry.identifier + "|" + value);
|
@@ -1553,24 +1609,24 @@
|
|
1553
1609
|
// Do not return any CPU metrics if Long Tasks API is not supported.
|
1554
1610
|
return "";
|
1555
1611
|
}
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1612
|
+
var sCPU = "";
|
1613
|
+
var hCPU = {};
|
1614
|
+
var hCPUDetails = {}; // TODO - Could remove this later after large totals go away.
|
1615
|
+
var longTaskEntries = getEntries("longtask");
|
1560
1616
|
// Add up totals for each "type" of long task
|
1561
1617
|
if (longTaskEntries.length) {
|
1562
|
-
|
1563
|
-
longTaskEntries.forEach((entry)
|
1564
|
-
|
1565
|
-
if (entry.startTime <
|
1618
|
+
var tZero_1 = getZeroTime();
|
1619
|
+
longTaskEntries.forEach(function (entry) {
|
1620
|
+
var dur = floor(entry.duration);
|
1621
|
+
if (entry.startTime < tZero_1) {
|
1566
1622
|
// In a SPA it is possible that we were in the middle of a Long Task when
|
1567
1623
|
// LUX.init() was called. If so, only include the duration after tZero.
|
1568
|
-
dur -=
|
1624
|
+
dur -= tZero_1 - entry.startTime;
|
1569
1625
|
}
|
1570
1626
|
// Only process entries that we calculated to have a valid duration
|
1571
1627
|
if (dur > 0) {
|
1572
1628
|
logger.logEvent(43 /* LogEvent.PerformanceEntryProcessed */, [entry]);
|
1573
|
-
|
1629
|
+
var type = entry.attribution[0].name;
|
1574
1630
|
if (!hCPU[type]) {
|
1575
1631
|
hCPU[type] = 0;
|
1576
1632
|
hCPUDetails[type] = "";
|
@@ -1582,14 +1638,14 @@
|
|
1582
1638
|
});
|
1583
1639
|
}
|
1584
1640
|
// TODO - Add more types if/when they become available.
|
1585
|
-
|
1641
|
+
var jsType = typeof hCPU["script"] !== "undefined" ? "script" : "unknown"; // spec changed from "script" to "unknown" Nov 2018
|
1586
1642
|
if (typeof hCPU[jsType] === "undefined") {
|
1587
1643
|
// Initialize default values for pages that have *no Long Tasks*.
|
1588
1644
|
hCPU[jsType] = 0;
|
1589
1645
|
hCPUDetails[jsType] = "";
|
1590
1646
|
}
|
1591
|
-
|
1592
|
-
|
1647
|
+
var hStats = cpuStats(hCPUDetails[jsType]);
|
1648
|
+
var sStats = ",n|" +
|
1593
1649
|
hStats.count +
|
1594
1650
|
",d|" +
|
1595
1651
|
hStats.median +
|
@@ -1602,19 +1658,19 @@
|
|
1602
1658
|
// Return a hash of "stats" about the CPU details incl. count, max, and median.
|
1603
1659
|
function cpuStats(sDetails) {
|
1604
1660
|
// tuples of starttime|duration, eg: ,456|250,789|250,1012|250
|
1605
|
-
|
1661
|
+
var max = 0;
|
1606
1662
|
// FCI is beginning of 5 second window of no Long Tasks _after_ first contentful paint
|
1607
|
-
|
1608
|
-
|
1663
|
+
var fcp = getFcp();
|
1664
|
+
var fci = fcp || 0;
|
1609
1665
|
// If FCP is not supported, we can't calculate a valid FCI.
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
for (
|
1614
|
-
|
1666
|
+
var bFoundFci = typeof fcp === "undefined";
|
1667
|
+
var aValues = [];
|
1668
|
+
var aTuples = sDetails.split(",");
|
1669
|
+
for (var i = 0; i < aTuples.length; i++) {
|
1670
|
+
var aTuple = aTuples[i].split("|");
|
1615
1671
|
if (aTuple.length === 2) {
|
1616
|
-
|
1617
|
-
|
1672
|
+
var start = parseInt(aTuple[0]);
|
1673
|
+
var dur = parseInt(aTuple[1]);
|
1618
1674
|
aValues.push(dur);
|
1619
1675
|
max = dur > max ? dur : max;
|
1620
1676
|
// FCI
|
@@ -1627,7 +1683,7 @@
|
|
1627
1683
|
}
|
1628
1684
|
else {
|
1629
1685
|
// Less than 5 seconds of inactivity
|
1630
|
-
|
1686
|
+
var val = processTimeMetric(start + dur);
|
1631
1687
|
if (shouldReportValue(val)) {
|
1632
1688
|
fci = val; // FCI is now the end of this Long Task
|
1633
1689
|
}
|
@@ -1635,16 +1691,16 @@
|
|
1635
1691
|
}
|
1636
1692
|
}
|
1637
1693
|
}
|
1638
|
-
|
1639
|
-
|
1640
|
-
return { count, median, max, fci };
|
1694
|
+
var count = aValues.length;
|
1695
|
+
var median = arrayMedian(aValues);
|
1696
|
+
return { count: count, median: median, max: max, fci: fci };
|
1641
1697
|
}
|
1642
1698
|
// Return the median value from an array of integers.
|
1643
1699
|
function arrayMedian(aValues) {
|
1644
1700
|
if (0 === aValues.length) {
|
1645
1701
|
return 0;
|
1646
1702
|
}
|
1647
|
-
|
1703
|
+
var half = floor(aValues.length / 2);
|
1648
1704
|
aValues.sort(sortNumeric);
|
1649
1705
|
if (aValues.length % 2) {
|
1650
1706
|
// Return the middle value.
|
@@ -1657,21 +1713,21 @@
|
|
1657
1713
|
}
|
1658
1714
|
// Track how long it took lux.js to load via Resource Timing.
|
1659
1715
|
function selfLoading() {
|
1660
|
-
|
1661
|
-
if (gbFirstPV && performance.getEntriesByName) {
|
1716
|
+
var sLuxjs = "";
|
1717
|
+
if (gbFirstPV && performance.getEntriesByName && thisScript.src) {
|
1662
1718
|
// Get the lux script URL (including querystring params).
|
1663
|
-
|
1719
|
+
var aResources = performance.getEntriesByName(thisScript.src);
|
1664
1720
|
if (aResources && aResources.length) {
|
1665
|
-
|
1721
|
+
var r = aResources[0];
|
1666
1722
|
// DO NOT USE DURATION!!!!!
|
1667
1723
|
// See https://www.stevesouders.com/blog/2014/11/25/serious-confusion-with-resource-timing/
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1724
|
+
var dns = floor(r.domainLookupEnd - r.domainLookupStart);
|
1725
|
+
var tcp = floor(r.connectEnd - r.connectStart);
|
1726
|
+
var fb = floor(r.responseStart - r.requestStart);
|
1727
|
+
var content = floor(r.responseEnd - r.responseStart);
|
1728
|
+
var networkDuration = dns + tcp + fb + content;
|
1729
|
+
var parseEval = scriptEndTime - scriptStartTime;
|
1730
|
+
var transferSize = r.encodedBodySize ? r.encodedBodySize : 0;
|
1675
1731
|
// Instead of a delimiter use a 1-letter abbreviation as a separator.
|
1676
1732
|
sLuxjs =
|
1677
1733
|
"d" +
|
@@ -1705,8 +1761,8 @@
|
|
1705
1761
|
}
|
1706
1762
|
// Return a string of Interaction Metrics formatted for beacon querystring.
|
1707
1763
|
function ixValues() {
|
1708
|
-
|
1709
|
-
for (
|
1764
|
+
var aIx = [];
|
1765
|
+
for (var key in ghIx) {
|
1710
1766
|
aIx.push(key + "|" + encodeURIComponent(ghIx[key]));
|
1711
1767
|
}
|
1712
1768
|
return aIx.join(",");
|
@@ -1736,35 +1792,23 @@
|
|
1736
1792
|
if (typeof gUid === "undefined" || typeof globalConfig.samplerate === "undefined") {
|
1737
1793
|
return false; // bail
|
1738
1794
|
}
|
1739
|
-
|
1795
|
+
var nThis = ("" + gUid).substr(-2); // number for THIS page - from 00 to 99
|
1740
1796
|
return parseInt(nThis) < globalConfig.samplerate;
|
1741
1797
|
}
|
1742
1798
|
/**
|
1743
1799
|
* Re-initialize lux.js to start a new "page". This is typically called within a SPA at the
|
1744
1800
|
* beginning of a page transition, but is also called internally when the BF cache is restored.
|
1745
1801
|
*/
|
1746
|
-
function _init(startTime, clearFlags
|
1747
|
-
|
1748
|
-
// cause some first-page-only data (like paint metrics) to be lost. To prevent this, we silently
|
1749
|
-
// bail from this function when we detect an unnecessary LUX.init call.
|
1750
|
-
const endMark = _getMark(END_MARK);
|
1751
|
-
if (!endMark) {
|
1752
|
-
return;
|
1753
|
-
}
|
1802
|
+
function _init(startTime, clearFlags) {
|
1803
|
+
if (clearFlags === void 0) { clearFlags = true; }
|
1754
1804
|
// Mark the "navigationStart" for this SPA page. A start time can be passed through, for example
|
1755
1805
|
// to set a page's start time as an event timestamp.
|
1756
1806
|
if (startTime) {
|
1757
|
-
_mark(START_MARK, { startTime });
|
1807
|
+
_mark(START_MARK, { startTime: startTime });
|
1758
1808
|
}
|
1759
1809
|
else {
|
1760
1810
|
_mark(START_MARK);
|
1761
1811
|
}
|
1762
|
-
logger.logEvent(3 /* LogEvent.InitCalled */);
|
1763
|
-
// This is an edge case where LUX.auto = true but LUX.init() has been called. In this case, the
|
1764
|
-
// POST beacon will not be sent automatically, so we need to send it here.
|
1765
|
-
if (globalConfig.auto && !beacon.isSent) {
|
1766
|
-
beacon.send();
|
1767
|
-
}
|
1768
1812
|
// Clear all interactions from the previous "page".
|
1769
1813
|
_clearIx();
|
1770
1814
|
// Since we actively disable IX handlers, we re-add them each time.
|
@@ -1795,7 +1839,7 @@
|
|
1795
1839
|
}
|
1796
1840
|
// Return the number of blocking (synchronous) external scripts in the page.
|
1797
1841
|
function blockingScripts() {
|
1798
|
-
|
1842
|
+
var lastViewportElem = lastViewportElement();
|
1799
1843
|
if (!lastViewportElem) {
|
1800
1844
|
// If we can not find the last DOM element in the viewport,
|
1801
1845
|
// use the old technique of just counting sync scripts.
|
@@ -1803,10 +1847,10 @@
|
|
1803
1847
|
}
|
1804
1848
|
// Find all the synchronous scripts that are ABOVE the last DOM element in the
|
1805
1849
|
// viewport. (If they are BELOW then they do not block rendering of initial viewport.)
|
1806
|
-
|
1807
|
-
|
1808
|
-
for (
|
1809
|
-
|
1850
|
+
var aElems = document.getElementsByTagName("script");
|
1851
|
+
var num = 0;
|
1852
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1853
|
+
var e = aElems[i];
|
1810
1854
|
if (e.src &&
|
1811
1855
|
!e.async &&
|
1812
1856
|
!e.defer &&
|
@@ -1820,10 +1864,10 @@
|
|
1820
1864
|
}
|
1821
1865
|
// Return the number of blocking (synchronous) external scripts in the page.
|
1822
1866
|
function blockingStylesheets() {
|
1823
|
-
|
1824
|
-
|
1825
|
-
for (
|
1826
|
-
|
1867
|
+
var nBlocking = 0;
|
1868
|
+
var aElems = document.getElementsByTagName("link");
|
1869
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1870
|
+
var e = aElems[i];
|
1827
1871
|
if (e.href && "stylesheet" === e.rel && 0 !== e.href.indexOf("data:")) {
|
1828
1872
|
if (
|
1829
1873
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
@@ -1840,10 +1884,10 @@
|
|
1840
1884
|
}
|
1841
1885
|
// Return the number of synchronous external scripts in the page.
|
1842
1886
|
function syncScripts() {
|
1843
|
-
|
1844
|
-
|
1845
|
-
for (
|
1846
|
-
|
1887
|
+
var aElems = document.getElementsByTagName("script");
|
1888
|
+
var num = 0;
|
1889
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1890
|
+
var e = aElems[i];
|
1847
1891
|
if (e.src && !e.async && !e.defer) {
|
1848
1892
|
// If the script has a SRC and async is false, then increment the counter.
|
1849
1893
|
num++;
|
@@ -1853,10 +1897,10 @@
|
|
1853
1897
|
}
|
1854
1898
|
// Return the number of external scripts in the page.
|
1855
1899
|
function numScripts() {
|
1856
|
-
|
1857
|
-
|
1858
|
-
for (
|
1859
|
-
|
1900
|
+
var aElems = document.getElementsByTagName("script");
|
1901
|
+
var num = 0;
|
1902
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1903
|
+
var e = aElems[i];
|
1860
1904
|
if (e.src) {
|
1861
1905
|
num++;
|
1862
1906
|
}
|
@@ -1865,10 +1909,10 @@
|
|
1865
1909
|
}
|
1866
1910
|
// Return the number of stylesheets in the page.
|
1867
1911
|
function numStylesheets() {
|
1868
|
-
|
1869
|
-
|
1870
|
-
for (
|
1871
|
-
|
1912
|
+
var aElems = document.getElementsByTagName("link");
|
1913
|
+
var num = 0;
|
1914
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1915
|
+
var e = aElems[i];
|
1872
1916
|
if (e.href && "stylesheet" == e.rel) {
|
1873
1917
|
num++;
|
1874
1918
|
}
|
@@ -1876,10 +1920,10 @@
|
|
1876
1920
|
return num;
|
1877
1921
|
}
|
1878
1922
|
function inlineTagSize(tagName) {
|
1879
|
-
|
1880
|
-
|
1881
|
-
for (
|
1882
|
-
|
1923
|
+
var aElems = document.getElementsByTagName(tagName);
|
1924
|
+
var size = 0;
|
1925
|
+
for (var i = 0, len = aElems.length; i < len; i++) {
|
1926
|
+
var e = aElems[i];
|
1883
1927
|
try {
|
1884
1928
|
size += e.innerHTML.length;
|
1885
1929
|
}
|
@@ -1892,16 +1936,16 @@
|
|
1892
1936
|
return size;
|
1893
1937
|
}
|
1894
1938
|
function getNavTiming() {
|
1895
|
-
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1939
|
+
var s = "";
|
1940
|
+
var ns = timing.navigationStart;
|
1941
|
+
var startMark = _getMark(START_MARK);
|
1942
|
+
var endMark = _getMark(END_MARK);
|
1899
1943
|
if (startMark && endMark && !getPageRestoreTime()) {
|
1900
1944
|
// This is a SPA page view, so send the SPA marks & measures instead of Nav Timing.
|
1901
1945
|
// Note: getPageRestoreTime() indicates this was a bfcache restore, which we don't want to treat as a SPA.
|
1902
|
-
|
1946
|
+
var start = floor(startMark.startTime); // the start mark is "zero"
|
1903
1947
|
ns += start; // "navigationStart" for a SPA is the real navigationStart plus the start mark
|
1904
|
-
|
1948
|
+
var end = floor(endMark.startTime) - start; // delta from start mark
|
1905
1949
|
s =
|
1906
1950
|
ns +
|
1907
1951
|
// fetchStart and activationStart are the same as navigationStart for a SPA
|
@@ -1909,21 +1953,17 @@
|
|
1909
1953
|
0 +
|
1910
1954
|
"fs" +
|
1911
1955
|
0 +
|
1912
|
-
"ls" +
|
1913
|
-
end +
|
1914
|
-
"le" +
|
1915
|
-
end +
|
1916
|
-
"";
|
1956
|
+
(end > 0 ? "ls" + end + "le" + end : "");
|
1917
1957
|
}
|
1918
1958
|
else if (performance.timing) {
|
1919
1959
|
// Return the real Nav Timing metrics because this is the "main" page view (not a SPA)
|
1920
|
-
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
if (typeof
|
1926
|
-
|
1960
|
+
var navEntry_1 = getNavigationEntry();
|
1961
|
+
var startRender = getStartRender();
|
1962
|
+
var fcp = getFcp();
|
1963
|
+
var lcp = getLcp();
|
1964
|
+
var prefixNTValue = function (key, prefix, ignoreZero) {
|
1965
|
+
if (typeof navEntry_1[key] === "number") {
|
1966
|
+
var value = navEntry_1[key];
|
1927
1967
|
// We allow zero values for most navigation timing metrics, but for some metrics we want
|
1928
1968
|
// to ignore zeroes. The exceptions are that all metrics can be zero if the page was either
|
1929
1969
|
// prerendered or restored from the BF cache.
|
@@ -1933,19 +1973,23 @@
|
|
1933
1973
|
}
|
1934
1974
|
return "";
|
1935
1975
|
};
|
1936
|
-
|
1937
|
-
|
1976
|
+
// loadEventStart always comes from navigation timing
|
1977
|
+
var loadEventStartStr = prefixNTValue("loadEventStart", "ls", true);
|
1978
|
+
// If LUX.markLoadTime() was called in SPA Mode, we allow the custom mark to override loadEventEnd
|
1979
|
+
var loadEventEndStr = globalConfig.spaMode && endMark
|
1980
|
+
? "le" + processTimeMetric(endMark.startTime)
|
1981
|
+
: prefixNTValue("loadEventEnd", "le", true);
|
1938
1982
|
if (getPageRestoreTime() && startMark && endMark) {
|
1939
1983
|
// For bfcache restores, we set the load time to the time it took for the page to be restored.
|
1940
|
-
|
1984
|
+
var loadTime = floor(endMark.startTime - startMark.startTime);
|
1941
1985
|
loadEventStartStr = "ls" + loadTime;
|
1942
1986
|
loadEventEndStr = "le" + loadTime;
|
1943
1987
|
}
|
1944
|
-
|
1945
|
-
|
1988
|
+
var redirect = wasRedirected();
|
1989
|
+
var isSecure = document.location.protocol === "https:";
|
1946
1990
|
s = [
|
1947
1991
|
ns,
|
1948
|
-
"as" + clamp(
|
1992
|
+
"as" + clamp(navEntry_1.activationStart),
|
1949
1993
|
redirect && !getPageRestoreTime() ? prefixNTValue("redirectStart", "rs") : "",
|
1950
1994
|
redirect && !getPageRestoreTime() ? prefixNTValue("redirectEnd", "re") : "",
|
1951
1995
|
prefixNTValue("fetchStart", "fs"),
|
@@ -1970,7 +2014,7 @@
|
|
1970
2014
|
}
|
1971
2015
|
else if (endMark) {
|
1972
2016
|
// This is a "main" page view that does NOT support Navigation Timing - strange.
|
1973
|
-
|
2017
|
+
var end = floor(endMark.startTime);
|
1974
2018
|
s =
|
1975
2019
|
ns +
|
1976
2020
|
"fs" +
|
@@ -1985,11 +2029,11 @@
|
|
1985
2029
|
}
|
1986
2030
|
// Return First Contentful Paint or undefined if not supported.
|
1987
2031
|
function getFcp() {
|
1988
|
-
|
1989
|
-
for (
|
1990
|
-
|
2032
|
+
var paintEntries = getEntriesByType("paint");
|
2033
|
+
for (var i = 0; i < paintEntries.length; i++) {
|
2034
|
+
var entry = paintEntries[i];
|
1991
2035
|
if (entry.name === "first-contentful-paint") {
|
1992
|
-
|
2036
|
+
var value = processTimeMetric(entry.startTime);
|
1993
2037
|
if (shouldReportValue(value)) {
|
1994
2038
|
return value;
|
1995
2039
|
}
|
@@ -1999,10 +2043,10 @@
|
|
1999
2043
|
}
|
2000
2044
|
// Return Largest Contentful Paint or undefined if not supported.
|
2001
2045
|
function getLcp() {
|
2002
|
-
|
2046
|
+
var lcpEntries = getEntries("largest-contentful-paint");
|
2003
2047
|
if (lcpEntries.length) {
|
2004
|
-
|
2005
|
-
|
2048
|
+
var lastEntry = lcpEntries[lcpEntries.length - 1];
|
2049
|
+
var value = processTimeMetric(lastEntry.startTime);
|
2006
2050
|
if (shouldReportValue(value)) {
|
2007
2051
|
logger.logEvent(43 /* LogEvent.PerformanceEntryProcessed */, [lastEntry]);
|
2008
2052
|
return value;
|
@@ -2015,12 +2059,12 @@
|
|
2015
2059
|
// Return undefined if not supported.
|
2016
2060
|
function getStartRender() {
|
2017
2061
|
if ("PerformancePaintTiming" in self) {
|
2018
|
-
|
2062
|
+
var paintEntries = getEntriesByType("paint");
|
2019
2063
|
if (paintEntries.length) {
|
2020
|
-
|
2064
|
+
var paintValues = paintEntries.map(function (entry) { return entry.startTime; }).sort(sortNumeric);
|
2021
2065
|
// Use the earliest valid paint entry as the start render time.
|
2022
|
-
for (
|
2023
|
-
|
2066
|
+
for (var i = 0; i < paintValues.length; i++) {
|
2067
|
+
var value = processTimeMetric(paintValues[i]);
|
2024
2068
|
if (shouldReportValue(value)) {
|
2025
2069
|
return value;
|
2026
2070
|
}
|
@@ -2064,17 +2108,17 @@
|
|
2064
2108
|
return String(_thisCustomerId);
|
2065
2109
|
}
|
2066
2110
|
function avgDomDepth() {
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2111
|
+
var aElems = document.getElementsByTagName("*");
|
2112
|
+
var i = aElems.length;
|
2113
|
+
var totalParents = 0;
|
2070
2114
|
while (i--) {
|
2071
2115
|
totalParents += numParents(aElems[i]);
|
2072
2116
|
}
|
2073
|
-
|
2117
|
+
var average = round(totalParents / aElems.length);
|
2074
2118
|
return average;
|
2075
2119
|
}
|
2076
2120
|
function numParents(elem) {
|
2077
|
-
|
2121
|
+
var n = 0;
|
2078
2122
|
if (elem.parentNode) {
|
2079
2123
|
while ((elem = elem.parentNode)) {
|
2080
2124
|
n++;
|
@@ -2083,13 +2127,13 @@
|
|
2083
2127
|
return n;
|
2084
2128
|
}
|
2085
2129
|
function docHeight(doc) {
|
2086
|
-
|
2087
|
-
|
2130
|
+
var body = doc.body, docelem = doc.documentElement;
|
2131
|
+
var height = max(body ? body.scrollHeight : 0, body ? body.offsetHeight : 0, docelem ? docelem.clientHeight : 0, docelem ? docelem.scrollHeight : 0, docelem ? docelem.offsetHeight : 0);
|
2088
2132
|
return height;
|
2089
2133
|
}
|
2090
2134
|
function docWidth(doc) {
|
2091
|
-
|
2092
|
-
|
2135
|
+
var body = doc.body, docelem = doc.documentElement;
|
2136
|
+
var width = max(body ? body.scrollWidth : 0, body ? body.offsetWidth : 0, docelem ? docelem.clientWidth : 0, docelem ? docelem.scrollWidth : 0, docelem ? docelem.offsetWidth : 0);
|
2093
2137
|
return width;
|
2094
2138
|
}
|
2095
2139
|
// Return the main HTML document transfer size (in bytes).
|
@@ -2099,8 +2143,8 @@
|
|
2099
2143
|
// Return the connection type based on Network Information API.
|
2100
2144
|
// Note this API is in flux.
|
2101
2145
|
function connectionType() {
|
2102
|
-
|
2103
|
-
|
2146
|
+
var c = navigator.connection;
|
2147
|
+
var connType = "";
|
2104
2148
|
if (c && c.effectiveType) {
|
2105
2149
|
connType = c.effectiveType;
|
2106
2150
|
if ("slow-2g" === connType) {
|
@@ -2117,11 +2161,11 @@
|
|
2117
2161
|
}
|
2118
2162
|
// Return an array of image elements that are in the top viewport.
|
2119
2163
|
function imagesATF() {
|
2120
|
-
|
2121
|
-
|
2164
|
+
var aImages = document.getElementsByTagName("img");
|
2165
|
+
var aImagesAtf = [];
|
2122
2166
|
if (aImages) {
|
2123
|
-
for (
|
2124
|
-
|
2167
|
+
for (var i = 0, len = aImages.length; i < len; i++) {
|
2168
|
+
var image = aImages[i];
|
2125
2169
|
if (inViewport(image)) {
|
2126
2170
|
aImagesAtf.push(image);
|
2127
2171
|
}
|
@@ -2136,15 +2180,15 @@
|
|
2136
2180
|
// but if no parent then start with BODY.
|
2137
2181
|
parent = document.body;
|
2138
2182
|
}
|
2139
|
-
|
2183
|
+
var lastChildInViewport;
|
2140
2184
|
if (parent) {
|
2141
2185
|
// Got errors that parent was null so testing again here.
|
2142
2186
|
// Find the last child that is in the viewport.
|
2143
2187
|
// Elements are listed in DOM order.
|
2144
|
-
|
2188
|
+
var aChildren = parent.children;
|
2145
2189
|
if (aChildren) {
|
2146
|
-
for (
|
2147
|
-
|
2190
|
+
for (var i = 0, len = aChildren.length; i < len; i++) {
|
2191
|
+
var child = aChildren[i];
|
2148
2192
|
if (inViewport(child)) {
|
2149
2193
|
// The children are in DOM order, so we just have to
|
2150
2194
|
// save the LAST child that was in the viewport.
|
@@ -2165,10 +2209,10 @@
|
|
2165
2209
|
}
|
2166
2210
|
// Return true if the element is in the viewport.
|
2167
2211
|
function inViewport(e) {
|
2168
|
-
|
2169
|
-
|
2212
|
+
var vh = document.documentElement.clientHeight;
|
2213
|
+
var vw = document.documentElement.clientWidth;
|
2170
2214
|
// Return true if the top-left corner is in the viewport and it has width & height.
|
2171
|
-
|
2215
|
+
var lt = findPos(e);
|
2172
2216
|
return (lt[0] >= 0 &&
|
2173
2217
|
lt[1] >= 0 &&
|
2174
2218
|
lt[0] < vw &&
|
@@ -2179,8 +2223,8 @@
|
|
2179
2223
|
// Return an array containing the top & left coordinates of the element.
|
2180
2224
|
// from http://www.quirksmode.org/js/findpos.html
|
2181
2225
|
function findPos(el) {
|
2182
|
-
|
2183
|
-
|
2226
|
+
var curleft = 0;
|
2227
|
+
var curtop = 0;
|
2184
2228
|
while (el) {
|
2185
2229
|
try {
|
2186
2230
|
curleft += el.offsetLeft;
|
@@ -2194,8 +2238,10 @@
|
|
2194
2238
|
}
|
2195
2239
|
return [curleft, curtop];
|
2196
2240
|
}
|
2197
|
-
|
2198
|
-
|
2241
|
+
/**
|
2242
|
+
* Mark the load time of the current page. Intended to be used in SPAs where it is not desirable
|
2243
|
+
* to send the beacon as soon as the page has finished loading.
|
2244
|
+
*/
|
2199
2245
|
function _markLoadTime(time) {
|
2200
2246
|
logger.logEvent(12 /* LogEvent.MarkLoadTimeCalled */, [time]);
|
2201
2247
|
if (time) {
|
@@ -2207,7 +2253,7 @@
|
|
2207
2253
|
}
|
2208
2254
|
function createMaxMeasureTimeout() {
|
2209
2255
|
clearMaxMeasureTimeout();
|
2210
|
-
gMaxMeasureTimeout = setTimeout(()
|
2256
|
+
gMaxMeasureTimeout = setTimeout(function () {
|
2211
2257
|
gFlags = addFlag(gFlags, 32 /* Flags.BeaconSentAfterTimeout */);
|
2212
2258
|
beacon.addFlag(32 /* Flags.BeaconSentAfterTimeout */);
|
2213
2259
|
_sendLux();
|
@@ -2219,7 +2265,7 @@
|
|
2219
2265
|
}
|
2220
2266
|
}
|
2221
2267
|
function _getBeaconUrl(customData) {
|
2222
|
-
|
2268
|
+
var queryParams = [
|
2223
2269
|
"v=" + versionAsFloat(),
|
2224
2270
|
"id=" + getCustomerId(),
|
2225
2271
|
"sid=" + gSyncId,
|
@@ -2231,7 +2277,10 @@
|
|
2231
2277
|
if (gFlags) {
|
2232
2278
|
queryParams.push("fl=" + gFlags);
|
2233
2279
|
}
|
2234
|
-
|
2280
|
+
if (LUX.snippetVersion) {
|
2281
|
+
queryParams.push("sv=" + LUX.snippetVersion);
|
2282
|
+
}
|
2283
|
+
var customDataValues = valuesToString(customData);
|
2235
2284
|
if (customDataValues) {
|
2236
2285
|
queryParams.push("CD=" + customDataValues);
|
2237
2286
|
clearUpdateCustomData();
|
@@ -2239,13 +2288,15 @@
|
|
2239
2288
|
return globalConfig.beaconUrl + "?" + queryParams.join("&");
|
2240
2289
|
}
|
2241
2290
|
// Beacon back the LUX data.
|
2242
|
-
function _sendLux(fromUnload
|
2291
|
+
function _sendLux(fromUnload) {
|
2292
|
+
var _a;
|
2293
|
+
if (fromUnload === void 0) { fromUnload = false; }
|
2243
2294
|
if (!isVisible() && !globalConfig.trackHiddenPages && !fromUnload) {
|
2244
2295
|
logger.logEvent(13 /* LogEvent.SendCancelledPageHidden */);
|
2245
2296
|
return;
|
2246
2297
|
}
|
2247
2298
|
clearMaxMeasureTimeout();
|
2248
|
-
|
2299
|
+
var customerid = getCustomerId();
|
2249
2300
|
if (!customerid ||
|
2250
2301
|
!gSyncId ||
|
2251
2302
|
!_sample() || // OUTSIDE the sampled range
|
@@ -2254,21 +2305,32 @@
|
|
2254
2305
|
return;
|
2255
2306
|
}
|
2256
2307
|
logger.logEvent(9 /* LogEvent.DataCollectionStart */);
|
2257
|
-
|
2258
|
-
|
2259
|
-
if (!startMark
|
2260
|
-
//
|
2261
|
-
//
|
2262
|
-
|
2308
|
+
var startMark = _getMark(START_MARK);
|
2309
|
+
var endMark = _getMark(END_MARK);
|
2310
|
+
if (!startMark) {
|
2311
|
+
// For hard navigations set the synthetic load time when the beacon is being sent, unless
|
2312
|
+
// one has already been set.
|
2313
|
+
if (!endMark) {
|
2314
|
+
_markLoadTime();
|
2315
|
+
}
|
2316
|
+
}
|
2317
|
+
else {
|
2318
|
+
// For soft navigations, only set the synthetic load time if SPA mode is not enabled, and...
|
2319
|
+
if (!globalConfig.spaMode) {
|
2320
|
+
// ...there is no existing end mark, or the end mark is from a previous SPA page.
|
2321
|
+
if (!endMark || endMark.startTime < startMark.startTime) {
|
2322
|
+
_markLoadTime();
|
2323
|
+
}
|
2324
|
+
}
|
2263
2325
|
}
|
2264
2326
|
// Store any tracking parameters as custom data
|
2265
|
-
|
2266
|
-
for (
|
2327
|
+
var trackingParams = getTrackingParams();
|
2328
|
+
for (var key in trackingParams) {
|
2267
2329
|
logger.logEvent(44 /* LogEvent.TrackingParamAdded */, [key, trackingParams[key]]);
|
2268
2330
|
addCustomDataValue("_" + key, trackingParams[key]);
|
2269
2331
|
}
|
2270
|
-
|
2271
|
-
|
2332
|
+
var sIx = "";
|
2333
|
+
var INP = getINPDetails();
|
2272
2334
|
// If we haven't already sent an interaction beacon, check for interaction metrics and include
|
2273
2335
|
// them in the main beacon.
|
2274
2336
|
if (!gbIxSent) {
|
@@ -2279,10 +2341,10 @@
|
|
2279
2341
|
INP = undefined;
|
2280
2342
|
}
|
2281
2343
|
}
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2344
|
+
var sET = elementTimingValues(); // Element Timing data
|
2345
|
+
var sCPU = cpuTimes();
|
2346
|
+
var clsData = getData$3(globalConfig);
|
2347
|
+
var sLuxjs = selfLoading();
|
2286
2348
|
if (!isVisible()) {
|
2287
2349
|
gFlags = addFlag(gFlags, 8 /* Flags.VisibilityStateNotVisible */);
|
2288
2350
|
beacon.addFlag(8 /* Flags.VisibilityStateNotVisible */);
|
@@ -2292,31 +2354,31 @@
|
|
2292
2354
|
beacon.addFlag(1024 /* Flags.PageWasPrerendered */);
|
2293
2355
|
}
|
2294
2356
|
if (globalConfig.serverTiming) {
|
2295
|
-
|
2357
|
+
var navEntry = getNavigationEntry();
|
2296
2358
|
if (navEntry.serverTiming) {
|
2297
|
-
|
2298
|
-
for (
|
2299
|
-
_addData(
|
2359
|
+
var stPairs = getKeyValuePairs(globalConfig.serverTiming, navEntry.serverTiming);
|
2360
|
+
for (var name_2 in stPairs) {
|
2361
|
+
_addData(name_2, stPairs[name_2]);
|
2300
2362
|
}
|
2301
2363
|
}
|
2302
2364
|
}
|
2303
2365
|
if (LUX.conversions) {
|
2304
|
-
getMatchesFromPatternMap(LUX.conversions, location.hostname, location.pathname).forEach((conversion)
|
2366
|
+
getMatchesFromPatternMap(LUX.conversions, location.hostname, location.pathname).forEach(function (conversion) {
|
2305
2367
|
LUX.addData(conversion, BOOLEAN_TRUE);
|
2306
2368
|
});
|
2307
2369
|
}
|
2308
2370
|
// We want ALL beacons to have ALL the data used for query filters (geo, pagelabel, browser, & custom data).
|
2309
2371
|
// So we create a base URL that has all the necessary information:
|
2310
|
-
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2372
|
+
var baseUrl = _getBeaconUrl(getAllCustomData());
|
2373
|
+
var is = inlineTagSize("script");
|
2374
|
+
var ic = inlineTagSize("style");
|
2375
|
+
var ds = docSize();
|
2376
|
+
var ct = connectionType();
|
2377
|
+
var dt = deliveryType();
|
2316
2378
|
// Note some page stat values (the `PS` query string) are non-numeric. To make extracting these
|
2317
2379
|
// values easier, we append an underscore "_" to the value. Values this is used for include
|
2318
2380
|
// connection type (ct) and delivery type (dt).
|
2319
|
-
|
2381
|
+
var metricsQueryString =
|
2320
2382
|
// only send Nav Timing and lux.js metrics on initial pageload (not for SPA page views)
|
2321
2383
|
(gbNavSent ? "" : "&NT=" + getNavTiming()) +
|
2322
2384
|
"&LJS=" +
|
@@ -2364,10 +2426,10 @@
|
|
2364
2426
|
// INP and sub-parts
|
2365
2427
|
(INP ? getINPString(INP) : "");
|
2366
2428
|
// We add the user timing entries last so that we can split them to reduce the URL size if necessary.
|
2367
|
-
|
2368
|
-
|
2429
|
+
var utValues = userTimingValues();
|
2430
|
+
var _b = fitUserTimingEntries(utValues, globalConfig, baseUrl + metricsQueryString), beaconUtValues = _b[0], remainingUtValues = _b[1];
|
2369
2431
|
// Send the MAIN LUX beacon.
|
2370
|
-
|
2432
|
+
var mainBeaconUrl = baseUrl +
|
2371
2433
|
metricsQueryString +
|
2372
2434
|
(beaconUtValues.length > 0 ? "&UT=" + beaconUtValues.join(",") : "");
|
2373
2435
|
logger.logEvent(23 /* LogEvent.MainBeaconSent */, [mainBeaconUrl]);
|
@@ -2378,20 +2440,20 @@
|
|
2378
2440
|
gbIxSent = sIx ? 1 : 0;
|
2379
2441
|
// Send other beacons for JUST User Timing.
|
2380
2442
|
while (remainingUtValues.length) {
|
2381
|
-
|
2382
|
-
|
2443
|
+
_a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl), beaconUtValues = _a[0], remainingUtValues = _a[1];
|
2444
|
+
var utBeaconUrl = baseUrl + "&UT=" + beaconUtValues.join(",");
|
2383
2445
|
logger.logEvent(24 /* LogEvent.UserTimingBeaconSent */, [utBeaconUrl]);
|
2384
2446
|
_sendBeacon(utBeaconUrl);
|
2385
2447
|
}
|
2386
2448
|
}
|
2387
|
-
|
2449
|
+
var ixTimerId;
|
2388
2450
|
function _sendIxAfterDelay() {
|
2389
2451
|
clearTimeout(ixTimerId);
|
2390
2452
|
ixTimerId = setTimeout(_sendIx, globalConfig.interactionBeaconDelay);
|
2391
2453
|
}
|
2392
2454
|
// Beacon back the IX data separately (need to sync with LUX beacon on the backend).
|
2393
2455
|
function _sendIx() {
|
2394
|
-
|
2456
|
+
var customerid = getCustomerId();
|
2395
2457
|
if (!customerid ||
|
2396
2458
|
!gSyncId ||
|
2397
2459
|
!_sample() || // OUTSIDE the sampled range
|
@@ -2400,10 +2462,10 @@
|
|
2400
2462
|
) {
|
2401
2463
|
return;
|
2402
2464
|
}
|
2403
|
-
|
2404
|
-
|
2465
|
+
var sIx = ixValues(); // Interaction Metrics
|
2466
|
+
var INP = getINPDetails();
|
2405
2467
|
if (sIx) {
|
2406
|
-
|
2468
|
+
var beaconUrl = _getBeaconUrl(getUpdatedCustomData()) +
|
2407
2469
|
"&IX=" +
|
2408
2470
|
sIx +
|
2409
2471
|
(typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
|
@@ -2416,7 +2478,7 @@
|
|
2416
2478
|
// Beacon back custom data that is recorded _after_ the main beacon was sent
|
2417
2479
|
// (i.e., custom data after window.onload).
|
2418
2480
|
function _sendCustomData() {
|
2419
|
-
|
2481
|
+
var customerid = getCustomerId();
|
2420
2482
|
if (!customerid ||
|
2421
2483
|
!gSyncId ||
|
2422
2484
|
!_sample() || // OUTSIDE the sampled range
|
@@ -2424,9 +2486,9 @@
|
|
2424
2486
|
) {
|
2425
2487
|
return;
|
2426
2488
|
}
|
2427
|
-
|
2489
|
+
var customDataValues = valuesToString(getUpdatedCustomData());
|
2428
2490
|
if (customDataValues) {
|
2429
|
-
|
2491
|
+
var beaconUrl = _getBeaconUrl(getUpdatedCustomData());
|
2430
2492
|
logger.logEvent(26 /* LogEvent.CustomDataBeaconSent */, [beaconUrl]);
|
2431
2493
|
_sendBeacon(beaconUrl);
|
2432
2494
|
}
|
@@ -2450,7 +2512,7 @@
|
|
2450
2512
|
}
|
2451
2513
|
}
|
2452
2514
|
function _keyHandler(e) {
|
2453
|
-
|
2515
|
+
var keyCode = e.keyCode;
|
2454
2516
|
/**
|
2455
2517
|
* Ignore modifier keys
|
2456
2518
|
*
|
@@ -2466,7 +2528,7 @@
|
|
2466
2528
|
if (typeof ghIx["k"] === "undefined") {
|
2467
2529
|
ghIx["k"] = msSincePageInit();
|
2468
2530
|
if (e && e.target instanceof Element) {
|
2469
|
-
|
2531
|
+
var trackId = getNodeSelector(e.target);
|
2470
2532
|
if (trackId) {
|
2471
2533
|
ghIx["ki"] = trackId;
|
2472
2534
|
}
|
@@ -2484,7 +2546,7 @@
|
|
2484
2546
|
// Only one interaction type is recorded. Scrolls are considered less important, so delete
|
2485
2547
|
// any scroll times if they exist.
|
2486
2548
|
delete ghIx["s"];
|
2487
|
-
|
2549
|
+
var target = void 0;
|
2488
2550
|
try {
|
2489
2551
|
// Seeing "Permission denied" errors, so do a simple try-catch.
|
2490
2552
|
if (e && e.target instanceof Element) {
|
@@ -2500,7 +2562,7 @@
|
|
2500
2562
|
ghIx["cx"] = e.clientX;
|
2501
2563
|
ghIx["cy"] = e.clientY;
|
2502
2564
|
}
|
2503
|
-
|
2565
|
+
var trackId = getNodeSelector(target);
|
2504
2566
|
if (trackId) {
|
2505
2567
|
ghIx["ci"] = trackId;
|
2506
2568
|
}
|
@@ -2510,7 +2572,7 @@
|
|
2510
2572
|
_removeIxHandlers();
|
2511
2573
|
}
|
2512
2574
|
function _addUnloadHandlers() {
|
2513
|
-
|
2575
|
+
var onunload = function () {
|
2514
2576
|
gFlags = addFlag(gFlags, 16 /* Flags.BeaconSentFromUnloadHandler */);
|
2515
2577
|
beacon.addFlag(16 /* Flags.BeaconSentFromUnloadHandler */);
|
2516
2578
|
logger.logEvent(10 /* LogEvent.UnloadHandlerTriggered */);
|
@@ -2528,7 +2590,7 @@
|
|
2528
2590
|
addListener("unload", onunload, true);
|
2529
2591
|
addListener("beforeunload", onunload, true);
|
2530
2592
|
}
|
2531
|
-
addListener("visibilitychange", ()
|
2593
|
+
addListener("visibilitychange", function () {
|
2532
2594
|
if (document.visibilityState === "hidden") {
|
2533
2595
|
onunload();
|
2534
2596
|
}
|
@@ -2547,8 +2609,9 @@
|
|
2547
2609
|
// This is a big number (epoch ms . random) that is used to matchup a LUX beacon with a separate IX beacon
|
2548
2610
|
// (because they get sent at different times). Each "page view" (including SPA) should have a
|
2549
2611
|
// unique gSyncId.
|
2550
|
-
function createSyncId(inSampleBucket
|
2551
|
-
|
2612
|
+
function createSyncId(inSampleBucket) {
|
2613
|
+
if (inSampleBucket === void 0) { inSampleBucket = false; }
|
2614
|
+
var syncId;
|
2552
2615
|
if (inSampleBucket) {
|
2553
2616
|
// "00" matches all sample rates
|
2554
2617
|
syncId = Number(new Date()) + "00000";
|
@@ -2564,16 +2627,16 @@
|
|
2564
2627
|
// If there is NOT a UID then set it to the new value (which is the same as the "sync ID" for this page).
|
2565
2628
|
// Refresh its expiration date and return its value.
|
2566
2629
|
function refreshUniqueId(newValue) {
|
2567
|
-
|
2630
|
+
var uid = _getCookie(SESSION_COOKIE_NAME);
|
2568
2631
|
if (!uid || uid.length < 11) {
|
2569
2632
|
uid = newValue;
|
2570
2633
|
}
|
2571
2634
|
else {
|
2572
2635
|
// Prevent sessions lasting more than 24 hours.
|
2573
2636
|
// The first 10 characters of uid is the epoch time when the session started.
|
2574
|
-
|
2575
|
-
|
2576
|
-
if (
|
2637
|
+
var uidStart = parseInt(uid.substring(0, 10));
|
2638
|
+
var now_2 = Number(new Date()) / 1000; // in seconds
|
2639
|
+
if (now_2 - uidStart > 24 * 60 * 60) {
|
2577
2640
|
// older than 24 hours - reset to new value
|
2578
2641
|
uid = newValue;
|
2579
2642
|
}
|
@@ -2598,7 +2661,7 @@
|
|
2598
2661
|
return LUX.label;
|
2599
2662
|
}
|
2600
2663
|
if (typeof LUX.pagegroups !== "undefined") {
|
2601
|
-
|
2664
|
+
var label = getMatchesFromPatternMap(LUX.pagegroups, location.hostname, location.pathname, true);
|
2602
2665
|
if (label) {
|
2603
2666
|
gFlags = addFlag(gFlags, 512 /* Flags.PageLabelFromUrlPattern */);
|
2604
2667
|
beacon.addFlag(512 /* Flags.PageLabelFromUrlPattern */);
|
@@ -2606,9 +2669,9 @@
|
|
2606
2669
|
}
|
2607
2670
|
}
|
2608
2671
|
if (typeof LUX.jspagelabel !== "undefined") {
|
2609
|
-
|
2672
|
+
var evaluateJsPageLabel = Function('"use strict"; return ' + LUX.jspagelabel);
|
2610
2673
|
try {
|
2611
|
-
|
2674
|
+
var label = evaluateJsPageLabel();
|
2612
2675
|
if (label) {
|
2613
2676
|
gFlags = addFlag(gFlags, 256 /* Flags.PageLabelFromGlobalVariable */);
|
2614
2677
|
beacon.addFlag(256 /* Flags.PageLabelFromGlobalVariable */);
|
@@ -2627,9 +2690,9 @@
|
|
2627
2690
|
function _getCookie(name) {
|
2628
2691
|
try {
|
2629
2692
|
// Seeing "Permission denied" errors, so do a simple try-catch.
|
2630
|
-
|
2631
|
-
for (
|
2632
|
-
|
2693
|
+
var aTuples = document.cookie.split(";");
|
2694
|
+
for (var i = 0; i < aTuples.length; i++) {
|
2695
|
+
var aTuple = aTuples[i].split("=");
|
2633
2696
|
if (name === aTuple[0].trim()) {
|
2634
2697
|
// cookie name starts with " " if not first
|
2635
2698
|
return unescape(aTuple[1]);
|
@@ -2658,7 +2721,7 @@
|
|
2658
2721
|
// Set "LUX.auto=false" to disable send results automatically and
|
2659
2722
|
// instead you must call LUX.send() explicitly.
|
2660
2723
|
if (globalConfig.auto) {
|
2661
|
-
|
2724
|
+
var sendBeaconWhenVisible_1 = function () {
|
2662
2725
|
if (globalConfig.trackHiddenPages) {
|
2663
2726
|
_sendLux();
|
2664
2727
|
}
|
@@ -2666,30 +2729,30 @@
|
|
2666
2729
|
onVisible(_sendLux);
|
2667
2730
|
}
|
2668
2731
|
};
|
2669
|
-
|
2670
|
-
|
2671
|
-
|
2732
|
+
var sendBeaconAfterMinimumMeasureTime_1 = function () {
|
2733
|
+
var elapsedTime = msSincePageInit();
|
2734
|
+
var timeRemaining = globalConfig.minMeasureTime - elapsedTime;
|
2672
2735
|
if (timeRemaining <= 0) {
|
2673
2736
|
logger.logEvent(11 /* LogEvent.OnloadHandlerTriggered */, [
|
2674
2737
|
elapsedTime,
|
2675
2738
|
globalConfig.minMeasureTime,
|
2676
2739
|
]);
|
2677
2740
|
if (globalConfig.measureUntil === "onload") {
|
2678
|
-
onPageLoad(
|
2741
|
+
onPageLoad(sendBeaconWhenVisible_1);
|
2679
2742
|
}
|
2680
2743
|
}
|
2681
2744
|
else {
|
2682
2745
|
// Try again after the minimum measurement time has elapsed
|
2683
|
-
setTimeout(
|
2746
|
+
setTimeout(sendBeaconAfterMinimumMeasureTime_1, timeRemaining);
|
2684
2747
|
}
|
2685
2748
|
};
|
2686
|
-
|
2749
|
+
sendBeaconAfterMinimumMeasureTime_1();
|
2687
2750
|
}
|
2688
2751
|
// When newBeaconOnPageShow = true, we initiate a new page view whenever a page is restored from
|
2689
2752
|
// bfcache. Since we have no "onload" event to hook into after a bfcache restore, we rely on the
|
2690
2753
|
// unload and maxMeasureTime handlers to send the beacon.
|
2691
2754
|
if (globalConfig.newBeaconOnPageShow) {
|
2692
|
-
addEventListener("pageshow", (event)
|
2755
|
+
addEventListener("pageshow", function (event) {
|
2693
2756
|
if (event.persisted) {
|
2694
2757
|
// Record the timestamp of the bfcache restore
|
2695
2758
|
setPageRestoreTime(event.timeStamp);
|
@@ -2697,8 +2760,9 @@
|
|
2697
2760
|
// restore. Wrapping this in a setTimeout ensures the browser has enough time to update the
|
2698
2761
|
// visibility.
|
2699
2762
|
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1133363
|
2700
|
-
setTimeout(()
|
2763
|
+
setTimeout(function () {
|
2701
2764
|
if (gbLuxSent) {
|
2765
|
+
logger.logEvent(17 /* LogEvent.BfCacheRestore */);
|
2702
2766
|
// If the beacon was already sent for this page, we start a new page view and mark the
|
2703
2767
|
// load time as the time it took to restore the page.
|
2704
2768
|
_init(getPageRestoreTime(), false);
|
@@ -2723,38 +2787,85 @@
|
|
2723
2787
|
* LUX functions and properties must be attached to the existing global object to ensure that
|
2724
2788
|
* changes made to the global object are reflected in the "internal" LUX object, and vice versa.
|
2725
2789
|
*/
|
2726
|
-
|
2790
|
+
var globalLux = globalConfig;
|
2727
2791
|
// Functions
|
2792
|
+
globalLux.addData = _addData;
|
2793
|
+
globalLux.cmd = _runCommand;
|
2794
|
+
globalLux.getSessionId = _getUniqueId;
|
2728
2795
|
globalLux.mark = _mark;
|
2729
|
-
globalLux.measure = _measure;
|
2730
|
-
globalLux.init = _init;
|
2731
2796
|
globalLux.markLoadTime = _markLoadTime;
|
2797
|
+
globalLux.measure = _measure;
|
2732
2798
|
globalLux.on = subscribe;
|
2733
|
-
globalLux.
|
2734
|
-
|
2799
|
+
globalLux.snippetVersion = LUX.snippetVersion;
|
2800
|
+
globalLux.version = VERSION;
|
2801
|
+
globalLux.init = function (time) {
|
2802
|
+
logger.logEvent(3 /* LogEvent.InitCalled */);
|
2803
|
+
// Some customers (incorrectly) call LUX.init on the very first page load of a SPA. This would
|
2804
|
+
// cause some first-page-only data (like paint metrics) to be lost. To prevent this, we silently
|
2805
|
+
// bail from this function when we detect an unnecessary LUX.init call.
|
2806
|
+
//
|
2807
|
+
// Some notes about how this is compatible with SPA mode:
|
2808
|
+
// - For "new" implementations where SPA mode has always been enabled, we expect
|
2809
|
+
// LUX.startSoftNavigation() to be called instead of LUX.init(), so this code path should
|
2810
|
+
// never be reached.
|
2811
|
+
//
|
2812
|
+
// - For "old" implementations, we expect LUX.send() is still being called. So we can rely on
|
2813
|
+
// there being an end mark from the previous LUX.send() call.
|
2814
|
+
//
|
2815
|
+
var endMark = _getMark(END_MARK);
|
2816
|
+
if (!endMark) {
|
2817
|
+
logger.logEvent(18 /* LogEvent.InitCallIgnored */);
|
2818
|
+
return;
|
2819
|
+
}
|
2820
|
+
// In SPA mode, ensure the previous page's beacon has been sent
|
2821
|
+
if (globalConfig.spaMode) {
|
2822
|
+
beacon.send();
|
2823
|
+
_sendLux();
|
2824
|
+
}
|
2825
|
+
_init(time);
|
2826
|
+
};
|
2827
|
+
globalLux.startSoftNavigation = function (time) {
|
2828
|
+
logger.logEvent(14 /* LogEvent.StartSoftNavigationCalled */);
|
2735
2829
|
beacon.send();
|
2736
2830
|
_sendLux();
|
2831
|
+
_init(time);
|
2737
2832
|
};
|
2738
|
-
globalLux.
|
2739
|
-
|
2740
|
-
|
2833
|
+
globalLux.send = function (force) {
|
2834
|
+
if (globalConfig.spaMode && !force) {
|
2835
|
+
// In SPA mode, sending the beacon manually is not necessary, and is ignored unless the `force`
|
2836
|
+
// parameter has been specified.
|
2837
|
+
logger.logEvent(16 /* LogEvent.SendCancelledSpaMode */);
|
2838
|
+
// If markLoadTime() has not already been called, we assume this send() call corresponds to a
|
2839
|
+
// "loaded" state and mark it as the load time. This mark is important as it is used to
|
2840
|
+
// decide whether an init() call can be ignored or not.
|
2841
|
+
var startMark = _getMark(START_MARK);
|
2842
|
+
var endMark = _getMark(END_MARK);
|
2843
|
+
if (!endMark || (startMark && endMark.startTime < startMark.startTime)) {
|
2844
|
+
_markLoadTime();
|
2845
|
+
}
|
2846
|
+
}
|
2847
|
+
else {
|
2848
|
+
logger.logEvent(7 /* LogEvent.SendCalled */);
|
2849
|
+
beacon.send();
|
2850
|
+
_sendLux();
|
2851
|
+
}
|
2852
|
+
};
|
2853
|
+
globalLux.getDebug = function () {
|
2741
2854
|
console.log("SpeedCurve RUM debugging documentation: https://support.speedcurve.com/docs/rum-js-api#luxgetdebug");
|
2742
2855
|
return logger.getEvents();
|
2743
2856
|
};
|
2744
|
-
globalLux.forceSample = ()
|
2857
|
+
globalLux.forceSample = function () {
|
2745
2858
|
logger.logEvent(8 /* LogEvent.ForceSampleCalled */);
|
2746
2859
|
setUniqueId(createSyncId(true));
|
2747
2860
|
};
|
2748
|
-
globalLux.doUpdate = ()
|
2861
|
+
globalLux.doUpdate = function () {
|
2749
2862
|
// Deprecated, intentionally empty.
|
2750
2863
|
};
|
2751
|
-
globalLux.cmd = _runCommand;
|
2752
|
-
// Public properties
|
2753
|
-
globalLux.version = VERSION;
|
2754
2864
|
/**
|
2755
2865
|
* Run a command from the command queue
|
2756
2866
|
*/
|
2757
|
-
function _runCommand(
|
2867
|
+
function _runCommand(_a) {
|
2868
|
+
var fn = _a[0], args = _a.slice(1);
|
2758
2869
|
if (typeof globalLux[fn] === "function") {
|
2759
2870
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
2760
2871
|
globalLux[fn].apply(globalLux, args);
|
@@ -2789,4 +2900,3 @@
|
|
2789
2900
|
// End of more settings
|
2790
2901
|
// ---------------------------------------------------------------------------
|
2791
2902
|
})();
|
2792
|
-
//# sourceMappingURL=lux.js.map
|