|  | // 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 | 
|  | }; |