| // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| import dom from "./dom.js"; |
| import scales from "./scales.js"; |
| import state from "./state.js"; |
| import url from "./url.js"; |
| |
| var chart = null; |
| var dataProvider = null; |
| |
| function getBenchmarkColors(theChart) { |
| const benchmarkColors = {}; |
| for (var datasetIndex = 0; |
| datasetIndex < theChart.data.datasets.length; |
| datasetIndex++) { |
| if (theChart.getDatasetMeta(datasetIndex).hidden) { |
| continue; |
| } |
| const dataset = theChart.data.datasets[datasetIndex]; |
| const benchmark = dataset.benchmark; |
| const benchmarkColor = dataset.borderColor; |
| if (!(benchmark in benchmarkColors)) { |
| benchmarkColors[benchmark] = benchmarkColor; |
| } |
| } |
| return benchmarkColors; |
| } |
| |
| function get() { |
| return chart; |
| } |
| |
| function getData() { |
| const filteredCommits = state.commits(state.zoom); |
| return dataProvider(filteredCommits); |
| } |
| |
| function getDataLabelFormatter(value, context) { |
| var percentageChange = getDataPercentageChange(context); |
| var percentageChangeTwoDecimals = Math.round(percentageChange * 100) / 100; |
| var glyph = percentageChange < 0 ? '▼' : '▲'; |
| return glyph + ' ' + percentageChangeTwoDecimals + '%'; |
| } |
| |
| function getDataPercentageChange(context) { |
| var i = context.dataIndex; |
| var value = context.dataset.data[i]; |
| var j = i; |
| var previousValue; |
| do { |
| if (j == 0) { |
| return null; |
| } |
| previousValue = context.dataset.data[--j]; |
| } while (previousValue === undefined || isNaN(previousValue)); |
| return (value - previousValue) / previousValue * 100; |
| } |
| |
| function initializeChart(options) { |
| chart = new Chart(dom.canvas, { |
| data: getData(), |
| options: options, |
| plugins: [ChartDataLabels] |
| }); |
| // Hide disabled legends. |
| if (state.selectedLegends.size < state.legends.size) { |
| update(false, true); |
| } else { |
| update(false, false); |
| } |
| document.addEventListener('keydown', e => { |
| state.handleKeyDownEvent(e, () => update(true, false)); |
| }); |
| } |
| |
| function setDataProvider(theDataProvider) { |
| dataProvider = theDataProvider; |
| } |
| |
| function update(dataChanged, legendsChanged) { |
| console.assert(state.zoom.left <= state.zoom.right); |
| |
| // Update datasets. |
| if (dataChanged) { |
| const newData = getData(); |
| Object.assign(chart.data, newData); |
| // Update chart. |
| chart.update(); |
| } |
| |
| // Update legends. |
| if (legendsChanged || (dataChanged && state.selectedLegends.size < state.legends.size)) { |
| for (var datasetIndex = 0; |
| datasetIndex < chart.data.datasets.length; |
| datasetIndex++) { |
| const datasetMeta = chart.getDatasetMeta(datasetIndex); |
| datasetMeta.hidden = !state.isLegendSelected(datasetMeta.label); |
| } |
| |
| // Update scales. |
| scales.update(chart.options.scales); |
| |
| // Update chart. |
| chart.update(); |
| } |
| |
| dom.updateBenchmarkColors(getBenchmarkColors(chart)); |
| dom.updateChartNavigation(); |
| url.updateHash(state); |
| } |
| |
| export default { |
| get: get, |
| getDataLabelFormatter: getDataLabelFormatter, |
| getDataPercentageChange: getDataPercentageChange, |
| initializeChart: initializeChart, |
| setDataProvider: setDataProvider, |
| update: update |
| }; |