resources/perf.webkit.org/public/v3/components/results-table.js (224 lines of code) (raw):

class ResultsTable extends ComponentBase { constructor(name) { super(name); this._repositoryList = []; this._analysisResultsView = null; } setAnalysisResultsView(analysisResultsView) { console.assert(analysisResultsView instanceof AnalysisResultsView); this._analysisResultsView = analysisResultsView; this.enqueueToRender(); } renderTable(valueFormatter, rowGroups, headingLabel, buildHeaders = (headers) => headers, buildColumns = (columns, row, rowIndex) => columns) { Instrumentation.startMeasuringTime('ResultsTable', 'renderTable'); const [repositoryList, constantCommits] = this._computeRepositoryList(rowGroups); const barGraphGroup = new BarGraphGroup(); barGraphGroup.setShowLabels(true); const element = ComponentBase.createElement; let hasGroupHeading = false; const tableBodies = rowGroups.map((group) => { const groupHeading = group.heading; const revisionSupressionCount = {}; hasGroupHeading = hasGroupHeading || groupHeading; return element('tbody', group.rows.map((row, rowIndex) => { const cells = []; if (groupHeading !== undefined && !rowIndex) cells.push(element('th', {rowspan: group.rows.length}, groupHeading)); cells.push(element('td', row.heading())); if (row.labelForWholeRow()) cells.push(element('td', {class: 'whole-row-label', colspan: repositoryList.length + 1}, row.labelForWholeRow())); else { cells.push(element('td', row.resultContent(valueFormatter, barGraphGroup))); cells.push(this._createRevisionListCells(repositoryList, revisionSupressionCount, group, row.commitSet(), rowIndex)); } return element('tr', buildColumns(cells, row, rowIndex)); })); }); this.renderReplace(this.content().querySelector('table'), [ tableBodies.length ? element('thead', [ buildHeaders([ ComponentBase.createElement('th', {colspan: hasGroupHeading ? 2 : 1}, headingLabel), element('th', 'Result'), repositoryList.map((repository) => element('th', repository.label())), ]), ]) : [], tableBodies, ]); this.renderReplace(this.content('constant-commits'), constantCommits.map((commit) => element('li', commit.title()))); Instrumentation.endMeasuringTime('ResultsTable', 'renderTable'); } _createRevisionListCells(repositoryList, revisionSupressionCount, testGroup, commitSet, rowIndex) { const element = ComponentBase.createElement; const link = ComponentBase.createLink; const cells = []; for (const repository of repositoryList) { const commit = commitSet ? commitSet.commitForRepository(repository) : null; if (revisionSupressionCount[repository.id()]) { revisionSupressionCount[repository.id()]--; continue; } let succeedingRowIndex = rowIndex + 1; while (succeedingRowIndex < testGroup.rows.length) { const succeedingCommitSet = testGroup.rows[succeedingRowIndex].commitSet(); if (succeedingCommitSet && commit != succeedingCommitSet.commitForRepository(repository)) break; succeedingRowIndex++; } const rowSpan = succeedingRowIndex - rowIndex; const attributes = {class: 'revision'}; if (rowSpan > 1) { revisionSupressionCount[repository.id()] = rowSpan - 1; attributes['rowspan'] = rowSpan; } if (rowIndex + rowSpan >= testGroup.rows.length) attributes['class'] += ' lastRevision'; let content = 'Missing'; if (commit) { const url = commit.url(); content = url ? link(commit.label(), url) : commit.label(); } cells.push(element('td', attributes, content)); } return cells; } _computeRepositoryList(rowGroups) { const allRepositories = Repository.sortByNamePreferringOnesWithURL(Repository.all()); const commitSets = []; for (let group of rowGroups) { for (let row of group.rows) { const commitSet = row.commitSet(); if (commitSet) commitSets.push(commitSet); } } if (!commitSets.length) return [[], []]; const changedRepositorySet = new Set; const constantCommits = new Set; for (let repository of allRepositories) { const someCommit = commitSets[0].commitForRepository(repository); if (CommitSet.containsMultipleCommitsForRepository(commitSets, repository)) changedRepositorySet.add(repository); else if (someCommit) constantCommits.add(someCommit); } return [allRepositories.filter((repository) => changedRepositorySet.has(repository)), [...constantCommits]]; } static htmlTemplate() { return `<table class="results-table"></table><ul id="constant-commits"></ul>`; } static cssTemplate() { return ` table.results-table { border-collapse: collapse; border: solid 0px #ccc; font-size: 0.9rem; } .results-table th { text-align: center; font-weight: inherit; font-size: 1rem; padding: 0.2rem; } .results-table td { height: 1.4rem; text-align: center; } .results-table td.whole-row-label { text-align: left; } .results-table thead { color: #c93; } .results-table td.revision { vertical-align: top; position: relative; } .results-table tr:not(:last-child) td.revision:after { display: block; content: ''; position: absolute; left: calc(50% - 0.25rem); bottom: -0.4rem; border-style: solid; border-width: 0.25rem; border-color: #999 transparent transparent transparent; } .results-table tr:not(:last-child) td.revision:before { display: block; content: ''; position: absolute; left: calc(50% - 1px); top: 1.2rem; height: calc(100% - 1.4rem); border: solid 1px #999; } .results-table tr:not(:last-child) td.revision.lastRevision:after { bottom: -0.2rem; } .results-table tr:not(:last-child) td.revision.lastRevision:before { height: calc(100% - 1.6rem); } .results-table tbody:not(:first-child) tr:first-child th, .results-table tbody:not(:first-child) tr:first-child td { border-top: solid 1px #ccc; } .results-table bar-graph { display: block; width: 7rem; height: 1rem; } #constant-commits { list-style: none; margin: 0; padding: 0.5rem 0 0 0.5rem; font-size: 0.8rem; } #constant-commits:empty { padding: 0; } #constant-commits li { display: inline; } #constant-commits li:not(:last-child):after { content: ', '; } `; } } class ResultsTableRow { constructor(heading, commitSet) { this._heading = heading; this._result = null; this._link = null; this._label = '-'; this._commitSet = commitSet; this._labelForWholeRow = null; } heading() { return this._heading; } commitSet() { return this._commitSet; } setResult(result) { this._result = result; } setLink(link, label) { this._link = link; this._label = label; } setLabelForWholeRow(label) { this._labelForWholeRow = label; } labelForWholeRow() { return this._labelForWholeRow; } resultContent(valueFormatter, barGraphGroup) { let resultContent = this._label; if (this._result) { const value = this._result.value; const interval = this._result.interval; let label = valueFormatter(value); if (interval) label += ' \u00B1' + ((value - interval[0]) * 100 / value).toFixed(2) + '%'; resultContent = barGraphGroup.addBar([value], [label], null, null, '#ccc', null); } return this._link ? ComponentBase.createLink(resultContent, this._label, this._link) : resultContent; } }