UNPKG

@syncfusion/ej2-spreadsheet

Version:

Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel

1,193 lines (1,192 loc) 179 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; /* eslint-disable @typescript-eslint/no-explicit-any */ import { getValue, Event, NotifyPropertyChanges, Base, isNullOrUndefined, isUndefined } from '@syncfusion/ej2-base'; import { BasicFormulas } from './../formulas/index'; import { CommonErrors, FormulasErrorsStrings } from '../common/enum'; import { Parser } from './parser'; import { getRangeIndexes, getCellIndexes, getCellAddress, isDateTime, workbookFormulaOperation } from '../../workbook/index'; import { getSheetIndexByName } from '../../workbook/index'; import { DataUtil } from '@syncfusion/ej2-data'; var maxRows = 1048576; var maxCols = 16384; /** * Represents the calculate library. * * @hidden */ var Calculate = /** @class */ (function (_super) { __extends(Calculate, _super); /** * Base constructor for creating Calculate library. * * @param {Object} parent - specify the parent */ function Calculate(parent) { var _this = _super.call(this, null, null) || this; _this.lFormulas = new Map(); /** @hidden */ _this.storedData = new Map(); _this.keyToRowsMap = new Map(); _this.rowsToKeyMap = new Map(); /** @hidden */ _this.rightBracket = String.fromCharCode(161); /** @hidden */ _this.leftBracket = String.fromCharCode(162); /** @hidden */ _this.sheetToken = '!'; _this.emptyString = ''; _this.leftBrace = '{'; _this.rightBrace = '}'; _this.cell = _this.emptyString; _this.cellPrefix = '!0!A'; _this.treatEmptyStringAsZero = false; /** @hidden */ _this.tic = '"'; /** @hidden */ _this.singleTic = '\''; /** @hidden */ _this.trueValue = 'TRUE'; /** @hidden */ _this.falseValue = 'FALSE'; _this.parseDecimalSeparator = '.'; /** @hidden */ _this.arithMarker = String.fromCharCode(180); /** @hidden */ _this.arithMarker2 = _this.arithMarker + _this.arithMarker; _this.dependentCells = null; _this.dependentFormulaCells = null; _this.minValue = Number.MIN_SAFE_INTEGER; _this.maxValue = Number.MAX_SAFE_INTEGER; _this.categoryCollection = ['All']; _this.dependencyLevel = 0; /** @hidden */ _this.randomValues = new Map(); /** @hidden */ _this.isRandomVal = false; /** @hidden */ _this.randCollection = []; /** @hidden */ _this.dependencyCollection = []; /** @hidden */ _this.uniqueRange = []; /** * @hidden */ _this.formulaErrorStrings = [ 'binary operators cannot start an expression', 'cannot parse', 'bad library', 'invalid char in front of', 'number contains 2 decimal points', 'expression cannot end with an operator', 'invalid characters following an operator', 'invalid character in number', 'mismatched parentheses', 'unknown formula name', 'requires a single argument', 'requires 3 arguments', 'invalid Math argument', 'requires 2 arguments', '#NAME?', 'too complex', '#CIRCULARREF!', 'missing formula', 'improper formula', 'invalid expression', 'cell empty', 'bad formula', 'empty expression', '', 'mismatched string quotes', 'wrong number of arguments', 'invalid arguments', 'iterations do not converge', 'Control is already registered', 'Calculation overflow', 'Missing sheet', 'cannot_parse', 'expression_cannot_end_with_an_operator', '#SPILL!', '#DIV/0!' ]; _this.errorStrings = null; _this.parseArgumentSeparator = ','; _this.dateTime1900 = new Date(1900, 0, 1, 0, 0, 0); _this.isParseDecimalSeparatorChanged = false; _this.isArgumentSeparatorChanged = false; _this.sheetFamilyID = 0; _this.defaultFamilyItem = null; _this.sheetFamiliesList = null; _this.modelToSheetID = null; /** @hidden */ _this.tokenCount = 0; _this.sortedSheetNames = null; _this.tempSheetPlaceHolder = String.fromCharCode(133); /** @hidden */ _this.namedRanges = new Map(); _this.formulaInfoTable = null; _this.millisecondsOfaDay = 24 * 60 * 60 * 1000; _this.parseDateTimeSeparator = '/'; new BasicFormulas(_this); _this.parentObject = isNullOrUndefined(parent) ? _this : parent; _this.grid = _this.parentObject; _this.parser = new Parser(_this); return _this; } Object.defineProperty(Calculate.prototype, "libraryFormulas", { get: function () { return this.lFormulas; }, set: function (formulaColl) { this.lFormulas.set(formulaColl.fName, { handler: formulaColl.handler, category: formulaColl.category, description: formulaColl.description }); }, enumerable: true, configurable: true }); /** * To get the argument separator to split the formula arguments. * * @returns {string} - To get the argument separator to split the formula arguments. */ Calculate.prototype.getParseArgumentSeparator = function () { var seperator = ','; if (!this.isArgumentSeparatorChanged && seperator !== this.parseArgumentSeparator) { this.parseArgumentSeparator = seperator; } return this.parseArgumentSeparator; }; /** * To set the argument separator to split the formula arguments. * * @param {string} value - Argument separator based on the culture. * @returns {void} - To set the argument separator to split the formula arguments. */ Calculate.prototype.setParseArgumentSeparator = function (value) { this.parseArgumentSeparator = value; this.isArgumentSeparatorChanged = true; }; /** * To get the date separator to split the date value. * * @returns {string} - To get the date separator to split the date value. */ Calculate.prototype.getParseDateTimeSeparator = function () { return this.parseDateTimeSeparator; }; /** * To set whether the empty string is treated as zero or not. * * @param {boolean} value - specify the boolean. * @returns {void} - To set whether the empty string is treated as zero or not. */ Calculate.prototype.setTreatEmptyStringAsZero = function (value) { this.treatEmptyStringAsZero = value; }; /** * To get whether the empty string is treated as zero or not. * * @returns {boolean} - To get whether the empty string is treated as zero or not. */ Calculate.prototype.getTreatEmptyStringAsZero = function () { return this.treatEmptyStringAsZero; }; /** * To set the date separator to split the date value. * * @param {string} value - Argument separator based on the culture. * @returns {void} - To set the date separator to split the date value. */ Calculate.prototype.setParseDateTimeSeparator = function (value) { this.parseDateTimeSeparator = value; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars Calculate.prototype.onPropertyChanged = function (newProp, oldProp) { /** code snippets */ }; Calculate.prototype.getModuleName = function () { return 'calculate'; }; /** * @hidden * @returns {string} - get Formula Character. */ Calculate.prototype.getFormulaCharacter = function () { return '='; }; /** * @hidden * @param {string} text - specify the text * @returns {boolean} - Returns boolean value. */ Calculate.prototype.isUpperChar = function (text) { var charCode = text.charCodeAt(0); return charCode > 64 && charCode < 91; }; Calculate.prototype.resetKeys = function () { this.storedData.clear(); this.keyToRowsMap.clear(); this.rowsToKeyMap.clear(); }; /** * @hidden * @param {string} cellRef - specify the cell reference * @returns {void} - update Dependent Cell */ Calculate.prototype.updateDependentCell = function (cellRef) { var _this = this; var formulaCell = this.cell; if (formulaCell !== this.emptyString) { var family = this.getSheetFamilyItem(this.grid); if (family.sheetNameToParentObject) { if (!formulaCell.includes(this.sheetToken)) { formulaCell = family.parentObjectToToken.get(this.grid) + formulaCell; } if (!cellRef.includes(this.sheetToken)) { cellRef = family.parentObjectToToken.get(this.grid) + cellRef; } } if (formulaCell !== cellRef) { var dependentCellMap = this.getDependentCells(); if (!dependentCellMap.has(cellRef)) { dependentCellMap.set(cellRef, []); } var dependentCells = dependentCellMap.get(cellRef); if (dependentCells.indexOf(formulaCell) === -1) { var formulaDependentCellMap_1 = this.getDependentFormulaCells(); var cellRefObj_1 = {}; var checkCircularReference_1 = function (refCell) { if (formulaDependentCellMap_1.has(refCell)) { var formalaRefCells = formulaDependentCellMap_1.get(refCell); if (formalaRefCells.has(formulaCell)) { throw _this.formulaErrorStrings[FormulasErrorsStrings.CircularReference]; } else if (!cellRefObj_1[refCell]) { cellRefObj_1[refCell] = true; formalaRefCells.forEach(function (refCell) { checkCircularReference_1(refCell); }); } } }; checkCircularReference_1(cellRef); dependentCells.push(formulaCell); if (!formulaDependentCellMap_1.has(formulaCell)) { formulaDependentCellMap_1.set(formulaCell, new Map()); formulaDependentCellMap_1.get(formulaCell).set(cellRef, cellRef); } else if (!formulaDependentCellMap_1.get(formulaCell).has(cellRef)) { formulaDependentCellMap_1.get(formulaCell).set(cellRef, cellRef); } } } } }; /** * @hidden * @returns {Map<string, string[]>} - get Dependent Cells */ Calculate.prototype.getDependentCells = function () { if (this.dependentCells == null) { this.dependentCells = new Map(); } return this.dependentCells; }; /** * @hidden * @returns {Map<string, Map<string, string>>} - get Dependent Formula Cells */ Calculate.prototype.getDependentFormulaCells = function () { if (this.isSheetMember()) { var family = this.getSheetFamilyItem(this.grid); if (family.sheetDependentFormulaCells == null) { family.sheetDependentFormulaCells = new Map(); } return family.sheetDependentFormulaCells; } else { if (this.dependentFormulaCells == null) { this.dependentFormulaCells = new Map(); } return this.dependentFormulaCells; } }; /** * To get library formulas collection. * * @returns {Map<string, Function>} - To get library formulas collection. */ Calculate.prototype.getLibraryFormulas = function () { return this.lFormulas; }; /** * To get library function. * * @param {string} libFormula - Library formula to get a corresponding function. * @returns {Function} - To get library function. */ Calculate.prototype.getFunction = function (libFormula) { if (this.getLibraryFormulas().has(libFormula.toUpperCase())) { return this.getLibraryFormulas().get(libFormula.toUpperCase()).handler; } else { return null; } }; /** * @hidden * @param {string} val - specify the value. * @returns {Date} - convert integer to date. */ Calculate.prototype.intToDate = function (val) { var dateVal = Number(val); dateVal = (dateVal > 0 && dateVal < 1) ? (1 + dateVal) : (dateVal === 0) ? 1 : dateVal; if (dateVal > 60) { dateVal -= 1; // Due to leap year issue of 1900 in MSExcel. } var startDate = new Date('01/01/1900'); var startDateUTC = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), startDate.getHours(), startDate.getMinutes(), startDate.getSeconds(), startDate.getMilliseconds()); return new Date(new Date(((dateVal - 1) * (1000 * 3600 * 24)) + startDateUTC).toUTCString().replace(' GMT', '')); }; Calculate.prototype.getFormulaInfoTable = function () { if (this.isSheetMember()) { var family = this.getSheetFamilyItem(this.grid); if (family.sheetFormulaInfotable === null) { family.sheetFormulaInfotable = new Map(); } return family.sheetFormulaInfotable; } else { if (this.formulaInfoTable === null) { this.formulaInfoTable = new Map(); } return this.formulaInfoTable; } }; /** * To get the formula text. * * @returns {void} - To get the formula text. */ Calculate.prototype.getParseDecimalSeparator = function () { var seperator = '.'; if (!this.isParseDecimalSeparatorChanged && seperator !== this.parseDecimalSeparator) { this.parseDecimalSeparator = seperator; } return this.parseDecimalSeparator; }; /** * To get the formula text. * * @param {string} value - Specifies the decimal separator value. * @returns {void} - To get the formula text. */ Calculate.prototype.setParseDecimalSeparator = function (value) { this.parseDecimalSeparator = value; this.isParseDecimalSeparatorChanged = true; }; /** * @hidden * @param {string} cellRef - specify the cell reference * @returns {string} - get sheet token. */ Calculate.prototype.getSheetToken = function (cellRef) { var i = 0; var temp = this.emptyString; if (i < cellRef.length && cellRef[i] === this.sheetToken) { i++; while (i < cellRef.length && cellRef[i] !== this.sheetToken) { i++; } temp = cellRef.substring(0, i + 1); } if (i < cellRef.length) { return temp; } throw this.formulaErrorStrings[FormulasErrorsStrings.BadIndex]; }; /** * @hidden * @param {Object} grd - specify the id * @returns {number} - get sheet id. */ Calculate.prototype.getSheetID = function (grd) { var family = this.getSheetFamilyItem(grd); if (family.sheetNameToParentObject != null && family.sheetNameToParentObject.size > 0) { var token = family.parentObjectToToken.get(grd); if (token) { token = token.split(this.sheetToken).join(this.emptyString); var id = this.parseFloat(token); if (!this.isNaN(id)) { return id; } } } return -1; }; /** * @hidden * @param {string | number} value - specify the value. * @returns {number} - parse float */ Calculate.prototype.parseFloat = function (value) { var convertedNum = Number(value); if (isNaN(convertedNum) && typeof value === 'string' && value.includes(',')) { convertedNum = Number(value.split(',').join('')); } return convertedNum; }; /** * To get the row index of the given cell. * * @param {string} cell - Cell address for getting row index. * @returns {number} - To get the row index of the given cell. */ Calculate.prototype.rowIndex = function (cell) { var i = 0; var isLetter = false; if (i < cell.length && cell[i] === this.sheetToken) { i++; while (i < cell.length && cell[i] !== this.sheetToken) { i++; } i++; } while (i < cell.length && this.isChar(cell[i])) { isLetter = true; i++; } var result = parseInt(cell.substring(i), 10); if (i < cell.length && !this.isNaN(result)) { return result; } if (isLetter) { return -1; } throw this.formulaErrorStrings[FormulasErrorsStrings.BadIndex]; }; /** * To get the column index of the given cell. * * @param {string} cell - Cell address for getting column index. * @returns {number} - To get the column index of the given cell. */ Calculate.prototype.colIndex = function (cell) { var j = 0; var k = 0; cell = cell.toUpperCase(); if (j < cell.length && cell[j] === this.sheetToken) { j++; while (j < cell.length && cell[j] !== this.sheetToken) { j++; } j++; } while (j < cell.length && this.isChar(cell[j])) { var charCode = cell[j].charCodeAt(0); k = k * 26 + charCode - 64; j++; } if (k === 0) { return -1; } return k; }; /** * To get the valid error strings. * * @hidden * @returns {string[]} - to get error strings. */ Calculate.prototype.getErrorStrings = function () { if (this.errorStrings === null) { this.errorStrings = ['#N/A', '#VALUE!', '#REF!', '#DIV/0!', '#NUM!', '#NAME?', '#NULL!', '#CALC!']; } return this.errorStrings; }; /** * @hidden * @param {string} text - specify the text * @param {number} startIndex - specify the start index * @param {number} length - specify the length * @returns {string} - Returns sub string */ Calculate.prototype.substring = function (text, startIndex, length) { return text.substring(startIndex, length + startIndex); }; /** * @hidden * @param {string} c - specify the characer of the string * @returns {boolean} - Return the boolean type */ Calculate.prototype.isChar = function (c) { if ((c.charCodeAt(0) >= 65 && c.charCodeAt(0) <= 90) || (c.charCodeAt(0) >= 97 && c.charCodeAt(0) <= 122)) { return true; } return false; }; /** * @hidden * @param {Object} model - specify the model * @param {Object} calcId - specify the calculate instance id. * @returns {CalcSheetFamilyItem} - get Sheet Family Item. */ Calculate.prototype.getSheetFamilyItem = function (model, calcId) { if (this.sheetFamilyID === 0) { if (this.defaultFamilyItem == null) { this.defaultFamilyItem = new CalcSheetFamilyItem(); } return this.defaultFamilyItem; } if (this.sheetFamiliesList == null) { this.sheetFamiliesList = new Map(); } if (calcId === undefined) { calcId = this.modelToSheetID.get(model); } if (!this.sheetFamiliesList.has(calcId)) { this.sheetFamiliesList.set(calcId, new CalcSheetFamilyItem()); } return this.sheetFamiliesList.get(calcId); }; /** * Register a key value pair for formula. * * @param {string} key - Key for formula reference . * @param {string | number} value - Value for the corresponding key. * @returns {void} - Register a key value pair for formula. */ Calculate.prototype.setKeyValue = function (key, value) { key = key.toUpperCase(); var str = value.toString().trim(); if (!this.storedData.get(key) || str.indexOf(this.leftBrace) === 0) { this.storedData.set(key, new FormulaInfo()); this.keyToRowsMap.set(key, this.keyToRowsMap.size + 1); this.rowsToKeyMap.set(this.rowsToKeyMap.size + 1, key); } var fInfo = this.storedData.get(key); if (fInfo.getFormulaText() != null && fInfo.getFormulaText().length > 0 && fInfo.getFormulaText() !== str) { var s1 = this.cellPrefix + this.keyToRowsMap.get(key).toString(); var formulaDependent = this.getDependentFormulaCells().get(s1); if (formulaDependent != null) { this.clearFormulaDependentCells(s1); } } if (str.length > 0 && str[0] === this.getFormulaCharacter()) { fInfo.setFormulaText(str); } else if (fInfo.getFormulaValue() !== str) { fInfo.setFormulaText(''); fInfo.setParsedFormula(''); fInfo.setFormulaValue(str); } }; /** * @hidden * @param {string} cell - specify the cell * @returns {void} - clears the Formula Dependent Cells. */ Calculate.prototype.clearFormulaDependentCells = function (cell) { var _this = this; var dependentFormula = this.getDependentFormulaCells().get(cell); if (dependentFormula) { dependentFormula.forEach(function (value, key) { var s = key; var dependent = _this.getDependentCells().get(s); _this.arrayRemove(dependent, cell); if (dependent.length === 0) { _this.getDependentCells().delete(s); } }); this.getDependentFormulaCells().delete(cell); } }; Calculate.prototype.arrayRemove = function (array, value) { var index = array.indexOf(value); if (index !== -1) { array.splice(index, 1); } return array; }; /** * Register a key value pair for formula. * * @param {string} key - Key for getting the corresponding value. * @returns {string | number} - to get key value. */ Calculate.prototype.getKeyValue = function (key) { key = key.toUpperCase(); if (this.storedData.has(key) !== null) { var fInfo = this.storedData.get(key); var fText = fInfo.getFormulaText(); if (fText.length > 0 && fText[0] === this.getFormulaCharacter()) { this.cell = this.cellPrefix + this.keyToRowsMap.get(key).toString(); fText = fText.substring(1); try { fInfo.setParsedFormula(this.parser.parseFormula(fText, key)); } catch (ex) { var args = { message: ex.message, exception: ex, isForceCalculable: false, computeForceCalculate: false }; this.trigger('onFailure', args); fInfo.setFormulaValue(args.message); return this.storedData.get(key).getFormulaValue(); } try { fInfo.setFormulaValue(this.computeFormula(fInfo.getParsedFormula())); } catch (ex) { var args = { message: ex.message, exception: ex, isForceCalculable: false, computeForceCalculate: false }; this.trigger('onFailure', args); var errorMessage = (typeof args.exception === 'string') ? args.exception : args.message; return (isNullOrUndefined(this.getErrorLine(ex)) ? '' : '#' + this.getErrorLine(ex) + ': ') + errorMessage; } } return this.storedData.get(key).getFormulaValue(); } else { return this.emptyString; } }; Calculate.prototype.getNamedRanges = function () { return this.namedRanges; }; /** * Adds a named range to the NamedRanges collection. * * @param {string} name - Name of the named range. * @param {string} range - Range for the specified name. * @returns {boolean} - Adds a named range to the NamedRanges collection. */ Calculate.prototype.addNamedRange = function (name, range) { var sheetScopeName = name.split(this.sheetToken); if (sheetScopeName.length > 1) { var sheetId = (this.getSheetId(this.grid)).toString(); var family = this.getSheetFamilyItem(sheetId); if (!family.parentObjectToToken.get(sheetId)) { return false; } name = sheetScopeName[0] + this.sheetToken + sheetScopeName[1].toUpperCase(); } else { name = name.toUpperCase(); } this.namedRanges.set(name, range); return true; }; /** * Update the sheet name changes in the named range collection. * * @hidden * @param {string} pName - Previous name of the sheet. * @param {string} name - Current name of the sheet. * @returns {void} - Update the sheet name changes in the named range collection. */ Calculate.prototype.updateNamedRange = function (pName, name) { var updatedRange = new Map(); this.namedRanges.forEach(function (value, key) { var updatedKey = key; if (key.includes(pName)) { var range = key.split('!'); range[0] = name; updatedKey = range.join('!'); } updatedRange.set(updatedKey, value); }); this.namedRanges = updatedRange; }; /** * Remove the specified named range form the named range collection. * * @param {string} name - Name of the specified named range. * @returns {boolean} - Remove the specified named range form the named range collection. */ Calculate.prototype.removeNamedRange = function (name) { name = name.toUpperCase(); if (this.namedRanges.get(name) != null) { this.namedRanges.delete(name); return true; } return false; }; /** * @hidden * @param {number} col - specify the column * @returns {string} - to convert the alpha. */ Calculate.prototype.convertAlpha = function (col) { var arrCol = []; var n = 0; var charText = 'A'; while (col > 0) { col--; var aCharValue = charText.charCodeAt(0); arrCol[n] = String.fromCharCode(col % 26 + aCharValue); col = parseInt((col / 26).toString(), 10); n++; } var arr = []; for (var i = 0; i < n; i++) { arr[n - i - 1] = arrCol[i]; } return arr.join(''); }; /** * @hidden * @param {string} cellRange - specify the cell range. * @returns {string} - to get cell collection. */ Calculate.prototype.getCellCollection = function (cellRange) { if (cellRange.indexOf(':') < 0) { if (!this.isCellReference(cellRange)) { return cellRange.split(this.getParseArgumentSeparator()); } else { cellRange = cellRange + ':' + cellRange; } } var token = this.emptyString; var sheetTokenIndex = cellRange.indexOf(this.sheetToken); if (sheetTokenIndex > -1) { var index = sheetTokenIndex; var s = index + 1; while (s < cellRange.length) { if (cellRange[s] === this.sheetToken) { token = cellRange.substr(0, s + 1); break; } s++; } } var i = cellRange.indexOf(':'); var row1; var row2; var col1; var col2; if (i > 0 && this.isChar(cellRange[i - 1])) { var k = i - 2; while (k >= 0 && this.isDigit(cellRange[k])) { k--; } } row1 = this.rowIndex(this.substring(cellRange, 0, i)); row2 = this.rowIndex(this.substring(cellRange, i + 1, i + cellRange.length - i - 1)); col1 = this.colIndex(this.substring(cellRange, 0, i)); col2 = this.colIndex(this.substring(cellRange, i + 1, i + cellRange.length - i - 1)); if (row1 >= maxRows || row2 >= maxRows || col1 >= maxCols || col2 >= maxCols) { return [this.getErrorStrings()[CommonErrors.Ref]]; } if (row1 > row2) { i = row2; row2 = row1; row1 = i; } if (col1 > col2) { i = col2; col2 = col1; col1 = i; } var cells = []; var j; var c = 0; for (i = row1; i <= row2; i++) { for (j = col1; j <= col2; j++) { cells[c] = token + this.emptyString + this.convertAlpha(j) + i.toString(); c++; } } return cells; }; /** * Compute the given formula. * * @param {string} formulaText - Specifies to compute the given formula. * @param {boolean} isFromComputeExpression - Specifies to confirm it was called from the ComputeExpression function. * @returns {string | number} - compute the given formula */ Calculate.prototype.computeFormula = function (formulaText, isFromComputeExpression) { return this.calculateFormula(formulaText, false, isFromComputeExpression); }; Calculate.prototype.calculateFormula = function (formulaText, refresh, isFromComputeExpression) { var _this = this; var parsedText; var lastIndexOfq; var formulatResult; var nestedFormula = false; var fNested; if (this.parser.isError) { return formulaText; } if (!this.parser.isFormulaParsed) { parsedText = this.parser.parseFormula(formulaText); } else { parsedText = formulaText; } this.parser.isFormulaParsed = false; try { lastIndexOfq = this.findLastIndexOfq(parsedText); if (lastIndexOfq > 0) { nestedFormula = true; } if (parsedText !== this.emptyString && lastIndexOfq > -1) { var i = lastIndexOfq + 1; var _loop_1 = function () { if (parsedText[i] !== this_1.rightBracket) { i++; return "continue"; } var sFormula = parsedText.substring(lastIndexOfq, i + 1); var libFormula = sFormula.split(this_1.leftBracket)[0].split('q').join(this_1.emptyString); var args = void 0; if (this_1.getLibraryFormulas().get(libFormula.toUpperCase()).isCustom) { args = sFormula.substring(sFormula.indexOf(this_1.leftBracket) + 1, sFormula.indexOf(this_1.rightBracket)) .split(this_1.getParseArgumentSeparator()); var j = 0; var customArgs = []; var cellCol = void 0; for (j = 0; j < args.length; j++) { if (args[j].includes(':') && this_1.isCellReference(args[j])) { cellCol = this_1.getCellCollection(args[j]); if (cellCol.length > 1) { customArgs.push(args[j]); cellCol.forEach(function (cell) { _this.updateDependentCell(cell); }); } else { customArgs.push(this_1.getValueFromArg(args[j])); } } else { customArgs.push(this_1.getValueFromArg(args[j])); } } args = customArgs; } else { var argStr = sFormula.substring(sFormula.indexOf(this_1.leftBracket) + 1, sFormula.indexOf(this_1.rightBracket)); args = []; var separator = this_1.getParseArgumentSeparator(); var parameter = ''; var isInString = void 0; for (var idx = 0, len = argStr.length - 1; idx <= len; idx++) { if (argStr[idx] === '"') { isInString = !isInString; } if (argStr[idx] === separator && !isInString) { args.push(parameter); parameter = ''; if (idx === len) { args.push(parameter); } } else { parameter += argStr[idx]; if (idx === len) { args.push(parameter); } } } if (!args.length) { args = ['']; } if (nestedFormula && libFormula) { var formulas = ['IF', 'INDEX', 'SORT', 'T', 'EXACT', 'PROPER', 'DOLLAR', 'DATE', 'TEXT']; if (formulas.some(function (formula) { return formula === libFormula; })) { args.push('nestedFormulaTrue'); } if (libFormula === 'IF') { args.push('nestedFormulaTrue'); } } if (isFromComputeExpression && libFormula === 'UNIQUE') { args.push('isComputeExp'); } } formulatResult = isNullOrUndefined(this_1.getFunction(libFormula)) ? this_1.getErrorStrings()[CommonErrors.Name] : this_1.getFunction(libFormula).apply(void 0, args); if (nestedFormula) { fNested = this_1.processNestedFormula(parsedText, sFormula, formulatResult); var q = this_1.findLastIndexOfq(fNested); if (q === 0) { nestedFormula = false; } if (q === -1) { formulatResult = this_1.computeValue(fNested, refresh); } lastIndexOfq = i = q; parsedText = fNested; return "continue"; } return "break"; }; var this_1 = this; while (i > -1) { var state_1 = _loop_1(); if (state_1 === "break") break; } } else if (this.formulaErrorStrings.indexOf(parsedText) > -1) { formulatResult = parsedText; } else if (parsedText !== this.emptyString && lastIndexOfq === -1) { formulatResult = this.computeValue(parsedText, refresh); } } catch (ex) { var args = { message: ex.message, exception: ex, isForceCalculable: false, computeForceCalculate: false }; this.trigger('onFailure', args); var errorMessage = (typeof args.exception === 'string') ? args.exception : args.message; formulatResult = (isNullOrUndefined(this.getErrorLine(ex)) ? '' : '#' + this.getErrorLine(ex) + ': ') + errorMessage; } return formulatResult; }; /** * @hidden * @param {string[]} range - Specify the range. * @param {string} isAvgIf - Specify the AVERAGEIF computation. * @returns {number[] | string} - To compute the sum if and average if. */ Calculate.prototype.computeSumIfAndAvgIf = function (range, isAvgIf) { if (isNullOrUndefined(range) || range[0] === this.emptyString || range.length === 0) { return this.formulaErrorStrings[FormulasErrorsStrings.WrongNumberArguments]; } var argArr = range; for (var i = 0; i < argArr.length; i++) { if (this.isCellReference(argArr[i]) && isNullOrUndefined(argArr[i].match(/[0-9]/)) && argArr[i].indexOf('!') < 0) { var splitArray = argArr[i].split(':'); argArr[i] = splitArray[0] + '1' + ':' + splitArray[1] + this.spreadSheetUsedRange[0]; } } var argCount = argArr.length; if (argCount !== 2 && argCount !== 3 && argCount === 0) { return this.formulaErrorStrings[FormulasErrorsStrings.WrongNumberArguments]; } if (argArr[1] === '') { return isAvgIf ? this.getErrorStrings()[CommonErrors.DivZero] : '0'; } var rangevalue = argArr[0]; var isStringVal = argArr[1].startsWith(this.tic) && argArr[1].endsWith(this.tic); var criteria = this.getANDComputedValue(argArr[1]); if (criteria.length > 255) { return this.getErrorStrings()[CommonErrors.Value]; } var isAsterisk = criteria.includes('*'); var isQuestionMark = criteria.includes('?'); var criteriaValue = isAsterisk ? criteria.replace(/\*/g, '').trim() : criteria; var isCellReferenceValue = false; if (!isStringVal && this.isCellReference(criteriaValue)) { criteriaValue = this.getValueFromArg(criteriaValue); isCellReferenceValue = true; } if (isAsterisk) { var asteriskIndex = criteria.indexOf('*'); if (criteria[0] === '*') { criteriaValue = '*' + criteriaValue; } if (criteria[criteria.length - 1] === '*') { criteriaValue += '*'; } if (asteriskIndex > 0 && asteriskIndex < criteria.length - 1) { criteriaValue = criteria.substring(0, asteriskIndex) + '*' + criteria.substring(asteriskIndex + 1); } } criteria = criteriaValue; var opt = this.parser.tokenEqual; if (criteria.startsWith('<=')) { opt = this.parser.tokenLessEq; criteria = criteria.substring(2); } else if (criteria.startsWith('>=')) { opt = this.parser.tokenGreaterEq; criteria = criteria.substring(2); } else if (criteria.startsWith('<>')) { opt = this.parser.tokenNotEqual; criteria = criteria.substring(2); } else if (criteria.startsWith('<')) { opt = this.parser.tokenLess; criteria = criteria.substring(1); } else if (criteria.startsWith('>')) { opt = this.parser.tokenGreater; criteria = criteria.substring(1); } else if (criteria.startsWith('=')) { opt = this.parser.tokenEqual; criteria = criteria.substring(1); } if ((!isStringVal && this.isCellReference(criteria) && !isCellReferenceValue) || criteria.includes(this.arithMarker) || (criteria.includes(this.getParseDecimalSeparator()) && !isAsterisk && !isQuestionMark)) { criteria = this.getValueFromArg(criteria); } var checkCriteria = this.parseFloat(criteria); var criteriaRangeArray = argArr[0]; var sumRange = this.getCellCollection(argCount > 2 ? argArr[2] : rangevalue); var criteriaRange = this.getCellCollection(criteriaRangeArray); if (sumRange[0] === '#REF!' || criteriaRange[0] === '#REF!') { return this.getErrorStrings()[CommonErrors.Name]; } if (criteriaRange.length > sumRange.length) { var sumEndCol = this.colIndex(sumRange[sumRange.length - 1]) + this.colIndex(criteriaRange[criteriaRange.length - 1]) - this.colIndex(criteriaRange[0]); var sumrange = argArr[2].split(':'); sumrange[1] = (this.convertAlpha(sumEndCol) + this.rowIndex(criteriaRange[criteriaRange.length - 1])).toString(); sumRange = this.getCellCollection(sumrange.join(':')); if (sumRange[0] === '#REF!') { return this.getErrorStrings()[CommonErrors.Name]; } } var result = this.getComputeSumIfValue(criteriaRange, sumRange, criteria.toLowerCase(), checkCriteria, opt, isAsterisk, isQuestionMark); return [result[0], result[1]]; }; /** * @hidden * @param {string[]} range - specify the range * @returns {string} - to compute lookup */ Calculate.prototype.computeLookup = function (range) { var _a, _b; var lookupArray; var matchArray; var lookupRange = []; var matchupRange = []; var checkCriteria = []; var findMaxVal = []; var lookupValue; var isArrayVector; var result = []; var argArr = range; var argCount = argArr.length; if (argCount === 1 || argCount > 3) { return this.formulaErrorStrings[FormulasErrorsStrings.WrongNumberArguments]; } if (argArr[1] === '' || argArr[2] === '') { return this.getErrorStrings()[CommonErrors.Value]; } lookupValue = this.getValueFromArg(argArr[0]); if (argArr[0].indexOf(this.tic) > -1 && argArr[0].toUpperCase().split(this.tic).join('') !== this.trueValue && argArr[0].toUpperCase().split(this.tic).join('') !== this.falseValue && this.isNaN(this.parseFloat(argArr[0].split(this.tic).join('')))) { lookupValue = lookupValue.split(this.tic).join(''); } if (this.getErrorStrings().indexOf(lookupValue) > -1) { return lookupValue; } var rangeSplit = argArr[1].split(':'); if (rangeSplit.length === 2 && this.isCellReference(rangeSplit[0]) && this.isCellReference(rangeSplit[1]) && argCount === 2) { var index = argArr[1].indexOf(':'); var rowIdx = this.rowIndex(this.substring(argArr[1], 0, index)); var colIdx = this.colIndex(this.substring(argArr[1], 0, index)); var endRowIdx = this.rowIndex(this.substring(argArr[1], index + 1, index + argArr[1].length - index - 1)); var endColIdx = this.colIndex(this.substring(argArr[1], index + 1, index + argArr[1].length - index - 1)); if (rowIdx > endRowIdx) { _a = [endRowIdx, rowIdx], rowIdx = _a[0], endRowIdx = _a[1]; } if (colIdx > endColIdx) { _b = [endColIdx, colIdx], colIdx = _b[0], endColIdx = _b[1]; } var sheetIdx = ''; if (argArr[1].indexOf('!') === 0) { sheetIdx = argArr[1]; sheetIdx = sheetIdx.replace('!', ''); sheetIdx = sheetIdx.indexOf('!'); sheetIdx = argArr[1].substring(0, sheetIdx + 2); } var colCount = endColIdx - colIdx + 1; var rowCount = endRowIdx - rowIdx + 1; if (rowCount > colCount || rowCount === colCount) { // Taller than Wide. vlookup lookupArray = sheetIdx + getAlphalabel(colIdx) + rowIdx + ':' + getAlphalabel(colIdx) + endRowIdx; matchArray = sheetIdx + getAlphalabel(endColIdx) + rowIdx + ':' + getAlphalabel(endColIdx) + endRowIdx; } else if (rowCount < colCount) { // Wider than Tall. hlookup lookupArray = sheetIdx + getAlphalabel(colIdx) + rowIdx + ':' + getAlphalabel(endColIdx) + rowIdx; matchArray = sheetIdx + getAlphalabel(colIdx) + endRowIdx + ':' + getAlphalabel(endColIdx) + endRowIdx; } if (rowIdx !== endRowIdx || colIdx !== endColIdx) { isArrayVector = true; } } if (isArrayVector) { lookupRange = this.getCellCollection(lookupArray); matchupRange = this.getCellCollection(matchArray); if (lookupRange[0] === '#REF!' || matchupRange[0] === '#REF!') { return this.getErrorStrings()[CommonErrors.Name]; } } else { lookupRange = this.getCellCollection(argArr[1]); var arrvalue = argCount === 2 ? argArr[1] : argArr[2]; matchupRange = this.getCellCollection(arrvalue); if (lookupRange[0] === '#REF!' || matchupRange[0] === '#REF!') { return this.getErrorStrings()[CommonErrors.Name]; } var lookupIndex = getRangeIndexes(argArr[1]); var matchIndex = getRangeIndexes(arrvalue); var isValidLookup = lookupIndex[1] === lookupIndex[3] ? true : lookupIndex[0] === lookupIndex[2]; var isValidMatch = matchIndex[1] === matchIndex[3] ? true : matchIndex[0] === matchIndex[2]; if (!isValidLookup || !isValidMatch) { return this.getErrorStrings()[CommonErrors.NA]; } } for (var i = 0; i < lookupRange.length; i++) { findMaxVal.push(this.getValueFromArg(lookupRange[i]).split(this.tic).join('')); } var num = findMaxVal.map(function (value) { return value === '' ? NaN : Number(value); }).sort(function (a, b) { return a - b; }); var maxVal = num[num.length - 1]; var minVal = num[0]; var lookupVal = this.parseFloat(lookupValue); if (!this.isNaN(lookupVal)) { for (var a = 0; a < num.length; a++) { checkCriteria[a] = num[a].toString().split(this.tic).join(''); if (!isNullOrUndefined(matchupRange[a]) && checkCriteria[a] !== '' && lookupVal === this.parseFloat(checkCriteria[a])) { result.push(this.getValueFromArg(matchupRange[a]).split(this.tic).join('') || '0'); } } } else { for (var j = 0; j < lookupRange.length; j++) { checkCriteria[j] = this.getValueFromArg(lookupRange[j]).split(this.tic).join(''); if (!isNullOrUndefined(matchupRange[j]) && lookupValue !== '' && checkCriteria[j] !== '') { if (lookupValue.toUpperCase() === checkCriteria[j].toUpperCase()) { result.push(this.getValueFromArg(matchupRange[j]).split(this.tic).join('') || '0'); } else if (lookupValue.indexOf('*') > -1 || lookupValue.indexOf('?') > -1) { var criteriaValue = lookupValue; if (lookupValue.indexOf('*') > -1) { criteriaValue = criteriaValue.replace(/\*/g, '').trim(); if (this.isCellReference(criteriaValue)) { criteriaValue = this.getValueFromArg(criteriaValue); } var asteriskIndex = lookupValue.indexOf('*'); if (lookupValue[0] === '*') { criteriaValue = '*' + criteriaValue; } if (lookupValue[lookupValue.length - 1] === '*') { criteriaValue += '*'; } if (asteriskIndex > 0 && asteriskIndex < lookupValue.length - 1) {