highcharts
Version:
JavaScript charting framework
1,596 lines (1,581 loc) • 258 kB
JavaScript
/**
* @license Highcharts JS v12.2.0 (2025-04-07)
* @module highcharts/modules/data-tools
* @requires highcharts
*
* Highcharts
*
* (c) 2010-2025 Highsoft AS
*
* License: www.highcharts.com/license
*/
import * as __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__ from "../highcharts.src.js";
/******/ // The require scope
/******/ var __webpack_require__ = {};
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/************************************************************************/
;// external ["../highcharts.src.js","default"]
const external_highcharts_src_js_default_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"];
var external_highcharts_src_js_default_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_namespaceObject);
;// ./code/es-modules/Data/Modifiers/DataModifier.js
/* *
*
* (c) 2009-2025 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Authors:
* - Sophie Bremer
* - Gøran Slettemark
*
* */
const { addEvent, fireEvent, merge } = (external_highcharts_src_js_default_default());
/* *
*
* Class
*
* */
/**
* Abstract class to provide an interface for modifying a table.
*
*/
class DataModifier {
/* *
*
* Functions
*
* */
/**
* Runs a timed execution of the modifier on the given datatable.
* Can be configured to run multiple times.
*
* @param {DataTable} dataTable
* The datatable to execute
*
* @param {DataModifier.BenchmarkOptions} options
* Options. Currently supports `iterations` for number of iterations.
*
* @return {Array<number>}
* An array of times in milliseconds
*
*/
benchmark(dataTable, options) {
const results = [];
const modifier = this;
const execute = () => {
modifier.modifyTable(dataTable);
modifier.emit({
type: 'afterBenchmarkIteration'
});
};
const defaultOptions = {
iterations: 1
};
const { iterations } = merge(defaultOptions, options);
modifier.on('afterBenchmarkIteration', () => {
if (results.length === iterations) {
modifier.emit({
type: 'afterBenchmark',
results
});
return;
}
// Run again
execute();
});
const times = {
startTime: 0,
endTime: 0
};
// Add timers
modifier.on('modify', () => {
times.startTime = window.performance.now();
});
modifier.on('afterModify', () => {
times.endTime = window.performance.now();
results.push(times.endTime - times.startTime);
});
// Initial run
execute();
return results;
}
/**
* Emits an event on the modifier to all registered callbacks of this event.
*
* @param {DataModifier.Event} [e]
* Event object containing additonal event information.
*/
emit(e) {
fireEvent(this, e.type, e);
}
/**
* Returns a modified copy of the given table.
*
* @param {Highcharts.DataTable} table
* Table to modify.
*
* @param {DataEvent.Detail} [eventDetail]
* Custom information for pending events.
*
* @return {Promise<Highcharts.DataTable>}
* Table with `modified` property as a reference.
*/
modify(table, eventDetail) {
const modifier = this;
return new Promise((resolve, reject) => {
if (table.modified === table) {
table.modified = table.clone(false, eventDetail);
}
try {
resolve(modifier.modifyTable(table, eventDetail));
}
catch (e) {
modifier.emit({
type: 'error',
detail: eventDetail,
table
});
reject(e);
}
});
}
/**
* Applies partial modifications of a cell change to the property `modified`
* of the given modified table.
*
* @param {Highcharts.DataTable} table
* Modified table.
*
* @param {string} columnName
* Column name of changed cell.
*
* @param {number|undefined} rowIndex
* Row index of changed cell.
*
* @param {Highcharts.DataTableCellType} cellValue
* Changed cell value.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Highcharts.DataTable}
* Table with `modified` property as a reference.
*/
modifyCell(table,
/* eslint-disable @typescript-eslint/no-unused-vars */
columnName, rowIndex, cellValue, eventDetail
/* eslint-enable @typescript-eslint/no-unused-vars */
) {
return this.modifyTable(table);
}
/**
* Applies partial modifications of column changes to the property
* `modified` of the given table.
*
* @param {Highcharts.DataTable} table
* Modified table.
*
* @param {Highcharts.DataTableColumnCollection} columns
* Changed columns as a collection, where the keys are the column names.
*
* @param {number} [rowIndex=0]
* Index of the first changed row.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Highcharts.DataTable}
* Table with `modified` property as a reference.
*/
modifyColumns(table,
/* eslint-disable @typescript-eslint/no-unused-vars */
columns, rowIndex, eventDetail
/* eslint-enable @typescript-eslint/no-unused-vars */
) {
return this.modifyTable(table);
}
/**
* Applies partial modifications of row changes to the property `modified`
* of the given table.
*
* @param {Highcharts.DataTable} table
* Modified table.
*
* @param {Array<(Highcharts.DataTableRow|Highcharts.DataTableRowObject)>} rows
* Changed rows.
*
* @param {number} [rowIndex]
* Index of the first changed row.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Highcharts.DataTable}
* Table with `modified` property as a reference.
*/
modifyRows(table,
/* eslint-disable @typescript-eslint/no-unused-vars */
rows, rowIndex, eventDetail
/* eslint-enable @typescript-eslint/no-unused-vars */
) {
return this.modifyTable(table);
}
/**
* Registers a callback for a specific modifier event.
*
* @param {string} type
* Event type as a string.
*
* @param {DataEventEmitter.Callback} callback
* Function to register for an modifier callback.
*
* @return {Function}
* Function to unregister callback from the modifier event.
*/
on(type, callback) {
return addEvent(this, type, callback);
}
}
/* *
*
* Class Namespace
*
* */
/**
* Additionally provided types for modifier events and options.
*/
(function (DataModifier) {
/* *
*
* Declarations
*
* */
/* *
*
* Constants
*
* */
/**
* Registry as a record object with modifier names and their class
* constructor.
*/
DataModifier.types = {};
/* *
*
* Functions
*
* */
/**
* Adds a modifier class to the registry. The modifier class has to provide
* the `DataModifier.options` property and the `DataModifier.modifyTable`
* method to modify the table.
*
* @private
*
* @param {string} key
* Registry key of the modifier class.
*
* @param {DataModifierType} DataModifierClass
* Modifier class (aka class constructor) to register.
*
* @return {boolean}
* Returns true, if the registration was successful. False is returned, if
* their is already a modifier registered with this key.
*/
function registerType(key, DataModifierClass) {
return (!!key &&
!DataModifier.types[key] &&
!!(DataModifier.types[key] = DataModifierClass));
}
DataModifier.registerType = registerType;
})(DataModifier || (DataModifier = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ const Modifiers_DataModifier = (DataModifier);
;// ./code/es-modules/Data/ColumnUtils.js
/* *
*
* (c) 2020-2025 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Authors:
* - Dawid Dragula
*
* */
/**
* Utility functions for columns that can be either arrays or typed arrays.
* @private
*/
var ColumnUtils;
(function (ColumnUtils) {
/* *
*
* Declarations
*
* */
/* *
*
* Functions
*
* */
/**
* Sets the length of the column array.
*
* @param {DataTable.Column} column
* Column to be modified.
*
* @param {number} length
* New length of the column.
*
* @param {boolean} asSubarray
* If column is a typed array, return a subarray instead of a new array. It
* is faster `O(1)`, but the entire buffer will be kept in memory until all
* views to it are destroyed. Default is `false`.
*
* @return {DataTable.Column}
* Modified column.
*
* @private
*/
function setLength(column, length, asSubarray) {
if (Array.isArray(column)) {
column.length = length;
return column;
}
return column[asSubarray ? 'subarray' : 'slice'](0, length);
}
ColumnUtils.setLength = setLength;
/**
* Splices a column array.
*
* @param {DataTable.Column} column
* Column to be modified.
*
* @param {number} start
* Index at which to start changing the array.
*
* @param {number} deleteCount
* An integer indicating the number of old array elements to remove.
*
* @param {boolean} removedAsSubarray
* If column is a typed array, return a subarray instead of a new array. It
* is faster `O(1)`, but the entire buffer will be kept in memory until all
* views to it are destroyed. Default is `true`.
*
* @param {Array<number>|TypedArray} items
* The elements to add to the array, beginning at the start index. If you
* don't specify any elements, `splice()` will only remove elements from the
* array.
*
* @return {SpliceResult}
* Object containing removed elements and the modified column.
*
* @private
*/
function splice(column, start, deleteCount, removedAsSubarray, items = []) {
if (Array.isArray(column)) {
if (!Array.isArray(items)) {
items = Array.from(items);
}
return {
removed: column.splice(start, deleteCount, ...items),
array: column
};
}
const Constructor = Object.getPrototypeOf(column)
.constructor;
const removed = column[removedAsSubarray ? 'subarray' : 'slice'](start, start + deleteCount);
const newLength = column.length - deleteCount + items.length;
const result = new Constructor(newLength);
result.set(column.subarray(0, start), 0);
result.set(items, start);
result.set(column.subarray(start + deleteCount), start + items.length);
return {
removed: removed,
array: result
};
}
ColumnUtils.splice = splice;
})(ColumnUtils || (ColumnUtils = {}));
/* *
*
* Default Export
*
* */
/* harmony default export */ const Data_ColumnUtils = (ColumnUtils);
;// ./code/es-modules/Data/DataTableCore.js
/* *
*
* (c) 2009-2025 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Authors:
* - Sophie Bremer
* - Gøran Slettemark
* - Torstein Hønsi
*
* */
const { setLength, splice } = Data_ColumnUtils;
const { fireEvent: DataTableCore_fireEvent, objectEach, uniqueKey } = (external_highcharts_src_js_default_default());
/* *
*
* Class
*
* */
/**
* Class to manage columns and rows in a table structure. It provides methods
* to add, remove, and manipulate columns and rows, as well as to retrieve data
* from specific cells.
*
* @class
* @name Highcharts.DataTable
*
* @param {Highcharts.DataTableOptions} [options]
* Options to initialize the new DataTable instance.
*/
class DataTableCore {
/**
* Constructs an instance of the DataTable class.
*
* @example
* const dataTable = new Highcharts.DataTableCore({
* columns: {
* year: [2020, 2021, 2022, 2023],
* cost: [11, 13, 12, 14],
* revenue: [12, 15, 14, 18]
* }
* });
*
* @param {Highcharts.DataTableOptions} [options]
* Options to initialize the new DataTable instance.
*/
constructor(options = {}) {
/**
* Whether the ID was automatic generated or given in the constructor.
*
* @name Highcharts.DataTable#autoId
* @type {boolean}
*/
this.autoId = !options.id;
this.columns = {};
/**
* ID of the table for identification purposes.
*
* @name Highcharts.DataTable#id
* @type {string}
*/
this.id = (options.id || uniqueKey());
this.modified = this;
this.rowCount = 0;
this.versionTag = uniqueKey();
let rowCount = 0;
objectEach(options.columns || {}, (column, columnName) => {
this.columns[columnName] = column.slice();
rowCount = Math.max(rowCount, column.length);
});
this.applyRowCount(rowCount);
}
/* *
*
* Functions
*
* */
/**
* Applies a row count to the table by setting the `rowCount` property and
* adjusting the length of all columns.
*
* @private
* @param {number} rowCount The new row count.
*/
applyRowCount(rowCount) {
this.rowCount = rowCount;
objectEach(this.columns, (column, columnName) => {
if (column.length !== rowCount) {
this.columns[columnName] = setLength(column, rowCount);
}
});
}
/**
* Delete rows. Simplified version of the full
* `DataTable.deleteRows` method.
*
* @param {number} rowIndex
* The start row index
*
* @param {number} [rowCount=1]
* The number of rows to delete
*
* @return {void}
*
* @emits #afterDeleteRows
*/
deleteRows(rowIndex, rowCount = 1) {
if (rowCount > 0 && rowIndex < this.rowCount) {
let length = 0;
objectEach(this.columns, (column, columnName) => {
this.columns[columnName] =
splice(column, rowIndex, rowCount).array;
length = column.length;
});
this.rowCount = length;
}
DataTableCore_fireEvent(this, 'afterDeleteRows', { rowIndex, rowCount });
this.versionTag = uniqueKey();
}
/**
* Fetches the given column by the canonical column name. Simplified version
* of the full `DataTable.getRow` method, always returning by reference.
*
* @param {string} columnName
* Name of the column to get.
*
* @return {Highcharts.DataTableColumn|undefined}
* A copy of the column, or `undefined` if not found.
*/
getColumn(columnName,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asReference) {
return this.columns[columnName];
}
/**
* Retrieves all or the given columns. Simplified version of the full
* `DataTable.getColumns` method, always returning by reference.
*
* @param {Array<string>} [columnNames]
* Column names to retrieve.
*
* @return {Highcharts.DataTableColumnCollection}
* Collection of columns. If a requested column was not found, it is
* `undefined`.
*/
getColumns(columnNames,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
asReference) {
return (columnNames || Object.keys(this.columns)).reduce((columns, columnName) => {
columns[columnName] = this.columns[columnName];
return columns;
}, {});
}
/**
* Retrieves the row at a given index.
*
* @param {number} rowIndex
* Row index to retrieve. First row has index 0.
*
* @param {Array<string>} [columnNames]
* Column names to retrieve.
*
* @return {Record<string, number|string|undefined>|undefined}
* Returns the row values, or `undefined` if not found.
*/
getRow(rowIndex, columnNames) {
return (columnNames || Object.keys(this.columns)).map((key) => this.columns[key]?.[rowIndex]);
}
/**
* Sets cell values for a column. Will insert a new column, if not found.
*
* @param {string} columnName
* Column name to set.
*
* @param {Highcharts.DataTableColumn} [column]
* Values to set in the column.
*
* @param {number} [rowIndex]
* Index of the first row to change. (Default: 0)
*
* @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
* Custom information for pending events.
*
* @emits #setColumns
* @emits #afterSetColumns
*/
setColumn(columnName, column = [], rowIndex = 0, eventDetail) {
this.setColumns({ [columnName]: column }, rowIndex, eventDetail);
}
/**
* Sets cell values for multiple columns. Will insert new columns, if not
* found. Simplified version of the full `DataTableCore.setColumns`, limited
* to full replacement of the columns (undefined `rowIndex`).
*
* @param {Highcharts.DataTableColumnCollection} columns
* Columns as a collection, where the keys are the column names.
*
* @param {number} [rowIndex]
* Index of the first row to change. Ignored in the `DataTableCore`, as it
* always replaces the full column.
*
* @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
* Custom information for pending events.
*
* @emits #setColumns
* @emits #afterSetColumns
*/
setColumns(columns, rowIndex, eventDetail) {
let rowCount = this.rowCount;
objectEach(columns, (column, columnName) => {
this.columns[columnName] = column.slice();
rowCount = column.length;
});
this.applyRowCount(rowCount);
if (!eventDetail?.silent) {
DataTableCore_fireEvent(this, 'afterSetColumns');
this.versionTag = uniqueKey();
}
}
/**
* Sets cell values of a row. Will insert a new row if no index was
* provided, or if the index is higher than the total number of table rows.
* A simplified version of the full `DateTable.setRow`, limited to objects.
*
* @param {Record<string, number|string|undefined>} row
* Cell values to set.
*
* @param {number} [rowIndex]
* Index of the row to set. Leave `undefined` to add as a new row.
*
* @param {boolean} [insert]
* Whether to insert the row at the given index, or to overwrite the row.
*
* @param {Record<string, (boolean|number|string|null|undefined)>} [eventDetail]
* Custom information for pending events.
*
* @emits #afterSetRows
*/
setRow(row, rowIndex = this.rowCount, insert, eventDetail) {
const { columns } = this, indexRowCount = insert ? this.rowCount + 1 : rowIndex + 1;
objectEach(row, (cellValue, columnName) => {
let column = columns[columnName] ||
eventDetail?.addColumns !== false && new Array(indexRowCount);
if (column) {
if (insert) {
column = splice(column, rowIndex, 0, true, [cellValue]).array;
}
else {
column[rowIndex] = cellValue;
}
columns[columnName] = column;
}
});
if (indexRowCount > this.rowCount) {
this.applyRowCount(indexRowCount);
}
if (!eventDetail?.silent) {
DataTableCore_fireEvent(this, 'afterSetRows');
this.versionTag = uniqueKey();
}
}
}
/* *
*
* Default Export
*
* */
/* harmony default export */ const Data_DataTableCore = (DataTableCore);
/* *
*
* API Declarations
*
* */
/**
* A typed array.
* @typedef {Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array} Highcharts.TypedArray
* //**
* A column of values in a data table.
* @typedef {Array<boolean|null|number|string|undefined>|Highcharts.TypedArray} Highcharts.DataTableColumn
*/ /**
* A collection of data table columns defined by a object where the key is the
* column name and the value is an array of the column values.
* @typedef {Record<string, Highcharts.DataTableColumn>} Highcharts.DataTableColumnCollection
*/
/**
* Options for the `DataTable` or `DataTableCore` classes.
* @interface Highcharts.DataTableOptions
*/ /**
* The column options for the data table. The columns are defined by an object
* where the key is the column ID and the value is an array of the column
* values.
*
* @name Highcharts.DataTableOptions.columns
* @type {Highcharts.DataTableColumnCollection|undefined}
*/ /**
* Custom ID to identify the new DataTable instance.
*
* @name Highcharts.DataTableOptions.id
* @type {string|undefined}
*/
(''); // Keeps doclets above in JS file
;// ./code/es-modules/Data/DataTable.js
/* *
*
* (c) 2009-2025 Highsoft AS
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* Authors:
* - Sophie Bremer
* - Gøran Slettemark
* - Jomar Hønsi
* - Dawid Dragula
*
* */
const { addEvent: DataTable_addEvent, defined, extend, fireEvent: DataTable_fireEvent, isNumber, uniqueKey: DataTable_uniqueKey } = (external_highcharts_src_js_default_default());
/* *
*
* Class
*
* */
/**
* Class to manage columns and rows in a table structure. It provides methods
* to add, remove, and manipulate columns and rows, as well as to retrieve data
* from specific cells.
*
* @class
* @name Highcharts.DataTable
*
* @param {Highcharts.DataTableOptions} [options]
* Options to initialize the new DataTable instance.
*/
class DataTable extends Data_DataTableCore {
/* *
*
* Static Functions
*
* */
/**
* Tests whether a row contains only `null` values or is equal to
* DataTable.NULL. If all columns have `null` values, the function returns
* `true`. Otherwise, it returns `false` to indicate that the row contains
* at least one non-null value.
*
* @function Highcharts.DataTable.isNull
*
* @param {Highcharts.DataTableRow|Highcharts.DataTableRowObject} row
* Row to test.
*
* @return {boolean}
* Returns `true`, if the row contains only null, otherwise `false`.
*
* @example
* if (DataTable.isNull(row)) {
* // handle null row
* }
*/
static isNull(row) {
if (row === DataTable.NULL) {
return true;
}
if (row instanceof Array) {
if (!row.length) {
return false;
}
for (let i = 0, iEnd = row.length; i < iEnd; ++i) {
if (row[i] !== null) {
return false;
}
}
}
else {
const columnNames = Object.keys(row);
if (!columnNames.length) {
return false;
}
for (let i = 0, iEnd = columnNames.length; i < iEnd; ++i) {
if (row[columnNames[i]] !== null) {
return false;
}
}
}
return true;
}
/* *
*
* Constructor
*
* */
constructor(options = {}) {
super(options);
this.modified = this;
}
/* *
*
* Functions
*
* */
/**
* Returns a clone of this table. The cloned table is completely independent
* of the original, and any changes made to the clone will not affect
* the original table.
*
* @function Highcharts.DataTable#clone
*
* @param {boolean} [skipColumns]
* Whether to clone columns or not.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Highcharts.DataTable}
* Clone of this data table.
*
* @emits #cloneTable
* @emits #afterCloneTable
*/
clone(skipColumns, eventDetail) {
const table = this, tableOptions = {};
table.emit({ type: 'cloneTable', detail: eventDetail });
if (!skipColumns) {
tableOptions.columns = table.columns;
}
if (!table.autoId) {
tableOptions.id = table.id;
}
const tableClone = new DataTable(tableOptions);
if (!skipColumns) {
tableClone.versionTag = table.versionTag;
tableClone.originalRowIndexes = table.originalRowIndexes;
tableClone.localRowIndexes = table.localRowIndexes;
}
table.emit({
type: 'afterCloneTable',
detail: eventDetail,
tableClone
});
return tableClone;
}
/**
* Deletes columns from the table.
*
* @function Highcharts.DataTable#deleteColumns
*
* @param {Array<string>} [columnNames]
* Names of columns to delete. If no array is provided, all
* columns will be deleted.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Highcharts.DataTableColumnCollection|undefined}
* Returns the deleted columns, if found.
*
* @emits #deleteColumns
* @emits #afterDeleteColumns
*/
deleteColumns(columnNames, eventDetail) {
const table = this, columns = table.columns, deletedColumns = {}, modifiedColumns = {}, modifier = table.modifier, rowCount = table.rowCount;
columnNames = (columnNames || Object.keys(columns));
if (columnNames.length) {
table.emit({
type: 'deleteColumns',
columnNames,
detail: eventDetail
});
for (let i = 0, iEnd = columnNames.length, column, columnName; i < iEnd; ++i) {
columnName = columnNames[i];
column = columns[columnName];
if (column) {
deletedColumns[columnName] = column;
modifiedColumns[columnName] = new Array(rowCount);
}
delete columns[columnName];
}
if (!Object.keys(columns).length) {
table.rowCount = 0;
this.deleteRowIndexReferences();
}
if (modifier) {
modifier.modifyColumns(table, modifiedColumns, 0, eventDetail);
}
table.emit({
type: 'afterDeleteColumns',
columns: deletedColumns,
columnNames,
detail: eventDetail
});
return deletedColumns;
}
}
/**
* Deletes the row index references. This is useful when the original table
* is deleted, and the references are no longer needed. This table is
* then considered an original table or a table that has the same row's
* order as the original table.
*/
deleteRowIndexReferences() {
delete this.originalRowIndexes;
delete this.localRowIndexes;
// Here, in case of future need, can be implemented updating of the
// modified tables' row indexes references.
}
/**
* Deletes rows in this table.
*
* @function Highcharts.DataTable#deleteRows
*
* @param {number} [rowIndex]
* Index to start delete of rows. If not specified, all rows will be
* deleted.
*
* @param {number} [rowCount=1]
* Number of rows to delete.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Array<Highcharts.DataTableRow>}
* Returns the deleted rows, if found.
*
* @emits #deleteRows
* @emits #afterDeleteRows
*/
deleteRows(rowIndex, rowCount = 1, eventDetail) {
const table = this, deletedRows = [], modifiedRows = [], modifier = table.modifier;
table.emit({
type: 'deleteRows',
detail: eventDetail,
rowCount,
rowIndex: (rowIndex || 0)
});
if (typeof rowIndex === 'undefined') {
rowIndex = 0;
rowCount = table.rowCount;
}
if (rowCount > 0 && rowIndex < table.rowCount) {
const columns = table.columns, columnNames = Object.keys(columns);
for (let i = 0, iEnd = columnNames.length, column, deletedCells, columnName; i < iEnd; ++i) {
columnName = columnNames[i];
column = columns[columnName];
const result = Data_ColumnUtils.splice(column, rowIndex, rowCount);
deletedCells = result.removed;
columns[columnName] = column = result.array;
if (!i) {
table.rowCount = column.length;
}
for (let j = 0, jEnd = deletedCells.length; j < jEnd; ++j) {
deletedRows[j] = (deletedRows[j] || []);
deletedRows[j][i] = deletedCells[j];
}
modifiedRows.push(new Array(iEnd));
}
}
if (modifier) {
modifier.modifyRows(table, modifiedRows, (rowIndex || 0), eventDetail);
}
table.emit({
type: 'afterDeleteRows',
detail: eventDetail,
rowCount,
rowIndex: (rowIndex || 0),
rows: deletedRows
});
return deletedRows;
}
/**
* Emits an event on this table to all registered callbacks of the given
* event.
* @private
*
* @param {DataTable.Event} e
* Event object with event information.
*/
emit(e) {
if ([
'afterDeleteColumns',
'afterDeleteRows',
'afterSetCell',
'afterSetColumns',
'afterSetRows'
].includes(e.type)) {
this.versionTag = DataTable_uniqueKey();
}
DataTable_fireEvent(this, e.type, e);
}
/**
* Fetches a single cell value.
*
* @function Highcharts.DataTable#getCell
*
* @param {string} columnName
* Column name of the cell to retrieve.
*
* @param {number} rowIndex
* Row index of the cell to retrieve.
*
* @return {Highcharts.DataTableCellType|undefined}
* Returns the cell value or `undefined`.
*/
getCell(columnName, rowIndex) {
const table = this;
const column = table.columns[columnName];
if (column) {
return column[rowIndex];
}
}
/**
* Fetches a cell value for the given row as a boolean.
*
* @function Highcharts.DataTable#getCellAsBoolean
*
* @param {string} columnName
* Column name to fetch.
*
* @param {number} rowIndex
* Row index to fetch.
*
* @return {boolean}
* Returns the cell value of the row as a boolean.
*/
getCellAsBoolean(columnName, rowIndex) {
const table = this;
const column = table.columns[columnName];
return !!(column && column[rowIndex]);
}
/**
* Fetches a cell value for the given row as a number.
*
* @function Highcharts.DataTable#getCellAsNumber
*
* @param {string} columnName
* Column name or to fetch.
*
* @param {number} rowIndex
* Row index to fetch.
*
* @param {boolean} [useNaN]
* Whether to return NaN instead of `null` and `undefined`.
*
* @return {number|null}
* Returns the cell value of the row as a number.
*/
getCellAsNumber(columnName, rowIndex, useNaN) {
const table = this;
const column = table.columns[columnName];
let cellValue = (column && column[rowIndex]);
switch (typeof cellValue) {
case 'boolean':
return (cellValue ? 1 : 0);
case 'number':
return (isNaN(cellValue) && !useNaN ? null : cellValue);
}
cellValue = parseFloat(`${cellValue ?? ''}`);
return (isNaN(cellValue) && !useNaN ? null : cellValue);
}
/**
* Fetches a cell value for the given row as a string.
*
* @function Highcharts.DataTable#getCellAsString
*
* @param {string} columnName
* Column name to fetch.
*
* @param {number} rowIndex
* Row index to fetch.
*
* @return {string}
* Returns the cell value of the row as a string.
*/
getCellAsString(columnName, rowIndex) {
const table = this;
const column = table.columns[columnName];
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${(column && column[rowIndex])}`;
}
/**
* Fetches the given column by the canonical column name.
* This function is a simplified wrap of {@link getColumns}.
*
* @function Highcharts.DataTable#getColumn
*
* @param {string} columnName
* Name of the column to get.
*
* @param {boolean} [asReference]
* Whether to return the column as a readonly reference.
*
* @return {Highcharts.DataTableColumn|undefined}
* A copy of the column, or `undefined` if not found.
*/
getColumn(columnName, asReference) {
return this.getColumns([columnName], asReference)[columnName];
}
/**
* Fetches the given column by the canonical column name, and
* validates the type of the first few cells. If the first defined cell is
* of type number, it assumes for performance reasons, that all cells are of
* type number or `null`. Otherwise it will convert all cells to number
* type, except `null`.
*
* @deprecated
*
* @function Highcharts.DataTable#getColumnAsNumbers
*
* @param {string} columnName
* Name of the column to get.
*
* @param {boolean} [useNaN]
* Whether to use NaN instead of `null` and `undefined`.
*
* @return {Array<(number|null)>}
* A copy of the column, or an empty array if not found.
*/
getColumnAsNumbers(columnName, useNaN) {
const table = this, columns = table.columns;
const column = columns[columnName], columnAsNumber = [];
if (column) {
const columnLength = column.length;
if (useNaN) {
for (let i = 0; i < columnLength; ++i) {
columnAsNumber.push(table.getCellAsNumber(columnName, i, true));
}
}
else {
for (let i = 0, cellValue; i < columnLength; ++i) {
cellValue = column[i];
if (typeof cellValue === 'number') {
// Assume unmixed data for performance reasons
return column.slice();
}
if (cellValue !== null &&
typeof cellValue !== 'undefined') {
break;
}
}
for (let i = 0; i < columnLength; ++i) {
columnAsNumber.push(table.getCellAsNumber(columnName, i));
}
}
}
return columnAsNumber;
}
/**
* Fetches all column names.
*
* @function Highcharts.DataTable#getColumnNames
*
* @return {Array<string>}
* Returns all column names.
*/
getColumnNames() {
const table = this, columnNames = Object.keys(table.columns);
return columnNames;
}
/**
* Retrieves all or the given columns.
*
* @function Highcharts.DataTable#getColumns
*
* @param {Array<string>} [columnNames]
* Column names to retrieve.
*
* @param {boolean} [asReference]
* Whether to return columns as a readonly reference.
*
* @param {boolean} [asBasicColumns]
* Whether to transform all typed array columns to normal arrays.
*
* @return {Highcharts.DataTableColumnCollection}
* Collection of columns. If a requested column was not found, it is
* `undefined`.
*/
getColumns(columnNames, asReference, asBasicColumns) {
const table = this, tableColumns = table.columns, columns = {};
columnNames = (columnNames || Object.keys(tableColumns));
for (let i = 0, iEnd = columnNames.length, column, columnName; i < iEnd; ++i) {
columnName = columnNames[i];
column = tableColumns[columnName];
if (column) {
if (asReference) {
columns[columnName] = column;
}
else if (asBasicColumns && !Array.isArray(column)) {
columns[columnName] = Array.from(column);
}
else {
columns[columnName] = column.slice();
}
}
}
return columns;
}
/**
* Takes the original row index and returns the local row index in the
* modified table for which this function is called.
*
* @param {number} originalRowIndex
* Original row index to get the local row index for.
*
* @return {number|undefined}
* Returns the local row index or `undefined` if not found.
*/
getLocalRowIndex(originalRowIndex) {
const { localRowIndexes } = this;
if (localRowIndexes) {
return localRowIndexes[originalRowIndex];
}
return originalRowIndex;
}
/**
* Retrieves the modifier for the table.
* @private
*
* @return {Highcharts.DataModifier|undefined}
* Returns the modifier or `undefined`.
*/
getModifier() {
return this.modifier;
}
/**
* Takes the local row index and returns the index of the corresponding row
* in the original table.
*
* @param {number} rowIndex
* Local row index to get the original row index for.
*
* @return {number|undefined}
* Returns the original row index or `undefined` if not found.
*/
getOriginalRowIndex(rowIndex) {
const { originalRowIndexes } = this;
if (originalRowIndexes) {
return originalRowIndexes[rowIndex];
}
return rowIndex;
}
/**
* Retrieves the row at a given index. This function is a simplified wrap of
* {@link getRows}.
*
* @function Highcharts.DataTable#getRow
*
* @param {number} rowIndex
* Row index to retrieve. First row has index 0.
*
* @param {Array<string>} [columnNames]
* Column names in order to retrieve.
*
* @return {Highcharts.DataTableRow}
* Returns the row values, or `undefined` if not found.
*/
getRow(rowIndex, columnNames) {
return this.getRows(rowIndex, 1, columnNames)[0];
}
/**
* Returns the number of rows in this table.
*
* @function Highcharts.DataTable#getRowCount
*
* @return {number}
* Number of rows in this table.
*/
getRowCount() {
// @todo Implement via property getter `.length` browsers supported
return this.rowCount;
}
/**
* Retrieves the index of the first row matching a specific cell value.
*
* @function Highcharts.DataTable#getRowIndexBy
*
* @param {string} columnName
* Column to search in.
*
* @param {Highcharts.DataTableCellType} cellValue
* Cell value to search for. `NaN` and `undefined` are not supported.
*
* @param {number} [rowIndexOffset]
* Index offset to start searching.
*
* @return {number|undefined}
* Index of the first row matching the cell value.
*/
getRowIndexBy(columnName, cellValue, rowIndexOffset) {
const table = this;
const column = table.columns[columnName];
if (column) {
let rowIndex = -1;
if (Array.isArray(column)) {
// Normal array
rowIndex = column.indexOf(cellValue, rowIndexOffset);
}
else if (isNumber(cellValue)) {
// Typed array
rowIndex = column.indexOf(cellValue, rowIndexOffset);
}
if (rowIndex !== -1) {
return rowIndex;
}
}
}
/**
* Retrieves the row at a given index. This function is a simplified wrap of
* {@link getRowObjects}.
*
* @function Highcharts.DataTable#getRowObject
*
* @param {number} rowIndex
* Row index.
*
* @param {Array<string>} [columnNames]
* Column names and their order to retrieve.
*
* @return {Highcharts.DataTableRowObject}
* Returns the row values, or `undefined` if not found.
*/
getRowObject(rowIndex, columnNames) {
return this.getRowObjects(rowIndex, 1, columnNames)[0];
}
/**
* Fetches all or a number of rows.
*
* @function Highcharts.DataTable#getRowObjects
*
* @param {number} [rowIndex]
* Index of the first row to fetch. Defaults to first row at index `0`.
*
* @param {number} [rowCount]
* Number of rows to fetch. Defaults to maximal number of rows.
*
* @param {Array<string>} [columnNames]
* Column names and their order to retrieve.
*
* @return {Highcharts.DataTableRowObject}
* Returns retrieved rows.
*/
getRowObjects(rowIndex = 0, rowCount = (this.rowCount - rowIndex), columnNames) {
const table = this, columns = table.columns, rows = new Array(rowCount);
columnNames = (columnNames || Object.keys(columns));
for (let i = rowIndex, i2 = 0, iEnd = Math.min(table.rowCount, (rowIndex + rowCount)), column, row; i < iEnd; ++i, ++i2) {
row = rows[i2] = {};
for (const columnName of columnNames) {
column = columns[columnName];
row[columnName] = (column ? column[i] : void 0);
}
}
return rows;
}
/**
* Fetches all or a number of rows.
*
* @function Highcharts.DataTable#getRows
*
* @param {number} [rowIndex]
* Index of the first row to fetch. Defaults to first row at index `0`.
*
* @param {number} [rowCount]
* Number of rows to fetch. Defaults to maximal number of rows.
*
* @param {Array<string>} [columnNames]
* Column names and their order to retrieve.
*
* @return {Highcharts.DataTableRow}
* Returns retrieved rows.
*/
getRows(rowIndex = 0, rowCount = (this.rowCount - rowIndex), columnNames) {
const table = this, columns = table.columns, rows = new Array(rowCount);
columnNames = (columnNames || Object.keys(columns));
for (let i = rowIndex, i2 = 0, iEnd = Math.min(table.rowCount, (rowIndex + rowCount)), column, row; i < iEnd; ++i, ++i2) {
row = rows[i2] = [];
for (const columnName of columnNames) {
column = columns[columnName];
row.push(column ? column[i] : void 0);
}
}
return rows;
}
/**
* Returns the unique version tag of the current state of the table.
*
* @function Highcharts.DataTable#getVersionTag
*
* @return {string}
* Unique version tag.
*/
getVersionTag() {
return this.versionTag;
}
/**
* Checks for given column names.
*
* @function Highcharts.DataTable#hasColumns
*
* @param {Array<string>} columnNames
* Column names to check.
*
* @return {boolean}
* Returns `true` if all columns have been found, otherwise `false`.
*/
hasColumns(columnNames) {
const table = this, columns = table.columns;
for (let i = 0, iEnd = columnNames.length, columnName; i < iEnd; ++i) {
columnName = columnNames[i];
if (!columns[columnName]) {
return false;
}
}
return true;
}
/**
* Searches for a specific cell value.
*
* @function Highcharts.DataTable#hasRowWith
*
* @param {string} columnName
* Column to search in.
*
* @param {Highcharts.DataTableCellType} cellValue
* Cell value to search for. `NaN` and `undefined` are not supported.
*
* @return {boolean}
* True, if a row has been found, otherwise false.
*/
hasRowWith(columnName, cellValue) {
const table = this;
const column = table.columns[columnName];
// Normal array
if (Array.isArray(column)) {
return (column.indexOf(cellValue) !== -1);
}
// Typed array
if (defined(cellValue) && Number.isFinite(cellValue)) {
return (column.indexOf(+cellValue) !== -1);
}
return false;
}
/**
* Registers a callback for a specific event.
*
* @function Highcharts.DataTable#on
*
* @param {string} type
* Event type as a string.
*
* @param {Highcharts.EventCallbackFunction<Highcharts.DataTable>} callback
* Function to register for an event callback.
*
* @return {Function}
* Function to unregister callback from the event.
*/
on(type, callback) {
return DataTable_addEvent(this, type, callback);
}
/**
* Renames a column of cell values.
*
* @function Highcharts.DataTable#renameColumn
*
* @param {string} columnName
* Name of the column to be renamed.
*
* @param {string} newColumnName
* New name of the column. An existing column with the same name will be
* replaced.
*
* @return {boolean}
* Returns `true` if successful, `false` if the column was not found.
*/
renameColumn(columnName, newColumnName) {
const table = this, columns = table.columns;
if (columns[columnName]) {
if (columnName !== newColumnName) {
columns[newColumnName] = columns[columnName];
delete columns[columnName];
}
return true;
}
return false;
}
/**
* Sets a cell value based on the row index and column. Will
* insert a new column, if not found.
*
* @function Highcharts.DataTable#setCell
*
* @param {string} columnName
* Column name to set.
*
* @param {number|undefined} rowIndex
* Row index to set.
*
* @param {Highcharts.DataTableCellType} cellValue
* Cell value to set.
*
* @param {Highcharts.DataTableEventDetail} [eventDetail]
* Custom information for pending events.
*
* @emits #setCell
* @emits #afterSetCell
*/
setCell(columnName, rowIndex, cellValue, eventDetail) {
const table = this, columns = table.columns, modifier = table.modifier;
let column = columns[columnName];
if (column && column[rowIndex] === cellValue) {
return;
}
table.emit({
type: 'setCell',
cellValue,
columnName: columnName,
detail: eventDetail,
rowIndex
});
if (!column) {
column = columns[columnName] = new Array(table.rowCount);
}
if (rowIndex >= table.rowCount) {
table.rowCount = (rowIndex + 1);
}
column[rowIndex] = cellValue;
if (modifier) {
modifier.modifyCell(table, columnName, rowIndex, cellValue);
}
table.emit({
type: 'afterSetCell',
cellValue,
columnName: columnName,
detail: eventDetail,
rowIndex
});
}
/**