sitespeed.io 30.3.0 → 30.4.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.
@@ -24,6 +24,11 @@ jobs:
24
24
  sudo apt-get update
25
25
  sudo apt-get --only-upgrade install google-chrome-stable
26
26
  google-chrome --version
27
+ - name: Install dependencies
28
+ run: |
29
+ python -m pip install --upgrade --user pip
30
+ python -m pip install --user scipy
31
+ python -m pip show scipy
27
32
  - name: Install Firefox
28
33
  uses: browser-actions/setup-firefox@latest
29
34
  #with:
@@ -69,4 +74,6 @@ jobs:
69
74
  - name: Run test with Influx 2.6.1
70
75
  run: bin/sitespeed.js http://127.0.0.1:3001/simple/ -n 1 --influxdb.host 127.0.0.1 --influxdb.port 8087 --influxdb.version 2 --influxdb.organisation sitespeed --influxdb.token sitespeed --xvfb
71
76
  - name: Run Chrome test with config
72
- run: node bin/sitespeed.js --config test/exampleConfig.json http://127.0.0.1:3001/simple/ --xvfb
77
+ run: node bin/sitespeed.js --config test/exampleConfig.json http://127.0.0.1:3001/simple/ --xvfb
78
+ - name: Run Chrome test using compare plugin
79
+ run: node bin/sitespeed.js --compare.id compare --compare.saveBaseline --compare.baselinePath test/ http://127.0.0.1:3001/simple/ --xvfb
package/CHANGELOG.md CHANGED
@@ -1,10 +1,16 @@
1
1
  # CHANGELOG - sitespeed.io (we use [semantic versioning](https://semver.org))
2
2
 
3
+ ## 30.4.0 - 2023-11-27
4
+ ### Fixed
5
+ * Upgrade to Browsretime 19.1.0 with a fix for Geckodriver so that the correct ARM version is installed on Mac Arm machines.
6
+ ### Added
7
+ * The new compare plugin makes it easy to use Mann Whitney U/Wilcox for support to find performance egressions. Read all about the plugin in the [documentation](https://www.sitespeed.io/documentation/sitespeed.io/compare/).
8
+
3
9
  ## 30.3.0 - 2023-11-09
4
10
 
5
11
  ### Added
6
12
  * Upgrade to Browsertime 18.0.0.
7
- * Added suupport to run user journeys with WebPageReplay [#4005](https://github.com/sitespeedio/sitespeed.io/pull/4005).
13
+ * Added support to run user journeys with WebPageReplay [#4005](https://github.com/sitespeedio/sitespeed.io/pull/4005).
8
14
 
9
15
  ### Fixed
10
16
  * Downgrade puppeteer in the +1 container for Lighthouse, thank you [bairov pavel](https://github.com/Amerousful) for PR [#123](https://github.com/sitespeedio/plugin-lighthouse/pull/123).
package/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM sitespeedio/webbrowsers:chrome-119.0-firefox-119.0-edge-119.0
1
+ FROM sitespeedio/webbrowsers:chrome-119.0-firefox-120.0-edge-119.0
2
2
 
3
3
  ARG TARGETPLATFORM=linux/amd64
4
4
 
@@ -44,4 +44,6 @@ RUN echo 'ALL ALL=NOPASSWD: /usr/sbin/tc, /usr/sbin/route, /usr/sbin/ip' > /etc/
44
44
 
45
45
  ENTRYPOINT ["/start.sh"]
46
46
  VOLUME /sitespeed.io
47
+ VOLUME /baseline
48
+
47
49
  WORKDIR /sitespeed.io
package/lib/cli/cli.js CHANGED
@@ -1870,6 +1870,68 @@ export async function parseCommandLine() {
1870
1870
  group: 'API'
1871
1871
  });
1872
1872
 
1873
+ parsed
1874
+ .option('compare.id', {
1875
+ type: 'string',
1876
+ describe:
1877
+ 'The id of the test. Will be used to find the baseline test, that is using the id as a part of the name.',
1878
+ group: 'compare'
1879
+ })
1880
+ .option('compare.baselinePath', {
1881
+ type: 'string',
1882
+ describe:
1883
+ 'Specifies the path to the baseline data file. This file is used as a reference for comparison against the current test data.',
1884
+ group: 'compare'
1885
+ })
1886
+ .option('compare.saveBaseline', {
1887
+ type: 'boolean',
1888
+ default: false,
1889
+ describe:
1890
+ 'Determines whether to save the current test data as the new baseline. Set to true to save the current data as baseline for future comparisons.',
1891
+ group: 'compare'
1892
+ })
1893
+ .option('compare.testType', {
1894
+ describe:
1895
+ 'Selects the statistical test type to be used for comparison. Options are mannwhitneyu for the Mann-Whitney U test and wilcoxon for the Wilcoxon signed-rank test.',
1896
+ choices: ['mannwhitneyu', ' wilcoxon'],
1897
+ default: 'mannwhitneyu',
1898
+ group: 'compare'
1899
+ })
1900
+ .option('compare.alternative', {
1901
+ choices: ['less', ' greater', 'two-sided'],
1902
+ default: 'less',
1903
+ describe:
1904
+ 'Specifies the alternative hypothesis to be tested. Options are less for one-sided test where the first group is expected to be less than the second, greater for one-sided test with the first group expected to be greater, or two-sided for a two-sided test.',
1905
+ group: 'compare'
1906
+ })
1907
+ .option('compare.wilcoxon.correction', {
1908
+ type: 'boolean',
1909
+ describe:
1910
+ 'Enables or disables the continuity correction in the Wilcoxon signed-rank test. Set to true to enable the correction.',
1911
+ default: false,
1912
+ group: 'compare'
1913
+ })
1914
+ .option('compare.wilcoxon.zeroMethod', {
1915
+ choices: ['wilcox', ' pratt', 'zsplit'],
1916
+ describe:
1917
+ 'Specifies the method for handling zero differences in the Wilcoxon test. wilcox discards all zero-difference pairs, pratt includes all, and zsplit splits them evenly among positive and negative ranks.',
1918
+ default: 'zsplit',
1919
+ group: 'compare'
1920
+ })
1921
+ .option('compare.mannwhitneyu.useContinuity', {
1922
+ type: 'boolean',
1923
+ default: false,
1924
+ describe:
1925
+ 'Determines whether to use continuity correction in the Mann-Whitney U test. Set to true to apply the correction.',
1926
+ group: 'compare'
1927
+ })
1928
+ .option('compare.mannwhitneyu.method', {
1929
+ choices: ['auto', ' exact', 'symptotic'],
1930
+ escribe:
1931
+ 'Selects the method for calculating the Mann-Whitney U test. auto automatically selects between exact and asymptotic based on sample size, exact uses the exact distribution of U, and symptotic uses a normal approximation.',
1932
+ default: 'auto',
1933
+ group: 'compare'
1934
+ });
1873
1935
  parsed
1874
1936
  .option('mobile', {
1875
1937
  describe:
@@ -0,0 +1,35 @@
1
+ import fs from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+
4
+ export async function getBaseline(id, compareOptions) {
5
+ try {
6
+ return JSON.parse(
7
+ await fs.readFile(
8
+ resolve(
9
+ join(compareOptions.baselinePath || process.cwd(), `${id}.json`)
10
+ )
11
+ )
12
+ );
13
+ } catch {
14
+ return;
15
+ }
16
+ }
17
+ /*
18
+ async function getBaselineFromInternet(url) {
19
+ try {
20
+ const response = await fetch(url);
21
+ return response.json();
22
+ } catch (error) {
23
+ log.error('Could not fetch', error);
24
+ }
25
+ }
26
+
27
+ async function getBaselineFromFile(path) {}
28
+
29
+ /*
30
+ export async function saveBaseline(json, options) {
31
+
32
+ }*/
33
+ export async function saveBaseline(json, name) {
34
+ return fs.writeFile(resolve(name), JSON.stringify(json));
35
+ }
@@ -0,0 +1,347 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { join } from 'node:path';
3
+ import path from 'node:path';
4
+
5
+ import { execa } from 'execa';
6
+ import { Stats } from 'fast-stats';
7
+ import intel from 'intel';
8
+ import { decimals } from '../../support/helpers/index.js';
9
+ const log = intel.getLogger('sitespeedio.plugin.compare');
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+
13
+ class Metric {
14
+ constructor(name, values) {
15
+ this.name = name;
16
+ this.stats = new Stats().push(values);
17
+ }
18
+
19
+ getName() {
20
+ return this.name;
21
+ }
22
+ getValues() {
23
+ return this.stats.data;
24
+ }
25
+ getStats() {
26
+ return this.stats;
27
+ }
28
+ }
29
+
30
+ export async function runStatisticalTests(data) {
31
+ let extras = '';
32
+ try {
33
+ const { stdout } = await execa(
34
+ process.env.PYTHON || 'python',
35
+ [join(__dirname, 'statistical.py')],
36
+ {
37
+ input: JSON.stringify(data)
38
+ }
39
+ );
40
+ extras = stdout;
41
+ const results = JSON.parse(stdout);
42
+ log.verbose('Result from the python script %j'.results);
43
+ return results;
44
+ } catch (error) {
45
+ log.error(error);
46
+ log.error(extras);
47
+ }
48
+ }
49
+
50
+ export function getStatistics(arrayOfValues) {
51
+ return new Stats().push(arrayOfValues);
52
+ }
53
+
54
+ function getExtras(data) {
55
+ const metrics = {};
56
+ const results = {};
57
+
58
+ for (const run of data.extras) {
59
+ for (const name of Object.keys(run)) {
60
+ if (!metrics[name]) {
61
+ metrics[name] = [];
62
+ }
63
+ metrics[name].push(run[name]);
64
+ }
65
+ }
66
+
67
+ for (const [metricName, values] of Object.entries(metrics)) {
68
+ results[metricName] = new Metric(metricName, values);
69
+ }
70
+
71
+ return results;
72
+ }
73
+
74
+ function getTimings(data) {
75
+ const timingMetrics = {
76
+ ttfb: [],
77
+ loadEventEnd: [],
78
+ firstContentfulPaint: [],
79
+ fullyLoaded: []
80
+ };
81
+
82
+ for (const run of data.browserScripts) {
83
+ timingMetrics['ttfb'].push(run.timings.ttfb);
84
+ timingMetrics['loadEventEnd'].push(run.timings.loadEventEnd);
85
+ timingMetrics['firstContentfulPaint'].push(
86
+ run.timings.paintTiming['first-contentful-paint']
87
+ );
88
+ }
89
+
90
+ for (const run of data.fullyLoaded) {
91
+ timingMetrics['fullyLoaded'].push(run);
92
+ }
93
+
94
+ const results = {};
95
+ for (const [metricName, values] of Object.entries(timingMetrics)) {
96
+ if (!results.timings) {
97
+ results.timings = {};
98
+ }
99
+ results.timings[metricName] = new Metric(`${metricName}`, values);
100
+ }
101
+ return results;
102
+ }
103
+
104
+ function getUserTimings(data) {
105
+ const userTimingMetrics = {};
106
+ for (const run of data.browserScripts) {
107
+ if (run.timings.userTimings) {
108
+ const { marks, measures } = run.timings.userTimings;
109
+
110
+ for (const mark of marks) {
111
+ if (!userTimingMetrics[mark.name]) {
112
+ userTimingMetrics[mark.name] = [];
113
+ }
114
+ userTimingMetrics[mark.name].push(decimals(mark.startTime));
115
+ }
116
+
117
+ for (const measure of measures) {
118
+ if (!userTimingMetrics[measure.name]) {
119
+ userTimingMetrics[measure.name] = [];
120
+ }
121
+ userTimingMetrics[measure.name].push(decimals(measure.startTime));
122
+ }
123
+ }
124
+ }
125
+
126
+ const results = {};
127
+ for (const [metricName, values] of Object.entries(userTimingMetrics)) {
128
+ if (!results.userTimings) {
129
+ results.userTimings = {};
130
+ }
131
+ results.userTimings[metricName] = new Metric(`${metricName}`, values);
132
+ }
133
+ return results;
134
+ }
135
+
136
+ function getElementTimings(data) {
137
+ const elementTimingMetrics = {};
138
+ for (const run of data.browserScripts) {
139
+ if (run.timings.elementTimings) {
140
+ for (const [name, timing] of Object.entries(run.timings.elementTimings)) {
141
+ if (!elementTimingMetrics[name]) {
142
+ elementTimingMetrics[name] = [];
143
+ }
144
+ elementTimingMetrics[name].push(timing.renderTime);
145
+ }
146
+ }
147
+ }
148
+
149
+ const results = {};
150
+ for (const [metricName, values] of Object.entries(elementTimingMetrics)) {
151
+ if (!results.elementTimings) {
152
+ results.elementTimings = {};
153
+ }
154
+ results.elementTimings[metricName] = new Metric(`${metricName}`, values);
155
+ }
156
+ return results;
157
+ }
158
+
159
+ function getGoogleWebVitals(data) {
160
+ const googleWebVitalsMetrics = {};
161
+ for (const run of data.googleWebVitals) {
162
+ for (const [name, value] of Object.entries(run)) {
163
+ if (!googleWebVitalsMetrics[name]) {
164
+ googleWebVitalsMetrics[name] = [];
165
+ }
166
+ googleWebVitalsMetrics[name].push(value);
167
+ }
168
+ }
169
+
170
+ const results = {};
171
+ for (const [metricName, values] of Object.entries(googleWebVitalsMetrics)) {
172
+ if (!results.googleWebVitals) {
173
+ results.googleWebVitals = {};
174
+ }
175
+ results.googleWebVitals[metricName] = new Metric(`${metricName}`, values);
176
+ }
177
+ return results;
178
+ }
179
+
180
+ function getVisualMetrics(data) {
181
+ const DO_NOT_USE = new Set([
182
+ 'VisualProgress',
183
+ 'videoRecordingStart',
184
+ 'VisualComplete85',
185
+ 'VisualComplete95',
186
+ 'VisualComplete99'
187
+ ]);
188
+
189
+ const visualMetrics = {};
190
+ for (const run of data.visualMetrics) {
191
+ for (const [name, value] of Object.entries(run)) {
192
+ if (!DO_NOT_USE.has(name)) {
193
+ if (!visualMetrics[name]) {
194
+ visualMetrics[name] = [];
195
+ }
196
+ visualMetrics[name].push(value);
197
+ }
198
+ }
199
+ }
200
+
201
+ const results = {};
202
+ for (const [metricName, values] of Object.entries(visualMetrics)) {
203
+ if (!results.visualMetrics) {
204
+ results.visualMetrics = {};
205
+ }
206
+ results.visualMetrics[metricName] = new Metric(`${metricName}`, values);
207
+ }
208
+ return results;
209
+ }
210
+
211
+ function getCDPPerformance(data) {
212
+ const metricsToKeep = new Set([
213
+ 'JSEventListeners',
214
+ 'LayoutCount',
215
+ 'RecalcStyleCount',
216
+ 'LayoutDuration',
217
+ 'RecalcStyleDuration',
218
+ 'ScriptDuration',
219
+ 'V8CompileDuration',
220
+ 'TaskDuration',
221
+ 'TaskOtherDuration',
222
+ 'JSHeapUsedSize'
223
+ ]);
224
+ const cdpPerformance = {};
225
+ for (const run of data.cdp.performance) {
226
+ for (const name of Object.keys(run)) {
227
+ if (metricsToKeep.has(name)) {
228
+ if (!cdpPerformance[name]) {
229
+ cdpPerformance[name] = [];
230
+ }
231
+ cdpPerformance[name].push(decimals(run[name]));
232
+ }
233
+ }
234
+ }
235
+
236
+ // Convert to Metric objects
237
+ const results = {};
238
+ for (const [metricName, values] of Object.entries(cdpPerformance)) {
239
+ if (!results.cdp) {
240
+ results.cdp = {};
241
+ }
242
+ results.cdp[metricName] = new Metric(`${metricName}`, values);
243
+ }
244
+ return results;
245
+ }
246
+
247
+ function getCPU(data) {
248
+ const cpuMetrics = {
249
+ tasks: [],
250
+ totalDuration: [],
251
+ lastLongTask: [],
252
+ beforeFirstContentfulPaint: [],
253
+ beforeLargestContentfulPaint: []
254
+ };
255
+
256
+ for (const run of data.cpu) {
257
+ const longTasks = run.longTasks;
258
+ cpuMetrics['tasks'].push(longTasks['tasks']);
259
+ cpuMetrics['totalDuration'].push(longTasks['totalDuration']);
260
+ cpuMetrics['lastLongTask'].push(longTasks['lastLongTask']);
261
+ cpuMetrics['beforeFirstContentfulPaint'].push(
262
+ longTasks['beforeFirstContentfulPaint'].totalDuration
263
+ );
264
+ cpuMetrics['beforeLargestContentfulPaint'].push(
265
+ longTasks['beforeLargestContentfulPaint'].totalDuration
266
+ );
267
+ }
268
+
269
+ const isEmpty = Object.values(cpuMetrics).every(arr => arr.length === 0);
270
+ if (isEmpty) {
271
+ return {}; // Return an empty object if no data
272
+ }
273
+
274
+ const results = {};
275
+ for (const [metricName, values] of Object.entries(cpuMetrics)) {
276
+ if (!results.cpu) {
277
+ results.cpu = {};
278
+ }
279
+ results.cpu[metricName] = new Metric(`${metricName}`, values);
280
+ }
281
+ return results;
282
+ }
283
+
284
+ function getRenderBlocking(data) {
285
+ const renderBlockingMetrics = {
286
+ beforeFCPms: [],
287
+ beforeLCPms: [],
288
+ beforeFCPelements: [],
289
+ beforeLCPelements: []
290
+ };
291
+
292
+ for (const run of data.renderBlocking) {
293
+ renderBlockingMetrics['beforeFCPms'].push(
294
+ run.recalculateStyle.beforeFCP.durationInMillis
295
+ );
296
+ renderBlockingMetrics['beforeFCPelements'].push(
297
+ run.recalculateStyle.beforeFCP.elements
298
+ );
299
+ renderBlockingMetrics['beforeLCPms'].push(
300
+ run.recalculateStyle.beforeLCP.durationInMillis
301
+ );
302
+ renderBlockingMetrics['beforeLCPelements'].push(
303
+ run.recalculateStyle.beforeLCP.elements
304
+ );
305
+ }
306
+
307
+ // Check if all arrays in renderBlockingMetrics are empty
308
+ const isEmpty = Object.values(renderBlockingMetrics).every(
309
+ arr => arr.length === 0
310
+ );
311
+ if (isEmpty) {
312
+ return {}; // Return an empty object if no data
313
+ }
314
+
315
+ const results = {};
316
+ for (const [metricName, values] of Object.entries(renderBlockingMetrics)) {
317
+ if (!results.renderBlocking) {
318
+ results.renderBlocking = {};
319
+ }
320
+ results.renderBlocking[metricName] = new Metric(`${metricName}`, values);
321
+ }
322
+
323
+ return results;
324
+ }
325
+
326
+ export function getMetrics(data) {
327
+ const userTimings = getUserTimings(data);
328
+ const elementTimings = getElementTimings(data);
329
+ const visualMetrics = getVisualMetrics(data);
330
+ const rb = getRenderBlocking(data);
331
+ const gWV = getGoogleWebVitals(data);
332
+ const cdp = getCDPPerformance(data);
333
+ const cpu = getCPU(data);
334
+ const timings = getTimings(data);
335
+ const extras = getExtras(data);
336
+ return {
337
+ ...extras,
338
+ ...timings,
339
+ ...cpu,
340
+ ...cdp,
341
+ ...visualMetrics,
342
+ ...gWV,
343
+ ...rb,
344
+ ...elementTimings,
345
+ ...userTimings
346
+ };
347
+ }
@@ -0,0 +1,229 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve, join } from 'node:path';
4
+
5
+ import { SitespeedioPlugin } from '@sitespeed.io/plugin';
6
+ import intel from 'intel';
7
+ import merge from 'lodash.merge';
8
+ import dayjs from 'dayjs';
9
+
10
+ import { throwIfMissing } from '../../support/util.js';
11
+ import { getStatistics, runStatisticalTests, getMetrics } from './helper.js';
12
+ import { getBaseline, saveBaseline } from './baseline.js';
13
+
14
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
15
+
16
+ const log = intel.getLogger('sitespeedio.plugin.compare');
17
+ const defaultConfig = {};
18
+
19
+ const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
20
+
21
+ const DEFAULT_METRICS_PAGESUMMARY = [
22
+ 'metrics.timings.*.statisticalTestU',
23
+ 'metrics.cpu.*.statisticalTestU',
24
+ 'metrics.cdp.*.statisticalTestU',
25
+ 'metrics.visualMetrics.*.statisticalTestU',
26
+ 'metrics.googleWebVitals.*.statisticalTestU',
27
+ 'metrics.renderBlocking.*.statisticalTestU',
28
+ 'metrics.elementTimings.*.statisticalTestU',
29
+ 'metrics.userTimings.*.statisticalTestU',
30
+ 'metrics.extras.*.statisticalTestU'
31
+ ];
32
+
33
+ export default class ComparePlugin extends SitespeedioPlugin {
34
+ constructor(options, context, queue) {
35
+ super({ name: 'compare', options, context, queue });
36
+ }
37
+
38
+ async open(context, options) {
39
+ this.page = 0;
40
+ this.make = context.messageMaker('compare').make;
41
+ this.compareOptions = merge({}, defaultConfig, options.compare);
42
+ this.options = options;
43
+ throwIfMissing(options.compare, ['id'], 'compare');
44
+ this.pug = readFileSync(resolve(__dirname, 'pug', 'index.pug'), 'utf8');
45
+ log.info(
46
+ 'Starting the compare plugin.' +
47
+ (this.compareOptions.saveBaseline
48
+ ? ' Will save this test as the baseline'
49
+ : '')
50
+ );
51
+ if (options.browsertime.iterations < 20) {
52
+ log.warning(
53
+ 'You should use 20+ iterations to get statistical significant data'
54
+ );
55
+ }
56
+ context.filterRegistry.registerFilterForType(
57
+ DEFAULT_METRICS_PAGESUMMARY,
58
+ 'compare.pageSummary'
59
+ );
60
+ }
61
+ async processMessage(message) {
62
+ switch (message.type) {
63
+ case 'sitespeedio.setup': {
64
+ super.sendMessage('compare.setup');
65
+ // Add the HTML pugs
66
+ super.sendMessage('html.pug', {
67
+ id: 'compare',
68
+ name: 'Compare',
69
+ pug: this.pug,
70
+ type: 'pageSummary'
71
+ });
72
+ break;
73
+ }
74
+ case 'browsertime.pageSummary': {
75
+ this.page++;
76
+ const baseline = await getBaseline(
77
+ this.options.compare.id + '-' + this.page,
78
+ this.compareOptions
79
+ );
80
+
81
+ if (baseline) {
82
+ if (
83
+ baseline &&
84
+ this.options.browsertime.iterations !== baseline.timestamps.length
85
+ )
86
+ log.warning(
87
+ 'The baseline test has %s runs and you current have %s. You should make sure you test the same amount of runs',
88
+ baseline.timestamps.length,
89
+ this.options.browsertime.iterations
90
+ );
91
+ log.info(
92
+ 'Got a baseline:' + this.options.compare.id + '-' + this.page
93
+ );
94
+ const newMetrics = getMetrics(message.data);
95
+ const baselineMetrics = getMetrics(baseline);
96
+ const metricsInputData = {
97
+ options: {
98
+ test_type: this.compareOptions.testType,
99
+ alternative: this.compareOptions.alternative
100
+ },
101
+ metrics: {}
102
+ };
103
+
104
+ if (this.compareOptions.testType === 'mannwhitneyu') {
105
+ metricsInputData.options.use_continuity =
106
+ this.compareOptions.mannwhitneyu.useContinuity;
107
+ metricsInputData.options.method =
108
+ this.compareOptions.mannwhitneyu.method;
109
+ metricsInputData.options.nan_policy = 'omit';
110
+ } else if (this.compareOptions.testType === 'wilcoxon') {
111
+ metricsInputData.options.correction =
112
+ this.compareOptions.wilcoxon.correction;
113
+ metricsInputData.options.zero_method =
114
+ this.compareOptions.wilcoxon.zeroMethod;
115
+ }
116
+
117
+ for (let group in newMetrics) {
118
+ if (baselineMetrics[group]) {
119
+ metricsInputData.metrics[group] = {};
120
+ for (let metricName in newMetrics[group]) {
121
+ // Ensure both current and baseline metrics are available
122
+ if (
123
+ baselineMetrics[group][metricName] &&
124
+ newMetrics[group][metricName]
125
+ ) {
126
+ // Directly access the Metric instance
127
+ const currentMetric = newMetrics[group][metricName];
128
+ const baselineMetric = baselineMetrics[group][metricName];
129
+
130
+ // Ensure these are indeed Metric instances
131
+ const currentStats = getStatistics(currentMetric.getValues());
132
+ const baselineStats = getStatistics(
133
+ baselineMetric.getValues()
134
+ );
135
+ metricsInputData.metrics[group][metricName] = {
136
+ sample1: baselineStats.data,
137
+ sample2: currentStats.data
138
+ };
139
+ } else {
140
+ log.info(
141
+ `Skipping ${group}.${metricName} as it's not present in both current and baseline metrics.`
142
+ );
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ const results = await runStatisticalTests(metricsInputData);
149
+ const finalResult = {};
150
+ for (let group in results) {
151
+ finalResult[group] = {};
152
+ for (let metricName in results[group]) {
153
+ const result = results[group][metricName];
154
+ // Again, accessing the metricName within the group
155
+ const currentStats = getStatistics(
156
+ newMetrics[group][metricName].getValues()
157
+ );
158
+ const baselineStats = getStatistics(
159
+ baselineMetrics[group][metricName].getValues()
160
+ );
161
+
162
+ finalResult[group][metricName] = {
163
+ current: {
164
+ stdev: currentStats.stddev(),
165
+ mean: currentStats.amean(),
166
+ median: currentStats.median(),
167
+ values: currentStats.data
168
+ },
169
+ baseline: {
170
+ stdev: baselineStats.stddev(),
171
+ mean: baselineStats.amean(),
172
+ median: baselineStats.median(),
173
+ values: baselineStats.data
174
+ },
175
+ statisticalTestU: result['p-value']
176
+ };
177
+ }
178
+ }
179
+ const meta = {
180
+ baseline: {
181
+ timestamp: dayjs(baseline.info.timestamp).format(TIME_FORMAT),
182
+ url: baseline.info.url,
183
+ alias: baseline.info.alias
184
+ },
185
+ current: {
186
+ timestamp: dayjs(message.data.info.timestamp).format(TIME_FORMAT),
187
+ url: message.data.info.url,
188
+ alias: message.data.info.alias
189
+ },
190
+ testOptions: this.compareOptions,
191
+ iterations: this.options.browsertime.iterations
192
+ };
193
+
194
+ if (this.compareOptions.saveBaseline) {
195
+ await saveBaseline(
196
+ message.data,
197
+ join(
198
+ this.compareOptions.baselinePath || process.cwd(),
199
+ `${this.options.compare.id}-${this.page}.json`
200
+ )
201
+ );
202
+ }
203
+
204
+ super.sendMessage(
205
+ 'compare.pageSummary',
206
+ { metrics: finalResult, meta },
207
+ {
208
+ url: message.url,
209
+ group: message.group,
210
+ runTime: message.runTime
211
+ }
212
+ );
213
+ } else {
214
+ if (this.compareOptions.saveBaseline) {
215
+ await saveBaseline(
216
+ message.data,
217
+ join(
218
+ this.compareOptions.baselinePath || process.cwd(),
219
+ `${this.options.compare.id}-${this.page}.json`
220
+ )
221
+ );
222
+ }
223
+ }
224
+
225
+ break;
226
+ }
227
+ }
228
+ }
229
+ }
@@ -0,0 +1,135 @@
1
+ - const compare = pageInfo.data.compare.pageSummary;
2
+
3
+ h1 Compare
4
+
5
+ if compare.meta.iterations < 21
6
+ p.error
7
+ | Warning: The number of iterations (#{compare.meta.iterations}) is less than the
8
+ a(href='https://en.wikipedia.org/wiki/Mann–Whitney_U_test#Calculations') recommended minimum of 21.
9
+ | This may impact the reliability of the statistical comparison.
10
+
11
+ p
12
+ | In this web performance comparison, statistical tests are employed to analyze the significance of performance changes between baseline and current measurements. The Mann-Whitney U test, ideal for comparing independent samples, is used when analyzing different web pages or different conditions (e.g., comparing load times of a page with and without a new optimization). The Wilcoxon signed-rank test is applied to related or paired samples, such as comparing the response times of the same website before and after applying a specific optimization technique.
13
+
14
+ h2 Settings
15
+ p
16
+ | The test conducted in this comparison is the #{compare.meta.testOptions.testType} test. The alternative hypothesis used for this test is "#{compare.meta.testOptions.alternative}".
17
+ if compare.meta.testOptions.testType === 'mannwhitneyu'
18
+ | For more information on the settings of the Mann-Whitney U test, please refer to the
19
+ a(href='https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mannwhitneyu.html') official documentation.
20
+ | The test was configured to use continuity (set to #{compare.meta.testOptions.mannwhitneyu.useContinuity}) and the method chosen was "#{compare.meta.testOptions.mannwhitneyu.method}".
21
+ else if compare.meta.testOptions.testType === 'wilcoxon'
22
+ | For more information on the settings of the Wilcoxon test, please refer to the
23
+ a(href='https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wilcoxon.html') official documentation.
24
+ | For this test, a correction parameter (#{compare.meta.testOptions.wilcoxon.correction ? 'enabled' : 'disabled'}) was applied to adjust for small sample sizes.
25
+
26
+
27
+ p
28
+ | The baseline test
29
+ if compare.meta.baseline.alias
30
+ a(href=compare.meta.baseline.url) #{compare.meta.baseline.alias}
31
+ else
32
+ a(href=compare.meta.baseline.url) #{compare.meta.baseline.url}
33
+ | was conducted at #{compare.meta.baseline.timestamp} and the current test
34
+ if compare.meta.current.alias
35
+ a(href=compare.meta.current.url) #{compare.meta.current.alias}
36
+ else
37
+ a(href=compare.meta.current.url) #{compare.meta.current.url}
38
+ | was conducted at #{compare.meta.current.timestamp}.
39
+
40
+
41
+ h2 Comparison Data
42
+
43
+ table
44
+ thead
45
+ tr
46
+ th Metric Name
47
+ th Score #{compare.meta.testOptions.testType}
48
+ th Baseline mean
49
+ th Current mean
50
+ th Baseline median
51
+ th Current median
52
+ th Baseline Std Dev
53
+ th Current Std Dev
54
+ th Significant Change?
55
+
56
+ tbody
57
+ each metricGroup, groupName in compare.metrics
58
+ each values, metricName in metricGroup
59
+ tr
60
+ td
61
+ b #{groupName + '.' + metricName}
62
+ if values.statisticalTestU === "N/A"
63
+ td N/A
64
+ else
65
+ td #{h.decimals(values.statisticalTestU)}
66
+ td #{h.decimals(values.baseline.mean)}
67
+ td #{h.decimals(values.current.mean)}
68
+ td #{h.decimals(values.baseline.median)}
69
+ td #{h.decimals(values.current.median)}
70
+ td #{h.decimals(values.baseline.stdev)}
71
+ td #{h.decimals(values.current.stdev)}
72
+ if values.statisticalTestU === "N/A"
73
+ td No Test Conducted
74
+ else
75
+ td #{values.statisticalTestU < 0.05 ? 'Yes' : 'No'}
76
+
77
+ h2 Graphs
78
+
79
+ each metricGroup, groupName in compare.metrics
80
+ each values, metricName in metricGroup
81
+ - var fullMetricName = groupName + '.' + metricName
82
+ - var metricId = fullMetricName.replace(/\./g, '_')
83
+ h3 #{fullMetricName}
84
+ .ct-chart(id=`chart-${metricId}`)
85
+ .ct-legend
86
+ span.ct-legend-item
87
+ i(style='background-color: #468847') &nbsp;
88
+ | Baseline: [#{values.baseline.values.join(', ')}]
89
+ .ct-legend
90
+ span.ct-legend-item
91
+ i(style='background-color: #c09853;') &nbsp;
92
+ | Current: [#{values.current.values.join(', ')}]
93
+ script(type='text/javascript').
94
+ document.addEventListener("DOMContentLoaded", function() {
95
+ var baselineData = !{JSON.stringify(values.baseline.values)};
96
+ var currentData = !{JSON.stringify(values.current.values)};
97
+ var metricId = '#{metricId}';
98
+ var overlapSeriesName = 'Overlap';
99
+
100
+ var chartData = {
101
+ series: [
102
+ {
103
+ name: 'Baseline',
104
+ data: baselineData.map((value, index) => ({ x: index + 1, y: value })),
105
+ className: 'baseline-series'
106
+ },
107
+ {
108
+ name: 'Current',
109
+ data: currentData.map((value, index) => ({ x: index + 1, y: value })),
110
+ className: 'current-series'
111
+ },
112
+ {
113
+ name: overlapSeriesName,
114
+ data: baselineData.map((value, index) => ({ x: index + 1, y: currentData[index] === value ? value : null }))
115
+ }
116
+ ]
117
+ };
118
+ var optionsApa = {
119
+ showLine: false,
120
+ axisX: {
121
+
122
+ },
123
+ axisY: {
124
+
125
+ }
126
+ };
127
+ var chart = new Chartist.Line(`#chart-${metricId}`, chartData, optionsApa);
128
+
129
+ // Take care of series that has the same value
130
+ chart.on('draw', function(data) {
131
+ if(data.type === 'point' && data.series.name === overlapSeriesName && data.value.y !== null) {
132
+ data.element._node.setAttribute('style', 'stroke: #59922b; stroke-width: 20px;');
133
+ }
134
+ });
135
+ });
@@ -0,0 +1,37 @@
1
+ import sys
2
+ import json
3
+ from scipy.stats import wilcoxon, mannwhitneyu
4
+
5
+ def has_variability(sample):
6
+ """Check if the sample has more than one unique value."""
7
+ return len(set(sample)) > 1
8
+
9
+ def perform_test(test_type, sample1, sample2, **kwargs):
10
+ """Perform the statistical test based on the test type."""
11
+ if not has_variability(sample1) or not has_variability(sample2):
12
+ return None, "No variability"
13
+
14
+ if test_type == 'wilcoxon':
15
+ return wilcoxon(sample1, sample2, **kwargs)
16
+ elif test_type == 'mannwhitneyu':
17
+ return mannwhitneyu(sample1, sample2, **kwargs)
18
+ else:
19
+ raise ValueError("Invalid test type. Choose 'wilcoxon' or 'mannwhitneyu'.")
20
+
21
+ input_data = json.loads(sys.stdin.read())
22
+ options = input_data['options']
23
+ test_type = options.pop('test_type')
24
+ final_results = {}
25
+
26
+ # Iterate over each metric group in the metrics dictionary
27
+ for group_name, metrics in input_data['metrics'].items():
28
+ group_results = {}
29
+ for metric_name, metric_data in metrics.items():
30
+ stat, p = perform_test(test_type, metric_data['sample1'], metric_data['sample2'], **options)
31
+ if p == "No variability":
32
+ group_results[metric_name] = {'statistic': "N/A", 'p-value': "N/A"}
33
+ else:
34
+ group_results[metric_name] = {'statistic': stat, 'p-value': p}
35
+ final_results[group_name] = group_results
36
+
37
+ print(json.dumps(final_results))
@@ -18,7 +18,7 @@ function keyPathFromMessage(message, options, includeQueryParameters, alias) {
18
18
 
19
19
  // always have browser and connectivity in Browsertime and related tools
20
20
  if (
21
- /(^pagexray|^coach|^browsertime|^largestassets|^slowestassets|^aggregateassets|^domains|^thirdparty|^axe|^sustainable)/.test(
21
+ /(^pagexray|^coach|^browsertime|^largestassets|^slowestassets|^aggregateassets|^domains|^thirdparty|^axe|^compare|^sustainable)/.test(
22
22
  message.type
23
23
  )
24
24
  ) {
@@ -1 +1 @@
1
- /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*,:after,:before{box-sizing:border-box}ol,ul{margin-bottom:2.5rem}ul{margin-top:0;padding-left:0;list-style:circle inside}ul ol,ul ul{margin:1.5rem 0 1.5rem 3rem;font-size:.9rem}ol{margin-top:0;padding-left:0;list-style:decimal inside}ol ol,ol ul{margin:1.5rem 0 1.5rem 3rem;font-size:.9rem}li{margin-bottom:0}figure{margin-bottom:2.5rem}footer{text-align:center}hr{margin-top:3rem;margin-bottom:3.5rem;border-width:0;border-top:1px solid #e1e1e1}.container:after,.row:after{content:"";display:table;clear:both}.container{position:relative;width:100%;max-width:1140px;margin:0 auto;padding:0 20px;box-sizing:border-box}.column,.columns{width:100%;float:left;box-sizing:border-box}@media (min-width:400px){.container{width:85%;padding:0}}@media (min-width:550px){.container{width:80%}.column,.columns{margin-left:4%}.column:first-child,.columns:first-child{margin-left:0}.one.column,.one.columns{width:4.6666666667%}.two.columns{width:13.3333333333%}.three.columns{width:22%}.four.columns{width:30.6666666667%}.five.columns{width:39.3333333333%}.six.columns{width:48%}.seven.columns{width:56.6666666667%}.eight.columns{width:65.3333333333%}.nine.columns{width:74%}.ten.columns{width:82.6666666667%}.eleven.columns{width:91.3333333333%}.twelve.columns{width:100%;margin-left:0}.one-third.column{width:30.6666666667%}.two-thirds.column{width:65.3333333333%}.one-half.column{width:48%}.offset-by-one.column,.offset-by-one.columns{margin-left:8.6666666667%}.offset-by-two.column,.offset-by-two.columns{margin-left:17.3333333333%}.offset-by-three.column,.offset-by-three.columns{margin-left:26%}.offset-by-four.column,.offset-by-four.columns{margin-left:34.6666666667%}.offset-by-five.column,.offset-by-five.columns{margin-left:43.3333333333%}.offset-by-six.column,.offset-by-six.columns{margin-left:52%}.offset-by-seven.column,.offset-by-seven.columns{margin-left:60.6666666667%}.offset-by-eight.column,.offset-by-eight.columns{margin-left:69.3333333333%}.offset-by-nine.column,.offset-by-nine.columns{margin-left:78%}.offset-by-ten.column,.offset-by-ten.columns{margin-left:86.6666666667%}.offset-by-eleven.column,.offset-by-eleven.columns{margin-left:95.3333333333%}.offset-by-one-third.column,.offset-by-one-third.columns{margin-left:34.6666666667%}.offset-by-two-thirds.column,.offset-by-two-thirds.columns{margin-left:69.3333333333%}.offset-by-one-half.column,.offset-by-one-half.columns{margin-left:52%}}@media (min-width:1550px){.container{max-width:1400px;font-size:1.2em}}@media (min-width:1900px){.container{max-width:1800px;font-size:1.4em}}table{width:100%;margin-bottom:2.5rem;border-collapse:separate;border-spacing:1px;background-color:#e1e1e1}td,th{padding:4px 1rem;vertical-align:top;text-align:left}td:first-child,th:first-child{padding-left:1rem}td:last-child,th:last-child{padding-right:1rem}tr:nth-child(odd){background:#fafafa}tr:nth-child(2n){background:#fff}tr.odd{background:#fafafa}tr.even{background:#fff}th{background:#f1fbff}td.number,th.number{text-align:right}td.right{text-align:right}td.url{overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;-moz-hyphens:auto;hyphens:auto}td.assetsurl{max-width:400px}td.pagesurl{max-width:350px}td.offendingurl{max-width:900px}td.break{word-break:break-all}td.extraheader{font-weight:700;background:#f5f5f5}@media only screen and (max-width:800px){.responsive table,.responsive tbody,.responsive td,.responsive th,.responsive thead,.responsive tr{display:block}.responsive tr.u-hideable{display:none}.responsive thead tr{position:absolute;top:-9999px;left:-9999px}.responsive tr{border:2px solid #e1e1e1}.responsive td{border:none;border-bottom:1px solid #e1e1e1;position:relative;padding-left:50%;white-space:normal;text-align:left;max-width:none}.responsive td:before{position:absolute;top:6px;left:6px;width:30%;padding-right:10px;white-space:nowrap;text-align:left;font-weight:700}.responsive td:before{content:attr(data-title)}.responsive td.url.offendingurl{word-break:break-all;padding-left:2px}.hidden-small{display:none}}html{font-size:100%}body{font-size:1em;line-height:1.6;font-weight:400;font-family:-apple-system,BlinkMacSystemFont,"Helvetica Neue",Helvetica,Arial,sans-serif;color:#222}h1,h2,h3,h4,h5,h6{margin-top:1rem;margin-bottom:2rem;font-weight:300}h1{font-size:3rem;line-height:1.2}h2{font-size:2.8rem;line-height:1.25}h3{font-size:2.6rem;line-height:1.3}h4{font-size:2.4rem;line-height:1.35}h5{font-size:1.8rem;line-height:1.5}h6{font-size:1.5rem;line-height:1.6;letter-spacing:0}p{margin-top:0}a{color:#0095d2}a:hover{color:#00719f}blockquote,dl,p,pre{margin-bottom:2.5rem}code{padding:.2rem .5rem;margin:0 .2rem;font-size:.9rem;white-space:nowrap;background:#fafafa;border:1px solid #e1e1e1;border-radius:4px}pre>code{display:block;padding:1rem 1.5rem;white-space:pre;overflow:auto}.u-full-width{width:100%}.u-max-full-width{max-width:100%}.u-pull-right{float:right}.u-pull-left{float:left}.u-cf{content:"";display:table;clear:both}.u-hideable{display:none}.button{display:inline-block;height:38px;padding:0 30px;margin-bottom:1rem;color:#222;text-align:center;font-size:80%;font-weight:600;line-height:38px;letter-spacing:.1rem;text-transform:uppercase;text-decoration:none;white-space:nowrap;background-color:transparent;border-radius:4px;border:1px solid #e1e1e1;cursor:pointer}.button:active,.button:focus,.button:hover{color:#222;border-color:#aeaeae;outline:0}.button--primary{color:#fff;background-color:#0095d2;border-color:#0095d2}.button--primary:active,.button--primary:focus,.button--primary:hover{color:#fff;background-color:#0087be;border-color:#0087be}.button-download{color:#fff;background-color:#ec971f;border-color:#eb9316;padding:0 10px;margin-right:1rem}.button-download:active,.button-download:focus,.button-download:hover{color:#fff;background-color:#e38d13;border-color:#da8813}.navgrid{width:100%;min-width:0;margin-left:0;margin-right:0;padding-left:0;padding-right:10px}.nav{background:#0095d2}.nav ul{list-style:none;text-align:center;padding:0;margin:0;background-color:#0095d2}.nav li{line-height:40px;height:40px;border-bottom:none;margin-bottom:0}.nav a{text-decoration:none;color:#fff;display:block}.nav a:hover{background-color:#0073b0}.nav a.active{background-color:#0073b0;color:#fff;cursor:default}.logo{text-align:center;background-color:#0095d2}.navbar-brand{padding:0;font-size:18px;max-width:250px}@media screen and (min-width:820px){body{padding-top:50px}.navgrid{width:100%;max-width:1140px;min-width:755px;margin:0 auto;overflow:hidden}.nav{height:50px;width:100%;z-index:1000;position:fixed;top:0}.navbar-brand{padding:0;font-size:18px;float:left;max-width:250px}.nav{z-index:10;top:0;background-color:#0095d2}.nav a{padding-left:20px;padding-right:20px}.nav li{border-bottom:none;height:50px;line-height:50px;float:left;display:inline-block;margin-right:0}.nav a{text-decoration:none;color:#fff;display:block}.nav ul{list-style:none;text-align:center;padding:0;margin:0;background-color:#0095d2}:target:before{content:"";display:block;height:50px;margin:-50px 0 0}}@media (min-width:1550px){.nav li{font-size:1.2em}}@media (min-width:1900px){.nav li{font-size:1.4em}}table[data-sortable] th[data-sorted=true]{color:#3a87ad;background:#d9edf7;border-bottom-color:#bce8f1}table[data-sortable] th:not([data-sortable=false]){cursor:pointer;color:#222;text-decoration:underline}.summarybox{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.summarybox.ok{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.summarybox.warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.summarybox.error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.summarybox.info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad}a.summaries{text-decoration:none}.summarynumber{font-size:2rem;line-height:1;font-weight:700}.summarysmall{font-size:1rem;line-height:1}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.normal{font-size:100%}.ok{background-color:#468847}.warning{background-color:#f0ad4e}.error{background-color:#d9534f}.info{background-color:#0095d2}ul.menu{list-style:none;font-size:125%;text-transform:uppercase}.errors{margin-bottom:1.333em;background:#ffb6c1;padding-left:1em}.subtableheader{background:#ffe3eb}.large{font-size:1.333rem;line-height:1.8rem}.hidden-small{display:inline}.url{overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;-moz-hyphens:auto;hyphens:auto}/*! github.com/micmro/PerfCascade Version:2.11.0 (24/11/2021) */.water-fall-chart{width:100%;overflow:visible;font-size:12px;line-height:1em}.water-fall-chart *{box-sizing:border-box}.water-fall-chart button{cursor:pointer}.water-fall-holder{fill:#ccc}.water-fall-chart .left-fixed-holder{overflow:visible}.water-fall-chart .marker-holder{width:100%}.water-fall-chart .line-label-holder{cursor:pointer}.water-fall-chart .line-holder{stroke-width:1;stroke:#ccc;stroke-opacity:0.5;transition:all 60ms}.water-fall-chart .line-holder .line-mark{fill:#69009e;opacity:.01;stroke-width:0;transition:all 60ms}.water-fall-chart .line-holder.active{stroke:#69009e;stroke-width:2;stroke-opacity:1}.water-fall-chart .line-holder.active .line-mark{opacity:.4}.water-fall-chart .type-onload .line-holder{stroke:#c0c0ff}.water-fall-chart .type-oncontentload .line-holder{stroke:#d888df}.water-fall-chart .labels{width:100%}.water-fall-chart .labels .inner-label{pointer-events:none}.water-fall-chart .time-block.active{opacity:.8}.water-fall-chart .line-end,.water-fall-chart .line-start{display:none;stroke-width:1;stroke-opacity:0.5;stroke:#000}.water-fall-chart .line-end.active,.water-fall-chart .line-start.active{display:block}.left-fixed-holder .label-full-bg{fill:#fff;opacity:.9}.time-scale line{stroke:#0cc;stroke-width:1}.time-scale line.sub-second-line{stroke:#ccc;opacity:.75;stroke-width:.5}.time-scale text{font-weight:700}.row-item{cursor:pointer}.row-item .even{fill:#ccc;opacity:.05}.row-item .odd{fill:#000;opacity:.05}.row-item:hover .even,.row-item:hover .odd{fill:#000;opacity:.1}.row-item:focus{outline:solid 1.5px #aaa;outline-offset:-1.5px}.row-item:focus .even,.row-item:focus .odd{fill:#000;opacity:.2}.row-item .rect-holder text{fill:#aaa}.row-item.status0 .even,.row-item.status5xx .even{fill:#f66}.row-item.status0 .odd,.row-item.status5xx .odd{fill:#f00}.row-item.status4xx .even{fill:#c33}.row-item.status4xx .odd{fill:#c00}.row-item.status3xx .even{fill:#ff6}.row-item.status3xx .odd{fill:#ff0}.row-item.potentiallyRenderBlocking .even,.row-item.potentiallyRenderBlocking .odd{fill:#e5a331}.row-item.renderBlocking .even,.row-item.renderBlocking .odd{fill:#e57231;stroke-dasharray:5;stroke:black;stroke-width:2px}.row-item.largestContentfulPaint .even,.row-item.largestContentfulPaint .odd{fill:#c9e531;stroke-dasharray:8;stroke-width:2px;stroke:black}.row-item.largestContentfulPaint .even,.row-item.largestContentfulPaint .odd,.row-item.potentiallyRenderBlocking .even,.row-item.potentiallyRenderBlocking .odd,.row-item.renderBlocking .even,.row-item.renderBlocking .odd,.row-item.status0 .even,.row-item.status0 .odd,.row-item.status3xx .even,.row-item.status3xx .odd,.row-item.status4xx .even,.row-item.status4xx .odd,.row-item.status5xx .even,.row-item.status5xx .odd{opacity:.3}.row-item.largestContentfulPaint:hover .even,.row-item.largestContentfulPaint:hover .odd,.row-item.potentiallyRenderBlocking:hover .even,.row-item.potentiallyRenderBlocking:hover .odd,.row-item.renderBlocking:hover .even,.row-item.renderBlocking:hover .odd,.row-item.status0:hover .even,.row-item.status0:hover .odd,.row-item.status3xx:hover .even,.row-item.status3xx:hover .odd,.row-item.status4xx:hover .even,.row-item.status4xx:hover .odd,.row-item.status5xx:hover .even,.row-item.status5xx:hover .odd{opacity:.5}.tooltip-holder{overflow:visible}.tooltip *{padding:0;margin:0}.tooltip html{font-size:10px;line-height:1.2em}.tooltip body{position:relative}.tooltip-payload{position:absolute;top:0;left:0;padding:.25em;font-size:10px;display:inline-block;background:rgba(255,255,255,.9);border:solid 1px #f0f0f0;word-break:break-all;overflow-wrap:break-word;transition:opacity .3s}.tooltip-payload.no-anim{transition:none}.row-item,.time-scale line,.time-scale text,.water-fall-chart .line-holder line,.water-fall-chart .line-label-holder{transition:transform 60ms}.water-fall-chart.closing{transition-delay:60ms}.labels{overflow:hidden}.block-css{fill:#a6d18f}.block-html,.block-iframe,.block-internal,.block-svg{fill:#82a8de}.block-image,.block-img{fill:#b394cf}.block-javascript,.block-js,.block-script{fill:#e0b483}.block-link{fill:#89afe6}.block-flash,.block-swf{fill:#42aab1}.block-font{fill:#e15d4e}.block-ajax,.block-xmlhttprequest{fill:#f00}.block-other,.block-plain{fill:#b3b3b3}.block-blocked{fill:#aaa}.block-dns{fill:#159588}.block-connect{fill:#fd9727}.block-ssl{fill:#c141cd}.block-send{fill:#b0bec5}.block-wait{fill:#1ec659}.block-receive{fill:#1eaaf1}.block-receive-chunk{fill:#a1c3fa}.block-undefined{fill:#0f0}.info-overlay-bg{fill:#fff;stroke:#cdcdcd}.info-overlay-close-btn{fill:rgba(205,205,205,0.8);transform:translate(-23px,-23px);cursor:pointer}.info-overlay-close-btn text{fill:#111;pointer-events:none}.info-overlay-close-btn:focus{border:solid 1px #36c}.info-overlay-holder .connect{border-right:solid 5px #fd9727;padding-right:5px}.info-overlay-holder .blocked{border-right:solid 5px #aaa;padding-right:5px}.info-overlay-holder .ssltls{border-right:solid 5px #c141cd;padding-right:5px}.info-overlay-holder .send{border-right:solid 5px #b0bec5;padding-right:5px}.info-overlay-holder .wait{border-right:solid 5px #1ec659;padding-right:5px}.info-overlay-holder .receive{border-right:solid 5px #1eaaf1;padding-right:5px}.info-overlay-holder .dns{border-right:solid 5px #159588;padding-right:5px}.type-css{background:#406b29}.type-html,.type-iframe,.type-internal,.type-svg{background:#1c4278}.type-image,.type-img{background:#4d2e69}.type-javascript,.type-js,.type-script{background:#7a4e1d}.type-link{background:#89afe6}.type-flash,.type-swf{background:#234980}.type-font{background:#ae2a1b}.type-ajax,.type-xmlhttprequest{background:#c00}.type-other,.type-plain{background:grey}.info-overlay-holder *{padding:0;margin:0;font-size:12px}.info-overlay-holder body{position:relative;height:450px;clear:both;padding:0;margin:0;width:100%;background:#fff;color:#666}.info-overlay-holder body .wrapper{height:450px;width:100%;overflow:scroll}.info-overlay-holder header{position:relative;box-shadow:0 0 2px 2px rgba(0,0,0,.25)}.info-overlay-holder header,.info-overlay-holder header a,.info-overlay-holder header button{color:#fff;text-decoration:none}.info-overlay-holder header a:focus,.info-overlay-holder header a:hover{text-decoration:underline}.info-overlay-holder .requestID{font-weight:700}.info-overlay-holder h3,.info-overlay-holder h3 a{font-size:1.1em;padding:1em;margin:0;font-weight:400;overflow-wrap:break-word}.info-overlay-holder h3 strong{font-size:1.1em}.info-overlay-holder .tab-nav ul{margin:0;padding:0}.info-overlay-holder .tab-nav li{margin:0;padding:0;display:inline-block}.info-overlay-holder button{background:0 0;outline:0;border:0;border-bottom:solid 2px transparent;padding:.5em 1em;margin:0 .25em}.info-overlay-holder li:first-child button{margin-left:1em}.info-overlay-holder button.active:focus,.info-overlay-holder button:focus,.info-overlay-holder button:hover{border-color:rgba(255,255,255,.6)}.info-overlay-holder button.active{border-color:#fff;cursor:default}.info-overlay-holder button.active:focus{border-color:rgba(255,255,255,.8)}.info-overlay-holder button.copy-tab-data{position:absolute;top:.5em;right:.5em;border:0;margin:0;border-radius:1em;background:#e0e0e0}.info-overlay-holder button.copy-tab-data:focus,.info-overlay-holder button.copy-tab-data:hover{background:#ccc}.info-overlay-holder dt{float:left;clear:both;margin-top:.5em;width:25%;text-align:right;font-weight:700}.info-overlay-holder dd{float:left;width:73%;margin:.5em 0 0 2%;padding:0 0 .5em 0}.info-overlay-holder dt:after{content:":"}.info-overlay-holder pre{font-size:11px;line-height:23px;border-radius:0;background:#f6f3f3}.info-overlay-holder .tab{float:left;position:relative;width:100%;height:350px;padding:12px 12px 24px}.info-overlay-holder .tab h2{font-size:1.2em;margin:.5em 0 0;padding:.5em 0 .5em 1em;clear:both;border-top:solid 1px #efefef}.info-overlay-holder .tab h2:first-child{border-top:0;padding-top:0}.info-overlay-holder .tab pre{overflow-y:hidden;width:100%;min-height:100%}.info-overlay-holder .tab .preview{width:auto;max-width:100%;max-height:500px;border:solid 1px #666}.info-overlay-holder .tab dl:after{content:"";display:table;clear:both}.info-overlay-holder .tab.rendered-data{padding:0}.info-overlay-holder .tab.rendered-data pre{padding:12px 12px 24px}.info-overlay-holder .tab.rendered-data pre>code{white-space:pre-wrap}.resource-legend{margin:0;padding:0;font-size:.75em;line-height:1.5em;display:inline-block}.resource-legend li{margin:0 1em 0 0;padding:0;white-space:nowrap;display:inline-block}.resource-legend li:before{content:"";width:1em;height:1em;margin:0 .5em 0 0;vertical-align:text-top;display:inline-block}.resource-legend .legend-blocked:before{background:#aaa}.resource-legend .legend-dns:before{background:#159588}.resource-legend .legend-connect:before{background:#fd9727}.resource-legend .legend-ssl:before{background:#c141cd}.resource-legend .legend-send:before{background:#b0bec5}.resource-legend .legend-wait:before{background:#1ec659}.resource-legend .legend-receive:before{background:#1eaaf1}.icon{fill:#666}.icon-4xx,.icon-5xx,.icon-no-cache,.icon-no-gzip,.icon-warning{fill:#b55}.water-fall-chart .type-firstpaint .line-holder{stroke:#42f46e;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-firstvisualchange .line-holder{stroke:#42f46e;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-visualcomplete85 .line-holder{stroke:#ee7777;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-lastvisualchange .line-holder{stroke:#ee42f4;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-onload .line-holder{stroke:#9c99e5;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-oncontentload .line-holder{stroke:#9842f4;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-dominteractivetime .line-holder{stroke:#1842f4;stroke-opacity:1;stroke-width:2}.water-fall-chart .type-domcontentloadedtime .line-holder{stroke:#1212f4;stroke-opacity:1;stroke-width:2}.water-fall-chart .type-lastcpulongtask .line-holder{stroke:#f41229;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-largestcontentfulpaint .line-holder{stroke:#c7f412;stroke-opacity:1;stroke-width:3}.water-fall-chart{font-size:14px}#page-selector{display:none;clear:both;margin:1em 0}*{box-sizing:border-box}@media screen and (max-width:400px){select{max-width:100%}}.screenshot{padding:4px;background-color:#fff;border:1px solid #ddd;border-radius:4px}*,:after,:before{margin:0;padding:0;box-sizing:border-box}section{display:block;padding:20px 0 0;border-top:1px solid #ddd}#tabs a{display:inline-block;margin:0 0 -1px;padding:15px 22px;font-weight:600;text-align:center;color:#bbb;border:1px solid transparent;text-decoration:none;text-transform:uppercase}#tabs a:before{font-weight:400}#tabs a:hover{color:#888;cursor:pointer}#tabs a:target:focus{outline:0}#tabs a[selected]{color:#555;border:1px solid #ddd;border-top:2px solid #0095d2;border-bottom:1px solid #fff}@media screen and (max-width:650px){#tabs a:before{margin:0;font-size:18px}}@media screen and (max-width:400px){#tabs a{padding:13px}}.group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd;font-weight:700}.group-item.active{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.loader,.loader:after,.loader:before{background:#000;-webkit-animation:load1 1s infinite ease-in-out;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#000;text-indent:-9999em;margin:88px auto;position:relative;font-size:11px;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation-delay:-.16s;animation-delay:-.16s}.loader:after,.loader:before{position:absolute;top:0;content:""}.loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.loader:after{left:1.5em}@-webkit-keyframes load1{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes load1{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}.ct-label{fill:rgba(0,0,0,0.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,0.2);stroke-width:1px;stroke-dasharray:2px}.ct-grid-background{fill:none}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{fill:none;stroke-width:4px}.ct-area{stroke:none;fill-opacity:0.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{content:"";display:table;clear:both}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{content:"";display:table;clear:both}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{content:"";display:table;clear:both}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{content:"";display:table;clear:both}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{content:"";display:table;clear:both}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{content:"";display:table;clear:both}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{content:"";display:table;clear:both}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{content:"";display:table;clear:both}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{content:"";display:table;clear:both}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{content:"";display:table;clear:both}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{content:"";display:table;clear:both}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{content:"";display:table;clear:both}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{content:"";display:table;clear:both}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{content:"";display:table;clear:both}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{content:"";display:table;clear:both}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{content:"";display:table;clear:both}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}.chartist-tooltip{position:absolute;display:inline-block;opacity:0;min-width:5em;padding:.5em;background:#f4c63d;color:#453d3f;font-family:Oxygen,Helvetica,Arial,sans-serif;font-weight:700;text-align:center;pointer-events:none;z-index:1;-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.chartist-tooltip:before{content:"";position:absolute;top:100%;left:50%;width:0;height:0;margin-left:-15px;border:15px solid transparent;border-top-color:#f4c63d}.chartist-tooltip.tooltip-show{opacity:1}.ct-area,.ct-line{pointer-events:none}.chartist-tooltip{background:#0095d2;color:#fff}.chartist-tooltip:before{border-top-color:#0095d2}.ct-bar{stroke-width:16px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#82b5fc}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#468847}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#b2ea94}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#c09853}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#fec584}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#b94a48}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#c49ae8}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#c49ae8}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#ff523e}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#ff523e}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#c4c4c4}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#c4c4c4}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#c4c4c4}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#c4c4c4}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#c4c4c4}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#c4c4c4}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#EAB839}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#EAB839}.ct-chart .ct-legend{position:relative;z-index:10;list-style:none;text-align:left;line-height:.8;font-size:.8em}.ct-chart .ct-legend li{padding-left:23px;margin-right:10px;margin-bottom:3px;cursor:pointer}.ct-chart .ct-legend li:before{width:12px;height:12px;position:absolute;left:0;content:"";border:3px solid transparent;border-radius:2px}.ct-chart .ct-legend li .inactive:before{background:0 0}.ct-chart .ct-legend li:first-child::before{background-color:#468847}.ct-chart .ct-legend li:nth-child(2)::before{background-color:#c09853}.ct-chart .ct-legend li:nth-child(3)::before{background-color:#b94a48}.ct-chart .ct-legend .ct-legend-inside{position:absolute;top:0;right:0}.ct-chart g:not(.ct-grids):not(.ct-labels) g:first-child .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:first-child .ct-point{stroke:#468847}.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(2) .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(2) .ct-point{stroke:#c09853}.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(3) .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(3) .ct-point{stroke:#b94a48}.filmstrip{padding-bottom:20px}.videoframe{vertical-align:top;display:inline-block;padding:4px;background-color:#fff;border:1px solid #ddd;border-radius:4px;width:100%}.videoframe.blue{border:2px solid #0095d2}.videoframetime{text-align:center;display:block}.videoframetext{text-align:left;display:block;line-height:1.2em;font-size:.8em;white-space:nowrap;margin-bottom:.2em}
1
+ /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*,:after,:before{box-sizing:border-box}ol,ul{margin-bottom:2.5rem}ul{margin-top:0;padding-left:0;list-style:circle inside}ul ol,ul ul{margin:1.5rem 0 1.5rem 3rem;font-size:.9rem}ol{margin-top:0;padding-left:0;list-style:decimal inside}ol ol,ol ul{margin:1.5rem 0 1.5rem 3rem;font-size:.9rem}li{margin-bottom:0}figure{margin-bottom:2.5rem}footer{text-align:center}hr{margin-top:3rem;margin-bottom:3.5rem;border-width:0;border-top:1px solid #e1e1e1}.container:after,.row:after{content:"";display:table;clear:both}.container{position:relative;width:100%;max-width:1140px;margin:0 auto;padding:0 20px;box-sizing:border-box}.column,.columns{width:100%;float:left;box-sizing:border-box}@media (min-width:400px){.container{width:85%;padding:0}}@media (min-width:550px){.container{width:80%}.column,.columns{margin-left:4%}.column:first-child,.columns:first-child{margin-left:0}.one.column,.one.columns{width:4.6666666667%}.two.columns{width:13.3333333333%}.three.columns{width:22%}.four.columns{width:30.6666666667%}.five.columns{width:39.3333333333%}.six.columns{width:48%}.seven.columns{width:56.6666666667%}.eight.columns{width:65.3333333333%}.nine.columns{width:74%}.ten.columns{width:82.6666666667%}.eleven.columns{width:91.3333333333%}.twelve.columns{width:100%;margin-left:0}.one-third.column{width:30.6666666667%}.two-thirds.column{width:65.3333333333%}.one-half.column{width:48%}.offset-by-one.column,.offset-by-one.columns{margin-left:8.6666666667%}.offset-by-two.column,.offset-by-two.columns{margin-left:17.3333333333%}.offset-by-three.column,.offset-by-three.columns{margin-left:26%}.offset-by-four.column,.offset-by-four.columns{margin-left:34.6666666667%}.offset-by-five.column,.offset-by-five.columns{margin-left:43.3333333333%}.offset-by-six.column,.offset-by-six.columns{margin-left:52%}.offset-by-seven.column,.offset-by-seven.columns{margin-left:60.6666666667%}.offset-by-eight.column,.offset-by-eight.columns{margin-left:69.3333333333%}.offset-by-nine.column,.offset-by-nine.columns{margin-left:78%}.offset-by-ten.column,.offset-by-ten.columns{margin-left:86.6666666667%}.offset-by-eleven.column,.offset-by-eleven.columns{margin-left:95.3333333333%}.offset-by-one-third.column,.offset-by-one-third.columns{margin-left:34.6666666667%}.offset-by-two-thirds.column,.offset-by-two-thirds.columns{margin-left:69.3333333333%}.offset-by-one-half.column,.offset-by-one-half.columns{margin-left:52%}}@media (min-width:1550px){.container{max-width:1400px;font-size:1.2em}}@media (min-width:1900px){.container{max-width:1800px;font-size:1.4em}}table{width:100%;margin-bottom:2.5rem;border-collapse:separate;border-spacing:1px;background-color:#e1e1e1}td,th{padding:4px 1rem;vertical-align:top;text-align:left}td:first-child,th:first-child{padding-left:1rem}td:last-child,th:last-child{padding-right:1rem}tr:nth-child(odd){background:#fafafa}tr:nth-child(2n){background:#fff}tr.odd{background:#fafafa}tr.even{background:#fff}th{background:#f1fbff}td.number,th.number{text-align:right}td.right{text-align:right}td.url{overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;-moz-hyphens:auto;hyphens:auto}td.assetsurl{max-width:400px}td.pagesurl{max-width:350px}td.offendingurl{max-width:900px}td.break{word-break:break-all}td.extraheader{font-weight:700;background:#f5f5f5}@media only screen and (max-width:800px){.responsive table,.responsive tbody,.responsive td,.responsive th,.responsive thead,.responsive tr{display:block}.responsive tr.u-hideable{display:none}.responsive thead tr{position:absolute;top:-9999px;left:-9999px}.responsive tr{border:2px solid #e1e1e1}.responsive td{border:none;border-bottom:1px solid #e1e1e1;position:relative;padding-left:50%;white-space:normal;text-align:left;max-width:none}.responsive td:before{position:absolute;top:6px;left:6px;width:30%;padding-right:10px;white-space:nowrap;text-align:left;font-weight:700}.responsive td:before{content:attr(data-title)}.responsive td.url.offendingurl{word-break:break-all;padding-left:2px}.hidden-small{display:none}}html{font-size:100%}body{font-size:1em;line-height:1.6;font-weight:400;font-family:-apple-system,BlinkMacSystemFont,"Helvetica Neue",Helvetica,Arial,sans-serif;color:#222}h1,h2,h3,h4,h5,h6{margin-top:1rem;margin-bottom:2rem;font-weight:300}h1{font-size:3rem;line-height:1.2}h2{font-size:2.8rem;line-height:1.25}h3{font-size:2.6rem;line-height:1.3}h4{font-size:2.4rem;line-height:1.35}h5{font-size:1.8rem;line-height:1.5}h6{font-size:1.5rem;line-height:1.6;letter-spacing:0}p{margin-top:0}a{color:#0095d2}a:hover{color:#00719f}blockquote,dl,p,pre{margin-bottom:2.5rem}code{padding:.2rem .5rem;margin:0 .2rem;font-size:.9rem;white-space:nowrap;background:#fafafa;border:1px solid #e1e1e1;border-radius:4px}pre>code{display:block;padding:1rem 1.5rem;white-space:pre;overflow:auto}.u-full-width{width:100%}.u-max-full-width{max-width:100%}.u-pull-right{float:right}.u-pull-left{float:left}.u-cf{content:"";display:table;clear:both}.u-hideable{display:none}.button{display:inline-block;height:38px;padding:0 30px;margin-bottom:1rem;color:#222;text-align:center;font-size:80%;font-weight:600;line-height:38px;letter-spacing:.1rem;text-transform:uppercase;text-decoration:none;white-space:nowrap;background-color:transparent;border-radius:4px;border:1px solid #e1e1e1;cursor:pointer}.button:active,.button:focus,.button:hover{color:#222;border-color:#aeaeae;outline:0}.button--primary{color:#fff;background-color:#0095d2;border-color:#0095d2}.button--primary:active,.button--primary:focus,.button--primary:hover{color:#fff;background-color:#0087be;border-color:#0087be}.button-download{color:#fff;background-color:#ec971f;border-color:#eb9316;padding:0 10px;margin-right:1rem}.button-download:active,.button-download:focus,.button-download:hover{color:#fff;background-color:#e38d13;border-color:#da8813}.navgrid{width:100%;min-width:0;margin-left:0;margin-right:0;padding-left:0;padding-right:10px}.nav{background:#0095d2}.nav ul{list-style:none;text-align:center;padding:0;margin:0;background-color:#0095d2}.nav li{line-height:40px;height:40px;border-bottom:none;margin-bottom:0}.nav a{text-decoration:none;color:#fff;display:block}.nav a:hover{background-color:#0073b0}.nav a.active{background-color:#0073b0;color:#fff;cursor:default}.logo{text-align:center;background-color:#0095d2}.navbar-brand{padding:0;font-size:18px;max-width:250px}@media screen and (min-width:820px){body{padding-top:50px}.navgrid{width:100%;max-width:1140px;min-width:755px;margin:0 auto;overflow:hidden}.nav{height:50px;width:100%;z-index:1000;position:fixed;top:0}.navbar-brand{padding:0;font-size:18px;float:left;max-width:250px}.nav{z-index:10;top:0;background-color:#0095d2}.nav a{padding-left:20px;padding-right:20px}.nav li{border-bottom:none;height:50px;line-height:50px;float:left;display:inline-block;margin-right:0}.nav a{text-decoration:none;color:#fff;display:block}.nav ul{list-style:none;text-align:center;padding:0;margin:0;background-color:#0095d2}:target:before{content:"";display:block;height:50px;margin:-50px 0 0}}@media (min-width:1550px){.nav li{font-size:1.2em}}@media (min-width:1900px){.nav li{font-size:1.4em}}table[data-sortable] th[data-sorted=true]{color:#3a87ad;background:#d9edf7;border-bottom-color:#bce8f1}table[data-sortable] th:not([data-sortable=false]){cursor:pointer;color:#222;text-decoration:underline}.summarybox{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.summarybox.ok{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.summarybox.warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.summarybox.error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.summarybox.info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad}a.summaries{text-decoration:none}.summarynumber{font-size:2rem;line-height:1;font-weight:700}.summarysmall{font-size:1rem;line-height:1}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.normal{font-size:100%}.ok{background-color:#468847}.warning{background-color:#f0ad4e}.error{background-color:#d9534f}.info{background-color:#0095d2}ul.menu{list-style:none;font-size:125%;text-transform:uppercase}.errors{margin-bottom:1.333em;background:#ffb6c1;padding-left:1em}.subtableheader{background:#ffe3eb}.large{font-size:1.333rem;line-height:1.8rem}.hidden-small{display:inline}.url{overflow-wrap:break-word;word-wrap:break-word;-webkit-hyphens:auto;-ms-hyphens:auto;-moz-hyphens:auto;hyphens:auto}/*! github.com/micmro/PerfCascade Version:2.11.0 (24/11/2021) */.water-fall-chart{width:100%;overflow:visible;font-size:12px;line-height:1em}.water-fall-chart *{box-sizing:border-box}.water-fall-chart button{cursor:pointer}.water-fall-holder{fill:#ccc}.water-fall-chart .left-fixed-holder{overflow:visible}.water-fall-chart .marker-holder{width:100%}.water-fall-chart .line-label-holder{cursor:pointer}.water-fall-chart .line-holder{stroke-width:1;stroke:#ccc;stroke-opacity:0.5;transition:all 60ms}.water-fall-chart .line-holder .line-mark{fill:#69009e;opacity:.01;stroke-width:0;transition:all 60ms}.water-fall-chart .line-holder.active{stroke:#69009e;stroke-width:2;stroke-opacity:1}.water-fall-chart .line-holder.active .line-mark{opacity:.4}.water-fall-chart .type-onload .line-holder{stroke:#c0c0ff}.water-fall-chart .type-oncontentload .line-holder{stroke:#d888df}.water-fall-chart .labels{width:100%}.water-fall-chart .labels .inner-label{pointer-events:none}.water-fall-chart .time-block.active{opacity:.8}.water-fall-chart .line-end,.water-fall-chart .line-start{display:none;stroke-width:1;stroke-opacity:0.5;stroke:#000}.water-fall-chart .line-end.active,.water-fall-chart .line-start.active{display:block}.left-fixed-holder .label-full-bg{fill:#fff;opacity:.9}.time-scale line{stroke:#0cc;stroke-width:1}.time-scale line.sub-second-line{stroke:#ccc;opacity:.75;stroke-width:.5}.time-scale text{font-weight:700}.row-item{cursor:pointer}.row-item .even{fill:#ccc;opacity:.05}.row-item .odd{fill:#000;opacity:.05}.row-item:hover .even,.row-item:hover .odd{fill:#000;opacity:.1}.row-item:focus{outline:solid 1.5px #aaa;outline-offset:-1.5px}.row-item:focus .even,.row-item:focus .odd{fill:#000;opacity:.2}.row-item .rect-holder text{fill:#aaa}.row-item.status0 .even,.row-item.status5xx .even{fill:#f66}.row-item.status0 .odd,.row-item.status5xx .odd{fill:#f00}.row-item.status4xx .even{fill:#c33}.row-item.status4xx .odd{fill:#c00}.row-item.status3xx .even{fill:#ff6}.row-item.status3xx .odd{fill:#ff0}.row-item.potentiallyRenderBlocking .even,.row-item.potentiallyRenderBlocking .odd{fill:#e5a331}.row-item.renderBlocking .even,.row-item.renderBlocking .odd{fill:#e57231;stroke-dasharray:5;stroke:black;stroke-width:2px}.row-item.largestContentfulPaint .even,.row-item.largestContentfulPaint .odd{fill:#c9e531;stroke-dasharray:8;stroke-width:2px;stroke:black}.row-item.largestContentfulPaint .even,.row-item.largestContentfulPaint .odd,.row-item.potentiallyRenderBlocking .even,.row-item.potentiallyRenderBlocking .odd,.row-item.renderBlocking .even,.row-item.renderBlocking .odd,.row-item.status0 .even,.row-item.status0 .odd,.row-item.status3xx .even,.row-item.status3xx .odd,.row-item.status4xx .even,.row-item.status4xx .odd,.row-item.status5xx .even,.row-item.status5xx .odd{opacity:.3}.row-item.largestContentfulPaint:hover .even,.row-item.largestContentfulPaint:hover .odd,.row-item.potentiallyRenderBlocking:hover .even,.row-item.potentiallyRenderBlocking:hover .odd,.row-item.renderBlocking:hover .even,.row-item.renderBlocking:hover .odd,.row-item.status0:hover .even,.row-item.status0:hover .odd,.row-item.status3xx:hover .even,.row-item.status3xx:hover .odd,.row-item.status4xx:hover .even,.row-item.status4xx:hover .odd,.row-item.status5xx:hover .even,.row-item.status5xx:hover .odd{opacity:.5}.tooltip-holder{overflow:visible}.tooltip *{padding:0;margin:0}.tooltip html{font-size:10px;line-height:1.2em}.tooltip body{position:relative}.tooltip-payload{position:absolute;top:0;left:0;padding:.25em;font-size:10px;display:inline-block;background:rgba(255,255,255,.9);border:solid 1px #f0f0f0;word-break:break-all;overflow-wrap:break-word;transition:opacity .3s}.tooltip-payload.no-anim{transition:none}.row-item,.time-scale line,.time-scale text,.water-fall-chart .line-holder line,.water-fall-chart .line-label-holder{transition:transform 60ms}.water-fall-chart.closing{transition-delay:60ms}.labels{overflow:hidden}.block-css{fill:#a6d18f}.block-html,.block-iframe,.block-internal,.block-svg{fill:#82a8de}.block-image,.block-img{fill:#b394cf}.block-javascript,.block-js,.block-script{fill:#e0b483}.block-link{fill:#89afe6}.block-flash,.block-swf{fill:#42aab1}.block-font{fill:#e15d4e}.block-ajax,.block-xmlhttprequest{fill:#f00}.block-other,.block-plain{fill:#b3b3b3}.block-blocked{fill:#aaa}.block-dns{fill:#159588}.block-connect{fill:#fd9727}.block-ssl{fill:#c141cd}.block-send{fill:#b0bec5}.block-wait{fill:#1ec659}.block-receive{fill:#1eaaf1}.block-receive-chunk{fill:#a1c3fa}.block-undefined{fill:#0f0}.info-overlay-bg{fill:#fff;stroke:#cdcdcd}.info-overlay-close-btn{fill:rgba(205,205,205,0.8);transform:translate(-23px,-23px);cursor:pointer}.info-overlay-close-btn text{fill:#111;pointer-events:none}.info-overlay-close-btn:focus{border:solid 1px #36c}.info-overlay-holder .connect{border-right:solid 5px #fd9727;padding-right:5px}.info-overlay-holder .blocked{border-right:solid 5px #aaa;padding-right:5px}.info-overlay-holder .ssltls{border-right:solid 5px #c141cd;padding-right:5px}.info-overlay-holder .send{border-right:solid 5px #b0bec5;padding-right:5px}.info-overlay-holder .wait{border-right:solid 5px #1ec659;padding-right:5px}.info-overlay-holder .receive{border-right:solid 5px #1eaaf1;padding-right:5px}.info-overlay-holder .dns{border-right:solid 5px #159588;padding-right:5px}.type-css{background:#406b29}.type-html,.type-iframe,.type-internal,.type-svg{background:#1c4278}.type-image,.type-img{background:#4d2e69}.type-javascript,.type-js,.type-script{background:#7a4e1d}.type-link{background:#89afe6}.type-flash,.type-swf{background:#234980}.type-font{background:#ae2a1b}.type-ajax,.type-xmlhttprequest{background:#c00}.type-other,.type-plain{background:grey}.info-overlay-holder *{padding:0;margin:0;font-size:12px}.info-overlay-holder body{position:relative;height:450px;clear:both;padding:0;margin:0;width:100%;background:#fff;color:#666}.info-overlay-holder body .wrapper{height:450px;width:100%;overflow:scroll}.info-overlay-holder header{position:relative;box-shadow:0 0 2px 2px rgba(0,0,0,.25)}.info-overlay-holder header,.info-overlay-holder header a,.info-overlay-holder header button{color:#fff;text-decoration:none}.info-overlay-holder header a:focus,.info-overlay-holder header a:hover{text-decoration:underline}.info-overlay-holder .requestID{font-weight:700}.info-overlay-holder h3,.info-overlay-holder h3 a{font-size:1.1em;padding:1em;margin:0;font-weight:400;overflow-wrap:break-word}.info-overlay-holder h3 strong{font-size:1.1em}.info-overlay-holder .tab-nav ul{margin:0;padding:0}.info-overlay-holder .tab-nav li{margin:0;padding:0;display:inline-block}.info-overlay-holder button{background:0 0;outline:0;border:0;border-bottom:solid 2px transparent;padding:.5em 1em;margin:0 .25em}.info-overlay-holder li:first-child button{margin-left:1em}.info-overlay-holder button.active:focus,.info-overlay-holder button:focus,.info-overlay-holder button:hover{border-color:rgba(255,255,255,.6)}.info-overlay-holder button.active{border-color:#fff;cursor:default}.info-overlay-holder button.active:focus{border-color:rgba(255,255,255,.8)}.info-overlay-holder button.copy-tab-data{position:absolute;top:.5em;right:.5em;border:0;margin:0;border-radius:1em;background:#e0e0e0}.info-overlay-holder button.copy-tab-data:focus,.info-overlay-holder button.copy-tab-data:hover{background:#ccc}.info-overlay-holder dt{float:left;clear:both;margin-top:.5em;width:25%;text-align:right;font-weight:700}.info-overlay-holder dd{float:left;width:73%;margin:.5em 0 0 2%;padding:0 0 .5em 0}.info-overlay-holder dt:after{content:":"}.info-overlay-holder pre{font-size:11px;line-height:23px;border-radius:0;background:#f6f3f3}.info-overlay-holder .tab{float:left;position:relative;width:100%;height:350px;padding:12px 12px 24px}.info-overlay-holder .tab h2{font-size:1.2em;margin:.5em 0 0;padding:.5em 0 .5em 1em;clear:both;border-top:solid 1px #efefef}.info-overlay-holder .tab h2:first-child{border-top:0;padding-top:0}.info-overlay-holder .tab pre{overflow-y:hidden;width:100%;min-height:100%}.info-overlay-holder .tab .preview{width:auto;max-width:100%;max-height:500px;border:solid 1px #666}.info-overlay-holder .tab dl:after{content:"";display:table;clear:both}.info-overlay-holder .tab.rendered-data{padding:0}.info-overlay-holder .tab.rendered-data pre{padding:12px 12px 24px}.info-overlay-holder .tab.rendered-data pre>code{white-space:pre-wrap}.resource-legend{margin:0;padding:0;font-size:.75em;line-height:1.5em;display:inline-block}.resource-legend li{margin:0 1em 0 0;padding:0;white-space:nowrap;display:inline-block}.resource-legend li:before{content:"";width:1em;height:1em;margin:0 .5em 0 0;vertical-align:text-top;display:inline-block}.resource-legend .legend-blocked:before{background:#aaa}.resource-legend .legend-dns:before{background:#159588}.resource-legend .legend-connect:before{background:#fd9727}.resource-legend .legend-ssl:before{background:#c141cd}.resource-legend .legend-send:before{background:#b0bec5}.resource-legend .legend-wait:before{background:#1ec659}.resource-legend .legend-receive:before{background:#1eaaf1}.icon{fill:#666}.icon-4xx,.icon-5xx,.icon-no-cache,.icon-no-gzip,.icon-warning{fill:#b55}.water-fall-chart .type-firstpaint .line-holder{stroke:#42f46e;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-firstvisualchange .line-holder{stroke:#42f46e;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-visualcomplete85 .line-holder{stroke:#ee7777;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-lastvisualchange .line-holder{stroke:#ee42f4;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-onload .line-holder{stroke:#9c99e5;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-oncontentload .line-holder{stroke:#9842f4;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-dominteractivetime .line-holder{stroke:#1842f4;stroke-opacity:1;stroke-width:2}.water-fall-chart .type-domcontentloadedtime .line-holder{stroke:#1212f4;stroke-opacity:1;stroke-width:2}.water-fall-chart .type-lastcpulongtask .line-holder{stroke:#f41229;stroke-opacity:1;stroke-width:3}.water-fall-chart .type-largestcontentfulpaint .line-holder{stroke:#c7f412;stroke-opacity:1;stroke-width:3}.water-fall-chart{font-size:14px}#page-selector{display:none;clear:both;margin:1em 0}*{box-sizing:border-box}@media screen and (max-width:400px){select{max-width:100%}}.screenshot{padding:4px;background-color:#fff;border:1px solid #ddd;border-radius:4px}*,:after,:before{margin:0;padding:0;box-sizing:border-box}section{display:block;padding:20px 0 0;border-top:1px solid #ddd}#tabs a{display:inline-block;margin:0 0 -1px;padding:15px 22px;font-weight:600;text-align:center;color:#bbb;border:1px solid transparent;text-decoration:none;text-transform:uppercase}#tabs a:before{font-weight:400}#tabs a:hover{color:#888;cursor:pointer}#tabs a:target:focus{outline:0}#tabs a[selected]{color:#555;border:1px solid #ddd;border-top:2px solid #0095d2;border-bottom:1px solid #fff}@media screen and (max-width:650px){#tabs a:before{margin:0;font-size:18px}}@media screen and (max-width:400px){#tabs a{padding:13px}}.group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd;font-weight:700}.group-item.active{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.loader,.loader:after,.loader:before{background:#000;-webkit-animation:load1 1s infinite ease-in-out;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#000;text-indent:-9999em;margin:88px auto;position:relative;font-size:11px;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation-delay:-.16s;animation-delay:-.16s}.loader:after,.loader:before{position:absolute;top:0;content:""}.loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.loader:after{left:1.5em}@-webkit-keyframes load1{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes load1{0%,100%,80%{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}.ct-label{fill:rgba(0,0,0,0.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,0.2);stroke-width:1px;stroke-dasharray:2px}.ct-grid-background{fill:none}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{fill:none;stroke-width:4px}.ct-area{stroke:none;fill-opacity:0.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{content:"";display:table;clear:both}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{content:"";display:table;clear:both}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{content:"";display:table;clear:both}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{content:"";display:table;clear:both}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{content:"";display:table;clear:both}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{content:"";display:table;clear:both}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{content:"";display:table;clear:both}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{content:"";display:table;clear:both}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{content:"";display:table;clear:both}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{content:"";display:table;clear:both}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{content:"";display:table;clear:both}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{content:"";display:table;clear:both}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{content:"";display:table;clear:both}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{content:"";display:table;clear:both}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{content:"";display:table;clear:both}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{content:"";display:table;clear:both}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0}.ct-legend{display:flex;margin-top:10px;margin-left:16px;margin-bottom:14x}.ct-legend-item{display:flex;margin-right:15px;line-height:18px}.ct-legend-item i{display:inline-block;width:18px;height:18px;margin-right:5px}.baseline-series .ct-line,.baseline-series .ct-point{stroke:green}.current-series .ct-line,.current-series .ct-point{stroke:blue}.chartist-tooltip{position:absolute;display:inline-block;opacity:0;min-width:5em;padding:.5em;background:#f4c63d;color:#453d3f;font-family:Oxygen,Helvetica,Arial,sans-serif;font-weight:700;text-align:center;pointer-events:none;z-index:1;-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.chartist-tooltip:before{content:"";position:absolute;top:100%;left:50%;width:0;height:0;margin-left:-15px;border:15px solid transparent;border-top-color:#f4c63d}.chartist-tooltip.tooltip-show{opacity:1}.ct-area,.ct-line{pointer-events:none}.chartist-tooltip{background:#0095d2;color:#fff}.chartist-tooltip:before{border-top-color:#0095d2}.ct-bar{stroke-width:16px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#82b5fc}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#468847}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#b2ea94}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#c09853}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#fec584}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#b94a48}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#c49ae8}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#c49ae8}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#ff523e}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#ff523e}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#c4c4c4}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#c4c4c4}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#c4c4c4}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#c4c4c4}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#c4c4c4}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#c4c4c4}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#EAB839}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#EAB839}.ct-chart .ct-legend{position:relative;z-index:10;list-style:none;text-align:left;line-height:.8;font-size:.8em}.ct-chart .ct-legend li{padding-left:23px;margin-right:10px;margin-bottom:3px;cursor:pointer}.ct-chart .ct-legend li:before{width:12px;height:12px;position:absolute;left:0;content:"";border:3px solid transparent;border-radius:2px}.ct-chart .ct-legend li .inactive:before{background:0 0}.ct-chart .ct-legend li:first-child::before{background-color:#468847}.ct-chart .ct-legend li:nth-child(2)::before{background-color:#c09853}.ct-chart .ct-legend li:nth-child(3)::before{background-color:#b94a48}.ct-chart .ct-legend .ct-legend-inside{position:absolute;top:0;right:0}.ct-chart g:not(.ct-grids):not(.ct-labels) g:first-child .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:first-child .ct-point{stroke:#468847}.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(2) .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(2) .ct-point{stroke:#c09853}.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(3) .ct-line,.ct-chart g:not(.ct-grids):not(.ct-labels) g:nth-child(3) .ct-point{stroke:#b94a48}.filmstrip{padding-bottom:20px}.videoframe{vertical-align:top;display:inline-block;padding:4px;background-color:#fff;border:1px solid #ddd;border-radius:4px;width:100%}.videoframe.blue{border:2px solid #0095d2}.videoframetime{text-align:center;display:block}.videoframetext{text-align:left;display:block;line-height:1.2em;font-size:.8em;white-space:nowrap;margin-bottom:.2em}
@@ -613,3 +613,34 @@
613
613
  left: 0; }
614
614
 
615
615
  /*# sourceMappingURL=chartist.css.map */
616
+
617
+ .ct-legend {
618
+ display: flex;
619
+ // justify-content: center;
620
+ // align-items: center;
621
+ margin-top: 10px;
622
+ margin-left: 16px;
623
+ margin-bottom: 14x;
624
+ }
625
+
626
+ .ct-legend-item {
627
+ display: flex;
628
+ // align-items: center;
629
+ margin-right: 15px;
630
+ line-height: 18px;
631
+ }
632
+
633
+ .ct-legend-item i {
634
+ display: inline-block;
635
+ width: 18px;
636
+ height: 18px;
637
+ margin-right: 5px;
638
+ }
639
+
640
+ .baseline-series .ct-point, .baseline-series .ct-line {
641
+ stroke: green;
642
+ }
643
+
644
+ .current-series .ct-point, .current-series .ct-line {
645
+ stroke: blue;
646
+ }
@@ -12,3 +12,6 @@ if d.browsertime && d.browsertime.har
12
12
  if options.browsertime.chrome && options.browsertime.chrome.collectNetLog
13
13
  - const netlogPath = 'data/chromeNetlog-' + (iteration ? iteration : 1) + '.json.gz'
14
14
  a.button.button-download(href=netlogPath, download=downloadName + '-netlog.json.gz') Download netlog
15
+ if options.browsertime.chrome && options.browsertime.chrome.collectConsoleLog
16
+ - const consoleLogPath = 'data/console-' + (iteration ? iteration : 1) + '.json.gz'
17
+ a.button.button-download(href=consoleLogPath, download=downloadName + '-consoleLogs.json.gz') Download Console Logs
@@ -11,4 +11,7 @@ if d.browsertime && d.browsertime.har
11
11
  - const harEnding = options.gzipHAR ? '.har.gz' : '.har'
12
12
  - const harName = 'data/browsertime' + harEnding
13
13
  - const harDownloadName = downloadName + harEnding
14
- a.button.button-download(href=harName, download=harDownloadName) Download HAR
14
+ a.button.button-download(href=harName, download=harDownloadName) Download HAR
15
+ if options.browsertime.chrome && options.browsertime.chrome.collectConsoleLog
16
+ - const consoleLogPath = 'data/console-' + (runNumber? runNumber : 1) + '.json.gz'
17
+ a.button.button-download(href=consoleLogPath, download=downloadName + '-consoleLogs.json.gz') Download Console Logs
@@ -67,6 +67,9 @@ block content
67
67
  if d.browsertime && d.browsertime.har
68
68
  a(href='#downloads') Download HAR
69
69
  | &nbsp;|&nbsp;
70
+ if options.browsertime.chrome && options.browsertime.chrome.collectConsoleLog
71
+ a(href='#downloads') Download Console Logs
72
+ | &nbsp;|&nbsp;
70
73
 
71
74
  a#summary
72
75
  h2 Summary
@@ -1,4 +1,16 @@
1
1
  export function decimals(decimals) {
2
- let number = Number(decimals).toFixed(3);
3
- return number === '0.000' ? 0 : number;
2
+ const num = Number(decimals);
3
+
4
+ // Check if the number is an integer (no decimals)
5
+ if (Number.isInteger(num)) {
6
+ return num;
7
+ }
8
+
9
+ // If the number is less than 1, use three decimals
10
+ if (num < 1) {
11
+ return Number(num.toFixed(3));
12
+ }
13
+
14
+ // If the number is greater than 1 and has decimals, use one decimal
15
+ return Number(num.toFixed(1));
4
16
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "sitespeed.io",
3
- "version": "30.3.0",
3
+ "version": "30.4.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "sitespeed.io",
9
- "version": "30.3.0",
9
+ "version": "30.4.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@google-cloud/storage": "6.9.5",
@@ -15,7 +15,7 @@
15
15
  "@tgwf/co2": "0.13.6",
16
16
  "aws-sdk": "2.1327.0",
17
17
  "axe-core": "4.8.2",
18
- "browsertime": "18.0.0",
18
+ "browsertime": "19.1.0",
19
19
  "cli-color": "2.0.3",
20
20
  "coach-core": "8.0.2",
21
21
  "concurrent-queue": "7.0.2",
@@ -985,9 +985,9 @@
985
985
  }
986
986
  },
987
987
  "node_modules/@sitespeed.io/geckodriver": {
988
- "version": "0.33.0",
989
- "resolved": "https://registry.npmjs.org/@sitespeed.io/geckodriver/-/geckodriver-0.33.0.tgz",
990
- "integrity": "sha512-w6w+x9/Q44JekTPi8NlRsfh5Uz4TfquJcUEs0tA/oEcxLVxRS7VtaiaJEE0GzzN6cUmFS6Twas7E4bCA4k/Yxg==",
988
+ "version": "0.33.0-c",
989
+ "resolved": "https://registry.npmjs.org/@sitespeed.io/geckodriver/-/geckodriver-0.33.0-c.tgz",
990
+ "integrity": "sha512-HRZubf9VOvPcMl9gjOjsom1vVGPZhc53n+BVdXh9uVloT603rO/1W6ny12WcR1EDVctPXfPoneFguqHdT5W5rQ==",
991
991
  "hasInstallScript": true,
992
992
  "dependencies": {
993
993
  "node-downloader-helper": "2.1.5",
@@ -1759,15 +1759,15 @@
1759
1759
  }
1760
1760
  },
1761
1761
  "node_modules/browsertime": {
1762
- "version": "18.0.0",
1763
- "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-18.0.0.tgz",
1764
- "integrity": "sha512-E0BJVPahR/MgzyuANrvm2gnA7SiMgDw83VzEiYttd9YA/5uO26wmaRMs46Xo9xzah/d+0LQA/GO9a7bzc0dx+g==",
1762
+ "version": "19.1.0",
1763
+ "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-19.1.0.tgz",
1764
+ "integrity": "sha512-LurCshUpj/W8XiN1Cfzem6RpqVSm/g9efW2PijO0y5KLuzZb4a+In5LIFH10Zm9PnHTyfs1aMow5ECnXNXQRag==",
1765
1765
  "dependencies": {
1766
1766
  "@cypress/xvfb": "1.2.4",
1767
1767
  "@devicefarmer/adbkit": "3.2.5",
1768
1768
  "@sitespeed.io/chromedriver": "119.0.6045-105",
1769
1769
  "@sitespeed.io/edgedriver": "119.0.2151-42",
1770
- "@sitespeed.io/geckodriver": "0.33.0",
1770
+ "@sitespeed.io/geckodriver": "0.33.0-c",
1771
1771
  "@sitespeed.io/throttle": "5.0.0",
1772
1772
  "@sitespeed.io/tracium": "0.3.3",
1773
1773
  "btoa": "1.2.1",
@@ -6115,9 +6115,9 @@
6115
6115
  "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
6116
6116
  },
6117
6117
  "node_modules/minipass": {
6118
- "version": "4.2.5",
6119
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz",
6120
- "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==",
6118
+ "version": "4.2.8",
6119
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
6120
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==",
6121
6121
  "engines": {
6122
6122
  "node": ">=8"
6123
6123
  }
@@ -9990,9 +9990,9 @@
9990
9990
  }
9991
9991
  },
9992
9992
  "@sitespeed.io/geckodriver": {
9993
- "version": "0.33.0",
9994
- "resolved": "https://registry.npmjs.org/@sitespeed.io/geckodriver/-/geckodriver-0.33.0.tgz",
9995
- "integrity": "sha512-w6w+x9/Q44JekTPi8NlRsfh5Uz4TfquJcUEs0tA/oEcxLVxRS7VtaiaJEE0GzzN6cUmFS6Twas7E4bCA4k/Yxg==",
9993
+ "version": "0.33.0-c",
9994
+ "resolved": "https://registry.npmjs.org/@sitespeed.io/geckodriver/-/geckodriver-0.33.0-c.tgz",
9995
+ "integrity": "sha512-HRZubf9VOvPcMl9gjOjsom1vVGPZhc53n+BVdXh9uVloT603rO/1W6ny12WcR1EDVctPXfPoneFguqHdT5W5rQ==",
9996
9996
  "requires": {
9997
9997
  "node-downloader-helper": "2.1.5",
9998
9998
  "node-stream-zip": "1.15.0",
@@ -10574,15 +10574,15 @@
10574
10574
  }
10575
10575
  },
10576
10576
  "browsertime": {
10577
- "version": "18.0.0",
10578
- "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-18.0.0.tgz",
10579
- "integrity": "sha512-E0BJVPahR/MgzyuANrvm2gnA7SiMgDw83VzEiYttd9YA/5uO26wmaRMs46Xo9xzah/d+0LQA/GO9a7bzc0dx+g==",
10577
+ "version": "19.1.0",
10578
+ "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-19.1.0.tgz",
10579
+ "integrity": "sha512-LurCshUpj/W8XiN1Cfzem6RpqVSm/g9efW2PijO0y5KLuzZb4a+In5LIFH10Zm9PnHTyfs1aMow5ECnXNXQRag==",
10580
10580
  "requires": {
10581
10581
  "@cypress/xvfb": "1.2.4",
10582
10582
  "@devicefarmer/adbkit": "3.2.5",
10583
10583
  "@sitespeed.io/chromedriver": "119.0.6045-105",
10584
10584
  "@sitespeed.io/edgedriver": "119.0.2151-42",
10585
- "@sitespeed.io/geckodriver": "0.33.0",
10585
+ "@sitespeed.io/geckodriver": "0.33.0-c",
10586
10586
  "@sitespeed.io/throttle": "5.0.0",
10587
10587
  "@sitespeed.io/tracium": "0.3.3",
10588
10588
  "btoa": "1.2.1",
@@ -13878,9 +13878,9 @@
13878
13878
  "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
13879
13879
  },
13880
13880
  "minipass": {
13881
- "version": "4.2.5",
13882
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz",
13883
- "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q=="
13881
+ "version": "4.2.8",
13882
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz",
13883
+ "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="
13884
13884
  },
13885
13885
  "minizlib": {
13886
13886
  "version": "2.1.2",
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "sitespeed.io": "./bin/sitespeed.js",
6
6
  "sitespeed.io-wpr": "./bin/browsertimeWebPageReplay.js"
7
7
  },
8
- "version": "30.3.0",
8
+ "version": "30.4.0",
9
9
  "description": "Analyze the web performance of your site",
10
10
  "keywords": [
11
11
  "performance",
@@ -83,7 +83,7 @@
83
83
  "@tgwf/co2": "0.13.6",
84
84
  "aws-sdk": "2.1327.0",
85
85
  "axe-core": "4.8.2",
86
- "browsertime": "18.0.0",
86
+ "browsertime": "19.1.0",
87
87
  "coach-core": "8.0.2",
88
88
  "cli-color": "2.0.3",
89
89
  "concurrent-queue": "7.0.2",