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