Plugin tab polish (#1794)

* Avoid crash for plugin has nothing to render.

Plugins that register a pug file, but has nothing to render (e.g. when WPT timed out for all urls), would still have their pugs rendered. This would break the assumption there was data to render, leading to a crash.

Unhandled rejection TypeError: Cannot read property 'run' of undefined on line 13

Filtering out the plugin pugs where there’s data from messages fixes this crash.

* Allow eslinting on js-files in html templates.

* Simplify css/js/pug for tab rendering.

* Split inline js into separate .js file (to allow linting and syntax highlighting in editor).
* Update js/css to show all tabs by default, and then hide them on page load via javascript. That way all content is visible if js fails to load, instead of all content being hidden.

* Don’t scroll page when switching result tab.

* Rename pagexray image for consistency.

* More granular checks for missing plugin data.

* Reflect tab switches in url fragment.
This commit is contained in:
Tobias Lidskog 2017-11-15 20:03:00 +01:00 committed by Peter Hedenskog
parent df0e9d4ca4
commit d621049827
12 changed files with 336 additions and 322 deletions

View File

@ -1580,11 +1580,11 @@ http://codepen.io/oknoblich/pen/tfjFl
box-sizing: border-box; }
section {
display: none;
display: block;
padding: 20px 0 0;
border-top: 1px solid #ddd; }
a[data-name="tabs"] {
#tabs a {
display: inline-block;
margin: 0 0 -1px;
padding: 15px 22px;
@ -1594,37 +1594,31 @@ a[data-name="tabs"] {
border: 1px solid transparent;
text-decoration: none; }
a[data-name="tabs"]:before {
#tabs a:before {
font-weight: normal; }
a[data-name="tabs"]:hover {
#tabs a:hover {
color: #888;
cursor: pointer; }
a[data-name="tabs"]:target:focus {
#tabs a:target:focus {
outline: none; }
a[selected] {
#tabs a[selected] {
color: #555;
border: 1px solid #ddd;
border-top: 2px solid #0095d2;
border-bottom: 1px solid #fff; }
#summary[selected] ~ #summary-panel,
#coach[selected] ~ #coach-panel,
#browsertime[selected] ~ #browsertime-panel,
#pagexray[selected] ~ #pagexray-panel {
display: block; }
@media screen and (max-width: 650px) {
a[data-name="tabs"] {
#tabs a {
font-size: 0; }
a[data-name="tabs"]:before {
#tabs a:before {
margin: 0;
font-size: 18px; } }
@media screen and (max-width: 400px) {
a[data-name="tabs"] {
#tabs a {
padding: 13px; } }
.group-item {

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -259,13 +259,16 @@ class HTMLBuilder {
locals
);
const pugs = { pugs: {} };
for (var summaries of this.pageSummaries) {
pugs.pugs[summaries.id] = renderer.renderTemplate(summaries.id, locals);
locals = merge(pugs, locals);
const pugs = {};
const pageSummaries = this.pageSummaries.filter(
summary => !!get(locals.pageInfo.data, [summary.id, 'pageSummary'])
);
for (const summary of pageSummaries) {
pugs[summary.id] = renderer.renderTemplate(summary.id, locals);
locals = merge({ pugs }, locals);
}
locals = merge({ pageSummaries: this.pageSummaries }, locals);
locals = merge({ pageSummaries }, locals);
return this.storageManager.writeHtmlForUrl(
renderer.renderTemplate('url/' + name, locals),
@ -299,13 +302,17 @@ class HTMLBuilder {
locals
);
const pugs = { pugs: {} };
for (var run of this.pageRuns) {
pugs.pugs[run.id] = renderer.renderTemplate(run.id, locals);
locals = merge(pugs, locals);
const pugs = {};
const pageRuns = this.pageRuns.filter(
run => !!get(locals.pageInfo.data, [run.id, 'run'])
);
for (const run of pageRuns) {
pugs[run.id] = renderer.renderTemplate(run.id, locals);
locals = merge({ pugs }, locals);
}
locals = merge({ pageRuns: this.pageRuns }, locals);
locals = merge({ pageRuns }, locals);
return this.storageManager.writeHtmlForUrl(
renderer.renderTemplate('url/run', locals),

View File

@ -12,63 +12,60 @@ http://codepen.io/oknoblich/pen/tfjFl
}
section {
display: none;
display: block;
padding: 20px 0 0;
border-top: 1px solid #ddd;
}
a[data-name="tabs"] {
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;
}
#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;
}
a[data-name="tabs"]:before {
//font-family: fontawesome;
font-weight: normal;
// margin-right: 10px;
}
a:before {
//font-family: fontawesome;
font-weight: normal;
// margin-right: 10px;
}
a[data-name="tabs"]:hover {
color: #888;
cursor: pointer;
}
a:hover {
color: #888;
cursor: pointer;
}
a[data-name="tabs"]:target:focus {
outline: none;
}
a:target:focus {
outline: none;
}
a[selected] {
color: #555;
border: 1px solid #ddd;
border-top: 2px solid $color--blue;
border-bottom: 1px solid #fff;
}
#summary[selected] ~ #summary-panel,
#coach[selected] ~ #coach-panel,
#browsertime[selected] ~ #browsertime-panel,
#pagexray[selected] ~ #pagexray-panel {
display: block;
a[selected] {
color: #555;
border: 1px solid #ddd;
border-top: 2px solid $color--blue;
border-bottom: 1px solid #fff;
}
}
@media screen and (max-width: 650px) {
a[data-name="tabs"] {
font-size: 0;
}
a[data-name="tabs"]:before {
margin: 0;
font-size: 18px;
#tabs {
a {
font-size: 0;
}
a:before {
margin: 0;
font-size: 18px;
}
}
}
@media screen and (max-width: 400px) {
a[data-name="tabs"] {
#tabs a {
padding: 13px;
}
}

View File

@ -0,0 +1,5 @@
{
"env": {
"browser": true
}
}

View File

@ -26,98 +26,99 @@ block content
include ./tabs/summaryTabs.pug
section#summary-panel
h2 Summary
p.small Summary of some of the most important metrics.
.row
.one-half.column
table
tr
th Metric
th Value
if d.coach && d.coach.pageSummary
#tabSections
section#summary-panel
h2 Summary
p.small Summary of some of the most important metrics.
.row
.one-half.column
table
tr
td Performance Score:
td #{d.coach.pageSummary.advice.performance.score}
if d.pagexray && d.pagexray.pageSummary
tr
td Total Page Size:
td #{h.size.format(d.pagexray.pageSummary.transferSize)}
if d.pagexray && d.pagexray.pageSummary
tr
td Requests:
td #{d.pagexray.pageSummary.requests}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.visualMetrics
tr
td First Visual Change [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.FirstVisualChange.median}
else if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.timings.firstPaint
tr
td First Paint [median]:
td #{d.browsertime.pageSummary.statistics.timings.firstPaint.median}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.visualMetrics
tr
td Speed Index [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.SpeedIndex.median}
tr
td Visual Complete 85% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete85.median}
tr
td Visual Complete 95% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete95.median}
tr
td Visual Complete 99% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete99.median}
tr
td Last Visual Change [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.LastVisualChange.median}
else if d.browsertime && d.browsertime.pageSummary
tr
td RUM Speed Index [median]:
td #{d.browsertime.pageSummary.statistics.timings.rumSpeedIndex.median}
if !d.browsertime && d.webpagetest
tr
td Render (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.render}
tr
td Speed Index (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.SpeedIndex}
tr
td Visual Complete 85% (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.visualComplete85}
tr
td Last Visual Change (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.lastVisualChange}
tr
td Requests:
td #{d.webpagetest.pageSummary.data.median.firstView.requestsFull}
th Metric
th Value
if d.coach && d.coach.pageSummary
tr
td Performance Score:
td #{d.coach.pageSummary.advice.performance.score}
if d.pagexray && d.pagexray.pageSummary
tr
td Total Page Size:
td #{h.size.format(d.pagexray.pageSummary.transferSize)}
if d.pagexray && d.pagexray.pageSummary
tr
td Requests:
td #{d.pagexray.pageSummary.requests}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.visualMetrics
tr
td First Visual Change [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.FirstVisualChange.median}
else if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.timings.firstPaint
tr
td First Paint [median]:
td #{d.browsertime.pageSummary.statistics.timings.firstPaint.median}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.visualMetrics
tr
td Speed Index [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.SpeedIndex.median}
tr
td Visual Complete 85% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete85.median}
tr
td Visual Complete 95% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete95.median}
tr
td Visual Complete 99% [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.VisualComplete99.median}
tr
td Last Visual Change [median]:
td #{d.browsertime.pageSummary.statistics.visualMetrics.LastVisualChange.median}
else if d.browsertime && d.browsertime.pageSummary
tr
td RUM Speed Index [median]:
td #{d.browsertime.pageSummary.statistics.timings.rumSpeedIndex.median}
if !d.browsertime && d.webpagetest
tr
td Render (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.render}
tr
td Speed Index (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.SpeedIndex}
tr
td Visual Complete 85% (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.visualComplete85}
tr
td Last Visual Change (first view):
td #{d.webpagetest.pageSummary.data.median.firstView.lastVisualChange}
tr
td Requests:
td #{d.webpagetest.pageSummary.data.median.firstView.requestsFull}
.one-half.column
if hasScreenShots
- var width = options.mobile ? 150 : '100%';
a(href='data/screenshots/0.png')
img.screenshot(src='data/screenshots/0.png', width=width, alt='Screenshot of run 1')
else if !d.browsertime && d.webpagetest
a(href='data/screenshots/0.png')
img.screenshot(src='data/screenshots/wpt-1-firstView.png', alt='Screenshot of run 1')
include ./summaryBox.pug
.one-half.column
if hasScreenShots
- var width = options.mobile ? 150 : '100%';
a(href='data/screenshots/0.png')
img.screenshot(src='data/screenshots/0.png', width=width, alt='Screenshot of run 1')
else if !d.browsertime && d.webpagetest
a(href='data/screenshots/0.png')
img.screenshot(src='data/screenshots/wpt-1-firstView.png', alt='Screenshot of run 1')
include ./summaryBox.pug
if d.browsertime && d.browsertime.har
include ./waterfall/index.pug
if d.browsertime && d.browsertime.har
include ./waterfall/index.pug
if d.coach && d.coach.pageSummary
section#coach-panel
include ./coach/index.pug
if d.coach && d.coach.pageSummary
section#coach-panel
include ./coach/index.pug
if d.browsertime && d.browsertime.pageSummary
section#browsertime-panel
include ./browsertime/index.pug
if d.browsertime && d.browsertime.pageSummary
section#browsertime-panel
include ./browsertime/index.pug
if d.pagexray && d.pagexray.pageSummary
section#pagexray-panel
include ./pagexray/index.pug
if d.pagexray && d.pagexray.pageSummary
section#pagexray-panel
include ./pagexray/index.pug
each pageSummary in pageSummaries
- var panelName = pageSummary.id + '-panel'
section(id=panelName)
p !{pugs[pageSummary.id]}
each pageSummary in pageSummaries
- var panelName = pageSummary.id + '-panel'
section(id=panelName)
p !{pugs[pageSummary.id]}

View File

@ -30,99 +30,100 @@ block content
.error= pageInfo.error
include ./tabs/runTabs.pug
section#summary-panel
h2 Summary
p.small Summary of some of the most important metrics.
.row
.one-half.column
table
tr
th Metric
th Value
if d.coach && d.coach.run
tr
td Performance score:
td #{d.coach.run.advice.performance.score}
if d.pagexray && d.pagexray.run
tr
td Total page size:
td #{h.size.format(d.pagexray.run.transferSize)}
if d.pagexray && d.pagexray.run
tr
td Requests:
td #{d.pagexray.run.requests}
if d.browsertime && d.browsertime.run && d.browsertime.run.visualMetrics
tr
td First Visual Change:
td #{d.browsertime.run.visualMetrics.FirstVisualChange}
else if d.browsertime && d.browsertime.run && d.browsertime.run.timings.firstPaint
tr
td First Paint:
td #{d.browsertime.run.timings.firstPaint}
if d.browsertime && d.browsertime.run && d.browsertime.run.visualMetrics
tr
td Speed Index:
td #{d.browsertime.run.visualMetrics.SpeedIndex}
tr
td Visual Complete 85%:
td #{d.browsertime.run.visualMetrics.VisualComplete85}
tr
td Visual Complete 95%:
td #{d.browsertime.run.visualMetrics.VisualComplete95}
tr
td Visual Complete 99%:
td #{d.browsertime.run.visualMetrics.VisualComplete99}
tr
td Last Visual Change:
td #{d.browsertime.run.visualMetrics.LastVisualChange}
else if d.browsertime && d.browsertime.run
tr
td RUM Speed Index:
td #{d.browsertime.run.timings.rumSpeedIndex}
if !d.browsertime && d.webpagetest
tr
td Render (first view):
td #{d.webpagetest.run.firstView.render}
tr
td Speed Index (first view):
td #{d.webpagetest.run.firstView.SpeedIndex}
tr
td Visual Complete 85% (first view):
td #{d.webpagetest.run.firstView.visualComplete85}
tr
td Last Visual Change (first view):
td #{d.webpagetest.run.firstView.lastVisualChange}
tr
td Requests:
td #{d.webpagetest.run.firstView.requestsFull}
#tabSections
section#summary-panel
h2 Summary
p.small Summary of some of the most important metrics.
.row
.one-half.column
table
tr
th Metric
th Value
if d.coach && d.coach.run
tr
td Performance score:
td #{d.coach.run.advice.performance.score}
if d.pagexray && d.pagexray.run
tr
td Total page size:
td #{h.size.format(d.pagexray.run.transferSize)}
if d.pagexray && d.pagexray.run
tr
td Requests:
td #{d.pagexray.run.requests}
if d.browsertime && d.browsertime.run && d.browsertime.run.visualMetrics
tr
td First Visual Change:
td #{d.browsertime.run.visualMetrics.FirstVisualChange}
else if d.browsertime && d.browsertime.run && d.browsertime.run.timings.firstPaint
tr
td First Paint:
td #{d.browsertime.run.timings.firstPaint}
if d.browsertime && d.browsertime.run && d.browsertime.run.visualMetrics
tr
td Speed Index:
td #{d.browsertime.run.visualMetrics.SpeedIndex}
tr
td Visual Complete 85%:
td #{d.browsertime.run.visualMetrics.VisualComplete85}
tr
td Visual Complete 95%:
td #{d.browsertime.run.visualMetrics.VisualComplete95}
tr
td Visual Complete 99%:
td #{d.browsertime.run.visualMetrics.VisualComplete99}
tr
td Last Visual Change:
td #{d.browsertime.run.visualMetrics.LastVisualChange}
else if d.browsertime && d.browsertime.run
tr
td RUM Speed Index:
td #{d.browsertime.run.timings.rumSpeedIndex}
if !d.browsertime && d.webpagetest
tr
td Render (first view):
td #{d.webpagetest.run.firstView.render}
tr
td Speed Index (first view):
td #{d.webpagetest.run.firstView.SpeedIndex}
tr
td Visual Complete 85% (first view):
td #{d.webpagetest.run.firstView.visualComplete85}
tr
td Last Visual Change (first view):
td #{d.webpagetest.run.firstView.lastVisualChange}
tr
td Requests:
td #{d.webpagetest.run.firstView.requestsFull}
.one-half.column
if hasScreenShots
- screenshotName = 'data/screenshots/' + runIndex + '.png'
- var width = options.mobile ? 150 : '100%';
a(href=screenshotName)
img.screenshot(src=screenshotName, width=width, alt='Screenshot')
else if !d.browsertime && d.webpagetest
- screenshotName = 'data/screenshots/wpt-' + (Number(runIndex)+1) + '-firstView.png'
a(href=screenshotName)
img.screenshot(src=screenshotName, alt='Screenshot')
.one-half.column
if hasScreenShots
- screenshotName = 'data/screenshots/' + runIndex + '.png'
- var width = options.mobile ? 150 : '100%';
a(href=screenshotName)
img.screenshot(src=screenshotName, width=width, alt='Screenshot')
else if !d.browsertime && d.webpagetest
- screenshotName = 'data/screenshots/wpt-' + (Number(runIndex)+1) + '-firstView.png'
a(href=screenshotName)
img.screenshot(src=screenshotName, alt='Screenshot')
if d.browsertime && d.browsertime.run && d.browsertime.run.har
include ./waterfall/index.pug
if d.browsertime && d.browsertime.run && d.browsertime.run.har
include ./waterfall/index.pug
if d.coach && d.coach.run
section#coach-panel
include ./coach/index.pug
if d.coach && d.coach.run
section#coach-panel
include ./coach/index.pug
if d.browsertime && d.browsertime.run
section#browsertime-panel
include ./browsertime/index.pug
if d.browsertime && d.browsertime.run
section#browsertime-panel
include ./browsertime/index.pug
if d.pagexray && d.pagexray.run
section#pagexray-panel
include ./pagexray/index.pug
if d.pagexray && d.pagexray.run
section#pagexray-panel
include ./pagexray/index.pug
each pageRun in pageRuns
- var panelName = pageRun.id + '-panel'
section(id=panelName)
p !{pugs[pageRun.id]}
each pageRun in pageRuns
- var panelName = pageRun.id + '-panel'
section(id=panelName)
p !{pugs[pageRun.id]}

View File

@ -1,20 +1,22 @@
a#summary(href='#summary', data-name='tabs', onclick='return selectTab(this, \"summary-panel\")', if-not-selected)
img(src= rootPath + 'img/summary64.png', height=32, alt='Summary')
| Summary
if d.coach && (d.coach.pageSummary || d.coach.run)
a#coach(href='#coach', data-name='tabs', onclick='return selectTab(this, \"coach-panel\")')
img(src= rootPath + 'img/coach64.png', height=32, alt='Coach')
| Coach
if d.browsertime && (d.browsertime.pageSummary || d.browsertime.run)
a#browsertime(href='#browsertime', data-name='tabs', onclick='return selectTab(this, \"browsertime-panel\")')
img(src= rootPath + 'img/browsertime64.png', height=32, alt='Browsertime')
| Browsertime
if d.pagexray && (d.pagexray.pageSummary || d.pagexray.run)
a#pagexray(href='#pagexray', data-name='tabs', onclick='return selectTab(this, \"pagexray-panel\")')
img(src= rootPath + 'img/xray64.png', height=32, alt='PageXray')
| PageXray
each pageRun in pageRuns
a(id=pageRun.id, href='#' + pageRun.id, data-name='tabs', onclick='return selectTab(this, \"' + pageRun.id + '-panel\"' +')')
| #{pageRun.name}
#tabs
a(id='summary', href='#summary', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/summary64.png', height=32, alt='Summary')
| Summary
if d.coach && d.coach.run
a(id='coach', href='#coach', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/coach64.png', height=32, alt='Coach')
| Coach
if d.browsertime && d.browsertime.run
a(id='browsertime', href='#browsertime', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/browsertime64.png', height=32, alt='Browsertime')
| Browsertime
if d.pagexray && d.pagexray.run
a(id='pagexray', href='#pagexray', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/pagexray64.png', height=32, alt='PageXray')
| PageXray
each pageRun in pageRuns
a(id=pageRun.id, href='#' + pageRun.id, onclick='return selectTab(this, true);')
| #{pageRun.name}
include ./scripts
script(type='text/javascript')
include ./scripts.js

View File

@ -0,0 +1,44 @@
window.addEventListener('DOMContentLoaded', function() {
let tabsRoot = document.querySelector('#tabs');
let currentTab = tabsRoot.querySelector(location.hash || 'a');
if (!currentTab) currentTab = tabsRoot.querySelector('a');
let sections = document.querySelectorAll('#tabSections section');
for (let i = 0; i < sections.length; i++) {
sections[i].style.display = 'none';
}
selectTab(currentTab, false);
});
function selectTab(newSelection, updateUrlFragment) {
let tabsRoot = document.querySelector('#tabs');
let sectionRoot = document.querySelector('#tabSections');
let previousSelection = tabsRoot.querySelector('[selected]');
if (previousSelection) {
previousSelection.removeAttribute('selected');
let section = sectionRoot.querySelector(
'#' + previousSelection.id + '-panel'
);
section.style.display = 'none';
}
newSelection.setAttribute('selected', '');
let section = sectionRoot.querySelector('#' + newSelection.id + '-panel');
section.style.display = 'block';
let charts = section.querySelectorAll('.ct-chart');
for (let i = 0; i < charts.length; i++) {
if (charts[i].__chartist__) {
charts[i].__chartist__.update();
}
}
if (updateUrlFragment && history.replaceState) {
history.replaceState(null, null, '#' + newSelection.id);
}
return false;
}

View File

@ -1,39 +0,0 @@
script(type='text/javascript').
function select(el) { el && el.setAttribute('selected', '') }
[].forEach.call(document.querySelectorAll('[data-name=tabs]'), function(el) {
if (location.hash === '#' + el.id) select(el);
});
if (!document.querySelectorAll('[data-name=tabs][selected]').length) {
select(document.querySelector('[if-not-selected]'));
}
function selectTab(el, panelId) {
var sel = document.querySelector('[data-name=tabs][selected]');
if (sel) sel.removeAttribute('selected');
select(el);
var sections = document.getElementsByTagName('section');
for (i = 0; i < sections.length; i++) {
sections[i].style.display = 'none';
}
if (panelId) {
var panel = document.getElementById(panelId);
panel.style.display = 'block';
}
var charts = document.querySelectorAll(".ct-chart");
for (i = 0; i < charts.length; i++) {
if (charts[i].__chartist__) {
charts[i].__chartist__.update();
}
}
if (history.replaceState) {
history.replaceState(null, null, el.href);
return false;
}
// IE9 fallback
}

View File

@ -1,20 +1,22 @@
a#summary(href='#summary', data-name='tabs', onclick='return selectTab(this, \"summary-panel\")', if-not-selected)
img(src= rootPath + 'img/summary64.png', height=32, alt='Summary')
| Summary
if d.coach && (d.coach.pageSummary || d.coach.run)
a#coach(href='#coach', data-name='tabs', onclick='return selectTab(this, \"coach-panel\")')
img(src= rootPath + 'img/coach64.png', height=32, alt='Coach')
| Coach
if d.browsertime && (d.browsertime.pageSummary || d.browsertime.run)
a#browsertime(href='#browsertime', data-name='tabs', onclick='return selectTab(this, \"browsertime-panel\")')
img(src= rootPath + 'img/browsertime64.png', height=32, alt='Browsertime')
| Browsertime
if d.pagexray && (d.pagexray.pageSummary || d.pagexray.run)
a#pagexray(href='#pagexray', data-name='tabs', onclick='return selectTab(this, \"pagexray-panel\")')
img(src= rootPath + 'img/xray64.png', height=32, alt='PageXray')
| PageXray
#tabs
a(id='summary', href='#summary', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/summary64.png', height=32, alt='Summary')
| Summary
if d.coach && d.coach.pageSummary
a(id='coach', href='#coach', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/coach64.png', height=32, alt='Coach')
| Coach
if d.browsertime && d.browsertime.pageSummary
a(id='browsertime', href='#browsertime', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/browsertime64.png', height=32, alt='Browsertime')
| Browsertime
if d.pagexray && d.pagexray.pageSummary
a(id='pagexray', href='#pagexray', onclick='return selectTab(this, true);')
img(src= rootPath + 'img/pagexray64.png', height=32, alt='PageXray')
| PageXray
each pageSummary in pageSummaries
a(id=pageSummary.id, href='#' + pageSummary.id, data-name='tabs', onclick='return selectTab(this, \"' + pageSummary.id + '-panel\"' +')')
| #{pageSummary.name}
a(id=pageSummary.id, href='#' + pageSummary.id, onclick='return selectTab(this, true);')
| #{pageSummary.name}
include ./scripts
script(type='text/javascript')
include ./scripts.js