lighthouse 9.6.7 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +4 -1
- package/CONTRIBUTING.md +15 -15
- package/build-tracker.config.js +0 -1
- package/cli/bin.d.ts +5 -0
- package/cli/bin.js +140 -0
- package/cli/cli-flags.d.ts +258 -0
- package/cli/cli-flags.js +553 -0
- package/cli/commands/commands.d.ts +4 -0
- package/cli/commands/commands.js +9 -0
- package/cli/commands/list-audits.d.ts +2 -0
- package/cli/commands/list-audits.js +15 -0
- package/cli/commands/list-locales.d.ts +2 -0
- package/cli/commands/list-locales.js +15 -0
- package/cli/commands/list-trace-categories.d.ts +2 -0
- package/cli/commands/list-trace-categories.js +14 -0
- package/cli/index.d.ts +3 -0
- package/cli/index.js +10 -0
- package/cli/printer.d.ts +28 -0
- package/cli/printer.js +99 -0
- package/cli/run.d.ts +24 -0
- package/cli/run.js +284 -0
- package/cli/sentry-prompt.d.ts +5 -0
- package/cli/sentry-prompt.js +78 -0
- package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +71 -0
- package/cli/test/smokehouse/config/exclusions.d.ts +12 -0
- package/cli/test/smokehouse/config/exclusions.js +36 -0
- package/cli/test/smokehouse/core-tests.d.ts +4 -0
- package/cli/test/smokehouse/core-tests.js +134 -0
- package/cli/test/smokehouse/frontends/back-compat-util.d.ts +16 -0
- package/cli/test/smokehouse/frontends/back-compat-util.js +41 -0
- package/cli/test/smokehouse/frontends/lib.d.ts +8 -0
- package/cli/test/smokehouse/frontends/lib.js +48 -0
- package/cli/test/smokehouse/frontends/node.d.ts +2 -0
- package/cli/test/smokehouse/frontends/node.js +12 -0
- package/cli/test/smokehouse/frontends/smokehouse-bin.d.ts +3 -0
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +258 -0
- package/cli/test/smokehouse/lib/child-process-error.d.ts +21 -0
- package/cli/test/smokehouse/lib/child-process-error.js +25 -0
- package/cli/test/smokehouse/lib/concurrent-mapper.d.ts +78 -0
- package/cli/test/smokehouse/lib/concurrent-mapper.js +125 -0
- package/cli/test/smokehouse/lib/local-console.d.ts +33 -0
- package/cli/test/smokehouse/lib/local-console.js +50 -0
- package/cli/test/smokehouse/lighthouse-runners/bundle.d.ts +16 -0
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +158 -0
- package/cli/test/smokehouse/lighthouse-runners/cli.d.ts +18 -0
- package/cli/test/smokehouse/lighthouse-runners/cli.js +134 -0
- package/cli/test/smokehouse/lighthouse-runners/devtools.d.ts +23 -0
- package/cli/test/smokehouse/lighthouse-runners/devtools.js +72 -0
- package/cli/test/smokehouse/readme.md +208 -0
- package/cli/test/smokehouse/report-assert-test.d.ts +2 -0
- package/cli/test/smokehouse/report-assert-test.js +298 -0
- package/cli/test/smokehouse/report-assert.d.ts +50 -0
- package/cli/test/smokehouse/report-assert.js +520 -0
- package/cli/test/smokehouse/smokehouse.d.ts +35 -0
- package/cli/test/smokehouse/smokehouse.js +336 -0
- package/cli/test/smokehouse/version-check-test.d.ts +2 -0
- package/cli/test/smokehouse/version-check-test.js +43 -0
- package/cli/test/smokehouse/version-check.d.ts +15 -0
- package/cli/test/smokehouse/version-check.js +48 -0
- package/commitlint.config.js +0 -1
- package/core/audits/accessibility/accesskeys.d.ts +10 -0
- package/core/audits/accessibility/accesskeys.js +44 -0
- package/core/audits/accessibility/aria-allowed-attr.d.ts +10 -0
- package/core/audits/accessibility/aria-allowed-attr.js +44 -0
- package/core/audits/accessibility/aria-command-name.d.ts +10 -0
- package/core/audits/accessibility/aria-command-name.js +42 -0
- package/core/audits/accessibility/aria-hidden-body.d.ts +10 -0
- package/core/audits/accessibility/aria-hidden-body.js +42 -0
- package/core/audits/accessibility/aria-hidden-focus.d.ts +10 -0
- package/core/audits/accessibility/aria-hidden-focus.js +42 -0
- package/core/audits/accessibility/aria-input-field-name.d.ts +10 -0
- package/core/audits/accessibility/aria-input-field-name.js +42 -0
- package/core/audits/accessibility/aria-meter-name.d.ts +10 -0
- package/core/audits/accessibility/aria-meter-name.js +42 -0
- package/core/audits/accessibility/aria-progressbar-name.d.ts +10 -0
- package/core/audits/accessibility/aria-progressbar-name.js +42 -0
- package/core/audits/accessibility/aria-required-attr.d.ts +10 -0
- package/core/audits/accessibility/aria-required-attr.js +43 -0
- package/core/audits/accessibility/aria-required-children.d.ts +10 -0
- package/core/audits/accessibility/aria-required-children.js +47 -0
- package/core/audits/accessibility/aria-required-parent.d.ts +10 -0
- package/core/audits/accessibility/aria-required-parent.js +45 -0
- package/core/audits/accessibility/aria-roles.d.ts +10 -0
- package/core/audits/accessibility/aria-roles.js +44 -0
- package/core/audits/accessibility/aria-toggle-field-name.d.ts +10 -0
- package/core/audits/accessibility/aria-toggle-field-name.js +42 -0
- package/core/audits/accessibility/aria-tooltip-name.d.ts +10 -0
- package/core/audits/accessibility/aria-tooltip-name.js +42 -0
- package/core/audits/accessibility/aria-treeitem-name.d.ts +10 -0
- package/core/audits/accessibility/aria-treeitem-name.js +42 -0
- package/core/audits/accessibility/aria-valid-attr-value.d.ts +10 -0
- package/core/audits/accessibility/aria-valid-attr-value.js +44 -0
- package/core/audits/accessibility/aria-valid-attr.d.ts +10 -0
- package/core/audits/accessibility/aria-valid-attr.js +44 -0
- package/core/audits/accessibility/axe-audit.d.ts +17 -0
- package/core/audits/accessibility/axe-audit.js +113 -0
- package/core/audits/accessibility/button-name.d.ts +10 -0
- package/core/audits/accessibility/button-name.js +44 -0
- package/core/audits/accessibility/bypass.d.ts +10 -0
- package/core/audits/accessibility/bypass.js +46 -0
- package/core/audits/accessibility/color-contrast.d.ts +10 -0
- package/core/audits/accessibility/color-contrast.js +45 -0
- package/core/audits/accessibility/definition-list.d.ts +10 -0
- package/core/audits/accessibility/definition-list.js +46 -0
- package/core/audits/accessibility/dlitem.d.ts +10 -0
- package/core/audits/accessibility/dlitem.js +44 -0
- package/core/audits/accessibility/document-title.d.ts +10 -0
- package/core/audits/accessibility/document-title.js +44 -0
- package/core/audits/accessibility/duplicate-id-active.d.ts +10 -0
- package/core/audits/accessibility/duplicate-id-active.js +42 -0
- package/core/audits/accessibility/duplicate-id-aria.d.ts +10 -0
- package/core/audits/accessibility/duplicate-id-aria.js +42 -0
- package/core/audits/accessibility/form-field-multiple-labels.d.ts +10 -0
- package/core/audits/accessibility/form-field-multiple-labels.js +43 -0
- package/core/audits/accessibility/frame-title.d.ts +10 -0
- package/core/audits/accessibility/frame-title.js +43 -0
- package/core/audits/accessibility/heading-order.d.ts +10 -0
- package/core/audits/accessibility/heading-order.js +42 -0
- package/core/audits/accessibility/html-has-lang.d.ts +10 -0
- package/core/audits/accessibility/html-has-lang.js +46 -0
- package/core/audits/accessibility/html-lang-valid.d.ts +10 -0
- package/core/audits/accessibility/html-lang-valid.js +45 -0
- package/core/audits/accessibility/image-alt.d.ts +10 -0
- package/core/audits/accessibility/image-alt.js +44 -0
- package/core/audits/accessibility/input-image-alt.d.ts +10 -0
- package/core/audits/accessibility/input-image-alt.js +44 -0
- package/core/audits/accessibility/label.d.ts +10 -0
- package/core/audits/accessibility/label.js +44 -0
- package/core/audits/accessibility/link-name.d.ts +10 -0
- package/core/audits/accessibility/link-name.js +45 -0
- package/core/audits/accessibility/list.d.ts +10 -0
- package/core/audits/accessibility/list.js +46 -0
- package/core/audits/accessibility/listitem.d.ts +10 -0
- package/core/audits/accessibility/listitem.js +45 -0
- package/core/audits/accessibility/manual/custom-controls-labels.d.ts +8 -0
- package/core/audits/accessibility/manual/custom-controls-labels.js +27 -0
- package/core/audits/accessibility/manual/custom-controls-roles.d.ts +8 -0
- package/core/audits/accessibility/manual/custom-controls-roles.js +27 -0
- package/core/audits/accessibility/manual/focus-traps.d.ts +8 -0
- package/core/audits/accessibility/manual/focus-traps.js +27 -0
- package/core/audits/accessibility/manual/focusable-controls.d.ts +8 -0
- package/core/audits/accessibility/manual/focusable-controls.js +27 -0
- package/core/audits/accessibility/manual/interactive-element-affordance.d.ts +8 -0
- package/core/audits/accessibility/manual/interactive-element-affordance.js +27 -0
- package/core/audits/accessibility/manual/logical-tab-order.d.ts +8 -0
- package/core/audits/accessibility/manual/logical-tab-order.js +27 -0
- package/core/audits/accessibility/manual/managed-focus.d.ts +8 -0
- package/core/audits/accessibility/manual/managed-focus.js +27 -0
- package/core/audits/accessibility/manual/offscreen-content-hidden.d.ts +9 -0
- package/core/audits/accessibility/manual/offscreen-content-hidden.js +28 -0
- package/core/audits/accessibility/manual/use-landmarks.d.ts +8 -0
- package/core/audits/accessibility/manual/use-landmarks.js +27 -0
- package/core/audits/accessibility/manual/visual-order-follows-dom.d.ts +8 -0
- package/core/audits/accessibility/manual/visual-order-follows-dom.js +27 -0
- package/core/audits/accessibility/meta-refresh.d.ts +10 -0
- package/core/audits/accessibility/meta-refresh.js +45 -0
- package/core/audits/accessibility/meta-viewport.d.ts +10 -0
- package/core/audits/accessibility/meta-viewport.js +46 -0
- package/core/audits/accessibility/object-alt.d.ts +10 -0
- package/core/audits/accessibility/object-alt.js +44 -0
- package/core/audits/accessibility/tabindex.d.ts +10 -0
- package/core/audits/accessibility/tabindex.js +44 -0
- package/core/audits/accessibility/td-headers-attr.d.ts +10 -0
- package/core/audits/accessibility/td-headers-attr.js +48 -0
- package/core/audits/accessibility/th-has-data-cells.d.ts +10 -0
- package/core/audits/accessibility/th-has-data-cells.js +48 -0
- package/core/audits/accessibility/valid-lang.d.ts +10 -0
- package/core/audits/accessibility/valid-lang.js +44 -0
- package/core/audits/accessibility/video-caption.d.ts +10 -0
- package/core/audits/accessibility/video-caption.js +46 -0
- package/core/audits/audit.d.ts +157 -0
- package/core/audits/audit.js +405 -0
- package/core/audits/autocomplete.d.ts +36 -0
- package/core/audits/autocomplete.js +290 -0
- package/core/audits/bf-cache.d.ts +21 -0
- package/core/audits/bf-cache.js +120 -0
- package/core/audits/bootup-time.d.ts +26 -0
- package/core/audits/bootup-time.js +151 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +82 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +260 -0
- package/core/audits/byte-efficiency/duplicated-javascript.d.ts +52 -0
- package/core/audits/byte-efficiency/duplicated-javascript.js +247 -0
- package/core/audits/byte-efficiency/efficient-animated-content.d.ts +23 -0
- package/core/audits/byte-efficiency/efficient-animated-content.js +92 -0
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +100 -0
- package/core/audits/byte-efficiency/legacy-javascript.js +479 -0
- package/core/audits/byte-efficiency/modern-image-formats.d.ts +38 -0
- package/core/audits/byte-efficiency/modern-image-formats.js +186 -0
- package/core/audits/byte-efficiency/offscreen-images.d.ts +75 -0
- package/core/audits/byte-efficiency/offscreen-images.js +253 -0
- package/{lighthouse-core → core}/audits/byte-efficiency/polyfill-graph-data.json +0 -0
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +55 -0
- package/core/audits/byte-efficiency/render-blocking-resources.js +307 -0
- package/core/audits/byte-efficiency/total-byte-weight.d.ts +21 -0
- package/core/audits/byte-efficiency/total-byte-weight.js +109 -0
- package/core/audits/byte-efficiency/unminified-css.d.ts +36 -0
- package/core/audits/byte-efficiency/unminified-css.js +114 -0
- package/core/audits/byte-efficiency/unminified-javascript.d.ts +37 -0
- package/core/audits/byte-efficiency/unminified-javascript.js +118 -0
- package/core/audits/byte-efficiency/unused-css-rules.d.ts +16 -0
- package/core/audits/byte-efficiency/unused-css-rules.js +69 -0
- package/core/audits/byte-efficiency/unused-javascript.d.ts +27 -0
- package/core/audits/byte-efficiency/unused-javascript.js +163 -0
- package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +61 -0
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +297 -0
- package/core/audits/byte-efficiency/uses-optimized-images.d.ts +33 -0
- package/core/audits/byte-efficiency/uses-optimized-images.js +145 -0
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +16 -0
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +100 -0
- package/core/audits/byte-efficiency/uses-responsive-images.d.ts +45 -0
- package/core/audits/byte-efficiency/uses-responsive-images.js +201 -0
- package/core/audits/byte-efficiency/uses-text-compression.d.ts +14 -0
- package/core/audits/byte-efficiency/uses-text-compression.js +100 -0
- package/core/audits/content-width.d.ts +17 -0
- package/core/audits/content-width.js +76 -0
- package/core/audits/critical-request-chains.d.ts +44 -0
- package/core/audits/critical-request-chains.js +218 -0
- package/core/audits/csp-xss.d.ts +51 -0
- package/core/audits/csp-xss.js +186 -0
- package/core/audits/deprecations.d.ts +19 -0
- package/core/audits/deprecations.js +114 -0
- package/core/audits/diagnostics.d.ts +11 -0
- package/core/audits/diagnostics.js +79 -0
- package/core/audits/dobetterweb/charset.d.ts +19 -0
- package/core/audits/dobetterweb/charset.js +93 -0
- package/core/audits/dobetterweb/doctype.d.ts +21 -0
- package/core/audits/dobetterweb/doctype.js +120 -0
- package/core/audits/dobetterweb/dom-size.d.ts +26 -0
- package/core/audits/dobetterweb/dom-size.js +134 -0
- package/core/audits/dobetterweb/geolocation-on-start.d.ts +16 -0
- package/core/audits/dobetterweb/geolocation-on-start.js +69 -0
- package/core/audits/dobetterweb/inspector-issues.d.ts +45 -0
- package/core/audits/dobetterweb/inspector-issues.js +191 -0
- package/core/audits/dobetterweb/js-libraries.d.ts +15 -0
- package/core/audits/dobetterweb/js-libraries.js +87 -0
- package/core/audits/dobetterweb/no-document-write.d.ts +16 -0
- package/core/audits/dobetterweb/no-document-write.js +84 -0
- package/core/audits/dobetterweb/notification-on-start.d.ts +16 -0
- package/core/audits/dobetterweb/notification-on-start.js +68 -0
- package/core/audits/dobetterweb/paste-preventing-inputs.d.ts +15 -0
- package/core/audits/dobetterweb/paste-preventing-inputs.js +67 -0
- package/core/audits/dobetterweb/uses-http2.d.ts +72 -0
- package/core/audits/dobetterweb/uses-http2.js +264 -0
- package/core/audits/dobetterweb/uses-passive-event-listeners.d.ts +16 -0
- package/core/audits/dobetterweb/uses-passive-event-listeners.js +67 -0
- package/core/audits/errors-in-console.d.ts +31 -0
- package/core/audits/errors-in-console.js +120 -0
- package/core/audits/final-screenshot.d.ts +11 -0
- package/core/audits/final-screenshot.js +58 -0
- package/core/audits/font-display.d.ts +32 -0
- package/core/audits/font-display.js +193 -0
- package/core/audits/image-aspect-ratio.d.ts +30 -0
- package/core/audits/image-aspect-ratio.js +122 -0
- package/core/audits/image-size-responsive.d.ts +28 -0
- package/core/audits/image-size-responsive.js +332 -0
- package/core/audits/installable-manifest.d.ts +118 -0
- package/core/audits/installable-manifest.js +265 -0
- package/core/audits/is-on-https.d.ts +23 -0
- package/core/audits/is-on-https.js +123 -0
- package/core/audits/largest-contentful-paint-element.d.ts +14 -0
- package/core/audits/largest-contentful-paint-element.js +69 -0
- package/core/audits/layout-shift-elements.d.ts +15 -0
- package/core/audits/layout-shift-elements.js +74 -0
- package/core/audits/lcp-lazy-loaded.d.ts +21 -0
- package/core/audits/lcp-lazy-loaded.js +84 -0
- package/core/audits/long-tasks.d.ts +16 -0
- package/core/audits/long-tasks.js +124 -0
- package/core/audits/main-thread-tasks.d.ts +11 -0
- package/core/audits/main-thread-tasks.js +58 -0
- package/core/audits/mainthread-work-breakdown.d.ts +28 -0
- package/core/audits/mainthread-work-breakdown.js +135 -0
- package/core/audits/manual/manual-audit.d.ts +13 -0
- package/core/audits/manual/manual-audit.js +36 -0
- package/core/audits/manual/pwa-cross-browser.d.ts +12 -0
- package/core/audits/manual/pwa-cross-browser.js +40 -0
- package/core/audits/manual/pwa-each-page-has-url.d.ts +12 -0
- package/core/audits/manual/pwa-each-page-has-url.js +38 -0
- package/core/audits/manual/pwa-page-transitions.d.ts +12 -0
- package/core/audits/manual/pwa-page-transitions.js +38 -0
- package/core/audits/maskable-icon.d.ts +25 -0
- package/core/audits/maskable-icon.js +70 -0
- package/core/audits/metrics/cumulative-layout-shift.d.ts +21 -0
- package/core/audits/metrics/cumulative-layout-shift.js +79 -0
- package/core/audits/metrics/experimental-interaction-to-next-paint.d.ts +21 -0
- package/core/audits/metrics/experimental-interaction-to-next-paint.js +88 -0
- package/core/audits/metrics/first-contentful-paint-3g.d.ts +15 -0
- package/core/audits/metrics/first-contentful-paint-3g.js +69 -0
- package/core/audits/metrics/first-contentful-paint.d.ts +25 -0
- package/core/audits/metrics/first-contentful-paint.js +85 -0
- package/core/audits/metrics/first-meaningful-paint.d.ts +28 -0
- package/core/audits/metrics/first-meaningful-paint.js +89 -0
- package/core/audits/metrics/interactive.d.ts +31 -0
- package/core/audits/metrics/interactive.js +93 -0
- package/core/audits/metrics/largest-contentful-paint.d.ts +25 -0
- package/core/audits/metrics/largest-contentful-paint.js +94 -0
- package/core/audits/metrics/max-potential-fid.d.ts +23 -0
- package/core/audits/metrics/max-potential-fid.js +77 -0
- package/core/audits/metrics/speed-index.d.ts +27 -0
- package/core/audits/metrics/speed-index.js +88 -0
- package/core/audits/metrics/total-blocking-time.d.ts +32 -0
- package/core/audits/metrics/total-blocking-time.js +118 -0
- package/core/audits/metrics.d.ts +11 -0
- package/core/audits/metrics.js +73 -0
- package/core/audits/multi-check-audit.d.ts +28 -0
- package/core/audits/multi-check-audit.js +82 -0
- package/core/audits/network-requests.d.ts +11 -0
- package/core/audits/network-requests.js +129 -0
- package/core/audits/network-rtt.d.ts +15 -0
- package/core/audits/network-rtt.js +89 -0
- package/core/audits/network-server-latency.d.ts +15 -0
- package/core/audits/network-server-latency.js +88 -0
- package/core/audits/no-unload-listeners.d.ts +16 -0
- package/core/audits/no-unload-listeners.js +86 -0
- package/core/audits/non-composited-animations.d.ts +21 -0
- package/core/audits/non-composited-animations.js +205 -0
- package/core/audits/oopif-iframe-test-audit.d.ts +14 -0
- package/core/audits/oopif-iframe-test-audit.js +29 -0
- package/core/audits/performance-budget.d.ts +39 -0
- package/core/audits/performance-budget.js +154 -0
- package/core/audits/predictive-perf.d.ts +11 -0
- package/core/audits/predictive-perf.js +100 -0
- package/core/audits/preload-fonts.d.ts +29 -0
- package/core/audits/preload-fonts.js +106 -0
- package/core/audits/prioritize-lcp-image.d.ts +76 -0
- package/core/audits/prioritize-lcp-image.js +290 -0
- package/core/audits/redirects.d.ts +33 -0
- package/core/audits/redirects.js +164 -0
- package/core/audits/resource-summary.d.ts +16 -0
- package/core/audits/resource-summary.js +104 -0
- package/core/audits/screenshot-thumbnails.d.ts +33 -0
- package/core/audits/screenshot-thumbnails.js +168 -0
- package/core/audits/script-elements-test-audit.d.ts +14 -0
- package/core/audits/script-elements-test-audit.js +29 -0
- package/core/audits/script-treemap-data.d.ts +42 -0
- package/core/audits/script-treemap-data.js +287 -0
- package/core/audits/seo/canonical.d.ts +51 -0
- package/core/audits/seo/canonical.js +217 -0
- package/core/audits/seo/crawlable-anchors.d.ts +16 -0
- package/core/audits/seo/crawlable-anchors.js +93 -0
- package/core/audits/seo/font-size.d.ts +24 -0
- package/core/audits/seo/font-size.js +344 -0
- package/core/audits/seo/hreflang.d.ts +28 -0
- package/core/audits/seo/hreflang.js +148 -0
- package/core/audits/seo/http-status-code.d.ts +16 -0
- package/core/audits/seo/http-status-code.js +68 -0
- package/core/audits/seo/is-crawlable.d.ts +38 -0
- package/core/audits/seo/is-crawlable.js +223 -0
- package/core/audits/seo/link-text.d.ts +16 -0
- package/core/audits/seo/link-text.js +160 -0
- package/core/audits/seo/manual/structured-data.d.ts +12 -0
- package/core/audits/seo/manual/structured-data.js +37 -0
- package/core/audits/seo/meta-description.d.ts +16 -0
- package/core/audits/seo/meta-description.js +66 -0
- package/core/audits/seo/plugins.d.ts +15 -0
- package/core/audits/seo/plugins.js +150 -0
- package/core/audits/seo/robots-txt.d.ts +18 -0
- package/core/audits/seo/robots-txt.js +255 -0
- package/core/audits/seo/tap-targets.d.ts +50 -0
- package/core/audits/seo/tap-targets.js +352 -0
- package/core/audits/server-response-time.d.ts +21 -0
- package/core/audits/server-response-time.js +90 -0
- package/core/audits/service-worker.d.ts +46 -0
- package/core/audits/service-worker.js +182 -0
- package/core/audits/splash-screen.d.ts +37 -0
- package/core/audits/splash-screen.js +98 -0
- package/core/audits/themed-omnibox.d.ts +42 -0
- package/core/audits/themed-omnibox.js +102 -0
- package/core/audits/third-party-facades.d.ts +42 -0
- package/core/audits/third-party-facades.js +224 -0
- package/core/audits/third-party-summary.d.ts +59 -0
- package/core/audits/third-party-summary.js +257 -0
- package/core/audits/timing-budget.d.ts +43 -0
- package/core/audits/timing-budget.js +174 -0
- package/core/audits/unsized-images.d.ts +39 -0
- package/core/audits/unsized-images.js +162 -0
- package/core/audits/user-timings.d.ts +43 -0
- package/core/audits/user-timings.js +118 -0
- package/core/audits/uses-rel-preconnect.d.ts +37 -0
- package/core/audits/uses-rel-preconnect.js +253 -0
- package/core/audits/uses-rel-preload.d.ts +61 -0
- package/core/audits/uses-rel-preload.js +269 -0
- package/core/audits/valid-source-maps.d.ts +29 -0
- package/core/audits/valid-source-maps.js +160 -0
- package/core/audits/viewport.d.ts +17 -0
- package/core/audits/viewport.js +64 -0
- package/core/audits/violation-audit.d.ts +14 -0
- package/core/audits/violation-audit.js +49 -0
- package/core/audits/work-during-interaction.d.ts +82 -0
- package/core/audits/work-during-interaction.js +279 -0
- package/core/computed/computed-artifact.d.ts +15 -0
- package/core/computed/computed-artifact.js +62 -0
- package/core/computed/critical-request-chains.d.ts +40 -0
- package/core/computed/critical-request-chains.js +143 -0
- package/core/computed/entity-classification.d.ts +29 -0
- package/core/computed/entity-classification.js +103 -0
- package/core/computed/image-records.d.ts +24 -0
- package/core/computed/image-records.js +64 -0
- package/core/computed/js-bundles.d.ts +13 -0
- package/core/computed/js-bundles.js +120 -0
- package/core/computed/load-simulator.d.ts +28 -0
- package/core/computed/load-simulator.js +92 -0
- package/core/computed/main-resource.d.ts +25 -0
- package/core/computed/main-resource.js +35 -0
- package/core/computed/main-thread-tasks.d.ts +15 -0
- package/core/computed/main-thread-tasks.js +25 -0
- package/core/computed/manifest-values.d.ts +24 -0
- package/core/computed/manifest-values.js +136 -0
- package/core/computed/metrics/cumulative-layout-shift.d.ts +53 -0
- package/core/computed/metrics/cumulative-layout-shift.js +125 -0
- package/core/computed/metrics/first-contentful-paint-all-frames.d.ts +19 -0
- package/core/computed/metrics/first-contentful-paint-all-frames.js +37 -0
- package/core/computed/metrics/first-contentful-paint.d.ts +15 -0
- package/core/computed/metrics/first-contentful-paint.js +40 -0
- package/core/computed/metrics/first-meaningful-paint.d.ts +15 -0
- package/core/computed/metrics/first-meaningful-paint.js +44 -0
- package/core/computed/metrics/interactive.d.ts +61 -0
- package/core/computed/metrics/interactive.js +192 -0
- package/core/computed/metrics/lantern-first-contentful-paint.d.ts +48 -0
- package/core/computed/metrics/lantern-first-contentful-paint.js +204 -0
- package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +12 -0
- package/core/computed/metrics/lantern-first-meaningful-paint.js +83 -0
- package/core/computed/metrics/lantern-interactive.d.ts +31 -0
- package/core/computed/metrics/lantern-interactive.js +113 -0
- package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +25 -0
- package/core/computed/metrics/lantern-largest-contentful-paint.js +111 -0
- package/core/computed/metrics/lantern-max-potential-fid.d.ts +30 -0
- package/core/computed/metrics/lantern-max-potential-fid.js +93 -0
- package/core/computed/metrics/lantern-metric.d.ts +78 -0
- package/core/computed/metrics/lantern-metric.js +162 -0
- package/core/computed/metrics/lantern-speed-index.d.ts +38 -0
- package/core/computed/metrics/lantern-speed-index.js +150 -0
- package/core/computed/metrics/lantern-total-blocking-time.d.ts +31 -0
- package/core/computed/metrics/lantern-total-blocking-time.js +126 -0
- package/core/computed/metrics/largest-contentful-paint-all-frames.d.ts +20 -0
- package/core/computed/metrics/largest-contentful-paint-all-frames.js +45 -0
- package/core/computed/metrics/largest-contentful-paint.d.ts +15 -0
- package/core/computed/metrics/largest-contentful-paint.js +52 -0
- package/core/computed/metrics/max-potential-fid.d.ts +15 -0
- package/core/computed/metrics/max-potential-fid.js +45 -0
- package/core/computed/metrics/metric.d.ts +38 -0
- package/core/computed/metrics/metric.js +100 -0
- package/core/computed/metrics/navigation-metric.d.ts +16 -0
- package/core/computed/metrics/navigation-metric.js +46 -0
- package/core/computed/metrics/responsiveness.d.ts +87 -0
- package/core/computed/metrics/responsiveness.js +160 -0
- package/core/computed/metrics/speed-index.d.ts +10 -0
- package/core/computed/metrics/speed-index.js +40 -0
- package/core/computed/metrics/tbt-utils.d.ts +18 -0
- package/core/computed/metrics/tbt-utils.js +56 -0
- package/core/computed/metrics/timing-summary.d.ts +46 -0
- package/core/computed/metrics/timing-summary.js +157 -0
- package/core/computed/metrics/total-blocking-time.d.ts +23 -0
- package/core/computed/metrics/total-blocking-time.js +74 -0
- package/core/computed/module-duplication.d.ts +37 -0
- package/core/computed/module-duplication.js +138 -0
- package/core/computed/network-analysis.d.ts +20 -0
- package/core/computed/network-analysis.js +64 -0
- package/core/computed/network-records.d.ts +15 -0
- package/core/computed/network-records.js +22 -0
- package/core/computed/page-dependency-graph.d.ts +88 -0
- package/core/computed/page-dependency-graph.js +516 -0
- package/core/computed/processed-navigation.d.ts +21 -0
- package/core/computed/processed-navigation.js +37 -0
- package/core/computed/processed-trace.d.ts +14 -0
- package/core/computed/processed-trace.js +21 -0
- package/core/computed/resource-summary.d.ts +46 -0
- package/core/computed/resource-summary.js +118 -0
- package/core/computed/screenshots.d.ts +20 -0
- package/core/computed/screenshots.js +29 -0
- package/core/computed/speedline.d.ts +15 -0
- package/core/computed/speedline.js +55 -0
- package/core/computed/unused-css.d.ts +62 -0
- package/core/computed/unused-css.js +153 -0
- package/core/computed/unused-javascript-summary.d.ts +71 -0
- package/core/computed/unused-javascript-summary.js +155 -0
- package/core/computed/user-timings.d.ts +31 -0
- package/core/computed/user-timings.js +83 -0
- package/core/computed/viewport-meta.d.ts +35 -0
- package/core/computed/viewport-meta.js +56 -0
- package/core/config/budget.d.ts +74 -0
- package/core/config/budget.js +340 -0
- package/core/config/config-helpers.d.ts +94 -0
- package/core/config/config-helpers.js +630 -0
- package/core/config/config-plugin.d.ts +46 -0
- package/core/config/config-plugin.js +250 -0
- package/core/config/config.d.ts +28 -0
- package/core/config/config.js +344 -0
- package/core/config/constants.d.ts +86 -0
- package/core/config/constants.js +175 -0
- package/core/config/default-config.d.ts +5 -0
- package/core/config/default-config.js +637 -0
- package/core/config/desktop-config.d.ts +5 -0
- package/core/config/desktop-config.js +21 -0
- package/core/config/experimental-config.d.ts +13 -0
- package/core/config/experimental-config.js +29 -0
- package/core/config/filters.d.ts +83 -0
- package/core/config/filters.js +344 -0
- package/core/config/full-config.d.ts +9 -0
- package/core/config/full-config.js +13 -0
- package/core/config/lr-desktop-config.d.ts +4 -0
- package/core/config/lr-desktop-config.js +28 -0
- package/core/config/lr-mobile-config.d.ts +9 -0
- package/core/config/lr-mobile-config.js +33 -0
- package/core/config/metrics-to-audits.d.ts +19 -0
- package/core/config/metrics-to-audits.js +61 -0
- package/core/config/perf-config.d.ts +9 -0
- package/core/config/perf-config.js +16 -0
- package/core/config/validation.d.ts +77 -0
- package/core/config/validation.js +312 -0
- package/core/gather/base-artifacts.d.ts +16 -0
- package/core/gather/base-artifacts.js +95 -0
- package/core/gather/base-gatherer.d.ts +72 -0
- package/core/gather/base-gatherer.js +108 -0
- package/core/gather/driver/dom.d.ts +20 -0
- package/core/gather/driver/dom.js +57 -0
- package/core/gather/driver/environment.d.ts +35 -0
- package/core/gather/driver/environment.js +105 -0
- package/core/gather/driver/execution-context.d.ts +101 -0
- package/core/gather/driver/execution-context.js +262 -0
- package/core/gather/driver/navigation.d.ts +33 -0
- package/core/gather/driver/navigation.js +182 -0
- package/core/gather/driver/network-monitor.d.ts +82 -0
- package/core/gather/driver/network-monitor.js +260 -0
- package/core/gather/driver/network.d.ts +10 -0
- package/core/gather/driver/network.js +27 -0
- package/core/gather/driver/prepare.d.ts +52 -0
- package/core/gather/driver/prepare.js +244 -0
- package/core/gather/driver/service-workers.d.ts +16 -0
- package/core/gather/driver/service-workers.js +52 -0
- package/core/gather/driver/storage.d.ts +24 -0
- package/core/gather/driver/storage.js +149 -0
- package/core/gather/driver/target-manager.d.ts +61 -0
- package/core/gather/driver/target-manager.js +209 -0
- package/core/gather/driver/wait-for-condition.d.ts +116 -0
- package/core/gather/driver/wait-for-condition.js +544 -0
- package/core/gather/driver.d.ts +30 -0
- package/core/gather/driver.js +92 -0
- package/core/gather/fetcher.d.ts +63 -0
- package/core/gather/fetcher.js +143 -0
- package/core/gather/gatherers/accessibility.d.ts +23 -0
- package/core/gather/gatherers/accessibility.js +199 -0
- package/core/gather/gatherers/anchor-elements.d.ts +10 -0
- package/core/gather/gatherers/anchor-elements.js +132 -0
- package/core/gather/gatherers/bf-cache-failures.d.ts +43 -0
- package/core/gather/gatherers/bf-cache-failures.js +179 -0
- package/core/gather/gatherers/cache-contents.d.ts +11 -0
- package/core/gather/gatherers/cache-contents.js +58 -0
- package/core/gather/gatherers/console-messages.d.ts +39 -0
- package/core/gather/gatherers/console-messages.js +174 -0
- package/core/gather/gatherers/css-usage.d.ts +39 -0
- package/core/gather/gatherers/css-usage.js +150 -0
- package/core/gather/gatherers/devtools-log-compat.d.ts +13 -0
- package/core/gather/gatherers/devtools-log-compat.js +35 -0
- package/core/gather/gatherers/devtools-log.d.ts +48 -0
- package/core/gather/gatherers/devtools-log.js +115 -0
- package/core/gather/gatherers/dobetterweb/doctype.d.ts +10 -0
- package/core/gather/gatherers/dobetterweb/doctype.js +47 -0
- package/core/gather/gatherers/dobetterweb/domstats.d.ts +10 -0
- package/core/gather/gatherers/dobetterweb/domstats.js +102 -0
- package/core/gather/gatherers/dobetterweb/optimized-images.d.ts +61 -0
- package/core/gather/gatherers/dobetterweb/optimized-images.js +193 -0
- package/core/gather/gatherers/dobetterweb/response-compression.d.ts +30 -0
- package/core/gather/gatherers/dobetterweb/response-compression.js +155 -0
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +53 -0
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +237 -0
- package/core/gather/gatherers/full-page-screenshot.d.ts +35 -0
- package/core/gather/gatherers/full-page-screenshot.js +258 -0
- package/core/gather/gatherers/gatherer.d.ts +46 -0
- package/core/gather/gatherers/gatherer.js +58 -0
- package/core/gather/gatherers/global-listeners.d.ts +27 -0
- package/core/gather/gatherers/global-listeners.js +91 -0
- package/core/gather/gatherers/iframe-elements.d.ts +11 -0
- package/core/gather/gatherers/iframe-elements.js +67 -0
- package/core/gather/gatherers/image-elements.d.ts +35 -0
- package/core/gather/gatherers/image-elements.js +378 -0
- package/core/gather/gatherers/inputs.d.ts +10 -0
- package/core/gather/gatherers/inputs.js +118 -0
- package/core/gather/gatherers/inspector-issues.d.ts +38 -0
- package/core/gather/gatherers/inspector-issues.js +126 -0
- package/core/gather/gatherers/installability-errors.d.ts +17 -0
- package/core/gather/gatherers/installability-errors.js +56 -0
- package/core/gather/gatherers/js-usage.d.ts +22 -0
- package/core/gather/gatherers/js-usage.js +74 -0
- package/core/gather/gatherers/link-elements.d.ts +39 -0
- package/core/gather/gatherers/link-elements.js +181 -0
- package/core/gather/gatherers/main-document-content.d.ts +27 -0
- package/core/gather/gatherers/main-document-content.js +53 -0
- package/core/gather/gatherers/meta-elements.d.ts +10 -0
- package/core/gather/gatherers/meta-elements.js +67 -0
- package/core/gather/gatherers/network-user-agent.d.ts +18 -0
- package/core/gather/gatherers/network-user-agent.js +41 -0
- package/core/gather/gatherers/script-elements.d.ts +26 -0
- package/core/gather/gatherers/script-elements.js +110 -0
- package/core/gather/gatherers/scripts.d.ts +27 -0
- package/core/gather/gatherers/scripts.js +139 -0
- package/core/gather/gatherers/seo/embedded-content.d.ts +10 -0
- package/core/gather/gatherers/seo/embedded-content.js +63 -0
- package/core/gather/gatherers/seo/font-size.d.ts +131 -0
- package/core/gather/gatherers/seo/font-size.js +339 -0
- package/core/gather/gatherers/seo/robots-txt.d.ts +10 -0
- package/core/gather/gatherers/seo/robots-txt.js +27 -0
- package/core/gather/gatherers/seo/tap-targets.d.ts +21 -0
- package/core/gather/gatherers/seo/tap-targets.js +389 -0
- package/core/gather/gatherers/service-worker.d.ts +16 -0
- package/core/gather/gatherers/service-worker.js +44 -0
- package/core/gather/gatherers/source-maps.d.ts +50 -0
- package/core/gather/gatherers/source-maps.js +155 -0
- package/core/gather/gatherers/stacks.d.ts +38 -0
- package/core/gather/gatherers/stacks.js +137 -0
- package/core/gather/gatherers/trace-compat.d.ts +13 -0
- package/core/gather/gatherers/trace-compat.js +35 -0
- package/core/gather/gatherers/trace-elements.d.ts +83 -0
- package/core/gather/gatherers/trace-elements.js +349 -0
- package/core/gather/gatherers/trace.d.ts +23 -0
- package/core/gather/gatherers/trace.js +130 -0
- package/core/gather/gatherers/viewport-dimensions.d.ts +10 -0
- package/core/gather/gatherers/viewport-dimensions.js +57 -0
- package/core/gather/gatherers/web-app-manifest.d.ts +32 -0
- package/core/gather/gatherers/web-app-manifest.js +106 -0
- package/core/gather/navigation-runner.d.ts +94 -0
- package/core/gather/navigation-runner.js +386 -0
- package/core/gather/runner-helpers.d.ts +39 -0
- package/core/gather/runner-helpers.js +159 -0
- package/core/gather/session.d.ts +50 -0
- package/core/gather/session.js +114 -0
- package/core/gather/snapshot-runner.d.ts +10 -0
- package/core/gather/snapshot-runner.js +67 -0
- package/core/gather/timespan-runner.d.ts +12 -0
- package/core/gather/timespan-runner.js +85 -0
- package/core/index.cjs +21 -0
- package/core/index.d.cts +4 -0
- package/core/index.d.ts +88 -0
- package/core/index.js +172 -0
- package/core/legacy/config/config.d.ts +107 -0
- package/core/legacy/config/config.js +566 -0
- package/core/legacy/config/legacy-default-config.d.ts +4 -0
- package/core/legacy/config/legacy-default-config.js +87 -0
- package/core/legacy/gather/connections/connection.d.ts +75 -0
- package/core/legacy/gather/connections/connection.js +181 -0
- package/core/legacy/gather/connections/cri.d.ts +27 -0
- package/core/legacy/gather/connections/cri.js +162 -0
- package/core/legacy/gather/connections/raw.d.ts +20 -0
- package/core/legacy/gather/connections/raw.js +57 -0
- package/core/legacy/gather/driver.d.ts +215 -0
- package/core/legacy/gather/driver.js +469 -0
- package/core/legacy/gather/gather-runner.d.ts +166 -0
- package/core/legacy/gather/gather-runner.js +590 -0
- package/core/lib/arbitrary-equality-map.d.ts +47 -0
- package/core/lib/arbitrary-equality-map.js +80 -0
- package/core/lib/asset-saver.d.ts +101 -0
- package/core/lib/asset-saver.js +451 -0
- package/core/lib/axe.d.ts +2 -0
- package/core/lib/axe.js +17 -0
- package/core/lib/bf-cache-strings.d.ts +121 -0
- package/core/lib/bf-cache-strings.js +661 -0
- package/core/lib/cdt/Common.d.ts +3 -0
- package/core/lib/cdt/Common.js +12 -0
- package/core/lib/cdt/Platform.d.ts +26 -0
- package/core/lib/cdt/Platform.js +57 -0
- package/core/lib/cdt/SDK.d.ts +2 -0
- package/core/lib/cdt/SDK.js +28 -0
- package/core/lib/cdt/generated/ParsedURL.d.ts +28 -0
- package/core/lib/cdt/generated/ParsedURL.js +178 -0
- package/core/lib/cdt/generated/SourceMap.d.ts +100 -0
- package/core/lib/cdt/generated/SourceMap.js +527 -0
- package/core/lib/cdt/package.json +4 -0
- package/core/lib/csp-evaluator.d.ts +41 -0
- package/core/lib/csp-evaluator.js +170 -0
- package/core/lib/dependency-graph/base-node.d.ts +161 -0
- package/core/lib/dependency-graph/base-node.js +364 -0
- package/core/lib/dependency-graph/cpu-node.d.ts +34 -0
- package/core/lib/dependency-graph/cpu-node.js +86 -0
- package/core/lib/dependency-graph/network-node.d.ts +43 -0
- package/core/lib/dependency-graph/network-node.js +99 -0
- package/core/lib/dependency-graph/simulator/connection-pool.d.ts +58 -0
- package/core/lib/dependency-graph/simulator/connection-pool.js +171 -0
- package/core/lib/dependency-graph/simulator/dns-cache.d.ts +42 -0
- package/core/lib/dependency-graph/simulator/dns-cache.js +74 -0
- package/core/lib/dependency-graph/simulator/network-analyzer.d.ts +166 -0
- package/core/lib/dependency-graph/simulator/network-analyzer.js +479 -0
- package/core/lib/dependency-graph/simulator/simulator-timing-map.d.ts +153 -0
- package/core/lib/dependency-graph/simulator/simulator-timing-map.js +220 -0
- package/core/lib/dependency-graph/simulator/simulator.d.ts +154 -0
- package/core/lib/dependency-graph/simulator/simulator.js +540 -0
- package/core/lib/dependency-graph/simulator/tcp-connection.d.ts +89 -0
- package/core/lib/dependency-graph/simulator/tcp-connection.js +230 -0
- package/core/lib/deprecations-strings.d.ts +66 -0
- package/core/lib/deprecations-strings.js +654 -0
- package/core/lib/emulation.d.ts +43 -0
- package/core/lib/emulation.js +163 -0
- package/core/lib/i18n/README.md +290 -0
- package/core/lib/i18n/i18n.d.ts +81 -0
- package/core/lib/i18n/i18n.js +229 -0
- package/core/lib/icons.d.ts +22 -0
- package/core/lib/icons.js +82 -0
- package/core/lib/lantern-trace-saver.d.ts +20 -0
- package/core/lib/lantern-trace-saver.js +260 -0
- package/core/lib/lh-env.d.ts +2 -0
- package/core/lib/lh-env.js +15 -0
- package/core/lib/lh-error.d.ts +328 -0
- package/core/lib/lh-error.js +425 -0
- package/core/lib/lh-trace-processor.d.ts +5 -0
- package/core/lib/lh-trace-processor.js +45 -0
- package/core/lib/lighthouse-compatibility.d.ts +8 -0
- package/core/lib/lighthouse-compatibility.js +139 -0
- package/core/lib/manifest-parser.d.ts +186 -0
- package/core/lib/manifest-parser.js +495 -0
- package/core/lib/median-run.d.ts +17 -0
- package/core/lib/median-run.js +92 -0
- package/core/lib/minification-estimator.d.ts +9 -0
- package/core/lib/minification-estimator.js +192 -0
- package/core/lib/minify-devtoolslog.d.ts +6 -0
- package/core/lib/minify-devtoolslog.js +86 -0
- package/core/lib/navigation-error.d.ts +39 -0
- package/core/lib/navigation-error.js +175 -0
- package/core/lib/network-recorder.d.ts +115 -0
- package/core/lib/network-recorder.js +341 -0
- package/core/lib/network-request.d.ts +247 -0
- package/core/lib/network-request.js +590 -0
- package/core/lib/page-functions.d.ts +149 -0
- package/core/lib/page-functions.js +544 -0
- package/core/lib/proto-preprocessor.d.ts +13 -0
- package/core/lib/proto-preprocessor.js +133 -0
- package/core/lib/rect-helpers.d.ts +100 -0
- package/core/lib/rect-helpers.js +251 -0
- package/core/lib/script-helpers.d.ts +17 -0
- package/core/lib/script-helpers.js +31 -0
- package/core/lib/sentry.d.ts +32 -0
- package/core/lib/sentry.js +143 -0
- package/core/lib/stack-packs.d.ts +16 -0
- package/core/lib/stack-packs.js +126 -0
- package/core/lib/statistics.d.ts +28 -0
- package/core/lib/statistics.js +103 -0
- package/core/lib/tappable-rects.d.ts +8 -0
- package/core/lib/tappable-rects.js +98 -0
- package/core/lib/third-party-web.d.ts +32 -0
- package/core/lib/third-party-web.js +52 -0
- package/core/lib/timing-trace-saver.d.ts +21 -0
- package/core/lib/timing-trace-saver.js +96 -0
- package/core/lib/tracehouse/cpu-profile-model.d.ts +228 -0
- package/core/lib/tracehouse/cpu-profile-model.js +591 -0
- package/core/lib/tracehouse/main-thread-tasks.d.ts +162 -0
- package/core/lib/tracehouse/main-thread-tasks.js +688 -0
- package/core/lib/tracehouse/task-groups.d.ts +66 -0
- package/core/lib/tracehouse/task-groups.js +111 -0
- package/core/lib/tracehouse/task-summary.d.ts +18 -0
- package/core/lib/tracehouse/task-summary.js +86 -0
- package/core/lib/tracehouse/trace-processor.d.ts +284 -0
- package/core/lib/tracehouse/trace-processor.js +1016 -0
- package/core/lib/traces/metric-trace-events.d.ts +65 -0
- package/core/lib/traces/metric-trace-events.js +200 -0
- package/core/lib/url-utils.d.ts +100 -0
- package/core/lib/url-utils.js +266 -0
- package/core/runner.d.ts +114 -0
- package/core/runner.js +563 -0
- package/core/scoring.d.ts +26 -0
- package/core/scoring.js +91 -0
- package/core/scripts/download-chrome.sh +36 -0
- package/core/scripts/manual-chrome-launcher.js +50 -0
- package/core/user-flow.d.ts +106 -0
- package/core/user-flow.js +354 -0
- package/dist/report/bundle.esm.js +1485 -1281
- package/dist/report/flow.js +37 -25
- package/dist/report/standalone.js +35 -19
- package/eslint-local-rules.cjs +106 -0
- package/eslint-local-rules.d.cts +6 -0
- package/esm-utils.d.ts +14 -0
- package/esm-utils.js +38 -0
- package/flow-report/api.d.ts +7 -0
- package/flow-report/assets/standalone-flow-template.html +1 -1
- package/flow-report/clients/standalone.d.ts +7 -0
- package/flow-report/src/app.d.ts +11 -0
- package/flow-report/src/app.tsx +1 -1
- package/flow-report/src/common.d.ts +20 -0
- package/flow-report/src/common.tsx +3 -3
- package/flow-report/src/header.d.ts +10 -0
- package/flow-report/src/header.tsx +1 -1
- package/flow-report/src/help-dialog.d.ts +10 -0
- package/flow-report/src/i18n/i18n.d.ts +212 -0
- package/flow-report/src/i18n/i18n.tsx +24 -19
- package/flow-report/src/i18n/localized-strings.d.ts +9 -0
- package/flow-report/src/i18n/ui-strings.d.ts +48 -0
- package/flow-report/src/icons.d.ts +18 -0
- package/flow-report/src/sidebar/flow.d.ts +8 -0
- package/flow-report/src/sidebar/sidebar.d.ts +17 -0
- package/flow-report/src/sidebar/sidebar.tsx +7 -4
- package/flow-report/src/summary/category.d.ts +19 -0
- package/flow-report/src/summary/category.tsx +10 -10
- package/flow-report/src/summary/summary.d.ts +18 -0
- package/flow-report/src/summary/summary.tsx +4 -4
- package/flow-report/src/topbar.d.ts +15 -0
- package/flow-report/src/topbar.tsx +14 -30
- package/flow-report/src/util.d.ts +30 -0
- package/flow-report/src/util.ts +2 -13
- package/flow-report/src/wrappers/category-score.d.ts +12 -0
- package/flow-report/src/wrappers/markdown.d.ts +10 -0
- package/flow-report/src/wrappers/report.d.ts +10 -0
- package/flow-report/src/wrappers/report.tsx +1 -1
- package/flow-report/src/wrappers/styles.d.ts +8 -0
- package/flow-report/test/app-test.d.ts +7 -0
- package/flow-report/test/common-test.d.ts +7 -0
- package/flow-report/test/common-test.tsx +11 -12
- package/flow-report/test/flow-report-pptr-test.d.ts +7 -0
- package/flow-report/test/flow-report-pptr-test.ts +21 -8
- package/flow-report/test/header-test.d.ts +7 -0
- package/flow-report/test/run-flow-report-tests.sh +20 -0
- package/flow-report/test/sample-flow.d.ts +7 -0
- package/flow-report/test/sample-flow.ts +1 -1
- package/flow-report/test/setup/env-setup.d.ts +11 -0
- package/flow-report/test/setup/env-setup.ts +47 -24
- package/flow-report/test/sidebar/flow-test.d.ts +7 -0
- package/flow-report/test/sidebar/sidebar-test.d.ts +7 -0
- package/flow-report/test/sidebar/sidebar-test.tsx +30 -3
- package/flow-report/test/summary/category-test.d.ts +7 -0
- package/flow-report/test/summary/summary-test.d.ts +7 -0
- package/flow-report/test/summary/summary-test.tsx +1 -1
- package/flow-report/test/topbar-test.d.ts +7 -0
- package/flow-report/test/topbar-test.tsx +11 -12
- package/flow-report/test/util-test.d.ts +7 -0
- package/flow-report/test/util-test.tsx +3 -3
- package/flow-report/test/wrappers/category-score-test.d.ts +7 -0
- package/flow-report/test/wrappers/markdown-test.d.ts +7 -0
- package/flow-report/tsconfig.json +5 -3
- package/flow-report/types/flow-report.d.ts +8 -1
- package/package.json +86 -80
- package/readme.md +27 -40
- package/report/README.md +8 -9
- package/report/assets/standalone-template.html +1 -1
- package/report/assets/styles.css +35 -17
- package/report/assets/templates.html +14 -37
- package/report/clients/bundle.d.ts +6 -0
- package/report/clients/bundle.js +1 -2
- package/report/clients/standalone.d.ts +2 -0
- package/report/clients/standalone.js +0 -1
- package/report/generator/file-namer.d.ts +35 -0
- package/report/generator/file-namer.js +7 -3
- package/report/generator/flow-report-assets.d.ts +9 -0
- package/report/generator/flow-report-assets.js +10 -6
- package/report/generator/report-assets.d.ts +8 -0
- package/report/generator/report-assets.js +8 -5
- package/report/generator/report-generator.d.ts +59 -0
- package/report/generator/report-generator.js +76 -34
- package/report/generator/tsconfig.json +1 -2
- package/report/renderer/api.d.ts +31 -0
- package/report/renderer/api.js +0 -2
- package/report/renderer/category-renderer.d.ts +176 -0
- package/report/renderer/category-renderer.js +39 -41
- package/report/renderer/components.d.ts +10 -0
- package/report/renderer/components.js +364 -448
- package/report/renderer/crc-details-renderer.d.ts +73 -0
- package/report/renderer/crc-details-renderer.js +28 -30
- package/report/renderer/details-renderer.d.ts +141 -0
- package/report/renderer/details-renderer.js +44 -92
- package/report/renderer/dom.d.ts +125 -0
- package/report/renderer/dom.js +13 -10
- package/report/renderer/drop-down-menu.d.ts +70 -0
- package/report/renderer/drop-down-menu.js +0 -1
- package/report/renderer/element-screenshot-renderer.d.ts +79 -0
- package/report/renderer/element-screenshot-renderer.js +11 -12
- package/report/renderer/features-util.d.ts +13 -0
- package/report/renderer/features-util.js +0 -1
- package/report/renderer/i18n-formatter.d.ts +100 -0
- package/report/renderer/i18n-formatter.js +263 -0
- package/report/renderer/logger.d.ts +47 -0
- package/report/renderer/logger.js +0 -1
- package/report/renderer/open-tab.d.ts +19 -0
- package/report/renderer/open-tab.js +4 -4
- package/report/renderer/performance-category-renderer.d.ts +56 -0
- package/report/renderer/performance-category-renderer.js +29 -26
- package/report/renderer/pwa-category-renderer.d.ts +55 -0
- package/report/renderer/pwa-category-renderer.js +8 -8
- package/report/renderer/report-globals.d.ts +21 -0
- package/report/renderer/report-globals.js +49 -0
- package/report/renderer/report-renderer.d.ts +57 -0
- package/report/renderer/report-renderer.js +54 -52
- package/report/renderer/report-ui-features.d.ts +71 -0
- package/report/renderer/report-ui-features.js +44 -39
- package/report/renderer/report-utils.d.ts +129 -0
- package/report/renderer/report-utils.js +448 -0
- package/report/renderer/snippet-renderer.d.ts +85 -0
- package/report/renderer/snippet-renderer.js +5 -5
- package/report/renderer/swap-locale-feature.d.ts +31 -0
- package/report/renderer/swap-locale-feature.js +2 -3
- package/report/renderer/text-encoding.d.ts +26 -0
- package/report/renderer/text-encoding.js +0 -1
- package/report/renderer/topbar-features.d.ts +82 -0
- package/report/renderer/topbar-features.js +2 -2
- package/report/test/clients/bundle-test.js +5 -10
- package/report/test/generator/file-namer-test.js +3 -4
- package/report/test/generator/report-generator-test.js +49 -24
- package/report/test/renderer/category-renderer-test.js +19 -15
- package/report/test/renderer/components-test.js +36 -37
- package/report/test/renderer/crc-details-renderer-test.js +11 -10
- package/report/test/renderer/details-renderer-test.js +91 -41
- package/report/test/renderer/dom-test.js +38 -14
- package/report/test/renderer/element-screenshot-renderer-test.js +14 -12
- package/report/test/renderer/i18n-formatter-test.js +179 -0
- package/report/test/renderer/performance-category-renderer-test.js +43 -31
- package/report/test/renderer/pwa-category-renderer-test.js +17 -13
- package/report/test/renderer/report-renderer-axe-test.js +12 -15
- package/report/test/renderer/report-renderer-test.js +17 -15
- package/report/test/renderer/report-ui-features-test.js +304 -89
- package/report/test/renderer/report-utils-test.js +219 -0
- package/report/test/renderer/snippet-renderer-test.js +11 -10
- package/report/test/renderer/text-encoding-test.js +2 -5
- package/report/test-assets/faux-psi.d.ts +34 -0
- package/report/tsconfig.json +1 -2
- package/report/types/augment-dom.d.ts +1 -1
- package/root.d.ts +3 -0
- package/root.js +9 -19
- package/shared/localization/format.d.ts +106 -0
- package/shared/localization/format.js +27 -21
- package/shared/localization/i18n-module.d.ts +4 -0
- package/shared/localization/i18n-module.js +7 -3
- package/shared/localization/locales/ar-XB.json +1658 -1007
- package/shared/localization/locales/ar.json +1659 -1008
- package/shared/localization/locales/bg.json +1657 -1006
- package/shared/localization/locales/ca.json +1664 -1013
- package/shared/localization/locales/cs.json +1659 -1008
- package/shared/localization/locales/da.json +1658 -1007
- package/shared/localization/locales/de.json +1794 -1143
- package/shared/localization/locales/el.json +1657 -1006
- package/shared/localization/locales/en-GB.json +1658 -1007
- package/shared/localization/locales/en-US.json +1570 -1207
- package/shared/localization/locales/en-XA.json +1658 -1007
- package/shared/localization/locales/en-XL.json +1570 -1207
- package/shared/localization/locales/es-419.json +1660 -1009
- package/shared/localization/locales/es.json +1665 -1014
- package/shared/localization/locales/fi.json +1658 -1007
- package/shared/localization/locales/fil.json +1659 -1008
- package/shared/localization/locales/fr.json +1660 -1009
- package/shared/localization/locales/he.json +1678 -1027
- package/shared/localization/locales/hi.json +1661 -1010
- package/shared/localization/locales/hr.json +1658 -1007
- package/shared/localization/locales/hu.json +1657 -1006
- package/shared/localization/locales/id.json +1658 -1007
- package/shared/localization/locales/it.json +1678 -1027
- package/shared/localization/locales/ja.json +1657 -1006
- package/shared/localization/locales/ko.json +1658 -1007
- package/shared/localization/locales/lt.json +1658 -1007
- package/shared/localization/locales/lv.json +1658 -1007
- package/shared/localization/locales/nl.json +1659 -1008
- package/shared/localization/locales/no.json +1662 -1011
- package/shared/localization/locales/pl.json +1657 -1006
- package/shared/localization/locales/pt-PT.json +1678 -1027
- package/shared/localization/locales/pt.json +1659 -1008
- package/shared/localization/locales/ro.json +1657 -1006
- package/shared/localization/locales/ru.json +1657 -1006
- package/shared/localization/locales/sk.json +1657 -1006
- package/shared/localization/locales/sl.json +1657 -1006
- package/shared/localization/locales/sr-Latn.json +1658 -1007
- package/shared/localization/locales/sr.json +1658 -1007
- package/shared/localization/locales/sv.json +1664 -1013
- package/shared/localization/locales/ta.json +1658 -1007
- package/shared/localization/locales/te.json +1688 -1037
- package/shared/localization/locales/th.json +1666 -1015
- package/shared/localization/locales/tr.json +1658 -1007
- package/shared/localization/locales/uk.json +1657 -1006
- package/shared/localization/locales/vi.json +1659 -1008
- package/shared/localization/locales/zh-HK.json +1658 -1007
- package/shared/localization/locales/zh-TW.json +1661 -1010
- package/shared/localization/locales/zh.json +1658 -1007
- package/shared/localization/locales.d.ts +7 -0
- package/shared/localization/locales.js +54 -51
- package/shared/localization/swap-flow-locale.d.ts +6 -0
- package/shared/localization/swap-flow-locale.js +6 -3
- package/shared/localization/swap-locale.d.ts +39 -0
- package/shared/localization/swap-locale.js +7 -8
- package/shared/test/localization/format-test.js +73 -56
- package/shared/test/localization/locales-test.js +2 -4
- package/shared/test/localization/swap-locale-test.js +10 -11
- package/shared/test/util-test.js +214 -0
- package/shared/tsconfig.json +1 -2
- package/shared/type-verifiers.d.ts +23 -0
- package/shared/type-verifiers.js +1 -2
- package/shared/types/shared.d.ts +2 -2
- package/shared/util.d.ts +122 -0
- package/shared/util.js +332 -0
- package/third-party/axe/valid-langs.d.ts +9 -0
- package/third-party/axe/valid-langs.js +1 -1
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +5 -7
- package/third-party/chromium-synchronization/installability-errors-test.js +4 -8
- package/third-party/download-content-shell/README.md +1 -0
- package/third-party/download-content-shell/download-content-shell.js +12 -6
- package/third-party/download-content-shell/utils.js +8 -8
- package/tsconfig-base.json +6 -1
- package/tsconfig.json +84 -90
- package/types/artifacts.d.ts +135 -78
- package/types/audit.d.ts +9 -8
- package/types/config.d.ts +26 -38
- package/types/externs.d.ts +9 -101
- package/types/gatherer.d.ts +35 -27
- package/types/internal/cssstyle.d.ts +23 -0
- package/types/{enquirer.d.ts → internal/enquirer.d.ts} +0 -0
- package/types/{es-main.d.ts → internal/es-main.d.ts} +0 -0
- package/types/internal/global.d.ts +15 -0
- package/types/{http-link-header/index.d.ts → internal/http-link-header.d.ts} +0 -0
- package/types/{jsonlint-mod.d.ts → internal/jsonlint-mod.d.ts} +0 -0
- package/types/internal/lighthouse-logger.d.ts +34 -0
- package/types/internal/lookup-closest-locale.d.ts +13 -0
- package/types/{metaviewport-parser/index.d.ts → internal/metaviewport-parser.d.ts} +0 -0
- package/types/{node-fetch.d.ts → internal/node-fetch.d.ts} +0 -0
- package/types/internal/node.d.ts +44 -0
- package/types/{parse-cache-control/index.d.ts → internal/parse-cache-control.d.ts} +0 -0
- package/types/{pretty-json-stringify/index.d.ts → internal/pretty-json-stringify.d.ts} +0 -0
- package/types/internal/query-selector.d.ts +36 -0
- package/types/{rollup-plugin-postprocess.d.ts → internal/rollup-plugin-postprocess.d.ts} +0 -0
- package/types/internal/smokehouse.d.ts +83 -0
- package/types/internal/test.d.ts +53 -0
- package/types/lh.d.ts +81 -0
- package/types/lh.js +13 -0
- package/types/lhr/audit-details.d.ts +6 -53
- package/types/lhr/audit-result.d.ts +2 -2
- package/types/lhr/flow-result.d.ts +28 -0
- package/types/lhr/lhr.d.ts +53 -6
- package/types/lhr/settings.d.ts +18 -1
- package/types/lhr/treemap.d.ts +5 -4
- package/types/lhr/tsconfig.json +0 -2
- package/types/protocol.d.ts +32 -18
- package/types/puppeteer.d.ts +27 -0
- package/types/user-flow.d.ts +27 -6
- package/types/utility-types.d.ts +72 -0
- package/vercel.json +6 -0
- package/eslint-local-rules.js +0 -106
- package/flow-report/.eslintrc.cjs +0 -51
- package/flow-report/jest.config.js +0 -24
- package/flow-report/package.json +0 -4
- package/flow-report/test/setup/global-setup.ts +0 -11
- package/jest.config.js +0 -30
- package/lighthouse-cli/.eslintrc.cjs +0 -29
- package/lighthouse-cli/bin.js +0 -152
- package/lighthouse-cli/cli-flags.js +0 -530
- package/lighthouse-cli/commands/commands.js +0 -10
- package/lighthouse-cli/commands/list-audits.js +0 -16
- package/lighthouse-cli/commands/list-locales.js +0 -16
- package/lighthouse-cli/commands/list-trace-categories.js +0 -16
- package/lighthouse-cli/index.js +0 -14
- package/lighthouse-cli/package.json +0 -4
- package/lighthouse-cli/printer.js +0 -100
- package/lighthouse-cli/run.js +0 -286
- package/lighthouse-cli/sentry-prompt.js +0 -79
- package/lighthouse-cli/test/smokehouse/core-tests.js +0 -129
- package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +0 -42
- package/lighthouse-cli/test/smokehouse/frontends/lib.js +0 -49
- package/lighthouse-cli/test/smokehouse/frontends/node.js +0 -13
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -262
- package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +0 -26
- package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +0 -126
- package/lighthouse-cli/test/smokehouse/lib/local-console.js +0 -51
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +0 -68
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +0 -151
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +0 -106
- package/lighthouse-cli/test/smokehouse/readme.md +0 -204
- package/lighthouse-cli/test/smokehouse/report-assert.js +0 -493
- package/lighthouse-cli/test/smokehouse/smokehouse.js +0 -284
- package/lighthouse-cli/test/smokehouse/version-check-test.js +0 -46
- package/lighthouse-cli/test/smokehouse/version-check.js +0 -49
- package/lighthouse-core/audits/accessibility/accesskeys.js +0 -45
- package/lighthouse-core/audits/accessibility/aria-allowed-attr.js +0 -45
- package/lighthouse-core/audits/accessibility/aria-command-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-hidden-body.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-hidden-focus.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-input-field-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-meter-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-required-attr.js +0 -44
- package/lighthouse-core/audits/accessibility/aria-required-children.js +0 -48
- package/lighthouse-core/audits/accessibility/aria-required-parent.js +0 -46
- package/lighthouse-core/audits/accessibility/aria-roles.js +0 -45
- package/lighthouse-core/audits/accessibility/aria-toggle-field-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +0 -43
- package/lighthouse-core/audits/accessibility/aria-valid-attr-value.js +0 -45
- package/lighthouse-core/audits/accessibility/aria-valid-attr.js +0 -45
- package/lighthouse-core/audits/accessibility/axe-audit.js +0 -114
- package/lighthouse-core/audits/accessibility/button-name.js +0 -45
- package/lighthouse-core/audits/accessibility/bypass.js +0 -46
- package/lighthouse-core/audits/accessibility/color-contrast.js +0 -46
- package/lighthouse-core/audits/accessibility/definition-list.js +0 -47
- package/lighthouse-core/audits/accessibility/dlitem.js +0 -45
- package/lighthouse-core/audits/accessibility/document-title.js +0 -45
- package/lighthouse-core/audits/accessibility/duplicate-id-active.js +0 -43
- package/lighthouse-core/audits/accessibility/duplicate-id-aria.js +0 -43
- package/lighthouse-core/audits/accessibility/form-field-multiple-labels.js +0 -44
- package/lighthouse-core/audits/accessibility/frame-title.js +0 -44
- package/lighthouse-core/audits/accessibility/heading-order.js +0 -43
- package/lighthouse-core/audits/accessibility/html-has-lang.js +0 -47
- package/lighthouse-core/audits/accessibility/html-lang-valid.js +0 -46
- package/lighthouse-core/audits/accessibility/image-alt.js +0 -45
- package/lighthouse-core/audits/accessibility/input-image-alt.js +0 -45
- package/lighthouse-core/audits/accessibility/label.js +0 -45
- package/lighthouse-core/audits/accessibility/link-name.js +0 -46
- package/lighthouse-core/audits/accessibility/list.js +0 -47
- package/lighthouse-core/audits/accessibility/listitem.js +0 -46
- package/lighthouse-core/audits/accessibility/manual/custom-controls-labels.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/custom-controls-roles.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/focus-traps.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/focusable-controls.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/interactive-element-affordance.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/logical-tab-order.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/managed-focus.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/offscreen-content-hidden.js +0 -29
- package/lighthouse-core/audits/accessibility/manual/use-landmarks.js +0 -28
- package/lighthouse-core/audits/accessibility/manual/visual-order-follows-dom.js +0 -28
- package/lighthouse-core/audits/accessibility/meta-refresh.js +0 -46
- package/lighthouse-core/audits/accessibility/meta-viewport.js +0 -47
- package/lighthouse-core/audits/accessibility/object-alt.js +0 -45
- package/lighthouse-core/audits/accessibility/tabindex.js +0 -45
- package/lighthouse-core/audits/accessibility/td-headers-attr.js +0 -49
- package/lighthouse-core/audits/accessibility/th-has-data-cells.js +0 -48
- package/lighthouse-core/audits/accessibility/valid-lang.js +0 -45
- package/lighthouse-core/audits/accessibility/video-caption.js +0 -46
- package/lighthouse-core/audits/apple-touch-icon.js +0 -72
- package/lighthouse-core/audits/audit.js +0 -391
- package/lighthouse-core/audits/autocomplete.js +0 -290
- package/lighthouse-core/audits/bootup-time.js +0 -152
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -258
- package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +0 -249
- package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +0 -92
- package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +0 -472
- package/lighthouse-core/audits/byte-efficiency/modern-image-formats.js +0 -186
- package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +0 -252
- package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +0 -309
- package/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +0 -111
- package/lighthouse-core/audits/byte-efficiency/unminified-css.js +0 -117
- package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +0 -120
- package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +0 -71
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +0 -161
- package/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -298
- package/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +0 -145
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -100
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +0 -177
- package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +0 -100
- package/lighthouse-core/audits/content-width.js +0 -77
- package/lighthouse-core/audits/critical-request-chains.js +0 -220
- package/lighthouse-core/audits/csp-xss.js +0 -186
- package/lighthouse-core/audits/deprecations.js +0 -684
- package/lighthouse-core/audits/diagnostics.js +0 -79
- package/lighthouse-core/audits/dobetterweb/charset.js +0 -96
- package/lighthouse-core/audits/dobetterweb/doctype.js +0 -94
- package/lighthouse-core/audits/dobetterweb/dom-size.js +0 -123
- package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +0 -70
- package/lighthouse-core/audits/dobetterweb/inspector-issues.js +0 -192
- package/lighthouse-core/audits/dobetterweb/js-libraries.js +0 -88
- package/lighthouse-core/audits/dobetterweb/no-document-write.js +0 -85
- package/lighthouse-core/audits/dobetterweb/no-vulnerable-libraries.js +0 -225
- package/lighthouse-core/audits/dobetterweb/notification-on-start.js +0 -69
- package/lighthouse-core/audits/dobetterweb/password-inputs-can-be-pasted-into.js +0 -66
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +0 -258
- package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +0 -68
- package/lighthouse-core/audits/errors-in-console.js +0 -118
- package/lighthouse-core/audits/final-screenshot.js +0 -59
- package/lighthouse-core/audits/font-display.js +0 -193
- package/lighthouse-core/audits/full-page-screenshot.js +0 -43
- package/lighthouse-core/audits/image-aspect-ratio.js +0 -122
- package/lighthouse-core/audits/image-size-responsive.js +0 -331
- package/lighthouse-core/audits/installable-manifest.js +0 -266
- package/lighthouse-core/audits/is-on-https.js +0 -125
- package/lighthouse-core/audits/largest-contentful-paint-element.js +0 -70
- package/lighthouse-core/audits/layout-shift-elements.js +0 -75
- package/lighthouse-core/audits/lcp-lazy-loaded.js +0 -85
- package/lighthouse-core/audits/long-tasks.js +0 -124
- package/lighthouse-core/audits/main-thread-tasks.js +0 -59
- package/lighthouse-core/audits/mainthread-work-breakdown.js +0 -134
- package/lighthouse-core/audits/manual/manual-audit.js +0 -37
- package/lighthouse-core/audits/manual/pwa-cross-browser.js +0 -41
- package/lighthouse-core/audits/manual/pwa-each-page-has-url.js +0 -39
- package/lighthouse-core/audits/manual/pwa-page-transitions.js +0 -39
- package/lighthouse-core/audits/maskable-icon.js +0 -71
- package/lighthouse-core/audits/metrics/cumulative-layout-shift.js +0 -79
- package/lighthouse-core/audits/metrics/experimental-interaction-to-next-paint.js +0 -88
- package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +0 -67
- package/lighthouse-core/audits/metrics/first-contentful-paint.js +0 -85
- package/lighthouse-core/audits/metrics/first-meaningful-paint.js +0 -89
- package/lighthouse-core/audits/metrics/interactive.js +0 -93
- package/lighthouse-core/audits/metrics/largest-contentful-paint.js +0 -114
- package/lighthouse-core/audits/metrics/max-potential-fid.js +0 -76
- package/lighthouse-core/audits/metrics/speed-index.js +0 -88
- package/lighthouse-core/audits/metrics/total-blocking-time.js +0 -117
- package/lighthouse-core/audits/metrics.js +0 -73
- package/lighthouse-core/audits/multi-check-audit.js +0 -83
- package/lighthouse-core/audits/network-requests.js +0 -129
- package/lighthouse-core/audits/network-rtt.js +0 -90
- package/lighthouse-core/audits/network-server-latency.js +0 -89
- package/lighthouse-core/audits/no-unload-listeners.js +0 -95
- package/lighthouse-core/audits/non-composited-animations.js +0 -206
- package/lighthouse-core/audits/oopif-iframe-test-audit.js +0 -30
- package/lighthouse-core/audits/performance-budget.js +0 -154
- package/lighthouse-core/audits/predictive-perf.js +0 -101
- package/lighthouse-core/audits/preload-fonts.js +0 -107
- package/lighthouse-core/audits/preload-lcp-image.js +0 -272
- package/lighthouse-core/audits/redirects.js +0 -163
- package/lighthouse-core/audits/resource-summary.js +0 -104
- package/lighthouse-core/audits/screenshot-thumbnails.js +0 -163
- package/lighthouse-core/audits/script-treemap-data.js +0 -288
- package/lighthouse-core/audits/seo/canonical.js +0 -218
- package/lighthouse-core/audits/seo/crawlable-anchors.js +0 -94
- package/lighthouse-core/audits/seo/font-size.js +0 -344
- package/lighthouse-core/audits/seo/hreflang.js +0 -149
- package/lighthouse-core/audits/seo/http-status-code.js +0 -70
- package/lighthouse-core/audits/seo/is-crawlable.js +0 -155
- package/lighthouse-core/audits/seo/link-text.js +0 -158
- package/lighthouse-core/audits/seo/manual/structured-data.js +0 -38
- package/lighthouse-core/audits/seo/meta-description.js +0 -67
- package/lighthouse-core/audits/seo/plugins.js +0 -152
- package/lighthouse-core/audits/seo/robots-txt.js +0 -256
- package/lighthouse-core/audits/seo/tap-targets.js +0 -352
- package/lighthouse-core/audits/server-response-time.js +0 -106
- package/lighthouse-core/audits/service-worker.js +0 -179
- package/lighthouse-core/audits/splash-screen.js +0 -99
- package/lighthouse-core/audits/themed-omnibox.js +0 -110
- package/lighthouse-core/audits/third-party-facades.js +0 -221
- package/lighthouse-core/audits/third-party-summary.js +0 -259
- package/lighthouse-core/audits/timing-budget.js +0 -174
- package/lighthouse-core/audits/unsized-images.js +0 -163
- package/lighthouse-core/audits/user-timings.js +0 -120
- package/lighthouse-core/audits/uses-rel-preconnect.js +0 -254
- package/lighthouse-core/audits/uses-rel-preload.js +0 -267
- package/lighthouse-core/audits/valid-source-maps.js +0 -157
- package/lighthouse-core/audits/viewport.js +0 -65
- package/lighthouse-core/audits/violation-audit.js +0 -50
- package/lighthouse-core/audits/work-during-interaction.js +0 -280
- package/lighthouse-core/computed/computed-artifact.js +0 -62
- package/lighthouse-core/computed/critical-request-chains.js +0 -146
- package/lighthouse-core/computed/image-records.js +0 -63
- package/lighthouse-core/computed/js-bundles.js +0 -119
- package/lighthouse-core/computed/load-simulator.js +0 -92
- package/lighthouse-core/computed/main-resource.js +0 -34
- package/lighthouse-core/computed/main-thread-tasks.js +0 -25
- package/lighthouse-core/computed/manifest-values.js +0 -135
- package/lighthouse-core/computed/metrics/cumulative-layout-shift.js +0 -129
- package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +0 -37
- package/lighthouse-core/computed/metrics/first-contentful-paint.js +0 -40
- package/lighthouse-core/computed/metrics/first-meaningful-paint.js +0 -44
- package/lighthouse-core/computed/metrics/interactive.js +0 -193
- package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +0 -204
- package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +0 -83
- package/lighthouse-core/computed/metrics/lantern-interactive.js +0 -113
- package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +0 -111
- package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +0 -93
- package/lighthouse-core/computed/metrics/lantern-metric.js +0 -167
- package/lighthouse-core/computed/metrics/lantern-speed-index.js +0 -150
- package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +0 -126
- package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +0 -45
- package/lighthouse-core/computed/metrics/largest-contentful-paint.js +0 -52
- package/lighthouse-core/computed/metrics/max-potential-fid.js +0 -45
- package/lighthouse-core/computed/metrics/metric.js +0 -100
- package/lighthouse-core/computed/metrics/navigation-metric.js +0 -46
- package/lighthouse-core/computed/metrics/responsiveness.js +0 -157
- package/lighthouse-core/computed/metrics/speed-index.js +0 -40
- package/lighthouse-core/computed/metrics/tbt-utils.js +0 -57
- package/lighthouse-core/computed/metrics/timing-summary.js +0 -150
- package/lighthouse-core/computed/metrics/total-blocking-time.js +0 -74
- package/lighthouse-core/computed/module-duplication.js +0 -136
- package/lighthouse-core/computed/network-analysis.js +0 -64
- package/lighthouse-core/computed/network-records.js +0 -21
- package/lighthouse-core/computed/page-dependency-graph.js +0 -488
- package/lighthouse-core/computed/processed-navigation.js +0 -21
- package/lighthouse-core/computed/processed-trace.js +0 -21
- package/lighthouse-core/computed/resource-summary.js +0 -114
- package/lighthouse-core/computed/screenshots.js +0 -29
- package/lighthouse-core/computed/speedline.js +0 -54
- package/lighthouse-core/computed/trace-of-tab.js +0 -30
- package/lighthouse-core/computed/unused-css.js +0 -154
- package/lighthouse-core/computed/unused-javascript-summary.js +0 -175
- package/lighthouse-core/computed/user-timings.js +0 -83
- package/lighthouse-core/computed/viewport-meta.js +0 -56
- package/lighthouse-core/config/budget.js +0 -341
- package/lighthouse-core/config/config-helpers.js +0 -590
- package/lighthouse-core/config/config-plugin.js +0 -225
- package/lighthouse-core/config/config.js +0 -544
- package/lighthouse-core/config/constants.js +0 -169
- package/lighthouse-core/config/default-config.js +0 -632
- package/lighthouse-core/config/desktop-config.js +0 -21
- package/lighthouse-core/config/experimental-config.js +0 -30
- package/lighthouse-core/config/full-config.js +0 -14
- package/lighthouse-core/config/lr-desktop-config.js +0 -25
- package/lighthouse-core/config/lr-mobile-config.js +0 -31
- package/lighthouse-core/config/metrics-to-audits.js +0 -61
- package/lighthouse-core/config/perf-config.js +0 -17
- package/lighthouse-core/fraggle-rock/api.js +0 -77
- package/lighthouse-core/fraggle-rock/config/config.js +0 -281
- package/lighthouse-core/fraggle-rock/config/default-config.js +0 -205
- package/lighthouse-core/fraggle-rock/config/filters.js +0 -335
- package/lighthouse-core/fraggle-rock/config/validation.js +0 -313
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +0 -99
- package/lighthouse-core/fraggle-rock/gather/base-gatherer.js +0 -107
- package/lighthouse-core/fraggle-rock/gather/driver.js +0 -88
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +0 -339
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +0 -160
- package/lighthouse-core/fraggle-rock/gather/session.js +0 -182
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +0 -68
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +0 -82
- package/lighthouse-core/fraggle-rock/user-flow.js +0 -229
- package/lighthouse-core/gather/connections/connection.js +0 -179
- package/lighthouse-core/gather/connections/cri.js +0 -171
- package/lighthouse-core/gather/connections/raw.js +0 -58
- package/lighthouse-core/gather/devtools-log.js +0 -61
- package/lighthouse-core/gather/driver/dom.js +0 -58
- package/lighthouse-core/gather/driver/environment.js +0 -105
- package/lighthouse-core/gather/driver/execution-context.js +0 -249
- package/lighthouse-core/gather/driver/navigation.js +0 -179
- package/lighthouse-core/gather/driver/network-monitor.js +0 -290
- package/lighthouse-core/gather/driver/network.js +0 -28
- package/lighthouse-core/gather/driver/prepare.js +0 -226
- package/lighthouse-core/gather/driver/service-workers.js +0 -53
- package/lighthouse-core/gather/driver/storage.js +0 -125
- package/lighthouse-core/gather/driver/target-manager.js +0 -125
- package/lighthouse-core/gather/driver/wait-for-condition.js +0 -535
- package/lighthouse-core/gather/driver.js +0 -482
- package/lighthouse-core/gather/fetcher.js +0 -318
- package/lighthouse-core/gather/gather-runner.js +0 -622
- package/lighthouse-core/gather/gatherers/accessibility.js +0 -172
- package/lighthouse-core/gather/gatherers/anchor-elements.js +0 -133
- package/lighthouse-core/gather/gatherers/cache-contents.js +0 -59
- package/lighthouse-core/gather/gatherers/console-messages.js +0 -173
- package/lighthouse-core/gather/gatherers/css-usage.js +0 -149
- package/lighthouse-core/gather/gatherers/devtools-log-compat.js +0 -36
- package/lighthouse-core/gather/gatherers/devtools-log.js +0 -66
- package/lighthouse-core/gather/gatherers/dobetterweb/doctype.js +0 -46
- package/lighthouse-core/gather/gatherers/dobetterweb/domstats.js +0 -103
- package/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js +0 -192
- package/lighthouse-core/gather/gatherers/dobetterweb/password-inputs-with-prevented-paste.js +0 -50
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +0 -154
- package/lighthouse-core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -238
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +0 -235
- package/lighthouse-core/gather/gatherers/gatherer.js +0 -59
- package/lighthouse-core/gather/gatherers/global-listeners.js +0 -92
- package/lighthouse-core/gather/gatherers/iframe-elements.js +0 -65
- package/lighthouse-core/gather/gatherers/image-elements.js +0 -375
- package/lighthouse-core/gather/gatherers/inputs.js +0 -113
- package/lighthouse-core/gather/gatherers/inspector-issues.js +0 -127
- package/lighthouse-core/gather/gatherers/installability-errors.js +0 -48
- package/lighthouse-core/gather/gatherers/js-usage.js +0 -145
- package/lighthouse-core/gather/gatherers/link-elements.js +0 -184
- package/lighthouse-core/gather/gatherers/main-document-content.js +0 -56
- package/lighthouse-core/gather/gatherers/meta-elements.js +0 -68
- package/lighthouse-core/gather/gatherers/network-user-agent.js +0 -42
- package/lighthouse-core/gather/gatherers/script-elements.js +0 -160
- package/lighthouse-core/gather/gatherers/seo/embedded-content.js +0 -64
- package/lighthouse-core/gather/gatherers/seo/font-size.js +0 -380
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +0 -56
- package/lighthouse-core/gather/gatherers/seo/tap-targets.js +0 -370
- package/lighthouse-core/gather/gatherers/service-worker.js +0 -45
- package/lighthouse-core/gather/gatherers/source-maps.js +0 -150
- package/lighthouse-core/gather/gatherers/stacks.js +0 -125
- package/lighthouse-core/gather/gatherers/trace-compat.js +0 -36
- package/lighthouse-core/gather/gatherers/trace-elements.js +0 -349
- package/lighthouse-core/gather/gatherers/trace.js +0 -131
- package/lighthouse-core/gather/gatherers/viewport-dimensions.js +0 -58
- package/lighthouse-core/gather/gatherers/web-app-manifest.js +0 -102
- package/lighthouse-core/index.js +0 -80
- package/lighthouse-core/lib/arbitrary-equality-map.js +0 -81
- package/lighthouse-core/lib/asset-saver.js +0 -341
- package/lighthouse-core/lib/axe.js +0 -13
- package/lighthouse-core/lib/cdt/Platform.js +0 -55
- package/lighthouse-core/lib/cdt/SDK.js +0 -28
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +0 -346
- package/lighthouse-core/lib/csp-evaluator.js +0 -173
- package/lighthouse-core/lib/dependency-graph/base-node.js +0 -363
- package/lighthouse-core/lib/dependency-graph/cpu-node.js +0 -86
- package/lighthouse-core/lib/dependency-graph/network-node.js +0 -99
- package/lighthouse-core/lib/dependency-graph/simulator/connection-pool.js +0 -171
- package/lighthouse-core/lib/dependency-graph/simulator/dns-cache.js +0 -73
- package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +0 -498
- package/lighthouse-core/lib/dependency-graph/simulator/simulator-timing-map.js +0 -212
- package/lighthouse-core/lib/dependency-graph/simulator/simulator.js +0 -523
- package/lighthouse-core/lib/dependency-graph/simulator/tcp-connection.js +0 -206
- package/lighthouse-core/lib/emulation.js +0 -164
- package/lighthouse-core/lib/i18n/README.md +0 -273
- package/lighthouse-core/lib/i18n/i18n.js +0 -221
- package/lighthouse-core/lib/icons.js +0 -85
- package/lighthouse-core/lib/lantern-trace-saver.js +0 -198
- package/lighthouse-core/lib/lh-env.js +0 -14
- package/lighthouse-core/lib/lh-error.js +0 -421
- package/lighthouse-core/lib/lh-trace-processor.js +0 -46
- package/lighthouse-core/lib/manifest-parser.js +0 -513
- package/lighthouse-core/lib/median-run.js +0 -92
- package/lighthouse-core/lib/minification-estimator.js +0 -193
- package/lighthouse-core/lib/minify-devtoolslog.js +0 -87
- package/lighthouse-core/lib/minify-trace.js +0 -173
- package/lighthouse-core/lib/navigation-error.js +0 -145
- package/lighthouse-core/lib/network-recorder.js +0 -337
- package/lighthouse-core/lib/network-request.js +0 -540
- package/lighthouse-core/lib/page-functions.js +0 -559
- package/lighthouse-core/lib/proto-preprocessor.js +0 -133
- package/lighthouse-core/lib/rect-helpers.js +0 -252
- package/lighthouse-core/lib/script-helpers.js +0 -24
- package/lighthouse-core/lib/sentry.js +0 -144
- package/lighthouse-core/lib/stack-packs.js +0 -120
- package/lighthouse-core/lib/statistics.js +0 -104
- package/lighthouse-core/lib/tappable-rects.js +0 -99
- package/lighthouse-core/lib/third-party-web.js +0 -53
- package/lighthouse-core/lib/timing-trace-saver.js +0 -97
- package/lighthouse-core/lib/tracehouse/cpu-profile-model.js +0 -592
- package/lighthouse-core/lib/tracehouse/main-thread-tasks.js +0 -688
- package/lighthouse-core/lib/tracehouse/task-groups.js +0 -112
- package/lighthouse-core/lib/tracehouse/task-summary.js +0 -87
- package/lighthouse-core/lib/tracehouse/trace-processor.js +0 -962
- package/lighthouse-core/lib/traces/pwmetrics-events.js +0 -200
- package/lighthouse-core/lib/url-shim.js +0 -274
- package/lighthouse-core/runner.js +0 -518
- package/lighthouse-core/scoring.js +0 -93
- package/lighthouse-core/scripts/download-chrome.sh +0 -36
- package/lighthouse-core/scripts/manual-chrome-launcher.js +0 -51
- package/lighthouse-core/scripts/package.json +0 -4
- package/lighthouse-core/util-commonjs.js +0 -708
- package/now.json +0 -14
- package/report/.eslintrc.cjs +0 -35
- package/report/generator/package.json +0 -4
- package/report/package.json +0 -4
- package/report/renderer/i18n.js +0 -198
- package/report/renderer/util.js +0 -705
- package/report/test/generator/package.json +0 -4
- package/report/test/renderer/i18n-test.js +0 -140
- package/report/test/renderer/util-test.js +0 -479
- package/report/test-assets/faux-psi-template.html +0 -177
- package/report/test-assets/faux-psi.js +0 -153
- package/third-party/snyk/snapshot.json +0 -182
- package/types/cssstyle/index.d.ts +0 -23
- package/types/global-lh.d.ts +0 -81
- package/types/jest.d.ts +0 -25
- package/types/jsonld/index.d.ts +0 -19
- package/types/lhr/flow.d.ts +0 -32
- package/types/lighthouse-logger/index.d.ts +0 -33
- package/types/lookup-closest-locale/index.d.ts +0 -11
- package/types/node.d.ts +0 -12
- package/types/query-selector.d.ts +0 -36
- package/types/smokehouse.d.ts +0 -80
- package/types/structured-data.d.ts +0 -36
|
@@ -15,14 +15,13 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
/** @
|
|
18
|
+
/** @typedef {import('../types/lhr/audit-details').default.SnippetValue} SnippetValue */
|
|
19
19
|
|
|
20
20
|
const ELLIPSIS = '\u2026';
|
|
21
21
|
const NBSP = '\xa0';
|
|
22
22
|
const PASS_THRESHOLD = 0.9;
|
|
23
|
-
const SCREENSHOT_PREFIX = 'data:image/jpeg;base64,';
|
|
24
23
|
|
|
25
|
-
const RATINGS = {
|
|
24
|
+
const RATINGS$1 = {
|
|
26
25
|
PASS: {label: 'pass', minScore: PASS_THRESHOLD},
|
|
27
26
|
AVERAGE: {label: 'average', minScore: 0.5},
|
|
28
27
|
FAIL: {label: 'fail'},
|
|
@@ -38,9 +37,9 @@ const listOfTlds = [
|
|
|
38
37
|
];
|
|
39
38
|
|
|
40
39
|
class Util {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
static get RATINGS() {
|
|
41
|
+
return RATINGS$1;
|
|
42
|
+
}
|
|
44
43
|
|
|
45
44
|
static get PASS_THRESHOLD() {
|
|
46
45
|
return PASS_THRESHOLD;
|
|
@@ -51,166 +50,40 @@ class Util {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* TODO(team): we all agree the LHR shape change is technical debt we should fix
|
|
59
|
-
* @param {LH.Result} result
|
|
60
|
-
* @return {LH.ReportResult}
|
|
53
|
+
* If LHR is older than 10.0 it will not have the `finalDisplayedUrl` property.
|
|
54
|
+
* Old LHRs should have the `finalUrl` property which will work fine for the report.
|
|
55
|
+
*
|
|
56
|
+
* @param {LH.Result} lhr
|
|
61
57
|
*/
|
|
62
|
-
static
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// If LHR is older (≤3.0.3), it has no locale setting. Set default.
|
|
67
|
-
if (!clone.configSettings.locale) {
|
|
68
|
-
clone.configSettings.locale = 'en';
|
|
69
|
-
}
|
|
70
|
-
if (!clone.configSettings.formFactor) {
|
|
71
|
-
// @ts-expect-error fallback handling for emulatedFormFactor
|
|
72
|
-
clone.configSettings.formFactor = clone.configSettings.emulatedFormFactor;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
for (const audit of Object.values(clone.audits)) {
|
|
76
|
-
// Turn 'not-applicable' (LHR <4.0) and 'not_applicable' (older proto versions)
|
|
77
|
-
// into 'notApplicable' (LHR ≥4.0).
|
|
78
|
-
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
|
|
79
|
-
// eslint-disable-next-line max-len
|
|
80
|
-
if (audit.scoreDisplayMode === 'not_applicable' || audit.scoreDisplayMode === 'not-applicable') {
|
|
81
|
-
audit.scoreDisplayMode = 'notApplicable';
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (audit.details) {
|
|
85
|
-
// Turn `auditDetails.type` of undefined (LHR <4.2) and 'diagnostic' (LHR <5.0)
|
|
86
|
-
// into 'debugdata' (LHR ≥5.0).
|
|
87
|
-
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
|
|
88
|
-
if (audit.details.type === undefined || audit.details.type === 'diagnostic') {
|
|
89
|
-
// @ts-expect-error details is of type never.
|
|
90
|
-
audit.details.type = 'debugdata';
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Add the jpg data URL prefix to filmstrip screenshots without them (LHR <5.0).
|
|
94
|
-
if (audit.details.type === 'filmstrip') {
|
|
95
|
-
for (const screenshot of audit.details.items) {
|
|
96
|
-
if (!screenshot.data.startsWith(SCREENSHOT_PREFIX)) {
|
|
97
|
-
screenshot.data = SCREENSHOT_PREFIX + screenshot.data;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// For convenience, smoosh all AuditResults into their auditRef (which has just weight & group)
|
|
105
|
-
if (typeof clone.categories !== 'object') throw new Error('No categories provided.');
|
|
106
|
-
|
|
107
|
-
/** @type {Map<string, Array<LH.ReportResult.AuditRef>>} */
|
|
108
|
-
const relevantAuditToMetricsMap = new Map();
|
|
109
|
-
|
|
110
|
-
// This backcompat converts old LHRs (<9.0.0) to use the new "hidden" group.
|
|
111
|
-
// Old LHRs used "no group" to identify audits that should be hidden in performance instead of the "hidden" group.
|
|
112
|
-
// Newer LHRs use "no group" to identify opportunities and diagnostics whose groups are assigned by details type.
|
|
113
|
-
const [majorVersion] = clone.lighthouseVersion.split('.').map(Number);
|
|
114
|
-
const perfCategory = clone.categories['performance'];
|
|
115
|
-
if (majorVersion < 9 && perfCategory) {
|
|
116
|
-
if (!clone.categoryGroups) clone.categoryGroups = {};
|
|
117
|
-
clone.categoryGroups['hidden'] = {title: ''};
|
|
118
|
-
for (const auditRef of perfCategory.auditRefs) {
|
|
119
|
-
if (!auditRef.group) {
|
|
120
|
-
auditRef.group = 'hidden';
|
|
121
|
-
} else if (['load-opportunities', 'diagnostics'].includes(auditRef.group)) {
|
|
122
|
-
delete auditRef.group;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
for (const category of Object.values(clone.categories)) {
|
|
128
|
-
// Make basic lookup table for relevantAudits
|
|
129
|
-
category.auditRefs.forEach(metricRef => {
|
|
130
|
-
if (!metricRef.relevantAudits) return;
|
|
131
|
-
metricRef.relevantAudits.forEach(auditId => {
|
|
132
|
-
const arr = relevantAuditToMetricsMap.get(auditId) || [];
|
|
133
|
-
arr.push(metricRef);
|
|
134
|
-
relevantAuditToMetricsMap.set(auditId, arr);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
category.auditRefs.forEach(auditRef => {
|
|
139
|
-
const result = clone.audits[auditRef.id];
|
|
140
|
-
auditRef.result = result;
|
|
141
|
-
|
|
142
|
-
// Attach any relevantMetric auditRefs
|
|
143
|
-
if (relevantAuditToMetricsMap.has(auditRef.id)) {
|
|
144
|
-
auditRef.relevantMetrics = relevantAuditToMetricsMap.get(auditRef.id);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// attach the stackpacks to the auditRef object
|
|
148
|
-
if (clone.stackPacks) {
|
|
149
|
-
clone.stackPacks.forEach(pack => {
|
|
150
|
-
if (pack.descriptions[auditRef.id]) {
|
|
151
|
-
auditRef.stackPacks = auditRef.stackPacks || [];
|
|
152
|
-
auditRef.stackPacks.push({
|
|
153
|
-
title: pack.title,
|
|
154
|
-
iconDataURL: pack.iconDataURL,
|
|
155
|
-
description: pack.descriptions[auditRef.id],
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return clone;
|
|
58
|
+
static getFinalDisplayedUrl(lhr) {
|
|
59
|
+
if (lhr.finalDisplayedUrl) return lhr.finalDisplayedUrl;
|
|
60
|
+
if (lhr.finalUrl) return lhr.finalUrl;
|
|
61
|
+
throw new Error('Could not determine final displayed URL');
|
|
164
62
|
}
|
|
165
63
|
|
|
166
64
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
65
|
+
* If LHR is older than 10.0 it will not have the `mainDocumentUrl` property.
|
|
66
|
+
* Old LHRs should have the `finalUrl` property which is the same as `mainDocumentUrl`.
|
|
169
67
|
*
|
|
170
|
-
* @param {
|
|
171
|
-
* @return {boolean}
|
|
68
|
+
* @param {LH.Result} lhr
|
|
172
69
|
*/
|
|
173
|
-
static
|
|
174
|
-
|
|
175
|
-
case 'manual':
|
|
176
|
-
case 'notApplicable':
|
|
177
|
-
return true;
|
|
178
|
-
case 'error':
|
|
179
|
-
case 'informative':
|
|
180
|
-
return false;
|
|
181
|
-
case 'numeric':
|
|
182
|
-
case 'binary':
|
|
183
|
-
default:
|
|
184
|
-
return Number(audit.score) >= RATINGS.PASS.minScore;
|
|
185
|
-
}
|
|
70
|
+
static getMainDocumentUrl(lhr) {
|
|
71
|
+
return lhr.mainDocumentUrl || lhr.finalUrl;
|
|
186
72
|
}
|
|
187
73
|
|
|
188
74
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* @param {number|null} score
|
|
193
|
-
* @param {string=} scoreDisplayMode
|
|
194
|
-
* @return {string}
|
|
75
|
+
* @param {LH.Result} lhr
|
|
76
|
+
* @return {LH.Result.FullPageScreenshot=}
|
|
195
77
|
*/
|
|
196
|
-
static
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return RATINGS.PASS.label;
|
|
200
|
-
} else if (scoreDisplayMode === 'error') {
|
|
201
|
-
return RATINGS.ERROR.label;
|
|
202
|
-
} else if (score === null) {
|
|
203
|
-
return RATINGS.FAIL.label;
|
|
78
|
+
static getFullPageScreenshot(lhr) {
|
|
79
|
+
if (lhr.fullPageScreenshot) {
|
|
80
|
+
return lhr.fullPageScreenshot;
|
|
204
81
|
}
|
|
205
82
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
} else if (score >= RATINGS.AVERAGE.minScore) {
|
|
211
|
-
rating = RATINGS.AVERAGE.label;
|
|
212
|
-
}
|
|
213
|
-
return rating;
|
|
83
|
+
// Prior to 10.0.
|
|
84
|
+
const details = /** @type {LH.Result.FullPageScreenshot=} */ (
|
|
85
|
+
lhr.audits['full-page-screenshot']?.details);
|
|
86
|
+
return details;
|
|
214
87
|
}
|
|
215
88
|
|
|
216
89
|
/**
|
|
@@ -411,72 +284,11 @@ class Util {
|
|
|
411
284
|
return hostname.split('.').slice(-splitTld.length).join('.');
|
|
412
285
|
}
|
|
413
286
|
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* @param {LH.Result['configSettings']} settings
|
|
417
|
-
* @return {!{deviceEmulation: string, networkThrottling: string, cpuThrottling: string, summary: string}}
|
|
418
|
-
*/
|
|
419
|
-
static getEmulationDescriptions(settings) {
|
|
420
|
-
let cpuThrottling;
|
|
421
|
-
let networkThrottling;
|
|
422
|
-
let summary;
|
|
423
|
-
|
|
424
|
-
const throttling = settings.throttling;
|
|
425
|
-
|
|
426
|
-
switch (settings.throttlingMethod) {
|
|
427
|
-
case 'provided':
|
|
428
|
-
summary = networkThrottling = cpuThrottling = Util.i18n.strings.throttlingProvided;
|
|
429
|
-
break;
|
|
430
|
-
case 'devtools': {
|
|
431
|
-
const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
|
|
432
|
-
cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
|
|
433
|
-
networkThrottling = `${Util.i18n.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` +
|
|
434
|
-
`${Util.i18n.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` +
|
|
435
|
-
`${Util.i18n.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
|
|
436
|
-
|
|
437
|
-
const isSlow4G = () => {
|
|
438
|
-
return requestLatencyMs === 150 * 3.75 &&
|
|
439
|
-
throttling.downloadThroughputKbps === 1.6 * 1024 * 0.9 &&
|
|
440
|
-
throttling.uploadThroughputKbps === 750 * 0.9;
|
|
441
|
-
};
|
|
442
|
-
summary = isSlow4G() ? Util.i18n.strings.runtimeSlow4g : Util.i18n.strings.runtimeCustom;
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
case 'simulate': {
|
|
446
|
-
const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling;
|
|
447
|
-
cpuThrottling = `${Util.i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
|
|
448
|
-
networkThrottling = `${Util.i18n.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` +
|
|
449
|
-
`${Util.i18n.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
|
|
450
|
-
|
|
451
|
-
const isSlow4G = () => {
|
|
452
|
-
return rttMs === 150 && throughputKbps === 1.6 * 1024;
|
|
453
|
-
};
|
|
454
|
-
summary = isSlow4G() ? Util.i18n.strings.runtimeSlow4g : Util.i18n.strings.runtimeCustom;
|
|
455
|
-
break;
|
|
456
|
-
}
|
|
457
|
-
default:
|
|
458
|
-
summary = cpuThrottling = networkThrottling = Util.i18n.strings.runtimeUnknown;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// TODO(paulirish): revise Runtime Settings strings: https://github.com/GoogleChrome/lighthouse/pull/11796
|
|
462
|
-
const deviceEmulation = {
|
|
463
|
-
mobile: Util.i18n.strings.runtimeMobileEmulation,
|
|
464
|
-
desktop: Util.i18n.strings.runtimeDesktopEmulation,
|
|
465
|
-
}[settings.formFactor] || Util.i18n.strings.runtimeNoEmulation;
|
|
466
|
-
|
|
467
|
-
return {
|
|
468
|
-
deviceEmulation,
|
|
469
|
-
cpuThrottling,
|
|
470
|
-
networkThrottling,
|
|
471
|
-
summary,
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
287
|
/**
|
|
476
288
|
* Returns only lines that are near a message, or the first few lines if there are
|
|
477
289
|
* no line messages.
|
|
478
|
-
* @param {
|
|
479
|
-
* @param {
|
|
290
|
+
* @param {SnippetValue['lines']} lines
|
|
291
|
+
* @param {SnippetValue['lineMessages']} lineMessages
|
|
480
292
|
* @param {number} surroundingLineCount Number of lines to include before and after
|
|
481
293
|
* the message. If this is e.g. 2 this function might return 5 lines.
|
|
482
294
|
*/
|
|
@@ -513,299 +325,116 @@ class Util {
|
|
|
513
325
|
|
|
514
326
|
return lines.filter(line => lineNumbersToKeep.has(line.lineNumber));
|
|
515
327
|
}
|
|
328
|
+
}
|
|
516
329
|
|
|
517
|
-
|
|
518
|
-
* @param {string} categoryId
|
|
519
|
-
*/
|
|
520
|
-
static isPluginCategory(categoryId) {
|
|
521
|
-
return categoryId.startsWith('lighthouse-plugin-');
|
|
522
|
-
}
|
|
330
|
+
// auto-generated by build/build-report-components.js
|
|
523
331
|
|
|
524
|
-
|
|
525
|
-
* @param {LH.Result.GatherMode} gatherMode
|
|
526
|
-
*/
|
|
527
|
-
static shouldDisplayAsFraction(gatherMode) {
|
|
528
|
-
return gatherMode === 'timespan' || gatherMode === 'snapshot';
|
|
529
|
-
}
|
|
332
|
+
/** @typedef {import('./dom.js').DOM} DOM */
|
|
530
333
|
|
|
531
|
-
|
|
532
|
-
* @param {LH.ReportResult.Category} category
|
|
533
|
-
*/
|
|
534
|
-
static calculateCategoryFraction(category) {
|
|
535
|
-
let numPassableAudits = 0;
|
|
536
|
-
let numPassed = 0;
|
|
537
|
-
let numInformative = 0;
|
|
538
|
-
let totalWeight = 0;
|
|
539
|
-
for (const auditRef of category.auditRefs) {
|
|
540
|
-
const auditPassed = Util.showAsPassed(auditRef.result);
|
|
334
|
+
/* eslint-disable max-len, quotes, comma-spacing */
|
|
541
335
|
|
|
542
|
-
// Don't count the audit if it's manual, N/A, or isn't displayed.
|
|
543
|
-
if (auditRef.group === 'hidden' ||
|
|
544
|
-
auditRef.result.scoreDisplayMode === 'manual' ||
|
|
545
|
-
auditRef.result.scoreDisplayMode === 'notApplicable') {
|
|
546
|
-
continue;
|
|
547
|
-
} else if (auditRef.result.scoreDisplayMode === 'informative') {
|
|
548
|
-
if (!auditPassed) {
|
|
549
|
-
++numInformative;
|
|
550
|
-
}
|
|
551
|
-
continue;
|
|
552
|
-
}
|
|
553
336
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
337
|
+
/**
|
|
338
|
+
* @param {DOM} dom
|
|
339
|
+
*/
|
|
340
|
+
function create3pFilterComponent(dom) {
|
|
341
|
+
const el0 = dom.createFragment();
|
|
342
|
+
const el1 = dom.createElement("style");
|
|
343
|
+
el1.append("\n .lh-3p-filter {\n color: var(--color-gray-600);\n float: right;\n padding: 6px var(--stackpack-padding-horizontal);\n }\n .lh-3p-filter-label, .lh-3p-filter-input {\n vertical-align: middle;\n user-select: none;\n }\n .lh-3p-filter-input:disabled + .lh-3p-ui-string {\n text-decoration: line-through;\n }\n ");
|
|
344
|
+
el0.append(el1);
|
|
345
|
+
const el2 = dom.createElement("div", "lh-3p-filter");
|
|
346
|
+
const el3 = dom.createElement("label", "lh-3p-filter-label");
|
|
347
|
+
const el4 = dom.createElement("input", "lh-3p-filter-input");
|
|
348
|
+
el4.setAttribute('type', 'checkbox');
|
|
349
|
+
el4.setAttribute('checked', '');
|
|
350
|
+
const el5 = dom.createElement("span", "lh-3p-ui-string");
|
|
351
|
+
el5.append("Show 3rd party resources");
|
|
352
|
+
const el6 = dom.createElement("span", "lh-3p-filter-count");
|
|
353
|
+
el3.append(" ",el4," ",el5," (",el6,") ");
|
|
354
|
+
el2.append(" ",el3," ");
|
|
355
|
+
el0.append(el2);
|
|
356
|
+
return el0;
|
|
560
357
|
}
|
|
561
358
|
|
|
562
359
|
/**
|
|
563
|
-
*
|
|
564
|
-
* through, we have this global.
|
|
565
|
-
* @type {LH.ReportResult | null}
|
|
360
|
+
* @param {DOM} dom
|
|
566
361
|
*/
|
|
567
|
-
|
|
362
|
+
function createAuditComponent(dom) {
|
|
363
|
+
const el0 = dom.createFragment();
|
|
364
|
+
const el1 = dom.createElement("div", "lh-audit");
|
|
365
|
+
const el2 = dom.createElement("details", "lh-expandable-details");
|
|
366
|
+
const el3 = dom.createElement("summary");
|
|
367
|
+
const el4 = dom.createElement("div", "lh-audit__header lh-expandable-details__summary");
|
|
368
|
+
const el5 = dom.createElement("span", "lh-audit__score-icon");
|
|
369
|
+
const el6 = dom.createElement("span", "lh-audit__title-and-text");
|
|
370
|
+
const el7 = dom.createElement("span", "lh-audit__title");
|
|
371
|
+
const el8 = dom.createElement("span", "lh-audit__display-text");
|
|
372
|
+
el6.append(" ",el7," ",el8," ");
|
|
373
|
+
const el9 = dom.createElement("div", "lh-chevron-container");
|
|
374
|
+
el4.append(" ",el5," ",el6," ",el9," ");
|
|
375
|
+
el3.append(" ",el4," ");
|
|
376
|
+
const el10 = dom.createElement("div", "lh-audit__description");
|
|
377
|
+
const el11 = dom.createElement("div", "lh-audit__stackpacks");
|
|
378
|
+
el2.append(" ",el3," ",el10," ",el11," ");
|
|
379
|
+
el1.append(" ",el2," ");
|
|
380
|
+
el0.append(el1);
|
|
381
|
+
return el0;
|
|
382
|
+
}
|
|
568
383
|
|
|
569
384
|
/**
|
|
570
|
-
*
|
|
385
|
+
* @param {DOM} dom
|
|
571
386
|
*/
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
387
|
+
function createCategoryHeaderComponent(dom) {
|
|
388
|
+
const el0 = dom.createFragment();
|
|
389
|
+
const el1 = dom.createElement("div", "lh-category-header");
|
|
390
|
+
const el2 = dom.createElement("div", "lh-score__gauge");
|
|
391
|
+
el2.setAttribute('role', 'heading');
|
|
392
|
+
el2.setAttribute('aria-level', '2');
|
|
393
|
+
const el3 = dom.createElement("div", "lh-category-header__description");
|
|
394
|
+
el1.append(" ",el2," ",el3," ");
|
|
395
|
+
el0.append(el1);
|
|
396
|
+
return el0;
|
|
397
|
+
}
|
|
578
398
|
|
|
579
399
|
/**
|
|
580
|
-
*
|
|
400
|
+
* @param {DOM} dom
|
|
581
401
|
*/
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
/** A label, shown next to an audit title or metric title, indicating that there was an error computing it. The user can hover on the label to reveal a tooltip with the extended error message. Translation should be short (< 20 characters). */
|
|
597
|
-
errorLabel: 'Error!',
|
|
598
|
-
/** This label is shown above a bulleted list of warnings. It is shown directly below an audit that produced warnings. Warnings describe situations the user should be aware of, as Lighthouse was unable to complete all the work required on this audit. For example, The 'Unable to decode image (biglogo.jpg)' warning may show up below an image encoding audit. */
|
|
599
|
-
warningHeader: 'Warnings: ',
|
|
600
|
-
/** Section heading shown above a list of passed audits that contain warnings. Audits under this section do not negatively impact the score, but Lighthouse has generated some potentially actionable suggestions that should be reviewed. This section is expanded by default and displays after the failing audits. */
|
|
601
|
-
warningAuditsGroupTitle: 'Passed audits but with warnings',
|
|
602
|
-
/** Section heading shown above a list of audits that are passing. 'Passed' here refers to a passing grade. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
603
|
-
passedAuditsGroupTitle: 'Passed audits',
|
|
604
|
-
/** Section heading shown above a list of audits that do not apply to the page. For example, if an audit is 'Are images optimized?', but the page has no images on it, the audit will be marked as not applicable. This is neither passing or failing. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
605
|
-
notApplicableAuditsGroupTitle: 'Not applicable',
|
|
606
|
-
/** Section heading shown above a list of audits that were not computed by Lighthouse. They serve as a list of suggestions for the user to go and manually check. For example, Lighthouse can't automate testing cross-browser compatibility, so that is listed within this section, so the user is reminded to test it themselves. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
607
|
-
manualAuditsGroupTitle: 'Additional items to manually check',
|
|
608
|
-
|
|
609
|
-
/** Label shown preceding any important warnings that may have invalidated the entire report. For example, if the user has Chrome extensions installed, they may add enough performance overhead that Lighthouse's performance metrics are unreliable. If shown, this will be displayed at the top of the report UI. */
|
|
610
|
-
toplevelWarningsMessage: 'There were issues affecting this run of Lighthouse:',
|
|
611
|
-
|
|
612
|
-
/** String of text shown in a graphical representation of the flow of network requests for the web page. This label represents the initial network request that fetches an HTML page. This navigation may be redirected (eg. Initial navigation to http://example.com redirects to https://www.example.com). */
|
|
613
|
-
crcInitialNavigation: 'Initial Navigation',
|
|
614
|
-
/** Label of value shown in the summary of critical request chains. Refers to the total amount of time (milliseconds) of the longest critical path chain/sequence of network requests. Example value: 2310 ms */
|
|
615
|
-
crcLongestDurationLabel: 'Maximum critical path latency:',
|
|
616
|
-
|
|
617
|
-
/** Label for button that shows all lines of the snippet when clicked */
|
|
618
|
-
snippetExpandButtonLabel: 'Expand snippet',
|
|
619
|
-
/** Label for button that only shows a few lines of the snippet when clicked */
|
|
620
|
-
snippetCollapseButtonLabel: 'Collapse snippet',
|
|
621
|
-
|
|
622
|
-
/** Explanation shown to users below performance results to inform them that the test was done with a 4G network connection and to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. 'Lighthouse' becomes link text to additional documentation. */
|
|
623
|
-
lsPerformanceCategoryDescription: '[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analysis of the current page on an emulated mobile network. Values are estimated and may vary.',
|
|
624
|
-
/** Title of the lab data section of the Performance category. Within this section are various speed metrics which quantify the pageload performance into values presented in seconds and milliseconds. "Lab" is an abbreviated form of "laboratory", and refers to the fact that the data is from a controlled test of a website, not measurements from real users visiting that site. */
|
|
625
|
-
labDataTitle: 'Lab Data',
|
|
626
|
-
|
|
627
|
-
/** This label is for a checkbox above a table of items loaded by a web page. The checkbox is used to show or hide third-party (or "3rd-party") resources in the table, where "third-party resources" refers to items loaded by a web page from URLs that aren't controlled by the owner of the web page. */
|
|
628
|
-
thirdPartyResourcesLabel: 'Show 3rd-party resources',
|
|
629
|
-
/** This label is for a button that opens a new tab to a webapp called "Treemap", which is a nested visual representation of a heierarchy of data related to the reports (script bytes and coverage, resource breakdown, etc.) */
|
|
630
|
-
viewTreemapLabel: 'View Treemap',
|
|
631
|
-
/** This label is for a button that will show the user a trace of the page. */
|
|
632
|
-
viewTraceLabel: 'View Trace',
|
|
633
|
-
/** This label is for a button that will show the user a trace of the page. */
|
|
634
|
-
viewOriginalTraceLabel: 'View Original Trace',
|
|
635
|
-
|
|
636
|
-
/** Option in a dropdown menu that opens a small, summary report in a print dialog. */
|
|
637
|
-
dropdownPrintSummary: 'Print Summary',
|
|
638
|
-
/** Option in a dropdown menu that opens a full Lighthouse report in a print dialog. */
|
|
639
|
-
dropdownPrintExpanded: 'Print Expanded',
|
|
640
|
-
/** Option in a dropdown menu that copies the Lighthouse JSON object to the system clipboard. */
|
|
641
|
-
dropdownCopyJSON: 'Copy JSON',
|
|
642
|
-
/** Option in a dropdown menu that saves the Lighthouse report HTML locally to the system as a '.html' file. */
|
|
643
|
-
dropdownSaveHTML: 'Save as HTML',
|
|
644
|
-
/** Option in a dropdown menu that saves the Lighthouse JSON object to the local system as a '.json' file. */
|
|
645
|
-
dropdownSaveJSON: 'Save as JSON',
|
|
646
|
-
/** Option in a dropdown menu that opens the current report in the Lighthouse Viewer Application. */
|
|
647
|
-
dropdownViewer: 'Open in Viewer',
|
|
648
|
-
/** Option in a dropdown menu that saves the current report as a new GitHub Gist. */
|
|
649
|
-
dropdownSaveGist: 'Save as Gist',
|
|
650
|
-
/** Option in a dropdown menu that toggles the themeing of the report between Light(default) and Dark themes. */
|
|
651
|
-
dropdownDarkTheme: 'Toggle Dark Theme',
|
|
652
|
-
|
|
653
|
-
/** Label for a row in a table that describes the kind of device that was emulated for the Lighthouse run. Example values for row elements: 'No Emulation', 'Emulated Desktop', etc. */
|
|
654
|
-
runtimeSettingsDevice: 'Device',
|
|
655
|
-
/** Label for a row in a table that describes the network throttling conditions that were used during a Lighthouse run, if any. */
|
|
656
|
-
runtimeSettingsNetworkThrottling: 'Network throttling',
|
|
657
|
-
/** Label for a row in a table that describes the CPU throttling conditions that were used during a Lighthouse run, if any.*/
|
|
658
|
-
runtimeSettingsCPUThrottling: 'CPU throttling',
|
|
659
|
-
/** Label for a row in a table that shows the User Agent that was used to send out all network requests during the Lighthouse run. */
|
|
660
|
-
runtimeSettingsUANetwork: 'User agent (network)',
|
|
661
|
-
/** Label for a row in a table that shows the estimated CPU power of the machine running Lighthouse. Example row values: 532, 1492, 783. */
|
|
662
|
-
runtimeSettingsBenchmark: 'CPU/Memory Power',
|
|
663
|
-
/** Label for a row in a table that shows the version of the Axe library used. Example row values: 2.1.0, 3.2.3 */
|
|
664
|
-
runtimeSettingsAxeVersion: 'Axe version',
|
|
665
|
-
|
|
666
|
-
/** Label for button to create an issue against the Lighthouse GitHub project. */
|
|
667
|
-
footerIssue: 'File an issue',
|
|
668
|
-
|
|
669
|
-
/** Descriptive explanation for emulation setting when no device emulation is set. */
|
|
670
|
-
runtimeNoEmulation: 'No emulation',
|
|
671
|
-
/** Descriptive explanation for emulation setting when emulating a Moto G4 mobile device. */
|
|
672
|
-
runtimeMobileEmulation: 'Emulated Moto G4',
|
|
673
|
-
/** Descriptive explanation for emulation setting when emulating a generic desktop form factor, as opposed to a mobile-device like form factor. */
|
|
674
|
-
runtimeDesktopEmulation: 'Emulated Desktop',
|
|
675
|
-
/** Descriptive explanation for a runtime setting that is set to an unknown value. */
|
|
676
|
-
runtimeUnknown: 'Unknown',
|
|
677
|
-
/** Descriptive label that this analysis run was from a single pageload of a browser (not a summary of hundreds of loads) */
|
|
678
|
-
runtimeSingleLoad: 'Single page load',
|
|
679
|
-
/** Descriptive label that this analysis only considers the initial load of the page, and no interaction beyond when the page had "fully loaded" */
|
|
680
|
-
runtimeAnalysisWindow: 'Initial page load',
|
|
681
|
-
/** Descriptive explanation that this analysis run was from a single pageload of a browser, whereas field data often summarizes hundreds+ of page loads */
|
|
682
|
-
runtimeSingleLoadTooltip: 'This data is taken from a single page load, as opposed to field data summarizing many sessions.', // eslint-disable-line max-len
|
|
683
|
-
|
|
684
|
-
/** Descriptive explanation for environment throttling that was provided by the runtime environment instead of provided by Lighthouse throttling. */
|
|
685
|
-
throttlingProvided: 'Provided by environment',
|
|
686
|
-
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
687
|
-
show: 'Show',
|
|
688
|
-
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
689
|
-
hide: 'Hide',
|
|
690
|
-
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
691
|
-
expandView: 'Expand view',
|
|
692
|
-
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
693
|
-
collapseView: 'Collapse view',
|
|
694
|
-
/** Label indicating that Lighthouse throttled the page to emulate a slow 4G network connection. */
|
|
695
|
-
runtimeSlow4g: 'Slow 4G throttling',
|
|
696
|
-
/** Label indicating that Lighthouse throttled the page using custom throttling settings. */
|
|
697
|
-
runtimeCustom: 'Custom throttling',
|
|
698
|
-
};
|
|
699
|
-
Util.UIStrings = UIStrings;
|
|
700
|
-
|
|
701
|
-
// auto-generated by build/build-report-components.js
|
|
702
|
-
|
|
703
|
-
/** @typedef {import('./dom.js').DOM} DOM */
|
|
704
|
-
|
|
705
|
-
/* eslint-disable max-len */
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* @param {DOM} dom
|
|
710
|
-
*/
|
|
711
|
-
function create3pFilterComponent(dom) {
|
|
712
|
-
const el0 = dom.createFragment();
|
|
713
|
-
const el1 = dom.createElement('style');
|
|
714
|
-
el1.append('\n .lh-3p-filter {\n color: var(--color-gray-600);\n float: right;\n padding: 6px var(--stackpack-padding-horizontal);\n }\n .lh-3p-filter-label, .lh-3p-filter-input {\n vertical-align: middle;\n user-select: none;\n }\n .lh-3p-filter-input:disabled + .lh-3p-ui-string {\n text-decoration: line-through;\n }\n ');
|
|
715
|
-
el0.append(el1);
|
|
716
|
-
const el2 = dom.createElement('div', 'lh-3p-filter');
|
|
717
|
-
const el3 = dom.createElement('label', 'lh-3p-filter-label');
|
|
718
|
-
const el4 = dom.createElement('input', 'lh-3p-filter-input');
|
|
719
|
-
el4.setAttribute('type', 'checkbox');
|
|
720
|
-
el4.setAttribute('checked', '');
|
|
721
|
-
const el5 = dom.createElement('span', 'lh-3p-ui-string');
|
|
722
|
-
el5.append('Show 3rd party resources');
|
|
723
|
-
const el6 = dom.createElement('span', 'lh-3p-filter-count');
|
|
724
|
-
el3.append(' ', el4, ' ', el5, ' (', el6, ') ');
|
|
725
|
-
el2.append(' ', el3, ' ');
|
|
726
|
-
el0.append(el2);
|
|
727
|
-
return el0;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
* @param {DOM} dom
|
|
732
|
-
*/
|
|
733
|
-
function createAuditComponent(dom) {
|
|
734
|
-
const el0 = dom.createFragment();
|
|
735
|
-
const el1 = dom.createElement('div', 'lh-audit');
|
|
736
|
-
const el2 = dom.createElement('details', 'lh-expandable-details');
|
|
737
|
-
const el3 = dom.createElement('summary');
|
|
738
|
-
const el4 = dom.createElement('div', 'lh-audit__header lh-expandable-details__summary');
|
|
739
|
-
const el5 = dom.createElement('span', 'lh-audit__score-icon');
|
|
740
|
-
const el6 = dom.createElement('span', 'lh-audit__title-and-text');
|
|
741
|
-
const el7 = dom.createElement('span', 'lh-audit__title');
|
|
742
|
-
const el8 = dom.createElement('span', 'lh-audit__display-text');
|
|
743
|
-
el6.append(' ', el7, ' ', el8, ' ');
|
|
744
|
-
const el9 = dom.createElement('div', 'lh-chevron-container');
|
|
745
|
-
el4.append(' ', el5, ' ', el6, ' ', el9, ' ');
|
|
746
|
-
el3.append(' ', el4, ' ');
|
|
747
|
-
const el10 = dom.createElement('div', 'lh-audit__description');
|
|
748
|
-
const el11 = dom.createElement('div', 'lh-audit__stackpacks');
|
|
749
|
-
el2.append(' ', el3, ' ', el10, ' ', el11, ' ');
|
|
750
|
-
el1.append(' ', el2, ' ');
|
|
751
|
-
el0.append(el1);
|
|
752
|
-
return el0;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* @param {DOM} dom
|
|
757
|
-
*/
|
|
758
|
-
function createCategoryHeaderComponent(dom) {
|
|
759
|
-
const el0 = dom.createFragment();
|
|
760
|
-
const el1 = dom.createElement('div', 'lh-category-header');
|
|
761
|
-
const el2 = dom.createElement('div', 'lh-score__gauge');
|
|
762
|
-
el2.setAttribute('role', 'heading');
|
|
763
|
-
el2.setAttribute('aria-level', '2');
|
|
764
|
-
const el3 = dom.createElement('div', 'lh-category-header__description');
|
|
765
|
-
el1.append(' ', el2, ' ', el3, ' ');
|
|
766
|
-
el0.append(el1);
|
|
767
|
-
return el0;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* @param {DOM} dom
|
|
772
|
-
*/
|
|
773
|
-
function createChevronComponent(dom) {
|
|
774
|
-
const el0 = dom.createFragment();
|
|
775
|
-
const el1 = dom.createElementNS('http://www.w3.org/2000/svg', 'svg', 'lh-chevron');
|
|
776
|
-
el1.setAttribute('viewBox', '0 0 100 100');
|
|
777
|
-
const el2 = dom.createElementNS('http://www.w3.org/2000/svg', 'g', 'lh-chevron__lines');
|
|
778
|
-
const el3 = dom.createElementNS('http://www.w3.org/2000/svg', 'path', 'lh-chevron__line lh-chevron__line-left');
|
|
779
|
-
el3.setAttribute('d', 'M10 50h40');
|
|
780
|
-
const el4 = dom.createElementNS('http://www.w3.org/2000/svg', 'path', 'lh-chevron__line lh-chevron__line-right');
|
|
781
|
-
el4.setAttribute('d', 'M90 50H50');
|
|
782
|
-
el2.append(' ', el3, ' ', el4, ' ');
|
|
783
|
-
el1.append(' ', el2, ' ');
|
|
784
|
-
el0.append(el1);
|
|
785
|
-
return el0;
|
|
786
|
-
}
|
|
402
|
+
function createChevronComponent(dom) {
|
|
403
|
+
const el0 = dom.createFragment();
|
|
404
|
+
const el1 = dom.createElementNS("http://www.w3.org/2000/svg", "svg", "lh-chevron");
|
|
405
|
+
el1.setAttribute('viewBox', '0 0 100 100');
|
|
406
|
+
const el2 = dom.createElementNS("http://www.w3.org/2000/svg", "g", "lh-chevron__lines");
|
|
407
|
+
const el3 = dom.createElementNS("http://www.w3.org/2000/svg", "path", "lh-chevron__line lh-chevron__line-left");
|
|
408
|
+
el3.setAttribute('d', 'M10 50h40');
|
|
409
|
+
const el4 = dom.createElementNS("http://www.w3.org/2000/svg", "path", "lh-chevron__line lh-chevron__line-right");
|
|
410
|
+
el4.setAttribute('d', 'M90 50H50');
|
|
411
|
+
el2.append(" ",el3," ",el4," ");
|
|
412
|
+
el1.append(" ",el2," ");
|
|
413
|
+
el0.append(el1);
|
|
414
|
+
return el0;
|
|
415
|
+
}
|
|
787
416
|
|
|
788
417
|
/**
|
|
789
418
|
* @param {DOM} dom
|
|
790
419
|
*/
|
|
791
420
|
function createClumpComponent(dom) {
|
|
792
421
|
const el0 = dom.createFragment();
|
|
793
|
-
const el1 = dom.createElement(
|
|
794
|
-
const el2 = dom.createElement(
|
|
795
|
-
const el3 = dom.createElement(
|
|
796
|
-
const el4 = dom.createElement(
|
|
797
|
-
const el5 = dom.createElement(
|
|
798
|
-
const el6 = dom.createElement(
|
|
799
|
-
const el7 = dom.createElement(
|
|
800
|
-
el5.append(
|
|
801
|
-
const el8 = dom.createElement(
|
|
802
|
-
const el9 = dom.createElement(
|
|
803
|
-
const el10 = dom.createElement(
|
|
804
|
-
el8.append(
|
|
805
|
-
el4.append(
|
|
806
|
-
el3.append(
|
|
807
|
-
el2.append(
|
|
808
|
-
el1.append(
|
|
422
|
+
const el1 = dom.createElement("div", "lh-audit-group");
|
|
423
|
+
const el2 = dom.createElement("details", "lh-clump");
|
|
424
|
+
const el3 = dom.createElement("summary");
|
|
425
|
+
const el4 = dom.createElement("div", "lh-audit-group__summary");
|
|
426
|
+
const el5 = dom.createElement("div", "lh-audit-group__header");
|
|
427
|
+
const el6 = dom.createElement("span", "lh-audit-group__title");
|
|
428
|
+
const el7 = dom.createElement("span", "lh-audit-group__itemcount");
|
|
429
|
+
el5.append(" ",el6," ",el7," "," "," ");
|
|
430
|
+
const el8 = dom.createElement("div", "lh-clump-toggle");
|
|
431
|
+
const el9 = dom.createElement("span", "lh-clump-toggletext--show");
|
|
432
|
+
const el10 = dom.createElement("span", "lh-clump-toggletext--hide");
|
|
433
|
+
el8.append(" ",el9," ",el10," ");
|
|
434
|
+
el4.append(" ",el5," ",el8," ");
|
|
435
|
+
el3.append(" ",el4," ");
|
|
436
|
+
el2.append(" ",el3," ");
|
|
437
|
+
el1.append(" "," ",el2," ");
|
|
809
438
|
el0.append(el1);
|
|
810
439
|
return el0;
|
|
811
440
|
}
|
|
@@ -815,19 +444,19 @@ function createClumpComponent(dom) {
|
|
|
815
444
|
*/
|
|
816
445
|
function createCrcComponent(dom) {
|
|
817
446
|
const el0 = dom.createFragment();
|
|
818
|
-
const el1 = dom.createElement(
|
|
819
|
-
const el2 = dom.createElement(
|
|
820
|
-
el2.append(
|
|
821
|
-
const el3 = dom.createElement(
|
|
822
|
-
const el4 = dom.createElement(
|
|
823
|
-
const el5 = dom.createElement(
|
|
824
|
-
const el6 = dom.createElement(
|
|
825
|
-
el4.append(
|
|
826
|
-
el3.append(
|
|
827
|
-
const el7 = dom.createElement(
|
|
828
|
-
const el8 = dom.createElement(
|
|
829
|
-
el7.append(
|
|
830
|
-
el1.append(
|
|
447
|
+
const el1 = dom.createElement("div", "lh-crc-container");
|
|
448
|
+
const el2 = dom.createElement("style");
|
|
449
|
+
el2.append("\n .lh-crc .lh-tree-marker {\n width: 12px;\n height: 26px;\n display: block;\n float: left;\n background-position: top left;\n }\n .lh-crc .lh-horiz-down {\n background: url('data:image/svg+xml;utf8,<svg width=\"16\" height=\"26\" viewBox=\"0 0 16 26\" xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"%23D8D8D8\" fill-rule=\"evenodd\"><path d=\"M16 12v2H-2v-2z\"/><path d=\"M9 12v14H7V12z\"/></g></svg>');\n }\n .lh-crc .lh-right {\n background: url('data:image/svg+xml;utf8,<svg width=\"16\" height=\"26\" viewBox=\"0 0 16 26\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M16 12v2H0v-2z\" fill=\"%23D8D8D8\" fill-rule=\"evenodd\"/></svg>');\n }\n .lh-crc .lh-up-right {\n background: url('data:image/svg+xml;utf8,<svg width=\"16\" height=\"26\" viewBox=\"0 0 16 26\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 0h2v14H7zm2 12h7v2H9z\" fill=\"%23D8D8D8\" fill-rule=\"evenodd\"/></svg>');\n }\n .lh-crc .lh-vert-right {\n background: url('data:image/svg+xml;utf8,<svg width=\"16\" height=\"26\" viewBox=\"0 0 16 26\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 0h2v27H7zm2 12h7v2H9z\" fill=\"%23D8D8D8\" fill-rule=\"evenodd\"/></svg>');\n }\n .lh-crc .lh-vert {\n background: url('data:image/svg+xml;utf8,<svg width=\"16\" height=\"26\" viewBox=\"0 0 16 26\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 0h2v26H7z\" fill=\"%23D8D8D8\" fill-rule=\"evenodd\"/></svg>');\n }\n .lh-crc .lh-crc-tree {\n font-size: 14px;\n width: 100%;\n overflow-x: auto;\n }\n .lh-crc .lh-crc-node {\n height: 26px;\n line-height: 26px;\n white-space: nowrap;\n }\n .lh-crc .lh-crc-node__tree-value {\n margin-left: 10px;\n }\n .lh-crc .lh-crc-node__tree-value div {\n display: inline;\n }\n .lh-crc .lh-crc-node__chain-duration {\n font-weight: 700;\n }\n .lh-crc .lh-crc-initial-nav {\n color: #595959;\n font-style: italic;\n }\n .lh-crc__summary-value {\n margin-bottom: 10px;\n }\n ");
|
|
450
|
+
const el3 = dom.createElement("div");
|
|
451
|
+
const el4 = dom.createElement("div", "lh-crc__summary-value");
|
|
452
|
+
const el5 = dom.createElement("span", "lh-crc__longest_duration_label");
|
|
453
|
+
const el6 = dom.createElement("b", "lh-crc__longest_duration");
|
|
454
|
+
el4.append(" ",el5," ",el6," ");
|
|
455
|
+
el3.append(" ",el4," ");
|
|
456
|
+
const el7 = dom.createElement("div", "lh-crc");
|
|
457
|
+
const el8 = dom.createElement("div", "lh-crc-initial-nav");
|
|
458
|
+
el7.append(" ",el8," "," ");
|
|
459
|
+
el1.append(" ",el2," ",el3," ",el7," ");
|
|
831
460
|
el0.append(el1);
|
|
832
461
|
return el0;
|
|
833
462
|
}
|
|
@@ -837,10 +466,10 @@ function createCrcComponent(dom) {
|
|
|
837
466
|
*/
|
|
838
467
|
function createCrcChainComponent(dom) {
|
|
839
468
|
const el0 = dom.createFragment();
|
|
840
|
-
const el1 = dom.createElement(
|
|
841
|
-
const el2 = dom.createElement(
|
|
842
|
-
const el3 = dom.createElement(
|
|
843
|
-
el1.append(
|
|
469
|
+
const el1 = dom.createElement("div", "lh-crc-node");
|
|
470
|
+
const el2 = dom.createElement("span", "lh-crc-node__tree-marker");
|
|
471
|
+
const el3 = dom.createElement("span", "lh-crc-node__tree-value");
|
|
472
|
+
el1.append(" ",el2," ",el3," ");
|
|
844
473
|
el0.append(el1);
|
|
845
474
|
return el0;
|
|
846
475
|
}
|
|
@@ -850,22 +479,23 @@ function createCrcChainComponent(dom) {
|
|
|
850
479
|
*/
|
|
851
480
|
function createElementScreenshotComponent(dom) {
|
|
852
481
|
const el0 = dom.createFragment();
|
|
853
|
-
const el1 = dom.createElement(
|
|
854
|
-
const el2 = dom.createElement(
|
|
855
|
-
const el3 = dom.createElement(
|
|
856
|
-
const el4 = dom.
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const el6 = dom.createElementNS(
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
const el8 = dom.createElement(
|
|
867
|
-
|
|
868
|
-
|
|
482
|
+
const el1 = dom.createElement("div", "lh-element-screenshot");
|
|
483
|
+
const el2 = dom.createElement("div", "lh-element-screenshot__content");
|
|
484
|
+
const el3 = dom.createElement("div", "lh-element-screenshot__image");
|
|
485
|
+
const el4 = dom.createElement("div", "lh-element-screenshot__mask");
|
|
486
|
+
const el5 = dom.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
487
|
+
el5.setAttribute('height', '0');
|
|
488
|
+
el5.setAttribute('width', '0');
|
|
489
|
+
const el6 = dom.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
490
|
+
const el7 = dom.createElementNS("http://www.w3.org/2000/svg", "clipPath");
|
|
491
|
+
el7.setAttribute('clipPathUnits', 'objectBoundingBox');
|
|
492
|
+
el6.append(" ",el7," "," ");
|
|
493
|
+
el5.append(" ",el6," ");
|
|
494
|
+
el4.append(" ",el5," ");
|
|
495
|
+
const el8 = dom.createElement("div", "lh-element-screenshot__element-marker");
|
|
496
|
+
el3.append(" ",el4," ",el8," ");
|
|
497
|
+
el2.append(" ",el3," ");
|
|
498
|
+
el1.append(" ",el2," ");
|
|
869
499
|
el0.append(el1);
|
|
870
500
|
return el0;
|
|
871
501
|
}
|
|
@@ -875,23 +505,23 @@ function createElementScreenshotComponent(dom) {
|
|
|
875
505
|
*/
|
|
876
506
|
function createFooterComponent(dom) {
|
|
877
507
|
const el0 = dom.createFragment();
|
|
878
|
-
const el1 = dom.createElement(
|
|
879
|
-
el1.append(
|
|
508
|
+
const el1 = dom.createElement("style");
|
|
509
|
+
el1.append("\n .lh-footer {\n padding: var(--footer-padding-vertical) calc(var(--default-padding) * 2);\n max-width: var(--report-content-max-width);\n margin: 0 auto;\n }\n .lh-footer .lh-generated {\n text-align: center;\n }\n ");
|
|
880
510
|
el0.append(el1);
|
|
881
|
-
const el2 = dom.createElement(
|
|
882
|
-
const el3 = dom.createElement(
|
|
883
|
-
el3.append(
|
|
884
|
-
const el4 = dom.createElement(
|
|
885
|
-
const el5 = dom.createElement(
|
|
886
|
-
el5.append(
|
|
887
|
-
const el6 = dom.createElement(
|
|
888
|
-
const el7 = dom.createElement(
|
|
511
|
+
const el2 = dom.createElement("footer", "lh-footer");
|
|
512
|
+
const el3 = dom.createElement("ul", "lh-meta__items");
|
|
513
|
+
el3.append(" ");
|
|
514
|
+
const el4 = dom.createElement("div", "lh-generated");
|
|
515
|
+
const el5 = dom.createElement("b");
|
|
516
|
+
el5.append("Lighthouse");
|
|
517
|
+
const el6 = dom.createElement("span", "lh-footer__version");
|
|
518
|
+
const el7 = dom.createElement("a", "lh-footer__version_issue");
|
|
889
519
|
el7.setAttribute('href', 'https://github.com/GoogleChrome/Lighthouse/issues');
|
|
890
520
|
el7.setAttribute('target', '_blank');
|
|
891
521
|
el7.setAttribute('rel', 'noopener');
|
|
892
|
-
el7.append(
|
|
893
|
-
el4.append(
|
|
894
|
-
el2.append(
|
|
522
|
+
el7.append("File an issue");
|
|
523
|
+
el4.append(" "," Generated by ",el5," ",el6," | ",el7," ");
|
|
524
|
+
el2.append(" ",el3," ",el4," ");
|
|
895
525
|
el0.append(el2);
|
|
896
526
|
return el0;
|
|
897
527
|
}
|
|
@@ -901,14 +531,14 @@ function createFooterComponent(dom) {
|
|
|
901
531
|
*/
|
|
902
532
|
function createFractionComponent(dom) {
|
|
903
533
|
const el0 = dom.createFragment();
|
|
904
|
-
const el1 = dom.createElement(
|
|
905
|
-
const el2 = dom.createElement(
|
|
906
|
-
const el3 = dom.createElement(
|
|
907
|
-
const el4 = dom.createElement(
|
|
908
|
-
el3.append(
|
|
909
|
-
el2.append(
|
|
910
|
-
const el5 = dom.createElement(
|
|
911
|
-
el1.append(
|
|
534
|
+
const el1 = dom.createElement("a", "lh-fraction__wrapper");
|
|
535
|
+
const el2 = dom.createElement("div", "lh-fraction__content-wrapper");
|
|
536
|
+
const el3 = dom.createElement("div", "lh-fraction__content");
|
|
537
|
+
const el4 = dom.createElement("div", "lh-fraction__background");
|
|
538
|
+
el3.append(" ",el4," ");
|
|
539
|
+
el2.append(" ",el3," ");
|
|
540
|
+
const el5 = dom.createElement("div", "lh-fraction__label");
|
|
541
|
+
el1.append(" ",el2," ",el5," ");
|
|
912
542
|
el0.append(el1);
|
|
913
543
|
return el0;
|
|
914
544
|
}
|
|
@@ -918,25 +548,25 @@ function createFractionComponent(dom) {
|
|
|
918
548
|
*/
|
|
919
549
|
function createGaugeComponent(dom) {
|
|
920
550
|
const el0 = dom.createFragment();
|
|
921
|
-
const el1 = dom.createElement(
|
|
922
|
-
const el2 = dom.createElement(
|
|
923
|
-
const el3 = dom.createElementNS(
|
|
551
|
+
const el1 = dom.createElement("a", "lh-gauge__wrapper");
|
|
552
|
+
const el2 = dom.createElement("div", "lh-gauge__svg-wrapper");
|
|
553
|
+
const el3 = dom.createElementNS("http://www.w3.org/2000/svg", "svg", "lh-gauge");
|
|
924
554
|
el3.setAttribute('viewBox', '0 0 120 120');
|
|
925
|
-
const el4 = dom.createElementNS(
|
|
555
|
+
const el4 = dom.createElementNS("http://www.w3.org/2000/svg", "circle", "lh-gauge-base");
|
|
926
556
|
el4.setAttribute('r', '56');
|
|
927
557
|
el4.setAttribute('cx', '60');
|
|
928
558
|
el4.setAttribute('cy', '60');
|
|
929
559
|
el4.setAttribute('stroke-width', '8');
|
|
930
|
-
const el5 = dom.createElementNS(
|
|
560
|
+
const el5 = dom.createElementNS("http://www.w3.org/2000/svg", "circle", "lh-gauge-arc");
|
|
931
561
|
el5.setAttribute('r', '56');
|
|
932
562
|
el5.setAttribute('cx', '60');
|
|
933
563
|
el5.setAttribute('cy', '60');
|
|
934
564
|
el5.setAttribute('stroke-width', '8');
|
|
935
|
-
el3.append(
|
|
936
|
-
el2.append(
|
|
937
|
-
const el6 = dom.createElement(
|
|
938
|
-
const el7 = dom.createElement(
|
|
939
|
-
el1.append(
|
|
565
|
+
el3.append(" ",el4," ",el5," ");
|
|
566
|
+
el2.append(" ",el3," ");
|
|
567
|
+
const el6 = dom.createElement("div", "lh-gauge__percentage");
|
|
568
|
+
const el7 = dom.createElement("div", "lh-gauge__label");
|
|
569
|
+
el1.append(" "," ",el2," ",el6," "," ",el7," ");
|
|
940
570
|
el0.append(el1);
|
|
941
571
|
return el0;
|
|
942
572
|
}
|
|
@@ -946,97 +576,97 @@ function createGaugeComponent(dom) {
|
|
|
946
576
|
*/
|
|
947
577
|
function createGaugePwaComponent(dom) {
|
|
948
578
|
const el0 = dom.createFragment();
|
|
949
|
-
const el1 = dom.createElement(
|
|
950
|
-
el1.append(
|
|
579
|
+
const el1 = dom.createElement("style");
|
|
580
|
+
el1.append("\n .lh-gauge--pwa .lh-gauge--pwa__component {\n display: none;\n }\n .lh-gauge--pwa__wrapper:not(.lh-badged--all) .lh-gauge--pwa__logo > path {\n /* Gray logo unless everything is passing. */\n fill: #B0B0B0;\n }\n\n .lh-gauge--pwa__disc {\n fill: var(--color-gray-200);\n }\n\n .lh-gauge--pwa__logo--primary-color {\n fill: #304FFE;\n }\n\n .lh-gauge--pwa__logo--secondary-color {\n fill: #3D3D3D;\n }\n .lh-dark .lh-gauge--pwa__logo--secondary-color {\n fill: #D8B6B6;\n }\n\n /* No passing groups. */\n .lh-gauge--pwa__wrapper:not([class*='lh-badged--']) .lh-gauge--pwa__na-line {\n display: inline;\n }\n /* Just optimized. Same n/a line as no passing groups. */\n .lh-gauge--pwa__wrapper.lh-badged--pwa-optimized:not(.lh-badged--pwa-installable) .lh-gauge--pwa__na-line {\n display: inline;\n }\n\n /* Just installable. */\n .lh-gauge--pwa__wrapper.lh-badged--pwa-installable .lh-gauge--pwa__installable-badge {\n display: inline;\n }\n\n /* All passing groups. */\n .lh-gauge--pwa__wrapper.lh-badged--all .lh-gauge--pwa__check-circle {\n display: inline;\n }\n ");
|
|
951
581
|
el0.append(el1);
|
|
952
|
-
const el2 = dom.createElement(
|
|
953
|
-
const el3 = dom.createElementNS(
|
|
582
|
+
const el2 = dom.createElement("a", "lh-gauge__wrapper lh-gauge--pwa__wrapper");
|
|
583
|
+
const el3 = dom.createElementNS("http://www.w3.org/2000/svg", "svg", "lh-gauge lh-gauge--pwa");
|
|
954
584
|
el3.setAttribute('viewBox', '0 0 60 60');
|
|
955
|
-
const el4 = dom.createElementNS(
|
|
956
|
-
const el5 = dom.createElementNS(
|
|
585
|
+
const el4 = dom.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
586
|
+
const el5 = dom.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
|
|
957
587
|
el5.setAttribute('id', 'lh-gauge--pwa__check-circle__gradient');
|
|
958
588
|
el5.setAttribute('x1', '50%');
|
|
959
589
|
el5.setAttribute('y1', '0%');
|
|
960
590
|
el5.setAttribute('x2', '50%');
|
|
961
591
|
el5.setAttribute('y2', '100%');
|
|
962
|
-
const el6 = dom.createElementNS(
|
|
592
|
+
const el6 = dom.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
963
593
|
el6.setAttribute('stop-color', '#00C852');
|
|
964
594
|
el6.setAttribute('offset', '0%');
|
|
965
|
-
const el7 = dom.createElementNS(
|
|
595
|
+
const el7 = dom.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
966
596
|
el7.setAttribute('stop-color', '#009688');
|
|
967
597
|
el7.setAttribute('offset', '100%');
|
|
968
|
-
el5.append(
|
|
969
|
-
const el8 = dom.createElementNS(
|
|
598
|
+
el5.append(" ",el6," ",el7," ");
|
|
599
|
+
const el8 = dom.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
|
|
970
600
|
el8.setAttribute('id', 'lh-gauge--pwa__installable__shadow-gradient');
|
|
971
601
|
el8.setAttribute('x1', '76.056%');
|
|
972
602
|
el8.setAttribute('x2', '24.111%');
|
|
973
603
|
el8.setAttribute('y1', '82.995%');
|
|
974
604
|
el8.setAttribute('y2', '24.735%');
|
|
975
|
-
const el9 = dom.createElementNS(
|
|
605
|
+
const el9 = dom.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
976
606
|
el9.setAttribute('stop-color', '#A5D6A7');
|
|
977
607
|
el9.setAttribute('offset', '0%');
|
|
978
|
-
const el10 = dom.createElementNS(
|
|
608
|
+
const el10 = dom.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
979
609
|
el10.setAttribute('stop-color', '#80CBC4');
|
|
980
610
|
el10.setAttribute('offset', '100%');
|
|
981
|
-
el8.append(
|
|
982
|
-
const el11 = dom.createElementNS(
|
|
611
|
+
el8.append(" ",el9," ",el10," ");
|
|
612
|
+
const el11 = dom.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
983
613
|
el11.setAttribute('id', 'lh-gauge--pwa__installable-badge');
|
|
984
|
-
const el12 = dom.createElementNS(
|
|
614
|
+
const el12 = dom.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
985
615
|
el12.setAttribute('fill', '#FFFFFF');
|
|
986
616
|
el12.setAttribute('cx', '10');
|
|
987
617
|
el12.setAttribute('cy', '10');
|
|
988
618
|
el12.setAttribute('r', '10');
|
|
989
|
-
const el13 = dom.createElementNS(
|
|
619
|
+
const el13 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
990
620
|
el13.setAttribute('fill', '#009688');
|
|
991
621
|
el13.setAttribute('d', 'M10 4.167A5.835 5.835 0 0 0 4.167 10 5.835 5.835 0 0 0 10 15.833 5.835 5.835 0 0 0 15.833 10 5.835 5.835 0 0 0 10 4.167zm2.917 6.416h-2.334v2.334H9.417v-2.334H7.083V9.417h2.334V7.083h1.166v2.334h2.334v1.166z');
|
|
992
|
-
el11.append(
|
|
993
|
-
el4.append(
|
|
994
|
-
const el14 = dom.createElementNS(
|
|
622
|
+
el11.append(" ",el12," ",el13," ");
|
|
623
|
+
el4.append(" ",el5," ",el8," ",el11," ");
|
|
624
|
+
const el14 = dom.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
995
625
|
el14.setAttribute('stroke', 'none');
|
|
996
626
|
el14.setAttribute('fill-rule', 'nonzero');
|
|
997
|
-
const el15 = dom.createElementNS(
|
|
627
|
+
const el15 = dom.createElementNS("http://www.w3.org/2000/svg", "circle", "lh-gauge--pwa__disc");
|
|
998
628
|
el15.setAttribute('cx', '30');
|
|
999
629
|
el15.setAttribute('cy', '30');
|
|
1000
630
|
el15.setAttribute('r', '30');
|
|
1001
|
-
const el16 = dom.createElementNS(
|
|
1002
|
-
const el17 = dom.createElementNS(
|
|
631
|
+
const el16 = dom.createElementNS("http://www.w3.org/2000/svg", "g", "lh-gauge--pwa__logo");
|
|
632
|
+
const el17 = dom.createElementNS("http://www.w3.org/2000/svg", "path", "lh-gauge--pwa__logo--secondary-color");
|
|
1003
633
|
el17.setAttribute('d', 'M35.66 19.39l.7-1.75h2L37.4 15 38.6 12l3.4 9h-2.51l-.58-1.61z');
|
|
1004
|
-
const el18 = dom.createElementNS(
|
|
634
|
+
const el18 = dom.createElementNS("http://www.w3.org/2000/svg", "path", "lh-gauge--pwa__logo--primary-color");
|
|
1005
635
|
el18.setAttribute('d', 'M33.52 21l3.65-9h-2.42l-2.5 5.82L30.5 12h-1.86l-1.9 5.82-1.35-2.65-1.21 3.72L25.4 21h2.38l1.72-5.2 1.64 5.2z');
|
|
1006
|
-
const el19 = dom.createElementNS(
|
|
636
|
+
const el19 = dom.createElementNS("http://www.w3.org/2000/svg", "path", "lh-gauge--pwa__logo--secondary-color");
|
|
1007
637
|
el19.setAttribute('fill-rule', 'nonzero');
|
|
1008
638
|
el19.setAttribute('d', 'M20.3 17.91h1.48c.45 0 .85-.05 1.2-.15l.39-1.18 1.07-3.3a2.64 2.64 0 0 0-.28-.37c-.55-.6-1.36-.91-2.42-.91H18v9h2.3V17.9zm1.96-3.84c.22.22.33.5.33.87 0 .36-.1.65-.29.87-.2.23-.59.35-1.15.35h-.86v-2.41h.87c.52 0 .89.1 1.1.32z');
|
|
1009
|
-
el16.append(
|
|
1010
|
-
const el20 = dom.createElementNS(
|
|
639
|
+
el16.append(" ",el17," ",el18," ",el19," ");
|
|
640
|
+
const el20 = dom.createElementNS("http://www.w3.org/2000/svg", "rect", "lh-gauge--pwa__component lh-gauge--pwa__na-line");
|
|
1011
641
|
el20.setAttribute('fill', '#FFFFFF');
|
|
1012
642
|
el20.setAttribute('x', '20');
|
|
1013
643
|
el20.setAttribute('y', '32');
|
|
1014
644
|
el20.setAttribute('width', '20');
|
|
1015
645
|
el20.setAttribute('height', '4');
|
|
1016
646
|
el20.setAttribute('rx', '2');
|
|
1017
|
-
const el21 = dom.createElementNS(
|
|
647
|
+
const el21 = dom.createElementNS("http://www.w3.org/2000/svg", "g", "lh-gauge--pwa__component lh-gauge--pwa__installable-badge");
|
|
1018
648
|
el21.setAttribute('transform', 'translate(20, 29)');
|
|
1019
|
-
const el22 = dom.createElementNS(
|
|
649
|
+
const el22 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1020
650
|
el22.setAttribute('fill', 'url(#lh-gauge--pwa__installable__shadow-gradient)');
|
|
1021
651
|
el22.setAttribute('d', 'M33.629 19.487c-4.272 5.453-10.391 9.39-17.415 10.869L3 17.142 17.142 3 33.63 19.487z');
|
|
1022
|
-
const el23 = dom.createElementNS(
|
|
652
|
+
const el23 = dom.createElementNS("http://www.w3.org/2000/svg", "use");
|
|
1023
653
|
el23.setAttribute('href', '#lh-gauge--pwa__installable-badge');
|
|
1024
|
-
el21.append(
|
|
1025
|
-
const el24 = dom.createElementNS(
|
|
654
|
+
el21.append(" ",el22," ",el23," ");
|
|
655
|
+
const el24 = dom.createElementNS("http://www.w3.org/2000/svg", "g", "lh-gauge--pwa__component lh-gauge--pwa__check-circle");
|
|
1026
656
|
el24.setAttribute('transform', 'translate(18, 28)');
|
|
1027
|
-
const el25 = dom.createElementNS(
|
|
657
|
+
const el25 = dom.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
1028
658
|
el25.setAttribute('fill', '#FFFFFF');
|
|
1029
659
|
el25.setAttribute('cx', '12');
|
|
1030
660
|
el25.setAttribute('cy', '12');
|
|
1031
661
|
el25.setAttribute('r', '12');
|
|
1032
|
-
const el26 = dom.createElementNS(
|
|
662
|
+
const el26 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1033
663
|
el26.setAttribute('fill', 'url(#lh-gauge--pwa__check-circle__gradient)');
|
|
1034
664
|
el26.setAttribute('d', 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z');
|
|
1035
|
-
el24.append(
|
|
1036
|
-
el14.append(
|
|
1037
|
-
el3.append(
|
|
1038
|
-
const el27 = dom.createElement(
|
|
1039
|
-
el2.append(
|
|
665
|
+
el24.append(" ",el25," ",el26," ");
|
|
666
|
+
el14.append(" "," ",el15," ",el16," "," ",el20," "," ",el21," "," ",el24," ");
|
|
667
|
+
el3.append(" ",el4," ",el14," ");
|
|
668
|
+
const el27 = dom.createElement("div", "lh-gauge__label");
|
|
669
|
+
el2.append(" ",el3," ",el27," ");
|
|
1040
670
|
el0.append(el2);
|
|
1041
671
|
return el0;
|
|
1042
672
|
}
|
|
@@ -1046,12 +676,12 @@ function createGaugePwaComponent(dom) {
|
|
|
1046
676
|
*/
|
|
1047
677
|
function createHeadingComponent(dom) {
|
|
1048
678
|
const el0 = dom.createFragment();
|
|
1049
|
-
const el1 = dom.createElement(
|
|
1050
|
-
el1.append(
|
|
679
|
+
const el1 = dom.createElement("style");
|
|
680
|
+
el1.append("\n /* CSS Fireworks. Originally by Eddie Lin\n https://codepen.io/paulirish/pen/yEVMbP\n */\n .lh-pyro {\n display: none;\n z-index: 1;\n pointer-events: none;\n }\n .lh-score100 .lh-pyro {\n display: block;\n }\n .lh-score100 .lh-lighthouse stop:first-child {\n stop-color: hsla(200, 12%, 95%, 0);\n }\n .lh-score100 .lh-lighthouse stop:last-child {\n stop-color: hsla(65, 81%, 76%, 1);\n }\n\n .lh-pyro > .lh-pyro-before, .lh-pyro > .lh-pyro-after {\n position: absolute;\n width: 5px;\n height: 5px;\n border-radius: 2.5px;\n box-shadow: 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff, 0 0 #fff;\n animation: 1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards;\n animation-delay: 1s, 1s, 1s;\n }\n\n .lh-pyro > .lh-pyro-after {\n animation-delay: 2.25s, 2.25s, 2.25s;\n animation-duration: 1.25s, 1.25s, 6.25s;\n }\n\n @keyframes bang {\n to {\n box-shadow: -70px -115.67px #47ebbc, -28px -99.67px #eb47a4, 58px -31.67px #7eeb47, 13px -141.67px #eb47c5, -19px 6.33px #7347eb, -2px -74.67px #ebd247, 24px -151.67px #eb47e0, 57px -138.67px #b4eb47, -51px -104.67px #479eeb, 62px 8.33px #ebcf47, -93px 0.33px #d547eb, -16px -118.67px #47bfeb, 53px -84.67px #47eb83, 66px -57.67px #eb47bf, -93px -65.67px #91eb47, 30px -13.67px #86eb47, -2px -59.67px #83eb47, -44px 1.33px #eb47eb, 61px -58.67px #47eb73, 5px -22.67px #47e8eb, -66px -28.67px #ebe247, 42px -123.67px #eb5547, -75px 26.33px #7beb47, 15px -52.67px #a147eb, 36px -51.67px #eb8347, -38px -12.67px #eb5547, -46px -59.67px #47eb81, 78px -114.67px #eb47ba, 15px -156.67px #eb47bf, -36px 1.33px #eb4783, -72px -86.67px #eba147, 31px -46.67px #ebe247, -68px 29.33px #47e2eb, -55px 19.33px #ebe047, -56px 27.33px #4776eb, -13px -91.67px #eb5547, -47px -138.67px #47ebc7, -18px -96.67px #eb47ac, 11px -88.67px #4783eb, -67px -28.67px #47baeb, 53px 10.33px #ba47eb, 11px 19.33px #5247eb, -5px -11.67px #eb4791, -68px -4.67px #47eba7, 95px -37.67px #eb478b, -67px -162.67px #eb5d47, -54px -120.67px #eb6847, 49px -12.67px #ebe047, 88px 8.33px #47ebda, 97px 33.33px #eb8147, 6px -71.67px #ebbc47;\n }\n }\n @keyframes gravity {\n to {\n transform: translateY(80px);\n opacity: 0;\n }\n }\n @keyframes position {\n 0%, 19.9% {\n margin-top: 4%;\n margin-left: 47%;\n }\n 20%, 39.9% {\n margin-top: 7%;\n margin-left: 30%;\n }\n 40%, 59.9% {\n margin-top: 6%;\n margin-left: 70%;\n }\n 60%, 79.9% {\n margin-top: 3%;\n margin-left: 20%;\n }\n 80%, 99.9% {\n margin-top: 3%;\n margin-left: 80%;\n }\n }\n ");
|
|
1051
681
|
el0.append(el1);
|
|
1052
|
-
const el2 = dom.createElement(
|
|
1053
|
-
const el3 = dom.createElement(
|
|
1054
|
-
el2.append(
|
|
682
|
+
const el2 = dom.createElement("div", "lh-header-container");
|
|
683
|
+
const el3 = dom.createElement("div", "lh-scores-wrapper-placeholder");
|
|
684
|
+
el2.append(" ",el3," ");
|
|
1055
685
|
el0.append(el2);
|
|
1056
686
|
return el0;
|
|
1057
687
|
}
|
|
@@ -1061,14 +691,14 @@ function createHeadingComponent(dom) {
|
|
|
1061
691
|
*/
|
|
1062
692
|
function createMetricComponent(dom) {
|
|
1063
693
|
const el0 = dom.createFragment();
|
|
1064
|
-
const el1 = dom.createElement(
|
|
1065
|
-
const el2 = dom.createElement(
|
|
1066
|
-
const el3 = dom.createElement(
|
|
1067
|
-
const el4 = dom.createElement(
|
|
1068
|
-
const el5 = dom.createElement(
|
|
1069
|
-
const el6 = dom.createElement(
|
|
1070
|
-
el2.append(
|
|
1071
|
-
el1.append(
|
|
694
|
+
const el1 = dom.createElement("div", "lh-metric");
|
|
695
|
+
const el2 = dom.createElement("div", "lh-metric__innerwrap");
|
|
696
|
+
const el3 = dom.createElement("div", "lh-metric__icon");
|
|
697
|
+
const el4 = dom.createElement("span", "lh-metric__title");
|
|
698
|
+
const el5 = dom.createElement("div", "lh-metric__value");
|
|
699
|
+
const el6 = dom.createElement("div", "lh-metric__description");
|
|
700
|
+
el2.append(" ",el3," ",el4," ",el5," ",el6," ");
|
|
701
|
+
el1.append(" ",el2," ");
|
|
1072
702
|
el0.append(el1);
|
|
1073
703
|
return el0;
|
|
1074
704
|
}
|
|
@@ -1078,31 +708,31 @@ function createMetricComponent(dom) {
|
|
|
1078
708
|
*/
|
|
1079
709
|
function createOpportunityComponent(dom) {
|
|
1080
710
|
const el0 = dom.createFragment();
|
|
1081
|
-
const el1 = dom.createElement(
|
|
1082
|
-
const el2 = dom.createElement(
|
|
1083
|
-
const el3 = dom.createElement(
|
|
1084
|
-
const el4 = dom.createElement(
|
|
1085
|
-
const el5 = dom.createElement(
|
|
1086
|
-
const el6 = dom.createElement(
|
|
1087
|
-
const el7 = dom.createElement(
|
|
1088
|
-
const el8 = dom.createElement(
|
|
1089
|
-
el6.append(
|
|
1090
|
-
const el9 = dom.createElement(
|
|
1091
|
-
const el10 = dom.createElement(
|
|
1092
|
-
const el11 = dom.createElement(
|
|
1093
|
-
const el12 = dom.createElement(
|
|
711
|
+
const el1 = dom.createElement("div", "lh-audit lh-audit--load-opportunity");
|
|
712
|
+
const el2 = dom.createElement("details", "lh-expandable-details");
|
|
713
|
+
const el3 = dom.createElement("summary");
|
|
714
|
+
const el4 = dom.createElement("div", "lh-audit__header");
|
|
715
|
+
const el5 = dom.createElement("div", "lh-load-opportunity__cols");
|
|
716
|
+
const el6 = dom.createElement("div", "lh-load-opportunity__col lh-load-opportunity__col--one");
|
|
717
|
+
const el7 = dom.createElement("span", "lh-audit__score-icon");
|
|
718
|
+
const el8 = dom.createElement("div", "lh-audit__title");
|
|
719
|
+
el6.append(" ",el7," ",el8," ");
|
|
720
|
+
const el9 = dom.createElement("div", "lh-load-opportunity__col lh-load-opportunity__col--two");
|
|
721
|
+
const el10 = dom.createElement("div", "lh-load-opportunity__sparkline");
|
|
722
|
+
const el11 = dom.createElement("div", "lh-sparkline");
|
|
723
|
+
const el12 = dom.createElement("div", "lh-sparkline__bar");
|
|
1094
724
|
el11.append(el12);
|
|
1095
|
-
el10.append(
|
|
1096
|
-
const el13 = dom.createElement(
|
|
1097
|
-
const el14 = dom.createElement(
|
|
1098
|
-
el9.append(
|
|
1099
|
-
el5.append(
|
|
1100
|
-
el4.append(
|
|
1101
|
-
el3.append(
|
|
1102
|
-
const el15 = dom.createElement(
|
|
1103
|
-
const el16 = dom.createElement(
|
|
1104
|
-
el2.append(
|
|
1105
|
-
el1.append(
|
|
725
|
+
el10.append(" ",el11," ");
|
|
726
|
+
const el13 = dom.createElement("div", "lh-audit__display-text");
|
|
727
|
+
const el14 = dom.createElement("div", "lh-chevron-container");
|
|
728
|
+
el9.append(" ",el10," ",el13," ",el14," ");
|
|
729
|
+
el5.append(" ",el6," ",el9," ");
|
|
730
|
+
el4.append(" ",el5," ");
|
|
731
|
+
el3.append(" ",el4," ");
|
|
732
|
+
const el15 = dom.createElement("div", "lh-audit__description");
|
|
733
|
+
const el16 = dom.createElement("div", "lh-audit__stackpacks");
|
|
734
|
+
el2.append(" ",el3," ",el15," ",el16," ");
|
|
735
|
+
el1.append(" ",el2," ");
|
|
1106
736
|
el0.append(el1);
|
|
1107
737
|
return el0;
|
|
1108
738
|
}
|
|
@@ -1112,10 +742,10 @@ function createOpportunityComponent(dom) {
|
|
|
1112
742
|
*/
|
|
1113
743
|
function createOpportunityHeaderComponent(dom) {
|
|
1114
744
|
const el0 = dom.createFragment();
|
|
1115
|
-
const el1 = dom.createElement(
|
|
1116
|
-
const el2 = dom.createElement(
|
|
1117
|
-
const el3 = dom.createElement(
|
|
1118
|
-
el1.append(
|
|
745
|
+
const el1 = dom.createElement("div", "lh-load-opportunity__header lh-load-opportunity__cols");
|
|
746
|
+
const el2 = dom.createElement("div", "lh-load-opportunity__col lh-load-opportunity__col--one");
|
|
747
|
+
const el3 = dom.createElement("div", "lh-load-opportunity__col lh-load-opportunity__col--two");
|
|
748
|
+
el1.append(" ",el2," ",el3," ");
|
|
1119
749
|
el0.append(el1);
|
|
1120
750
|
return el0;
|
|
1121
751
|
}
|
|
@@ -1125,14 +755,14 @@ function createOpportunityHeaderComponent(dom) {
|
|
|
1125
755
|
*/
|
|
1126
756
|
function createScorescaleComponent(dom) {
|
|
1127
757
|
const el0 = dom.createFragment();
|
|
1128
|
-
const el1 = dom.createElement(
|
|
1129
|
-
const el2 = dom.createElement(
|
|
1130
|
-
el2.append(
|
|
1131
|
-
const el3 = dom.createElement(
|
|
1132
|
-
el3.append(
|
|
1133
|
-
const el4 = dom.createElement(
|
|
1134
|
-
el4.append(
|
|
1135
|
-
el1.append(
|
|
758
|
+
const el1 = dom.createElement("div", "lh-scorescale");
|
|
759
|
+
const el2 = dom.createElement("span", "lh-scorescale-range lh-scorescale-range--fail");
|
|
760
|
+
el2.append("0–49");
|
|
761
|
+
const el3 = dom.createElement("span", "lh-scorescale-range lh-scorescale-range--average");
|
|
762
|
+
el3.append("50–89");
|
|
763
|
+
const el4 = dom.createElement("span", "lh-scorescale-range lh-scorescale-range--pass");
|
|
764
|
+
el4.append("90–100");
|
|
765
|
+
el1.append(" ",el2," ",el3," ",el4," ");
|
|
1136
766
|
el0.append(el1);
|
|
1137
767
|
return el0;
|
|
1138
768
|
}
|
|
@@ -1142,17 +772,17 @@ function createScorescaleComponent(dom) {
|
|
|
1142
772
|
*/
|
|
1143
773
|
function createScoresWrapperComponent(dom) {
|
|
1144
774
|
const el0 = dom.createFragment();
|
|
1145
|
-
const el1 = dom.createElement(
|
|
1146
|
-
el1.append(
|
|
775
|
+
const el1 = dom.createElement("style");
|
|
776
|
+
el1.append("\n .lh-scores-container {\n display: flex;\n flex-direction: column;\n padding: var(--default-padding) 0;\n position: relative;\n width: 100%;\n }\n\n .lh-sticky-header {\n --gauge-circle-size: var(--gauge-circle-size-sm);\n --plugin-badge-size: 16px;\n --plugin-icon-size: 75%;\n --gauge-wrapper-width: 60px;\n --gauge-percentage-font-size: 13px;\n position: fixed;\n left: 0;\n right: 0;\n top: var(--topbar-height);\n font-weight: 500;\n display: none;\n justify-content: center;\n background-color: var(--sticky-header-background-color);\n border-bottom: 1px solid var(--color-gray-200);\n padding-top: var(--score-container-padding);\n padding-bottom: 4px;\n z-index: 1;\n pointer-events: none;\n }\n\n .lh-devtools .lh-sticky-header {\n /* The report within DevTools is placed in a container with overflow, which changes the placement of this header unless we change `position` to `sticky.` */\n position: sticky;\n }\n\n .lh-sticky-header--visible {\n display: grid;\n grid-auto-flow: column;\n pointer-events: auto;\n }\n\n /* Disable the gauge arc animation for the sticky header, so toggling display: none\n does not play the animation. */\n .lh-sticky-header .lh-gauge-arc {\n animation: none;\n }\n\n .lh-sticky-header .lh-gauge__label,\n .lh-sticky-header .lh-fraction__label {\n display: none;\n }\n\n .lh-highlighter {\n width: var(--gauge-wrapper-width);\n height: 1px;\n background-color: var(--highlighter-background-color);\n /* Position at bottom of first gauge in sticky header. */\n position: absolute;\n grid-column: 1;\n bottom: -1px;\n }\n\n .lh-gauge__wrapper:first-of-type {\n contain: none;\n }\n ");
|
|
1147
777
|
el0.append(el1);
|
|
1148
|
-
const el2 = dom.createElement(
|
|
1149
|
-
const el3 = dom.createElement(
|
|
1150
|
-
const el4 = dom.createElement(
|
|
1151
|
-
const el5 = dom.createElement(
|
|
1152
|
-
const el6 = dom.createElement(
|
|
1153
|
-
el4.append(
|
|
1154
|
-
el3.append(
|
|
1155
|
-
el2.append(
|
|
778
|
+
const el2 = dom.createElement("div", "lh-scores-wrapper");
|
|
779
|
+
const el3 = dom.createElement("div", "lh-scores-container");
|
|
780
|
+
const el4 = dom.createElement("div", "lh-pyro");
|
|
781
|
+
const el5 = dom.createElement("div", "lh-pyro-before");
|
|
782
|
+
const el6 = dom.createElement("div", "lh-pyro-after");
|
|
783
|
+
el4.append(" ",el5," ",el6," ");
|
|
784
|
+
el3.append(" ",el4," ");
|
|
785
|
+
el2.append(" ",el3," ");
|
|
1156
786
|
el0.append(el2);
|
|
1157
787
|
return el0;
|
|
1158
788
|
}
|
|
@@ -1162,10 +792,10 @@ function createScoresWrapperComponent(dom) {
|
|
|
1162
792
|
*/
|
|
1163
793
|
function createSnippetComponent(dom) {
|
|
1164
794
|
const el0 = dom.createFragment();
|
|
1165
|
-
const el1 = dom.createElement(
|
|
1166
|
-
const el2 = dom.createElement(
|
|
1167
|
-
el2.append(
|
|
1168
|
-
el1.append(
|
|
795
|
+
const el1 = dom.createElement("div", "lh-snippet");
|
|
796
|
+
const el2 = dom.createElement("style");
|
|
797
|
+
el2.append("\n :root {\n --snippet-highlight-light: #fbf1f2;\n --snippet-highlight-dark: #ffd6d8;\n }\n\n .lh-snippet__header {\n position: relative;\n overflow: hidden;\n padding: 10px;\n border-bottom: none;\n color: var(--snippet-color);\n background-color: var(--snippet-background-color);\n border: 1px solid var(--report-border-color-secondary);\n }\n .lh-snippet__title {\n font-weight: bold;\n float: left;\n }\n .lh-snippet__node {\n float: left;\n margin-left: 4px;\n }\n .lh-snippet__toggle-expand {\n padding: 1px 7px;\n margin-top: -1px;\n margin-right: -7px;\n float: right;\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 14px;\n color: #0c50c7;\n }\n\n .lh-snippet__snippet {\n overflow: auto;\n border: 1px solid var(--report-border-color-secondary);\n }\n /* Container needed so that all children grow to the width of the scroll container */\n .lh-snippet__snippet-inner {\n display: inline-block;\n min-width: 100%;\n }\n\n .lh-snippet:not(.lh-snippet--expanded) .lh-snippet__show-if-expanded {\n display: none;\n }\n .lh-snippet.lh-snippet--expanded .lh-snippet__show-if-collapsed {\n display: none;\n }\n\n .lh-snippet__line {\n background: white;\n white-space: pre;\n display: flex;\n }\n .lh-snippet__line:not(.lh-snippet__line--message):first-child {\n padding-top: 4px;\n }\n .lh-snippet__line:not(.lh-snippet__line--message):last-child {\n padding-bottom: 4px;\n }\n .lh-snippet__line--content-highlighted {\n background: var(--snippet-highlight-dark);\n }\n .lh-snippet__line--message {\n background: var(--snippet-highlight-light);\n }\n .lh-snippet__line--message .lh-snippet__line-number {\n padding-top: 10px;\n padding-bottom: 10px;\n }\n .lh-snippet__line--message code {\n padding: 10px;\n padding-left: 5px;\n color: var(--color-fail);\n font-family: var(--report-font-family);\n }\n .lh-snippet__line--message code {\n white-space: normal;\n }\n .lh-snippet__line-icon {\n padding-top: 10px;\n display: none;\n }\n .lh-snippet__line--message .lh-snippet__line-icon {\n display: block;\n }\n .lh-snippet__line-icon:before {\n content: \"\";\n display: inline-block;\n vertical-align: middle;\n margin-right: 4px;\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n background-image: var(--fail-icon-url);\n }\n .lh-snippet__line-number {\n flex-shrink: 0;\n width: 40px;\n text-align: right;\n font-family: monospace;\n padding-right: 5px;\n margin-right: 5px;\n color: var(--color-gray-600);\n user-select: none;\n }\n ");
|
|
798
|
+
el1.append(" ",el2," ");
|
|
1169
799
|
el0.append(el1);
|
|
1170
800
|
return el0;
|
|
1171
801
|
}
|
|
@@ -1175,9 +805,9 @@ function createSnippetComponent(dom) {
|
|
|
1175
805
|
*/
|
|
1176
806
|
function createSnippetContentComponent(dom) {
|
|
1177
807
|
const el0 = dom.createFragment();
|
|
1178
|
-
const el1 = dom.createElement(
|
|
1179
|
-
const el2 = dom.createElement(
|
|
1180
|
-
el1.append(
|
|
808
|
+
const el1 = dom.createElement("div", "lh-snippet__snippet");
|
|
809
|
+
const el2 = dom.createElement("div", "lh-snippet__snippet-inner");
|
|
810
|
+
el1.append(" ",el2," ");
|
|
1181
811
|
el0.append(el1);
|
|
1182
812
|
return el0;
|
|
1183
813
|
}
|
|
@@ -1187,14 +817,14 @@ function createSnippetContentComponent(dom) {
|
|
|
1187
817
|
*/
|
|
1188
818
|
function createSnippetHeaderComponent(dom) {
|
|
1189
819
|
const el0 = dom.createFragment();
|
|
1190
|
-
const el1 = dom.createElement(
|
|
1191
|
-
const el2 = dom.createElement(
|
|
1192
|
-
const el3 = dom.createElement(
|
|
1193
|
-
const el4 = dom.createElement(
|
|
1194
|
-
const el5 = dom.createElement(
|
|
1195
|
-
const el6 = dom.createElement(
|
|
1196
|
-
el4.append(
|
|
1197
|
-
el1.append(
|
|
820
|
+
const el1 = dom.createElement("div", "lh-snippet__header");
|
|
821
|
+
const el2 = dom.createElement("div", "lh-snippet__title");
|
|
822
|
+
const el3 = dom.createElement("div", "lh-snippet__node");
|
|
823
|
+
const el4 = dom.createElement("button", "lh-snippet__toggle-expand");
|
|
824
|
+
const el5 = dom.createElement("span", "lh-snippet__btn-label-collapse lh-snippet__show-if-expanded");
|
|
825
|
+
const el6 = dom.createElement("span", "lh-snippet__btn-label-expand lh-snippet__show-if-collapsed");
|
|
826
|
+
el4.append(" ",el5," ",el6," ");
|
|
827
|
+
el1.append(" ",el2," ",el3," ",el4," ");
|
|
1198
828
|
el0.append(el1);
|
|
1199
829
|
return el0;
|
|
1200
830
|
}
|
|
@@ -1204,11 +834,11 @@ function createSnippetHeaderComponent(dom) {
|
|
|
1204
834
|
*/
|
|
1205
835
|
function createSnippetLineComponent(dom) {
|
|
1206
836
|
const el0 = dom.createFragment();
|
|
1207
|
-
const el1 = dom.createElement(
|
|
1208
|
-
const el2 = dom.createElement(
|
|
1209
|
-
const el3 = dom.createElement(
|
|
1210
|
-
const el4 = dom.createElement(
|
|
1211
|
-
el1.append(
|
|
837
|
+
const el1 = dom.createElement("div", "lh-snippet__line");
|
|
838
|
+
const el2 = dom.createElement("div", "lh-snippet__line-number");
|
|
839
|
+
const el3 = dom.createElement("div", "lh-snippet__line-icon");
|
|
840
|
+
const el4 = dom.createElement("code");
|
|
841
|
+
el1.append(" ",el2," ",el3," ",el4," ");
|
|
1212
842
|
el0.append(el1);
|
|
1213
843
|
return el0;
|
|
1214
844
|
}
|
|
@@ -1218,8 +848,8 @@ function createSnippetLineComponent(dom) {
|
|
|
1218
848
|
*/
|
|
1219
849
|
function createStylesComponent(dom) {
|
|
1220
850
|
const el0 = dom.createFragment();
|
|
1221
|
-
const el1 = dom.createElement(
|
|
1222
|
-
el1.append('/**\n * @license\n * Copyright 2017 The Lighthouse Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS-IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Naming convention:\n\n If a variable is used for a specific component: --{component}-{property name}-{modifier}\n\n Both {component} and {property name} should be kebab-case. If the target is the entire page,\n use \'report\' for the component. The property name should not be abbreviated. Use the\n property name the variable is intended for - if it\'s used for multiple, a common descriptor\n is fine (ex: \'size\' for a variable applied to \'width\' and \'height\'). If a variable is shared\n across multiple components, either create more variables or just drop the "{component}-"\n part of the name. Append any modifiers at the end (ex: \'big\', \'dark\').\n\n For colors: --color-{hue}-{intensity}\n\n {intensity} is the Material Design tag - 700, A700, etc.\n*/\n.lh-vars {\n /* Palette using Material Design Colors\n * https://www.materialui.co/colors */\n --color-amber-50: #FFF8E1;\n --color-blue-200: #90CAF9;\n --color-blue-900: #0D47A1;\n --color-blue-A700: #2962FF;\n --color-blue-primary: #06f;\n --color-cyan-500: #00BCD4;\n --color-gray-100: #F5F5F5;\n --color-gray-300: #CFCFCF;\n --color-gray-200: #E0E0E0;\n --color-gray-400: #BDBDBD;\n --color-gray-50: #FAFAFA;\n --color-gray-500: #9E9E9E;\n --color-gray-600: #757575;\n --color-gray-700: #616161;\n --color-gray-800: #424242;\n --color-gray-900: #212121;\n --color-gray: #000000;\n --color-green-700: #080;\n --color-green: #0c6;\n --color-lime-400: #D3E156;\n --color-orange-50: #FFF3E0;\n --color-orange-700: #C33300;\n --color-orange: #fa3;\n --color-red-700: #c00;\n --color-red: #f33;\n --color-teal-600: #00897B;\n --color-white: #FFFFFF;\n\n /* Context-specific colors */\n --color-average-secondary: var(--color-orange-700);\n --color-average: var(--color-orange);\n --color-fail-secondary: var(--color-red-700);\n --color-fail: var(--color-red);\n --color-hover: var(--color-gray-50);\n --color-informative: var(--color-blue-900);\n --color-pass-secondary: var(--color-green-700);\n --color-pass: var(--color-green);\n --color-not-applicable: var(--color-gray-600);\n\n /* Component variables */\n --audit-description-padding-left: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right));\n --audit-explanation-line-height: 16px;\n --audit-group-margin-bottom: calc(var(--default-padding) * 6);\n --audit-group-padding-vertical: 8px;\n --audit-margin-horizontal: 5px;\n --audit-padding-vertical: 8px;\n --category-padding: calc(var(--default-padding) * 6) var(--edge-gap-padding) calc(var(--default-padding) * 4);\n --chevron-line-stroke: var(--color-gray-600);\n --chevron-size: 12px;\n --default-padding: 8px;\n --edge-gap-padding: calc(var(--default-padding) * 4);\n --env-item-background-color: var(--color-gray-100);\n --env-item-font-size: 28px;\n --env-item-line-height: 36px;\n --env-item-padding: 10px 0px;\n --env-name-min-width: 220px;\n --footer-padding-vertical: 16px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 48px;\n --gauge-circle-size-sm: 32px;\n --gauge-label-font-size-big: 18px;\n --gauge-label-font-size: var(--report-font-size-secondary);\n --gauge-label-line-height-big: 24px;\n --gauge-label-line-height: var(--report-line-height-secondary);\n --gauge-percentage-font-size-big: 38px;\n --gauge-percentage-font-size: var(--report-font-size-secondary);\n --gauge-wrapper-width: 120px;\n --header-line-height: 24px;\n --highlighter-background-color: var(--report-text-color);\n --icon-square-size: calc(var(--score-icon-size) * 0.88);\n --image-preview-size: 48px;\n --link-color: var(--color-blue-primary);\n --locale-selector-background-color: var(--color-white);\n --metric-toggle-lines-fill: #7F7F7F;\n --metric-value-font-size: calc(var(--report-font-size) * 1.8);\n --metrics-toggle-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-white);\n --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);\n --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);\n --plugin-icon-size: 65%;\n --pwa-icon-margin: 0 var(--default-padding);\n --pwa-icon-size: var(--topbar-logo-size);\n --report-background-color: #fff;\n --report-border-color-secondary: #ebebeb;\n --report-font-family-monospace: \'Roboto Mono\', \'Menlo\', \'dejavu sans mono\', \'Consolas\', \'Lucida Console\', monospace;\n --report-font-family: Roboto, Helvetica, Arial, sans-serif;\n --report-font-size: 14px;\n --report-font-size-secondary: 12px;\n --report-icon-size: var(--score-icon-background-size);\n --report-line-height: 24px;\n --report-line-height-secondary: 20px;\n --report-monospace-font-size: calc(var(--report-font-size) * 0.85);\n --report-text-color-secondary: var(--color-gray-800);\n --report-text-color: var(--color-gray-900);\n --report-content-max-width: calc(60 * var(--report-font-size)); /* defaults to 840px */\n --report-content-min-width: 360px;\n --report-content-max-width-minus-edge-gap: calc(var(--report-content-max-width) - var(--edge-gap-padding) * 2);\n --score-container-padding: 8px;\n --score-icon-background-size: 24px;\n --score-icon-margin-left: 6px;\n --score-icon-margin-right: 14px;\n --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);\n --score-icon-size: 12px;\n --score-icon-size-big: 16px;\n --screenshot-overlay-background: rgba(0, 0, 0, 0.3);\n --section-padding-vertical: calc(var(--default-padding) * 6);\n --snippet-background-color: var(--color-gray-50);\n --snippet-color: #0938C2;\n --sparkline-height: 5px;\n --stackpack-padding-horizontal: 10px;\n --sticky-header-background-color: var(--report-background-color);\n --table-higlight-background-color: hsla(210, 17%, 77%, 0.1);\n --tools-icon-color: var(--color-gray-600);\n --topbar-background-color: var(--color-white);\n --topbar-height: 32px;\n --topbar-logo-size: 24px;\n --topbar-padding: 0 8px;\n --toplevel-warning-background-color: hsla(30, 100%, 75%, 10%);\n --toplevel-warning-message-text-color: var(--color-average-secondary);\n --toplevel-warning-padding: 18px;\n --toplevel-warning-text-color: var(--report-text-color);\n\n /* SVGs */\n --plugin-icon-url-dark: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="%23FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></svg>\');\n --plugin-icon-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="%23757575"><path d="M0 0h24v24H0z" fill="none"/><path d="M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z"/></svg>\');\n\n --pass-icon-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>check</title><path fill="%23178239" d="M24 4C12.95 4 4 12.95 4 24c0 11.04 8.95 20 20 20 11.04 0 20-8.96 20-20 0-11.05-8.96-20-20-20zm-4 30L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z"/></svg>\');\n --average-icon-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>info</title><path fill="%23E67700" d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/></svg>\');\n --fail-icon-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>warn</title><path fill="%23C7221F" d="M2 42h44L24 4 2 42zm24-6h-4v-4h4v4zm0-8h-4v-8h4v8z"/></svg>\');\n\n --pwa-installable-gray-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="nonzero"><circle fill="%23DAE0E3" cx="12" cy="12" r="12"/><path d="M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z" fill="%23FFF"/></g></svg>\');\n --pwa-optimized-gray-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect fill="%23DAE0E3" width="24" height="24" rx="12"/><path fill="%23FFF" d="M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z"/><path d="M5 5h14v14H5z"/></g></svg>\');\n\n --pwa-installable-gray-url-dark: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="nonzero"><circle fill="%23424242" cx="12" cy="12" r="12"/><path d="M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z" fill="%23FFF"/></g></svg>\');\n --pwa-optimized-gray-url-dark: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect fill="%23424242" width="24" height="24" rx="12"/><path fill="%23FFF" d="M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z"/><path d="M5 5h14v14H5z"/></g></svg>\');\n\n --pwa-installable-color-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><circle fill="%230CCE6B" cx="12" cy="12" r="12"/><path d="M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z" fill="%23FFF"/></g></svg>\');\n --pwa-optimized-color-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect fill="%230CCE6B" width="24" height="24" rx="12"/><path d="M5 5h14v14H5z"/><path fill="%23FFF" d="M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z"/></g></svg>\');\n\n --swap-locale-icon-url: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></svg>\');\n}\n\n@media not print {\n .lh-dark {\n /* Pallete */\n --color-gray-200: var(--color-gray-800);\n --color-gray-300: #616161;\n --color-gray-400: var(--color-gray-600);\n --color-gray-700: var(--color-gray-400);\n --color-gray-50: #757575;\n --color-gray-600: var(--color-gray-500);\n --color-green-700: var(--color-green);\n --color-orange-700: var(--color-orange);\n --color-red-700: var(--color-red);\n --color-teal-600: var(--color-cyan-500);\n\n /* Context-specific colors */\n --color-hover: rgba(0, 0, 0, 0.2);\n --color-informative: var(--color-blue-200);\n\n /* Component variables */\n --env-item-background-color: #393535;\n --link-color: var(--color-blue-200);\n --locale-selector-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-gray-800);\n --report-background-color: var(--color-gray-900);\n --report-border-color-secondary: var(--color-gray-200);\n --report-text-color-secondary: var(--color-gray-400);\n --report-text-color: var(--color-gray-100);\n --snippet-color: var(--color-cyan-500);\n --topbar-background-color: var(--color-gray);\n --toplevel-warning-background-color: hsl(33deg 14% 18%);\n --toplevel-warning-message-text-color: var(--color-orange-700);\n --toplevel-warning-text-color: var(--color-gray-100);\n\n /* SVGs */\n --plugin-icon-url: var(--plugin-icon-url-dark);\n --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark);\n --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark);\n }\n}\n\n@media only screen and (max-width: 480px) {\n .lh-vars {\n --audit-group-margin-bottom: 20px;\n --edge-gap-padding: var(--default-padding);\n --env-name-min-width: 120px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 72px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 112px;\n --header-padding: 16px 0 16px 0;\n --image-preview-size: 24px;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-size: 14px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --topbar-height: 28px;\n --topbar-logo-size: 20px;\n }\n\n /* Not enough space to adequately show the relative savings bars. */\n .lh-sparkline {\n display: none;\n }\n}\n\n.lh-vars.lh-devtools {\n --audit-explanation-line-height: 14px;\n --audit-group-margin-bottom: 20px;\n --audit-group-padding-vertical: 12px;\n --audit-padding-vertical: 4px;\n --category-padding: 12px;\n --default-padding: 12px;\n --env-name-min-width: 120px;\n --footer-padding-vertical: 8px;\n --gauge-circle-size-big: 72px;\n --gauge-circle-size: 64px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 97px;\n --header-line-height: 20px;\n --header-padding: 16px 0 16px 0;\n --screenshot-overlay-background: transparent;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-family-monospace: \'Menlo\', \'dejavu sans mono\', \'Consolas\', \'Lucida Console\', monospace;\n --report-font-family: \'.SFNSDisplay-Regular\', \'Helvetica Neue\', \'Lucida Grande\', sans-serif;\n --report-font-size: 12px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --section-padding-vertical: 8px;\n}\n\n.lh-devtools.lh-root {\n height: 100%;\n}\n.lh-devtools.lh-root img {\n /* Override devtools default \'min-width: 0\' so svg without size in a flexbox isn\'t collapsed. */\n min-width: auto;\n}\n.lh-devtools .lh-container {\n overflow-y: scroll;\n height: calc(100% - var(--topbar-height));\n}\n@media print {\n .lh-devtools .lh-container {\n overflow: unset;\n }\n}\n.lh-devtools .lh-sticky-header {\n /* This is normally the height of the topbar, but we want it to stick to the top of our scroll container .lh-container` */\n top: 0;\n}\n.lh-devtools .lh-element-screenshot__overlay {\n position: absolute;\n}\n\n@keyframes fadeIn {\n 0% { opacity: 0;}\n 100% { opacity: 0.6;}\n}\n\n.lh-root *, .lh-root *::before, .lh-root *::after {\n box-sizing: border-box;\n}\n\n.lh-root {\n font-family: var(--report-font-family);\n font-size: var(--report-font-size);\n margin: 0;\n line-height: var(--report-line-height);\n background: var(--report-background-color);\n color: var(--report-text-color);\n}\n\n.lh-root :focus {\n outline: -webkit-focus-ring-color auto 3px;\n}\n.lh-root summary:focus {\n outline: none;\n box-shadow: 0 0 0 1px hsl(217, 89%, 61%);\n}\n\n.lh-root [hidden] {\n display: none !important;\n}\n\n.lh-root pre {\n margin: 0;\n}\n\n.lh-root details > summary {\n cursor: pointer;\n}\n\n.lh-hidden {\n display: none !important;\n}\n\n.lh-container {\n /*\n Text wrapping in the report is so much FUN!\n We have a `word-break: break-word;` globally here to prevent a few common scenarios, namely\n long non-breakable text (usually URLs) found in:\n 1. The footer\n 2. .lh-node (outerHTML)\n 3. .lh-code\n\n With that sorted, the next challenge is appropriate column sizing and text wrapping inside our\n .lh-details tables. Even more fun.\n * We don\'t want table headers ("Potential Savings (ms)") to wrap or their column values, but\n we\'d be happy for the URL column to wrap if the URLs are particularly long.\n * We want the narrow columns to remain narrow, providing the most column width for URL\n * We don\'t want the table to extend past 100% width.\n * Long URLs in the URL column can wrap. Util.getURLDisplayName maxes them out at 64 characters,\n but they do not get any overflow:ellipsis treatment.\n */\n word-break: break-word;\n}\n\n.lh-audit-group a,\n.lh-category-header__description a,\n.lh-audit__description a,\n.lh-warnings a,\n.lh-footer a,\n.lh-table-column--link a {\n color: var(--link-color);\n}\n\n.lh-audit__description, .lh-audit__stackpack {\n --inner-audit-padding-right: var(--stackpack-padding-horizontal);\n padding-left: var(--audit-description-padding-left);\n padding-right: var(--inner-audit-padding-right);\n padding-top: 8px;\n padding-bottom: 8px;\n}\n\n.lh-details {\n margin-top: var(--default-padding);\n margin-bottom: var(--default-padding);\n margin-left: var(--audit-description-padding-left);\n /* whatever the .lh-details side margins are */\n width: 100%;\n}\n\n.lh-audit__stackpack {\n display: flex;\n align-items: center;\n}\n\n.lh-audit__stackpack__img {\n max-width: 30px;\n margin-right: var(--default-padding)\n}\n\n/* Report header */\n\n.lh-report-icon {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n cursor: pointer;\n}\n.lh-report-icon[disabled] {\n opacity: 0.3;\n pointer-events: none;\n}\n\n.lh-report-icon::before {\n content: "";\n margin: 4px;\n background-repeat: no-repeat;\n width: var(--report-icon-size);\n height: var(--report-icon-size);\n opacity: 0.7;\n display: inline-block;\n vertical-align: middle;\n}\n.lh-report-icon:hover::before {\n opacity: 1;\n}\n.lh-dark .lh-report-icon::before {\n filter: invert(1);\n}\n.lh-report-icon--print::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/><path fill="none" d="M0 0h24v24H0z"/></svg>\');\n}\n.lh-report-icon--copy::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>\');\n}\n.lh-report-icon--open::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z"/></svg>\');\n}\n.lh-report-icon--download::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>\');\n}\n.lh-report-icon--dark::before {\n background-image:url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 100 125"><path d="M50 23.587c-16.27 0-22.799 12.574-22.799 21.417 0 12.917 10.117 22.451 12.436 32.471h20.726c2.32-10.02 12.436-19.554 12.436-32.471 0-8.843-6.528-21.417-22.799-21.417zM39.637 87.161c0 3.001 1.18 4.181 4.181 4.181h.426l.41 1.231C45.278 94.449 46.042 95 48.019 95h3.963c1.978 0 2.74-.551 3.365-2.427l.409-1.231h.427c3.002 0 4.18-1.18 4.18-4.181V80.91H39.637v6.251zM50 18.265c1.26 0 2.072-.814 2.072-2.073v-9.12C52.072 5.813 51.26 5 50 5c-1.259 0-2.072.813-2.072 2.073v9.12c0 1.259.813 2.072 2.072 2.072zM68.313 23.727c.994.774 2.135.634 2.91-.357l5.614-7.187c.776-.992.636-2.135-.356-2.909-.992-.776-2.135-.636-2.91.357l-5.613 7.186c-.778.993-.636 2.135.355 2.91zM91.157 36.373c-.306-1.222-1.291-1.815-2.513-1.51l-8.85 2.207c-1.222.305-1.814 1.29-1.51 2.512.305 1.223 1.291 1.814 2.513 1.51l8.849-2.206c1.223-.305 1.816-1.291 1.511-2.513zM86.757 60.48l-8.331-3.709c-1.15-.512-2.225-.099-2.736 1.052-.512 1.151-.1 2.224 1.051 2.737l8.33 3.707c1.15.514 2.225.101 2.736-1.05.513-1.149.1-2.223-1.05-2.737zM28.779 23.37c.775.992 1.917 1.131 2.909.357.992-.776 1.132-1.917.357-2.91l-5.615-7.186c-.775-.992-1.917-1.132-2.909-.357s-1.131 1.917-.356 2.909l5.614 7.187zM21.715 39.583c.305-1.223-.288-2.208-1.51-2.513l-8.849-2.207c-1.222-.303-2.208.289-2.513 1.511-.303 1.222.288 2.207 1.511 2.512l8.848 2.206c1.222.304 2.208-.287 2.513-1.509zM21.575 56.771l-8.331 3.711c-1.151.511-1.563 1.586-1.05 2.735.511 1.151 1.586 1.563 2.736 1.052l8.331-3.711c1.151-.511 1.563-1.586 1.05-2.735-.512-1.15-1.585-1.562-2.736-1.052z"/></svg>\');\n}\n.lh-report-icon--treemap::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="black"><path d="M3 5v14h19V5H3zm2 2h15v4H5V7zm0 10v-4h4v4H5zm6 0v-4h9v4h-9z"/></svg>\');\n}\n.lh-report-icon--date::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 11h2v2H7v-2zm14-5v14a2 2 0 01-2 2H5a2 2 0 01-2-2V6c0-1.1.9-2 2-2h1V2h2v2h8V2h2v2h1a2 2 0 012 2zM5 8h14V6H5v2zm14 12V10H5v10h14zm-4-7h2v-2h-2v2zm-4 0h2v-2h-2v2z"/></svg>\');\n}\n.lh-report-icon--devices::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 6h18V4H4a2 2 0 00-2 2v11H0v3h14v-3H4V6zm19 2h-6a1 1 0 00-1 1v10c0 .6.5 1 1 1h6c.6 0 1-.5 1-1V9c0-.6-.5-1-1-1zm-1 9h-4v-7h4v7z"/></svg>\');\n}\n.lh-report-icon--world::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm7 6h-3c-.3-1.3-.8-2.5-1.4-3.6A8 8 0 0 1 18.9 8zm-7-4a14 14 0 0 1 2 4h-4a14 14 0 0 1 2-4zM4.3 14a8.2 8.2 0 0 1 0-4h3.3a16.5 16.5 0 0 0 0 4H4.3zm.8 2h3a14 14 0 0 0 1.3 3.6A8 8 0 0 1 5.1 16zm3-8H5a8 8 0 0 1 4.3-3.6L8 8zM12 20a14 14 0 0 1-2-4h4a14 14 0 0 1-2 4zm2.3-6H9.7a14.7 14.7 0 0 1 0-4h4.6a14.6 14.6 0 0 1 0 4zm.3 5.6c.6-1.2 1-2.4 1.4-3.6h3a8 8 0 0 1-4.4 3.6zm1.8-5.6a16.5 16.5 0 0 0 0-4h3.3a8.2 8.2 0 0 1 0 4h-3.3z"/></svg>\');\n}\n.lh-report-icon--stopwatch::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.1-6.6L20.5 6l-1.4-1.4L17.7 6A9 9 0 0 0 3 13a9 9 0 1 0 16-5.6zm-7 12.6a7 7 0 1 1 0-14 7 7 0 0 1 0 14z"/></svg>\');\n}\n.lh-report-icon--networkspeed::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.9 5c-.2 0-.3 0-.4.2v.2L10.1 17a2 2 0 0 0-.2 1 2 2 0 0 0 4 .4l2.4-12.9c0-.3-.2-.5-.5-.5zM1 9l2 2c2.9-2.9 6.8-4 10.5-3.6l1.2-2.7C10 3.8 4.7 5.3 1 9zm20 2 2-2a15.4 15.4 0 0 0-5.6-3.6L17 8.2c1.5.7 2.9 1.6 4.1 2.8zm-4 4 2-2a9.9 9.9 0 0 0-2.7-1.9l-.5 3 1.2.9zM5 13l2 2a7.1 7.1 0 0 1 4-2l1.3-2.9C9.7 10.1 7 11 5 13z"/></svg>\');\n}\n.lh-report-icon--samples-one::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="7" cy="14" r="3"/><path d="M7 18a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5.6 17.6a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>\');\n}\n.lh-report-icon--samples-many::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 18a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5.6 17.6a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/><circle cx="7" cy="14" r="3"/><circle cx="11" cy="6" r="3"/></svg>\');\n}\n.lh-report-icon--chrome::before {\n background-image: url(\'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 562 562"><path d="M256 25.6v25.6a204 204 0 0 1 144.8 60 204 204 0 0 1 60 144.8 204 204 0 0 1-60 144.8 204 204 0 0 1-144.8 60 204 204 0 0 1-144.8-60 204 204 0 0 1-60-144.8 204 204 0 0 1 60-144.8 204 204 0 0 1 144.8-60V0a256 256 0 1 0 0 512 256 256 0 0 0 0-512v25.6z"/><path d="M256 179.2v25.6a51.3 51.3 0 0 1 0 102.4 51.3 51.3 0 0 1 0-102.4v-51.2a102.3 102.3 0 1 0-.1 204.7 102.3 102.3 0 0 0 .1-204.7v25.6z"/><path d="M256 204.8h217.6a25.6 25.6 0 0 0 0-51.2H256a25.6 25.6 0 0 0 0 51.2m44.3 76.8L191.5 470.1a25.6 25.6 0 1 0 44.4 25.6l108.8-188.5a25.6 25.6 0 1 0-44.4-25.6m-88.6 0L102.9 93.2a25.7 25.7 0 0 0-35-9.4 25.7 25.7 0 0 0-9.4 35l108.8 188.5a25.7 25.7 0 0 0 35 9.4 25.9 25.9 0 0 0 9.4-35.1"/></svg>\');\n}\n\n\n\n.lh-buttons {\n display: flex;\n flex-wrap: wrap;\n margin: var(--default-padding) 0;\n}\n.lh-button {\n height: 32px;\n border: 1px solid var(--report-border-color-secondary);\n border-radius: 3px;\n color: var(--link-color);\n background-color: var(--report-background-color);\n margin: 5px;\n}\n\n.lh-button:first-of-type {\n margin-left: 0;\n}\n\n/* Node */\n.lh-node__snippet {\n font-family: var(--report-font-family-monospace);\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n line-height: 20px;\n}\n\n/* Score */\n\n.lh-audit__score-icon {\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n margin: var(--score-icon-margin);\n}\n\n.lh-audit--pass .lh-audit__display-text {\n color: var(--color-pass-secondary);\n}\n.lh-audit--pass .lh-audit__score-icon,\n.lh-scorescale-range--pass::before {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-audit__display-text {\n color: var(--color-average-secondary);\n}\n.lh-audit--average .lh-audit__score-icon,\n.lh-scorescale-range--average::before {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-audit--fail .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n.lh-audit--fail .lh-audit__score-icon,\n.lh-audit--error .lh-audit__score-icon,\n.lh-scorescale-range--fail::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-audit--manual .lh-audit__display-text,\n.lh-audit--notapplicable .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n.lh-audit--manual .lh-audit__score-icon,\n.lh-audit--notapplicable .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n background: none;\n}\n\n.lh-audit--informative .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n\n.lh-audit--informative .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n}\n\n.lh-audit__description,\n.lh-audit__stackpack {\n color: var(--report-text-color-secondary);\n}\n.lh-audit__adorn {\n border: 1px solid slategray;\n border-radius: 3px;\n margin: 0 3px;\n padding: 0 2px;\n line-height: 1.1;\n display: inline-block;\n font-size: 90%;\n}\n\n.lh-category-header__description {\n text-align: center;\n color: var(--color-gray-700);\n margin: 0px auto;\n max-width: 400px;\n}\n\n\n.lh-audit__display-text,\n.lh-load-opportunity__sparkline,\n.lh-chevron-container {\n margin: 0 var(--audit-margin-horizontal);\n}\n.lh-chevron-container {\n margin-right: 0;\n}\n\n.lh-audit__title-and-text {\n flex: 1;\n}\n\n.lh-audit__title-and-text code {\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n}\n\n/* Prepend display text with em dash separator. But not in Opportunities. */\n.lh-audit__display-text:not(:empty):before {\n content: \'—\';\n margin-right: var(--audit-margin-horizontal);\n}\n.lh-audit-group.lh-audit-group--load-opportunities .lh-audit__display-text:not(:empty):before {\n display: none;\n}\n\n/* Expandable Details (Audit Groups, Audits) */\n.lh-audit__header {\n display: flex;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-audit--load-opportunity .lh-audit__header {\n display: block;\n}\n\n\n.lh-metricfilter {\n display: grid;\n justify-content: end;\n align-items: center;\n grid-auto-flow: column;\n gap: 4px;\n color: var(--color-gray-700);\n}\n\n.lh-metricfilter__radio {\n position: absolute;\n left: -9999px;\n}\n.lh-metricfilter input[type=\'radio\']:focus-visible + label {\n outline: -webkit-focus-ring-color auto 1px;\n}\n\n.lh-metricfilter__label {\n display: inline-flex;\n padding: 0 4px;\n height: 16px;\n text-decoration: underline;\n align-items: center;\n cursor: pointer;\n font-size: 90%;\n}\n\n.lh-metricfilter__label--active {\n background: var(--color-blue-primary);\n color: var(--color-white);\n border-radius: 3px;\n text-decoration: none;\n}\n/* Give the \'All\' choice a more muted display */\n.lh-metricfilter__label--active[for="metric-All"] {\n background-color: var(--color-blue-200) !important;\n color: black !important;\n}\n\n.lh-metricfilter__text {\n margin-right: 8px;\n}\n\n/* If audits are filtered, hide the itemcount for Passed Audits… */\n.lh-category--filtered .lh-audit-group .lh-audit-group__itemcount {\n display: none;\n}\n\n\n.lh-audit__header:hover {\n background-color: var(--color-hover);\n}\n\n/* We want to hide the browser\'s default arrow marker on summary elements. Admittedly, it\'s complicated. */\n.lh-root details > summary {\n /* Blink 89+ and Firefox will hide the arrow when display is changed from (new) default of `list-item` to block. https://chromestatus.com/feature/6730096436051968*/\n display: block;\n}\n/* Safari and Blink <=88 require using the -webkit-details-marker selector */\n.lh-root details > summary::-webkit-details-marker {\n display: none;\n}\n\n/* Perf Metric */\n\n.lh-metrics-container {\n display: grid;\n grid-auto-rows: 1fr;\n grid-template-columns: 1fr 1fr;\n grid-column-gap: var(--report-line-height);\n margin-bottom: var(--default-padding);\n}\n\n.lh-metric {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-metric:nth-last-child(-n+2) {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n\n.lh-metric__innerwrap {\n display: grid;\n /**\n * Icon -- Metric Name\n * -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 1fr;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-metric__details {\n order: -1;\n}\n\n.lh-metric__title {\n flex: 1;\n}\n\n.lh-calclink {\n padding-left: calc(1ex / 3);\n}\n\n.lh-metric__description {\n display: none;\n grid-column-start: 2;\n grid-column-end: 4;\n color: var(--report-text-color-secondary);\n}\n\n.lh-metric__value {\n font-size: var(--metric-value-font-size);\n margin: calc(var(--default-padding) / 2) 0;\n white-space: nowrap; /* No wrapping between metric value and the icon */\n grid-column-start: 2;\n}\n\n\n@media screen and (max-width: 535px) {\n .lh-metrics-container {\n display: block;\n }\n\n .lh-metric {\n border-bottom: none !important;\n }\n .lh-metric:nth-last-child(1) {\n border-bottom: 1px solid var(--report-border-color-secondary) !important;\n }\n\n /* Change the grid to 3 columns for narrow viewport. */\n .lh-metric__innerwrap {\n /**\n * Icon -- Metric Name -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 2fr 1fr;\n }\n .lh-metric__value {\n justify-self: end;\n grid-column-start: unset;\n }\n}\n\n/* No-JS toggle switch */\n/* Keep this selector sync\'d w/ `magicSelector` in report-ui-features-test.js */\n .lh-metrics-toggle__input:checked ~ .lh-metrics-container .lh-metric__description {\n display: block;\n}\n\n/* TODO get rid of the SVGS and clean up these some more */\n.lh-metrics-toggle__input {\n opacity: 0;\n position: absolute;\n right: 0;\n top: 0px;\n}\n\n.lh-metrics-toggle__input + div > label > .lh-metrics-toggle__labeltext--hide,\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--show {\n display: none;\n}\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--hide {\n display: inline;\n}\n.lh-metrics-toggle__input:focus + div > label {\n outline: -webkit-focus-ring-color auto 3px;\n}\n\n.lh-metrics-toggle__label {\n cursor: pointer;\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n/* Pushes the metric description toggle button to the right. */\n.lh-audit-group--metrics .lh-audit-group__header {\n display: flex;\n justify-content: space-between;\n}\n\n.lh-metric__icon,\n.lh-scorescale-range::before {\n content: \'\';\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n display: inline-block;\n margin: var(--score-icon-margin);\n}\n\n.lh-metric--pass .lh-metric__value {\n color: var(--color-pass-secondary);\n}\n.lh-metric--pass .lh-metric__icon {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-metric--average .lh-metric__value {\n color: var(--color-average-secondary);\n}\n.lh-metric--average .lh-metric__icon {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-metric--fail .lh-metric__value {\n color: var(--color-fail-secondary);\n}\n.lh-metric--fail .lh-metric__icon,\n.lh-metric--error .lh-metric__icon {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-metric--error .lh-metric__value,\n.lh-metric--error .lh-metric__description {\n color: var(--color-fail-secondary);\n}\n\n/* Perf load opportunity */\n\n.lh-load-opportunity__cols {\n display: flex;\n align-items: flex-start;\n}\n\n.lh-load-opportunity__header .lh-load-opportunity__col {\n color: var(--color-gray-600);\n display: unset;\n line-height: calc(2.3 * var(--report-font-size));\n}\n\n.lh-load-opportunity__col {\n display: flex;\n}\n\n.lh-load-opportunity__col--one {\n flex: 5;\n align-items: center;\n margin-right: 2px;\n}\n.lh-load-opportunity__col--two {\n flex: 4;\n text-align: right;\n}\n\n.lh-audit--load-opportunity .lh-audit__display-text {\n text-align: right;\n flex: 0 0 calc(3 * var(--report-font-size));\n}\n\n\n/* Sparkline */\n\n.lh-load-opportunity__sparkline {\n flex: 1;\n margin-top: calc((var(--report-line-height) - var(--sparkline-height)) / 2);\n}\n\n.lh-sparkline {\n height: var(--sparkline-height);\n width: 100%;\n}\n\n.lh-sparkline__bar {\n height: 100%;\n float: right;\n}\n\n.lh-audit--pass .lh-sparkline__bar {\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-sparkline__bar {\n background: var(--color-average);\n}\n\n.lh-audit--fail .lh-sparkline__bar {\n background: var(--color-fail);\n}\n\n/* Filmstrip */\n\n.lh-filmstrip-container {\n /* smaller gap between metrics and filmstrip */\n margin: -8px auto 0 auto;\n}\n\n.lh-filmstrip {\n display: grid;\n justify-content: space-between;\n padding-bottom: var(--default-padding);\n width: 100%;\n grid-template-columns: repeat(auto-fit, 60px);\n}\n\n.lh-filmstrip__frame {\n text-align: right;\n position: relative;\n}\n\n.lh-filmstrip__thumbnail {\n border: 1px solid var(--report-border-color-secondary);\n max-height: 100px;\n max-width: 60px;\n}\n\n/* Audit */\n\n.lh-audit {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n/* Apply border-top to just the first audit. */\n.lh-audit {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n.lh-audit ~ .lh-audit {\n border-top: none;\n}\n\n\n.lh-audit--error .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n\n/* Audit Group */\n\n.lh-audit-group {\n margin-bottom: var(--audit-group-margin-bottom);\n position: relative;\n}\n.lh-audit-group--metrics {\n margin-bottom: calc(var(--audit-group-margin-bottom) / 2);\n}\n\n.lh-audit-group__header::before {\n /* By default, groups don\'t get an icon */\n content: none;\n width: var(--pwa-icon-size);\n height: var(--pwa-icon-size);\n margin: var(--pwa-icon-margin);\n display: inline-block;\n vertical-align: middle;\n}\n\n/* Style the "over budget" columns red. */\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(4),\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(5),\n.lh-audit-group--budgets #timing-budget tbody tr td:nth-child(3) {\n color: var(--color-red-700);\n}\n\n/* Align the "over budget request count" text to be close to the "over budget bytes" column. */\n.lh-audit-group--budgets .lh-table tbody tr td:nth-child(4){\n text-align: right;\n}\n\n.lh-audit-group--budgets .lh-details--budget {\n width: 100%;\n margin: 0 0 var(--default-padding);\n}\n\n.lh-audit-group--pwa-installable .lh-audit-group__header::before {\n content: \'\';\n background-image: var(--pwa-installable-gray-url);\n}\n.lh-audit-group--pwa-optimized .lh-audit-group__header::before {\n content: \'\';\n background-image: var(--pwa-optimized-gray-url);\n}\n.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-installable-color-url);\n}\n.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-optimized-color-url);\n}\n\n.lh-audit-group--metrics .lh-audit-group__summary {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.lh-audit-group__summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.lh-audit-group__header .lh-chevron {\n margin-top: calc((var(--report-line-height) - 5px) / 2);\n}\n\n.lh-audit-group__header {\n letter-spacing: 0.8px;\n padding: var(--default-padding);\n padding-left: 0;\n}\n\n.lh-audit-group__header, .lh-audit-group__summary {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n.lh-audit-group__title {\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.lh-audit-group__itemcount {\n color: var(--color-gray-600);\n}\n\n.lh-audit-group__footer {\n color: var(--color-gray-600);\n display: block;\n margin-top: var(--default-padding);\n}\n\n.lh-details,\n.lh-category-header__description,\n.lh-load-opportunity__header,\n.lh-audit-group__footer {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n}\n\n.lh-audit-explanation {\n margin: var(--audit-padding-vertical) 0 calc(var(--audit-padding-vertical) / 2) var(--audit-margin-horizontal);\n line-height: var(--audit-explanation-line-height);\n display: inline-block;\n}\n\n.lh-audit--fail .lh-audit-explanation {\n color: var(--color-fail-secondary);\n}\n\n/* Report */\n.lh-list > :not(:last-child) {\n margin-bottom: calc(var(--default-padding) * 2);\n}\n\n.lh-header-container {\n display: block;\n margin: 0 auto;\n position: relative;\n word-wrap: break-word;\n}\n\n.lh-header-container .lh-scores-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n\n\n.lh-report {\n min-width: var(--report-content-min-width);\n}\n\n.lh-exception {\n font-size: large;\n}\n\n.lh-code {\n white-space: normal;\n margin-top: 0;\n font-size: var(--report-monospace-font-size);\n}\n\n.lh-warnings {\n --item-margin: calc(var(--report-line-height) / 6);\n color: var(--color-average-secondary);\n margin: var(--audit-padding-vertical) 0;\n padding: var(--default-padding)\n var(--default-padding)\n var(--default-padding)\n calc(var(--audit-description-padding-left));\n background-color: var(--toplevel-warning-background-color);\n}\n.lh-warnings span {\n font-weight: bold;\n}\n\n.lh-warnings--toplevel {\n --item-margin: calc(var(--header-line-height) / 4);\n color: var(--toplevel-warning-text-color);\n margin-left: auto;\n margin-right: auto;\n max-width: var(--report-content-max-width-minus-edge-gap);\n padding: var(--toplevel-warning-padding);\n border-radius: 8px;\n}\n\n.lh-warnings__msg {\n color: var(--toplevel-warning-message-text-color);\n margin: 0;\n}\n\n.lh-warnings ul {\n margin: 0;\n}\n.lh-warnings li {\n margin: var(--item-margin) 0;\n}\n.lh-warnings li:last-of-type {\n margin-bottom: 0;\n}\n\n.lh-scores-header {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n}\n.lh-scores-header__solo {\n padding: 0;\n border: 0;\n}\n\n/* Gauge */\n\n.lh-gauge__wrapper--pass {\n color: var(--color-pass-secondary);\n fill: var(--color-pass);\n stroke: var(--color-pass);\n}\n\n.lh-gauge__wrapper--average {\n color: var(--color-average-secondary);\n fill: var(--color-average);\n stroke: var(--color-average);\n}\n\n.lh-gauge__wrapper--fail {\n color: var(--color-fail-secondary);\n fill: var(--color-fail);\n stroke: var(--color-fail);\n}\n\n.lh-gauge__wrapper--not-applicable {\n color: var(--color-not-applicable);\n fill: var(--color-not-applicable);\n stroke: var(--color-not-applicable);\n}\n\n.lh-fraction__wrapper .lh-fraction__content::before {\n content: \'\';\n height: var(--score-icon-size);\n width: var(--score-icon-size);\n margin: var(--score-icon-margin);\n display: inline-block;\n}\n.lh-fraction__wrapper--pass .lh-fraction__content {\n color: var(--color-pass-secondary);\n}\n.lh-fraction__wrapper--pass .lh-fraction__background {\n background-color: var(--color-pass);\n}\n.lh-fraction__wrapper--pass .lh-fraction__content::before {\n background-color: var(--color-pass);\n border-radius: 50%;\n}\n.lh-fraction__wrapper--average .lh-fraction__content {\n color: var(--color-average-secondary);\n}\n.lh-fraction__wrapper--average .lh-fraction__background,\n.lh-fraction__wrapper--average .lh-fraction__content::before {\n background-color: var(--color-average);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content {\n color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__background {\n background-color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n.lh-fraction__wrapper--null .lh-fraction__content {\n color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__background {\n background-color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__content::before {\n border-radius: 50%;\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-700);\n}\n\n.lh-fraction__background {\n position: absolute;\n height: 100%;\n width: 100%;\n border-radius: calc(var(--gauge-circle-size) / 2);\n opacity: 0.1;\n z-index: -1;\n}\n\n.lh-fraction__content-wrapper {\n height: var(--gauge-circle-size);\n display: flex;\n align-items: center;\n}\n\n.lh-fraction__content {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: center;\n font-size: calc(0.3 * var(--gauge-circle-size));\n line-height: calc(0.4 * var(--gauge-circle-size));\n width: max-content;\n min-width: calc(1.5 * var(--gauge-circle-size));\n padding: calc(0.1 * var(--gauge-circle-size)) calc(0.2 * var(--gauge-circle-size));\n --score-icon-size: calc(0.21 * var(--gauge-circle-size));\n --score-icon-margin: 0 calc(0.15 * var(--gauge-circle-size)) 0 0;\n}\n\n.lh-gauge {\n stroke-linecap: round;\n width: var(--gauge-circle-size);\n height: var(--gauge-circle-size);\n}\n\n.lh-category .lh-gauge {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n.lh-gauge-base {\n opacity: 0.1;\n}\n\n.lh-gauge-arc {\n fill: none;\n transform-origin: 50% 50%;\n animation: load-gauge var(--transition-length) ease forwards;\n animation-delay: 250ms;\n}\n\n.lh-gauge__svg-wrapper {\n position: relative;\n height: var(--gauge-circle-size);\n}\n.lh-category .lh-gauge__svg-wrapper,\n.lh-category .lh-fraction__wrapper {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n/* The plugin badge overlay */\n.lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size);\n height: var(--plugin-badge-size);\n background-color: var(--plugin-badge-background-color);\n background-image: var(--plugin-icon-url);\n background-repeat: no-repeat;\n background-size: var(--plugin-icon-size);\n background-position: 58% 50%;\n content: "";\n position: absolute;\n right: -6px;\n bottom: 0px;\n display: block;\n z-index: 100;\n box-shadow: 0 0 4px rgba(0,0,0,.2);\n border-radius: 25%;\n}\n.lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size-big);\n height: var(--plugin-badge-size-big);\n}\n\n@keyframes load-gauge {\n from { stroke-dasharray: 0 352; }\n}\n\n.lh-gauge__percentage {\n width: 100%;\n height: var(--gauge-circle-size);\n position: absolute;\n font-family: var(--report-font-family-monospace);\n font-size: calc(var(--gauge-circle-size) * 0.34 + 1.3px);\n line-height: 0;\n text-align: center;\n top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 2);\n}\n\n.lh-category .lh-gauge__percentage {\n --gauge-circle-size: var(--gauge-circle-size-big);\n --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);\n}\n\n.lh-gauge__wrapper,\n.lh-fraction__wrapper {\n position: relative;\n display: flex;\n align-items: center;\n flex-direction: column;\n text-decoration: none;\n padding: var(--score-container-padding);\n\n --transition-length: 1s;\n\n /* Contain the layout style paint & layers during animation*/\n contain: content;\n will-change: opacity; /* Only using for layer promotion */\n}\n\n.lh-gauge__label,\n.lh-fraction__label {\n font-size: var(--gauge-label-font-size);\n font-weight: 500;\n line-height: var(--gauge-label-line-height);\n margin-top: 10px;\n text-align: center;\n color: var(--report-text-color);\n word-break: keep-all;\n}\n\n/* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */\n.lh-category .lh-gauge__label,\n.lh-category .lh-fraction__label {\n --gauge-label-font-size: var(--gauge-label-font-size-big);\n --gauge-label-line-height: var(--gauge-label-line-height-big);\n margin-top: 14px;\n}\n\n.lh-scores-header .lh-gauge__wrapper,\n.lh-scores-header .lh-fraction__wrapper,\n.lh-scores-header .lh-gauge--pwa__wrapper,\n.lh-sticky-header .lh-gauge__wrapper,\n.lh-sticky-header .lh-fraction__wrapper,\n.lh-sticky-header .lh-gauge--pwa__wrapper {\n width: var(--gauge-wrapper-width);\n}\n\n.lh-scorescale {\n display: inline-flex;\n\n gap: calc(var(--default-padding) * 4);\n margin: 16px auto 0 auto;\n font-size: var(--report-font-size-secondary);\n color: var(--color-gray-700);\n\n}\n\n.lh-scorescale-range {\n display: flex;\n align-items: center;\n font-family: var(--report-font-family-monospace);\n white-space: nowrap;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale {\n border: 0;\n display: flex;\n justify-content: center;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale-range {\n font-family: unset;\n font-size: 12px;\n}\n\n.lh-scorescale-wrap {\n display: contents;\n}\n\n/* Hide category score gauages if it\'s a single category report */\n.lh-header--solo-category .lh-scores-wrapper {\n display: none;\n}\n\n\n.lh-categories {\n width: 100%;\n overflow: hidden;\n}\n\n.lh-category {\n padding: var(--category-padding);\n max-width: var(--report-content-max-width);\n margin: 0 auto;\n\n --sticky-header-height: calc(var(--gauge-circle-size-sm) + var(--score-container-padding) * 2);\n --topbar-plus-sticky-header: calc(var(--topbar-height) + var(--sticky-header-height));\n scroll-margin-top: var(--topbar-plus-sticky-header);\n\n /* Faster recalc style & layout of the report. https://web.dev/content-visibility/ */\n content-visibility: auto;\n contain-intrinsic-size: 1000px;\n}\n\n.lh-category-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n\n.lh-category-header {\n margin-bottom: var(--section-padding-vertical);\n}\n\n.lh-category-header .lh-score__gauge {\n max-width: 400px;\n width: auto;\n margin: 0px auto;\n}\n\n.lh-category-header__finalscreenshot {\n display: grid;\n grid-template: none / 1fr 1px 1fr;\n justify-items: center;\n align-items: center;\n gap: var(--report-line-height);\n min-height: 288px;\n margin-bottom: var(--default-padding);\n}\n\n.lh-final-ss-image {\n /* constrain the size of the image to not be too large */\n max-height: calc(var(--gauge-circle-size-big) * 2.8);\n max-width: calc(var(--gauge-circle-size-big) * 3.5);\n border: 1px solid var(--color-gray-200);\n padding: 4px;\n border-radius: 3px;\n display: block;\n}\n\n.lh-category-headercol--separator {\n background: var(--color-gray-200);\n width: 1px;\n height: var(--gauge-circle-size-big);\n}\n\n@media screen and (max-width: 780px) {\n .lh-category-header__finalscreenshot {\n grid-template: 1fr 1fr / none\n }\n .lh-category-headercol--separator {\n display: none;\n }\n}\n\n\n/* 964 fits the min-width of the filmstrip */\n@media screen and (max-width: 964px) {\n .lh-report {\n margin-left: 0;\n width: 100%;\n }\n}\n\n@media print {\n body {\n -webkit-print-color-adjust: exact; /* print background colors */\n }\n .lh-container {\n display: block;\n }\n .lh-report {\n margin-left: 0;\n padding-top: 0;\n }\n .lh-categories {\n margin-top: 0;\n }\n}\n\n.lh-table {\n border-collapse: collapse;\n /* Can\'t assign padding to table, so shorten the width instead. */\n width: calc(100% - var(--audit-description-padding-left) - var(--stackpack-padding-horizontal));\n border: 1px solid var(--report-border-color-secondary);\n\n}\n\n.lh-table thead th {\n font-weight: normal;\n color: var(--color-gray-600);\n /* See text-wrapping comment on .lh-container. */\n word-break: normal;\n}\n\n.lh-row--even {\n background-color: var(--table-higlight-background-color);\n}\n.lh-row--hidden {\n display: none;\n}\n\n.lh-table th,\n.lh-table td {\n padding: var(--default-padding);\n}\n\n.lh-table tr {\n vertical-align: middle;\n}\n\n/* Looks unnecessary, but mostly for keeping the <th>s left-aligned */\n.lh-table-column--text,\n.lh-table-column--source-location,\n.lh-table-column--url,\n/* .lh-table-column--thumbnail, */\n/* .lh-table-column--empty,*/\n.lh-table-column--code,\n.lh-table-column--node {\n text-align: left;\n}\n\n.lh-table-column--code {\n min-width: 100px;\n}\n\n.lh-table-column--bytes,\n.lh-table-column--timespanMs,\n.lh-table-column--ms,\n.lh-table-column--numeric {\n text-align: right;\n word-break: normal;\n}\n\n\n\n.lh-table .lh-table-column--thumbnail {\n width: var(--image-preview-size);\n}\n\n.lh-table-column--url {\n min-width: 250px;\n}\n\n.lh-table-column--text {\n min-width: 80px;\n}\n\n/* Keep columns narrow if they follow the URL column */\n/* 12% was determined to be a decent narrow width, but wide enough for column headings */\n.lh-table-column--url + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--ms,\n.lh-table-column--url + .lh-table-column--ms + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--timespanMs {\n width: 12%;\n}\n\n.lh-text__url-host {\n display: inline;\n}\n\n.lh-text__url-host {\n margin-left: calc(var(--report-font-size) / 2);\n opacity: 0.6;\n font-size: 90%\n}\n\n.lh-thumbnail {\n object-fit: cover;\n width: var(--image-preview-size);\n height: var(--image-preview-size);\n display: block;\n}\n\n.lh-unknown pre {\n overflow: scroll;\n border: solid 1px var(--color-gray-200);\n}\n\n.lh-text__url > a {\n color: inherit;\n text-decoration: none;\n}\n\n.lh-text__url > a:hover {\n text-decoration: underline dotted #999;\n}\n\n.lh-sub-item-row {\n margin-left: 20px;\n margin-bottom: 0;\n color: var(--color-gray-700);\n}\n.lh-sub-item-row td {\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 20px;\n}\n\n/* Chevron\n https://codepen.io/paulirish/pen/LmzEmK\n */\n.lh-chevron {\n --chevron-angle: 42deg;\n /* Edge doesn\'t support transform: rotate(calc(...)), so we define it here */\n --chevron-angle-right: -42deg;\n width: var(--chevron-size);\n height: var(--chevron-size);\n margin-top: calc((var(--report-line-height) - 12px) / 2);\n}\n\n.lh-chevron__lines {\n transition: transform 0.4s;\n transform: translateY(var(--report-line-height));\n}\n.lh-chevron__line {\n stroke: var(--chevron-line-stroke);\n stroke-width: var(--chevron-size);\n stroke-linecap: square;\n transform-origin: 50%;\n transform: rotate(var(--chevron-angle));\n transition: transform 300ms, stroke 300ms;\n}\n\n.lh-expandable-details .lh-chevron__line-right,\n.lh-expandable-details[open] .lh-chevron__line-left {\n transform: rotate(var(--chevron-angle-right));\n}\n\n.lh-expandable-details[open] .lh-chevron__line-right {\n transform: rotate(var(--chevron-angle));\n}\n\n\n.lh-expandable-details[open] .lh-chevron__lines {\n transform: translateY(calc(var(--chevron-size) * -1));\n}\n\n.lh-expandable-details[open] {\n animation: 300ms openDetails forwards;\n padding-bottom: var(--default-padding);\n}\n\n@keyframes openDetails {\n from {\n outline: 1px solid var(--report-background-color);\n }\n to {\n outline: 1px solid;\n box-shadow: 0 2px 4px rgba(0, 0, 0, .24);\n }\n}\n\n@media screen and (max-width: 780px) {\n /* no black outline if we\'re not confident the entire table can be displayed within bounds */\n .lh-expandable-details[open] {\n animation: none;\n }\n}\n\n.lh-expandable-details[open] summary, details.lh-clump > summary {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\ndetails.lh-clump[open] > summary {\n border-bottom-width: 0;\n}\n\n\n\ndetails .lh-clump-toggletext--hide,\ndetails[open] .lh-clump-toggletext--show { display: none; }\ndetails[open] .lh-clump-toggletext--hide { display: block;}\n\n\n/* Tooltip */\n.lh-tooltip-boundary {\n position: relative;\n}\n\n.lh-tooltip {\n position: absolute;\n display: none; /* Don\'t retain these layers when not needed */\n opacity: 0;\n background: #ffffff;\n white-space: pre-line; /* Render newlines in the text */\n min-width: 246px;\n max-width: 275px;\n padding: 15px;\n border-radius: 5px;\n text-align: initial;\n line-height: 1.4;\n}\n/* shrink tooltips to not be cutoff on left edge of narrow viewports\n 45vw is chosen to be ~= width of the left column of metrics\n*/\n@media screen and (max-width: 535px) {\n .lh-tooltip {\n min-width: 45vw;\n padding: 3vw;\n }\n}\n\n.lh-tooltip-boundary:hover .lh-tooltip {\n display: block;\n animation: fadeInTooltip 250ms;\n animation-fill-mode: forwards;\n animation-delay: 850ms;\n bottom: 100%;\n z-index: 1;\n will-change: opacity;\n right: 0;\n pointer-events: none;\n}\n\n.lh-tooltip::before {\n content: "";\n border: solid transparent;\n border-bottom-color: #fff;\n border-width: 10px;\n position: absolute;\n bottom: -20px;\n right: 6px;\n transform: rotate(180deg);\n pointer-events: none;\n}\n\n@keyframes fadeInTooltip {\n 0% { opacity: 0; }\n 75% { opacity: 1; }\n 100% { opacity: 1; filter: drop-shadow(1px 0px 1px #aaa) drop-shadow(0px 2px 4px hsla(206, 6%, 25%, 0.15)); pointer-events: auto; }\n}\n\n/* Element screenshot */\n.lh-element-screenshot {\n position: relative;\n overflow: hidden;\n float: left;\n margin-right: 20px;\n}\n.lh-element-screenshot__content {\n overflow: hidden;\n}\n.lh-element-screenshot__image {\n /* Set by ElementScreenshotRenderer.installFullPageScreenshotCssVariable */\n background-image: var(--element-screenshot-url);\n outline: 2px solid #777;\n background-color: white;\n background-repeat: no-repeat;\n}\n.lh-element-screenshot__mask {\n position: absolute;\n background: #555;\n opacity: 0.8;\n}\n.lh-element-screenshot__element-marker {\n position: absolute;\n outline: 2px solid var(--color-lime-400);\n}\n.lh-element-screenshot__overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2000; /* .lh-topbar is 1000 */\n background: var(--screenshot-overlay-background);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n\n.lh-element-screenshot__overlay .lh-element-screenshot {\n margin-right: 0; /* clearing margin used in thumbnail case */\n outline: 1px solid var(--color-gray-700);\n}\n\n.lh-screenshot-overlay--enabled .lh-element-screenshot {\n cursor: zoom-out;\n}\n.lh-screenshot-overlay--enabled .lh-node .lh-element-screenshot {\n cursor: zoom-in;\n}\n\n\n.lh-meta__items {\n --meta-icon-size: calc(var(--report-icon-size) * 0.667);\n padding: var(--default-padding);\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n background-color: var(--env-item-background-color);\n border-radius: 3px;\n margin: 0 0 var(--default-padding) 0;\n font-size: 12px;\n column-gap: var(--default-padding);\n color: var(--color-gray-700);\n}\n\n.lh-meta__item {\n display: block;\n list-style-type: none;\n position: relative;\n padding: 0 0 0 calc(var(--meta-icon-size) + var(--default-padding) * 2);\n cursor: unset; /* disable pointer cursor from report-icon */\n}\n\n.lh-meta__item.lh-tooltip-boundary {\n text-decoration: dotted underline var(--color-gray-500);\n cursor: help;\n}\n\n.lh-meta__item.lh-report-icon::before {\n position: absolute;\n left: var(--default-padding);\n width: var(--meta-icon-size);\n height: var(--meta-icon-size);\n}\n\n.lh-meta__item.lh-report-icon:hover::before {\n opacity: 0.7;\n}\n\n.lh-meta__item .lh-tooltip {\n color: var(--color-gray-800);\n}\n\n.lh-meta__item .lh-tooltip::before {\n right: auto; /* Set the tooltip arrow to the leftside */\n left: 6px;\n}\n\n/* Change the grid for narrow viewport. */\n@media screen and (max-width: 640px) {\n .lh-meta__items {\n grid-template-columns: 1fr 1fr;\n }\n}\n@media screen and (max-width: 535px) {\n .lh-meta__items {\n display: block;\n }\n}\n\n\n/*# sourceURL=report-styles.css */\n');
|
|
851
|
+
const el1 = dom.createElement("style");
|
|
852
|
+
el1.append("/**\n * @license\n * Copyright 2017 The Lighthouse Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS-IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Naming convention:\n\n If a variable is used for a specific component: --{component}-{property name}-{modifier}\n\n Both {component} and {property name} should be kebab-case. If the target is the entire page,\n use 'report' for the component. The property name should not be abbreviated. Use the\n property name the variable is intended for - if it's used for multiple, a common descriptor\n is fine (ex: 'size' for a variable applied to 'width' and 'height'). If a variable is shared\n across multiple components, either create more variables or just drop the \"{component}-\"\n part of the name. Append any modifiers at the end (ex: 'big', 'dark').\n\n For colors: --color-{hue}-{intensity}\n\n {intensity} is the Material Design tag - 700, A700, etc.\n*/\n.lh-vars {\n /* Palette using Material Design Colors\n * https://www.materialui.co/colors */\n --color-amber-50: #FFF8E1;\n --color-blue-200: #90CAF9;\n --color-blue-900: #0D47A1;\n --color-blue-A700: #2962FF;\n --color-blue-primary: #06f;\n --color-cyan-500: #00BCD4;\n --color-gray-100: #F5F5F5;\n --color-gray-300: #CFCFCF;\n --color-gray-200: #E0E0E0;\n --color-gray-400: #BDBDBD;\n --color-gray-50: #FAFAFA;\n --color-gray-500: #9E9E9E;\n --color-gray-600: #757575;\n --color-gray-700: #616161;\n --color-gray-800: #424242;\n --color-gray-900: #212121;\n --color-gray: #000000;\n --color-green-700: #080;\n --color-green: #0c6;\n --color-lime-400: #D3E156;\n --color-orange-50: #FFF3E0;\n --color-orange-700: #C33300;\n --color-orange: #fa3;\n --color-red-700: #c00;\n --color-red: #f33;\n --color-teal-600: #00897B;\n --color-white: #FFFFFF;\n\n /* Context-specific colors */\n --color-average-secondary: var(--color-orange-700);\n --color-average: var(--color-orange);\n --color-fail-secondary: var(--color-red-700);\n --color-fail: var(--color-red);\n --color-hover: var(--color-gray-50);\n --color-informative: var(--color-blue-900);\n --color-pass-secondary: var(--color-green-700);\n --color-pass: var(--color-green);\n --color-not-applicable: var(--color-gray-600);\n\n /* Component variables */\n --audit-description-padding-left: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right));\n --audit-explanation-line-height: 16px;\n --audit-group-margin-bottom: calc(var(--default-padding) * 6);\n --audit-group-padding-vertical: 8px;\n --audit-margin-horizontal: 5px;\n --audit-padding-vertical: 8px;\n --category-padding: calc(var(--default-padding) * 6) var(--edge-gap-padding) calc(var(--default-padding) * 4);\n --chevron-line-stroke: var(--color-gray-600);\n --chevron-size: 12px;\n --default-padding: 8px;\n --edge-gap-padding: calc(var(--default-padding) * 4);\n --env-item-background-color: var(--color-gray-100);\n --env-item-font-size: 28px;\n --env-item-line-height: 36px;\n --env-item-padding: 10px 0px;\n --env-name-min-width: 220px;\n --footer-padding-vertical: 16px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 48px;\n --gauge-circle-size-sm: 32px;\n --gauge-label-font-size-big: 18px;\n --gauge-label-font-size: var(--report-font-size-secondary);\n --gauge-label-line-height-big: 24px;\n --gauge-label-line-height: var(--report-line-height-secondary);\n --gauge-percentage-font-size-big: 38px;\n --gauge-percentage-font-size: var(--report-font-size-secondary);\n --gauge-wrapper-width: 120px;\n --header-line-height: 24px;\n --highlighter-background-color: var(--report-text-color);\n --icon-square-size: calc(var(--score-icon-size) * 0.88);\n --image-preview-size: 48px;\n --link-color: var(--color-blue-primary);\n --locale-selector-background-color: var(--color-white);\n --metric-toggle-lines-fill: #7F7F7F;\n --metric-value-font-size: calc(var(--report-font-size) * 1.8);\n --metrics-toggle-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-white);\n --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);\n --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);\n --plugin-icon-size: 65%;\n --pwa-icon-margin: 0 var(--default-padding);\n --pwa-icon-size: var(--topbar-logo-size);\n --report-background-color: #fff;\n --report-border-color-secondary: #ebebeb;\n --report-font-family-monospace: 'Roboto Mono', 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: Roboto, Helvetica, Arial, sans-serif;\n --report-font-size: 14px;\n --report-font-size-secondary: 12px;\n --report-icon-size: var(--score-icon-background-size);\n --report-line-height: 24px;\n --report-line-height-secondary: 20px;\n --report-monospace-font-size: calc(var(--report-font-size) * 0.85);\n --report-text-color-secondary: var(--color-gray-800);\n --report-text-color: var(--color-gray-900);\n --report-content-max-width: calc(60 * var(--report-font-size)); /* defaults to 840px */\n --report-content-min-width: 360px;\n --report-content-max-width-minus-edge-gap: calc(var(--report-content-max-width) - var(--edge-gap-padding) * 2);\n --score-container-padding: 8px;\n --score-icon-background-size: 24px;\n --score-icon-margin-left: 6px;\n --score-icon-margin-right: 14px;\n --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);\n --score-icon-size: 12px;\n --score-icon-size-big: 16px;\n --screenshot-overlay-background: rgba(0, 0, 0, 0.3);\n --section-padding-vertical: calc(var(--default-padding) * 6);\n --snippet-background-color: var(--color-gray-50);\n --snippet-color: #0938C2;\n --sparkline-height: 5px;\n --stackpack-padding-horizontal: 10px;\n --sticky-header-background-color: var(--report-background-color);\n --sticky-header-buffer: calc(var(--topbar-height) + var(--sticky-header-height));\n --sticky-header-height: calc(var(--gauge-circle-size-sm) + var(--score-container-padding) * 2);\n --table-higlight-background-color: hsla(210, 17%, 77%, 0.1);\n --tools-icon-color: var(--color-gray-600);\n --topbar-background-color: var(--color-white);\n --topbar-height: 32px;\n --topbar-logo-size: 24px;\n --topbar-padding: 0 8px;\n --toplevel-warning-background-color: hsla(30, 100%, 75%, 10%);\n --toplevel-warning-message-text-color: var(--color-average-secondary);\n --toplevel-warning-padding: 18px;\n --toplevel-warning-text-color: var(--report-text-color);\n\n /* SVGs */\n --plugin-icon-url-dark: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24px\" height=\"24px\" viewBox=\"0 0 24 24\" fill=\"%23FFFFFF\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z\"/></svg>');\n --plugin-icon-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24px\" height=\"24px\" viewBox=\"0 0 24 24\" fill=\"%23757575\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z\"/></svg>');\n\n --pass-icon-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><title>check</title><path fill=\"%23178239\" d=\"M24 4C12.95 4 4 12.95 4 24c0 11.04 8.95 20 20 20 11.04 0 20-8.96 20-20 0-11.05-8.96-20-20-20zm-4 30L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z\"/></svg>');\n --average-icon-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><title>info</title><path fill=\"%23E67700\" d=\"M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z\"/></svg>');\n --fail-icon-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><title>warn</title><path fill=\"%23C7221F\" d=\"M2 42h44L24 4 2 42zm24-6h-4v-4h4v4zm0-8h-4v-8h4v8z\"/></svg>');\n\n --pwa-installable-gray-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"none\" fill-rule=\"nonzero\"><circle fill=\"%23DAE0E3\" cx=\"12\" cy=\"12\" r=\"12\"/><path d=\"M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z\" fill=\"%23FFF\"/></g></svg>');\n --pwa-optimized-gray-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"none\" fill-rule=\"evenodd\"><rect fill=\"%23DAE0E3\" width=\"24\" height=\"24\" rx=\"12\"/><path fill=\"%23FFF\" d=\"M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z\"/><path d=\"M5 5h14v14H5z\"/></g></svg>');\n\n --pwa-installable-gray-url-dark: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"none\" fill-rule=\"nonzero\"><circle fill=\"%23424242\" cx=\"12\" cy=\"12\" r=\"12\"/><path d=\"M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z\" fill=\"%23FFF\"/></g></svg>');\n --pwa-optimized-gray-url-dark: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"none\" fill-rule=\"evenodd\"><rect fill=\"%23424242\" width=\"24\" height=\"24\" rx=\"12\"/><path fill=\"%23FFF\" d=\"M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z\"/><path d=\"M5 5h14v14H5z\"/></g></svg>');\n\n --pwa-installable-color-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"nonzero\" fill=\"none\"><circle fill=\"%230CCE6B\" cx=\"12\" cy=\"12\" r=\"12\"/><path d=\"M12 5a7 7 0 1 0 0 14 7 7 0 0 0 0-14zm3.5 7.7h-2.8v2.8h-1.4v-2.8H8.5v-1.4h2.8V8.5h1.4v2.8h2.8v1.4z\" fill=\"%23FFF\"/></g></svg>');\n --pwa-optimized-color-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\"><g fill=\"none\" fill-rule=\"evenodd\"><rect fill=\"%230CCE6B\" width=\"24\" height=\"24\" rx=\"12\"/><path d=\"M5 5h14v14H5z\"/><path fill=\"%23FFF\" d=\"M12 15.07l3.6 2.18-.95-4.1 3.18-2.76-4.2-.36L12 6.17l-1.64 3.86-4.2.36 3.2 2.76-.96 4.1z\"/></g></svg>');\n\n --swap-locale-icon-url: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"#000000\"><path d=\"M0 0h24v24H0V0z\" fill=\"none\"/><path d=\"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z\"/></svg>');\n}\n\n@media not print {\n .lh-dark {\n /* Pallete */\n --color-gray-200: var(--color-gray-800);\n --color-gray-300: #616161;\n --color-gray-400: var(--color-gray-600);\n --color-gray-700: var(--color-gray-400);\n --color-gray-50: #757575;\n --color-gray-600: var(--color-gray-500);\n --color-green-700: var(--color-green);\n --color-orange-700: var(--color-orange);\n --color-red-700: var(--color-red);\n --color-teal-600: var(--color-cyan-500);\n\n /* Context-specific colors */\n --color-hover: rgba(0, 0, 0, 0.2);\n --color-informative: var(--color-blue-200);\n\n /* Component variables */\n --env-item-background-color: #393535;\n --link-color: var(--color-blue-200);\n --locale-selector-background-color: var(--color-gray-200);\n --plugin-badge-background-color: var(--color-gray-800);\n --report-background-color: var(--color-gray-900);\n --report-border-color-secondary: var(--color-gray-200);\n --report-text-color-secondary: var(--color-gray-400);\n --report-text-color: var(--color-gray-100);\n --snippet-color: var(--color-cyan-500);\n --topbar-background-color: var(--color-gray);\n --toplevel-warning-background-color: hsl(33deg 14% 18%);\n --toplevel-warning-message-text-color: var(--color-orange-700);\n --toplevel-warning-text-color: var(--color-gray-100);\n\n /* SVGs */\n --plugin-icon-url: var(--plugin-icon-url-dark);\n --pwa-installable-gray-url: var(--pwa-installable-gray-url-dark);\n --pwa-optimized-gray-url: var(--pwa-optimized-gray-url-dark);\n }\n}\n\n@media only screen and (max-width: 480px) {\n .lh-vars {\n --audit-group-margin-bottom: 20px;\n --edge-gap-padding: var(--default-padding);\n --env-name-min-width: 120px;\n --gauge-circle-size-big: 96px;\n --gauge-circle-size: 72px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 112px;\n --header-padding: 16px 0 16px 0;\n --image-preview-size: 24px;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-size: 14px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --topbar-height: 28px;\n --topbar-logo-size: 20px;\n }\n\n /* Not enough space to adequately show the relative savings bars. */\n .lh-sparkline {\n display: none;\n }\n}\n\n.lh-vars.lh-devtools {\n --audit-explanation-line-height: 14px;\n --audit-group-margin-bottom: 20px;\n --audit-group-padding-vertical: 12px;\n --audit-padding-vertical: 4px;\n --category-padding: 12px;\n --default-padding: 12px;\n --env-name-min-width: 120px;\n --footer-padding-vertical: 8px;\n --gauge-circle-size-big: 72px;\n --gauge-circle-size: 64px;\n --gauge-label-font-size-big: 22px;\n --gauge-label-font-size: 14px;\n --gauge-label-line-height-big: 26px;\n --gauge-label-line-height: 20px;\n --gauge-percentage-font-size-big: 34px;\n --gauge-percentage-font-size: 26px;\n --gauge-wrapper-width: 97px;\n --header-line-height: 20px;\n --header-padding: 16px 0 16px 0;\n --screenshot-overlay-background: transparent;\n --plugin-icon-size: 75%;\n --pwa-icon-margin: 0 7px 0 -3px;\n --report-font-family-monospace: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;\n --report-font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif;\n --report-font-size: 12px;\n --report-line-height: 20px;\n --score-icon-margin-left: 2px;\n --score-icon-size: 10px;\n --section-padding-vertical: 8px;\n}\n\n.lh-container:not(.lh-topbar + .lh-container) {\n --topbar-height: 0;\n --sticky-header-height: 0;\n --sticky-header-buffer: 0;\n}\n\n.lh-devtools.lh-root {\n height: 100%;\n}\n.lh-devtools.lh-root img {\n /* Override devtools default 'min-width: 0' so svg without size in a flexbox isn't collapsed. */\n min-width: auto;\n}\n.lh-devtools .lh-container {\n overflow-y: scroll;\n height: calc(100% - var(--topbar-height));\n /** The .lh-container is the scroll parent in DevTools so we exclude the topbar from the sticky header buffer. */\n --sticky-header-buffer: calc(var(--sticky-header-height));\n}\n@media print {\n .lh-devtools .lh-container {\n overflow: unset;\n }\n}\n.lh-devtools .lh-sticky-header {\n /* This is normally the height of the topbar, but we want it to stick to the top of our scroll container .lh-container` */\n top: 0;\n}\n.lh-devtools .lh-element-screenshot__overlay {\n position: absolute;\n}\n\n@keyframes fadeIn {\n 0% { opacity: 0;}\n 100% { opacity: 0.6;}\n}\n\n.lh-root *, .lh-root *::before, .lh-root *::after {\n box-sizing: border-box;\n}\n\n.lh-root {\n font-family: var(--report-font-family);\n font-size: var(--report-font-size);\n margin: 0;\n line-height: var(--report-line-height);\n background: var(--report-background-color);\n color: var(--report-text-color);\n}\n\n.lh-root :focus-visible {\n outline: -webkit-focus-ring-color auto 3px;\n}\n.lh-root summary:focus {\n outline: none;\n box-shadow: 0 0 0 1px hsl(217, 89%, 61%);\n}\n\n.lh-root [hidden] {\n display: none !important;\n}\n\n.lh-root pre {\n margin: 0;\n}\n\n.lh-root details > summary {\n cursor: pointer;\n}\n\n.lh-hidden {\n display: none !important;\n}\n\n.lh-container {\n /*\n Text wrapping in the report is so much FUN!\n We have a `word-break: break-word;` globally here to prevent a few common scenarios, namely\n long non-breakable text (usually URLs) found in:\n 1. The footer\n 2. .lh-node (outerHTML)\n 3. .lh-code\n\n With that sorted, the next challenge is appropriate column sizing and text wrapping inside our\n .lh-details tables. Even more fun.\n * We don't want table headers (\"Potential Savings (ms)\") to wrap or their column values, but\n we'd be happy for the URL column to wrap if the URLs are particularly long.\n * We want the narrow columns to remain narrow, providing the most column width for URL\n * We don't want the table to extend past 100% width.\n * Long URLs in the URL column can wrap. Util.getURLDisplayName maxes them out at 64 characters,\n but they do not get any overflow:ellipsis treatment.\n */\n word-break: break-word;\n}\n\n.lh-audit-group a,\n.lh-category-header__description a,\n.lh-audit__description a,\n.lh-warnings a,\n.lh-footer a,\n.lh-table-column--link a {\n color: var(--link-color);\n}\n\n.lh-audit__description, .lh-audit__stackpack {\n --inner-audit-padding-right: var(--stackpack-padding-horizontal);\n padding-left: var(--audit-description-padding-left);\n padding-right: var(--inner-audit-padding-right);\n padding-top: 8px;\n padding-bottom: 8px;\n}\n\n.lh-details {\n margin-top: var(--default-padding);\n margin-bottom: var(--default-padding);\n margin-left: var(--audit-description-padding-left);\n /* whatever the .lh-details side margins are */\n width: 100%;\n}\n\n.lh-audit__stackpack {\n display: flex;\n align-items: center;\n}\n\n.lh-audit__stackpack__img {\n max-width: 30px;\n margin-right: var(--default-padding)\n}\n\n/* Report header */\n\n.lh-report-icon {\n display: flex;\n align-items: center;\n padding: 10px 12px;\n cursor: pointer;\n}\n.lh-report-icon[disabled] {\n opacity: 0.3;\n pointer-events: none;\n}\n\n.lh-report-icon::before {\n content: \"\";\n margin: 4px;\n background-repeat: no-repeat;\n width: var(--report-icon-size);\n height: var(--report-icon-size);\n opacity: 0.7;\n display: inline-block;\n vertical-align: middle;\n}\n.lh-report-icon:hover::before {\n opacity: 1;\n}\n.lh-dark .lh-report-icon::before {\n filter: invert(1);\n}\n.lh-report-icon--print::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path d=\"M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z\"/><path fill=\"none\" d=\"M0 0h24v24H0z\"/></svg>');\n}\n.lh-report-icon--copy::before {\n background-image: url('data:image/svg+xml;utf8,<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"/></svg>');\n}\n.lh-report-icon--open::before {\n background-image: url('data:image/svg+xml;utf8,<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6l-4 4h3v6h2v-6h3l-4-4z\"/></svg>');\n}\n.lh-report-icon--download::before {\n background-image: url('data:image/svg+xml;utf8,<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\"/><path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>');\n}\n.lh-report-icon--dark::before {\n background-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24\" viewBox=\"0 0 100 125\"><path d=\"M50 23.587c-16.27 0-22.799 12.574-22.799 21.417 0 12.917 10.117 22.451 12.436 32.471h20.726c2.32-10.02 12.436-19.554 12.436-32.471 0-8.843-6.528-21.417-22.799-21.417zM39.637 87.161c0 3.001 1.18 4.181 4.181 4.181h.426l.41 1.231C45.278 94.449 46.042 95 48.019 95h3.963c1.978 0 2.74-.551 3.365-2.427l.409-1.231h.427c3.002 0 4.18-1.18 4.18-4.181V80.91H39.637v6.251zM50 18.265c1.26 0 2.072-.814 2.072-2.073v-9.12C52.072 5.813 51.26 5 50 5c-1.259 0-2.072.813-2.072 2.073v9.12c0 1.259.813 2.072 2.072 2.072zM68.313 23.727c.994.774 2.135.634 2.91-.357l5.614-7.187c.776-.992.636-2.135-.356-2.909-.992-.776-2.135-.636-2.91.357l-5.613 7.186c-.778.993-.636 2.135.355 2.91zM91.157 36.373c-.306-1.222-1.291-1.815-2.513-1.51l-8.85 2.207c-1.222.305-1.814 1.29-1.51 2.512.305 1.223 1.291 1.814 2.513 1.51l8.849-2.206c1.223-.305 1.816-1.291 1.511-2.513zM86.757 60.48l-8.331-3.709c-1.15-.512-2.225-.099-2.736 1.052-.512 1.151-.1 2.224 1.051 2.737l8.33 3.707c1.15.514 2.225.101 2.736-1.05.513-1.149.1-2.223-1.05-2.737zM28.779 23.37c.775.992 1.917 1.131 2.909.357.992-.776 1.132-1.917.357-2.91l-5.615-7.186c-.775-.992-1.917-1.132-2.909-.357s-1.131 1.917-.356 2.909l5.614 7.187zM21.715 39.583c.305-1.223-.288-2.208-1.51-2.513l-8.849-2.207c-1.222-.303-2.208.289-2.513 1.511-.303 1.222.288 2.207 1.511 2.512l8.848 2.206c1.222.304 2.208-.287 2.513-1.509zM21.575 56.771l-8.331 3.711c-1.151.511-1.563 1.586-1.05 2.735.511 1.151 1.586 1.563 2.736 1.052l8.331-3.711c1.151-.511 1.563-1.586 1.05-2.735-.512-1.15-1.585-1.562-2.736-1.052z\"/></svg>');\n}\n.lh-report-icon--treemap::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\" fill=\"black\"><path d=\"M3 5v14h19V5H3zm2 2h15v4H5V7zm0 10v-4h4v4H5zm6 0v-4h9v4h-9z\"/></svg>');\n}\n.lh-report-icon--date::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M7 11h2v2H7v-2zm14-5v14a2 2 0 01-2 2H5a2 2 0 01-2-2V6c0-1.1.9-2 2-2h1V2h2v2h8V2h2v2h1a2 2 0 012 2zM5 8h14V6H5v2zm14 12V10H5v10h14zm-4-7h2v-2h-2v2zm-4 0h2v-2h-2v2z\"/></svg>');\n}\n.lh-report-icon--devices::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M4 6h18V4H4a2 2 0 00-2 2v11H0v3h14v-3H4V6zm19 2h-6a1 1 0 00-1 1v10c0 .6.5 1 1 1h6c.6 0 1-.5 1-1V9c0-.6-.5-1-1-1zm-1 9h-4v-7h4v7z\"/></svg>');\n}\n.lh-report-icon--world::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm7 6h-3c-.3-1.3-.8-2.5-1.4-3.6A8 8 0 0 1 18.9 8zm-7-4a14 14 0 0 1 2 4h-4a14 14 0 0 1 2-4zM4.3 14a8.2 8.2 0 0 1 0-4h3.3a16.5 16.5 0 0 0 0 4H4.3zm.8 2h3a14 14 0 0 0 1.3 3.6A8 8 0 0 1 5.1 16zm3-8H5a8 8 0 0 1 4.3-3.6L8 8zM12 20a14 14 0 0 1-2-4h4a14 14 0 0 1-2 4zm2.3-6H9.7a14.7 14.7 0 0 1 0-4h4.6a14.6 14.6 0 0 1 0 4zm.3 5.6c.6-1.2 1-2.4 1.4-3.6h3a8 8 0 0 1-4.4 3.6zm1.8-5.6a16.5 16.5 0 0 0 0-4h3.3a8.2 8.2 0 0 1 0 4h-3.3z\"/></svg>');\n}\n.lh-report-icon--stopwatch::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M15 1H9v2h6V1zm-4 13h2V8h-2v6zm8.1-6.6L20.5 6l-1.4-1.4L17.7 6A9 9 0 0 0 3 13a9 9 0 1 0 16-5.6zm-7 12.6a7 7 0 1 1 0-14 7 7 0 0 1 0 14z\"/></svg>');\n}\n.lh-report-icon--networkspeed::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M15.9 5c-.2 0-.3 0-.4.2v.2L10.1 17a2 2 0 0 0-.2 1 2 2 0 0 0 4 .4l2.4-12.9c0-.3-.2-.5-.5-.5zM1 9l2 2c2.9-2.9 6.8-4 10.5-3.6l1.2-2.7C10 3.8 4.7 5.3 1 9zm20 2 2-2a15.4 15.4 0 0 0-5.6-3.6L17 8.2c1.5.7 2.9 1.6 4.1 2.8zm-4 4 2-2a9.9 9.9 0 0 0-2.7-1.9l-.5 3 1.2.9zM5 13l2 2a7.1 7.1 0 0 1 4-2l1.3-2.9C9.7 10.1 7 11 5 13z\"/></svg>');\n}\n.lh-report-icon--samples-one::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><circle cx=\"7\" cy=\"14\" r=\"3\"/><path d=\"M7 18a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5.6 17.6a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"/></svg>');\n}\n.lh-report-icon--samples-many::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M7 18a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm4-2a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm5.6 17.6a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z\"/><circle cx=\"7\" cy=\"14\" r=\"3\"/><circle cx=\"11\" cy=\"6\" r=\"3\"/></svg>');\n}\n.lh-report-icon--chrome::before {\n background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-50 -50 562 562\"><path d=\"M256 25.6v25.6a204 204 0 0 1 144.8 60 204 204 0 0 1 60 144.8 204 204 0 0 1-60 144.8 204 204 0 0 1-144.8 60 204 204 0 0 1-144.8-60 204 204 0 0 1-60-144.8 204 204 0 0 1 60-144.8 204 204 0 0 1 144.8-60V0a256 256 0 1 0 0 512 256 256 0 0 0 0-512v25.6z\"/><path d=\"M256 179.2v25.6a51.3 51.3 0 0 1 0 102.4 51.3 51.3 0 0 1 0-102.4v-51.2a102.3 102.3 0 1 0-.1 204.7 102.3 102.3 0 0 0 .1-204.7v25.6z\"/><path d=\"M256 204.8h217.6a25.6 25.6 0 0 0 0-51.2H256a25.6 25.6 0 0 0 0 51.2m44.3 76.8L191.5 470.1a25.6 25.6 0 1 0 44.4 25.6l108.8-188.5a25.6 25.6 0 1 0-44.4-25.6m-88.6 0L102.9 93.2a25.7 25.7 0 0 0-35-9.4 25.7 25.7 0 0 0-9.4 35l108.8 188.5a25.7 25.7 0 0 0 35 9.4 25.9 25.9 0 0 0 9.4-35.1\"/></svg>');\n}\n\n\n\n.lh-buttons {\n display: flex;\n flex-wrap: wrap;\n margin: var(--default-padding) 0;\n}\n.lh-button {\n height: 32px;\n border: 1px solid var(--report-border-color-secondary);\n border-radius: 3px;\n color: var(--link-color);\n background-color: var(--report-background-color);\n margin: 5px;\n}\n\n.lh-button:first-of-type {\n margin-left: 0;\n}\n\n/* Node */\n.lh-node__snippet {\n font-family: var(--report-font-family-monospace);\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n line-height: 20px;\n}\n\n/* Score */\n\n.lh-audit__score-icon {\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n margin: var(--score-icon-margin);\n}\n\n.lh-audit--pass .lh-audit__display-text {\n color: var(--color-pass-secondary);\n}\n.lh-audit--pass .lh-audit__score-icon,\n.lh-scorescale-range--pass::before {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-audit__display-text {\n color: var(--color-average-secondary);\n}\n.lh-audit--average .lh-audit__score-icon,\n.lh-scorescale-range--average::before {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-audit--fail .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n.lh-audit--fail .lh-audit__score-icon,\n.lh-audit--error .lh-audit__score-icon,\n.lh-scorescale-range--fail::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-audit--manual .lh-audit__display-text,\n.lh-audit--notapplicable .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n.lh-audit--manual .lh-audit__score-icon,\n.lh-audit--notapplicable .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n background: none;\n}\n\n.lh-audit--informative .lh-audit__display-text {\n color: var(--color-gray-600);\n}\n\n.lh-audit--informative .lh-audit__score-icon {\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-400);\n border-radius: 100%;\n}\n\n.lh-audit__description,\n.lh-audit__stackpack {\n color: var(--report-text-color-secondary);\n}\n.lh-audit__adorn {\n border: 1px solid slategray;\n border-radius: 3px;\n margin: 0 3px;\n padding: 0 2px;\n line-height: 1.1;\n display: inline-block;\n font-size: 90%;\n}\n\n.lh-category-header__description {\n text-align: center;\n color: var(--color-gray-700);\n margin: 0px auto;\n max-width: 400px;\n}\n\n\n.lh-audit__display-text,\n.lh-load-opportunity__sparkline,\n.lh-chevron-container {\n margin: 0 var(--audit-margin-horizontal);\n}\n.lh-chevron-container {\n margin-right: 0;\n}\n\n.lh-audit__title-and-text {\n flex: 1;\n}\n\n.lh-audit__title-and-text code {\n color: var(--snippet-color);\n font-size: var(--report-monospace-font-size);\n}\n\n/* Prepend display text with em dash separator. But not in Opportunities. */\n.lh-audit__display-text:not(:empty):before {\n content: '—';\n margin-right: var(--audit-margin-horizontal);\n}\n.lh-audit-group.lh-audit-group--load-opportunities .lh-audit__display-text:not(:empty):before {\n display: none;\n}\n\n/* Expandable Details (Audit Groups, Audits) */\n.lh-audit__header {\n display: flex;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-audit--load-opportunity .lh-audit__header {\n display: block;\n}\n\n\n.lh-metricfilter {\n display: grid;\n justify-content: end;\n align-items: center;\n grid-auto-flow: column;\n gap: 4px;\n color: var(--color-gray-700);\n}\n\n.lh-metricfilter__radio {\n position: absolute;\n left: -9999px;\n}\n.lh-metricfilter input[type='radio']:focus-visible + label {\n outline: -webkit-focus-ring-color auto 1px;\n}\n\n.lh-metricfilter__label {\n display: inline-flex;\n padding: 0 4px;\n height: 16px;\n text-decoration: underline;\n align-items: center;\n cursor: pointer;\n font-size: 90%;\n}\n\n.lh-metricfilter__label--active {\n background: var(--color-blue-primary);\n color: var(--color-white);\n border-radius: 3px;\n text-decoration: none;\n}\n/* Give the 'All' choice a more muted display */\n.lh-metricfilter__label--active[for=\"metric-All\"] {\n background-color: var(--color-blue-200) !important;\n color: black !important;\n}\n\n.lh-metricfilter__text {\n margin-right: 8px;\n}\n\n/* If audits are filtered, hide the itemcount for Passed Audits… */\n.lh-category--filtered .lh-audit-group .lh-audit-group__itemcount {\n display: none;\n}\n\n\n.lh-audit__header:hover {\n background-color: var(--color-hover);\n}\n\n/* We want to hide the browser's default arrow marker on summary elements. Admittedly, it's complicated. */\n.lh-root details > summary {\n /* Blink 89+ and Firefox will hide the arrow when display is changed from (new) default of `list-item` to block. https://chromestatus.com/feature/6730096436051968*/\n display: block;\n}\n/* Safari and Blink <=88 require using the -webkit-details-marker selector */\n.lh-root details > summary::-webkit-details-marker {\n display: none;\n}\n\n/* Perf Metric */\n\n.lh-metrics-container {\n display: grid;\n grid-auto-rows: 1fr;\n grid-template-columns: 1fr 1fr;\n grid-column-gap: var(--report-line-height);\n margin-bottom: var(--default-padding);\n}\n\n.lh-metric {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(-n+2) {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-metric__innerwrap {\n display: grid;\n /**\n * Icon -- Metric Name\n * -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 1fr;\n align-items: center;\n padding: var(--default-padding);\n}\n\n.lh-metric__details {\n order: -1;\n}\n\n.lh-metric__title {\n flex: 1;\n}\n\n.lh-calclink {\n padding-left: calc(1ex / 3);\n}\n\n.lh-metric__description {\n display: none;\n grid-column-start: 2;\n grid-column-end: 4;\n color: var(--report-text-color-secondary);\n}\n\n.lh-metric__value {\n font-size: var(--metric-value-font-size);\n margin: calc(var(--default-padding) / 2) 0;\n white-space: nowrap; /* No wrapping between metric value and the icon */\n grid-column-start: 2;\n}\n\n\n@media screen and (max-width: 535px) {\n .lh-metrics-container {\n display: block;\n }\n\n .lh-metric {\n border-bottom: none !important;\n }\n .lh-category:not(.lh--hoisted-meta) .lh-metric:nth-last-child(1) {\n border-bottom: 1px solid var(--report-border-color-secondary) !important;\n }\n\n /* Change the grid to 3 columns for narrow viewport. */\n .lh-metric__innerwrap {\n /**\n * Icon -- Metric Name -- Metric Value\n */\n grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 2fr 1fr;\n }\n .lh-metric__value {\n justify-self: end;\n grid-column-start: unset;\n }\n}\n\n/* No-JS toggle switch */\n/* Keep this selector sync'd w/ `magicSelector` in report-ui-features-test.js */\n .lh-metrics-toggle__input:checked ~ .lh-metrics-container .lh-metric__description {\n display: block;\n}\n\n/* TODO get rid of the SVGS and clean up these some more */\n.lh-metrics-toggle__input {\n opacity: 0;\n position: absolute;\n right: 0;\n top: 0px;\n}\n\n.lh-metrics-toggle__input + div > label > .lh-metrics-toggle__labeltext--hide,\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--show {\n display: none;\n}\n.lh-metrics-toggle__input:checked + div > label > .lh-metrics-toggle__labeltext--hide {\n display: inline;\n}\n.lh-metrics-toggle__input:focus + div > label {\n outline: -webkit-focus-ring-color auto 3px;\n}\n\n.lh-metrics-toggle__label {\n cursor: pointer;\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n/* Pushes the metric description toggle button to the right. */\n.lh-audit-group--metrics .lh-audit-group__header {\n display: flex;\n justify-content: space-between;\n}\n\n.lh-metric__icon,\n.lh-scorescale-range::before {\n content: '';\n width: var(--score-icon-size);\n height: var(--score-icon-size);\n display: inline-block;\n margin: var(--score-icon-margin);\n}\n\n.lh-metric--pass .lh-metric__value {\n color: var(--color-pass-secondary);\n}\n.lh-metric--pass .lh-metric__icon {\n border-radius: 100%;\n background: var(--color-pass);\n}\n\n.lh-metric--average .lh-metric__value {\n color: var(--color-average-secondary);\n}\n.lh-metric--average .lh-metric__icon {\n background: var(--color-average);\n width: var(--icon-square-size);\n height: var(--icon-square-size);\n}\n\n.lh-metric--fail .lh-metric__value {\n color: var(--color-fail-secondary);\n}\n.lh-metric--fail .lh-metric__icon,\n.lh-metric--error .lh-metric__icon {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n\n.lh-metric--error .lh-metric__value,\n.lh-metric--error .lh-metric__description {\n color: var(--color-fail-secondary);\n}\n\n/* Perf load opportunity */\n\n.lh-load-opportunity__cols {\n display: flex;\n align-items: flex-start;\n}\n\n.lh-load-opportunity__header .lh-load-opportunity__col {\n color: var(--color-gray-600);\n display: unset;\n line-height: calc(2.3 * var(--report-font-size));\n}\n\n.lh-load-opportunity__col {\n display: flex;\n}\n\n.lh-load-opportunity__col--one {\n flex: 5;\n align-items: center;\n margin-right: 2px;\n}\n.lh-load-opportunity__col--two {\n flex: 4;\n text-align: right;\n}\n\n.lh-audit--load-opportunity .lh-audit__display-text {\n text-align: right;\n flex: 0 0 7.5ch;\n}\n\n\n/* Sparkline */\n\n.lh-load-opportunity__sparkline {\n flex: 1;\n margin-top: calc((var(--report-line-height) - var(--sparkline-height)) / 2);\n}\n\n.lh-sparkline {\n height: var(--sparkline-height);\n width: 100%;\n}\n\n.lh-sparkline__bar {\n height: 100%;\n float: right;\n}\n\n.lh-audit--pass .lh-sparkline__bar {\n background: var(--color-pass);\n}\n\n.lh-audit--average .lh-sparkline__bar {\n background: var(--color-average);\n}\n\n.lh-audit--fail .lh-sparkline__bar {\n background: var(--color-fail);\n}\n\n/* Filmstrip */\n\n.lh-filmstrip-container {\n /* smaller gap between metrics and filmstrip */\n margin: -8px auto 0 auto;\n}\n\n.lh-filmstrip {\n display: grid;\n justify-content: space-between;\n padding-bottom: var(--default-padding);\n width: 100%;\n grid-template-columns: repeat(auto-fit, 90px);\n}\n\n.lh-filmstrip__frame {\n text-align: right;\n position: relative;\n}\n\n.lh-filmstrip__thumbnail {\n border: 1px solid var(--report-border-color-secondary);\n max-height: 150px;\n max-width: 120px;\n}\n\n/* Audit */\n\n.lh-audit {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\n\n/* Apply border-top to just the first audit. */\n.lh-audit {\n border-top: 1px solid var(--report-border-color-secondary);\n}\n.lh-audit ~ .lh-audit {\n border-top: none;\n}\n\n\n.lh-audit--error .lh-audit__display-text {\n color: var(--color-fail-secondary);\n}\n\n/* Audit Group */\n\n.lh-audit-group {\n margin-bottom: var(--audit-group-margin-bottom);\n position: relative;\n}\n.lh-audit-group--metrics {\n margin-bottom: calc(var(--audit-group-margin-bottom) / 2);\n}\n\n.lh-audit-group__header::before {\n /* By default, groups don't get an icon */\n content: none;\n width: var(--pwa-icon-size);\n height: var(--pwa-icon-size);\n margin: var(--pwa-icon-margin);\n display: inline-block;\n vertical-align: middle;\n}\n\n/* Style the \"over budget\" columns red. */\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(4),\n.lh-audit-group--budgets #performance-budget tbody tr td:nth-child(5),\n.lh-audit-group--budgets #timing-budget tbody tr td:nth-child(3) {\n color: var(--color-red-700);\n}\n\n/* Align the \"over budget request count\" text to be close to the \"over budget bytes\" column. */\n.lh-audit-group--budgets .lh-table tbody tr td:nth-child(4){\n text-align: right;\n}\n\n.lh-audit-group--budgets .lh-details--budget {\n width: 100%;\n margin: 0 0 var(--default-padding);\n}\n\n.lh-audit-group--pwa-installable .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-installable-gray-url);\n}\n.lh-audit-group--pwa-optimized .lh-audit-group__header::before {\n content: '';\n background-image: var(--pwa-optimized-gray-url);\n}\n.lh-audit-group--pwa-installable.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-installable-color-url);\n}\n.lh-audit-group--pwa-optimized.lh-badged .lh-audit-group__header::before {\n background-image: var(--pwa-optimized-color-url);\n}\n\n.lh-audit-group--metrics .lh-audit-group__summary {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.lh-audit-group__summary {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.lh-audit-group__header .lh-chevron {\n margin-top: calc((var(--report-line-height) - 5px) / 2);\n}\n\n.lh-audit-group__header {\n letter-spacing: 0.8px;\n padding: var(--default-padding);\n padding-left: 0;\n}\n\n.lh-audit-group__header, .lh-audit-group__summary {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n color: var(--color-gray-700);\n}\n\n.lh-audit-group__title {\n text-transform: uppercase;\n font-weight: 500;\n}\n\n.lh-audit-group__itemcount {\n color: var(--color-gray-600);\n}\n\n.lh-audit-group__footer {\n color: var(--color-gray-600);\n display: block;\n margin-top: var(--default-padding);\n}\n\n.lh-details,\n.lh-category-header__description,\n.lh-load-opportunity__header,\n.lh-audit-group__footer {\n font-size: var(--report-font-size-secondary);\n line-height: var(--report-line-height-secondary);\n}\n\n.lh-audit-explanation {\n margin: var(--audit-padding-vertical) 0 calc(var(--audit-padding-vertical) / 2) var(--audit-margin-horizontal);\n line-height: var(--audit-explanation-line-height);\n display: inline-block;\n}\n\n.lh-audit--fail .lh-audit-explanation {\n color: var(--color-fail-secondary);\n}\n\n/* Report */\n.lh-list > :not(:last-child) {\n margin-bottom: calc(var(--default-padding) * 2);\n}\n\n.lh-header-container {\n display: block;\n margin: 0 auto;\n position: relative;\n word-wrap: break-word;\n}\n\n.lh-header-container .lh-scores-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n\n\n.lh-report {\n min-width: var(--report-content-min-width);\n}\n\n.lh-exception {\n font-size: large;\n}\n\n.lh-code {\n white-space: normal;\n margin-top: 0;\n font-size: var(--report-monospace-font-size);\n}\n\n.lh-warnings {\n --item-margin: calc(var(--report-line-height) / 6);\n color: var(--color-average-secondary);\n margin: var(--audit-padding-vertical) 0;\n padding: var(--default-padding)\n var(--default-padding)\n var(--default-padding)\n calc(var(--audit-description-padding-left));\n background-color: var(--toplevel-warning-background-color);\n}\n.lh-warnings span {\n font-weight: bold;\n}\n\n.lh-warnings--toplevel {\n --item-margin: calc(var(--header-line-height) / 4);\n color: var(--toplevel-warning-text-color);\n margin-left: auto;\n margin-right: auto;\n max-width: var(--report-content-max-width-minus-edge-gap);\n padding: var(--toplevel-warning-padding);\n border-radius: 8px;\n}\n\n.lh-warnings__msg {\n color: var(--toplevel-warning-message-text-color);\n margin: 0;\n}\n\n.lh-warnings ul {\n margin: 0;\n}\n.lh-warnings li {\n margin: var(--item-margin) 0;\n}\n.lh-warnings li:last-of-type {\n margin-bottom: 0;\n}\n\n.lh-scores-header {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n}\n.lh-scores-header__solo {\n padding: 0;\n border: 0;\n}\n\n/* Gauge */\n\n.lh-gauge__wrapper--pass {\n color: var(--color-pass-secondary);\n fill: var(--color-pass);\n stroke: var(--color-pass);\n}\n\n.lh-gauge__wrapper--average {\n color: var(--color-average-secondary);\n fill: var(--color-average);\n stroke: var(--color-average);\n}\n\n.lh-gauge__wrapper--fail {\n color: var(--color-fail-secondary);\n fill: var(--color-fail);\n stroke: var(--color-fail);\n}\n\n.lh-gauge__wrapper--not-applicable {\n color: var(--color-not-applicable);\n fill: var(--color-not-applicable);\n stroke: var(--color-not-applicable);\n}\n\n.lh-fraction__wrapper .lh-fraction__content::before {\n content: '';\n height: var(--score-icon-size);\n width: var(--score-icon-size);\n margin: var(--score-icon-margin);\n display: inline-block;\n}\n.lh-fraction__wrapper--pass .lh-fraction__content {\n color: var(--color-pass-secondary);\n}\n.lh-fraction__wrapper--pass .lh-fraction__background {\n background-color: var(--color-pass);\n}\n.lh-fraction__wrapper--pass .lh-fraction__content::before {\n background-color: var(--color-pass);\n border-radius: 50%;\n}\n.lh-fraction__wrapper--average .lh-fraction__content {\n color: var(--color-average-secondary);\n}\n.lh-fraction__wrapper--average .lh-fraction__background,\n.lh-fraction__wrapper--average .lh-fraction__content::before {\n background-color: var(--color-average);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content {\n color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__background {\n background-color: var(--color-fail);\n}\n.lh-fraction__wrapper--fail .lh-fraction__content::before {\n border-left: calc(var(--score-icon-size) / 2) solid transparent;\n border-right: calc(var(--score-icon-size) / 2) solid transparent;\n border-bottom: var(--score-icon-size) solid var(--color-fail);\n}\n.lh-fraction__wrapper--null .lh-fraction__content {\n color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__background {\n background-color: var(--color-gray-700);\n}\n.lh-fraction__wrapper--null .lh-fraction__content::before {\n border-radius: 50%;\n border: calc(0.2 * var(--score-icon-size)) solid var(--color-gray-700);\n}\n\n.lh-fraction__background {\n position: absolute;\n height: 100%;\n width: 100%;\n border-radius: calc(var(--gauge-circle-size) / 2);\n opacity: 0.1;\n z-index: -1;\n}\n\n.lh-fraction__content-wrapper {\n height: var(--gauge-circle-size);\n display: flex;\n align-items: center;\n}\n\n.lh-fraction__content {\n display: flex;\n position: relative;\n align-items: center;\n justify-content: center;\n font-size: calc(0.3 * var(--gauge-circle-size));\n line-height: calc(0.4 * var(--gauge-circle-size));\n width: max-content;\n min-width: calc(1.5 * var(--gauge-circle-size));\n padding: calc(0.1 * var(--gauge-circle-size)) calc(0.2 * var(--gauge-circle-size));\n --score-icon-size: calc(0.21 * var(--gauge-circle-size));\n --score-icon-margin: 0 calc(0.15 * var(--gauge-circle-size)) 0 0;\n}\n\n.lh-gauge {\n stroke-linecap: round;\n width: var(--gauge-circle-size);\n height: var(--gauge-circle-size);\n}\n\n.lh-category .lh-gauge {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n.lh-gauge-base {\n opacity: 0.1;\n}\n\n.lh-gauge-arc {\n fill: none;\n transform-origin: 50% 50%;\n animation: load-gauge var(--transition-length) ease both;\n animation-delay: 250ms;\n}\n\n.lh-gauge__svg-wrapper {\n position: relative;\n height: var(--gauge-circle-size);\n}\n.lh-category .lh-gauge__svg-wrapper,\n.lh-category .lh-fraction__wrapper {\n --gauge-circle-size: var(--gauge-circle-size-big);\n}\n\n/* The plugin badge overlay */\n.lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size);\n height: var(--plugin-badge-size);\n background-color: var(--plugin-badge-background-color);\n background-image: var(--plugin-icon-url);\n background-repeat: no-repeat;\n background-size: var(--plugin-icon-size);\n background-position: 58% 50%;\n content: \"\";\n position: absolute;\n right: -6px;\n bottom: 0px;\n display: block;\n z-index: 100;\n box-shadow: 0 0 4px rgba(0,0,0,.2);\n border-radius: 25%;\n}\n.lh-category .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {\n width: var(--plugin-badge-size-big);\n height: var(--plugin-badge-size-big);\n}\n\n@keyframes load-gauge {\n from { stroke-dasharray: 0 352; }\n}\n\n.lh-gauge__percentage {\n width: 100%;\n height: var(--gauge-circle-size);\n position: absolute;\n font-family: var(--report-font-family-monospace);\n font-size: calc(var(--gauge-circle-size) * 0.34 + 1.3px);\n line-height: 0;\n text-align: center;\n top: calc(var(--score-container-padding) + var(--gauge-circle-size) / 2);\n}\n\n.lh-category .lh-gauge__percentage {\n --gauge-circle-size: var(--gauge-circle-size-big);\n --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);\n}\n\n.lh-gauge__wrapper,\n.lh-fraction__wrapper {\n position: relative;\n display: flex;\n align-items: center;\n flex-direction: column;\n text-decoration: none;\n padding: var(--score-container-padding);\n\n --transition-length: 1s;\n\n /* Contain the layout style paint & layers during animation*/\n contain: content;\n will-change: opacity; /* Only using for layer promotion */\n}\n\n.lh-gauge__label,\n.lh-fraction__label {\n font-size: var(--gauge-label-font-size);\n font-weight: 500;\n line-height: var(--gauge-label-line-height);\n margin-top: 10px;\n text-align: center;\n color: var(--report-text-color);\n word-break: keep-all;\n}\n\n/* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */\n.lh-category .lh-gauge__label,\n.lh-category .lh-fraction__label {\n --gauge-label-font-size: var(--gauge-label-font-size-big);\n --gauge-label-line-height: var(--gauge-label-line-height-big);\n margin-top: 14px;\n}\n\n.lh-scores-header .lh-gauge__wrapper,\n.lh-scores-header .lh-fraction__wrapper,\n.lh-scores-header .lh-gauge--pwa__wrapper,\n.lh-sticky-header .lh-gauge__wrapper,\n.lh-sticky-header .lh-fraction__wrapper,\n.lh-sticky-header .lh-gauge--pwa__wrapper {\n width: var(--gauge-wrapper-width);\n}\n\n.lh-scorescale {\n display: inline-flex;\n\n gap: calc(var(--default-padding) * 4);\n margin: 16px auto 0 auto;\n font-size: var(--report-font-size-secondary);\n color: var(--color-gray-700);\n\n}\n\n.lh-scorescale-range {\n display: flex;\n align-items: center;\n font-family: var(--report-font-family-monospace);\n white-space: nowrap;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale {\n border: 0;\n display: flex;\n justify-content: center;\n}\n\n.lh-category-header__finalscreenshot .lh-scorescale-range {\n font-family: unset;\n font-size: 12px;\n}\n\n.lh-scorescale-wrap {\n display: contents;\n}\n\n/* Hide category score gauages if it's a single category report */\n.lh-header--solo-category .lh-scores-wrapper {\n display: none;\n}\n\n\n.lh-categories {\n width: 100%;\n}\n\n.lh-category {\n padding: var(--category-padding);\n max-width: var(--report-content-max-width);\n margin: 0 auto;\n\n scroll-margin-top: var(--sticky-header-buffer);\n\n /* Faster recalc style & layout of the report. https://web.dev/content-visibility/ */\n content-visibility: auto;\n contain-intrinsic-size: 1000px;\n}\n\n.lh-category-wrapper {\n border-bottom: 1px solid var(--color-gray-200);\n}\n.lh-category-wrapper:last-of-type {\n border-bottom: 0;\n}\n\n.lh-category-header {\n margin-bottom: var(--section-padding-vertical);\n}\n\n.lh-category-header .lh-score__gauge {\n max-width: 400px;\n width: auto;\n margin: 0px auto;\n}\n\n.lh-category-header__finalscreenshot {\n display: grid;\n grid-template: none / 1fr 1px 1fr;\n justify-items: center;\n align-items: center;\n gap: var(--report-line-height);\n min-height: 288px;\n margin-bottom: var(--default-padding);\n}\n\n.lh-final-ss-image {\n /* constrain the size of the image to not be too large */\n max-height: calc(var(--gauge-circle-size-big) * 2.8);\n max-width: calc(var(--gauge-circle-size-big) * 3.5);\n border: 1px solid var(--color-gray-200);\n padding: 4px;\n border-radius: 3px;\n display: block;\n}\n\n.lh-category-headercol--separator {\n background: var(--color-gray-200);\n width: 1px;\n height: var(--gauge-circle-size-big);\n}\n\n@media screen and (max-width: 780px) {\n .lh-category-header__finalscreenshot {\n grid-template: 1fr 1fr / none\n }\n .lh-category-headercol--separator {\n display: none;\n }\n}\n\n\n/* 964 fits the min-width of the filmstrip */\n@media screen and (max-width: 964px) {\n .lh-report {\n margin-left: 0;\n width: 100%;\n }\n}\n\n@media print {\n body {\n -webkit-print-color-adjust: exact; /* print background colors */\n }\n .lh-container {\n display: block;\n }\n .lh-report {\n margin-left: 0;\n padding-top: 0;\n }\n .lh-categories {\n margin-top: 0;\n }\n}\n\n.lh-table {\n position: relative;\n border-collapse: separate;\n border-spacing: 0;\n /* Can't assign padding to table, so shorten the width instead. */\n width: calc(100% - var(--audit-description-padding-left) - var(--stackpack-padding-horizontal));\n border: 1px solid var(--report-border-color-secondary);\n}\n\n.lh-table thead th {\n position: sticky;\n top: calc(var(--sticky-header-buffer) + 1em);\n z-index: 1;\n background-color: var(--report-background-color);\n border-bottom: 1px solid var(--report-border-color-secondary);\n font-weight: normal;\n color: var(--color-gray-600);\n /* See text-wrapping comment on .lh-container. */\n word-break: normal;\n}\n\n.lh-row--even {\n background-color: var(--table-higlight-background-color);\n}\n.lh-row--hidden {\n display: none;\n}\n\n.lh-table th,\n.lh-table td {\n padding: var(--default-padding);\n}\n\n.lh-table tr {\n vertical-align: middle;\n}\n\n/* Looks unnecessary, but mostly for keeping the <th>s left-aligned */\n.lh-table-column--text,\n.lh-table-column--source-location,\n.lh-table-column--url,\n/* .lh-table-column--thumbnail, */\n/* .lh-table-column--empty,*/\n.lh-table-column--code,\n.lh-table-column--node {\n text-align: left;\n}\n\n.lh-table-column--code {\n min-width: 100px;\n}\n\n.lh-table-column--bytes,\n.lh-table-column--timespanMs,\n.lh-table-column--ms,\n.lh-table-column--numeric {\n text-align: right;\n word-break: normal;\n}\n\n\n\n.lh-table .lh-table-column--thumbnail {\n width: var(--image-preview-size);\n}\n\n.lh-table-column--url {\n min-width: 250px;\n}\n\n.lh-table-column--text {\n min-width: 80px;\n}\n\n/* Keep columns narrow if they follow the URL column */\n/* 12% was determined to be a decent narrow width, but wide enough for column headings */\n.lh-table-column--url + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--ms,\n.lh-table-column--url + .lh-table-column--ms + th.lh-table-column--bytes,\n.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--timespanMs {\n width: 12%;\n}\n\n.lh-text__url-host {\n display: inline;\n}\n\n.lh-text__url-host {\n margin-left: calc(var(--report-font-size) / 2);\n opacity: 0.6;\n font-size: 90%\n}\n\n.lh-thumbnail {\n object-fit: cover;\n width: var(--image-preview-size);\n height: var(--image-preview-size);\n display: block;\n}\n\n.lh-unknown pre {\n overflow: scroll;\n border: solid 1px var(--color-gray-200);\n}\n\n.lh-text__url > a {\n color: inherit;\n text-decoration: none;\n}\n\n.lh-text__url > a:hover {\n text-decoration: underline dotted #999;\n}\n\n.lh-sub-item-row {\n margin-left: 20px;\n margin-bottom: 0;\n color: var(--color-gray-700);\n}\n.lh-sub-item-row td {\n padding-top: 4px;\n padding-bottom: 4px;\n padding-left: 20px;\n}\n\n/* Chevron\n https://codepen.io/paulirish/pen/LmzEmK\n */\n.lh-chevron {\n --chevron-angle: 42deg;\n /* Edge doesn't support transform: rotate(calc(...)), so we define it here */\n --chevron-angle-right: -42deg;\n width: var(--chevron-size);\n height: var(--chevron-size);\n margin-top: calc((var(--report-line-height) - 12px) / 2);\n}\n\n.lh-chevron__lines {\n transition: transform 0.4s;\n transform: translateY(var(--report-line-height));\n}\n.lh-chevron__line {\n stroke: var(--chevron-line-stroke);\n stroke-width: var(--chevron-size);\n stroke-linecap: square;\n transform-origin: 50%;\n transform: rotate(var(--chevron-angle));\n transition: transform 300ms, stroke 300ms;\n}\n\n.lh-expandable-details .lh-chevron__line-right,\n.lh-expandable-details[open] .lh-chevron__line-left {\n transform: rotate(var(--chevron-angle-right));\n}\n\n.lh-expandable-details[open] .lh-chevron__line-right {\n transform: rotate(var(--chevron-angle));\n}\n\n\n.lh-expandable-details[open] .lh-chevron__lines {\n transform: translateY(calc(var(--chevron-size) * -1));\n}\n\n.lh-expandable-details[open] {\n animation: 300ms openDetails forwards;\n padding-bottom: var(--default-padding);\n}\n\n@keyframes openDetails {\n from {\n outline: 1px solid var(--report-background-color);\n }\n to {\n outline: 1px solid;\n box-shadow: 0 2px 4px rgba(0, 0, 0, .24);\n }\n}\n\n@media screen and (max-width: 780px) {\n /* no black outline if we're not confident the entire table can be displayed within bounds */\n .lh-expandable-details[open] {\n animation: none;\n }\n}\n\n.lh-expandable-details[open] summary, details.lh-clump > summary {\n border-bottom: 1px solid var(--report-border-color-secondary);\n}\ndetails.lh-clump[open] > summary {\n border-bottom-width: 0;\n}\n\n\n\ndetails .lh-clump-toggletext--hide,\ndetails[open] .lh-clump-toggletext--show { display: none; }\ndetails[open] .lh-clump-toggletext--hide { display: block;}\n\n\n/* Tooltip */\n.lh-tooltip-boundary {\n position: relative;\n}\n\n.lh-tooltip {\n position: absolute;\n display: none; /* Don't retain these layers when not needed */\n opacity: 0;\n background: #ffffff;\n white-space: pre-line; /* Render newlines in the text */\n min-width: 246px;\n max-width: 275px;\n padding: 15px;\n border-radius: 5px;\n text-align: initial;\n line-height: 1.4;\n}\n/* shrink tooltips to not be cutoff on left edge of narrow viewports\n 45vw is chosen to be ~= width of the left column of metrics\n*/\n@media screen and (max-width: 535px) {\n .lh-tooltip {\n min-width: 45vw;\n padding: 3vw;\n }\n}\n\n.lh-tooltip-boundary:hover .lh-tooltip {\n display: block;\n animation: fadeInTooltip 250ms;\n animation-fill-mode: forwards;\n animation-delay: 850ms;\n bottom: 100%;\n z-index: 1;\n will-change: opacity;\n right: 0;\n pointer-events: none;\n}\n\n.lh-tooltip::before {\n content: \"\";\n border: solid transparent;\n border-bottom-color: #fff;\n border-width: 10px;\n position: absolute;\n bottom: -20px;\n right: 6px;\n transform: rotate(180deg);\n pointer-events: none;\n}\n\n@keyframes fadeInTooltip {\n 0% { opacity: 0; }\n 75% { opacity: 1; }\n 100% { opacity: 1; filter: drop-shadow(1px 0px 1px #aaa) drop-shadow(0px 2px 4px hsla(206, 6%, 25%, 0.15)); pointer-events: auto; }\n}\n\n/* Element screenshot */\n.lh-element-screenshot {\n float: left;\n margin-right: 20px;\n}\n.lh-element-screenshot__content {\n overflow: hidden;\n min-width: 110px;\n display: flex;\n justify-content: center;\n background-color: var(--report-background-color);\n}\n.lh-element-screenshot__image {\n position: relative;\n /* Set by ElementScreenshotRenderer.installFullPageScreenshotCssVariable */\n background-image: var(--element-screenshot-url);\n outline: 2px solid #777;\n background-color: white;\n background-repeat: no-repeat;\n}\n.lh-element-screenshot__mask {\n position: absolute;\n background: #555;\n opacity: 0.8;\n}\n.lh-element-screenshot__element-marker {\n position: absolute;\n outline: 2px solid var(--color-lime-400);\n}\n.lh-element-screenshot__overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 2000; /* .lh-topbar is 1000 */\n background: var(--screenshot-overlay-background);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: zoom-out;\n}\n\n.lh-element-screenshot__overlay .lh-element-screenshot {\n margin-right: 0; /* clearing margin used in thumbnail case */\n outline: 1px solid var(--color-gray-700);\n}\n\n.lh-screenshot-overlay--enabled .lh-element-screenshot {\n cursor: zoom-out;\n}\n.lh-screenshot-overlay--enabled .lh-node .lh-element-screenshot {\n cursor: zoom-in;\n}\n\n\n.lh-meta__items {\n --meta-icon-size: calc(var(--report-icon-size) * 0.667);\n padding: var(--default-padding);\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n background-color: var(--env-item-background-color);\n border-radius: 3px;\n margin: 0 0 var(--default-padding) 0;\n font-size: 12px;\n column-gap: var(--default-padding);\n color: var(--color-gray-700);\n}\n\n.lh-meta__item {\n display: block;\n list-style-type: none;\n position: relative;\n padding: 0 0 0 calc(var(--meta-icon-size) + var(--default-padding) * 2);\n cursor: unset; /* disable pointer cursor from report-icon */\n}\n\n.lh-meta__item.lh-tooltip-boundary {\n text-decoration: dotted underline var(--color-gray-500);\n cursor: help;\n}\n\n.lh-meta__item.lh-report-icon::before {\n position: absolute;\n left: var(--default-padding);\n width: var(--meta-icon-size);\n height: var(--meta-icon-size);\n}\n\n.lh-meta__item.lh-report-icon:hover::before {\n opacity: 0.7;\n}\n\n.lh-meta__item .lh-tooltip {\n color: var(--color-gray-800);\n}\n\n.lh-meta__item .lh-tooltip::before {\n right: auto; /* Set the tooltip arrow to the leftside */\n left: 6px;\n}\n\n/* Change the grid for narrow viewport. */\n@media screen and (max-width: 640px) {\n .lh-meta__items {\n grid-template-columns: 1fr 1fr;\n }\n}\n@media screen and (max-width: 535px) {\n .lh-meta__items {\n display: block;\n }\n}\n\n\n/*# sourceURL=report-styles.css */\n");
|
|
1223
853
|
el0.append(el1);
|
|
1224
854
|
return el0;
|
|
1225
855
|
}
|
|
@@ -1229,214 +859,131 @@ function createStylesComponent(dom) {
|
|
|
1229
859
|
*/
|
|
1230
860
|
function createTopbarComponent(dom) {
|
|
1231
861
|
const el0 = dom.createFragment();
|
|
1232
|
-
const el1 = dom.createElement(
|
|
1233
|
-
el1.append(
|
|
862
|
+
const el1 = dom.createElement("style");
|
|
863
|
+
el1.append("\n .lh-topbar {\n position: sticky;\n top: 0;\n left: 0;\n right: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n height: var(--topbar-height);\n padding: var(--topbar-padding);\n font-size: var(--report-font-size-secondary);\n background-color: var(--topbar-background-color);\n border-bottom: 1px solid var(--color-gray-200);\n }\n\n .lh-topbar__logo {\n width: var(--topbar-logo-size);\n height: var(--topbar-logo-size);\n user-select: none;\n flex: none;\n }\n\n .lh-topbar__url {\n margin: var(--topbar-padding);\n text-decoration: none;\n color: var(--report-text-color);\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n }\n\n .lh-tools {\n display: flex;\n align-items: center;\n margin-left: auto;\n will-change: transform;\n min-width: var(--report-icon-size);\n }\n .lh-tools__button {\n width: var(--report-icon-size);\n min-width: 24px;\n height: var(--report-icon-size);\n cursor: pointer;\n margin-right: 5px;\n /* This is actually a button element, but we want to style it like a transparent div. */\n display: flex;\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n outline: inherit;\n }\n .lh-tools__button svg {\n fill: var(--tools-icon-color);\n }\n .lh-dark .lh-tools__button svg {\n filter: invert(1);\n }\n .lh-tools__button.lh-active + .lh-tools__dropdown {\n opacity: 1;\n clip: rect(-1px, 194px, 242px, -3px);\n visibility: visible;\n }\n .lh-tools__dropdown {\n position: absolute;\n background-color: var(--report-background-color);\n border: 1px solid var(--report-border-color);\n border-radius: 3px;\n padding: calc(var(--default-padding) / 2) 0;\n cursor: pointer;\n top: 36px;\n right: 0;\n box-shadow: 1px 1px 3px #ccc;\n min-width: 125px;\n clip: rect(0, 164px, 0, 0);\n visibility: hidden;\n opacity: 0;\n transition: all 200ms cubic-bezier(0,0,0.2,1);\n }\n .lh-tools__dropdown a {\n color: currentColor;\n text-decoration: none;\n white-space: nowrap;\n padding: 0 6px;\n line-height: 2;\n }\n .lh-tools__dropdown a:hover,\n .lh-tools__dropdown a:focus {\n background-color: var(--color-gray-200);\n outline: none;\n }\n /* save-gist option hidden in report. */\n .lh-tools__dropdown a[data-action='save-gist'] {\n display: none;\n }\n\n .lh-locale-selector {\n width: 100%;\n color: var(--report-text-color);\n background-color: var(--locale-selector-background-color);\n padding: 2px;\n }\n .lh-tools-locale {\n display: flex;\n align-items: center;\n flex-direction: row-reverse;\n }\n .lh-tools-locale__selector-wrapper {\n transition: opacity 0.15s;\n opacity: 0;\n max-width: 200px;\n }\n .lh-button.lh-tool-locale__button {\n height: var(--topbar-height);\n color: var(--tools-icon-color);\n padding: calc(var(--default-padding) / 2);\n }\n .lh-tool-locale__button.lh-active + .lh-tools-locale__selector-wrapper {\n opacity: 1;\n clip: rect(-1px, 194px, 242px, -3px);\n visibility: visible;\n margin: 0 4px;\n }\n\n @media screen and (max-width: 964px) {\n .lh-tools__dropdown {\n right: 0;\n left: initial;\n }\n }\n @media print {\n .lh-topbar {\n position: static;\n margin-left: 0;\n }\n\n .lh-tools__dropdown {\n display: none;\n }\n }\n ");
|
|
1234
864
|
el0.append(el1);
|
|
1235
|
-
const el2 = dom.createElement(
|
|
1236
|
-
const el3 = dom.createElementNS(
|
|
1237
|
-
el3.setAttribute('
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
el6.
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
el7.
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
el8.
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
el16.setAttribute('offset', '100%');
|
|
1298
|
-
el14.append(' ', el15, ' ', el16, ' ');
|
|
1299
|
-
el4.append(' ', el5, ' ', el8, ' ', el11, ' ', el14, ' ');
|
|
1300
|
-
const el17 = dom.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
865
|
+
const el2 = dom.createElement("div", "lh-topbar");
|
|
866
|
+
const el3 = dom.createElementNS("http://www.w3.org/2000/svg", "svg", "lh-topbar__logo");
|
|
867
|
+
el3.setAttribute('role', 'img');
|
|
868
|
+
el3.setAttribute('title', 'Lighthouse logo');
|
|
869
|
+
el3.setAttribute('fill', 'none');
|
|
870
|
+
el3.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
871
|
+
el3.setAttribute('viewBox', '0 0 48 48');
|
|
872
|
+
const el4 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
873
|
+
el4.setAttribute('d', 'm14 7 10-7 10 7v10h5v7h-5l5 24H9l5-24H9v-7h5V7Z');
|
|
874
|
+
el4.setAttribute('fill', '#F63');
|
|
875
|
+
const el5 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
876
|
+
el5.setAttribute('d', 'M31.561 24H14l-1.689 8.105L31.561 24ZM18.983 48H9l1.022-4.907L35.723 32.27l1.663 7.98L18.983 48Z');
|
|
877
|
+
el5.setAttribute('fill', '#FFA385');
|
|
878
|
+
const el6 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
879
|
+
el6.setAttribute('fill', '#FF3');
|
|
880
|
+
el6.setAttribute('d', 'M20.5 10h7v7h-7z');
|
|
881
|
+
el3.append(" ",el4," ",el5," ",el6," ");
|
|
882
|
+
const el7 = dom.createElement("a", "lh-topbar__url");
|
|
883
|
+
el7.setAttribute('href', '');
|
|
884
|
+
el7.setAttribute('target', '_blank');
|
|
885
|
+
el7.setAttribute('rel', 'noopener');
|
|
886
|
+
const el8 = dom.createElement("div", "lh-tools");
|
|
887
|
+
const el9 = dom.createElement("div", "lh-tools-locale lh-hidden");
|
|
888
|
+
const el10 = dom.createElement("button", "lh-button lh-tool-locale__button");
|
|
889
|
+
el10.setAttribute('id', 'lh-button__swap-locales');
|
|
890
|
+
el10.setAttribute('title', 'Show Language Picker');
|
|
891
|
+
el10.setAttribute('aria-label', 'Toggle language picker');
|
|
892
|
+
el10.setAttribute('aria-haspopup', 'menu');
|
|
893
|
+
el10.setAttribute('aria-expanded', 'false');
|
|
894
|
+
el10.setAttribute('aria-controls', 'lh-tools-locale__selector-wrapper');
|
|
895
|
+
const el11 = dom.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
896
|
+
el11.setAttribute('width', '20px');
|
|
897
|
+
el11.setAttribute('height', '20px');
|
|
898
|
+
el11.setAttribute('viewBox', '0 0 24 24');
|
|
899
|
+
el11.setAttribute('fill', 'currentColor');
|
|
900
|
+
const el12 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
901
|
+
el12.setAttribute('d', 'M0 0h24v24H0V0z');
|
|
902
|
+
el12.setAttribute('fill', 'none');
|
|
903
|
+
const el13 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
904
|
+
el13.setAttribute('d', 'M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z');
|
|
905
|
+
el11.append(el12,el13);
|
|
906
|
+
el10.append(" ",el11," ");
|
|
907
|
+
const el14 = dom.createElement("div", "lh-tools-locale__selector-wrapper");
|
|
908
|
+
el14.setAttribute('id', 'lh-tools-locale__selector-wrapper');
|
|
909
|
+
el14.setAttribute('role', 'menu');
|
|
910
|
+
el14.setAttribute('aria-labelledby', 'lh-button__swap-locales');
|
|
911
|
+
el14.setAttribute('aria-hidden', 'true');
|
|
912
|
+
el14.append(" "," ");
|
|
913
|
+
el9.append(" ",el10," ",el14," ");
|
|
914
|
+
const el15 = dom.createElement("button", "lh-tools__button");
|
|
915
|
+
el15.setAttribute('id', 'lh-tools-button');
|
|
916
|
+
el15.setAttribute('title', 'Tools menu');
|
|
917
|
+
el15.setAttribute('aria-label', 'Toggle report tools menu');
|
|
918
|
+
el15.setAttribute('aria-haspopup', 'menu');
|
|
919
|
+
el15.setAttribute('aria-expanded', 'false');
|
|
920
|
+
el15.setAttribute('aria-controls', 'lh-tools-dropdown');
|
|
921
|
+
const el16 = dom.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
922
|
+
el16.setAttribute('width', '100%');
|
|
923
|
+
el16.setAttribute('height', '100%');
|
|
924
|
+
el16.setAttribute('viewBox', '0 0 24 24');
|
|
925
|
+
const el17 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
926
|
+
el17.setAttribute('d', 'M0 0h24v24H0z');
|
|
1301
927
|
el17.setAttribute('fill', 'none');
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
const el19 = dom.
|
|
1307
|
-
el19.setAttribute('
|
|
1308
|
-
el19.setAttribute('
|
|
1309
|
-
|
|
1310
|
-
el20.
|
|
1311
|
-
el20.setAttribute('
|
|
1312
|
-
el20.setAttribute('
|
|
1313
|
-
el20.setAttribute('
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
el21.
|
|
1317
|
-
el21.setAttribute('
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
el22.
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
el32.setAttribute('id', 'lh-tools-locale__selector-wrapper');
|
|
1362
|
-
el32.setAttribute('role', 'menu');
|
|
1363
|
-
el32.setAttribute('aria-labelledby', 'lh-button__swap-locales');
|
|
1364
|
-
el32.setAttribute('aria-hidden', 'true');
|
|
1365
|
-
el32.append(' ', ' ');
|
|
1366
|
-
el27.append(' ', el28, ' ', el32, ' ');
|
|
1367
|
-
const el33 = dom.createElement('button', 'lh-tools__button');
|
|
1368
|
-
el33.setAttribute('id', 'lh-tools-button');
|
|
1369
|
-
el33.setAttribute('title', 'Tools menu');
|
|
1370
|
-
el33.setAttribute('aria-label', 'Toggle report tools menu');
|
|
1371
|
-
el33.setAttribute('aria-haspopup', 'menu');
|
|
1372
|
-
el33.setAttribute('aria-expanded', 'false');
|
|
1373
|
-
el33.setAttribute('aria-controls', 'lh-tools-dropdown');
|
|
1374
|
-
const el34 = dom.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1375
|
-
el34.setAttribute('width', '100%');
|
|
1376
|
-
el34.setAttribute('height', '100%');
|
|
1377
|
-
el34.setAttribute('viewBox', '0 0 24 24');
|
|
1378
|
-
const el35 = dom.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1379
|
-
el35.setAttribute('d', 'M0 0h24v24H0z');
|
|
1380
|
-
el35.setAttribute('fill', 'none');
|
|
1381
|
-
const el36 = dom.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
1382
|
-
el36.setAttribute('d', 'M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z');
|
|
1383
|
-
el34.append(' ', el35, ' ', el36, ' ');
|
|
1384
|
-
el33.append(' ', el34, ' ');
|
|
1385
|
-
const el37 = dom.createElement('div', 'lh-tools__dropdown');
|
|
1386
|
-
el37.setAttribute('id', 'lh-tools-dropdown');
|
|
1387
|
-
el37.setAttribute('role', 'menu');
|
|
1388
|
-
el37.setAttribute('aria-labelledby', 'lh-tools-button');
|
|
1389
|
-
const el38 = dom.createElement('a', 'lh-report-icon lh-report-icon--print');
|
|
1390
|
-
el38.setAttribute('role', 'menuitem');
|
|
1391
|
-
el38.setAttribute('tabindex', '-1');
|
|
1392
|
-
el38.setAttribute('href', '#');
|
|
1393
|
-
el38.setAttribute('data-i18n', 'dropdownPrintSummary');
|
|
1394
|
-
el38.setAttribute('data-action', 'print-summary');
|
|
1395
|
-
const el39 = dom.createElement('a', 'lh-report-icon lh-report-icon--print');
|
|
1396
|
-
el39.setAttribute('role', 'menuitem');
|
|
1397
|
-
el39.setAttribute('tabindex', '-1');
|
|
1398
|
-
el39.setAttribute('href', '#');
|
|
1399
|
-
el39.setAttribute('data-i18n', 'dropdownPrintExpanded');
|
|
1400
|
-
el39.setAttribute('data-action', 'print-expanded');
|
|
1401
|
-
const el40 = dom.createElement('a', 'lh-report-icon lh-report-icon--copy');
|
|
1402
|
-
el40.setAttribute('role', 'menuitem');
|
|
1403
|
-
el40.setAttribute('tabindex', '-1');
|
|
1404
|
-
el40.setAttribute('href', '#');
|
|
1405
|
-
el40.setAttribute('data-i18n', 'dropdownCopyJSON');
|
|
1406
|
-
el40.setAttribute('data-action', 'copy');
|
|
1407
|
-
const el41 = dom.createElement('a', 'lh-report-icon lh-report-icon--download lh-hidden');
|
|
1408
|
-
el41.setAttribute('role', 'menuitem');
|
|
1409
|
-
el41.setAttribute('tabindex', '-1');
|
|
1410
|
-
el41.setAttribute('href', '#');
|
|
1411
|
-
el41.setAttribute('data-i18n', 'dropdownSaveHTML');
|
|
1412
|
-
el41.setAttribute('data-action', 'save-html');
|
|
1413
|
-
const el42 = dom.createElement('a', 'lh-report-icon lh-report-icon--download');
|
|
1414
|
-
el42.setAttribute('role', 'menuitem');
|
|
1415
|
-
el42.setAttribute('tabindex', '-1');
|
|
1416
|
-
el42.setAttribute('href', '#');
|
|
1417
|
-
el42.setAttribute('data-i18n', 'dropdownSaveJSON');
|
|
1418
|
-
el42.setAttribute('data-action', 'save-json');
|
|
1419
|
-
const el43 = dom.createElement('a', 'lh-report-icon lh-report-icon--open');
|
|
1420
|
-
el43.setAttribute('role', 'menuitem');
|
|
1421
|
-
el43.setAttribute('tabindex', '-1');
|
|
1422
|
-
el43.setAttribute('href', '#');
|
|
1423
|
-
el43.setAttribute('data-i18n', 'dropdownViewer');
|
|
1424
|
-
el43.setAttribute('data-action', 'open-viewer');
|
|
1425
|
-
const el44 = dom.createElement('a', 'lh-report-icon lh-report-icon--open');
|
|
1426
|
-
el44.setAttribute('role', 'menuitem');
|
|
1427
|
-
el44.setAttribute('tabindex', '-1');
|
|
1428
|
-
el44.setAttribute('href', '#');
|
|
1429
|
-
el44.setAttribute('data-i18n', 'dropdownSaveGist');
|
|
1430
|
-
el44.setAttribute('data-action', 'save-gist');
|
|
1431
|
-
const el45 = dom.createElement('a', 'lh-report-icon lh-report-icon--dark');
|
|
1432
|
-
el45.setAttribute('role', 'menuitem');
|
|
1433
|
-
el45.setAttribute('tabindex', '-1');
|
|
1434
|
-
el45.setAttribute('href', '#');
|
|
1435
|
-
el45.setAttribute('data-i18n', 'dropdownDarkTheme');
|
|
1436
|
-
el45.setAttribute('data-action', 'toggle-dark');
|
|
1437
|
-
el37.append(' ', el38, ' ', el39, ' ', el40, ' ', ' ', el41, ' ', el42, ' ', el43, ' ', el44, ' ', el45, ' ');
|
|
1438
|
-
el26.append(' ', el27, ' ', el33, ' ', el37, ' ');
|
|
1439
|
-
el2.append(' ', ' ', el3, ' ', el25, ' ', el26, ' ');
|
|
928
|
+
const el18 = dom.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
929
|
+
el18.setAttribute('d', 'M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z');
|
|
930
|
+
el16.append(" ",el17," ",el18," ");
|
|
931
|
+
el15.append(" ",el16," ");
|
|
932
|
+
const el19 = dom.createElement("div", "lh-tools__dropdown");
|
|
933
|
+
el19.setAttribute('id', 'lh-tools-dropdown');
|
|
934
|
+
el19.setAttribute('role', 'menu');
|
|
935
|
+
el19.setAttribute('aria-labelledby', 'lh-tools-button');
|
|
936
|
+
const el20 = dom.createElement("a", "lh-report-icon lh-report-icon--print");
|
|
937
|
+
el20.setAttribute('role', 'menuitem');
|
|
938
|
+
el20.setAttribute('tabindex', '-1');
|
|
939
|
+
el20.setAttribute('href', '#');
|
|
940
|
+
el20.setAttribute('data-i18n', 'dropdownPrintSummary');
|
|
941
|
+
el20.setAttribute('data-action', 'print-summary');
|
|
942
|
+
const el21 = dom.createElement("a", "lh-report-icon lh-report-icon--print");
|
|
943
|
+
el21.setAttribute('role', 'menuitem');
|
|
944
|
+
el21.setAttribute('tabindex', '-1');
|
|
945
|
+
el21.setAttribute('href', '#');
|
|
946
|
+
el21.setAttribute('data-i18n', 'dropdownPrintExpanded');
|
|
947
|
+
el21.setAttribute('data-action', 'print-expanded');
|
|
948
|
+
const el22 = dom.createElement("a", "lh-report-icon lh-report-icon--copy");
|
|
949
|
+
el22.setAttribute('role', 'menuitem');
|
|
950
|
+
el22.setAttribute('tabindex', '-1');
|
|
951
|
+
el22.setAttribute('href', '#');
|
|
952
|
+
el22.setAttribute('data-i18n', 'dropdownCopyJSON');
|
|
953
|
+
el22.setAttribute('data-action', 'copy');
|
|
954
|
+
const el23 = dom.createElement("a", "lh-report-icon lh-report-icon--download lh-hidden");
|
|
955
|
+
el23.setAttribute('role', 'menuitem');
|
|
956
|
+
el23.setAttribute('tabindex', '-1');
|
|
957
|
+
el23.setAttribute('href', '#');
|
|
958
|
+
el23.setAttribute('data-i18n', 'dropdownSaveHTML');
|
|
959
|
+
el23.setAttribute('data-action', 'save-html');
|
|
960
|
+
const el24 = dom.createElement("a", "lh-report-icon lh-report-icon--download");
|
|
961
|
+
el24.setAttribute('role', 'menuitem');
|
|
962
|
+
el24.setAttribute('tabindex', '-1');
|
|
963
|
+
el24.setAttribute('href', '#');
|
|
964
|
+
el24.setAttribute('data-i18n', 'dropdownSaveJSON');
|
|
965
|
+
el24.setAttribute('data-action', 'save-json');
|
|
966
|
+
const el25 = dom.createElement("a", "lh-report-icon lh-report-icon--open");
|
|
967
|
+
el25.setAttribute('role', 'menuitem');
|
|
968
|
+
el25.setAttribute('tabindex', '-1');
|
|
969
|
+
el25.setAttribute('href', '#');
|
|
970
|
+
el25.setAttribute('data-i18n', 'dropdownViewer');
|
|
971
|
+
el25.setAttribute('data-action', 'open-viewer');
|
|
972
|
+
const el26 = dom.createElement("a", "lh-report-icon lh-report-icon--open");
|
|
973
|
+
el26.setAttribute('role', 'menuitem');
|
|
974
|
+
el26.setAttribute('tabindex', '-1');
|
|
975
|
+
el26.setAttribute('href', '#');
|
|
976
|
+
el26.setAttribute('data-i18n', 'dropdownSaveGist');
|
|
977
|
+
el26.setAttribute('data-action', 'save-gist');
|
|
978
|
+
const el27 = dom.createElement("a", "lh-report-icon lh-report-icon--dark");
|
|
979
|
+
el27.setAttribute('role', 'menuitem');
|
|
980
|
+
el27.setAttribute('tabindex', '-1');
|
|
981
|
+
el27.setAttribute('href', '#');
|
|
982
|
+
el27.setAttribute('data-i18n', 'dropdownDarkTheme');
|
|
983
|
+
el27.setAttribute('data-action', 'toggle-dark');
|
|
984
|
+
el19.append(" ",el20," ",el21," ",el22," "," ",el23," ",el24," ",el25," ",el26," ",el27," ");
|
|
985
|
+
el8.append(" ",el9," ",el15," ",el19," ");
|
|
986
|
+
el2.append(" "," ",el3," ",el7," ",el8," ");
|
|
1440
987
|
el0.append(el2);
|
|
1441
988
|
return el0;
|
|
1442
989
|
}
|
|
@@ -1446,10 +993,10 @@ function createTopbarComponent(dom) {
|
|
|
1446
993
|
*/
|
|
1447
994
|
function createWarningsToplevelComponent(dom) {
|
|
1448
995
|
const el0 = dom.createFragment();
|
|
1449
|
-
const el1 = dom.createElement(
|
|
1450
|
-
const el2 = dom.createElement(
|
|
1451
|
-
const el3 = dom.createElement(
|
|
1452
|
-
el1.append(
|
|
996
|
+
const el1 = dom.createElement("div", "lh-warnings lh-warnings--toplevel");
|
|
997
|
+
const el2 = dom.createElement("p", "lh-warnings__msg");
|
|
998
|
+
const el3 = dom.createElement("ul");
|
|
999
|
+
el1.append(" ",el2," ",el3," ");
|
|
1453
1000
|
el0.append(el1);
|
|
1454
1001
|
return el0;
|
|
1455
1002
|
}
|
|
@@ -1583,7 +1130,7 @@ class DOM {
|
|
|
1583
1130
|
*/
|
|
1584
1131
|
createChildOf(parentElem, elementName, className) {
|
|
1585
1132
|
const element = this.createElement(elementName, className);
|
|
1586
|
-
parentElem.
|
|
1133
|
+
parentElem.append(element);
|
|
1587
1134
|
return element;
|
|
1588
1135
|
}
|
|
1589
1136
|
|
|
@@ -1619,16 +1166,20 @@ class DOM {
|
|
|
1619
1166
|
const element = this.createElement('span');
|
|
1620
1167
|
|
|
1621
1168
|
for (const segment of Util.splitMarkdownLink(text)) {
|
|
1169
|
+
const processedSegment = segment.text.includes('`') ?
|
|
1170
|
+
this.convertMarkdownCodeSnippets(segment.text) :
|
|
1171
|
+
segment.text;
|
|
1172
|
+
|
|
1622
1173
|
if (!segment.isLink) {
|
|
1623
1174
|
// Plain text segment.
|
|
1624
|
-
element.
|
|
1175
|
+
element.append(processedSegment);
|
|
1625
1176
|
continue;
|
|
1626
1177
|
}
|
|
1627
1178
|
|
|
1628
1179
|
// Otherwise, append any links found.
|
|
1629
1180
|
const url = new URL(segment.linkHref);
|
|
1630
1181
|
|
|
1631
|
-
const DOCS_ORIGINS = ['https://developers.google.com', 'https://web.dev'];
|
|
1182
|
+
const DOCS_ORIGINS = ['https://developers.google.com', 'https://web.dev', 'https://developer.chrome.com'];
|
|
1632
1183
|
if (DOCS_ORIGINS.includes(url.origin)) {
|
|
1633
1184
|
url.searchParams.set('utm_source', 'lighthouse');
|
|
1634
1185
|
url.searchParams.set('utm_medium', this._lighthouseChannel);
|
|
@@ -1637,9 +1188,9 @@ class DOM {
|
|
|
1637
1188
|
const a = this.createElement('a');
|
|
1638
1189
|
a.rel = 'noopener';
|
|
1639
1190
|
a.target = '_blank';
|
|
1640
|
-
a.
|
|
1191
|
+
a.append(processedSegment);
|
|
1641
1192
|
this.safelySetHref(a, url.href);
|
|
1642
|
-
element.
|
|
1193
|
+
element.append(a);
|
|
1643
1194
|
}
|
|
1644
1195
|
|
|
1645
1196
|
return element;
|
|
@@ -1696,9 +1247,9 @@ class DOM {
|
|
|
1696
1247
|
if (segment.isCode) {
|
|
1697
1248
|
const pre = this.createElement('code');
|
|
1698
1249
|
pre.textContent = segment.text;
|
|
1699
|
-
element.
|
|
1250
|
+
element.append(pre);
|
|
1700
1251
|
} else {
|
|
1701
|
-
element.
|
|
1252
|
+
element.append(this._document.createTextNode(segment.text));
|
|
1702
1253
|
}
|
|
1703
1254
|
}
|
|
1704
1255
|
|
|
@@ -1762,34 +1313,652 @@ class DOM {
|
|
|
1762
1313
|
}
|
|
1763
1314
|
|
|
1764
1315
|
/**
|
|
1765
|
-
* Fires a custom DOM event on target.
|
|
1766
|
-
* @param {string} name Name of the event.
|
|
1767
|
-
* @param {Node=} target DOM node to fire the event on.
|
|
1768
|
-
* @param {*=} detail Custom data to include.
|
|
1316
|
+
* Fires a custom DOM event on target.
|
|
1317
|
+
* @param {string} name Name of the event.
|
|
1318
|
+
* @param {Node=} target DOM node to fire the event on.
|
|
1319
|
+
* @param {*=} detail Custom data to include.
|
|
1320
|
+
*/
|
|
1321
|
+
fireEventOn(name, target = this._document, detail) {
|
|
1322
|
+
const event = new CustomEvent(name, detail ? {detail} : undefined);
|
|
1323
|
+
target.dispatchEvent(event);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Downloads a file (blob) using a[download].
|
|
1328
|
+
* @param {Blob|File} blob The file to save.
|
|
1329
|
+
* @param {string} filename
|
|
1330
|
+
*/
|
|
1331
|
+
saveFile(blob, filename) {
|
|
1332
|
+
const a = this.createElement('a');
|
|
1333
|
+
a.download = filename;
|
|
1334
|
+
this.safelySetBlobHref(a, blob);
|
|
1335
|
+
this._document.body.append(a); // Firefox requires anchor to be in the DOM.
|
|
1336
|
+
a.click();
|
|
1337
|
+
|
|
1338
|
+
// cleanup.
|
|
1339
|
+
this._document.body.removeChild(a);
|
|
1340
|
+
setTimeout(() => URL.revokeObjectURL(a.href), 500);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
|
|
1346
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
1347
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
1348
|
+
*/
|
|
1349
|
+
|
|
1350
|
+
/** @typedef {import('./i18n-formatter').I18nFormatter} I18nFormatter */
|
|
1351
|
+
|
|
1352
|
+
let svgSuffix = 0;
|
|
1353
|
+
|
|
1354
|
+
class Globals {
|
|
1355
|
+
/** @type {I18nFormatter} */
|
|
1356
|
+
// @ts-expect-error: Set in report renderer.
|
|
1357
|
+
static i18n = null;
|
|
1358
|
+
|
|
1359
|
+
/** @type {typeof UIStrings} */
|
|
1360
|
+
// @ts-expect-error: Set in report renderer.
|
|
1361
|
+
static strings = {};
|
|
1362
|
+
|
|
1363
|
+
/** @type {LH.ReportResult | null} */
|
|
1364
|
+
static reportJson = null;
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* @param {{providedStrings: Record<string, string>; i18n: I18nFormatter; reportJson: LH.ReportResult | null}} options
|
|
1368
|
+
*/
|
|
1369
|
+
static apply(options) {
|
|
1370
|
+
Globals.strings = {
|
|
1371
|
+
// Set missing renderer strings to default (english) values.
|
|
1372
|
+
...UIStrings,
|
|
1373
|
+
...options.providedStrings,
|
|
1374
|
+
};
|
|
1375
|
+
Globals.i18n = options.i18n;
|
|
1376
|
+
Globals.reportJson = options.reportJson;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
static getUniqueSuffix() {
|
|
1380
|
+
return svgSuffix++;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
static resetUniqueSuffix() {
|
|
1384
|
+
svgSuffix = 0;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
|
|
1390
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
1391
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
1392
|
+
*/
|
|
1393
|
+
|
|
1394
|
+
const SCREENSHOT_PREFIX = 'data:image/jpeg;base64,';
|
|
1395
|
+
|
|
1396
|
+
/** @typedef {import('../../types/lhr/audit-details').default.ItemValueType} ItemValueType */
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* Upgrades an lhr object in-place to account for changes in the data structure over major versions.
|
|
1400
|
+
* @param {LH.Result} lhr
|
|
1401
|
+
*/
|
|
1402
|
+
function upgradeLhrForCompatibility(lhr) {
|
|
1403
|
+
// If LHR is older (≤3.0.3), it has no locale setting. Set default.
|
|
1404
|
+
if (!lhr.configSettings.locale) {
|
|
1405
|
+
lhr.configSettings.locale = 'en';
|
|
1406
|
+
}
|
|
1407
|
+
if (!lhr.configSettings.formFactor) {
|
|
1408
|
+
// @ts-expect-error fallback handling for emulatedFormFactor
|
|
1409
|
+
lhr.configSettings.formFactor = lhr.configSettings.emulatedFormFactor;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
lhr.finalDisplayedUrl = Util.getFinalDisplayedUrl(lhr);
|
|
1413
|
+
lhr.mainDocumentUrl = Util.getMainDocumentUrl(lhr);
|
|
1414
|
+
|
|
1415
|
+
for (const audit of Object.values(lhr.audits)) {
|
|
1416
|
+
// Turn 'not-applicable' (LHR <4.0) and 'not_applicable' (older proto versions)
|
|
1417
|
+
// into 'notApplicable' (LHR ≥4.0).
|
|
1418
|
+
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
|
|
1419
|
+
// eslint-disable-next-line max-len
|
|
1420
|
+
if (audit.scoreDisplayMode === 'not_applicable' || audit.scoreDisplayMode === 'not-applicable') {
|
|
1421
|
+
audit.scoreDisplayMode = 'notApplicable';
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if (audit.details) {
|
|
1425
|
+
// Turn `auditDetails.type` of undefined (LHR <4.2) and 'diagnostic' (LHR <5.0)
|
|
1426
|
+
// into 'debugdata' (LHR ≥5.0).
|
|
1427
|
+
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
|
|
1428
|
+
if (audit.details.type === undefined || audit.details.type === 'diagnostic') {
|
|
1429
|
+
// @ts-expect-error details is of type never.
|
|
1430
|
+
audit.details.type = 'debugdata';
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// Add the jpg data URL prefix to filmstrip screenshots without them (LHR <5.0).
|
|
1434
|
+
if (audit.details.type === 'filmstrip') {
|
|
1435
|
+
for (const screenshot of audit.details.items) {
|
|
1436
|
+
if (!screenshot.data.startsWith(SCREENSHOT_PREFIX)) {
|
|
1437
|
+
screenshot.data = SCREENSHOT_PREFIX + screenshot.data;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// Circa 10.0, table items were refactored.
|
|
1443
|
+
if (audit.details.type === 'table') {
|
|
1444
|
+
for (const heading of audit.details.headings) {
|
|
1445
|
+
/** @type {{itemType: ItemValueType|undefined, text: string|undefined}} */
|
|
1446
|
+
// @ts-expect-error
|
|
1447
|
+
const {itemType, text} = heading;
|
|
1448
|
+
if (itemType !== undefined) {
|
|
1449
|
+
heading.valueType = itemType;
|
|
1450
|
+
// @ts-expect-error
|
|
1451
|
+
delete heading.itemType;
|
|
1452
|
+
}
|
|
1453
|
+
if (text !== undefined) {
|
|
1454
|
+
heading.label = text;
|
|
1455
|
+
// @ts-expect-error
|
|
1456
|
+
delete heading.text;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
// @ts-expect-error
|
|
1460
|
+
const subItemsItemType = heading.subItemsHeading?.itemType;
|
|
1461
|
+
if (heading.subItemsHeading && subItemsItemType !== undefined) {
|
|
1462
|
+
heading.subItemsHeading.valueType = subItemsItemType;
|
|
1463
|
+
// @ts-expect-error
|
|
1464
|
+
delete heading.subItemsHeading.itemType;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// TODO: convert printf-style displayValue.
|
|
1470
|
+
// Added: #5099, v3
|
|
1471
|
+
// Removed: #6767, v4
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// This backcompat converts old LHRs (<9.0.0) to use the new "hidden" group.
|
|
1476
|
+
// Old LHRs used "no group" to identify audits that should be hidden in performance instead of the "hidden" group.
|
|
1477
|
+
// Newer LHRs use "no group" to identify opportunities and diagnostics whose groups are assigned by details type.
|
|
1478
|
+
const [majorVersion] = lhr.lighthouseVersion.split('.').map(Number);
|
|
1479
|
+
const perfCategory = lhr.categories['performance'];
|
|
1480
|
+
if (majorVersion < 9 && perfCategory) {
|
|
1481
|
+
if (!lhr.categoryGroups) lhr.categoryGroups = {};
|
|
1482
|
+
lhr.categoryGroups['hidden'] = {title: ''};
|
|
1483
|
+
for (const auditRef of perfCategory.auditRefs) {
|
|
1484
|
+
if (!auditRef.group) {
|
|
1485
|
+
auditRef.group = 'hidden';
|
|
1486
|
+
} else if (['load-opportunities', 'diagnostics'].includes(auditRef.group)) {
|
|
1487
|
+
delete auditRef.group;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// Add some minimal stuff so older reports still work.
|
|
1493
|
+
if (!lhr.environment) {
|
|
1494
|
+
// @ts-expect-error
|
|
1495
|
+
lhr.environment = {benchmarkIndex: 0};
|
|
1496
|
+
}
|
|
1497
|
+
if (!lhr.configSettings.screenEmulation) {
|
|
1498
|
+
// @ts-expect-error
|
|
1499
|
+
lhr.configSettings.screenEmulation = {};
|
|
1500
|
+
}
|
|
1501
|
+
if (!lhr.i18n) {
|
|
1502
|
+
// @ts-expect-error
|
|
1503
|
+
lhr.i18n = {};
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// In 10.0, full-page-screenshot became a top-level property on the LHR.
|
|
1507
|
+
if (lhr.audits['full-page-screenshot']) {
|
|
1508
|
+
const details = /** @type {LH.Result.FullPageScreenshot=} */ (
|
|
1509
|
+
lhr.audits['full-page-screenshot'].details);
|
|
1510
|
+
if (details) {
|
|
1511
|
+
lhr.fullPageScreenshot = {
|
|
1512
|
+
screenshot: details.screenshot,
|
|
1513
|
+
nodes: details.nodes,
|
|
1514
|
+
};
|
|
1515
|
+
} else {
|
|
1516
|
+
lhr.fullPageScreenshot = null;
|
|
1517
|
+
}
|
|
1518
|
+
delete lhr.audits['full-page-screenshot'];
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/**
|
|
1523
|
+
* @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
|
|
1524
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
1525
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
1526
|
+
*/
|
|
1527
|
+
|
|
1528
|
+
const RATINGS = Util.RATINGS;
|
|
1529
|
+
|
|
1530
|
+
class ReportUtils {
|
|
1531
|
+
/**
|
|
1532
|
+
* Returns a new LHR that's reshaped for slightly better ergonomics within the report rendereer.
|
|
1533
|
+
* Also, sets up the localized UI strings used within renderer and makes changes to old LHRs to be
|
|
1534
|
+
* compatible with current renderer.
|
|
1535
|
+
* The LHR passed in is not mutated.
|
|
1536
|
+
* TODO(team): we all agree the LHR shape change is technical debt we should fix
|
|
1537
|
+
* @param {LH.Result} lhr
|
|
1538
|
+
* @return {LH.ReportResult}
|
|
1539
|
+
*/
|
|
1540
|
+
static prepareReportResult(lhr) {
|
|
1541
|
+
// If any mutations happen to the report within the renderers, we want the original object untouched
|
|
1542
|
+
const clone = /** @type {LH.ReportResult} */ (JSON.parse(JSON.stringify(lhr)));
|
|
1543
|
+
upgradeLhrForCompatibility(clone);
|
|
1544
|
+
|
|
1545
|
+
for (const audit of Object.values(clone.audits)) {
|
|
1546
|
+
// Attach table/opportunity items with entity information.
|
|
1547
|
+
ReportUtils.classifyEntities(clone.entities, audit);
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// For convenience, smoosh all AuditResults into their auditRef (which has just weight & group)
|
|
1551
|
+
if (typeof clone.categories !== 'object') throw new Error('No categories provided.');
|
|
1552
|
+
|
|
1553
|
+
/** @type {Map<string, Array<LH.ReportResult.AuditRef>>} */
|
|
1554
|
+
const relevantAuditToMetricsMap = new Map();
|
|
1555
|
+
|
|
1556
|
+
for (const category of Object.values(clone.categories)) {
|
|
1557
|
+
// Make basic lookup table for relevantAudits
|
|
1558
|
+
category.auditRefs.forEach(metricRef => {
|
|
1559
|
+
if (!metricRef.relevantAudits) return;
|
|
1560
|
+
metricRef.relevantAudits.forEach(auditId => {
|
|
1561
|
+
const arr = relevantAuditToMetricsMap.get(auditId) || [];
|
|
1562
|
+
arr.push(metricRef);
|
|
1563
|
+
relevantAuditToMetricsMap.set(auditId, arr);
|
|
1564
|
+
});
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
category.auditRefs.forEach(auditRef => {
|
|
1568
|
+
const result = clone.audits[auditRef.id];
|
|
1569
|
+
auditRef.result = result;
|
|
1570
|
+
|
|
1571
|
+
// Attach any relevantMetric auditRefs
|
|
1572
|
+
if (relevantAuditToMetricsMap.has(auditRef.id)) {
|
|
1573
|
+
auditRef.relevantMetrics = relevantAuditToMetricsMap.get(auditRef.id);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// attach the stackpacks to the auditRef object
|
|
1577
|
+
if (clone.stackPacks) {
|
|
1578
|
+
clone.stackPacks.forEach(pack => {
|
|
1579
|
+
if (pack.descriptions[auditRef.id]) {
|
|
1580
|
+
auditRef.stackPacks = auditRef.stackPacks || [];
|
|
1581
|
+
auditRef.stackPacks.push({
|
|
1582
|
+
title: pack.title,
|
|
1583
|
+
iconDataURL: pack.iconDataURL,
|
|
1584
|
+
description: pack.descriptions[auditRef.id],
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
return clone;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Given an audit's details, identify and return a URL locator function that
|
|
1597
|
+
* can be called later with an `item` to extract the URL of it.
|
|
1598
|
+
* @param {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading[]>} headings
|
|
1599
|
+
* @return {{(item: LH.FormattedIcu<LH.Audit.Details.TableItem>): string|undefined}=}
|
|
1600
|
+
*/
|
|
1601
|
+
static getUrlLocatorFn(headings) {
|
|
1602
|
+
// The most common type, valueType=url.
|
|
1603
|
+
const urlKey = headings.find(heading => heading.valueType === 'url')?.key;
|
|
1604
|
+
if (urlKey && typeof urlKey === 'string') {
|
|
1605
|
+
// Return a function that extracts item.url.
|
|
1606
|
+
return (item) => {
|
|
1607
|
+
const url = item[urlKey];
|
|
1608
|
+
if (typeof url === 'string') return url;
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// The second common type, valueType=source-location.
|
|
1613
|
+
const srcLocationKey = headings.find(heading => heading.valueType === 'source-location')?.key;
|
|
1614
|
+
if (srcLocationKey) {
|
|
1615
|
+
// Return a function that extracts item.source.url.
|
|
1616
|
+
return (item) => {
|
|
1617
|
+
const sourceLocation = item[srcLocationKey];
|
|
1618
|
+
if (typeof sourceLocation === 'object' && sourceLocation.type === 'source-location') {
|
|
1619
|
+
return sourceLocation.url;
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// More specific tests go here, as we need to identify URLs in more audits.
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* Mark TableItems/OpportunityItems with entity names.
|
|
1629
|
+
* @param {LH.Result.Entities|undefined} entities
|
|
1630
|
+
* @param {import('../../types/lhr/audit-result').Result} audit
|
|
1631
|
+
*/
|
|
1632
|
+
static classifyEntities(entities, audit) {
|
|
1633
|
+
if (!entities) return;
|
|
1634
|
+
if (audit.details?.type !== 'opportunity' && audit.details?.type !== 'table') {
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// If details.items are already marked with entity attribute during an audit, nothing to do here.
|
|
1639
|
+
const {items, headings} = audit.details;
|
|
1640
|
+
if (!items.length || items.some(item => item.entity)) return;
|
|
1641
|
+
|
|
1642
|
+
// Identify a URL-locator function that we could call against each item to get its URL.
|
|
1643
|
+
const urlLocatorFn = ReportUtils.getUrlLocatorFn(headings);
|
|
1644
|
+
if (!urlLocatorFn) return;
|
|
1645
|
+
|
|
1646
|
+
for (const item of items) {
|
|
1647
|
+
const url = urlLocatorFn(item);
|
|
1648
|
+
if (!url) continue;
|
|
1649
|
+
|
|
1650
|
+
let origin = '';
|
|
1651
|
+
try {
|
|
1652
|
+
// Non-URLs can appear in valueType: url columns, like 'Unattributable'
|
|
1653
|
+
origin = Util.parseURL(url).origin;
|
|
1654
|
+
} catch {}
|
|
1655
|
+
if (!origin) continue;
|
|
1656
|
+
|
|
1657
|
+
const entity = entities.find(e => e.origins.includes(origin));
|
|
1658
|
+
if (entity) item.entity = entity.name;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* @param {LH.Result['configSettings']} settings
|
|
1664
|
+
* @return {!{deviceEmulation: string, screenEmulation?: string, networkThrottling: string, cpuThrottling: string, summary: string}}
|
|
1665
|
+
*/
|
|
1666
|
+
static getEmulationDescriptions(settings) {
|
|
1667
|
+
let cpuThrottling;
|
|
1668
|
+
let networkThrottling;
|
|
1669
|
+
let summary;
|
|
1670
|
+
|
|
1671
|
+
const throttling = settings.throttling;
|
|
1672
|
+
const i18n = Globals.i18n;
|
|
1673
|
+
const strings = Globals.strings;
|
|
1674
|
+
|
|
1675
|
+
switch (settings.throttlingMethod) {
|
|
1676
|
+
case 'provided':
|
|
1677
|
+
summary = networkThrottling = cpuThrottling = strings.throttlingProvided;
|
|
1678
|
+
break;
|
|
1679
|
+
case 'devtools': {
|
|
1680
|
+
const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
|
|
1681
|
+
// eslint-disable-next-line max-len
|
|
1682
|
+
cpuThrottling = `${i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
|
|
1683
|
+
networkThrottling = `${i18n.formatMilliseconds(requestLatencyMs)} HTTP RTT, ` +
|
|
1684
|
+
`${i18n.formatKbps(throttling.downloadThroughputKbps)} down, ` +
|
|
1685
|
+
`${i18n.formatKbps(throttling.uploadThroughputKbps)} up (DevTools)`;
|
|
1686
|
+
|
|
1687
|
+
const isSlow4G = () => {
|
|
1688
|
+
return requestLatencyMs === 150 * 3.75 &&
|
|
1689
|
+
throttling.downloadThroughputKbps === 1.6 * 1024 * 0.9 &&
|
|
1690
|
+
throttling.uploadThroughputKbps === 750 * 0.9;
|
|
1691
|
+
};
|
|
1692
|
+
summary = isSlow4G() ? strings.runtimeSlow4g : strings.runtimeCustom;
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
case 'simulate': {
|
|
1696
|
+
const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling;
|
|
1697
|
+
// eslint-disable-next-line max-len
|
|
1698
|
+
cpuThrottling = `${i18n.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
|
|
1699
|
+
networkThrottling = `${i18n.formatMilliseconds(rttMs)} TCP RTT, ` +
|
|
1700
|
+
`${i18n.formatKbps(throughputKbps)} throughput (Simulated)`;
|
|
1701
|
+
|
|
1702
|
+
const isSlow4G = () => {
|
|
1703
|
+
return rttMs === 150 && throughputKbps === 1.6 * 1024;
|
|
1704
|
+
};
|
|
1705
|
+
summary = isSlow4G() ?
|
|
1706
|
+
strings.runtimeSlow4g : strings.runtimeCustom;
|
|
1707
|
+
break;
|
|
1708
|
+
}
|
|
1709
|
+
default:
|
|
1710
|
+
summary = cpuThrottling = networkThrottling = strings.runtimeUnknown;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
// devtools-entry.js always sets `screenEmulation.disabled` when using mobile emulation,
|
|
1714
|
+
// because we handle the emulation outside of Lighthouse. Since the screen truly is emulated
|
|
1715
|
+
// as a mobile device, ignore `.disabled` in devtools and just check the form factor
|
|
1716
|
+
const isScreenEmulationDisabled = settings.channel === 'devtools' ?
|
|
1717
|
+
false :
|
|
1718
|
+
settings.screenEmulation.disabled;
|
|
1719
|
+
const isScreenEmulationMobile = settings.channel === 'devtools' ?
|
|
1720
|
+
settings.formFactor === 'mobile' :
|
|
1721
|
+
settings.screenEmulation.mobile;
|
|
1722
|
+
|
|
1723
|
+
let deviceEmulation = strings.runtimeMobileEmulation;
|
|
1724
|
+
if (isScreenEmulationDisabled) {
|
|
1725
|
+
deviceEmulation = strings.runtimeNoEmulation;
|
|
1726
|
+
} else if (!isScreenEmulationMobile) {
|
|
1727
|
+
deviceEmulation = strings.runtimeDesktopEmulation;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const screenEmulation = isScreenEmulationDisabled ?
|
|
1731
|
+
undefined :
|
|
1732
|
+
// eslint-disable-next-line max-len
|
|
1733
|
+
`${settings.screenEmulation.width}x${settings.screenEmulation.height}, DPR ${settings.screenEmulation.deviceScaleFactor}`;
|
|
1734
|
+
|
|
1735
|
+
return {
|
|
1736
|
+
deviceEmulation,
|
|
1737
|
+
screenEmulation,
|
|
1738
|
+
cpuThrottling,
|
|
1739
|
+
networkThrottling,
|
|
1740
|
+
summary,
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* Used to determine if the "passed" for the purposes of showing up in the "failed" or "passed"
|
|
1746
|
+
* sections of the report.
|
|
1747
|
+
*
|
|
1748
|
+
* @param {{score: (number|null), scoreDisplayMode: string}} audit
|
|
1749
|
+
* @return {boolean}
|
|
1750
|
+
*/
|
|
1751
|
+
static showAsPassed(audit) {
|
|
1752
|
+
switch (audit.scoreDisplayMode) {
|
|
1753
|
+
case 'manual':
|
|
1754
|
+
case 'notApplicable':
|
|
1755
|
+
return true;
|
|
1756
|
+
case 'error':
|
|
1757
|
+
case 'informative':
|
|
1758
|
+
return false;
|
|
1759
|
+
case 'numeric':
|
|
1760
|
+
case 'binary':
|
|
1761
|
+
default:
|
|
1762
|
+
return Number(audit.score) >= RATINGS.PASS.minScore;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Convert a score to a rating label.
|
|
1768
|
+
* TODO: Return `'error'` for `score === null && !scoreDisplayMode`.
|
|
1769
|
+
*
|
|
1770
|
+
* @param {number|null} score
|
|
1771
|
+
* @param {string=} scoreDisplayMode
|
|
1772
|
+
* @return {string}
|
|
1773
|
+
*/
|
|
1774
|
+
static calculateRating(score, scoreDisplayMode) {
|
|
1775
|
+
// Handle edge cases first, manual and not applicable receive 'pass', errored audits receive 'error'
|
|
1776
|
+
if (scoreDisplayMode === 'manual' || scoreDisplayMode === 'notApplicable') {
|
|
1777
|
+
return RATINGS.PASS.label;
|
|
1778
|
+
} else if (scoreDisplayMode === 'error') {
|
|
1779
|
+
return RATINGS.ERROR.label;
|
|
1780
|
+
} else if (score === null) {
|
|
1781
|
+
return RATINGS.FAIL.label;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// At this point, we're rating a standard binary/numeric audit
|
|
1785
|
+
let rating = RATINGS.FAIL.label;
|
|
1786
|
+
if (score >= RATINGS.PASS.minScore) {
|
|
1787
|
+
rating = RATINGS.PASS.label;
|
|
1788
|
+
} else if (score >= RATINGS.AVERAGE.minScore) {
|
|
1789
|
+
rating = RATINGS.AVERAGE.label;
|
|
1790
|
+
}
|
|
1791
|
+
return rating;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* @param {LH.ReportResult.Category} category
|
|
1796
|
+
*/
|
|
1797
|
+
static calculateCategoryFraction(category) {
|
|
1798
|
+
let numPassableAudits = 0;
|
|
1799
|
+
let numPassed = 0;
|
|
1800
|
+
let numInformative = 0;
|
|
1801
|
+
let totalWeight = 0;
|
|
1802
|
+
for (const auditRef of category.auditRefs) {
|
|
1803
|
+
const auditPassed = ReportUtils.showAsPassed(auditRef.result);
|
|
1804
|
+
|
|
1805
|
+
// Don't count the audit if it's manual, N/A, or isn't displayed.
|
|
1806
|
+
if (auditRef.group === 'hidden' ||
|
|
1807
|
+
auditRef.result.scoreDisplayMode === 'manual' ||
|
|
1808
|
+
auditRef.result.scoreDisplayMode === 'notApplicable') {
|
|
1809
|
+
continue;
|
|
1810
|
+
} else if (auditRef.result.scoreDisplayMode === 'informative') {
|
|
1811
|
+
if (!auditPassed) {
|
|
1812
|
+
++numInformative;
|
|
1813
|
+
}
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
++numPassableAudits;
|
|
1818
|
+
totalWeight += auditRef.weight;
|
|
1819
|
+
if (auditPassed) numPassed++;
|
|
1820
|
+
}
|
|
1821
|
+
return {numPassed, numPassableAudits, numInformative, totalWeight};
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/**
|
|
1825
|
+
* @param {string} categoryId
|
|
1769
1826
|
*/
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
target.dispatchEvent(event);
|
|
1827
|
+
static isPluginCategory(categoryId) {
|
|
1828
|
+
return categoryId.startsWith('lighthouse-plugin-');
|
|
1773
1829
|
}
|
|
1774
1830
|
|
|
1775
1831
|
/**
|
|
1776
|
-
*
|
|
1777
|
-
* @param {Blob|File} blob The file to save.
|
|
1778
|
-
* @param {string} filename
|
|
1832
|
+
* @param {LH.Result.GatherMode} gatherMode
|
|
1779
1833
|
*/
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
a.download = filename;
|
|
1783
|
-
this.safelySetBlobHref(a, blob);
|
|
1784
|
-
this._document.body.appendChild(a); // Firefox requires anchor to be in the DOM.
|
|
1785
|
-
a.click();
|
|
1786
|
-
|
|
1787
|
-
// cleanup.
|
|
1788
|
-
this._document.body.removeChild(a);
|
|
1789
|
-
setTimeout(() => URL.revokeObjectURL(a.href), 500);
|
|
1834
|
+
static shouldDisplayAsFraction(gatherMode) {
|
|
1835
|
+
return gatherMode === 'timespan' || gatherMode === 'snapshot';
|
|
1790
1836
|
}
|
|
1791
1837
|
}
|
|
1792
1838
|
|
|
1839
|
+
/**
|
|
1840
|
+
* Report-renderer-specific strings.
|
|
1841
|
+
*/
|
|
1842
|
+
const UIStrings = {
|
|
1843
|
+
/** Disclaimer shown to users below the metric values (First Contentful Paint, Time to Interactive, etc) to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. */
|
|
1844
|
+
varianceDisclaimer: 'Values are estimated and may vary. The [performance score is calculated](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/) directly from these metrics.',
|
|
1845
|
+
/** Text link pointing to an interactive calculator that explains Lighthouse scoring. The link text should be fairly short. */
|
|
1846
|
+
calculatorLink: 'See calculator.',
|
|
1847
|
+
/** Label preceding a radio control for filtering the list of audits. The radio choices are various performance metrics (FCP, LCP, TBT), and if chosen, the audits in the report are hidden if they are not relevant to the selected metric. */
|
|
1848
|
+
showRelevantAudits: 'Show audits relevant to:',
|
|
1849
|
+
/** Column heading label for the listing of opportunity audits. Each audit title represents an opportunity. There are only 2 columns, so no strict character limit. */
|
|
1850
|
+
opportunityResourceColumnLabel: 'Opportunity',
|
|
1851
|
+
/** Column heading label for the estimated page load savings of opportunity audits. Estimated Savings is the total amount of time (in seconds) that Lighthouse computed could be reduced from the total page load time, if the suggested action is taken. There are only 2 columns, so no strict character limit. */
|
|
1852
|
+
opportunitySavingsColumnLabel: 'Estimated Savings',
|
|
1853
|
+
|
|
1854
|
+
/** An error string displayed next to a particular audit when it has errored, but not provided any specific error message. */
|
|
1855
|
+
errorMissingAuditInfo: 'Report error: no audit information',
|
|
1856
|
+
/** A label, shown next to an audit title or metric title, indicating that there was an error computing it. The user can hover on the label to reveal a tooltip with the extended error message. Translation should be short (< 20 characters). */
|
|
1857
|
+
errorLabel: 'Error!',
|
|
1858
|
+
/** This label is shown above a bulleted list of warnings. It is shown directly below an audit that produced warnings. Warnings describe situations the user should be aware of, as Lighthouse was unable to complete all the work required on this audit. For example, The 'Unable to decode image (biglogo.jpg)' warning may show up below an image encoding audit. */
|
|
1859
|
+
warningHeader: 'Warnings: ',
|
|
1860
|
+
/** Section heading shown above a list of passed audits that contain warnings. Audits under this section do not negatively impact the score, but Lighthouse has generated some potentially actionable suggestions that should be reviewed. This section is expanded by default and displays after the failing audits. */
|
|
1861
|
+
warningAuditsGroupTitle: 'Passed audits but with warnings',
|
|
1862
|
+
/** Section heading shown above a list of audits that are passing. 'Passed' here refers to a passing grade. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
1863
|
+
passedAuditsGroupTitle: 'Passed audits',
|
|
1864
|
+
/** Section heading shown above a list of audits that do not apply to the page. For example, if an audit is 'Are images optimized?', but the page has no images on it, the audit will be marked as not applicable. This is neither passing or failing. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
1865
|
+
notApplicableAuditsGroupTitle: 'Not applicable',
|
|
1866
|
+
/** Section heading shown above a list of audits that were not computed by Lighthouse. They serve as a list of suggestions for the user to go and manually check. For example, Lighthouse can't automate testing cross-browser compatibility, so that is listed within this section, so the user is reminded to test it themselves. This section is collapsed by default, as the user should be focusing on the failed audits instead. Users can click this heading to reveal the list. */
|
|
1867
|
+
manualAuditsGroupTitle: 'Additional items to manually check',
|
|
1868
|
+
|
|
1869
|
+
/** Label shown preceding any important warnings that may have invalidated the entire report. For example, if the user has Chrome extensions installed, they may add enough performance overhead that Lighthouse's performance metrics are unreliable. If shown, this will be displayed at the top of the report UI. */
|
|
1870
|
+
toplevelWarningsMessage: 'There were issues affecting this run of Lighthouse:',
|
|
1871
|
+
|
|
1872
|
+
/** String of text shown in a graphical representation of the flow of network requests for the web page. This label represents the initial network request that fetches an HTML page. This navigation may be redirected (eg. Initial navigation to http://example.com redirects to https://www.example.com). */
|
|
1873
|
+
crcInitialNavigation: 'Initial Navigation',
|
|
1874
|
+
/** Label of value shown in the summary of critical request chains. Refers to the total amount of time (milliseconds) of the longest critical path chain/sequence of network requests. Example value: 2310 ms */
|
|
1875
|
+
crcLongestDurationLabel: 'Maximum critical path latency:',
|
|
1876
|
+
|
|
1877
|
+
/** Label for button that shows all lines of the snippet when clicked */
|
|
1878
|
+
snippetExpandButtonLabel: 'Expand snippet',
|
|
1879
|
+
/** Label for button that only shows a few lines of the snippet when clicked */
|
|
1880
|
+
snippetCollapseButtonLabel: 'Collapse snippet',
|
|
1881
|
+
|
|
1882
|
+
/** Explanation shown to users below performance results to inform them that the test was done with a 4G network connection and to warn them that the numbers they see will likely change slightly the next time they run Lighthouse. 'Lighthouse' becomes link text to additional documentation. */
|
|
1883
|
+
lsPerformanceCategoryDescription: '[Lighthouse](https://developers.google.com/web/tools/lighthouse/) analysis of the current page on an emulated mobile network. Values are estimated and may vary.',
|
|
1884
|
+
/** Title of the lab data section of the Performance category. Within this section are various speed metrics which quantify the pageload performance into values presented in seconds and milliseconds. "Lab" is an abbreviated form of "laboratory", and refers to the fact that the data is from a controlled test of a website, not measurements from real users visiting that site. */
|
|
1885
|
+
labDataTitle: 'Lab Data',
|
|
1886
|
+
|
|
1887
|
+
/** This label is for a checkbox above a table of items loaded by a web page. The checkbox is used to show or hide third-party (or "3rd-party") resources in the table, where "third-party resources" refers to items loaded by a web page from URLs that aren't controlled by the owner of the web page. */
|
|
1888
|
+
thirdPartyResourcesLabel: 'Show 3rd-party resources',
|
|
1889
|
+
/** This label is for a button that opens a new tab to a webapp called "Treemap", which is a nested visual representation of a heierarchy of data related to the reports (script bytes and coverage, resource breakdown, etc.) */
|
|
1890
|
+
viewTreemapLabel: 'View Treemap',
|
|
1891
|
+
/** This label is for a button that will show the user a trace of the page. */
|
|
1892
|
+
viewTraceLabel: 'View Trace',
|
|
1893
|
+
/** This label is for a button that will show the user a trace of the page. */
|
|
1894
|
+
viewOriginalTraceLabel: 'View Original Trace',
|
|
1895
|
+
|
|
1896
|
+
/** Option in a dropdown menu that opens a small, summary report in a print dialog. */
|
|
1897
|
+
dropdownPrintSummary: 'Print Summary',
|
|
1898
|
+
/** Option in a dropdown menu that opens a full Lighthouse report in a print dialog. */
|
|
1899
|
+
dropdownPrintExpanded: 'Print Expanded',
|
|
1900
|
+
/** Option in a dropdown menu that copies the Lighthouse JSON object to the system clipboard. */
|
|
1901
|
+
dropdownCopyJSON: 'Copy JSON',
|
|
1902
|
+
/** Option in a dropdown menu that saves the Lighthouse report HTML locally to the system as a '.html' file. */
|
|
1903
|
+
dropdownSaveHTML: 'Save as HTML',
|
|
1904
|
+
/** Option in a dropdown menu that saves the Lighthouse JSON object to the local system as a '.json' file. */
|
|
1905
|
+
dropdownSaveJSON: 'Save as JSON',
|
|
1906
|
+
/** Option in a dropdown menu that opens the current report in the Lighthouse Viewer Application. */
|
|
1907
|
+
dropdownViewer: 'Open in Viewer',
|
|
1908
|
+
/** Option in a dropdown menu that saves the current report as a new GitHub Gist. */
|
|
1909
|
+
dropdownSaveGist: 'Save as Gist',
|
|
1910
|
+
/** Option in a dropdown menu that toggles the themeing of the report between Light(default) and Dark themes. */
|
|
1911
|
+
dropdownDarkTheme: 'Toggle Dark Theme',
|
|
1912
|
+
|
|
1913
|
+
/** Label for a row in a table that describes the kind of device that was emulated for the Lighthouse run. Example values for row elements: 'No Emulation', 'Emulated Desktop', etc. */
|
|
1914
|
+
runtimeSettingsDevice: 'Device',
|
|
1915
|
+
/** Label for a row in a table that describes the network throttling conditions that were used during a Lighthouse run, if any. */
|
|
1916
|
+
runtimeSettingsNetworkThrottling: 'Network throttling',
|
|
1917
|
+
/** Label for a row in a table that describes the CPU throttling conditions that were used during a Lighthouse run, if any.*/
|
|
1918
|
+
runtimeSettingsCPUThrottling: 'CPU throttling',
|
|
1919
|
+
/** Label for a row in a table that shows the User Agent that was used to send out all network requests during the Lighthouse run. */
|
|
1920
|
+
runtimeSettingsUANetwork: 'User agent (network)',
|
|
1921
|
+
/** Label for a row in a table that shows the estimated CPU power of the machine running Lighthouse. Example row values: 532, 1492, 783. */
|
|
1922
|
+
runtimeSettingsBenchmark: 'Unthrottled CPU/Memory Power',
|
|
1923
|
+
/** Label for a row in a table that shows the version of the Axe library used. Example row values: 2.1.0, 3.2.3 */
|
|
1924
|
+
runtimeSettingsAxeVersion: 'Axe version',
|
|
1925
|
+
/** Label for a row in a table that shows the screen resolution and DPR that was emulated for the Lighthouse run. Example values: '800x600, DPR: 3' */
|
|
1926
|
+
runtimeSettingsScreenEmulation: 'Screen emulation',
|
|
1927
|
+
|
|
1928
|
+
/** Label for button to create an issue against the Lighthouse GitHub project. */
|
|
1929
|
+
footerIssue: 'File an issue',
|
|
1930
|
+
|
|
1931
|
+
/** Descriptive explanation for emulation setting when no device emulation is set. */
|
|
1932
|
+
runtimeNoEmulation: 'No emulation',
|
|
1933
|
+
/** Descriptive explanation for emulation setting when emulating a Moto G Power mobile device. */
|
|
1934
|
+
runtimeMobileEmulation: 'Emulated Moto G Power',
|
|
1935
|
+
/** Descriptive explanation for emulation setting when emulating a generic desktop form factor, as opposed to a mobile-device like form factor. */
|
|
1936
|
+
runtimeDesktopEmulation: 'Emulated Desktop',
|
|
1937
|
+
/** Descriptive explanation for a runtime setting that is set to an unknown value. */
|
|
1938
|
+
runtimeUnknown: 'Unknown',
|
|
1939
|
+
/** Descriptive label that this analysis run was from a single pageload of a browser (not a summary of hundreds of loads) */
|
|
1940
|
+
runtimeSingleLoad: 'Single page load',
|
|
1941
|
+
/** Descriptive label that this analysis only considers the initial load of the page, and no interaction beyond when the page had "fully loaded" */
|
|
1942
|
+
runtimeAnalysisWindow: 'Initial page load',
|
|
1943
|
+
/** Descriptive explanation that this analysis run was from a single pageload of a browser, whereas field data often summarizes hundreds+ of page loads */
|
|
1944
|
+
runtimeSingleLoadTooltip: 'This data is taken from a single page load, as opposed to field data summarizing many sessions.', // eslint-disable-line max-len
|
|
1945
|
+
|
|
1946
|
+
/** Descriptive explanation for environment throttling that was provided by the runtime environment instead of provided by Lighthouse throttling. */
|
|
1947
|
+
throttlingProvided: 'Provided by environment',
|
|
1948
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
1949
|
+
show: 'Show',
|
|
1950
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Show' and 'Hide'. */
|
|
1951
|
+
hide: 'Hide',
|
|
1952
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
1953
|
+
expandView: 'Expand view',
|
|
1954
|
+
/** Label for an interactive control that will reveal or hide a group of content. This control toggles between the text 'Expand view' and 'Collapse view'. */
|
|
1955
|
+
collapseView: 'Collapse view',
|
|
1956
|
+
/** Label indicating that Lighthouse throttled the page to emulate a slow 4G network connection. */
|
|
1957
|
+
runtimeSlow4g: 'Slow 4G throttling',
|
|
1958
|
+
/** Label indicating that Lighthouse throttled the page using custom throttling settings. */
|
|
1959
|
+
runtimeCustom: 'Custom throttling',
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1793
1962
|
/**
|
|
1794
1963
|
* @license
|
|
1795
1964
|
* Copyright 2017 The Lighthouse Authors. All Rights Reserved.
|
|
@@ -1824,10 +1993,10 @@ class CategoryRenderer {
|
|
|
1824
1993
|
*/
|
|
1825
1994
|
get _clumpTitles() {
|
|
1826
1995
|
return {
|
|
1827
|
-
warning:
|
|
1828
|
-
manual:
|
|
1829
|
-
passed:
|
|
1830
|
-
notApplicable:
|
|
1996
|
+
warning: Globals.strings.warningAuditsGroupTitle,
|
|
1997
|
+
manual: Globals.strings.manualAuditsGroupTitle,
|
|
1998
|
+
passed: Globals.strings.passedAuditsGroupTitle,
|
|
1999
|
+
notApplicable: Globals.strings.notApplicableAuditsGroupTitle,
|
|
1831
2000
|
};
|
|
1832
2001
|
}
|
|
1833
2002
|
|
|
@@ -1847,7 +2016,7 @@ class CategoryRenderer {
|
|
|
1847
2016
|
* @return {!Element}
|
|
1848
2017
|
*/
|
|
1849
2018
|
populateAuditValues(audit, component) {
|
|
1850
|
-
const strings =
|
|
2019
|
+
const strings = Globals.strings;
|
|
1851
2020
|
const auditEl = this.dom.find('.lh-audit', component);
|
|
1852
2021
|
auditEl.id = audit.result.id;
|
|
1853
2022
|
const scoreDisplayMode = audit.result.scoreDisplayMode;
|
|
@@ -1857,9 +2026,9 @@ class CategoryRenderer {
|
|
|
1857
2026
|
}
|
|
1858
2027
|
|
|
1859
2028
|
const titleEl = this.dom.find('.lh-audit__title', auditEl);
|
|
1860
|
-
titleEl.
|
|
2029
|
+
titleEl.append(this.dom.convertMarkdownCodeSnippets(audit.result.title));
|
|
1861
2030
|
const descEl = this.dom.find('.lh-audit__description', auditEl);
|
|
1862
|
-
descEl.
|
|
2031
|
+
descEl.append(this.dom.convertMarkdownLinkSnippets(audit.result.description));
|
|
1863
2032
|
|
|
1864
2033
|
for (const relevantMetric of audit.relevantMetrics || []) {
|
|
1865
2034
|
const adornEl = this.dom.createChildOf(descEl, 'span', 'lh-audit__adorn');
|
|
@@ -1869,19 +2038,16 @@ class CategoryRenderer {
|
|
|
1869
2038
|
|
|
1870
2039
|
if (audit.stackPacks) {
|
|
1871
2040
|
audit.stackPacks.forEach(pack => {
|
|
1872
|
-
const
|
|
1873
|
-
packElm.classList.add('lh-audit__stackpack');
|
|
1874
|
-
|
|
1875
|
-
const packElmImg = this.dom.createElement('img');
|
|
1876
|
-
packElmImg.classList.add('lh-audit__stackpack__img');
|
|
2041
|
+
const packElmImg = this.dom.createElement('img', 'lh-audit__stackpack__img');
|
|
1877
2042
|
packElmImg.src = pack.iconDataURL;
|
|
1878
2043
|
packElmImg.alt = pack.title;
|
|
1879
|
-
packElm.appendChild(packElmImg);
|
|
1880
2044
|
|
|
1881
|
-
|
|
2045
|
+
const snippets = this.dom.convertMarkdownLinkSnippets(pack.description);
|
|
2046
|
+
const packElm = this.dom.createElement('div', 'lh-audit__stackpack');
|
|
2047
|
+
packElm.append(packElmImg, snippets);
|
|
1882
2048
|
|
|
1883
2049
|
this.dom.find('.lh-audit__stackpacks', auditEl)
|
|
1884
|
-
.
|
|
2050
|
+
.append(packElm);
|
|
1885
2051
|
});
|
|
1886
2052
|
}
|
|
1887
2053
|
|
|
@@ -1890,12 +2056,12 @@ class CategoryRenderer {
|
|
|
1890
2056
|
const elem = this.detailsRenderer.render(audit.result.details);
|
|
1891
2057
|
if (elem) {
|
|
1892
2058
|
elem.classList.add('lh-details');
|
|
1893
|
-
header.
|
|
2059
|
+
header.append(elem);
|
|
1894
2060
|
}
|
|
1895
2061
|
}
|
|
1896
2062
|
|
|
1897
2063
|
// Add chevron SVG to the end of the summary
|
|
1898
|
-
this.dom.find('.lh-chevron-container', auditEl).
|
|
2064
|
+
this.dom.find('.lh-chevron-container', auditEl).append(this._createChevron());
|
|
1899
2065
|
this._setRatingClass(auditEl, audit.result.score, scoreDisplayMode);
|
|
1900
2066
|
|
|
1901
2067
|
if (audit.result.scoreDisplayMode === 'error') {
|
|
@@ -1917,7 +2083,7 @@ class CategoryRenderer {
|
|
|
1917
2083
|
const warningsEl = this.dom.createChildOf(summaryEl, 'div', 'lh-warnings');
|
|
1918
2084
|
this.dom.createChildOf(warningsEl, 'span').textContent = strings.warningHeader;
|
|
1919
2085
|
if (warnings.length === 1) {
|
|
1920
|
-
warningsEl.
|
|
2086
|
+
warningsEl.append(this.dom.createTextNode(warnings.join('')));
|
|
1921
2087
|
} else {
|
|
1922
2088
|
const warningsUl = this.dom.createChildOf(warningsEl, 'ul');
|
|
1923
2089
|
for (const warning of warnings) {
|
|
@@ -1973,7 +2139,7 @@ class CategoryRenderer {
|
|
|
1973
2139
|
* @return {!Element}
|
|
1974
2140
|
*/
|
|
1975
2141
|
_setRatingClass(element, score, scoreDisplayMode) {
|
|
1976
|
-
const rating =
|
|
2142
|
+
const rating = ReportUtils.calculateRating(score, scoreDisplayMode);
|
|
1977
2143
|
element.classList.add(`lh-audit--${scoreDisplayMode.toLowerCase()}`);
|
|
1978
2144
|
if (scoreDisplayMode !== 'informative') {
|
|
1979
2145
|
element.classList.add(`lh-audit--${rating}`);
|
|
@@ -1992,11 +2158,11 @@ class CategoryRenderer {
|
|
|
1992
2158
|
|
|
1993
2159
|
const gaugeContainerEl = this.dom.find('.lh-score__gauge', component);
|
|
1994
2160
|
const gaugeEl = this.renderCategoryScore(category, groupDefinitions, options);
|
|
1995
|
-
gaugeContainerEl.
|
|
2161
|
+
gaugeContainerEl.append(gaugeEl);
|
|
1996
2162
|
|
|
1997
2163
|
if (category.description) {
|
|
1998
2164
|
const descEl = this.dom.convertMarkdownLinkSnippets(category.description);
|
|
1999
|
-
this.dom.find('.lh-category-header__description', component).
|
|
2165
|
+
this.dom.find('.lh-category-header__description', component).append(descEl);
|
|
2000
2166
|
}
|
|
2001
2167
|
|
|
2002
2168
|
return component;
|
|
@@ -2015,13 +2181,13 @@ class CategoryRenderer {
|
|
|
2015
2181
|
|
|
2016
2182
|
this.dom.createChildOf(auditGroupHeader, 'span', 'lh-audit-group__title')
|
|
2017
2183
|
.textContent = group.title;
|
|
2018
|
-
groupEl.
|
|
2184
|
+
groupEl.append(auditGroupHeader);
|
|
2019
2185
|
|
|
2020
2186
|
let footerEl = null;
|
|
2021
2187
|
if (group.description) {
|
|
2022
2188
|
footerEl = this.dom.convertMarkdownLinkSnippets(group.description);
|
|
2023
2189
|
footerEl.classList.add('lh-audit-group__description', 'lh-audit-group__footer');
|
|
2024
|
-
groupEl.
|
|
2190
|
+
groupEl.append(footerEl);
|
|
2025
2191
|
}
|
|
2026
2192
|
|
|
2027
2193
|
return [groupEl, footerEl];
|
|
@@ -2085,7 +2251,7 @@ class CategoryRenderer {
|
|
|
2085
2251
|
renderUnexpandableClump(auditRefs, groupDefinitions) {
|
|
2086
2252
|
const clumpElement = this.dom.createElement('div');
|
|
2087
2253
|
const elements = this._renderGroupedAudits(auditRefs, groupDefinitions);
|
|
2088
|
-
elements.forEach(elem => clumpElement.
|
|
2254
|
+
elements.forEach(elem => clumpElement.append(elem));
|
|
2089
2255
|
return clumpElement;
|
|
2090
2256
|
}
|
|
2091
2257
|
|
|
@@ -2119,11 +2285,11 @@ class CategoryRenderer {
|
|
|
2119
2285
|
if (description) {
|
|
2120
2286
|
const descriptionEl = this.dom.convertMarkdownLinkSnippets(description);
|
|
2121
2287
|
descriptionEl.classList.add('lh-audit-group__description', 'lh-audit-group__footer');
|
|
2122
|
-
el.
|
|
2288
|
+
el.append(descriptionEl);
|
|
2123
2289
|
}
|
|
2124
2290
|
|
|
2125
|
-
this.dom.find('.lh-clump-toggletext--show', el).textContent =
|
|
2126
|
-
this.dom.find('.lh-clump-toggletext--hide', el).textContent =
|
|
2291
|
+
this.dom.find('.lh-clump-toggletext--show', el).textContent = Globals.strings.show;
|
|
2292
|
+
this.dom.find('.lh-clump-toggletext--hide', el).textContent = Globals.strings.hide;
|
|
2127
2293
|
|
|
2128
2294
|
clumpElement.classList.add(`lh-clump--${clumpId.toLowerCase()}`);
|
|
2129
2295
|
return el;
|
|
@@ -2137,7 +2303,7 @@ class CategoryRenderer {
|
|
|
2137
2303
|
*/
|
|
2138
2304
|
renderCategoryScore(category, groupDefinitions, options) {
|
|
2139
2305
|
let categoryScore;
|
|
2140
|
-
if (options &&
|
|
2306
|
+
if (options && ReportUtils.shouldDisplayAsFraction(options.gatherMode)) {
|
|
2141
2307
|
categoryScore = this.renderCategoryFraction(category);
|
|
2142
2308
|
} else {
|
|
2143
2309
|
categoryScore = this.renderScoreGauge(category, groupDefinitions);
|
|
@@ -2165,7 +2331,7 @@ class CategoryRenderer {
|
|
|
2165
2331
|
const tmpl = this.dom.createComponent('gauge');
|
|
2166
2332
|
const wrapper = this.dom.find('a.lh-gauge__wrapper', tmpl);
|
|
2167
2333
|
|
|
2168
|
-
if (
|
|
2334
|
+
if (ReportUtils.isPluginCategory(category.id)) {
|
|
2169
2335
|
wrapper.classList.add('lh-gauge__wrapper--plugin');
|
|
2170
2336
|
}
|
|
2171
2337
|
|
|
@@ -2181,16 +2347,16 @@ class CategoryRenderer {
|
|
|
2181
2347
|
percentageEl.textContent = scoreOutOf100.toString();
|
|
2182
2348
|
if (category.score === null) {
|
|
2183
2349
|
percentageEl.textContent = '?';
|
|
2184
|
-
percentageEl.title =
|
|
2350
|
+
percentageEl.title = Globals.strings.errorLabel;
|
|
2185
2351
|
}
|
|
2186
2352
|
|
|
2187
2353
|
// Render a numerical score if the category has applicable audits, or no audits whatsoever.
|
|
2188
2354
|
if (category.auditRefs.length === 0 || this.hasApplicableAudits(category)) {
|
|
2189
|
-
wrapper.classList.add(`lh-gauge__wrapper--${
|
|
2355
|
+
wrapper.classList.add(`lh-gauge__wrapper--${ReportUtils.calculateRating(category.score)}`);
|
|
2190
2356
|
} else {
|
|
2191
2357
|
wrapper.classList.add(`lh-gauge__wrapper--not-applicable`);
|
|
2192
2358
|
percentageEl.textContent = '-';
|
|
2193
|
-
percentageEl.title =
|
|
2359
|
+
percentageEl.title = Globals.strings.notApplicableAuditsGroupTitle;
|
|
2194
2360
|
}
|
|
2195
2361
|
|
|
2196
2362
|
this.dom.find('.lh-gauge__label', tmpl).textContent = category.title;
|
|
@@ -2205,15 +2371,16 @@ class CategoryRenderer {
|
|
|
2205
2371
|
const tmpl = this.dom.createComponent('fraction');
|
|
2206
2372
|
const wrapper = this.dom.find('a.lh-fraction__wrapper', tmpl);
|
|
2207
2373
|
|
|
2208
|
-
const {numPassed, numPassableAudits, totalWeight} =
|
|
2374
|
+
const {numPassed, numPassableAudits, totalWeight} =
|
|
2375
|
+
ReportUtils.calculateCategoryFraction(category);
|
|
2209
2376
|
|
|
2210
2377
|
const fraction = numPassed / numPassableAudits;
|
|
2211
2378
|
const content = this.dom.find('.lh-fraction__content', tmpl);
|
|
2212
2379
|
const text = this.dom.createElement('span');
|
|
2213
2380
|
text.textContent = `${numPassed}/${numPassableAudits}`;
|
|
2214
|
-
content.
|
|
2381
|
+
content.append(text);
|
|
2215
2382
|
|
|
2216
|
-
let rating =
|
|
2383
|
+
let rating = ReportUtils.calculateRating(fraction);
|
|
2217
2384
|
|
|
2218
2385
|
// If none of the available audits can affect the score, a rating isn't useful.
|
|
2219
2386
|
// The flow report should display the fraction with neutral icon and coloring in this case.
|
|
@@ -2278,7 +2445,7 @@ class CategoryRenderer {
|
|
|
2278
2445
|
return scoreDisplayMode;
|
|
2279
2446
|
}
|
|
2280
2447
|
|
|
2281
|
-
if (
|
|
2448
|
+
if (ReportUtils.showAsPassed(auditRef.result)) {
|
|
2282
2449
|
if (this._auditHasWarning(auditRef)) {
|
|
2283
2450
|
return 'warning';
|
|
2284
2451
|
} else {
|
|
@@ -2315,7 +2482,7 @@ class CategoryRenderer {
|
|
|
2315
2482
|
render(category, groupDefinitions = {}, options) {
|
|
2316
2483
|
const element = this.dom.createElement('div', 'lh-category');
|
|
2317
2484
|
element.id = category.id;
|
|
2318
|
-
element.
|
|
2485
|
+
element.append(this.renderCategoryHeader(category, groupDefinitions, options));
|
|
2319
2486
|
|
|
2320
2487
|
// Top level clumps for audits, in order they will appear in the report.
|
|
2321
2488
|
/** @type {Map<TopLevelClumpId, Array<LH.ReportResult.AuditRef>>} */
|
|
@@ -2348,13 +2515,13 @@ class CategoryRenderer {
|
|
|
2348
2515
|
if (clumpId === 'failed') {
|
|
2349
2516
|
const clumpElem = this.renderUnexpandableClump(auditRefs, groupDefinitions);
|
|
2350
2517
|
clumpElem.classList.add(`lh-clump--failed`);
|
|
2351
|
-
element.
|
|
2518
|
+
element.append(clumpElem);
|
|
2352
2519
|
continue;
|
|
2353
2520
|
}
|
|
2354
2521
|
|
|
2355
2522
|
const description = clumpId === 'manual' ? category.manualDescription : undefined;
|
|
2356
2523
|
const clumpElem = this.renderClump(clumpId, {auditRefs, description});
|
|
2357
|
-
element.
|
|
2524
|
+
element.append(clumpElem);
|
|
2358
2525
|
}
|
|
2359
2526
|
|
|
2360
2527
|
return element;
|
|
@@ -2461,44 +2628,43 @@ class CriticalRequestChainRenderer {
|
|
|
2461
2628
|
|
|
2462
2629
|
// Construct lines and add spacers for sub requests.
|
|
2463
2630
|
segment.treeMarkers.forEach(separator => {
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2631
|
+
const classSeparator = separator ?
|
|
2632
|
+
'lh-tree-marker lh-vert' :
|
|
2633
|
+
'lh-tree-marker';
|
|
2634
|
+
treeMarkeEl.append(
|
|
2635
|
+
dom.createElement('span', classSeparator),
|
|
2636
|
+
dom.createElement('span', 'lh-tree-marker')
|
|
2637
|
+
);
|
|
2471
2638
|
});
|
|
2472
2639
|
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
}
|
|
2640
|
+
const classLastChild = segment.isLastChild ?
|
|
2641
|
+
'lh-tree-marker lh-up-right' :
|
|
2642
|
+
'lh-tree-marker lh-vert-right';
|
|
2643
|
+
const classHasChildren = segment.hasChildren ?
|
|
2644
|
+
'lh-tree-marker lh-horiz-down' :
|
|
2645
|
+
'lh-tree-marker lh-right';
|
|
2646
|
+
|
|
2647
|
+
treeMarkeEl.append(
|
|
2648
|
+
dom.createElement('span', classLastChild),
|
|
2649
|
+
dom.createElement('span', 'lh-tree-marker lh-right'),
|
|
2650
|
+
dom.createElement('span', classHasChildren)
|
|
2651
|
+
);
|
|
2486
2652
|
|
|
2487
2653
|
// Fill in url, host, and request size information.
|
|
2488
2654
|
const url = segment.node.request.url;
|
|
2489
2655
|
const linkEl = detailsRenderer.renderTextURL(url);
|
|
2490
2656
|
const treevalEl = dom.find('.lh-crc-node__tree-value', chainEl);
|
|
2491
|
-
treevalEl.
|
|
2657
|
+
treevalEl.append(linkEl);
|
|
2492
2658
|
|
|
2493
2659
|
if (!segment.hasChildren) {
|
|
2494
2660
|
const {startTime, endTime, transferSize} = segment.node.request;
|
|
2495
2661
|
const span = dom.createElement('span', 'lh-crc-node__chain-duration');
|
|
2496
|
-
span.textContent =
|
|
2662
|
+
span.textContent =
|
|
2663
|
+
' - ' + Globals.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', ';
|
|
2497
2664
|
const span2 = dom.createElement('span', 'lh-crc-node__chain-duration');
|
|
2498
|
-
span2.textContent =
|
|
2665
|
+
span2.textContent = Globals.i18n.formatBytesToKiB(transferSize, 0.01);
|
|
2499
2666
|
|
|
2500
|
-
treevalEl.
|
|
2501
|
-
treevalEl.appendChild(span2);
|
|
2667
|
+
treevalEl.append(span, span2);
|
|
2502
2668
|
}
|
|
2503
2669
|
|
|
2504
2670
|
return chainEl;
|
|
@@ -2514,7 +2680,7 @@ class CriticalRequestChainRenderer {
|
|
|
2514
2680
|
* @param {DetailsRenderer} detailsRenderer
|
|
2515
2681
|
*/
|
|
2516
2682
|
static buildTree(dom, tmpl, segment, elem, details, detailsRenderer) {
|
|
2517
|
-
elem.
|
|
2683
|
+
elem.append(CRCRenderer.createChainNode(dom, segment, detailsRenderer));
|
|
2518
2684
|
if (segment.node.children) {
|
|
2519
2685
|
for (const key of Object.keys(segment.node.children)) {
|
|
2520
2686
|
const childSegment = CRCRenderer.createSegment(segment.node.children, key,
|
|
@@ -2535,11 +2701,11 @@ class CriticalRequestChainRenderer {
|
|
|
2535
2701
|
const containerEl = dom.find('.lh-crc', tmpl);
|
|
2536
2702
|
|
|
2537
2703
|
// Fill in top summary.
|
|
2538
|
-
dom.find('.lh-crc-initial-nav', tmpl).textContent =
|
|
2704
|
+
dom.find('.lh-crc-initial-nav', tmpl).textContent = Globals.strings.crcInitialNavigation;
|
|
2539
2705
|
dom.find('.lh-crc__longest_duration_label', tmpl).textContent =
|
|
2540
|
-
|
|
2706
|
+
Globals.strings.crcLongestDurationLabel;
|
|
2541
2707
|
dom.find('.lh-crc__longest_duration', tmpl).textContent =
|
|
2542
|
-
|
|
2708
|
+
Globals.i18n.formatMilliseconds(details.longestChain.duration);
|
|
2543
2709
|
|
|
2544
2710
|
// Construct visual tree.
|
|
2545
2711
|
const root = CRCRenderer.initTree(details.chains);
|
|
@@ -2561,8 +2727,20 @@ const CRCRenderer = CriticalRequestChainRenderer;
|
|
|
2561
2727
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
2562
2728
|
*/
|
|
2563
2729
|
|
|
2730
|
+
/** @typedef {import('./dom.js').DOM} DOM */
|
|
2731
|
+
/** @typedef {LH.Audit.Details.Rect} Rect */
|
|
2732
|
+
/** @typedef {{width: number, height: number}} Size */
|
|
2733
|
+
|
|
2734
|
+
/**
|
|
2735
|
+
* @typedef InstallOverlayFeatureParams
|
|
2736
|
+
* @property {DOM} dom
|
|
2737
|
+
* @property {Element} rootEl
|
|
2738
|
+
* @property {Element} overlayContainerEl
|
|
2739
|
+
* @property {LH.Result.FullPageScreenshot} fullPageScreenshot
|
|
2740
|
+
*/
|
|
2741
|
+
|
|
2564
2742
|
/**
|
|
2565
|
-
* @param {LH.
|
|
2743
|
+
* @param {LH.Result.FullPageScreenshot['screenshot']} screenshot
|
|
2566
2744
|
* @param {LH.Audit.Details.Rect} rect
|
|
2567
2745
|
* @return {boolean}
|
|
2568
2746
|
*/
|
|
@@ -2639,7 +2817,7 @@ class ElementScreenshotRenderer {
|
|
|
2639
2817
|
*/
|
|
2640
2818
|
static renderClipPathInScreenshot(dom, maskEl, positionClip, elementRect, elementPreviewSize) {
|
|
2641
2819
|
const clipPathEl = dom.find('clipPath', maskEl);
|
|
2642
|
-
const clipId = `clip-${
|
|
2820
|
+
const clipId = `clip-${Globals.getUniqueSuffix()}`;
|
|
2643
2821
|
clipPathEl.id = clipId;
|
|
2644
2822
|
maskEl.style.clipPath = `url(#${clipId})`;
|
|
2645
2823
|
|
|
@@ -2668,7 +2846,7 @@ class ElementScreenshotRenderer {
|
|
|
2668
2846
|
* Allows for multiple Lighthouse reports to be rendered on the page, each with their
|
|
2669
2847
|
* own full page screenshot.
|
|
2670
2848
|
* @param {HTMLElement} el
|
|
2671
|
-
* @param {LH.
|
|
2849
|
+
* @param {LH.Result.FullPageScreenshot['screenshot']} screenshot
|
|
2672
2850
|
*/
|
|
2673
2851
|
static installFullPageScreenshot(el, screenshot) {
|
|
2674
2852
|
el.style.setProperty('--element-screenshot-url', `url('${screenshot.data}')`);
|
|
@@ -2723,7 +2901,7 @@ class ElementScreenshotRenderer {
|
|
|
2723
2901
|
overlay.remove();
|
|
2724
2902
|
return;
|
|
2725
2903
|
}
|
|
2726
|
-
overlay.
|
|
2904
|
+
overlay.append(screenshotElement);
|
|
2727
2905
|
overlay.addEventListener('click', () => overlay.remove());
|
|
2728
2906
|
});
|
|
2729
2907
|
}
|
|
@@ -2750,7 +2928,7 @@ class ElementScreenshotRenderer {
|
|
|
2750
2928
|
* Used to render both the thumbnail preview in details tables and the full-page screenshot in the lightbox.
|
|
2751
2929
|
* Returns null if element rect is outside screenshot bounds.
|
|
2752
2930
|
* @param {DOM} dom
|
|
2753
|
-
* @param {LH.
|
|
2931
|
+
* @param {LH.Result.FullPageScreenshot['screenshot']} screenshot
|
|
2754
2932
|
* @param {Rect} elementRectSC Region of screenshot to highlight.
|
|
2755
2933
|
* @param {Size} maxRenderSizeDC e.g. maxThumbnailSize or maxLightboxSize.
|
|
2756
2934
|
* @return {Element|null}
|
|
@@ -2776,7 +2954,10 @@ class ElementScreenshotRenderer {
|
|
|
2776
2954
|
width: maxRenderSizeDC.width / zoomFactor,
|
|
2777
2955
|
height: maxRenderSizeDC.height / zoomFactor,
|
|
2778
2956
|
};
|
|
2957
|
+
|
|
2779
2958
|
elementPreviewSizeSC.width = Math.min(screenshot.width, elementPreviewSizeSC.width);
|
|
2959
|
+
elementPreviewSizeSC.height = Math.min(screenshot.height, elementPreviewSizeSC.height);
|
|
2960
|
+
|
|
2780
2961
|
/* This preview size is either the size of the thumbnail or size of the Lightbox */
|
|
2781
2962
|
const elementPreviewSizeDC = {
|
|
2782
2963
|
width: elementPreviewSizeSC.width * zoomFactor,
|
|
@@ -2789,9 +2970,6 @@ class ElementScreenshotRenderer {
|
|
|
2789
2970
|
{width: screenshot.width, height: screenshot.height}
|
|
2790
2971
|
);
|
|
2791
2972
|
|
|
2792
|
-
const contentEl = dom.find('div.lh-element-screenshot__content', containerEl);
|
|
2793
|
-
contentEl.style.top = `-${elementPreviewSizeDC.height}px`;
|
|
2794
|
-
|
|
2795
2973
|
const imageEl = dom.find('div.lh-element-screenshot__image', containerEl);
|
|
2796
2974
|
imageEl.style.width = elementPreviewSizeDC.width + 'px';
|
|
2797
2975
|
imageEl.style.height = elementPreviewSizeDC.height + 'px';
|
|
@@ -2845,7 +3023,7 @@ const URL_PREFIXES = ['http://', 'https://', 'data:'];
|
|
|
2845
3023
|
class DetailsRenderer {
|
|
2846
3024
|
/**
|
|
2847
3025
|
* @param {DOM} dom
|
|
2848
|
-
* @param {{fullPageScreenshot?: LH.
|
|
3026
|
+
* @param {{fullPageScreenshot?: LH.Result.FullPageScreenshot}} [options]
|
|
2849
3027
|
*/
|
|
2850
3028
|
constructor(dom, options = {}) {
|
|
2851
3029
|
this._dom = dom;
|
|
@@ -2863,22 +3041,20 @@ class DetailsRenderer {
|
|
|
2863
3041
|
case 'list':
|
|
2864
3042
|
return this._renderList(details);
|
|
2865
3043
|
case 'table':
|
|
3044
|
+
case 'opportunity':
|
|
2866
3045
|
return this._renderTable(details);
|
|
2867
3046
|
case 'criticalrequestchain':
|
|
2868
3047
|
return CriticalRequestChainRenderer.render(this._dom, details, this);
|
|
2869
|
-
case 'opportunity':
|
|
2870
|
-
return this._renderTable(details);
|
|
2871
3048
|
|
|
2872
3049
|
// Internal-only details, not for rendering.
|
|
2873
3050
|
case 'screenshot':
|
|
2874
3051
|
case 'debugdata':
|
|
2875
|
-
case 'full-page-screenshot':
|
|
2876
3052
|
case 'treemap-data':
|
|
2877
3053
|
return null;
|
|
2878
3054
|
|
|
2879
3055
|
default: {
|
|
2880
|
-
// @ts-expect-error tsc thinks this is unreachable
|
|
2881
|
-
// with new unexpected detail types.
|
|
3056
|
+
// @ts-expect-error - all detail types need to be handled above so tsc thinks this is unreachable.
|
|
3057
|
+
// Call _renderUnknown() to be forward compatible with new, unexpected detail types.
|
|
2882
3058
|
return this._renderUnknown(details.type, details);
|
|
2883
3059
|
}
|
|
2884
3060
|
}
|
|
@@ -2889,11 +3065,10 @@ class DetailsRenderer {
|
|
|
2889
3065
|
* @return {Element}
|
|
2890
3066
|
*/
|
|
2891
3067
|
_renderBytes(details) {
|
|
2892
|
-
// TODO: handle displayUnit once we have something other than '
|
|
2893
|
-
|
|
2894
|
-
const value = Util.i18n.formatBytesToKiB(details.value, details.granularity);
|
|
3068
|
+
// TODO: handle displayUnit once we have something other than 'KiB'
|
|
3069
|
+
const value = Globals.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
|
|
2895
3070
|
const textEl = this._renderText(value);
|
|
2896
|
-
textEl.title =
|
|
3071
|
+
textEl.title = Globals.i18n.formatBytes(details.value);
|
|
2897
3072
|
return textEl;
|
|
2898
3073
|
}
|
|
2899
3074
|
|
|
@@ -2902,9 +3077,11 @@ class DetailsRenderer {
|
|
|
2902
3077
|
* @return {Element}
|
|
2903
3078
|
*/
|
|
2904
3079
|
_renderMilliseconds(details) {
|
|
2905
|
-
let value
|
|
3080
|
+
let value;
|
|
2906
3081
|
if (details.displayUnit === 'duration') {
|
|
2907
|
-
value =
|
|
3082
|
+
value = Globals.i18n.formatDuration(details.value);
|
|
3083
|
+
} else {
|
|
3084
|
+
value = Globals.i18n.formatMilliseconds(details.value, details.granularity || 10);
|
|
2908
3085
|
}
|
|
2909
3086
|
|
|
2910
3087
|
return this._renderText(value);
|
|
@@ -2930,12 +3107,12 @@ class DetailsRenderer {
|
|
|
2930
3107
|
}
|
|
2931
3108
|
|
|
2932
3109
|
const element = this._dom.createElement('div', 'lh-text__url');
|
|
2933
|
-
element.
|
|
3110
|
+
element.append(this._renderLink({text: displayedPath, url}));
|
|
2934
3111
|
|
|
2935
3112
|
if (displayedHost) {
|
|
2936
3113
|
const hostElem = this._renderText(displayedHost);
|
|
2937
3114
|
hostElem.classList.add('lh-text__url-host');
|
|
2938
|
-
element.
|
|
3115
|
+
element.append(hostElem);
|
|
2939
3116
|
}
|
|
2940
3117
|
|
|
2941
3118
|
if (title) {
|
|
@@ -2983,7 +3160,7 @@ class DetailsRenderer {
|
|
|
2983
3160
|
* @return {Element}
|
|
2984
3161
|
*/
|
|
2985
3162
|
_renderNumeric(details) {
|
|
2986
|
-
const value =
|
|
3163
|
+
const value = Globals.i18n.formatNumber(details.value, details.granularity || 0.1);
|
|
2987
3164
|
const element = this._dom.createElement('div', 'lh-numeric');
|
|
2988
3165
|
element.textContent = value;
|
|
2989
3166
|
return element;
|
|
@@ -3024,7 +3201,7 @@ class DetailsRenderer {
|
|
|
3024
3201
|
* based on the heading's valueType, unless the value itself has a `type`
|
|
3025
3202
|
* property to override it.
|
|
3026
3203
|
* @param {TableItemValue} value
|
|
3027
|
-
* @param {LH.Audit.Details.
|
|
3204
|
+
* @param {LH.Audit.Details.TableColumnHeading} heading
|
|
3028
3205
|
* @return {Element|null}
|
|
3029
3206
|
*/
|
|
3030
3207
|
_renderTableValue(value, heading) {
|
|
@@ -3109,73 +3286,14 @@ class DetailsRenderer {
|
|
|
3109
3286
|
}
|
|
3110
3287
|
}
|
|
3111
3288
|
|
|
3112
|
-
/**
|
|
3113
|
-
* Get the headings of a table-like details object, converted into the
|
|
3114
|
-
* OpportunityColumnHeading type until we have all details use the same
|
|
3115
|
-
* heading format.
|
|
3116
|
-
* @param {Table|OpportunityTable} tableLike
|
|
3117
|
-
* @return {OpportunityTable['headings']}
|
|
3118
|
-
*/
|
|
3119
|
-
_getCanonicalizedHeadingsFromTable(tableLike) {
|
|
3120
|
-
if (tableLike.type === 'opportunity') {
|
|
3121
|
-
return tableLike.headings;
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
return tableLike.headings.map(heading => this._getCanonicalizedHeading(heading));
|
|
3125
|
-
}
|
|
3126
|
-
|
|
3127
|
-
/**
|
|
3128
|
-
* Get the headings of a table-like details object, converted into the
|
|
3129
|
-
* OpportunityColumnHeading type until we have all details use the same
|
|
3130
|
-
* heading format.
|
|
3131
|
-
* @param {Table['headings'][number]} heading
|
|
3132
|
-
* @return {OpportunityTable['headings'][number]}
|
|
3133
|
-
*/
|
|
3134
|
-
_getCanonicalizedHeading(heading) {
|
|
3135
|
-
let subItemsHeading;
|
|
3136
|
-
if (heading.subItemsHeading) {
|
|
3137
|
-
subItemsHeading = this._getCanonicalizedsubItemsHeading(heading.subItemsHeading, heading);
|
|
3138
|
-
}
|
|
3139
|
-
|
|
3140
|
-
return {
|
|
3141
|
-
key: heading.key,
|
|
3142
|
-
valueType: heading.itemType,
|
|
3143
|
-
subItemsHeading,
|
|
3144
|
-
label: heading.text,
|
|
3145
|
-
displayUnit: heading.displayUnit,
|
|
3146
|
-
granularity: heading.granularity,
|
|
3147
|
-
};
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
/**
|
|
3151
|
-
* @param {Exclude<LH.Audit.Details.TableColumnHeading['subItemsHeading'], undefined>} subItemsHeading
|
|
3152
|
-
* @param {LH.Audit.Details.TableColumnHeading} parentHeading
|
|
3153
|
-
* @return {LH.Audit.Details.OpportunityColumnHeading['subItemsHeading']}
|
|
3154
|
-
*/
|
|
3155
|
-
_getCanonicalizedsubItemsHeading(subItemsHeading, parentHeading) {
|
|
3156
|
-
// Low-friction way to prevent committing a falsy key (which is never allowed for
|
|
3157
|
-
// a subItemsHeading) from passing in CI.
|
|
3158
|
-
if (!subItemsHeading.key) {
|
|
3159
|
-
// eslint-disable-next-line no-console
|
|
3160
|
-
console.warn('key should not be null');
|
|
3161
|
-
}
|
|
3162
|
-
|
|
3163
|
-
return {
|
|
3164
|
-
key: subItemsHeading.key || '',
|
|
3165
|
-
valueType: subItemsHeading.itemType || parentHeading.itemType,
|
|
3166
|
-
granularity: subItemsHeading.granularity || parentHeading.granularity,
|
|
3167
|
-
displayUnit: subItemsHeading.displayUnit || parentHeading.displayUnit,
|
|
3168
|
-
};
|
|
3169
|
-
}
|
|
3170
|
-
|
|
3171
3289
|
/**
|
|
3172
3290
|
* Returns a new heading where the values are defined first by `heading.subItemsHeading`,
|
|
3173
3291
|
* and secondly by `heading`. If there is no subItemsHeading, returns null, which will
|
|
3174
3292
|
* be rendered as an empty column.
|
|
3175
|
-
* @param {LH.Audit.Details.
|
|
3176
|
-
* @return {LH.Audit.Details.
|
|
3293
|
+
* @param {LH.Audit.Details.TableColumnHeading} heading
|
|
3294
|
+
* @return {LH.Audit.Details.TableColumnHeading | null}
|
|
3177
3295
|
*/
|
|
3178
|
-
|
|
3296
|
+
_getDerivedSubItemsHeading(heading) {
|
|
3179
3297
|
if (!heading.subItemsHeading) return null;
|
|
3180
3298
|
return {
|
|
3181
3299
|
key: heading.subItemsHeading.key || '',
|
|
@@ -3188,7 +3306,7 @@ class DetailsRenderer {
|
|
|
3188
3306
|
|
|
3189
3307
|
/**
|
|
3190
3308
|
* @param {TableItem} item
|
|
3191
|
-
* @param {(LH.Audit.Details.
|
|
3309
|
+
* @param {(LH.Audit.Details.TableColumnHeading | null)[]} headings
|
|
3192
3310
|
*/
|
|
3193
3311
|
_renderTableRow(item, headings) {
|
|
3194
3312
|
const rowElem = this._dom.createElement('tr');
|
|
@@ -3208,7 +3326,7 @@ class DetailsRenderer {
|
|
|
3208
3326
|
|
|
3209
3327
|
if (valueElement) {
|
|
3210
3328
|
const classes = `lh-table-column--${heading.valueType}`;
|
|
3211
|
-
this._dom.createChildOf(rowElem, 'td', classes).
|
|
3329
|
+
this._dom.createChildOf(rowElem, 'td', classes).append(valueElement);
|
|
3212
3330
|
} else {
|
|
3213
3331
|
// Empty cell is rendered for a column if:
|
|
3214
3332
|
// - the pair is null
|
|
@@ -3225,7 +3343,7 @@ class DetailsRenderer {
|
|
|
3225
3343
|
* Renders one or more rows from a details table item. A single table item can
|
|
3226
3344
|
* expand into multiple rows, if there is a subItemsHeading.
|
|
3227
3345
|
* @param {TableItem} item
|
|
3228
|
-
* @param {LH.Audit.Details.
|
|
3346
|
+
* @param {LH.Audit.Details.TableColumnHeading[]} headings
|
|
3229
3347
|
*/
|
|
3230
3348
|
_renderTableRowsFromItem(item, headings) {
|
|
3231
3349
|
const fragment = this._dom.createFragment();
|
|
@@ -3233,7 +3351,7 @@ class DetailsRenderer {
|
|
|
3233
3351
|
|
|
3234
3352
|
if (!item.subItems) return fragment;
|
|
3235
3353
|
|
|
3236
|
-
const subItemsHeadings = headings.map(this.
|
|
3354
|
+
const subItemsHeadings = headings.map(this._getDerivedSubItemsHeading);
|
|
3237
3355
|
if (!subItemsHeadings.some(Boolean)) return fragment;
|
|
3238
3356
|
|
|
3239
3357
|
for (const subItem of item.subItems.items) {
|
|
@@ -3246,7 +3364,7 @@ class DetailsRenderer {
|
|
|
3246
3364
|
}
|
|
3247
3365
|
|
|
3248
3366
|
/**
|
|
3249
|
-
* @param {
|
|
3367
|
+
* @param {{headings: TableColumnHeading[], items: TableItem[]}} details
|
|
3250
3368
|
* @return {Element}
|
|
3251
3369
|
*/
|
|
3252
3370
|
_renderTable(details) {
|
|
@@ -3256,23 +3374,34 @@ class DetailsRenderer {
|
|
|
3256
3374
|
const theadElem = this._dom.createChildOf(tableElem, 'thead');
|
|
3257
3375
|
const theadTrElem = this._dom.createChildOf(theadElem, 'tr');
|
|
3258
3376
|
|
|
3259
|
-
const
|
|
3260
|
-
|
|
3261
|
-
for (const heading of headings) {
|
|
3377
|
+
for (const heading of details.headings) {
|
|
3262
3378
|
const valueType = heading.valueType || 'text';
|
|
3263
3379
|
const classes = `lh-table-column--${valueType}`;
|
|
3264
3380
|
const labelEl = this._dom.createElement('div', 'lh-text');
|
|
3265
3381
|
labelEl.textContent = heading.label;
|
|
3266
|
-
this._dom.createChildOf(theadTrElem, 'th', classes).
|
|
3382
|
+
this._dom.createChildOf(theadTrElem, 'th', classes).append(labelEl);
|
|
3267
3383
|
}
|
|
3268
3384
|
|
|
3269
3385
|
const tbodyElem = this._dom.createChildOf(tableElem, 'tbody');
|
|
3270
3386
|
let even = true;
|
|
3271
3387
|
for (const item of details.items) {
|
|
3272
|
-
const rowsFragment = this._renderTableRowsFromItem(item, headings);
|
|
3388
|
+
const rowsFragment = this._renderTableRowsFromItem(item, details.headings);
|
|
3389
|
+
|
|
3390
|
+
// The attribute item.entity could be a string (entity-classification), or
|
|
3391
|
+
// a LinkValue for ThirdPartySummary audit.
|
|
3392
|
+
let entityName;
|
|
3393
|
+
if (typeof item.entity === 'object' && item.entity.type === 'link') {
|
|
3394
|
+
entityName = item.entity.text;
|
|
3395
|
+
} else if (typeof item.entity === 'string') {
|
|
3396
|
+
entityName = item.entity;
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3273
3399
|
for (const rowEl of this._dom.findAll('tr', rowsFragment)) {
|
|
3274
3400
|
// For zebra styling.
|
|
3275
3401
|
rowEl.classList.add(even ? 'lh-row--even' : 'lh-row--odd');
|
|
3402
|
+
if (entityName && !rowEl.classList.contains('lh-sub-item-row')) {
|
|
3403
|
+
rowEl.dataset.entity = entityName;
|
|
3404
|
+
}
|
|
3276
3405
|
}
|
|
3277
3406
|
even = !even;
|
|
3278
3407
|
tbodyElem.append(rowsFragment);
|
|
@@ -3306,13 +3435,13 @@ class DetailsRenderer {
|
|
|
3306
3435
|
if (item.nodeLabel) {
|
|
3307
3436
|
const nodeLabelEl = this._dom.createElement('div');
|
|
3308
3437
|
nodeLabelEl.textContent = item.nodeLabel;
|
|
3309
|
-
element.
|
|
3438
|
+
element.append(nodeLabelEl);
|
|
3310
3439
|
}
|
|
3311
3440
|
if (item.snippet) {
|
|
3312
3441
|
const snippetEl = this._dom.createElement('div');
|
|
3313
3442
|
snippetEl.classList.add('lh-node__snippet');
|
|
3314
3443
|
snippetEl.textContent = item.snippet;
|
|
3315
|
-
element.
|
|
3444
|
+
element.append(snippetEl);
|
|
3316
3445
|
}
|
|
3317
3446
|
if (item.selector) {
|
|
3318
3447
|
element.title = item.selector;
|
|
@@ -3423,37 +3552,87 @@ const NBSP2 = '\xa0';
|
|
|
3423
3552
|
const KiB = 1024;
|
|
3424
3553
|
const MiB = KiB * KiB;
|
|
3425
3554
|
|
|
3426
|
-
|
|
3427
|
-
* @template T
|
|
3428
|
-
*/
|
|
3429
|
-
class I18n {
|
|
3555
|
+
class I18nFormatter {
|
|
3430
3556
|
/**
|
|
3431
3557
|
* @param {LH.Locale} locale
|
|
3432
|
-
* @param {T} strings
|
|
3433
3558
|
*/
|
|
3434
|
-
constructor(locale
|
|
3559
|
+
constructor(locale) {
|
|
3435
3560
|
// When testing, use a locale with more exciting numeric formatting.
|
|
3436
3561
|
if (locale === 'en-XA') locale = 'de';
|
|
3437
3562
|
|
|
3438
|
-
this.
|
|
3439
|
-
this.
|
|
3440
|
-
this._percentFormatter = new Intl.NumberFormat(locale, {style: 'percent'});
|
|
3441
|
-
this._strings = strings;
|
|
3563
|
+
this._locale = locale;
|
|
3564
|
+
this._cachedNumberFormatters = new Map();
|
|
3442
3565
|
}
|
|
3443
3566
|
|
|
3444
|
-
|
|
3445
|
-
|
|
3567
|
+
/**
|
|
3568
|
+
* @param {number} number
|
|
3569
|
+
* @param {number|undefined} granularity
|
|
3570
|
+
* @param {Intl.NumberFormatOptions=} opts
|
|
3571
|
+
* @return {string}
|
|
3572
|
+
*/
|
|
3573
|
+
_formatNumberWithGranularity(number, granularity, opts = {}) {
|
|
3574
|
+
if (granularity !== undefined) {
|
|
3575
|
+
const log10 = -Math.log10(granularity);
|
|
3576
|
+
if (!Number.isInteger(log10)) {
|
|
3577
|
+
console.warn(`granularity of ${granularity} is invalid. Using 1 instead`);
|
|
3578
|
+
granularity = 1;
|
|
3579
|
+
}
|
|
3580
|
+
|
|
3581
|
+
if (granularity < 1) {
|
|
3582
|
+
opts = {...opts};
|
|
3583
|
+
opts.minimumFractionDigits = opts.maximumFractionDigits = Math.ceil(log10);
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
number = Math.round(number / granularity) * granularity;
|
|
3587
|
+
|
|
3588
|
+
// Avoid displaying a negative value that rounds to zero as "0".
|
|
3589
|
+
if (Object.is(number, -0)) number = 0;
|
|
3590
|
+
} else if (Math.abs(number) < 0.0005) {
|
|
3591
|
+
// Also avoids "-0".
|
|
3592
|
+
number = 0;
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
let formatter;
|
|
3596
|
+
// eslint-disable-next-line max-len
|
|
3597
|
+
const cacheKey = [
|
|
3598
|
+
opts.minimumFractionDigits,
|
|
3599
|
+
opts.maximumFractionDigits,
|
|
3600
|
+
opts.style,
|
|
3601
|
+
opts.unit,
|
|
3602
|
+
opts.unitDisplay,
|
|
3603
|
+
this._locale,
|
|
3604
|
+
].join('');
|
|
3605
|
+
|
|
3606
|
+
formatter = this._cachedNumberFormatters.get(cacheKey);
|
|
3607
|
+
if (!formatter) {
|
|
3608
|
+
formatter = new Intl.NumberFormat(this._locale, opts);
|
|
3609
|
+
this._cachedNumberFormatters.set(cacheKey, formatter);
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
return formatter.format(number).replace(' ', NBSP2);
|
|
3446
3613
|
}
|
|
3447
3614
|
|
|
3448
3615
|
/**
|
|
3449
3616
|
* Format number.
|
|
3450
3617
|
* @param {number} number
|
|
3451
|
-
* @param {number=} granularity
|
|
3618
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3619
|
+
* If undefined, the number will be displayed as described
|
|
3620
|
+
* by the Intl defaults: tinyurl.com/7s67w5x7
|
|
3621
|
+
* @return {string}
|
|
3622
|
+
*/
|
|
3623
|
+
formatNumber(number, granularity) {
|
|
3624
|
+
return this._formatNumberWithGranularity(number, granularity);
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
/**
|
|
3628
|
+
* Format integer.
|
|
3629
|
+
* Just like {@link formatNumber} but uses a granularity of 1, rounding to the nearest
|
|
3630
|
+
* whole number.
|
|
3631
|
+
* @param {number} number
|
|
3452
3632
|
* @return {string}
|
|
3453
3633
|
*/
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
return this._numberFormatter.format(coarseValue);
|
|
3634
|
+
formatInteger(number) {
|
|
3635
|
+
return this._formatNumberWithGranularity(number, 1);
|
|
3457
3636
|
}
|
|
3458
3637
|
|
|
3459
3638
|
/**
|
|
@@ -3462,92 +3641,99 @@ class I18n {
|
|
|
3462
3641
|
* @return {string}
|
|
3463
3642
|
*/
|
|
3464
3643
|
formatPercent(number) {
|
|
3465
|
-
return this.
|
|
3644
|
+
return new Intl.NumberFormat(this._locale, {style: 'percent'}).format(number);
|
|
3466
3645
|
}
|
|
3467
3646
|
|
|
3468
3647
|
/**
|
|
3469
3648
|
* @param {number} size
|
|
3470
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3649
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3650
|
+
* If undefined, the number will be displayed in full.
|
|
3471
3651
|
* @return {string}
|
|
3472
3652
|
*/
|
|
3473
|
-
formatBytesToKiB(size, granularity =
|
|
3474
|
-
|
|
3475
|
-
const kbs = formatter.format(Math.round(size / 1024 / granularity) * granularity);
|
|
3476
|
-
return `${kbs}${NBSP2}KiB`;
|
|
3653
|
+
formatBytesToKiB(size, granularity = undefined) {
|
|
3654
|
+
return this._formatNumberWithGranularity(size / KiB, granularity) + `${NBSP2}KiB`;
|
|
3477
3655
|
}
|
|
3478
3656
|
|
|
3479
3657
|
/**
|
|
3480
3658
|
* @param {number} size
|
|
3481
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3659
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3660
|
+
* If undefined, the number will be displayed in full.
|
|
3482
3661
|
* @return {string}
|
|
3483
3662
|
*/
|
|
3484
|
-
formatBytesToMiB(size, granularity =
|
|
3485
|
-
|
|
3486
|
-
const kbs = formatter.format(Math.round(size / (1024 ** 2) / granularity) * granularity);
|
|
3487
|
-
return `${kbs}${NBSP2}MiB`;
|
|
3663
|
+
formatBytesToMiB(size, granularity = undefined) {
|
|
3664
|
+
return this._formatNumberWithGranularity(size / MiB, granularity) + `${NBSP2}MiB`;
|
|
3488
3665
|
}
|
|
3489
3666
|
|
|
3490
3667
|
/**
|
|
3491
3668
|
* @param {number} size
|
|
3492
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3669
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3670
|
+
* If undefined, the number will be displayed in full.
|
|
3493
3671
|
* @return {string}
|
|
3494
3672
|
*/
|
|
3495
3673
|
formatBytes(size, granularity = 1) {
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3674
|
+
return this._formatNumberWithGranularity(size, granularity, {
|
|
3675
|
+
style: 'unit',
|
|
3676
|
+
unit: 'byte',
|
|
3677
|
+
unitDisplay: 'long',
|
|
3678
|
+
});
|
|
3499
3679
|
}
|
|
3500
3680
|
|
|
3501
3681
|
/**
|
|
3502
3682
|
* @param {number} size
|
|
3503
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3683
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3684
|
+
* If undefined, the number will be displayed in full.
|
|
3504
3685
|
* @return {string}
|
|
3505
3686
|
*/
|
|
3506
|
-
formatBytesWithBestUnit(size, granularity =
|
|
3687
|
+
formatBytesWithBestUnit(size, granularity = undefined) {
|
|
3507
3688
|
if (size >= MiB) return this.formatBytesToMiB(size, granularity);
|
|
3508
3689
|
if (size >= KiB) return this.formatBytesToKiB(size, granularity);
|
|
3509
|
-
return this.
|
|
3690
|
+
return this._formatNumberWithGranularity(size, granularity, {
|
|
3691
|
+
style: 'unit',
|
|
3692
|
+
unit: 'byte',
|
|
3693
|
+
unitDisplay: 'narrow',
|
|
3694
|
+
});
|
|
3510
3695
|
}
|
|
3511
3696
|
|
|
3512
3697
|
/**
|
|
3513
|
-
*
|
|
3514
|
-
* @param {number} granularity Controls how coarse the displayed value is
|
|
3515
|
-
*
|
|
3698
|
+
* @param {number} size
|
|
3699
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3700
|
+
* If undefined, the number will be displayed in full.
|
|
3701
|
+
* @return {string}
|
|
3516
3702
|
*/
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
return new Intl.NumberFormat(this._numberDateLocale, {
|
|
3525
|
-
...this._numberFormatter.resolvedOptions(),
|
|
3526
|
-
maximumFractionDigits: numberOfFractionDigits,
|
|
3527
|
-
minimumFractionDigits: numberOfFractionDigits,
|
|
3703
|
+
formatKbps(size, granularity = undefined) {
|
|
3704
|
+
return this._formatNumberWithGranularity(size, granularity, {
|
|
3705
|
+
style: 'unit',
|
|
3706
|
+
unit: 'kilobit-per-second',
|
|
3707
|
+
unitDisplay: 'short',
|
|
3528
3708
|
});
|
|
3529
3709
|
}
|
|
3530
3710
|
|
|
3531
3711
|
/**
|
|
3532
3712
|
* @param {number} ms
|
|
3533
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3713
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3714
|
+
* If undefined, the number will be displayed in full.
|
|
3534
3715
|
* @return {string}
|
|
3535
3716
|
*/
|
|
3536
|
-
formatMilliseconds(ms, granularity =
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
:
|
|
3717
|
+
formatMilliseconds(ms, granularity = undefined) {
|
|
3718
|
+
return this._formatNumberWithGranularity(ms, granularity, {
|
|
3719
|
+
style: 'unit',
|
|
3720
|
+
unit: 'millisecond',
|
|
3721
|
+
unitDisplay: 'short',
|
|
3722
|
+
});
|
|
3541
3723
|
}
|
|
3542
3724
|
|
|
3543
3725
|
/**
|
|
3544
3726
|
* @param {number} ms
|
|
3545
|
-
* @param {number=} granularity Controls how coarse the displayed value is
|
|
3727
|
+
* @param {number=} granularity Controls how coarse the displayed value is.
|
|
3728
|
+
* If undefined, the number will be displayed in full.
|
|
3546
3729
|
* @return {string}
|
|
3547
3730
|
*/
|
|
3548
|
-
formatSeconds(ms, granularity =
|
|
3549
|
-
|
|
3550
|
-
|
|
3731
|
+
formatSeconds(ms, granularity = undefined) {
|
|
3732
|
+
return this._formatNumberWithGranularity(ms / 1000, granularity, {
|
|
3733
|
+
style: 'unit',
|
|
3734
|
+
unit: 'second',
|
|
3735
|
+
unitDisplay: 'narrow',
|
|
3736
|
+
});
|
|
3551
3737
|
}
|
|
3552
3738
|
|
|
3553
3739
|
/**
|
|
@@ -3567,10 +3753,10 @@ class I18n {
|
|
|
3567
3753
|
// and https://github.com/GoogleChrome/lighthouse/pull/9822
|
|
3568
3754
|
let formatter;
|
|
3569
3755
|
try {
|
|
3570
|
-
formatter = new Intl.DateTimeFormat(this.
|
|
3756
|
+
formatter = new Intl.DateTimeFormat(this._locale, options);
|
|
3571
3757
|
} catch (err) {
|
|
3572
3758
|
options.timeZone = 'UTC';
|
|
3573
|
-
formatter = new Intl.DateTimeFormat(this.
|
|
3759
|
+
formatter = new Intl.DateTimeFormat(this._locale, options);
|
|
3574
3760
|
}
|
|
3575
3761
|
|
|
3576
3762
|
return formatter.format(new Date(date));
|
|
@@ -3582,6 +3768,10 @@ class I18n {
|
|
|
3582
3768
|
* @return {string}
|
|
3583
3769
|
*/
|
|
3584
3770
|
formatDuration(timeInMilliseconds) {
|
|
3771
|
+
// There is a proposal for a Intl.DurationFormat.
|
|
3772
|
+
// https://github.com/tc39/proposal-intl-duration-format
|
|
3773
|
+
// Until then, we do things a bit more manually.
|
|
3774
|
+
|
|
3585
3775
|
let timeInSeconds = timeInMilliseconds / 1000;
|
|
3586
3776
|
if (Math.round(timeInSeconds) === 0) {
|
|
3587
3777
|
return 'None';
|
|
@@ -3590,19 +3780,24 @@ class I18n {
|
|
|
3590
3780
|
/** @type {Array<string>} */
|
|
3591
3781
|
const parts = [];
|
|
3592
3782
|
/** @type {Record<string, number>} */
|
|
3593
|
-
const
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3783
|
+
const unitToSecondsPer = {
|
|
3784
|
+
day: 60 * 60 * 24,
|
|
3785
|
+
hour: 60 * 60,
|
|
3786
|
+
minute: 60,
|
|
3787
|
+
second: 1,
|
|
3598
3788
|
};
|
|
3599
3789
|
|
|
3600
|
-
Object.keys(
|
|
3601
|
-
const
|
|
3602
|
-
const numberOfUnits = Math.floor(timeInSeconds /
|
|
3790
|
+
Object.keys(unitToSecondsPer).forEach(unit => {
|
|
3791
|
+
const secondsPerUnit = unitToSecondsPer[unit];
|
|
3792
|
+
const numberOfUnits = Math.floor(timeInSeconds / secondsPerUnit);
|
|
3603
3793
|
if (numberOfUnits > 0) {
|
|
3604
|
-
timeInSeconds -= numberOfUnits *
|
|
3605
|
-
|
|
3794
|
+
timeInSeconds -= numberOfUnits * secondsPerUnit;
|
|
3795
|
+
const part = this._formatNumberWithGranularity(numberOfUnits, 1, {
|
|
3796
|
+
style: 'unit',
|
|
3797
|
+
unit,
|
|
3798
|
+
unitDisplay: 'narrow',
|
|
3799
|
+
});
|
|
3800
|
+
parts.push(part);
|
|
3606
3801
|
}
|
|
3607
3802
|
});
|
|
3608
3803
|
|
|
@@ -3636,7 +3831,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3636
3831
|
const tmpl = this.dom.createComponent('metric');
|
|
3637
3832
|
const element = this.dom.find('.lh-metric', tmpl);
|
|
3638
3833
|
element.id = audit.result.id;
|
|
3639
|
-
const rating =
|
|
3834
|
+
const rating = ReportUtils.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
|
|
3640
3835
|
element.classList.add(`lh-metric--${rating}`);
|
|
3641
3836
|
|
|
3642
3837
|
const titleEl = this.dom.find('.lh-metric__title', tmpl);
|
|
@@ -3646,7 +3841,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3646
3841
|
valueEl.textContent = audit.result.displayValue || '';
|
|
3647
3842
|
|
|
3648
3843
|
const descriptionEl = this.dom.find('.lh-metric__description', tmpl);
|
|
3649
|
-
descriptionEl.
|
|
3844
|
+
descriptionEl.append(this.dom.convertMarkdownLinkSnippets(audit.result.description));
|
|
3650
3845
|
|
|
3651
3846
|
if (audit.result.scoreDisplayMode === 'error') {
|
|
3652
3847
|
descriptionEl.textContent = '';
|
|
@@ -3684,7 +3879,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3684
3879
|
this.dom.find('span.lh-audit__display-text, div.lh-audit__display-text', element);
|
|
3685
3880
|
const sparklineWidthPct = `${details.overallSavingsMs / scale * 100}%`;
|
|
3686
3881
|
this.dom.find('div.lh-sparkline__bar', element).style.width = sparklineWidthPct;
|
|
3687
|
-
displayEl.textContent =
|
|
3882
|
+
displayEl.textContent = Globals.i18n.formatSeconds(details.overallSavingsMs, 0.01);
|
|
3688
3883
|
|
|
3689
3884
|
// Set [title] tooltips
|
|
3690
3885
|
if (audit.result.displayValue) {
|
|
@@ -3723,8 +3918,10 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3723
3918
|
_getScoringCalculatorHref(auditRefs) {
|
|
3724
3919
|
// TODO: filter by !!acronym when dropping renderer support of v7 LHRs.
|
|
3725
3920
|
const metrics = auditRefs.filter(audit => audit.group === 'metrics');
|
|
3921
|
+
const tti = auditRefs.find(audit => audit.id === 'interactive');
|
|
3726
3922
|
const fci = auditRefs.find(audit => audit.id === 'first-cpu-idle');
|
|
3727
3923
|
const fmp = auditRefs.find(audit => audit.id === 'first-meaningful-paint');
|
|
3924
|
+
if (tti) metrics.push(tti);
|
|
3728
3925
|
if (fci) metrics.push(fci);
|
|
3729
3926
|
if (fmp) metrics.push(fmp);
|
|
3730
3927
|
|
|
@@ -3749,9 +3946,9 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3749
3946
|
});
|
|
3750
3947
|
const paramPairs = [...metricPairs];
|
|
3751
3948
|
|
|
3752
|
-
if (
|
|
3753
|
-
paramPairs.push(['device',
|
|
3754
|
-
paramPairs.push(['version',
|
|
3949
|
+
if (Globals.reportJson) {
|
|
3950
|
+
paramPairs.push(['device', Globals.reportJson.configSettings.formFactor]);
|
|
3951
|
+
paramPairs.push(['version', Globals.reportJson.lighthouseVersion]);
|
|
3755
3952
|
}
|
|
3756
3953
|
|
|
3757
3954
|
const params = new URLSearchParams(paramPairs);
|
|
@@ -3783,10 +3980,10 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3783
3980
|
* @override
|
|
3784
3981
|
*/
|
|
3785
3982
|
render(category, groups, options) {
|
|
3786
|
-
const strings =
|
|
3983
|
+
const strings = Globals.strings;
|
|
3787
3984
|
const element = this.dom.createElement('div', 'lh-category');
|
|
3788
3985
|
element.id = category.id;
|
|
3789
|
-
element.
|
|
3986
|
+
element.append(this.renderCategoryHeader(category, groups, options));
|
|
3790
3987
|
|
|
3791
3988
|
// Metrics.
|
|
3792
3989
|
const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
|
|
@@ -3795,7 +3992,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3795
3992
|
|
|
3796
3993
|
// Metric descriptions toggle.
|
|
3797
3994
|
const checkboxEl = this.dom.createElement('input', 'lh-metrics-toggle__input');
|
|
3798
|
-
const checkboxId = `lh-metrics-toggle${
|
|
3995
|
+
const checkboxId = `lh-metrics-toggle${Globals.getUniqueSuffix()}`;
|
|
3799
3996
|
checkboxEl.setAttribute('aria-label', 'Toggle the display of metric descriptions');
|
|
3800
3997
|
checkboxEl.type = 'checkbox';
|
|
3801
3998
|
checkboxEl.id = checkboxId;
|
|
@@ -3805,13 +4002,13 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3805
4002
|
labelEl.htmlFor = checkboxId;
|
|
3806
4003
|
const showEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--show');
|
|
3807
4004
|
const hideEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--hide');
|
|
3808
|
-
showEl.textContent =
|
|
3809
|
-
hideEl.textContent =
|
|
4005
|
+
showEl.textContent = Globals.strings.expandView;
|
|
4006
|
+
hideEl.textContent = Globals.strings.collapseView;
|
|
3810
4007
|
|
|
3811
4008
|
const metricsBoxesEl = this.dom.createElement('div', 'lh-metrics-container');
|
|
3812
4009
|
metricsGroupEl.insertBefore(metricsBoxesEl, metricsFooterEl);
|
|
3813
4010
|
metricAudits.forEach(item => {
|
|
3814
|
-
metricsBoxesEl.
|
|
4011
|
+
metricsBoxesEl.append(this._renderMetric(item));
|
|
3815
4012
|
});
|
|
3816
4013
|
|
|
3817
4014
|
// Only add the disclaimer with the score calculator link if the category was rendered with a score gauge.
|
|
@@ -3819,7 +4016,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3819
4016
|
const descriptionEl = this.dom.find('.lh-category-header__description', element);
|
|
3820
4017
|
const estValuesEl = this.dom.createChildOf(descriptionEl, 'div', 'lh-metrics__disclaimer');
|
|
3821
4018
|
const disclaimerEl = this.dom.convertMarkdownLinkSnippets(strings.varianceDisclaimer);
|
|
3822
|
-
estValuesEl.
|
|
4019
|
+
estValuesEl.append(disclaimerEl);
|
|
3823
4020
|
|
|
3824
4021
|
// Add link to score calculator.
|
|
3825
4022
|
const calculatorLink = this.dom.createChildOf(estValuesEl, 'a', 'lh-calclink');
|
|
@@ -3829,7 +4026,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3829
4026
|
}
|
|
3830
4027
|
|
|
3831
4028
|
metricsGroupEl.classList.add('lh-audit-group--metrics');
|
|
3832
|
-
element.
|
|
4029
|
+
element.append(metricsGroupEl);
|
|
3833
4030
|
}
|
|
3834
4031
|
|
|
3835
4032
|
// Filmstrip
|
|
@@ -3839,13 +4036,13 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3839
4036
|
if (thumbnailResult?.details) {
|
|
3840
4037
|
timelineEl.id = thumbnailResult.id;
|
|
3841
4038
|
const filmstripEl = this.detailsRenderer.render(thumbnailResult.details);
|
|
3842
|
-
filmstripEl && timelineEl.
|
|
4039
|
+
filmstripEl && timelineEl.append(filmstripEl);
|
|
3843
4040
|
}
|
|
3844
4041
|
|
|
3845
4042
|
// Opportunities
|
|
3846
4043
|
const opportunityAudits = category.auditRefs
|
|
3847
4044
|
.filter(audit => this._classifyPerformanceAudit(audit) === 'load-opportunity')
|
|
3848
|
-
.filter(audit => !
|
|
4045
|
+
.filter(audit => !ReportUtils.showAsPassed(audit.result))
|
|
3849
4046
|
.sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
|
|
3850
4047
|
|
|
3851
4048
|
const filterableMetrics = metricAudits.filter(a => !!a.relevantAudits);
|
|
@@ -3873,13 +4070,13 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3873
4070
|
opportunityAudits.forEach(item =>
|
|
3874
4071
|
groupEl.insertBefore(this._renderOpportunity(item, scale), footerEl));
|
|
3875
4072
|
groupEl.classList.add('lh-audit-group--load-opportunities');
|
|
3876
|
-
element.
|
|
4073
|
+
element.append(groupEl);
|
|
3877
4074
|
}
|
|
3878
4075
|
|
|
3879
4076
|
// Diagnostics
|
|
3880
4077
|
const diagnosticAudits = category.auditRefs
|
|
3881
4078
|
.filter(audit => this._classifyPerformanceAudit(audit) === 'diagnostic')
|
|
3882
|
-
.filter(audit => !
|
|
4079
|
+
.filter(audit => !ReportUtils.showAsPassed(audit.result))
|
|
3883
4080
|
.sort((a, b) => {
|
|
3884
4081
|
const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
|
|
3885
4082
|
const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
|
|
@@ -3890,12 +4087,13 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3890
4087
|
const [groupEl, footerEl] = this.renderAuditGroup(groups['diagnostics']);
|
|
3891
4088
|
diagnosticAudits.forEach(item => groupEl.insertBefore(this.renderAudit(item), footerEl));
|
|
3892
4089
|
groupEl.classList.add('lh-audit-group--diagnostics');
|
|
3893
|
-
element.
|
|
4090
|
+
element.append(groupEl);
|
|
3894
4091
|
}
|
|
3895
4092
|
|
|
3896
4093
|
// Passed audits
|
|
3897
4094
|
const passedAudits = category.auditRefs
|
|
3898
|
-
.filter(audit =>
|
|
4095
|
+
.filter(audit =>
|
|
4096
|
+
this._classifyPerformanceAudit(audit) && ReportUtils.showAsPassed(audit.result));
|
|
3899
4097
|
|
|
3900
4098
|
if (!passedAudits.length) return element;
|
|
3901
4099
|
|
|
@@ -3904,7 +4102,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3904
4102
|
groupDefinitions: groups,
|
|
3905
4103
|
};
|
|
3906
4104
|
const passedElem = this.renderClump('passed', clumpOpts);
|
|
3907
|
-
element.
|
|
4105
|
+
element.append(passedElem);
|
|
3908
4106
|
|
|
3909
4107
|
// Budgets
|
|
3910
4108
|
/** @type {Array<Element>} */
|
|
@@ -3924,7 +4122,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3924
4122
|
const [groupEl, footerEl] = this.renderAuditGroup(groups.budgets);
|
|
3925
4123
|
budgetTableEls.forEach(table => groupEl.insertBefore(table, footerEl));
|
|
3926
4124
|
groupEl.classList.add('lh-audit-group--budgets');
|
|
3927
|
-
element.
|
|
4125
|
+
element.append(groupEl);
|
|
3928
4126
|
}
|
|
3929
4127
|
|
|
3930
4128
|
return element;
|
|
@@ -3938,7 +4136,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3938
4136
|
renderMetricAuditFilter(filterableMetrics, categoryEl) {
|
|
3939
4137
|
const metricFilterEl = this.dom.createElement('div', 'lh-metricfilter');
|
|
3940
4138
|
const textEl = this.dom.createChildOf(metricFilterEl, 'span', 'lh-metricfilter__text');
|
|
3941
|
-
textEl.textContent =
|
|
4139
|
+
textEl.textContent = Globals.strings.showRelevantAudits;
|
|
3942
4140
|
|
|
3943
4141
|
const filterChoices = /** @type {LH.ReportResult.AuditRef[]} */ ([
|
|
3944
4142
|
({acronym: 'All'}),
|
|
@@ -3947,7 +4145,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
3947
4145
|
|
|
3948
4146
|
// Form labels need to reference unique IDs, but multiple reports rendered in the same DOM (eg PSI)
|
|
3949
4147
|
// would mean ID conflict. To address this, we 'scope' these radio inputs with a unique suffix.
|
|
3950
|
-
const uniqSuffix =
|
|
4148
|
+
const uniqSuffix = Globals.getUniqueSuffix();
|
|
3951
4149
|
for (const metric of filterChoices) {
|
|
3952
4150
|
const elemId = `metric-${metric.acronym}-${uniqSuffix}`;
|
|
3953
4151
|
const radioEl = this.dom.createChildOf(metricFilterEl, 'input', 'lh-metricfilter__radio');
|
|
@@ -4024,7 +4222,7 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4024
4222
|
render(category, groupDefinitions = {}) {
|
|
4025
4223
|
const categoryElem = this.dom.createElement('div', 'lh-category');
|
|
4026
4224
|
categoryElem.id = category.id;
|
|
4027
|
-
categoryElem.
|
|
4225
|
+
categoryElem.append(this.renderCategoryHeader(category, groupDefinitions));
|
|
4028
4226
|
|
|
4029
4227
|
const auditRefs = category.auditRefs;
|
|
4030
4228
|
|
|
@@ -4032,13 +4230,13 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4032
4230
|
// all put in a top-level clump that isn't expandable/collapsible.
|
|
4033
4231
|
const regularAuditRefs = auditRefs.filter(ref => ref.result.scoreDisplayMode !== 'manual');
|
|
4034
4232
|
const auditsElem = this._renderAudits(regularAuditRefs, groupDefinitions);
|
|
4035
|
-
categoryElem.
|
|
4233
|
+
categoryElem.append(auditsElem);
|
|
4036
4234
|
|
|
4037
4235
|
// Manual audits are still in a manual clump.
|
|
4038
4236
|
const manualAuditRefs = auditRefs.filter(ref => ref.result.scoreDisplayMode === 'manual');
|
|
4039
4237
|
const manualElem = this.renderClump('manual',
|
|
4040
4238
|
{auditRefs: manualAuditRefs, description: category.manualDescription});
|
|
4041
|
-
categoryElem.
|
|
4239
|
+
categoryElem.append(manualElem);
|
|
4042
4240
|
|
|
4043
4241
|
return categoryElem;
|
|
4044
4242
|
}
|
|
@@ -4098,7 +4296,7 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4098
4296
|
|
|
4099
4297
|
// Remove any that have a failing audit.
|
|
4100
4298
|
for (const auditRef of auditRefs) {
|
|
4101
|
-
if (!
|
|
4299
|
+
if (!ReportUtils.showAsPassed(auditRef.result) && auditRef.group) {
|
|
4102
4300
|
uniqueGroupIds.delete(auditRef.group);
|
|
4103
4301
|
}
|
|
4104
4302
|
}
|
|
@@ -4119,7 +4317,7 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4119
4317
|
for (const groupId of groupIds) {
|
|
4120
4318
|
const groupAuditRefs = auditRefs.filter(ref => ref.group === groupId);
|
|
4121
4319
|
const auditCount = groupAuditRefs.length;
|
|
4122
|
-
const passedCount = groupAuditRefs.filter(ref =>
|
|
4320
|
+
const passedCount = groupAuditRefs.filter(ref => ReportUtils.showAsPassed(ref.result)).length;
|
|
4123
4321
|
|
|
4124
4322
|
const title = groupDefinitions[groupId].title;
|
|
4125
4323
|
tips.push(`${title}: ${passedCount}/${auditCount}`);
|
|
@@ -4159,7 +4357,7 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4159
4357
|
const defsEl = svgRoot.querySelector('defs');
|
|
4160
4358
|
if (!defsEl) return;
|
|
4161
4359
|
|
|
4162
|
-
const idSuffix =
|
|
4360
|
+
const idSuffix = Globals.getUniqueSuffix();
|
|
4163
4361
|
const elementsToUpdate = defsEl.querySelectorAll('[id]');
|
|
4164
4362
|
for (const el of elementsToUpdate) {
|
|
4165
4363
|
const oldId = el.id;
|
|
@@ -4201,7 +4399,6 @@ class PwaCategoryRenderer extends CategoryRenderer {
|
|
|
4201
4399
|
* (this is handled by terser)
|
|
4202
4400
|
*/
|
|
4203
4401
|
|
|
4204
|
-
|
|
4205
4402
|
class ReportRenderer {
|
|
4206
4403
|
/**
|
|
4207
4404
|
* @param {DOM} dom
|
|
@@ -4240,10 +4437,10 @@ class ReportRenderer {
|
|
|
4240
4437
|
|
|
4241
4438
|
this._dom.setLighthouseChannel(lhr.configSettings.channel || 'unknown');
|
|
4242
4439
|
|
|
4243
|
-
const report =
|
|
4440
|
+
const report = ReportUtils.prepareReportResult(lhr);
|
|
4244
4441
|
|
|
4245
4442
|
this._dom.rootEl.textContent = ''; // Remove previous report.
|
|
4246
|
-
this._dom.rootEl.
|
|
4443
|
+
this._dom.rootEl.append(this._renderReport(report));
|
|
4247
4444
|
|
|
4248
4445
|
return this._dom.rootEl;
|
|
4249
4446
|
}
|
|
@@ -4255,9 +4452,9 @@ class ReportRenderer {
|
|
|
4255
4452
|
_renderReportTopbar(report) {
|
|
4256
4453
|
const el = this._dom.createComponent('topbar');
|
|
4257
4454
|
const metadataUrl = this._dom.find('a.lh-topbar__url', el);
|
|
4258
|
-
metadataUrl.textContent = report.
|
|
4259
|
-
metadataUrl.title = report.
|
|
4260
|
-
this._dom.safelySetHref(metadataUrl, report.
|
|
4455
|
+
metadataUrl.textContent = report.finalDisplayedUrl;
|
|
4456
|
+
metadataUrl.title = report.finalDisplayedUrl;
|
|
4457
|
+
this._dom.safelySetHref(metadataUrl, report.finalDisplayedUrl);
|
|
4261
4458
|
return el;
|
|
4262
4459
|
}
|
|
4263
4460
|
|
|
@@ -4281,7 +4478,7 @@ class ReportRenderer {
|
|
|
4281
4478
|
|
|
4282
4479
|
this._renderMetaBlock(report, footer);
|
|
4283
4480
|
|
|
4284
|
-
this._dom.find('.lh-footer__version_issue', footer).textContent =
|
|
4481
|
+
this._dom.find('.lh-footer__version_issue', footer).textContent = Globals.strings.footerIssue;
|
|
4285
4482
|
this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
|
|
4286
4483
|
return footer;
|
|
4287
4484
|
}
|
|
@@ -4291,9 +4488,7 @@ class ReportRenderer {
|
|
|
4291
4488
|
* @param {DocumentFragment} footer
|
|
4292
4489
|
*/
|
|
4293
4490
|
_renderMetaBlock(report, footer) {
|
|
4294
|
-
const envValues =
|
|
4295
|
-
|
|
4296
|
-
|
|
4491
|
+
const envValues = ReportUtils.getEmulationDescriptions(report.configSettings || {});
|
|
4297
4492
|
const match = report.userAgent.match(/(\w*Chrome\/[\d.]+)/); // \w* to include 'HeadlessChrome'
|
|
4298
4493
|
const chromeVer = Array.isArray(match)
|
|
4299
4494
|
? match[1].replace('/', ' ').replace('Chrome', 'Chromium')
|
|
@@ -4302,26 +4497,36 @@ class ReportRenderer {
|
|
|
4302
4497
|
const benchmarkIndex = report.environment.benchmarkIndex.toFixed(0);
|
|
4303
4498
|
const axeVersion = report.environment.credits?.['axe-core'];
|
|
4304
4499
|
|
|
4500
|
+
const devicesTooltipTextLines = [
|
|
4501
|
+
`${Globals.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}`,
|
|
4502
|
+
`${Globals.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}`,
|
|
4503
|
+
];
|
|
4504
|
+
if (envValues.screenEmulation) {
|
|
4505
|
+
devicesTooltipTextLines.push(
|
|
4506
|
+
`${Globals.strings.runtimeSettingsScreenEmulation}: ${envValues.screenEmulation}`);
|
|
4507
|
+
}
|
|
4508
|
+
if (axeVersion) {
|
|
4509
|
+
devicesTooltipTextLines.push(`${Globals.strings.runtimeSettingsAxeVersion}: ${axeVersion}`);
|
|
4510
|
+
}
|
|
4511
|
+
|
|
4305
4512
|
// [CSS icon class, textContent, tooltipText]
|
|
4306
4513
|
const metaItems = [
|
|
4307
4514
|
['date',
|
|
4308
|
-
`Captured at ${
|
|
4515
|
+
`Captured at ${Globals.i18n.formatDateTime(report.fetchTime)}`],
|
|
4309
4516
|
['devices',
|
|
4310
4517
|
`${envValues.deviceEmulation} with Lighthouse ${report.lighthouseVersion}`,
|
|
4311
|
-
|
|
4312
|
-
`\n${Util.i18n.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}` +
|
|
4313
|
-
(axeVersion ? `\n${Util.i18n.strings.runtimeSettingsAxeVersion}: ${axeVersion}` : '')],
|
|
4518
|
+
devicesTooltipTextLines.join('\n')],
|
|
4314
4519
|
['samples-one',
|
|
4315
|
-
|
|
4316
|
-
|
|
4520
|
+
Globals.strings.runtimeSingleLoad,
|
|
4521
|
+
Globals.strings.runtimeSingleLoadTooltip],
|
|
4317
4522
|
['stopwatch',
|
|
4318
|
-
|
|
4523
|
+
Globals.strings.runtimeAnalysisWindow],
|
|
4319
4524
|
['networkspeed',
|
|
4320
4525
|
`${envValues.summary}`,
|
|
4321
|
-
`${
|
|
4526
|
+
`${Globals.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
|
|
4322
4527
|
['chrome',
|
|
4323
4528
|
`Using ${chromeVer}` + (channel ? ` with ${channel}` : ''),
|
|
4324
|
-
`${
|
|
4529
|
+
`${Globals.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
|
|
4325
4530
|
];
|
|
4326
4531
|
|
|
4327
4532
|
const metaItemsEl = this._dom.find('.lh-meta__items', footer);
|
|
@@ -4349,13 +4554,15 @@ class ReportRenderer {
|
|
|
4349
4554
|
|
|
4350
4555
|
const container = this._dom.createComponent('warningsToplevel');
|
|
4351
4556
|
const message = this._dom.find('.lh-warnings__msg', container);
|
|
4352
|
-
message.textContent =
|
|
4557
|
+
message.textContent = Globals.strings.toplevelWarningsMessage;
|
|
4353
4558
|
|
|
4354
|
-
const warnings =
|
|
4559
|
+
const warnings = [];
|
|
4355
4560
|
for (const warningString of report.runWarnings) {
|
|
4356
|
-
const warning =
|
|
4357
|
-
warning.
|
|
4561
|
+
const warning = this._dom.createElement('li');
|
|
4562
|
+
warning.append(this._dom.convertMarkdownLinkSnippets(warningString));
|
|
4563
|
+
warnings.push(warning);
|
|
4358
4564
|
}
|
|
4565
|
+
this._dom.find('ul', container).append(...warnings);
|
|
4359
4566
|
|
|
4360
4567
|
return container;
|
|
4361
4568
|
}
|
|
@@ -4401,7 +4608,7 @@ class ReportRenderer {
|
|
|
4401
4608
|
}
|
|
4402
4609
|
|
|
4403
4610
|
|
|
4404
|
-
if (
|
|
4611
|
+
if (ReportUtils.isPluginCategory(category.id)) {
|
|
4405
4612
|
pluginGauges.push(categoryGauge);
|
|
4406
4613
|
} else if (renderer.renderCategoryScore === categoryRenderer.renderCategoryScore) {
|
|
4407
4614
|
// The renderer for default categories is just the default CategoryRenderer.
|
|
@@ -4423,20 +4630,14 @@ class ReportRenderer {
|
|
|
4423
4630
|
* @return {!DocumentFragment}
|
|
4424
4631
|
*/
|
|
4425
4632
|
_renderReport(report) {
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4633
|
+
Globals.apply({
|
|
4634
|
+
providedStrings: report.i18n.rendererFormattedStrings,
|
|
4635
|
+
i18n: new I18nFormatter(report.configSettings.locale),
|
|
4636
|
+
reportJson: report,
|
|
4430
4637
|
});
|
|
4431
|
-
Util.i18n = i18n;
|
|
4432
|
-
Util.reportJson = report;
|
|
4433
4638
|
|
|
4434
|
-
const fullPageScreenshot =
|
|
4435
|
-
report.audits['full-page-screenshot']?.details &&
|
|
4436
|
-
report.audits['full-page-screenshot'].details.type === 'full-page-screenshot' ?
|
|
4437
|
-
report.audits['full-page-screenshot'].details : undefined;
|
|
4438
4639
|
const detailsRenderer = new DetailsRenderer(this._dom, {
|
|
4439
|
-
fullPageScreenshot,
|
|
4640
|
+
fullPageScreenshot: report.fullPageScreenshot ?? undefined,
|
|
4440
4641
|
});
|
|
4441
4642
|
|
|
4442
4643
|
const categoryRenderer = new CategoryRenderer(this._dom, detailsRenderer);
|
|
@@ -4448,11 +4649,11 @@ class ReportRenderer {
|
|
|
4448
4649
|
};
|
|
4449
4650
|
|
|
4450
4651
|
const headerContainer = this._dom.createElement('div');
|
|
4451
|
-
headerContainer.
|
|
4652
|
+
headerContainer.append(this._renderReportHeader());
|
|
4452
4653
|
|
|
4453
4654
|
const reportContainer = this._dom.createElement('div', 'lh-container');
|
|
4454
4655
|
const reportSection = this._dom.createElement('div', 'lh-report');
|
|
4455
|
-
reportSection.
|
|
4656
|
+
reportSection.append(this._renderReportWarnings(report));
|
|
4456
4657
|
|
|
4457
4658
|
let scoreHeader;
|
|
4458
4659
|
const isSoloCategory = Object.keys(report.categories).length === 1;
|
|
@@ -4469,23 +4670,23 @@ class ReportRenderer {
|
|
|
4469
4670
|
const scoresContainer = this._dom.find('.lh-scores-container', headerContainer);
|
|
4470
4671
|
scoreHeader.append(
|
|
4471
4672
|
...this._renderScoreGauges(report, categoryRenderer, specificCategoryRenderers));
|
|
4472
|
-
scoresContainer.
|
|
4473
|
-
scoresContainer.appendChild(scoreScale);
|
|
4673
|
+
scoresContainer.append(scoreHeader, scoreScale);
|
|
4474
4674
|
|
|
4475
4675
|
const stickyHeader = this._dom.createElement('div', 'lh-sticky-header');
|
|
4476
4676
|
stickyHeader.append(
|
|
4477
4677
|
...this._renderScoreGauges(report, categoryRenderer, specificCategoryRenderers));
|
|
4478
|
-
reportContainer.
|
|
4678
|
+
reportContainer.append(stickyHeader);
|
|
4479
4679
|
}
|
|
4480
4680
|
|
|
4481
|
-
const categories =
|
|
4681
|
+
const categories = this._dom.createElement('div', 'lh-categories');
|
|
4682
|
+
reportSection.append(categories);
|
|
4482
4683
|
const categoryOptions = {gatherMode: report.gatherMode};
|
|
4483
4684
|
for (const category of Object.values(report.categories)) {
|
|
4484
4685
|
const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
|
|
4485
4686
|
// .lh-category-wrapper is full-width and provides horizontal rules between categories.
|
|
4486
4687
|
// .lh-category within has the max-width: var(--report-content-max-width);
|
|
4487
4688
|
const wrapper = renderer.dom.createChildOf(categories, 'div', 'lh-category-wrapper');
|
|
4488
|
-
wrapper.
|
|
4689
|
+
wrapper.append(renderer.render(
|
|
4489
4690
|
category,
|
|
4490
4691
|
report.categoryGroups,
|
|
4491
4692
|
categoryOptions
|
|
@@ -4500,17 +4701,16 @@ class ReportRenderer {
|
|
|
4500
4701
|
}
|
|
4501
4702
|
|
|
4502
4703
|
if (!this._opts.omitTopbar) {
|
|
4503
|
-
reportFragment.
|
|
4704
|
+
reportFragment.append(this._renderReportTopbar(report));
|
|
4504
4705
|
}
|
|
4505
4706
|
|
|
4506
|
-
reportFragment.
|
|
4507
|
-
|
|
4508
|
-
reportContainer.
|
|
4509
|
-
reportSection.appendChild(this._renderReportFooter(report));
|
|
4707
|
+
reportFragment.append(reportContainer);
|
|
4708
|
+
reportSection.append(this._renderReportFooter(report));
|
|
4709
|
+
reportContainer.append(headerContainer, reportSection);
|
|
4510
4710
|
|
|
4511
|
-
if (fullPageScreenshot) {
|
|
4711
|
+
if (report.fullPageScreenshot) {
|
|
4512
4712
|
ElementScreenshotRenderer.installFullPageScreenshot(
|
|
4513
|
-
this._dom.rootEl, fullPageScreenshot.screenshot);
|
|
4713
|
+
this._dom.rootEl, report.fullPageScreenshot.screenshot);
|
|
4514
4714
|
}
|
|
4515
4715
|
|
|
4516
4716
|
return reportFragment;
|
|
@@ -4641,7 +4841,7 @@ function getAppsOrigin() {
|
|
|
4641
4841
|
const isDev = new URLSearchParams(window.location.search).has('dev');
|
|
4642
4842
|
|
|
4643
4843
|
if (isVercel) return `https://${window.location.host}/gh-pages`;
|
|
4644
|
-
if (isDev) return 'http://localhost:
|
|
4844
|
+
if (isDev) return 'http://localhost:7333';
|
|
4645
4845
|
return 'https://googlechrome.github.io/lighthouse';
|
|
4646
4846
|
}
|
|
4647
4847
|
|
|
@@ -4654,7 +4854,7 @@ function computeWindowNameSuffix(json) {
|
|
|
4654
4854
|
// @ts-expect-error - If this is a v2 LHR, use old `generatedTime`.
|
|
4655
4855
|
const fallbackFetchTime = /** @type {string} */ (json.generatedTime);
|
|
4656
4856
|
const fetchTime = json.fetchTime || fallbackFetchTime;
|
|
4657
|
-
return `${json.lighthouseVersion}-${json.
|
|
4857
|
+
return `${json.lighthouseVersion}-${json.finalDisplayedUrl}-${fetchTime}`;
|
|
4658
4858
|
}
|
|
4659
4859
|
|
|
4660
4860
|
/**
|
|
@@ -4736,8 +4936,9 @@ function openTreemap(json) {
|
|
|
4736
4936
|
/** @type {LH.Treemap.Options} */
|
|
4737
4937
|
const treemapOptions = {
|
|
4738
4938
|
lhr: {
|
|
4739
|
-
|
|
4939
|
+
mainDocumentUrl: json.mainDocumentUrl,
|
|
4740
4940
|
finalUrl: json.finalUrl,
|
|
4941
|
+
finalDisplayedUrl: json.finalDisplayedUrl,
|
|
4741
4942
|
audits: {
|
|
4742
4943
|
'script-treemap-data': json.audits['script-treemap-data'],
|
|
4743
4944
|
},
|
|
@@ -5198,7 +5399,8 @@ class TopbarFeatures {
|
|
|
5198
5399
|
*/
|
|
5199
5400
|
_setUpCollapseDetailsAfterPrinting() {
|
|
5200
5401
|
// FF and IE implement these old events.
|
|
5201
|
-
|
|
5402
|
+
const supportsOldPrintEvents = 'onbeforeprint' in self;
|
|
5403
|
+
if (supportsOldPrintEvents) {
|
|
5202
5404
|
self.addEventListener('afterprint', this.collapseAllDetails);
|
|
5203
5405
|
} else {
|
|
5204
5406
|
// Note: FF implements both window.onbeforeprint and media listeners. However,
|
|
@@ -5311,27 +5513,14 @@ function getFilenamePrefix(name, fetchTime) {
|
|
|
5311
5513
|
|
|
5312
5514
|
/**
|
|
5313
5515
|
* Generate a filenamePrefix of hostname_YYYY-MM-DD_HH-MM-SS.
|
|
5314
|
-
* @param {{
|
|
5516
|
+
* @param {{finalDisplayedUrl: string, fetchTime: string}} lhr
|
|
5315
5517
|
* @return {string}
|
|
5316
5518
|
*/
|
|
5317
5519
|
function getLhrFilenamePrefix(lhr) {
|
|
5318
|
-
const hostname = new URL(lhr.
|
|
5520
|
+
const hostname = new URL(lhr.finalDisplayedUrl).hostname;
|
|
5319
5521
|
return getFilenamePrefix(hostname, lhr.fetchTime);
|
|
5320
5522
|
}
|
|
5321
5523
|
|
|
5322
|
-
/**
|
|
5323
|
-
* Generate a filenamePrefix of name_YYYY-MM-DD_HH-MM-SS.
|
|
5324
|
-
* @param {{name: string, steps: Array<{lhr: {fetchTime: string}}>}} flowResult
|
|
5325
|
-
* @return {string}
|
|
5326
|
-
*/
|
|
5327
|
-
function getFlowResultFilenamePrefix(flowResult) {
|
|
5328
|
-
const lhr = flowResult.steps[0].lhr;
|
|
5329
|
-
const name = flowResult.name.replace(/\s/g, '-');
|
|
5330
|
-
return getFilenamePrefix(name, lhr.fetchTime);
|
|
5331
|
-
}
|
|
5332
|
-
|
|
5333
|
-
var fileNamer = {getLhrFilenamePrefix, getFilenamePrefix, getFlowResultFilenamePrefix};
|
|
5334
|
-
|
|
5335
5524
|
/**
|
|
5336
5525
|
* @license
|
|
5337
5526
|
* Copyright 2017 The Lighthouse Authors. All Rights Reserved.
|
|
@@ -5380,6 +5569,7 @@ class ReportUIFeatures {
|
|
|
5380
5569
|
*/
|
|
5381
5570
|
initFeatures(lhr) {
|
|
5382
5571
|
this.json = lhr;
|
|
5572
|
+
this._fullPageScreenshot = Util.getFullPageScreenshot(lhr);
|
|
5383
5573
|
|
|
5384
5574
|
if (this._topbar) {
|
|
5385
5575
|
this._topbar.enable(lhr);
|
|
@@ -5425,7 +5615,7 @@ class ReportUIFeatures {
|
|
|
5425
5615
|
this.json.audits['script-treemap-data'] && this.json.audits['script-treemap-data'].details;
|
|
5426
5616
|
if (showTreemapApp) {
|
|
5427
5617
|
this.addButton({
|
|
5428
|
-
text:
|
|
5618
|
+
text: Globals.strings.viewTreemapLabel,
|
|
5429
5619
|
icon: 'treemap',
|
|
5430
5620
|
onClick: () => openTreemap(this.json),
|
|
5431
5621
|
});
|
|
@@ -5434,8 +5624,8 @@ class ReportUIFeatures {
|
|
|
5434
5624
|
if (this._opts.onViewTrace) {
|
|
5435
5625
|
this.addButton({
|
|
5436
5626
|
text: lhr.configSettings.throttlingMethod === 'simulate' ?
|
|
5437
|
-
|
|
5438
|
-
|
|
5627
|
+
Globals.strings.viewOriginalTraceLabel :
|
|
5628
|
+
Globals.strings.viewTraceLabel,
|
|
5439
5629
|
onClick: () => this._opts.onViewTrace?.(),
|
|
5440
5630
|
});
|
|
5441
5631
|
}
|
|
@@ -5446,11 +5636,11 @@ class ReportUIFeatures {
|
|
|
5446
5636
|
|
|
5447
5637
|
// Fill in all i18n data.
|
|
5448
5638
|
for (const node of this._dom.findAll('[data-i18n]', this._dom.rootEl)) {
|
|
5449
|
-
// These strings are guaranteed to (at least) have a default English string in
|
|
5639
|
+
// These strings are guaranteed to (at least) have a default English string in UIStrings,
|
|
5450
5640
|
// so this cannot be undefined as long as `report-ui-features.data-i18n` test passes.
|
|
5451
5641
|
const i18nKey = node.getAttribute('data-i18n');
|
|
5452
|
-
const i18nAttr = /** @type {keyof typeof
|
|
5453
|
-
node.textContent =
|
|
5642
|
+
const i18nAttr = /** @type {keyof typeof Globals.strings} */ (i18nKey);
|
|
5643
|
+
node.textContent = Globals.strings[i18nAttr];
|
|
5454
5644
|
}
|
|
5455
5645
|
}
|
|
5456
5646
|
|
|
@@ -5562,7 +5752,9 @@ class ReportUIFeatures {
|
|
|
5562
5752
|
|
|
5563
5753
|
tablesWithUrls.forEach((tableEl) => {
|
|
5564
5754
|
const rowEls = getTableRows(tableEl);
|
|
5565
|
-
const
|
|
5755
|
+
const primaryRowEls = rowEls.filter(rowEl => !rowEl.classList.contains('lh-sub-item-row'));
|
|
5756
|
+
const thirdPartyRowEls = this._getThirdPartyRows(
|
|
5757
|
+
primaryRowEls, Util.getFinalDisplayedUrl(this.json));
|
|
5566
5758
|
|
|
5567
5759
|
// create input box
|
|
5568
5760
|
const filterTemplate = this._dom.createComponent('3pFilter');
|
|
@@ -5571,9 +5763,9 @@ class ReportUIFeatures {
|
|
|
5571
5763
|
filterInput.addEventListener('change', e => {
|
|
5572
5764
|
const shouldHideThirdParty = e.target instanceof HTMLInputElement && !e.target.checked;
|
|
5573
5765
|
let even = true;
|
|
5574
|
-
let rowEl =
|
|
5766
|
+
let rowEl = primaryRowEls[0];
|
|
5575
5767
|
while (rowEl) {
|
|
5576
|
-
const shouldHide = shouldHideThirdParty &&
|
|
5768
|
+
const shouldHide = shouldHideThirdParty && thirdPartyRowEls.includes(rowEl);
|
|
5577
5769
|
|
|
5578
5770
|
// Iterate subsequent associated sub item rows.
|
|
5579
5771
|
do {
|
|
@@ -5590,12 +5782,12 @@ class ReportUIFeatures {
|
|
|
5590
5782
|
});
|
|
5591
5783
|
|
|
5592
5784
|
this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
|
|
5593
|
-
`${
|
|
5785
|
+
`${thirdPartyRowEls.length}`;
|
|
5594
5786
|
this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
|
|
5595
|
-
|
|
5787
|
+
Globals.strings.thirdPartyResourcesLabel;
|
|
5596
5788
|
|
|
5597
|
-
const allThirdParty =
|
|
5598
|
-
const allFirstParty = !
|
|
5789
|
+
const allThirdParty = thirdPartyRowEls.length === primaryRowEls.length;
|
|
5790
|
+
const allFirstParty = !thirdPartyRowEls.length;
|
|
5599
5791
|
|
|
5600
5792
|
// If all or none of the rows are 3rd party, hide the control.
|
|
5601
5793
|
if (allThirdParty || allFirstParty) {
|
|
@@ -5619,18 +5811,13 @@ class ReportUIFeatures {
|
|
|
5619
5811
|
* @param {Element} rootEl
|
|
5620
5812
|
*/
|
|
5621
5813
|
_setupElementScreenshotOverlay(rootEl) {
|
|
5622
|
-
|
|
5623
|
-
this.json.audits['full-page-screenshot'] &&
|
|
5624
|
-
this.json.audits['full-page-screenshot'].details &&
|
|
5625
|
-
this.json.audits['full-page-screenshot'].details.type === 'full-page-screenshot' &&
|
|
5626
|
-
this.json.audits['full-page-screenshot'].details;
|
|
5627
|
-
if (!fullPageScreenshot) return;
|
|
5814
|
+
if (!this._fullPageScreenshot) return;
|
|
5628
5815
|
|
|
5629
5816
|
ElementScreenshotRenderer.installOverlayFeature({
|
|
5630
5817
|
dom: this._dom,
|
|
5631
5818
|
rootEl: rootEl,
|
|
5632
5819
|
overlayContainerEl: rootEl,
|
|
5633
|
-
fullPageScreenshot,
|
|
5820
|
+
fullPageScreenshot: this._fullPageScreenshot,
|
|
5634
5821
|
});
|
|
5635
5822
|
}
|
|
5636
5823
|
|
|
@@ -5638,29 +5825,33 @@ class ReportUIFeatures {
|
|
|
5638
5825
|
* From a table with URL entries, finds the rows containing third-party URLs
|
|
5639
5826
|
* and returns them.
|
|
5640
5827
|
* @param {HTMLElement[]} rowEls
|
|
5641
|
-
* @param {string}
|
|
5828
|
+
* @param {string} finalDisplayedUrl
|
|
5642
5829
|
* @return {Array<HTMLElement>}
|
|
5643
5830
|
*/
|
|
5644
|
-
_getThirdPartyRows(rowEls,
|
|
5645
|
-
|
|
5646
|
-
const
|
|
5647
|
-
const finalUrlRootDomain = Util.getRootDomain(finalUrl);
|
|
5831
|
+
_getThirdPartyRows(rowEls, finalDisplayedUrl) {
|
|
5832
|
+
const finalDisplayedUrlRootDomain = Util.getRootDomain(finalDisplayedUrl);
|
|
5833
|
+
const firstPartyEntityName = this.json.entities?.find(e => e.isFirstParty === true)?.name;
|
|
5648
5834
|
|
|
5835
|
+
/** @type {Array<HTMLElement>} */
|
|
5836
|
+
const thirdPartyRowEls = [];
|
|
5649
5837
|
for (const rowEl of rowEls) {
|
|
5650
|
-
if (
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5838
|
+
if (firstPartyEntityName) {
|
|
5839
|
+
// We rely on entity-classification for new LHRs that support it.
|
|
5840
|
+
if (!rowEl.dataset.entity || rowEl.dataset.entity === firstPartyEntityName) continue;
|
|
5841
|
+
} else {
|
|
5842
|
+
// Without 10.0's entity classification, fallback to the older root domain-based filtering.
|
|
5843
|
+
const urlItem = rowEl.querySelector('div.lh-text__url');
|
|
5844
|
+
if (!urlItem) continue;
|
|
5845
|
+
const datasetUrl = urlItem.dataset.url;
|
|
5846
|
+
if (!datasetUrl) continue;
|
|
5847
|
+
const isThirdParty = Util.getRootDomain(datasetUrl) !== finalDisplayedUrlRootDomain;
|
|
5848
|
+
if (!isThirdParty) continue;
|
|
5849
|
+
}
|
|
5659
5850
|
|
|
5660
|
-
|
|
5851
|
+
thirdPartyRowEls.push(rowEl);
|
|
5661
5852
|
}
|
|
5662
5853
|
|
|
5663
|
-
return
|
|
5854
|
+
return thirdPartyRowEls;
|
|
5664
5855
|
}
|
|
5665
5856
|
|
|
5666
5857
|
/**
|
|
@@ -5668,7 +5859,10 @@ class ReportUIFeatures {
|
|
|
5668
5859
|
*/
|
|
5669
5860
|
_saveFile(blob) {
|
|
5670
5861
|
const ext = blob.type.match('json') ? '.json' : '.html';
|
|
5671
|
-
const filename =
|
|
5862
|
+
const filename = getLhrFilenamePrefix({
|
|
5863
|
+
finalDisplayedUrl: Util.getFinalDisplayedUrl(this.json),
|
|
5864
|
+
fetchTime: this.json.fetchTime,
|
|
5865
|
+
}) + ext;
|
|
5672
5866
|
if (this._opts.onSaveFileOverride) {
|
|
5673
5867
|
this._opts.onSaveFileOverride(blob, filename);
|
|
5674
5868
|
} else {
|
|
@@ -5728,6 +5922,16 @@ function swapLocale(lhr, requestedLocale) {
|
|
|
5728
5922
|
function registerLocaleData(locale, lhlMessages) {
|
|
5729
5923
|
// Stub function only included for types
|
|
5730
5924
|
}
|
|
5731
|
-
|
|
5925
|
+
|
|
5926
|
+
/**
|
|
5927
|
+
* Returns whether the requestedLocale is registered and available for use
|
|
5928
|
+
* @param {LH.Locale} requestedLocale
|
|
5929
|
+
* @return {boolean}
|
|
5930
|
+
*/
|
|
5931
|
+
function hasLocale(requestedLocale) {
|
|
5932
|
+
// Stub function only included for types
|
|
5933
|
+
return false;
|
|
5934
|
+
}
|
|
5935
|
+
const format = {registerLocaleData, hasLocale};
|
|
5732
5936
|
|
|
5733
5937
|
export { DOM, ReportRenderer, ReportUIFeatures, format, renderReport, swapLocale };
|