hyperformula
Version:
HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas
1,411 lines (1,409 loc) • 168 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.HyperFormula = void 0;
var _AbsoluteCellRange = require("./AbsoluteCellRange");
var _ArgumentSanitization = require("./ArgumentSanitization");
var _BuildEngineFactory = require("./BuildEngineFactory");
var _Cell = require("./Cell");
var _CellContentParser = require("./CellContentParser");
var _Config = require("./Config");
var _DateTimeHelper = require("./DateTimeHelper");
var _Destroy = require("./Destroy");
var _Emitter = require("./Emitter");
var _errors = require("./errors");
var _i18n = require("./i18n");
var _FunctionRegistry = require("./interpreter/FunctionRegistry");
var _Operations = require("./Operations");
var _parser2 = require("./parser");
/**
* @license
* Copyright (c) 2025 Handsoncode. All rights reserved.
*/
/**
* This is a class for creating HyperFormula instance, all the following public methods
* are related to this class.
*
* The instance can be created only by calling one of the static methods
* `buildFromArray`, `buildFromSheets` or `buildEmpty` and should be disposed of with the
* `destroy` method when it's no longer needed to free the resources.
*
* The instance can be seen as a workbook where worksheets can be created and
* manipulated. They are organized within a widely known structure of columns and rows
* which can be manipulated as well. The smallest possible data unit are the cells, which
* may contain simple values or formulas to be calculated.
*
* All CRUD methods are called directly on HyperFormula instance and will trigger
* corresponding lifecycle events. The events are marked accordingly, as well as thrown
* errors, so they can be correctly handled.
*/
class HyperFormula {
/**
* Constructor
*
* @internal
*/
constructor(_config, _stats, _dependencyGraph, _columnSearch, _parser, _unparser, _cellContentParser, _evaluator, _lazilyTransformingAstService, _crudOperations, _exporter, _namedExpressions, _serialization, _functionRegistry) {
this._config = _config;
this._stats = _stats;
this._dependencyGraph = _dependencyGraph;
this._columnSearch = _columnSearch;
this._parser = _parser;
this._unparser = _unparser;
this._cellContentParser = _cellContentParser;
this._evaluator = _evaluator;
this._lazilyTransformingAstService = _lazilyTransformingAstService;
this._crudOperations = _crudOperations;
this._exporter = _exporter;
this._namedExpressions = _namedExpressions;
this._serialization = _serialization;
this._functionRegistry = _functionRegistry;
this._emitter = new _Emitter.Emitter();
this._evaluationSuspended = false;
}
/**
* Returns all of HyperFormula's default [configuration options](/guide/configuration-options.md).
*
* @example
* ```js
* // returns all default configuration options
* const defaultConfig = HyperFormula.defaultConfig;
* ```
*
* @category Static Accessors
*/
static get defaultConfig() {
return (0, _Config.getDefaultConfig)();
}
/**
* Calls the `graph` method on the dependency graph.
* Allows for executing `graph` directly, without a need to refer to `dependencyGraph`.
*
* @internal
*/
get graph() {
return this.dependencyGraph.graph;
}
/**
* Calls the `rangeMapping` method on the dependency graph.
* Allows for executing `rangeMapping` directly, without a need to refer to `dependencyGraph`.
*
* @internal
*/
get rangeMapping() {
return this.dependencyGraph.rangeMapping;
}
/**
* Calls the `arrayMapping` method on the dependency graph.
* Allows for executing `arrayMapping` directly, without a need to refer to `dependencyGraph`.
*
* @internal
*/
get arrayMapping() {
return this.dependencyGraph.arrayMapping;
}
/**
* Calls the `sheetMapping` method on the dependency graph.
* Allows for executing `sheetMapping` directly, without a need to refer to `dependencyGraph`.
*
* @internal
*/
get sheetMapping() {
return this.dependencyGraph.sheetMapping;
}
/**
* Calls the `addressMapping` method on the dependency graph.
* Allows for executing `addressMapping` directly, without a need to refer to `dependencyGraph`.
*
* @internal
*/
get addressMapping() {
return this.dependencyGraph.addressMapping;
}
/** @internal */
get dependencyGraph() {
return this._dependencyGraph;
}
/** @internal */
get evaluator() {
return this._evaluator;
}
/** @internal */
get columnSearch() {
return this._columnSearch;
}
/** @internal */
get lazilyTransformingAstService() {
return this._lazilyTransformingAstService;
}
/**
* Returns state of the validity of the license key.
*
* @internal
*/
get licenseKeyValidityState() {
return this._config.licenseKeyValidityState;
}
/**
* Builds the engine for a sheet from a two-dimensional array representation.
* The engine is created with a single sheet.
* Can be configured with the optional second parameter that represents a [[ConfigParams]].
* If not specified, the engine will be built with the default configuration.
*
* @param {Sheet} sheet - two-dimensional array representation of sheet
* @param {Partial<ConfigParams>} configInput - engine configuration
* @param {SerializedNamedExpression[]} namedExpressions - starting named expressions
*
* @throws [[SheetSizeLimitExceededError]] when sheet size exceeds the limits
* @throws [[InvalidArgumentsError]] when sheet is not an array of arrays
* @throws [[FunctionPluginValidationError]] when plugin class definition is not consistent with metadata
*
* @example
* ```js
* // data represented as an array
* const sheetData = [
* ['0', '=SUM(1, 2, 3)', '52'],
* ['=SUM(A1:C1)', '', '=A1'],
* ['2', '=SUM(A1:C1)', '=theUltimateQuestionOfLife'],
* ];
*
* const namedExpressions = [
* {
* name: 'theUltimateQuestionOfLife',
* expression: '=42',
* },
* ];
*
* // method with optional config parameter maxColumns
* const hfInstance = HyperFormula.buildFromArray(sheetData, { maxColumns: 1000 }, namedExpressions);
* ```
*
* @category Factories
*/
static buildFromArray(sheet, configInput = {}, namedExpressions = []) {
return this.buildFromEngineState(_BuildEngineFactory.BuildEngineFactory.buildFromSheet(sheet, configInput, namedExpressions));
}
/**
* Builds the engine from an object containing multiple sheets with names.
* The engine is created with one or more sheets.
* Can be configured with the optional second parameter that represents a [[ConfigParams]].
* If not specified the engine will be built with the default configuration.
*
* @param {Sheet} sheets - object with sheets definition
* @param {Partial<ConfigParams>} configInput - engine configuration
* @param {SerializedNamedExpression[]} namedExpressions - starting named expressions
*
* @throws [[SheetSizeLimitExceededError]] when sheet size exceeds the limits
* @throws [[InvalidArgumentsError]] when any sheet is not an array of arrays
* @throws [[FunctionPluginValidationError]] when plugin class definition is not consistent with metadata
*
* @example
* ```js
* // data represented as an object with sheets: Sheet1 and Sheet2
* const sheetData = {
* 'Sheet1': [
* ['1', '', '=Sheet2!$A1'],
* ['', '2', '=SUM(1, 2, 3)'],
* ['=Sheet2!$A2', '2', ''],
* ],
* 'Sheet2': [
* ['', '4', '=Sheet1!$B1'],
* ['', '8', '=SUM(9, 3, 3)'],
* ['=Sheet1!$B1', '2', '=theUltimateQuestionOfLife'],
* ],
* };
*
* const namedExpressions = [
* {
* name: 'theUltimateQuestionOfLife',
* expression: '=42',
* },
* ];
*
* // method with optional config parameter useColumnIndex
* const hfInstance = HyperFormula.buildFromSheets(sheetData, { useColumnIndex: true }, namedExpressions);
* ```
*
* @category Factories
*/
static buildFromSheets(sheets, configInput = {}, namedExpressions = []) {
return this.buildFromEngineState(_BuildEngineFactory.BuildEngineFactory.buildFromSheets(sheets, configInput, namedExpressions));
}
/**
* Builds an empty engine instance.
* Can be configured with the optional parameter that represents a [[ConfigParams]].
* If not specified the engine will be built with the default configuration.
*
* @param {Partial<ConfigParams>} configInput - engine configuration
* @param {SerializedNamedExpression[]} namedExpressions - starting named expressions
*
* @example
* ```js
* const namedExpressions = [
* {
* name: 'theUltimateQuestionOfLife',
* expression: '=42',
* },
* ];
*
* // build with no initial data and with optional config parameter maxColumns
* const hfInstance = HyperFormula.buildEmpty({ maxColumns: 1000 }, namedExpressions);
* ```
*
* @category Factories
*/
static buildEmpty(configInput = {}, namedExpressions = []) {
return this.buildFromEngineState(_BuildEngineFactory.BuildEngineFactory.buildEmpty(configInput, namedExpressions));
}
/**
* Returns registered language from its code string.
*
* For more information, see the [Localizing functions guide](/guide/localizing-functions.md).
*
* @param {string} languageCode - code string of the translation package
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[LanguageNotRegisteredError]] when trying to retrieve not registered language
*
* @example
* ```js
* // return registered language
* const language = HyperFormula.getLanguage('enGB');
* ```
*
* @category Static Methods
*/
static getLanguage(languageCode) {
(0, _ArgumentSanitization.validateArgToType)(languageCode, 'string', 'languageCode');
const val = this.registeredLanguages.get(languageCode);
if (val === undefined) {
throw new _errors.LanguageNotRegisteredError();
} else {
return val;
}
}
/**
* Registers language under given code string.
*
* For more information, see the [Localizing functions guide](/guide/localizing-functions.md).
*
* @param {string} languageCode - code string of the translation package
* @param {RawTranslationPackage} languagePackage - translation package to be registered
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[ProtectedFunctionTranslationError]] when trying to register translation for protected function
* @throws [[LanguageAlreadyRegisteredError]] when given language is already registered
*
* @example
* ```js
* // return registered language
* HyperFormula.registerLanguage('enUS', enUS);
* const engine = HyperFormula.buildEmpty({language: 'enUS'});
* ```
*
* @category Static Methods
*/
static registerLanguage(languageCode, languagePackage) {
(0, _ArgumentSanitization.validateArgToType)(languageCode, 'string', 'languageCode');
if (this.registeredLanguages.has(languageCode)) {
throw new _errors.LanguageAlreadyRegisteredError();
} else {
this.registeredLanguages.set(languageCode, (0, _i18n.buildTranslationPackage)(languagePackage));
}
}
/**
* Unregisters language that is registered under given code string.
*
* For more information, see the [Localizing functions guide](/guide/localizing-functions.md).
*
* @param {string} languageCode - code string of the translation package
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[LanguageNotRegisteredError]] when given language is not registered
*
* @example
* ```js
* // register the language for the instance
* HyperFormula.registerLanguage('plPL', plPL);
*
* // unregister plPL
* HyperFormula.unregisterLanguage('plPL');
* ```
*
* @category Static Methods
*/
static unregisterLanguage(languageCode) {
(0, _ArgumentSanitization.validateArgToType)(languageCode, 'string', 'languageCode');
if (this.registeredLanguages.has(languageCode)) {
this.registeredLanguages.delete(languageCode);
} else {
throw new _errors.LanguageNotRegisteredError();
}
}
/**
* Returns all registered languages codes.
*
* @example
* ```js
* // should return all registered language codes: ['enGB', 'plPL']
* const registeredLanguages = HyperFormula.getRegisteredLanguagesCodes();
* ```
*
* @category Static Methods
*/
static getRegisteredLanguagesCodes() {
return Array.from(this.registeredLanguages.keys());
}
/**
* Registers all functions in a given plugin with optional translations.
*
* For more information, see the [Custom functions guide](/guide/custom-functions.md).
*
* Note: FunctionPlugins must be registered prior to the creation of HyperFormula instances in which they are used.
* HyperFormula instances created prior to the registration of a FunctionPlugin are unable to access the FunctionPlugin.
* Registering a FunctionPlugin with [[custom-functions]] requires the translations parameter.
*
* @param {FunctionPluginDefinition} plugin - plugin class
* @param {FunctionTranslationsPackage} translations - optional package of function names translations
*
* @throws [[FunctionPluginValidationError]] when plugin class definition is not consistent with metadata
* @throws [[ProtectedFunctionTranslationError]] when trying to register translation for protected function
*
* @example
* ```js
* // import your own plugin
* import { MyExamplePlugin } from './file_with_your_plugin';
*
* // register the plugin
* HyperFormula.registerFunctionPlugin(MyExamplePlugin);
* ```
*
* @category Static Methods
*/
static registerFunctionPlugin(plugin, translations) {
_FunctionRegistry.FunctionRegistry.registerFunctionPlugin(plugin, translations);
}
/**
* Unregisters all functions defined in given plugin.
*
* For more information, see the [Custom functions guide](/guide/custom-functions.md).
*
* Note: This method does not affect the existing HyperFormula instances.
*
* @param {FunctionPluginDefinition} plugin - plugin class
*
* @example
* ```js
* // get the class of a plugin
* const registeredPluginClass = HyperFormula.getFunctionPlugin('EXAMPLE');
*
* // unregister all functions defined in a plugin of ID 'EXAMPLE'
* HyperFormula.unregisterFunctionPlugin(registeredPluginClass);
* ```
*
* @category Static Methods
*/
static unregisterFunctionPlugin(plugin) {
_FunctionRegistry.FunctionRegistry.unregisterFunctionPlugin(plugin);
}
/**
* Registers a function with a given id if such exists in a plugin.
*
* For more information, see the [Custom functions guide](/guide/custom-functions.md).
*
* Note: This method does not affect the existing HyperFormula instances.
*
* @param {string} functionId - function id, e.g., 'SUMIF'
* @param {FunctionPluginDefinition} plugin - plugin class
* @param {FunctionTranslationsPackage} translations - translations for the function name
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[FunctionPluginValidationError]] when function with a given id does not exist in plugin or plugin class definition is not consistent with metadata
* @throws [[ProtectedFunctionTranslationError]] when trying to register translation for protected function
*
* @example
* ```js
* // import your own plugin
* import { MyExamplePlugin } from './file_with_your_plugin';
*
* // register a function
* HyperFormula.registerFunction('EXAMPLE', MyExamplePlugin);
* ```
*
* @category Static Methods
*/
static registerFunction(functionId, plugin, translations) {
(0, _ArgumentSanitization.validateArgToType)(functionId, 'string', 'functionId');
_FunctionRegistry.FunctionRegistry.registerFunction(functionId, plugin, translations);
}
/**
* Unregisters a function with a given id.
*
* For more information, see the [Custom functions guide](/guide/custom-functions.md).
*
* Note: This method does not affect the existing HyperFormula instances.
*
* @param {string} functionId - function id, e.g., 'SUMIF'
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
* @example
* ```js
* // import your own plugin
* import { MyExamplePlugin } from './file_with_your_plugin';
*
* // register a function
* HyperFormula.registerFunction('EXAMPLE', MyExamplePlugin);
*
* // unregister a function
* HyperFormula.unregisterFunction('EXAMPLE');
* ```
*
* @category Static Methods
*/
static unregisterFunction(functionId) {
(0, _ArgumentSanitization.validateArgToType)(functionId, 'string', 'functionId');
_FunctionRegistry.FunctionRegistry.unregisterFunction(functionId);
}
/**
* Clears function registry.
*
* Note: This method does not affect the existing HyperFormula instances.
*
* @example
* ```js
* HyperFormula.unregisterAllFunctions();
* ```
*
* @category Static Methods
*/
static unregisterAllFunctions() {
_FunctionRegistry.FunctionRegistry.unregisterAll();
}
/**
* Returns translated names of all registered functions for a given language
*
* @param {string} code - language code
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
* @example
* ```js
* // return a list of function names registered for enGB
* const allNames = HyperFormula.getRegisteredFunctionNames('enGB');
* ```
*
* @category Static Methods
*/
static getRegisteredFunctionNames(code) {
(0, _ArgumentSanitization.validateArgToType)(code, 'string', 'code');
const functionIds = _FunctionRegistry.FunctionRegistry.getRegisteredFunctionIds();
const language = this.getLanguage(code);
return language.getFunctionTranslations(functionIds);
}
/**
* Returns class of a plugin used by function with given id
*
* For more information, see the [Custom functions guide](/guide/custom-functions.md).
*
* @param {string} functionId - id of a function, e.g., 'SUMIF'
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
* @example
* ```js
* // import your own plugin
* import { MyExamplePlugin } from './file_with_your_plugin';
*
* // register a plugin
* HyperFormula.registerFunctionPlugin(MyExamplePlugin);
*
* // return the class of a given plugin
* const myFunctionClass = HyperFormula.getFunctionPlugin('EXAMPLE');
* ```
*
* @category Static Methods
*/
static getFunctionPlugin(functionId) {
(0, _ArgumentSanitization.validateArgToType)(functionId, 'string', 'functionId');
return _FunctionRegistry.FunctionRegistry.getFunctionPlugin(functionId);
}
/**
* Returns classes of all plugins registered in HyperFormula.
*
* @example
* ```js
* // return classes of all plugins
* const allClasses = HyperFormula.getAllFunctionPlugins();
* ```
*
* @category Static Methods
*/
static getAllFunctionPlugins() {
return _FunctionRegistry.FunctionRegistry.getPlugins();
}
/**
* @internal
*/
static buildFromEngineState(engine) {
return new HyperFormula(engine.config, engine.stats, engine.dependencyGraph, engine.columnSearch, engine.parser, engine.unparser, engine.cellContentParser, engine.evaluator, engine.lazilyTransformingAstService, engine.crudOperations, engine.exporter, engine.namedExpressions, engine.serialization, engine.functionRegistry);
}
/**
* Returns the cell value of a given address.
* Applies rounding and post-processing.
*
* @param {SimpleCellAddress} cellAddress - cell coordinates
*
* @throws [[ExpectedValueOfTypeError]] when cellAddress is of incorrect type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['=SUM(1, 2, 3)', '2'],
* ]);
*
* // get value of A1 cell, should be '6'
* const A1Value = hfInstance.getCellValue({ sheet: 0, col: 0, row: 0 });
*
* // get value of B1 cell, should be '2'
* const B1Value = hfInstance.getCellValue({ sheet: 0, col: 1, row: 0 });
* ```
*
* @category Cells
*/
getCellValue(cellAddress) {
if (!(0, _Cell.isSimpleCellAddress)(cellAddress)) {
throw new _errors.ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress');
}
this.ensureEvaluationIsNotSuspended();
return this._serialization.getCellValue(cellAddress);
}
/**
* Returns a normalized formula string from the cell of a given address or `undefined` for an address that does not exist and empty values.
*
* @param {SimpleCellAddress} cellAddress - cell coordinates
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[ExpectedValueOfTypeError]] when cellAddress is of incorrect type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['=SUM(1, 2, 3)', '0'],
* ]);
*
* // should return a normalized A1 cell formula: '=SUM(1, 2, 3)'
* const A1Formula = hfInstance.getCellFormula({ sheet: 0, col: 0, row: 0 });
*
* // should return a normalized B1 cell formula: 'undefined'
* const B1Formula = hfInstance.getCellFormula({ sheet: 0, col: 1, row: 0 });
* ```
*
* @category Cells
*/
getCellFormula(cellAddress) {
if (!(0, _Cell.isSimpleCellAddress)(cellAddress)) {
throw new _errors.ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress');
}
return this._serialization.getCellFormula(cellAddress);
}
/**
* Returns the `HYPERLINK` url for a cell of a given address or `undefined` for an address that does not exist or a cell that is not `HYPERLINK`
*
* @param {SimpleCellAddress} cellAddress - cell coordinates
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[ExpectedValueOfTypeError]] when cellAddress is of incorrect type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['=HYPERLINK("https://hyperformula.handsontable.com/", "HyperFormula")', '0'],
* ]);
*
* // should return url of 'HYPERLINK': https://hyperformula.handsontable.com/
* const A1Hyperlink = hfInstance.getCellHyperlink({ sheet: 0, col: 0, row: 0 });
*
* // should return 'undefined' for a cell that is not 'HYPERLINK'
* const B1Hyperlink = hfInstance.getCellHyperlink({ sheet: 0, col: 1, row: 0 });
* ```
*
* @category Cells
*/
getCellHyperlink(cellAddress) {
if (!(0, _Cell.isSimpleCellAddress)(cellAddress)) {
throw new _errors.ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress');
}
this.ensureEvaluationIsNotSuspended();
return this._serialization.getCellHyperlink(cellAddress);
}
/**
* Returns [[RawCellContent]] with a serialized content of the cell of a given address: either a cell formula, an explicit value, or an error.
*
* @param {SimpleCellAddress} cellAddress - cell coordinates
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
* @throws [[ExpectedValueOfTypeError]] when cellAddress is of incorrect type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['=SUM(1, 2, 3)', '0'],
* ]);
*
* // should return serialized content of A1 cell: '=SUM(1, 2, 3)'
* const cellA1Serialized = hfInstance.getCellSerialized({ sheet: 0, col: 0, row: 0 });
*
* // should return serialized content of B1 cell: '0'
* const cellB1Serialized = hfInstance.getCellSerialized({ sheet: 0, col: 1, row: 0 });
* ```
*
* @category Cells
*/
getCellSerialized(cellAddress) {
if (!(0, _Cell.isSimpleCellAddress)(cellAddress)) {
throw new _errors.ExpectedValueOfTypeError('SimpleCellAddress', 'cellAddress');
}
this.ensureEvaluationIsNotSuspended();
return this._serialization.getCellSerialized(cellAddress);
}
/**
* Returns an array of arrays of [[CellValue]] with values of all cells from [[Sheet]].
* Applies rounding and post-processing.
*
* @param {number} sheetId - sheet ID number
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['0', '=SUM(1, 2, 3)', '=A1'],
* ['1', '=TEXT(A2, "0.0%")', '=C1'],
* ['2', '=SUM(A1:C1)', '=C1'],
* ]);
*
* // should return all values of a sheet: [[0, 6, 0], [1, '1.0%', 0], [2, 6, 0]]
* const sheetValues = hfInstance.getSheetValues(0);
* ```
*
* @category Sheets
*/
getSheetValues(sheetId) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
this.ensureEvaluationIsNotSuspended();
return this._serialization.getSheetValues(sheetId);
}
/**
* Returns an array with normalized formula strings from [[Sheet]] or `undefined` for a cells that have no value.
*
* @param {SimpleCellAddress} sheetId - sheet ID number
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['0', '=SUM(1, 2, 3)', '=A1'],
* ['1', '=TEXT(A2, "0.0%")', '=C1'],
* ['2', '=SUM(A1:C1)', '=C1'],
* ]);
*
* // should return all formulas of a sheet:
* // [
* // [undefined, '=SUM(1, 2, 3)', '=A1'],
* // [undefined, '=TEXT(A2, "0.0%")', '=C1'],
* // [undefined, '=SUM(A1:C1)', '=C1'],
* // ];
* const sheetFormulas = hfInstance.getSheetFormulas(0);
* ```
*
* @category Sheets
*/
getSheetFormulas(sheetId) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
return this._serialization.getSheetFormulas(sheetId);
}
/**
* Returns an array of arrays of [[RawCellContent]] with serialized content of cells from [[Sheet]], either a cell formula or an explicit value.
*
* @param {SimpleCellAddress} sheetId - sheet ID number
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['0', '=SUM(1, 2, 3)', '=A1'],
* ['1', '=TEXT(A2, "0.0%")', '=C1'],
* ['2', '=SUM(A1:C1)', '=C1'],
* ]);
*
* // should return:
* // [
* // ['0', '=SUM(1, 2, 3)', '=A1'],
* // ['1', '=TEXT(A2, "0.0%")', '=C1'],
* // ['2', '=SUM(A1:C1)', '=C1'],
* // ];
* const serializedContent = hfInstance.getSheetSerialized(0);
* ```
*
* @category Sheets
*/
getSheetSerialized(sheetId) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
this.ensureEvaluationIsNotSuspended();
return this._serialization.getSheetSerialized(sheetId);
}
/**
* Returns a map containing dimensions of all sheets for the engine instance represented as a key-value pairs where keys are sheet IDs and dimensions are returned as numbers, width and height respectively.
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromSheets({
* Sheet1: [
* ['1', '2', '=Sheet2!$A1'],
* ],
* Sheet2: [
* ['3'],
* ['4'],
* ],
* });
*
* // should return the dimensions of all sheets:
* // { Sheet1: { width: 3, height: 1 }, Sheet2: { width: 1, height: 2 } }
* const allSheetsDimensions = hfInstance.getAllSheetsDimensions();
* ```
*
* @category Sheets
*/
getAllSheetsDimensions() {
return this._serialization.genericAllSheetsGetter(arg => this.getSheetDimensions(arg));
}
/**
* Returns dimensions of a specified sheet.
* The sheet dimensions is represented with numbers: width and height.
*
* Note: Due to the memory optimizations, some of the empty bottom rows and rightmost columns are not counted to the dimensions.
*
* @param {number} sheetId - sheet ID number
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2', '=Sheet2!$A1'],
* ]);
*
* // should return provided sheet's dimensions: { width: 3, height: 1 }
* const sheetDimensions = hfInstance.getSheetDimensions(0);
* ```
*
* @category Sheets
*/
getSheetDimensions(sheetId) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
return {
width: this.dependencyGraph.getSheetWidth(sheetId),
height: this.dependencyGraph.getSheetHeight(sheetId)
};
}
/**
* Returns values of all sheets in a form of an object which property keys are strings and values are 2D arrays of [[CellValue]].
*
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '=A1+10', '3'],
* ]);
*
* // should return all sheets values: { Sheet1: [ [ 1, 11, 3 ] ] }
* const allSheetsValues = hfInstance.getAllSheetsValues();
* ```
*
* @category Sheets
*/
getAllSheetsValues() {
this.ensureEvaluationIsNotSuspended();
return this._serialization.getAllSheetsValues();
}
/**
* Returns formulas of all sheets in a form of an object which property keys are strings and values are 2D arrays of strings or possibly `undefined` when the call does not contain a formula.
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2', '=A1+10'],
* ]);
*
* // should return only formulas: { Sheet1: [ [ undefined, undefined, '=A1+10' ] ] }
* const allSheetsFormulas = hfInstance.getAllSheetsFormulas();
* ```
* @category Sheets
*/
getAllSheetsFormulas() {
return this._serialization.getAllSheetsFormulas();
}
/**
* Returns formulas or values of all sheets in a form of an object which property keys are strings and values are 2D arrays of [[RawCellContent]].
*
* @throws [[EvaluationSuspendedError]] when the evaluation is suspended
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2', '=A1+10'],
* ]);
*
* // should return all sheets serialized content: { Sheet1: [ [ 1, 2, '=A1+10' ] ] }
* const allSheetsSerialized = hfInstance.getAllSheetsSerialized();
* ```
*
* @category Sheets
*/
getAllSheetsSerialized() {
this.ensureEvaluationIsNotSuspended();
return this._serialization.getAllSheetsSerialized();
}
/**
* Updates the config with given new metadata. It is an expensive operation, as it might trigger rebuilding the engine and recalculation of all formulas.
*
* For more information, see the [Configuration options guide](/guide/configuration-options.md).
*
* @param {Partial<ConfigParams>} newParams configuration options to be updated or added
*
* @throws [[ExpectedValueOfTypeError]] when some parameters of config are of wrong type (e.g., currencySymbol)
* @throws [[ConfigValueEmpty]] when some parameters of config are of invalid value (e.g., currencySymbol)
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2'],
* ]);
*
* // add a config param, for example maxColumns,
* // you can check the configuration with getConfig method
* hfInstance.updateConfig({ maxColumns: 1000 });
* ```
*
* @category Instance
*/
updateConfig(newParams) {
const isNewConfigTheSame = Object.entries(newParams).every(([key, value]) => this._config[key] === value);
if (isNewConfigTheSame) {
return;
}
this.rebuildWithConfig(newParams);
}
/**
* Returns current configuration of the engine instance.
*
* For more information, see the [Configuration options guide](/guide/configuration-options.md).
*
* @example
* ```js
* // should return all config metadata including default and those which were added
* const hfConfig = hfInstance.getConfig();
* ```
*
* @category Instance
*/
getConfig() {
return this._config.getConfig();
}
/**
* Rebuilds the HyperFormula instance preserving the current sheets data.
*
* @example
* ```js
* hfInstance.rebuildAndRecalculate();
* ```
*
* @category Instance
*/
rebuildAndRecalculate() {
this.rebuildWithConfig({});
}
/**
* Returns a snapshot of computation time statistics.
* It returns a map with key-value pairs where keys are enums for stat type and time (number).
*
* @internal
*
* @category Instance
*/
getStats() {
return this._stats.snapshot();
}
/**
* Undo the previous operation.
*
* For more information, see the [Undo-Redo guide](/guide/undo-redo.md).
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note that this method may trigger dependency graph recalculation.
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[NoOperationToUndoError]] when there is no operation running that can be undone
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2'],
* ['3', ''],
* ]);
*
* // perform CRUD operation, for example remove the second row
* hfInstance.removeRows(0, [1, 1]);
*
* // undo the operation, it should return the changes
* const changes = hfInstance.undo();
* ```
*
* @category Undo and Redo
*/
undo() {
this._crudOperations.undo();
return this.recomputeIfDependencyGraphNeedsIt();
}
/**
* Re-do recently undone operation.
*
* For more information, see the [Undo-Redo guide](/guide/undo-redo.md).
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note that this method may trigger dependency graph recalculation.
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[NoOperationToRedoError]] when there is no operation running that can be re-done
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1'],
* ['2'],
* ['3'],
* ]);
*
* // perform CRUD operation, for example remove the second row
* hfInstance.removeRows(0, [1, 1]);
*
* // undo the operation, it should return previous values: [['1'], ['2'], ['3']]
* hfInstance.undo();
*
* // do a redo, it should return the values after removing the second row: [['1'], ['3']]
* const changes = hfInstance.redo();
* ```
*
* @category Undo and Redo
*/
redo() {
this._crudOperations.redo();
return this.recomputeIfDependencyGraphNeedsIt();
}
/**
* Checks if there is at least one operation that can be undone.
*
* For more information, see the [Undo-Redo guide](/guide/undo-redo.md).
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1'],
* ['2'],
* ['3'],
* ]);
*
* // perform CRUD operation, for example remove the second row
* hfInstance.removeRows(0, [1, 1]);
*
* // should return 'true', it is possible to undo last operation
* // which is removing rows in this example
* const isSomethingToUndo = hfInstance.isThereSomethingToUndo();
* ```
*
* @category Undo and Redo
*/
isThereSomethingToUndo() {
return this._crudOperations.isThereSomethingToUndo();
}
/**
* Checks if there is at least one operation that can be re-done.
*
* For more information, see the [Undo-Redo guide](/guide/undo-redo.md).
*
* @example
* ```js
* hfInstance.undo();
*
* // when there is an action to redo, this returns 'true'
* const isSomethingToRedo = hfInstance.isThereSomethingToRedo();
* ```
*
* @category Undo and Redo
*/
isThereSomethingToRedo() {
return this._crudOperations.isThereSomethingToRedo();
}
/**
* Returns information whether it is possible to change the content in a rectangular area bounded by the box.
* If returns `true`, doing [[setCellContents]] operation won't throw any errors.
* Returns `false` if the address is invalid or the sheet does not exist.
*
* @param {SimpleCellAddress | SimpleCellRange} address - single cell or block of cells to check
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[SheetsNotEqual]] if range provided has distinct sheet numbers for start and end
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2'],
* ]);
*
* // top left corner
* const address1 = { col: 0, row: 0, sheet: 0 };
* // bottom right corner
* const address2 = { col: 1, row: 0, sheet: 0 };
*
* // should return 'true' for this example, it is possible to set content of
* // width 2, height 1 in the first row and column of sheet 0
* const isSettable = hfInstance.isItPossibleToSetCellContents({ start: address1, end: address2 });
* ```
*
* @category Cells
*/
isItPossibleToSetCellContents(address) {
let range;
if ((0, _Cell.isSimpleCellAddress)(address)) {
range = new _AbsoluteCellRange.AbsoluteCellRange(address, address);
} else if ((0, _AbsoluteCellRange.isSimpleCellRange)(address)) {
range = new _AbsoluteCellRange.AbsoluteCellRange(address.start, address.end);
} else {
throw new _errors.ExpectedValueOfTypeError('SimpleCellAddress | SimpleCellRange', 'address');
}
try {
this._crudOperations.ensureRangeInSizeLimits(range);
for (const it of range.addresses(this._dependencyGraph)) {
this._crudOperations.ensureItIsPossibleToChangeContent(it);
}
} catch (e) {
return false;
}
return true;
}
/**
* Sets the content for a block of cells of a given coordinates.
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note that this method may trigger dependency graph recalculation.
*
* @param {SimpleCellAddress} topLeftCornerAddress - top left corner of block of cells
* @param {(RawCellContent[][]|RawCellContent)} cellContents - array with content
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[InvalidArgumentsError]] when the value is not an array of arrays or a raw cell value
* @throws [[SheetSizeLimitExceededError]] when performing this operation would result in sheet size limits exceeding
* @throws [[ExpectedValueOfTypeError]] if topLeftCornerAddress argument is of wrong type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['1', '2', '=A1'],
* ]);
*
* // should set the content, returns:
* // [{
* // address: { sheet: 0, col: 3, row: 0 },
* // newValue: 2,
* // }]
* const changes = hfInstance.setCellContents({ col: 3, row: 0, sheet: 0 }, [['=B1']]);
* ```
*
* @category Cells
*/
setCellContents(topLeftCornerAddress, cellContents) {
this._crudOperations.setCellContents(topLeftCornerAddress, cellContents);
return this.recomputeIfDependencyGraphNeedsIt();
}
/**
* Reorders rows of a sheet according to a source-target mapping.
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note that this method may trigger dependency graph recalculation.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {[number, number][]} rowMapping - array mapping original positions to final positions of rows
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[InvalidArgumentsError]] when rowMapping does not define correct row permutation for some subset of rows of the given sheet
* @throws [[SourceLocationHasArrayError]] when the selected position has array inside
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* [1],
* [2],
* [4, 5],
* ]);
*
* // should set swap rows 0 and 2 in place, returns:
* // [{
* // address: { sheet: 0, col: 0, row: 2 },
* // newValue: 1,
* // },
* // {
* // address: { sheet: 0, col: 1, row: 2 },
* // newValue: null,
* // },
* // {
* // address: { sheet: 0, col: 0, row: 0 },
* // newValue: 4,
* // },
* // {
* // address: { sheet: 0, col: 1, row: 0 },
* // newValue: 5,
* // }]
* const changes = hfInstance.swapRowIndexes(0, [[0, 2], [2, 0]]);
* ```
*
* @category Rows
*/
swapRowIndexes(sheetId, rowMapping) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
this._crudOperations.setRowOrder(sheetId, rowMapping);
return this.recomputeIfDependencyGraphNeedsIt();
}
/**
* Checks if it is possible to reorder rows of a sheet according to a source-target mapping.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {[number, number][]} rowMapping - array mapping original positions to final positions of rows
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* [1],
* [2],
* [4, 5],
* ]);
*
* // returns true
* const isSwappable = hfInstance.isItPossibleToSwapRowIndexes(0, [[0, 2], [2, 0]]);
*
* // returns false
* const isSwappable = hfInstance.isItPossibleToSwapRowIndexes(0, [[0, 1]]);
* ```
*
* @category Rows
*/
isItPossibleToSwapRowIndexes(sheetId, rowMapping) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
try {
this._crudOperations.validateSwapRowIndexes(sheetId, rowMapping);
this._crudOperations.testRowOrderForArrays(sheetId, rowMapping);
return true;
} catch (e) {
return false;
}
}
/**
* Reorders rows of a sheet according to a permutation of 0-based indexes.
* Parameter `newRowOrder` should have a form `[ newPositionForRow0, newPositionForRow1, newPositionForRow2, ... ]`.
* This method might be used to [sort the rows of a sheet](../../guide/sorting-data.md).
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note: This method may trigger dependency graph recalculation.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {number[]} newRowOrder - permutation of rows; array length must match the number of rows returned by [getSheetDimensions()](#getsheetdimensions)
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[InvalidArgumentsError]] when rowMapping does not define correct row permutation for some subset of rows of the given sheet
* @throws [[SourceLocationHasArrayError]] when the selected position has array inside
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* ['A'],
* ['B'],
* ['C'],
* ['D']
* ]);
*
* const newRowOrder = [0, 3, 2, 1]; // [ newPosForA, newPosForB, newPosForC, newPosForD ]
*
* const changes = hfInstance.setRowOrder(0, newRowOrder);
*
* // Sheet after this operation: [['A'], ['D'], ['C'], ['B']]
* ```
*
* @category Rows
*/
setRowOrder(sheetId, newRowOrder) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
const mapping = this._crudOperations.mappingFromOrder(sheetId, newRowOrder, 'row');
return this.swapRowIndexes(sheetId, mapping);
}
/**
* Checks if it is possible to reorder rows of a sheet according to a permutation.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {number[]} newRowOrder - permutation of rows
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* [1],
* [2],
* [4, 5],
* ]);
*
* // returns true
* hfInstance.isItPossibleToSetRowOrder(0, [2, 1, 0]);
*
* // returns false
* hfInstance.isItPossibleToSetRowOrder(0, [2]);
* ```
*
* @category Rows
*/
isItPossibleToSetRowOrder(sheetId, newRowOrder) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
try {
const rowMapping = this._crudOperations.mappingFromOrder(sheetId, newRowOrder, 'row');
this._crudOperations.validateSwapRowIndexes(sheetId, rowMapping);
this._crudOperations.testRowOrderForArrays(sheetId, rowMapping);
return true;
} catch (e) {
return false;
}
}
/**
* Reorders columns of a sheet according to a source-target mapping.
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note that this method may trigger dependency graph recalculation.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {[number, number][]} columnMapping - array mapping original positions to final positions of columns
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @throws [[NoSheetWithIdError]] when the given sheet ID does not exist
* @throws [[InvalidArgumentsError]] when columnMapping does not define correct column permutation for some subset of columns of the given sheet
* @throws [[SourceLocationHasArrayError]] when the selected position has array inside
*
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* [1, 2, 4],
* [5]
* ]);
*
* // should set swap columns 0 and 2 in place, returns:
* // [{
* // address: { sheet: 0, col: 2, row: 0 },
* // newValue: 1,
* // },
* // {
* // address: { sheet: 0, col: 2, row: 1 },
* // newValue: 5,
* // },
* // {
* // address: { sheet: 0, col: 0, row: 0 },
* // newValue: 4,
* // },
* // {
* // address: { sheet: 0, col: 0, row: 1 },
* // newValue: null,
* // }]
* const changes = hfInstance.swapColumnIndexes(0, [[0, 2], [2, 0]]);
* ```
*
* @category Columns
*/
swapColumnIndexes(sheetId, columnMapping) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
this._crudOperations.setColumnOrder(sheetId, columnMapping);
return this.recomputeIfDependencyGraphNeedsIt();
}
/**
* Checks if it is possible to reorder columns of a sheet according to a source-target mapping.
*
* @fires [[valuesUpdated]] if recalculation was triggered by this change
*
* @throws [[ExpectedValueOfTypeError]] if any of its basic type argument is of wrong type
* @example
* ```js
* const hfInstance = HyperFormula.buildFromArray([
* [1, 2, 4],
* [5]
* ]);
*
* // returns true
* hfInstance.isItPossibleToSwapColumnIndexes(0, [[0, 2], [2, 0]]);
*
* // returns false
* hfInstance.isItPossibleToSwapColumnIndexes(0, [[0, 1]]);
* ```
*
* @category Columns
*/
isItPossibleToSwapColumnIndexes(sheetId, columnMapping) {
(0, _ArgumentSanitization.validateArgToType)(sheetId, 'number', 'sheetId');
try {
this._crudOperations.validateSwapColumnIndexes(sheetId, columnMapping);
this._crudOperations.testColumnOrderForArrays(sheetId, columnMapping);
return true;
} catch (e) {
return false;
}
}
/**
* Reorders columns of a sheet according to a permutation of 0-based indexes.
* Parameter `newColumnOrder` should have a form `[ newPositionForColumn0, newPositionForColumn1, newPositionForColumn2, ... ]`.
* This method might be used to [sort the columns of a sheet](../../guide/sorting-data.md).
*
* Returns [an array of cells whose values changed as a result of this operation](/guide/basic-operations.md#changes-array).
*
* Note: This method may trigger dependency graph recalculation.
*
* @param {number} sheetId - ID of a sheet to operate on
* @param {number[]} newColumnOrder - permutation of columns; array length must match the number of columns returned by [getSheetDimensions()](#getsheetdimensions)
*
* @