UNPKG

hyperformula

Version:

HyperFormula is a JavaScript engine for efficient processing of spreadsheet-like data and formulas

1,411 lines (1,409 loc) 168 kB
"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) * * @