UNPKG

highcharts

Version:
288 lines (287 loc) 9.07 kB
/* * * * (c) 2020-2026 Highsoft AS * * A commercial license may be required depending on use. * See www.highcharts.com/license * * * Authors: * - Sophie Bremer * * */ 'use strict'; import DataTable from '../../Data/DataTable.js'; import H from '../Globals.js'; const { composed } = H; import { addEvent, fireEvent, isNumber, merge, pushUnique, wrap } from '../../Shared/Utilities.js'; /* * * * Functions * * */ /** @internal */ function wrapSeriesGeneratePoints(proceed) { if (this.hasGroupedData) { return proceed.call(this); } const PointClass = this.pointClass, cropStart = this.cropStart || 0, data = this.data || [], points = [], processedXData = this.getColumn('x', true), processedYData = this.getColumn('y', true); let cursor, point; for (let i = 0, iEnd = processedXData.length; i < iEnd; ++i) { cursor = cropStart + i; point = data[cursor]; if (!point) { point = data[cursor] = new PointClass(this, processedYData[cursor], processedXData[i]); } point.index = cursor; points[i] = point; } this.data = data; this.points = points; fireEvent(this, 'afterGeneratePoints'); } /** @internal */ function wrapSeriesSetData(proceed, data = [], redraw = true, animation) { const datas = this.datas; if (this.hasGroupedData || !this.options.dataAsColumns) { return proceed.call(this, data, redraw, animation); } data = this.options.data = this.userOptions.data = (this.chart.options.chart.allowMutatingData ? (data || []) : merge(true, data)); const columns = {}, keys = (this.options.keys || this.parallelArrays).slice(); if (isNumber(data[0]) || keys.length === 1) { // First column is implicit index const xData = columns.x = []; for (let i = 0, iEnd = data.length; i < iEnd; ++i) { xData.push(this.autoIncrement()); } columns[keys[1] || 'y'] = data; } else { if (keys.indexOf('x') === -1 && keys.length > data.length) { // First column is implicit index const xData = columns.x = []; for (let i = 0, iEnd = data.length; i < iEnd; ++i) { xData.push(this.autoIncrement()); } } for (let i = 0, iEnd = Math.min(data.length, keys.length); i < iEnd; ++i) { if (data[i] instanceof Array) { columns[keys[i]] = data[i]; } } } datas.setTable(new DataTable({ columns, id: this.name })); } /* * * * Class * * */ /** @internal */ class DataSeriesAdditions { /* * * * Static Functions * * */ /** * @internal */ static compose(SeriesClass) { if (pushUnique(composed, 'Core.DataSeries')) { const seriesProto = SeriesClass.prototype; addEvent(SeriesClass, 'init', function () { this.datas = new DataSeriesAdditions(this); }); wrap(seriesProto, 'generatePoints', wrapSeriesGeneratePoints); wrap(seriesProto, 'setData', wrapSeriesSetData); } } /* * * * Constructor * * */ constructor(series) { this.unlisteners = []; const columns = {}, keys = series.parallelArrays; for (let i = 0, iEnd = keys.length; i < iEnd; ++i) { columns[keys[i]] = []; } this.series = series; this.table = new DataTable(); } /* * * * Functions * * */ /** * Triggers processing and redrawing * @internal */ processTable(redraw, animation) { const series = this.series; if (series.options.legendType === 'point') { series.processData(); series.generatePoints(); } if (redraw) { const chart = series.chart; series.isDirty = chart.isDirtyBox = true; series.isDirtyData = true; chart.redraw(animation); } } /** * Experimental integration of the data layer * @internal */ setTable(table, redraw = true, animation) { const series = this.series, anySeries = series, oldData = series.points, keys = series.parallelArrays, rowCount = table.getRowCount(); let key; if (oldData) { const xAxis = series.xAxis; series.colorCounter = 0; series.data = []; delete anySeries.points; delete anySeries.processedXData; delete anySeries.processedYData; delete anySeries.xIncrement; for (let i = 0, iEnd = keys.length; i < iEnd; ++i) { key = keys[i]; anySeries[`${key}Data`] = []; } for (let i = 0, iEnd = oldData.length; i < iEnd; ++i) { if (oldData[i] && !!oldData[i].destroy) { oldData[i].destroy(); } } if (xAxis) { xAxis.minRange = xAxis.userMinRange; } } let column, failure = false, indexAsX = false; for (let i = 0, iEnd = keys.length; i < iEnd; ++i) { key = keys[i]; column = table.getColumn(key, true); if (!column) { if (key === 'x') { indexAsX = true; continue; } else { failure = true; break; } } anySeries[`${key}Data`] = column; } if (failure) { // Fallback to index const columnIds = table.getColumnIds(), emptyColumn = []; emptyColumn.length = rowCount; let columnOffset = 0; if (columnIds.length === keys.length - 1) { // Table index becomes x columnOffset = 1; indexAsX = true; } for (let i = columnOffset, iEnd = keys.length; i < iEnd; ++i) { column = table.getColumn(columnIds[i], true); key = keys[i]; anySeries[`${key}Data`] = column || emptyColumn.slice(); } } this.indexAsX = indexAsX; if (indexAsX && keys.indexOf('x') !== -1) { column = []; for (let x = 0; x < rowCount; ++x) { column.push(series.autoIncrement()); } anySeries.xData = column; } this.syncOff(); this.table = table; if (redraw) { this.syncOn(); } this.processTable(redraw, oldData && animation); } /** * Stops synchronization of table changes with series. * @internal */ syncOff() { const unlisteners = this.unlisteners; for (let i = 0, iEnd = unlisteners.length; i < iEnd; ++i) { unlisteners[i](); } unlisteners.length = 0; } /** * Activates synchronization of table changes with series. * @internal */ syncOn() { if (this.unlisteners.length) { return; } const series = this.series, table = this.table, anySeries = series, onChange = (e) => { const type = e.type; if (type === 'afterDeleteColumns') { // Deletion affects all points this.setTable(table, true); return; } if (type === 'afterDeleteRows') { const { rowIndex, rowCount } = e; if (Array.isArray(rowIndex) || (rowIndex > 0 && rowIndex + rowCount < series.points.length)) { // Deletion affects trailing points this.setTable(table, true); return; } for (let i = rowIndex, iEnd = i + rowCount; i < iEnd; ++i) { series.removePoint(i, false); } } if (this.indexAsX) { if (type === 'afterSetCell') { anySeries.xData[e.rowIndex] = e.rowIndex; } else if (type === 'afterSetRows' && isNumber(e.rowIndex)) { for (let i = e.rowIndex, iEnd = i + e.rowCount; i < iEnd; ++i) { anySeries.xData[i] = series.autoIncrement(); } } } this.processTable(true); }; this.unlisteners.push(table.on('afterDeleteColumns', onChange), table.on('afterDeleteRows', onChange), table.on('afterSetCell', onChange), table.on('afterSetRows', onChange)); } } /* * * * Default Export * * */ /** @internal */ export default DataSeriesAdditions; /* * * * API Options * * */ /* * * Indicates data is structured as columns instead of rows. * * @type {boolean} * @since Future * @apioption plotOptions.series.dataAsColumns */ (''); // Keeps doclets above in JS file