UNPKG

@highcharts/dashboards

Version:
1,780 lines (1,739 loc) 76.5 kB
/** * @license Highcharts Dashboards Math 4.0.0 (2025-10-29) * @module dashboards/modules/math-modifier * @requires dashboards * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(root["_Dashboards"], root["_Dashboards"]["DataModifier"]); else if(typeof define === 'function' && define.amd) define("dashboards/modules/math-modifier", ["dashboards/dashboards"], function (amd1) {return factory(amd1,amd1["DataModifier"]);}); else if(typeof exports === 'object') exports["dashboards/modules/math-modifier"] = factory(root["_Dashboards"], root["_Dashboards"]["DataModifier"]); else root["Dashboards"] = factory(root["Dashboards"], root["Dashboards"]["DataModifier"]); })(typeof window === 'undefined' ? this : window, (__WEBPACK_EXTERNAL_MODULE__668__, __WEBPACK_EXTERNAL_MODULE__784__) => { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 668: /***/ ((module) => { module.exports = __WEBPACK_EXTERNAL_MODULE__668__; /***/ }), /***/ 784: /***/ ((module) => { module.exports = __WEBPACK_EXTERNAL_MODULE__784__; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* 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)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // EXPORTS __webpack_require__.d(__webpack_exports__, { "default": () => (/* binding */ math_modifier_src) }); // EXTERNAL MODULE: external {"amd":["dashboards/dashboards"],"commonjs":["dashboards"],"commonjs2":["dashboards"],"root":["Dashboards"]} var dashboards_commonjs_dashboards_commonjs2_dashboards_root_Dashboards_ = __webpack_require__(668); var dashboards_commonjs_dashboards_commonjs2_dashboards_root_Dashboards_default = /*#__PURE__*/__webpack_require__.n(dashboards_commonjs_dashboards_commonjs2_dashboards_root_Dashboards_); ;// ./code/dashboards/es-modules/Data/Formula/FormulaParser.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { isString } = (dashboards_commonjs_dashboards_commonjs2_dashboards_root_Dashboards_default()); /* * * * Constants * * */ /** * @private */ const booleanRegExp = /^(?:FALSE|TRUE)/; /** * `.`-separated decimal. * @private */ const decimal1RegExp = /^[+\-]?\d+(?:\.\d+)?(?:e[+\-]\d+)?/; /** * `,`-separated decimal. * @private */ const decimal2RegExp = /^[+\-]?\d+(?:,\d+)?(?:e[+\-]\d+)?/; /** * - Group 1: Function name * @private */ const functionRegExp = /^([A-Z][A-Z\d\.]*)\(/; /** * @private */ const operatorRegExp = /^(?:[+\-*\/^<=>]|<=|=>)/; /** * - Group 1: Start column * - Group 2: Start row * - Group 3: End column * - Group 4: End row * @private */ const rangeA1RegExp = /^(\$?[A-Z]+)(\$?\d+)\:(\$?[A-Z]+)(\$?\d+)/; /** * - Group 1: Start row * - Group 2: Start column * - Group 3: End row * - Group 4: End column * @private */ const rangeR1C1RegExp = /^R(\d*|\[\d+\])C(\d*|\[\d+\])\:R(\d*|\[\d+\])C(\d*|\[\d+\])/; /** * - Group 1: Column * - Group 2: Row * @private */ const referenceA1RegExp = /^(\$?[A-Z]+)(\$?\d+)(?![\:C])/; /** * - Group 1: Row * - Group 2: Column * @private */ const referenceR1C1RegExp = /^R(\d*|\[\d+\])C(\d*|\[\d+\])(?!\:)/; /* * * * Functions * * */ /** * Extracts the inner string of the most outer parantheses. * * @private * * @param {string} text * Text string to extract from. * * @return {string} * Extracted parantheses. If not found an exception will be thrown. */ function extractParantheses(text) { let parantheseLevel = 0; for (let i = 0, iEnd = text.length, char, parantheseStart = 1; i < iEnd; ++i) { char = text[i]; if (char === '(') { if (!parantheseLevel) { parantheseStart = i + 1; } ++parantheseLevel; continue; } if (char === ')') { --parantheseLevel; if (!parantheseLevel) { return text.substring(parantheseStart, i); } } } if (parantheseLevel > 0) { const error = new Error('Incomplete parantheses.'); error.name = 'FormulaParseError'; throw error; } return ''; } /** * Extracts the inner string value. * * @private * * @param {string} text * Text string to extract from. * * @return {string} * Extracted string. If not found an exception will be thrown. */ function extractString(text) { let start = -1; for (let i = 0, iEnd = text.length, char, escaping = false; i < iEnd; ++i) { char = text[i]; if (char === '\\') { escaping = !escaping; continue; } if (escaping) { escaping = false; continue; } if (char === '"') { if (start < 0) { start = i; } else { return text.substring(start + 1, i); // `ì` is excluding } } } const error = new Error('Incomplete string.'); error.name = 'FormulaParseError'; throw error; } /** * Parses an argument string. Formula arrays with a single term will be * simplified to the term. * * @private * * @param {string} text * Argument string to parse. * * @param {boolean} alternativeSeparators * Whether to expect `;` as argument separator and `,` as decimal separator. * * @return {Formula|Function|Range|Reference|Value} * The recognized term structure. */ function parseArgument(text, alternativeSeparators) { let match; // Check for a R1C1:R1C1 range notation match = text.match(rangeR1C1RegExp); if (match) { const beginColumnRelative = (match[2] === '' || match[2][0] === '['); const beginRowRelative = (match[1] === '' || match[1][0] === '['); const endColumnRelative = (match[4] === '' || match[4][0] === '['); const endRowRelative = (match[3] === '' || match[3][0] === '['); const range = { type: 'range', beginColumn: (beginColumnRelative ? parseInt(match[2].substring(1, -1) || '0', 10) : parseInt(match[2], 10) - 1), beginRow: (beginRowRelative ? parseInt(match[1].substring(1, -1) || '0', 10) : parseInt(match[1], 10) - 1), endColumn: (endColumnRelative ? parseInt(match[4].substring(1, -1) || '0', 10) : parseInt(match[4], 10) - 1), endRow: (endRowRelative ? parseInt(match[3].substring(1, -1) || '0', 10) : parseInt(match[3], 10) - 1) }; if (beginColumnRelative) { range.beginColumnRelative = true; } if (beginRowRelative) { range.beginRowRelative = true; } if (endColumnRelative) { range.endColumnRelative = true; } if (endRowRelative) { range.endRowRelative = true; } return range; } // Check for a A1:A1 range notation match = text.match(rangeA1RegExp); if (match) { const beginColumnRelative = match[1][0] !== '$'; const beginRowRelative = match[2][0] !== '$'; const endColumnRelative = match[3][0] !== '$'; const endRowRelative = match[4][0] !== '$'; const range = { type: 'range', beginColumn: parseReferenceColumn(beginColumnRelative ? match[1] : match[1].substring(1)) - 1, beginRow: parseInt(beginRowRelative ? match[2] : match[2].substring(1), 10) - 1, endColumn: parseReferenceColumn(endColumnRelative ? match[3] : match[3].substring(1)) - 1, endRow: parseInt(endRowRelative ? match[4] : match[4].substring(1), 10) - 1 }; if (beginColumnRelative) { range.beginColumnRelative = true; } if (beginRowRelative) { range.beginRowRelative = true; } if (endColumnRelative) { range.endColumnRelative = true; } if (endRowRelative) { range.endRowRelative = true; } return range; } // Fallback to formula processing for other pattern types const formula = parseFormula(text, alternativeSeparators); return (formula.length === 1 && typeof formula[0] !== 'string' ? formula[0] : formula); } /** * Parse arguments string inside function parantheses. * * @private * * @param {string} text * Parantheses string of the function. * * @param {boolean} alternativeSeparators * Whether to expect `;` as argument separator and `,` as decimal separator. * * @return {Highcharts.FormulaArguments} * Parsed arguments array. */ function parseArguments(text, alternativeSeparators) { const args = [], argumentsSeparator = (alternativeSeparators ? ';' : ','); let parantheseLevel = 0, term = ''; for (let i = 0, iEnd = text.length, char; i < iEnd; ++i) { char = text[i]; // Check for separator if (char === argumentsSeparator && !parantheseLevel && term) { args.push(parseArgument(term, alternativeSeparators)); term = ''; // Check for a quoted string before skip logic } else if (char === '"' && !parantheseLevel && !term) { const string = extractString(text.substring(i)); args.push(string); i += string.length + 1; // Only +1 to cover ++i in for-loop // Skip space and check paranthesis nesting } else if (char !== ' ') { term += char; if (char === '(') { ++parantheseLevel; } else if (char === ')') { --parantheseLevel; } } } // Look for left-overs from last argument if (!parantheseLevel && term) { args.push(parseArgument(term, alternativeSeparators)); } return args; } /** * Checks if there's one of the following operator before the negative number * value: '*', '/' or '^'. * * Used to properly indicate a negative value reference or negate a directly * passed number value. */ function negativeReference(formula) { const formulaLength = formula.length; const priorFormula = formula[formulaLength - 2]; return (formula[formulaLength - 1] === '-' && isString(priorFormula) && !!priorFormula.match(/\*|\/|\^/)); } /** * Converts a spreadsheet formula string into a formula array. Throws a * `FormulaParserError` when the string can not be parsed. * * @private * @function Formula.parseFormula * * @param {string} text * Spreadsheet formula string, without the leading `=`. * * @param {boolean} alternativeSeparators * * `false` to expect `,` between arguments and `.` in decimals. * * `true` to expect `;` between arguments and `,` in decimals. * * @return {Formula.Formula} * Formula array representing the string. */ function parseFormula(text, alternativeSeparators) { const decimalRegExp = (alternativeSeparators ? decimal2RegExp : decimal1RegExp), formula = []; let match, next = (text[0] === '=' ? text.substring(1) : text).trim(); while (next) { // Check for an R1C1 reference notation match = next.match(referenceR1C1RegExp); if (match) { const columnRelative = (match[2] === '' || match[2][0] === '['); const rowRelative = (match[1] === '' || match[1][0] === '['); const reference = { type: 'reference', column: (columnRelative ? parseInt(match[2].substring(1, -1) || '0', 10) : parseInt(match[2], 10) - 1), row: (rowRelative ? parseInt(match[1].substring(1, -1) || '0', 10) : parseInt(match[1], 10) - 1) }; if (columnRelative) { reference.columnRelative = true; } if (rowRelative) { reference.rowRelative = true; } if (negativeReference(formula)) { formula.pop(); reference.isNegative = true; } formula.push(reference); next = next.substring(match[0].length).trim(); continue; } // Check for an A1 reference notation match = next.match(referenceA1RegExp); if (match) { const columnRelative = match[1][0] !== '$'; const rowRelative = match[2][0] !== '$'; const reference = { type: 'reference', column: parseReferenceColumn(columnRelative ? match[1] : match[1].substring(1)) - 1, row: parseInt(rowRelative ? match[2] : match[2].substring(1), 10) - 1 }; if (columnRelative) { reference.columnRelative = true; } if (rowRelative) { reference.rowRelative = true; } if (negativeReference(formula)) { formula.pop(); reference.isNegative = true; } formula.push(reference); next = next.substring(match[0].length).trim(); continue; } // Check for a formula operator match = next.match(operatorRegExp); if (match) { formula.push(match[0]); next = next.substring(match[0].length).trim(); continue; } // Check for a boolean value match = next.match(booleanRegExp); if (match) { formula.push(match[0] === 'TRUE'); next = next.substring(match[0].length).trim(); continue; } // Check for a number value match = next.match(decimalRegExp); if (match) { let number = parseFloat(match[0]); // If the current value is multiplication-related and the previous // one is a minus sign, set the current value to negative and remove // the minus sign. if (negativeReference(formula)) { formula.pop(); number = -number; } formula.push(number); next = next.substring(match[0].length).trim(); continue; } // Check for a quoted string if (next[0] === '"') { const string = extractString(next); formula.push(string.substring(1, -1)); next = next.substring(string.length + 2).trim(); continue; } // Check for a function match = next.match(functionRegExp); if (match) { next = next.substring(match[1].length).trim(); const parantheses = extractParantheses(next); formula.push({ type: 'function', name: match[1], args: parseArguments(parantheses, alternativeSeparators) }); next = next.substring(parantheses.length + 2).trim(); continue; } // Check for a formula in parantheses if (next[0] === '(') { const paranteses = extractParantheses(next); if (paranteses) { formula .push(parseFormula(paranteses, alternativeSeparators)); next = next.substring(paranteses.length + 2).trim(); continue; } } // Something is not right const position = text.length - next.length, error = new Error('Unexpected character `' + text.substring(position, position + 1) + '` at position ' + (position + 1) + '. (`...' + text.substring(position - 5, position + 6) + '...`)'); error.name = 'FormulaParseError'; throw error; } return formula; } /** * Converts a reference column `A` of `A1` into a number. Supports endless sizes * `ZZZ...`, just limited by integer precision. * * @private * * @param {string} text * Column string to convert. * * @return {number} * Converted column index. */ function parseReferenceColumn(text) { let column = 0; for (let i = 0, iEnd = text.length, code, factor = text.length - 1; i < iEnd; ++i) { code = text.charCodeAt(i); if (code >= 65 && code <= 90) { column += (code - 64) * Math.pow(26, factor); } --factor; } return column; } /* * * * Default Export * * */ const FormulaParser = { parseFormula }; /* harmony default export */ const Formula_FormulaParser = (FormulaParser); ;// ./code/dashboards/es-modules/Data/Formula/FormulaTypes.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ /* * * * Constants * * */ /** * Array of all possible operators. * @private */ const operators = ['+', '-', '*', '/', '^', '=', '<', '<=', '>', '>=']; /* * * * Functions * * */ /** * Tests an item for a Formula array. * * @private * * @param {Highcharts.FormulaItem} item * Item to test. * * @return {boolean} * `true`, if the item is a formula (or argument) array. */ function isFormula(item) { return Array.isArray(item); } /** * Tests an item for a Function structure. * * @private * * @param {Highcharts.FormulaItem} item * Item to test. * * @return {boolean} * `true`, if the item is a formula function. */ function isFunction(item) { return (typeof item === 'object' && !(Array.isArray(item)) && item.type === 'function'); } /** * Tests an item for an Operator string. * * @private * * @param {Highcharts.FormulaItem} item * Item to test. * * @return {boolean} * `true`, if the item is an operator string. */ function isOperator(item) { return (typeof item === 'string' && operators.indexOf(item) >= 0); } /** * Tests an item for a Range structure. * * @private * * @param {Highcharts.FormulaItem} item * Item to test. * * @return {boolean} * `true`, if the item is a range. */ function isRange(item) { return (typeof item === 'object' && !(Array.isArray(item)) && item.type === 'range'); } /** * Tests an item for a Reference structure. * * @private * * @param {Highcharts.FormulaItem} item * Item to test. * * @return {boolean} * `true`, if the item is a reference. */ function isReference(item) { return (typeof item === 'object' && !(Array.isArray(item)) && item.type === 'reference'); } /** * Tests an item for a Value structure. * * @private * * @param {Highcharts.FormulaItem|null|undefined} item * Item to test. * * @return {boolean} * `true`, if the item is a value. */ function isValue(item) { return (typeof item === 'boolean' || typeof item === 'number' || typeof item === 'string'); } /* * * * Default Export * * */ const MathFormula = { isFormula, isFunction, isOperator, isRange, isReference, isValue }; /* harmony default export */ const FormulaTypes = (MathFormula); ;// ./code/dashboards/es-modules/Data/Formula/FormulaProcessor.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { isFormula: FormulaProcessor_isFormula, isFunction: FormulaProcessor_isFunction, isOperator: FormulaProcessor_isOperator, isRange: FormulaProcessor_isRange, isReference: FormulaProcessor_isReference, isValue: FormulaProcessor_isValue } = FormulaTypes; const { defined } = (dashboards_commonjs_dashboards_commonjs2_dashboards_root_Dashboards_default()); /* * * * Constants * * */ const asLogicalStringRegExp = / */; const MAX_FALSE = Number.MAX_VALUE / 1.000000000001; const MAX_STRING = Number.MAX_VALUE / 1.000000000002; const MAX_TRUE = Number.MAX_VALUE; const operatorPriority = { '^': 3, '*': 2, '/': 2, '+': 1, '-': 1, '=': 0, '<': 0, '<=': 0, '>': 0, '>=': 0 }; const processorFunctions = {}; const processorFunctionNameRegExp = /^[A-Z][A-Z\.]*$/; /* * * * Functions * * */ /** * Converts non-number types to logical numbers. * * @param {Highcharts.FormulaValue} value * Value to convert. * * @return {number} * Logical number value. `NaN` if not convertable. */ function asLogicalNumber(value) { switch (typeof value) { case 'boolean': return value ? MAX_TRUE : MAX_FALSE; case 'string': return MAX_STRING; case 'number': return value; default: return NaN; } } /** * Converts strings to logical strings, while other types get passed through. In * logical strings the space character is the lowest value and letters are case * insensitive. * * @param {Highcharts.FormulaValue} value * Value to convert. * * @return {Highcharts.FormulaValue} * Logical string value or passed through value. */ function asLogicalString(value) { if (typeof value === 'string') { return value.toLowerCase().replace(asLogicalStringRegExp, '\0'); } return value; } /** * Converts non-number types to a logic number. * * @param {Highcharts.FormulaValue} value * Value to convert. * * @return {number} * Number value. `NaN` if not convertable. */ function asNumber(value) { switch (typeof value) { case 'boolean': return value ? 1 : 0; case 'string': return parseFloat(value.replace(',', '.')); case 'number': return value; default: return NaN; } } /** * Process a basic operation of two given values. * * @private * * @param {Highcharts.FormulaOperator} operator * Operator between values. * * @param {Highcharts.FormulaValue} x * First value for operation. * * @param {Highcharts.FormulaValue} y * Second value for operation. * * @return {Highcharts.FormulaValue} * Operation result. `NaN` if operation is not support. */ function basicOperation(operator, x, y) { switch (operator) { case '=': return asLogicalString(x) === asLogicalString(y); case '<': if (typeof x === typeof y) { return asLogicalString(x) < asLogicalString(y); } return asLogicalNumber(x) < asLogicalNumber(y); case '<=': if (typeof x === typeof y) { return asLogicalString(x) <= asLogicalString(y); } return asLogicalNumber(x) <= asLogicalNumber(y); case '>': if (typeof x === typeof y) { return asLogicalString(x) > asLogicalString(y); } return asLogicalNumber(x) > asLogicalNumber(y); case '>=': if (typeof x === typeof y) { return asLogicalString(x) >= asLogicalString(y); } return asLogicalNumber(x) >= asLogicalNumber(y); } x = asNumber(x); y = asNumber(y); let result; switch (operator) { case '+': result = x + y; break; case '-': result = x - y; break; case '*': result = x * y; break; case '/': result = x / y; break; case '^': result = Math.pow(x, y); break; default: return NaN; } // Limit decimal to 9 digits return (result % 1 ? Math.round(result * 1000000000) / 1000000000 : result); } /** * Converts an argument to Value and in case of a range to an array of Values. * * @function Highcharts.Formula.getArgumentValue * * @param {Highcharts.FormulaRange|Highcharts.FormulaTerm} arg * Formula range or term to convert. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {Highcharts.FormulaValue|Array<Highcharts.FormulaValue>} * Converted value. */ function getArgumentValue(arg, table) { // Add value if (FormulaProcessor_isValue(arg)) { return arg; } // Add values of a range if (FormulaProcessor_isRange(arg)) { return (table && getRangeValues(arg, table) || []); } // Add values of a function if (FormulaProcessor_isFunction(arg)) { return processFunction(arg, table); } // Process functions, operations, references with formula processor return processFormula((FormulaProcessor_isFormula(arg) ? arg : [arg]), table); } /** * Converts all arguments to Values and in case of ranges to arrays of Values. * * @function Highcharts.Formula.getArgumentsValues * * @param {Highcharts.FormulaArguments} args * Formula arguments to convert. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {Array<(Highcharts.FormulaValue|Array<Highcharts.FormulaValue>)>} * Converted values. */ function getArgumentsValues(args, table) { const values = []; for (let i = 0, iEnd = args.length; i < iEnd; ++i) { values.push(getArgumentValue(args[i], table)); } return values; } /** * Extracts cell values from a table for a given range. * * @function Highcharts.Formula.getRangeValues * * @param {Highcharts.FormulaRange} range * Formula range to use. * * @param {Highcharts.DataTable} table * Table to extract from. * * @return {Array<Highcharts.FormulaValue>} * Extracted values. */ function getRangeValues(range, table) { const columnIds = table .getColumnIds() .slice(range.beginColumn, range.endColumn + 1), values = []; for (let i = 0, iEnd = columnIds.length, cell; i < iEnd; ++i) { const cells = table.getColumn(columnIds[i], true) || []; for (let j = range.beginRow, jEnd = range.endRow + 1; j < jEnd; ++j) { cell = cells[j]; if (typeof cell === 'string' && cell[0] === '=' && table !== table.getModified()) { // Look in the modified table for formula result cell = table.getModified().getCell(columnIds[i], j); } values.push(FormulaProcessor_isValue(cell) ? cell : NaN); } } return values; } /** * Extracts the cell value from a table for a given reference. * * @private * * @param {Highcharts.FormulaReference} reference * Formula reference to use. * * @param {Highcharts.DataTable} table * Table to extract from. * * @return {Highcharts.FormulaValue} * Extracted value. 'undefined' might also indicate that the cell was not found. */ function getReferenceValue(reference, table) { const columnId = table.getColumnIds()[reference.column]; if (columnId) { const cell = table.getCell(columnId, reference.row); if (typeof cell === 'string' && cell[0] === '=' && table !== table.getModified()) { // Look in the modified table for formula result const result = table.getModified().getCell(columnId, reference.row); return FormulaProcessor_isValue(result) ? result : NaN; } if (FormulaProcessor_isValue(cell)) { return reference.isNegative ? -cell : cell; } return NaN; } return NaN; } /** * Calculates a value based on the two top values and the related operator. * * Used to properly process the formula's values based on its operators. * * @private * @function Highcharts.applyOperator * * @param {Array<Highcharts.Value>} values * Processed formula values. * * @param {Array<Highcharts.Operator>} operators * Processed formula operators. */ function applyOperator(values, operators) { if (values.length < 2 || operators.length < 1) { values.push(NaN); } const secondValue = values.pop(); const firstValue = values.pop(); const operator = operators.pop(); if (!defined(secondValue) || !defined(firstValue) || !defined(operator)) { values.push(NaN); } else { values.push(basicOperation(operator, firstValue, secondValue)); } } /** * Processes a formula array on the given table. If the formula does not contain * references or ranges, then no table has to be provided. * * Performs formulas considering the operators precedence. * * // Example of the `2 * 3 + 4` formula: * 2 -> values: [2], operators: [] * * -> values: [2], operators: [*] * 3 -> values: [2, 3], operators: [*] * // Since the higher precedence operator exists (* > +), perform it first. * + -> values: [6], operators: [+] * 4 -> values: [6, 4], operators: [+] * // When non-higher precedence operators remain, perform rest calculations. * -> values: [10], operators: [] * * @private * @function Highcharts.processFormula * * @param {Highcharts.Formula} formula * Formula array to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {Highcharts.FormulaValue} * Result value of the process. `NaN` indicates an error. */ function processFormula(formula, table) { // Keeps all the values to calculate them in a proper priority, based on the // given operators. const values = []; // Keeps all the operators to calculate the values above, following the // proper priority. const operators = []; // Indicates if the next item is a value (not an operator). let expectingValue = true; for (let i = 0, iEnd = formula.length; i < iEnd; ++i) { const item = formula[i]; if (FormulaProcessor_isOperator(item)) { if (expectingValue && item === '-') { // Split the negative values to be handled as a binary // operation if the next item is a value. values.push(0); operators.push('-'); expectingValue = true; } else { // Perform if the higher precedence operator exist. while (operators.length && operatorPriority[operators[operators.length - 1]] >= operatorPriority[item]) { applyOperator(values, operators); } operators.push(item); expectingValue = true; } continue; } let value; // Assign the proper value, starting from the most common types. if (FormulaProcessor_isValue(item)) { value = item; } else if (FormulaProcessor_isReference(item)) { value = table ? getReferenceValue(item, table) : NaN; } else if (FormulaProcessor_isFunction(item)) { const result = processFunction(item, table); value = FormulaProcessor_isValue(result) ? result : NaN; } else if (FormulaProcessor_isFormula(item)) { value = processFormula(item, table); } if (typeof value !== 'undefined') { values.push(value); expectingValue = false; } else { return NaN; } } // Handle the remaining operators that weren't taken into consideration, due // to non-higher precedence. while (operators.length) { applyOperator(values, operators); } if (values.length !== 1) { return NaN; } return values[0]; } /** * Process a function on the given table. If the arguments do not contain * references or ranges, then no table has to be provided. * * @private * * @param {Highcharts.FormulaFunction} formulaFunction * Formula function to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @param {Highcharts.FormulaReference} [reference] * Table cell reference to use for relative references and ranges. * * @return {Highcharts.FormulaValue|Array<Highcharts.FormulaValue>} * Result value (or values) of the process. `NaN` indicates an error. */ function processFunction(formulaFunction, table, // eslint-disable-next-line @typescript-eslint/no-unused-vars reference // @todo ) { const processor = processorFunctions[formulaFunction.name]; if (processor) { try { return processor(formulaFunction.args, table); } catch { return NaN; } } const error = new Error(`Function "${formulaFunction.name}" not found.`); error.name = 'FormulaProcessError'; throw error; } /** * Registers a function for the FormulaProcessor. * * @param {string} name * Name of the function in spreadsheets notation with upper case. * * @param {Highcharts.FormulaFunction} processorFunction * ProcessorFunction for the FormulaProcessor. This is an object so that it * can take additional parameter for future validation routines. * * @return {boolean} * Return true, if the ProcessorFunction has been registered. */ function registerProcessorFunction(name, processorFunction) { return (processorFunctionNameRegExp.test(name) && !processorFunctions[name] && !!(processorFunctions[name] = processorFunction)); } /** * Translates relative references and ranges in-place. * * @param {Highcharts.Formula} formula * Formula to translate references and ranges in. * * @param {number} [columnDelta=0] * Column delta to translate to. Negative translate back. * * @param {number} [rowDelta=0] * Row delta to translate to. Negative numbers translate back. * * @return {Highcharts.Formula} * Formula with translated reference and ranges. This formula is equal to the * first argument. */ function translateReferences(formula, columnDelta = 0, rowDelta = 0) { for (let i = 0, iEnd = formula.length, item; i < iEnd; ++i) { item = formula[i]; if (Array.isArray(item)) { translateReferences(item, columnDelta, rowDelta); } else if (FormulaProcessor_isFunction(item)) { translateReferences(item.args, columnDelta, rowDelta); } else if (FormulaProcessor_isRange(item)) { if (item.beginColumnRelative) { item.beginColumn += columnDelta; } if (item.beginRowRelative) { item.beginRow += rowDelta; } if (item.endColumnRelative) { item.endColumn += columnDelta; } if (item.endRowRelative) { item.endRow += rowDelta; } } else if (FormulaProcessor_isReference(item)) { if (item.columnRelative) { item.column += columnDelta; } if (item.rowRelative) { item.row += rowDelta; } } } return formula; } /* * * * Default Export * * */ const FormulaProcessor = { asNumber, getArgumentValue, getArgumentsValues, getRangeValues, getReferenceValue, processFormula, processorFunctions, registerProcessorFunction, translateReferences }; /* harmony default export */ const Formula_FormulaProcessor = (FormulaProcessor); ;// ./code/dashboards/es-modules/Data/Formula/Functions/ABS.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentValue: ABS_getArgumentValue } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `ABS(value)` implementation. Returns positive numbers. * * @private * @function Formula.processorFunctions.AND * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {Array<number>} * Result value of the process. */ function ABS(args, table) { const value = ABS_getArgumentValue(args[0], table); switch (typeof value) { case 'number': return Math.abs(value); case 'object': { const values = []; for (let i = 0, iEnd = value.length, value2; i < iEnd; ++i) { value2 = value[i]; if (typeof value2 !== 'number') { return NaN; } values.push(Math.abs(value2)); } return values; } default: return NaN; } } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('ABS', ABS); /* * * * Default Export * * */ /* harmony default export */ const Functions_ABS = ((/* unused pure expression or super */ null && (ABS))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/AND.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentValue: AND_getArgumentValue } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `AND(...tests)` implementation. Returns `TRUE`, if all test * results are not `0` or `FALSE`. * * @private * @function Formula.processorFunctions.AND * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {boolean} * Result value of the process. */ function AND(args, table) { for (let i = 0, iEnd = args.length, value; i < iEnd; ++i) { value = AND_getArgumentValue(args[i], table); if (!value || (typeof value === 'object' && !AND(value, table))) { return false; } } return true; } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('AND', AND); /* * * * Default Export * * */ /* harmony default export */ const Functions_AND = ((/* unused pure expression or super */ null && (AND))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/AVERAGE.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentsValues: AVERAGE_getArgumentsValues } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `AVERAGE(...values)` implementation. Calculates the average * of the given values that are numbers. * * @private * @function Formula.processorFunctions.AVERAGE * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {number} * Result value of the process. */ function AVERAGE(args, table) { const values = AVERAGE_getArgumentsValues(args, table); let count = 0, result = 0; for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) { value = values[i]; switch (typeof value) { case 'number': if (!isNaN(value)) { ++count; result += value; } break; case 'object': for (let j = 0, jEnd = value.length, value2; j < jEnd; ++j) { value2 = value[j]; if (typeof value2 === 'number' && !isNaN(value2)) { ++count; result += value2; } } break; } } return (count ? (result / count) : 0); } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('AVERAGE', AVERAGE); /* * * * Default Export * * */ /* harmony default export */ const Functions_AVERAGE = ((/* unused pure expression or super */ null && (AVERAGE))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/AVERAGEA.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentValue: AVERAGEA_getArgumentValue } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `AVERAGEA(...values)` implementation. Calculates the * average of the given values. Strings and FALSE are calculated as 0. * * @private * @function Formula.processorFunctions.AVERAGEA * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {number} * Result value of the process. */ function AVERAGEA(args, table) { let count = 0, result = 0; for (let i = 0, iEnd = args.length, value; i < iEnd; ++i) { value = AVERAGEA_getArgumentValue(args[i], table); switch (typeof value) { case 'boolean': ++count; result += (value ? 1 : 0); continue; case 'number': if (!isNaN(value)) { ++count; result += value; } continue; case 'string': ++count; continue; default: for (let j = 0, jEnd = value.length, value2; j < jEnd; ++j) { value2 = value[j]; switch (typeof value2) { case 'boolean': ++count; result += (value2 ? 1 : 0); continue; case 'number': if (!isNaN(value2)) { ++count; result += value2; } continue; case 'string': ++count; continue; } } continue; } } return (count ? (result / count) : 0); } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('AVERAGEA', AVERAGEA); /* * * * Default Export * * */ /* harmony default export */ const Functions_AVERAGEA = ((/* unused pure expression or super */ null && (AVERAGEA))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/COUNT.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ /* * * * Functions * * */ /** * Processor for the `COUNT(...values)` implementation. Returns the count of * given values that are numbers. * * @private * @function Formula.processorFunctions.COUNT * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {number} * Result value of the process. */ function COUNT(args, table) { const values = Formula_FormulaProcessor.getArgumentsValues(args, table); let count = 0; for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) { value = values[i]; switch (typeof value) { case 'number': if (!isNaN(value)) { ++count; } break; case 'object': count += COUNT(value, table); break; } } return count; } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('COUNT', COUNT); /* * * * Default Export * * */ /* harmony default export */ const Functions_COUNT = ((/* unused pure expression or super */ null && (COUNT))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/COUNTA.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ /* * * * Functions * * */ /** * Processor for the `COUNTA(...values)` implementation. Returns the count of * given values that are not empty. * * @private * @function Formula.processorFunctions.COUNT * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {number} * Result value of the process. */ function COUNTA(args, table) { const values = Formula_FormulaProcessor.getArgumentsValues(args, table); let count = 0; for (let i = 0, iEnd = values.length, value; i < iEnd; ++i) { value = values[i]; switch (typeof value) { case 'number': if (isNaN(value)) { continue; } break; case 'object': count += COUNTA(value, table); continue; case 'string': if (!value) { continue; } break; } ++count; } return count; } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('COUNTA', COUNTA); /* * * * Default Export * * */ /* harmony default export */ const Functions_COUNTA = ((/* unused pure expression or super */ null && (COUNTA))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/IF.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentValue: IF_getArgumentValue } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `IF(test, value1, value2)` implementation. Returns one of * the values based on the test result. `value1` will be returned, if the test * result is not `0` or `FALSE`. * * @private * @function Formula.processorFunctions.IF * * @param {Highcharts.FormulaArguments} args * Arguments to process. * * @param {Highcharts.DataTable} [table] * Table to use for references and ranges. * * @return {Highcharts.FormulaValue|Array<Highcharts.FormulaValue>} * Result value of the process. */ function IF(args, table) { return (IF_getArgumentValue(args[0], table) ? IF_getArgumentValue(args[1], table) : IF_getArgumentValue(args[2], table)); } /* * * * Registry * * */ Formula_FormulaProcessor.registerProcessorFunction('IF', IF); /* * * * Default Export * * */ /* harmony default export */ const Functions_IF = ((/* unused pure expression or super */ null && (IF))); ;// ./code/dashboards/es-modules/Data/Formula/Functions/ISNA.js /* * * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * Authors: * - Sophie Bremer * * */ const { getArgumentValue: ISNA_getArgumentValue } = Formula_FormulaProcessor; /* * * * Functions * * */ /** * Processor for the `ISNA(value)` implementation. Returns TRUE if value is not *