window.benchmarkClient = { displayUnit: 'runs/min', iterationCount: 10, stepCount: null, suitesCount: null, _measuredValuesList: [], _finishedTestCount: 0, _progressCompleted: null, willAddTestFrame: function (frame) { var main = document.querySelector('main'); var style = getComputedStyle(main); frame.style.left = main.offsetLeft + parseInt(style.borderLeftWidth) + parseInt(style.paddingLeft) + 'px'; frame.style.top = main.offsetTop + parseInt(style.borderTopWidth) + parseInt(style.paddingTop) + 'px'; }, willRunTest: function (suite, test) { document.getElementById('info').textContent = suite.name + ' ( ' + this._finishedTestCount + ' / ' + this.stepCount + ' )'; }, didRunTest: function () { this._finishedTestCount++; this._progressCompleted.style.width = (this._finishedTestCount * 100 / this.stepCount) + '%'; }, didRunSuites: function (measuredValues) { this._measuredValuesList.push(measuredValues); }, willStartFirstIteration: function () { this._measuredValuesList = []; this._finishedTestCount = 0; this._progressCompleted = document.getElementById('progress-completed'); document.getElementById('logo-link').onclick = function (event) { event.preventDefault(); return false; } }, didFinishLastIteration: function () { document.getElementById('logo-link').onclick = null; var results = this._computeResults(this._measuredValuesList, this.displayUnit); this._updateGaugeNeedle(results.mean); document.getElementById('result-number').textContent = results.formattedMean; if (results.formattedDelta) document.getElementById('confidence-number').textContent = '\u00b1 ' + results.formattedDelta; this._populateDetailedResults(results.formattedValues); document.getElementById('results-with-statistics').textContent = results.formattedMeanAndDelta; if (this.displayUnit == 'ms') { document.getElementById('show-summary').style.display = 'none'; showResultDetails(); } else showResultsSummary(); }, _computeResults: function (measuredValuesList, displayUnit) { var suitesCount = this.suitesCount; function valueForUnit(measuredValues) { if (displayUnit == 'ms') return measuredValues.geomean; return measuredValues.score; } function sigFigFromPercentDelta(percentDelta) { return Math.ceil(-Math.log(percentDelta)/Math.log(10)) + 3; } function toSigFigPrecision(number, sigFig) { var nonDecimalDigitCount = number < 1 ? 0 : (Math.floor(Math.log(number)/Math.log(10)) + 1); return number.toPrecision(Math.max(nonDecimalDigitCount, Math.min(6, sigFig))); } var values = measuredValuesList.map(valueForUnit); var sum = values.reduce(function (a, b) { return a + b; }, 0); var arithmeticMean = sum / values.length; var meanSigFig = 4; var formattedDelta; var formattedPercentDelta; if (window.Statistics) { var delta = Statistics.confidenceIntervalDelta(0.95, values.length, sum, Statistics.squareSum(values)); if (!isNaN(delta)) { var percentDelta = delta * 100 / arithmeticMean; meanSigFig = sigFigFromPercentDelta(percentDelta); formattedDelta = toSigFigPrecision(delta, 2); formattedPercentDelta = toSigFigPrecision(percentDelta, 2) + '%'; } } var formattedMean = toSigFigPrecision(arithmeticMean, Math.max(meanSigFig, 3)); return { formattedValues: values.map(function (value) { return toSigFigPrecision(value, 4) + ' ' + displayUnit; }), mean: arithmeticMean, formattedMean: formattedMean, formattedDelta: formattedDelta, formattedMeanAndDelta: formattedMean + (formattedDelta ? ' \xb1 ' + formattedDelta + ' (' + formattedPercentDelta + ')' : ''), }; }, _addDetailedResultsRow: function (table, iterationNumber, value) { var row = document.createElement('tr'); var th = document.createElement('th'); th.textContent = 'Iteration ' + (iterationNumber + 1); var td = document.createElement('td'); td.textContent = value; row.appendChild(th); row.appendChild(td); table.appendChild(row); }, _updateGaugeNeedle: function (rpm) { var needleAngle = Math.max(0, Math.min(rpm, 140)) - 70; var needleRotationValue = 'rotate(' + needleAngle + 'deg)'; var gaugeNeedleElement = document.querySelector('#summarized-results > .gauge .needle'); gaugeNeedleElement.style.setProperty('-webkit-transform', needleRotationValue); gaugeNeedleElement.style.setProperty('-moz-transform', needleRotationValue); gaugeNeedleElement.style.setProperty('-ms-transform', needleRotationValue); gaugeNeedleElement.style.setProperty('transform', needleRotationValue); }, _populateDetailedResults: function (formattedValues) { var resultsTables = document.querySelectorAll('.results-table'); var i = 0; resultsTables[0].innerHTML = ''; for (; i < Math.ceil(formattedValues.length / 2); i++) this._addDetailedResultsRow(resultsTables[0], i, formattedValues[i]); resultsTables[1].innerHTML = ''; for (; i < formattedValues.length; i++) this._addDetailedResultsRow(resultsTables[1], i, formattedValues[i]); }, prepareUI: function () { window.addEventListener('popstate', function (event) { if (event.state) { var sectionToShow = event.state.section; if (sectionToShow) { var sections = document.querySelectorAll('main > section'); for (var i = 0; i < sections.length; i++) { if (sections[i].id === sectionToShow) return showSection(sectionToShow, false); } } } return showSection('home', false); }, false); function updateScreenSize() { // FIXME: Detect when the window size changes during the test. var screenIsTooSmall = window.innerWidth < 850 || window.innerHeight < 650; document.getElementById('screen-size').textContent = window.innerWidth + 'px by ' + window.innerHeight + 'px'; document.getElementById('screen-size-warning').style.display = screenIsTooSmall ? null : 'none'; } window.addEventListener('resize', updateScreenSize); updateScreenSize(); } } function enableOneSuite(suites, suiteToEnable) { suiteToEnable = suiteToEnable.toLowerCase(); var found = false; for (var i = 0; i < suites.length; i++) { var currentSuite = suites[i]; if (currentSuite.name.toLowerCase() == suiteToEnable) { currentSuite.disabled = false; found = true; } else currentSuite.disabled = true; } return found; } function startBenchmark() { if (location.search.length > 1) { var parts = location.search.substring(1).split('&'); for (var i = 0; i < parts.length; i++) { var keyValue = parts[i].split('='); var key = keyValue[0]; var value = keyValue[1]; switch (key) { case 'unit': if (value == 'ms') benchmarkClient.displayUnit = 'ms'; else console.error('Invalid unit: ' + value); break; case 'iterationCount': var parsedValue = parseInt(value); if (!isNaN(parsedValue)) benchmarkClient.iterationCount = parsedValue; else console.error('Invalid iteration count: ' + value); break; case 'suite': if (!enableOneSuite(Suites, value)) { alert('Suite "' + value + '" does not exist. No tests to run.'); return false; } break; } } } var enabledSuites = Suites.filter(function (suite) { return !suite.disabled; }); var totalSubtestsCount = enabledSuites.reduce(function (testsCount, suite) { return testsCount + suite.tests.length; }, 0); benchmarkClient.stepCount = benchmarkClient.iterationCount * totalSubtestsCount; benchmarkClient.suitesCount = enabledSuites.length; var runner = new BenchmarkRunner(Suites, benchmarkClient); runner.runMultipleIterations(benchmarkClient.iterationCount); return true; } function showSection(sectionIdentifier, pushState) { var currentSectionElement = document.querySelector('section.selected'); console.assert(currentSectionElement); var newSectionElement = document.getElementById(sectionIdentifier); console.assert(newSectionElement); currentSectionElement.classList.remove('selected'); newSectionElement.classList.add('selected'); if (pushState) history.pushState({section: sectionIdentifier}, document.title); } function showHome() { showSection('home', true); } function startTest() { if (startBenchmark()) showSection('running'); } function showResultsSummary() { showSection('summarized-results', true); } function showResultDetails() { showSection('detailed-results', true); } function showAbout() { showSection('about', true); } window.addEventListener('DOMContentLoaded', function () { if (benchmarkClient.prepareUI) benchmarkClient.prepareUI(); });