@syncfusion/ej2-spreadsheet
Version:
Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel
1,241 lines (1,240 loc) • 48.9 kB
JavaScript
import { getCell, setCell, getSheetIndex, getCellIndexes, getRow, getColumn } from './../index';
import { getCellAddress, getRangeIndexes, beforeCellUpdate, workbookEditOperation, getRangeAddress, getSwapRange, workbookReadonlyAlert } from './index';
import { getColumnHeaderText, clearFormulaDependentCells } from './index';
import { isHiddenCol, isHiddenRow, checkDateFormat, checkNumberFormat } from './../index';
import { isUndefined, getNumberDependable, getNumericObject, Internationalization, defaultCurrencyCode, isNullOrUndefined } from '@syncfusion/ej2-base';
import { parseThousandSeparator } from './internalization';
import { isNumber, getFormattedCellObject } from './../index';
import { DataManager, Query, Predicate } from '@syncfusion/ej2-data';
/**
* Check whether the text is formula or not.
*
* @param {string} text - Specify the text.
* @param {boolean} isEditing - Specify the isEditing.
* @returns {boolean} - Check whether the text is formula or not.
*/
export function checkIsFormula(text, isEditing) {
return text && text[0] === '=' && (text.length > 1 || isEditing);
}
/**
* Check whether the value is cell reference or not.
*
* @param {string} value - Specify the value to check.
* @returns {boolean} - Returns boolean value
*/
export function isCellReference(value) {
var range = value;
range = range.split('$').join('');
if (range.indexOf(':') > -1) {
var rangeSplit = range.split(':');
if (isValidCellReference(rangeSplit[0]) && isValidCellReference(rangeSplit[1])) {
return true;
}
}
else if (range.indexOf(':') < 0) {
if (isValidCellReference(range)) {
return true;
}
}
return false;
}
/**
* Check whether the value is character or not.
*
* @param {string} value - Specify the value to check.
* @returns {boolean} - Returns boolean value
*/
export function isChar(value) {
if ((value.charCodeAt(0) >= 65 && value.charCodeAt(0) <= 90) || (value.charCodeAt(0) >= 97 && value.charCodeAt(0) <= 122)) {
return true;
}
return false;
}
/**
* Check whether the range selection is on complete row.
*
* @param {SheetModel} sheet - Specify the sheet.
* @param {number[]} range - Specify the range index.
* @returns {boolean} - Returns boolean value
* @hidden
*/
export function isRowSelected(sheet, range) {
return range[1] === 0 && range[3] === sheet.colCount - 1;
}
/**
* Check whether the range selection is on complete column.
*
* @param {SheetModel} sheet - Specify the sheet.
* @param {number[]} range - Specify the range index.
* @returns {boolean} - Returns boolean value
* @hidden
*/
export function isColumnSelected(sheet, range) {
return range[0] === 0 && range[2] === sheet.rowCount - 1;
}
/**
* @param {number[]} range - Specify the range
* @param {number} rowIdx - Specify the row index
* @param {number} colIdx - Specify the col index
* @returns {boolean} - Returns boolean value
*/
export function inRange(range, rowIdx, colIdx) {
return range && (rowIdx >= range[0] && rowIdx <= range[2] && colIdx >= range[1] && colIdx <= range[3]);
}
/**
* @param {number[]} address - Specify the address
* @param {number} rowIdx - Specify the row index
* @param {number} colIdx - Specify the col index
* @returns {boolean} - Returns boolean value
*/
export function isInMultipleRange(address, rowIdx, colIdx) {
var range;
var isInRange;
var splitedAddress = address.split(' ');
for (var i = 0, len = splitedAddress.length; i < len; i++) {
range = getRangeIndexes(splitedAddress[i]);
isInRange = inRange(range, rowIdx, colIdx);
if (isInRange) {
break;
}
}
return isInRange;
}
/** @hidden
* @param {number[]} range - Specify the range
* @param {number[]} testRange - Specify the test range
* @param {boolean} isModify - Specify the boolean value
* @returns {boolean} - Returns boolean value
*/
export function isInRange(range, testRange, isModify) {
var inRange = range[0] <= testRange[0] && range[2] >= testRange[2] && range[1] <= testRange[1] && range[3] >= testRange[3];
if (inRange) {
return true;
}
if (isModify) {
if (testRange[0] < range[0] && testRange[2] < range[0] || testRange[0] > range[2] && testRange[2] > range[2]) {
return false;
}
else {
if (testRange[0] < range[0] && testRange[2] > range[0]) {
testRange[0] = range[0];
inRange = true;
}
if (testRange[2] > range[2]) {
testRange[2] = range[2];
inRange = true;
}
}
if (testRange[1] < range[1] && testRange[3] < range[1] || testRange[1] > range[3] && testRange[3] > range[3]) {
return false;
}
else {
if (testRange[1] < range[1] && testRange[3] > range[1]) {
testRange[1] = range[1];
inRange = true;
}
if (testRange[3] > range[3]) {
testRange[3] = range[3];
inRange = true;
}
}
}
return inRange;
}
/**
* @hidden
* @param {string} address - Specifies the address for whole column.
* @param {number[]} testRange - Specifies range used to split the address.
* @param {number} colIdx - Specifies the column index.
* @returns {string} - returns the modified address.
*/
export function getSplittedAddressForColumn(address, testRange, colIdx) {
var colName = getColumnHeaderText(colIdx + 1);
if (address) {
address.split(' ').forEach(function (addrs) {
var range = getRangeIndexes(addrs);
if (isInRange(range, testRange)) {
address = address.split(addrs).join(colName + (range[0] + 1) +
':' + colName + testRange[0] + ' ' + colName + (testRange[2] + 2) +
':' + colName + (range[2] + 1));
}
else if (isInRange(range, testRange, true)) {
var modifiedAddress = void 0;
if (testRange[0] > range[0]) {
modifiedAddress = colName + (range[0] + 1) + ':' + colName + testRange[0];
}
else {
modifiedAddress = colName + (testRange[2] + 2) + ':' + colName + (range[2] + 1);
}
address = address.split(addrs).join(modifiedAddress);
}
});
}
else {
address = colName + '1:' + colName + testRange[0] + ' ' + colName + (testRange[2] + 2) + ':' + colName + '1048576';
}
return address;
}
/**
* Check whether the cell is locked or not
*
* @param {CellModel} cell - Specify the cell.
* @param {ColumnModel} column - Specify the column.
* @returns {boolean} - Returns boolean value
* @hidden
*/
export function isLocked(cell, column) {
if (!cell) {
cell = {};
}
if (cell.isLocked) {
return true;
}
else if (cell.isLocked === false) {
return false;
}
else if (column && column.isLocked) {
return true;
}
else if (!cell.isLocked && (column && column.isLocked !== false)) {
return true;
}
return false;
}
/**
* Check whether the value is cell reference or not.
*
* @param {string} value - Specify the value to check.
* @returns {boolean} - Returns boolean value
* @hidden
*/
export function isValidCellReference(value) {
var text = value;
var endNum = 0;
var textLength = text.length;
for (var i = 0; i < textLength; i++) {
if (isChar(text[i])) {
endNum++;
}
}
var cellText = text.substring(0, endNum);
var cellTextLength = cellText.length;
if (cellTextLength !== textLength) {
if (cellTextLength < 4) {
if (textLength !== 1 && (isNaN(parseInt(text, 10)))) {
var cellColIndex = columnIndex(cellText);
// XFD is the last column, 16384 - Maximum number of columns in excel.
if (cellColIndex < 1 || cellColIndex > 16384) {
return false;
}
var rowText = text.substring(endNum, textLength);
if (rowText.length > 0) {
var isNumeric = true;
for (var j = 0; j < rowText.length; j++) {
var charCode = rowText.charCodeAt(j);
if (charCode < 48 || charCode > 57) {
isNumeric = false;
break;
}
}
if (isNumeric) {
var cellNumber = parseFloat(rowText);
if (cellNumber > 0 && cellNumber < 1048577) { // 1048576 - Maximum number of rows in excel.
return true;
}
}
}
}
}
}
return false;
}
/**
* 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.
* @hidden
*/
export function columnIndex(cell) {
var j = 0;
var k = 0;
cell = cell.toUpperCase();
if (j < cell.length && cell[j] === '!') {
j++;
while (j < cell.length && cell[j] !== '!') {
j++;
}
j++;
}
while (j < cell.length && isChar(cell[j])) {
var charCode = cell[j].charCodeAt(0);
k = k * 26 + charCode - 64;
j++;
}
if (k === 0) {
return -1;
}
return k;
}
/**
* @hidden
* @param {SheetModel} sheet - Specify the sheet
* @param {number} index - specify the index
* @param {boolean} increase - specify the boolean value.
* @param {string} layout - specify the string
* @param {number} count - specify the count.
* @returns {number} - To skip the hidden index
*
*/
export function skipHiddenIdx(sheet, index, increase, layout, count) {
if (layout === void 0) { layout = 'rows'; }
var rowColObj;
if (sheet) {
if (increase) {
for (var i = index; i < Infinity; i++) {
rowColObj = sheet["" + layout];
if (rowColObj[index] && rowColObj[index].hidden) {
index++;
}
else {
if (count) {
count--;
index++;
}
else {
break;
}
}
}
}
else {
for (var i = index; i > -1; i--) {
rowColObj = sheet["" + layout];
if (rowColObj[index] && rowColObj[index].hidden) {
index--;
}
else {
break;
}
}
}
}
return index;
}
/**
* @param {CellStyleModel} style - Cell style.
* @param {boolean} onActionUpdate - Specifies the action.
* @returns {boolean} - retruns `true` is height needs to be checked.
* @hidden
*/
export function isHeightCheckNeeded(style, onActionUpdate) {
var keys = Object.keys(style);
return (onActionUpdate ? keys.indexOf('fontSize') > -1 : keys.indexOf('fontSize') > -1
&& Number(style.fontSize.split('pt')[0]) > 12) || keys.indexOf('fontFamily') > -1 || keys.indexOf('borderTop') > -1
|| keys.indexOf('borderBottom') > -1;
}
/**
* Fixes row index overflow by wrapping it within the valid Excel row index range (0 to 1048575).
*
* @param {number} index - The row index to adjust.
* @returns {number} - The adjusted row index.
* @hidden
*/
function fixRowIndexOverflow(index) {
if (index > 1048575) {
return index - 1048576;
}
else if (index < 0) {
return index + 1048576;
}
return index;
}
/**
* Fixes column index overflow by wrapping it within the valid Excel column index range (0 to 16383).
*
* @param {number} index - The column index to adjust.
* @returns {number} - The adjusted column index.
* @hidden
*/
function fixColIndexOverflow(index) {
if (index > 16383) {
return index - 16384;
}
else if (index < 0) {
return index + 16384;
}
return index;
}
/**
* @param {number[]} currIndexes - current indexes in which formula get updated
* @param {number[]} prevIndexes - copied indexes
* @param {SheetModel} sheet - sheet model
* @param {Workbook} context - Represents workbook instance
* @param {CellModel} prevCell - Copied or previous cell model
* @param {boolean} isSort - Represents sort action
* @param {boolean} isValidation - Represents validation action
* @returns {string} - retruns updated formula
* @hidden
*/
export function getUpdatedFormula(currIndexes, prevIndexes, sheet, context, prevCell, isSort, isValidation) {
var cIdxValue;
var cell;
if (prevIndexes) {
cell = prevCell || getCell(prevIndexes[0], prevIndexes[1], sheet, false, true);
cIdxValue = cell.formula || '';
}
if (cIdxValue) {
if (isSort) {
context.notify(clearFormulaDependentCells, { cellRef: getCellAddress(prevIndexes[0], prevIndexes[1]) });
}
if (cIdxValue.indexOf('=') === 0) {
cIdxValue = cIdxValue.slice(1);
}
cIdxValue = cIdxValue.split('(').join(context.listSeparator).split(')').join(context.listSeparator);
var formulaOperators = ['+', '-', '*', '/', '>=', '<=', '<>', '>', '<', '=', '%', '&', '^'];
var splitArray = void 0;
var value = cIdxValue;
for (var i = 0; i < formulaOperators.length; i++) {
splitArray = value.split(formulaOperators[i]);
value = splitArray.join(context.listSeparator);
}
splitArray = value.split(context.listSeparator);
var newAddress = [];
var newRef = void 0;
var refObj = void 0;
var isSheetRef = void 0;
var cellRefSheet = void 0;
var cellRef = void 0;
for (var j = 0; j < splitArray.length; j++) {
isSheetRef = splitArray[j].includes('!');
if (isSheetRef) {
var lastIndex = splitArray[j].lastIndexOf('!');
cellRefSheet = splitArray[j].substring(0, lastIndex);
cellRef = splitArray[j].substring(lastIndex + 1).toUpperCase();
}
else {
cellRef = splitArray[j].toUpperCase();
}
if (isCellReference(cellRef.trim()) && !cellRef.includes('$') && (!isSort || !isSheetRef)) {
var leadingSpaces = getLeadingSpaces(cellRef);
var trailingSpaces = getTrailingSpaces(cellRef);
var range = getRangeIndexes(cellRef);
var newRange = void 0;
if (isValidation) {
newRange = [fixRowIndexOverflow(currIndexes[0] - (prevIndexes[0] - range[0])),
fixColIndexOverflow(currIndexes[1] - (prevIndexes[1] - range[1])),
fixRowIndexOverflow(currIndexes[0] - (prevIndexes[0] - range[2])),
fixColIndexOverflow(currIndexes[1] - (prevIndexes[1] - range[3]))];
}
else {
newRange = [currIndexes[0] - (prevIndexes[0] - range[0]), currIndexes[1] - (prevIndexes[1] - range[1]),
currIndexes[0] - (prevIndexes[0] - range[2]), currIndexes[1] - (prevIndexes[1] - range[3])];
}
if (newRange[1] < 0 || newRange[2] < 0 || newRange[3] < 0 || (!isSort && newRange[0] < 0)) {
newRef = '#REF!';
}
else {
if (isSort && newRange[0] < 0) {
newRange[0] = newRange[2];
}
newRef = getCellAddress(newRange[0], newRange[1]);
if (cellRef.includes(':')) {
newRef += (':' + getCellAddress(newRange[2], newRange[3]));
}
newRef = isCellReference(newRef) ? newRef : '#REF!';
}
refObj = {};
if (isSheetRef) {
newRef = cellRefSheet + "!" + newRef;
}
refObj[splitArray[j]] = "" + leadingSpaces + newRef + trailingSpaces;
if (splitArray[j].includes(':')) {
newAddress.splice(0, 0, refObj);
}
else {
newAddress.push(refObj);
}
}
}
var objKey = void 0;
var objValue = void 0;
cIdxValue = cell.formula;
var newCIdxValue = cIdxValue;
for (var j = 0; j < newAddress.length; j++) {
objKey = Object.keys(newAddress[j])[0];
objValue = newAddress[j]["" + objKey];
var objKeyLen = objKey.length;
var positionIdx = newCIdxValue.indexOf(objKey);
var emptyString = '';
for (var idx = 0; idx < objValue.length; idx++) {
emptyString += ' ';
}
cIdxValue = cIdxValue.slice(0, positionIdx) + objValue + cIdxValue.slice(positionIdx + objKeyLen);
newCIdxValue = newCIdxValue.slice(0, positionIdx) + emptyString + newCIdxValue.slice(positionIdx + objKeyLen);
}
return cIdxValue;
}
else {
return null;
}
}
/**
* Retrieves the leading spaces from a given string.
*
* @param {string} string - The input string from which to retrieve leading spaces.
* @returns {string} - A string containing all leading spaces from the input string.
* @hidden
*/
export function getLeadingSpaces(string) {
var leadingSpaces = '';
for (var i = 0; i < string.length; i++) {
if (string[i] === ' ') {
leadingSpaces += ' ';
}
else {
break;
}
}
return leadingSpaces;
}
/**
* Retrieves the trailing spaces from a given string.
*
* @param {string} string - The input string from which to retrieve trailing spaces.
* @returns {string} - A string containing all trailing spaces from the input string.
* @hidden
*/
export function getTrailingSpaces(string) {
var trailingSpaces = '';
for (var i = string.length - 1; i >= 0; i--) {
if (string[i] === ' ') {
trailingSpaces = ' ' + trailingSpaces;
}
else {
break;
}
}
return trailingSpaces;
}
/**
* @param {Workbook} context - Specifies the context.
* @param {SheetModel} sheet - Specifies the sheet.
* @param {CellUpdateArgs} prop - Specifies the props.
* @param {BeforeActionData} actionData - It holds the undoRedoCollection cell details.
* @param {boolean} isUndo - It holds the undo information.
* @returns {boolean} - returns args cancel value.
* @hidden */
export function updateCell(context, sheet, prop, actionData, isUndo) {
var args = { cell: prop.cell, rowIndex: prop.rowIdx, colIndex: prop.colIdx, cancel: false, sheet: sheet.name };
if (!prop.preventEvt) { // Prevent event triggering for public method cell update.
context.trigger(beforeCellUpdate, args);
}
if (!prop.eventOnly && !args.cancel) { // `eventOnly` - To trigger event and return without cell model update.
if (prop.valChange) {
var prevCell = getCell(args.rowIndex, args.colIndex, sheet);
var prevCellVal = !prop.preventEvt && context.getDisplayText(prevCell);
var isFormulaCell = !!(prevCell && prevCell.formula);
setCell(args.rowIndex, args.colIndex, sheet, args.cell, !prop.pvtExtend);
var cell = getCell(args.rowIndex, args.colIndex, sheet, false, true);
if (prop.mergedCells) {
delete cell.value;
delete cell.formula;
}
if (cell.formattedText) {
delete cell.formattedText;
}
if (args.cell && args.cell.style) {
cleanEmptyBorderProperties(cell.style);
}
var evtArgs = {
action: 'updateCellValue',
address: [args.rowIndex, args.colIndex], sheetIndex: getSheetIndex(context, sheet.name), value: isFormulaCell && !cell.formula ? (cell.value || (cell.value === 0 ? '0' : '')) :
(cell.formula || cell.value || (cell.value === 0 ? '0' : '')),
skipFormatCheck: prop.skipFormatCheck, isRandomFormula: prop.isRandomFormula,
isDelete: prop.isDelete, deletedRange: prop.deletedRange, fillType: prop.fillType,
cellInformation: actionData, isRedo: !isUndo, actionName: prop.fillType,
isPaste: prop.requestType === 'paste'
};
context.notify(workbookEditOperation, evtArgs);
prop.isFormulaDependent = evtArgs.isFormulaDependent;
if (prop.requestType && args.cell === null) {
setCell(args.rowIndex, args.colIndex, sheet, args.cell, !prop.pvtExtend);
}
if (prop.cellDelete) {
delete cell.value;
delete cell.formula;
delete cell.hyperlink;
}
if (prop.uiRefresh) {
context.serviceLocator.getService('cell').refresh(args.rowIndex, args.colIndex, prop.lastCell, prop.td, prop.checkCF, prop.checkWrap, prop.skipFormatCheck, prop.isRandomFormula, prop.fillType);
}
if (!prop.preventEvt) {
var cellDisplayText = context.getDisplayText(cell);
if (cellDisplayText !== prevCellVal) {
var cellValue = getCell(args.rowIndex, args.colIndex, sheet, false, true).value;
cellValue = cellValue || (cellValue === 0 ? '0' : '');
var evtArgs_1 = { value: cellValue, oldValue: prevCellVal, formula: cell.formula || '',
address: sheet.name + "!" + getCellAddress(args.rowIndex, args.colIndex), displayText: cellDisplayText };
if (prop.requestType) {
evtArgs_1.requestType = prop.requestType;
}
context.trigger('cellSave', evtArgs_1);
}
}
}
else {
setCell(args.rowIndex, args.colIndex, sheet, args.cell, !prop.pvtExtend);
if (args.cell && args.cell.style) {
var cell = getCell(args.rowIndex, args.colIndex, sheet, false, true);
cleanEmptyBorderProperties(cell.style);
}
}
}
return args.cancel;
}
/**
* Removes empty border properties from cell style
*
* @param {CellStyleModel} style - The cell style object to clean
* @returns {void}
* @hidden
*/
function cleanEmptyBorderProperties(style) {
if (style.border === '') {
delete style.border;
delete style.borderTop;
delete style.borderBottom;
delete style.borderLeft;
delete style.borderRight;
}
else {
if (style.borderTop === '') {
delete style.borderTop;
}
if (style.borderBottom === '') {
delete style.borderBottom;
}
if (style.borderLeft === '') {
delete style.borderLeft;
}
if (style.borderRight === '') {
delete style.borderRight;
}
}
}
/**
* @param {number} rowIdx - row index
* @param {number} colIdx - column index
* @param {SheetModel} sheet - sheet model
* @returns {number[]} - retruns data range
* @hidden
*/
export function getDataRange(rowIdx, colIdx, sheet) {
var sRowIdx = rowIdx;
var eRowIdx = rowIdx;
var sColIdx = colIdx;
var eColIdx = colIdx;
var usedRowIdx = sheet.usedRange.rowIndex;
var usedColIdx = sheet.usedRange.colIndex;
var isEmptyRow = function (idx) {
for (var i = 0; i <= usedColIdx; i++) {
if (!isUndefined(getCell(idx, i, sheet, null, true).value)) {
return false;
}
}
return true;
};
var isEmptyColumn = function (idx) {
for (var i = sRowIdx; i <= eRowIdx; i++) {
if (!isUndefined(getCell(i, idx, sheet, null, true).value)) {
return false;
}
}
return true;
};
for (var i = sRowIdx; i <= usedRowIdx; i++) { // To find end row index
if (isUndefined(getCell(i, colIdx, sheet, null, true).value) && isEmptyRow(i)) {
break;
}
else {
eRowIdx = i;
}
}
for (var i = sRowIdx; i >= 0; i--) { // To find start row index
if (isUndefined(getCell(i, colIdx, sheet, null, true).value) && isEmptyRow(i)) {
break;
}
else {
sRowIdx = i;
}
}
for (var i = sColIdx; i <= usedColIdx; i++) { // To find end column index
if (isUndefined(getCell(rowIdx, i, sheet, null, true).value) && isEmptyColumn(i)) {
break;
}
else {
eColIdx = i;
}
}
for (var i = sColIdx; i >= 0; i--) { // To find start column index
if (isUndefined(getCell(rowIdx, i, sheet, null, true).value) && isEmptyColumn(i)) {
break;
}
else {
sColIdx = i;
}
}
return [sRowIdx, sColIdx, eRowIdx, eColIdx];
}
/**
* @param {InsertDeleteModelArgs} args - row index
* @param {number[]} formatRange - format range index
* @param {boolean} isAction - specifies isAction.
* @returns {number[]} - retruns updated range
* @hidden
*/
export function insertFormatRange(args, formatRange, isAction) {
var sltRangeIndex = getRangeIndexes(args.model.selectedRange);
var insertStartIndex = 0;
var insertEndIndex = 0;
if (args.modelType === 'Column') {
if (isAction || (args.insertType === 'before' && args.isUndoRedo)) {
sltRangeIndex = [0, args.start, 0, args.end];
}
if (args.insertType === 'before') {
if ((formatRange[1] <= sltRangeIndex[1] && formatRange[3] >= sltRangeIndex[1])) {
insertStartIndex = 0;
insertEndIndex = (sltRangeIndex[3] - sltRangeIndex[1]) + 1;
}
else if (sltRangeIndex[1] < formatRange[1]) {
insertStartIndex = insertEndIndex = (sltRangeIndex[3] - sltRangeIndex[1]) + 1;
}
}
else {
if (args.isUndoRedo) {
var diffValue = (args.end - args.start) + 1;
sltRangeIndex = [0, args.start - diffValue, 0, args.end - diffValue];
}
if ((formatRange[1] <= sltRangeIndex[3] && formatRange[3] >= sltRangeIndex[3])) {
insertStartIndex = 0;
insertEndIndex = (sltRangeIndex[3] - sltRangeIndex[1]) + 1;
}
else if (sltRangeIndex[3] < formatRange[3]) {
insertStartIndex = insertEndIndex = (sltRangeIndex[3] - sltRangeIndex[1]) + 1;
}
}
return [formatRange[0], formatRange[1] + insertStartIndex, formatRange[2], formatRange[3] + insertEndIndex];
}
else {
if (isAction || (args.insertType === 'above' && args.isUndoRedo)) {
sltRangeIndex = [args.start, 0, args.end, 0];
}
if (args.insertType === 'above') {
if ((formatRange[0] <= sltRangeIndex[0] && formatRange[2] >= sltRangeIndex[0])) {
insertStartIndex = 0;
insertEndIndex = (sltRangeIndex[2] - sltRangeIndex[0]) + 1;
}
else if (sltRangeIndex[0] < formatRange[0]) {
insertStartIndex = insertEndIndex = (sltRangeIndex[2] - sltRangeIndex[0]) + 1;
}
}
else {
if (args.isUndoRedo) {
var diffValue = (args.end - args.start) + 1;
sltRangeIndex = [args.start - diffValue, 0, args.end - diffValue, 0];
}
if ((formatRange[0] <= sltRangeIndex[2] && formatRange[2] >= sltRangeIndex[2])) {
insertStartIndex = 0;
insertEndIndex = (sltRangeIndex[2] - sltRangeIndex[0]) + 1;
}
else if (sltRangeIndex[2] < formatRange[2]) {
insertStartIndex = insertEndIndex = (sltRangeIndex[2] - sltRangeIndex[0]) + 1;
}
}
return [formatRange[0] + insertStartIndex, formatRange[1], formatRange[2] + insertEndIndex, formatRange[3]];
}
}
/**
* @param {InsertDeleteModelArgs} args - row index
* @param {number[]} formatRange - cell range index
* @returns {number[]} - retruns data range
* @hidden
*/
export function deleteFormatRange(args, formatRange) {
var cellRange;
var deleteStartIndex = 0;
var deleteEndIndex = 0;
if (args.modelType === 'Column') {
cellRange = [0, args.start, args.model.usedRange.rowIndex, args.end];
if (cellRange[3] < formatRange[1]) {
deleteStartIndex = deleteEndIndex = cellRange[3] - cellRange[1] + 1;
}
else if (cellRange[1] >= formatRange[1] && cellRange[3] <= formatRange[3]) {
deleteEndIndex = cellRange[3] - cellRange[1] + 1;
}
else if (cellRange[1] >= formatRange[1] && cellRange[1] <= formatRange[3]) {
deleteEndIndex = formatRange[3] - cellRange[1] + 1;
}
else if (cellRange[1] < formatRange[1] && cellRange[3] >= formatRange[1]) {
deleteStartIndex = formatRange[1] - cellRange[1];
deleteEndIndex = cellRange[3] - cellRange[1] + 1;
}
else if (cellRange[1] < formatRange[1] && cellRange[3] < formatRange[3]) {
deleteStartIndex = (cellRange[3] - formatRange[1]) + (cellRange[3] - cellRange[1]) + 1;
deleteEndIndex = cellRange[3] - cellRange[1] + 1;
}
return [formatRange[0], formatRange[1] - deleteStartIndex, formatRange[2], formatRange[3] - deleteEndIndex];
}
else {
cellRange = [args.start, 0, args.end, args.model.usedRange.colIndex];
if (cellRange[2] < formatRange[0]) {
deleteStartIndex = deleteEndIndex = cellRange[2] - cellRange[0] + 1;
}
else if (cellRange[0] >= formatRange[0] && cellRange[2] <= formatRange[2]) {
deleteEndIndex = cellRange[2] - cellRange[0] + 1;
}
else if (cellRange[0] >= formatRange[0] && cellRange[0] <= formatRange[2]) {
deleteEndIndex = formatRange[2] - cellRange[0] + 1;
}
else if (cellRange[0] < formatRange[0] && cellRange[2] >= formatRange[0]) {
deleteStartIndex = formatRange[0] - cellRange[0];
deleteEndIndex = cellRange[2] - cellRange[0] + 1;
}
else if (cellRange[0] < formatRange[0] && cellRange[2] < formatRange[2]) {
deleteStartIndex = (cellRange[2] - formatRange[0]) + (cellRange[2] - cellRange[0]) + 1;
deleteEndIndex = cellRange[2] - cellRange[0] + 1;
}
return [formatRange[0] - deleteStartIndex, formatRange[1], formatRange[2] - deleteEndIndex, formatRange[3]];
}
}
/**
* @param {ConditionalFormat[]} curCF - Specifies current Conditional formatting.
* @param {ConditionalFormatModel[]} cfRule - Specifies conditional formatting rules.
* @param {number} rowIdx - Specifies the row index.
* @param {number} colIdx -Specifies the col index.
* @param {number[]} startRanges - Specifies conditional formatting origin index.
* @param {number[]} fillRanges - Specifies the conditional formatting fill ranges.
* @param {SheetModel} sheet -Specifies the conditional formatted sheet.
* @returns {void} - Updates Conditional formatting model.
* @hidden
*/
export function updateCFModel(curCF, cfRule, rowIdx, colIdx, startRanges, fillRanges, sheet) {
var cfRange;
var indexes;
for (var i = curCF.length - 1; i >= 0; i--) {
cfRange = curCF[i].range.trim().split(',');
for (var j = 0; j < cfRange.length; j++) {
indexes = getRangeIndexes(cfRange[j].includes(':') ? cfRange[j] :
cfRange[j] + ":" + cfRange[j]);
if (rowIdx >= indexes[0] && colIdx >= indexes[1] && rowIdx <= indexes[2] && colIdx <= indexes[3]) {
cfRule.push(curCF[i]);
curCF.splice(i, 1);
break;
}
else if (startRanges && startRanges[0] >= indexes[0] && startRanges[1] >= indexes[1] &&
startRanges[0] <= indexes[2] && startRanges[1] <= indexes[3]) {
var newCondiFormat = {
action: 'autofillWithCF',
cFColor: curCF[i].cFColor,
range: getRangeAddress(fillRanges),
type: curCF[i].type,
value: curCF[i].value
};
curCF.splice(i, 1);
for (var i_1 = 0, cfMembers = sheet.conditionalFormats; i_1 < cfMembers.length; i_1++) {
if (cfMembers[i_1].action === 'autofillWithCF' && cfMembers[i_1].range === newCondiFormat.range) {
break;
}
else if (i_1 === cfMembers.length - 1) {
cfRule.push(newCondiFormat);
sheet.conditionalFormats.push(newCondiFormat);
}
}
break;
}
}
}
}
/**
* @param {number} indexes - Specifies the indexes.
* @param {string} range - Specifies the range.
* @returns {boolean} - Return is range or not.
* @hidden
*/
export function checkRange(indexes, range) {
var ranges = range.trim().split(',');
var left;
var right;
var top;
var bottom;
var cfIdx;
var checkRange = function (idx) {
for (var i = 0; i < ranges.length; i++) {
cfIdx = getRangeIndexes(ranges[i].includes(':') ? ranges[i] : ranges[i] + ":" + ranges[i]);
if (idx[0] <= cfIdx[0] && idx[1] <= cfIdx[1] && idx[2] >= cfIdx[2] && idx[3] >= cfIdx[3]) {
return true;
}
else {
top = idx[0] >= cfIdx[0] && idx[0] <= cfIdx[2];
bottom = idx[2] >= cfIdx[0] && idx[2] <= cfIdx[2];
left = idx[1] >= cfIdx[1] && idx[1] <= cfIdx[3];
right = idx[3] >= cfIdx[1] && idx[3] <= cfIdx[3];
if (top && bottom) {
if (left || right || (idx[1] < cfIdx[1] && idx[3] > cfIdx[3])) {
if (idx[0] - cfIdx[0] > 0) {
return true;
}
if (cfIdx[2] - idx[2] > 0) {
return true;
}
}
if (left && idx[1] !== cfIdx[1]) {
return true;
}
if (right && idx[3] !== cfIdx[3]) {
return true;
}
}
else if (left && right) {
if (top || bottom || (idx[0] < cfIdx[0] && idx[2] > cfIdx[2])) {
if (idx[1] - cfIdx[1] > 0) {
return true;
}
if (cfIdx[3] - idx[3] > 0) {
return true;
}
}
if (top) {
if (idx[0] !== cfIdx[0]) {
return true;
}
}
else if (bottom && idx[2] !== cfIdx[2]) {
return true;
}
}
else if (top || bottom) {
if (left) {
if (idx[1] !== cfIdx[1]) {
return true;
}
if (idx[0] - cfIdx[0] > 0) {
return true;
}
else if (cfIdx[2] - idx[2] > 0) {
return true;
}
}
else if (right) {
if (idx[3] !== cfIdx[3]) {
return true;
}
if (idx[0] - cfIdx[0] > 0) {
return true;
}
else if (cfIdx[2] - idx[2] > 0) {
return true;
}
}
else if (idx[1] < cfIdx[1] && idx[3] > cfIdx[3]) {
return true;
}
}
else if ((left || right) && idx[0] < cfIdx[0] && idx[2] > cfIdx[2]) {
return true;
}
}
}
return false;
};
for (var j = 0; j < indexes.length; j++) {
if (checkRange(indexes[j])) {
return true;
}
}
return false;
}
/**
* Parse the formatted text to get the actual cell value.
*
* @param {string[]} valArr - Specifies the value array.
* @param {string} context - Specifies the workbook instance.
* @param {LocaleNumericSettings} numObj - Specifies the locale numeric options like decimal and group separators.
* @returns {string[]} - Returns the parsed number collection.
* @hidden
*/
export function parseLocaleNumber(valArr, context, numObj) {
var formatArgs;
if (!numObj) {
numObj = getNumericObject(context.locale);
}
for (var idx = 0; idx < valArr.length; idx++) {
if (isNumber(valArr[idx])) {
if (numObj.group === '.') {
valArr[idx] = valArr[idx].toString();
if (valArr[idx].indexOf('.') &&
parseThousandSeparator(valArr[idx], context.locale, numObj.group, numObj.decimal)) {
valArr[idx] = valArr[idx].split(numObj.group).join('');
}
}
}
else {
formatArgs = { formattedText: valArr[idx], value: valArr[idx], format: 'General',
cell: { value: valArr[idx], format: 'General' }, isEdit: true };
context.notify(getFormattedCellObject, formatArgs);
if (isNumber(formatArgs.value)) {
valArr[idx] = formatArgs.value.toString();
}
}
}
return valArr;
}
/**
* Returns the overall viewport indexes by including the freeze and movable part.
*
* @param {Workbook} parent - Specify the Workbook object.
* @param {number} viewport - Specifies the top, bottom, left, and right index of the current viewport.
* @param {number} viewport.topIndex - Specifies the top index of the current viewport.
* @param {number} viewport.leftIndex - Specifies the left index of the current viewport.
* @param {number} viewport.bottomIndex - Specifies the bottom index of the current viewport.
* @param {number} viewport.rightIndex - Specifies the right index of the current viewport.
* @returns {number} - Returns the viewport indexes.
* @hidden
*/
export function getViewportIndexes(parent, viewport) {
var sheet = parent.getActiveSheet();
var indexes = [[viewport.topIndex + parent.frozenRowCount(sheet), viewport.leftIndex + parent.frozenColCount(sheet),
viewport.bottomIndex, viewport.rightIndex]];
if (sheet.frozenRows || sheet.frozenColumns) {
var froezenRow = parent.frozenRowCount(sheet);
var froezenCol = parent.frozenColCount(sheet);
var topLeftCell = getCellIndexes(sheet.topLeftCell);
if (froezenRow && froezenCol) {
indexes.push([topLeftCell[0], topLeftCell[1], froezenRow - 1, froezenCol - 1]);
var paneTopLeftCell = getCellIndexes(sheet.paneTopLeftCell);
indexes.push([paneTopLeftCell[0], topLeftCell[1], viewport.bottomIndex, froezenCol - 1]);
}
if (froezenRow) {
indexes.push([topLeftCell[0], viewport.leftIndex + froezenCol, froezenRow - 1, viewport.rightIndex]);
}
if (froezenCol) {
indexes.push([viewport.topIndex + froezenRow, topLeftCell[1], viewport.bottomIndex,
froezenCol - 1]);
}
}
return indexes;
}
/**
* If the primary cell in the merged range row/column is hidden, then this method will update
* the next visible row/column index within the merged range.
*
* @param {VisibleMergeIndexArgs} args - Specifies the args.
* @returns {void} - Update the next visible row/column index within the merged range.
*/
export function setVisibleMergeIndex(args) {
if (isHiddenRow(args.sheet, args.rowIdx)) {
var idx = skipHiddenIdx(args.sheet, args.rowIdx, true);
if (idx < args.rowIdx + args.cell.rowSpan) {
args.rowIdx = idx;
args.isMergedHiddenCell = true;
}
}
if (isHiddenCol(args.sheet, args.colIdx)) {
var idx = skipHiddenIdx(args.sheet, args.colIdx, true, 'columns');
if (idx < args.colIdx + args.cell.colSpan) {
args.colIdx = idx;
args.isMergedHiddenCell = true;
}
}
}
/**
* Check whether the sheets are imported.
*
* @param {Workbook} context - Specifies the spreadsheet instance.
* @returns {boolean} - It returns true if the sheets are imported otherwise false.
* @hidden
*/
export function isImported(context) {
return context.workbookOpenModule && context.workbookOpenModule.preventFormatCheck;
}
/**
* Return a function that will auto-detect the number format of the formatted cell value.
*
* @param {Workbook} context - Specifies the Workbook instance.
* @returns {void} - Defines the common variables and returns the auto-detect number format function.
* @hidden
*/
export function getAutoDetectFormatParser(context) {
var intl = new Internationalization();
var eventArgs = { intl: intl, updateValue: true, value: '',
curSymbol: getNumberDependable(context.locale, defaultCurrencyCode) };
var options = { args: eventArgs, intl: intl };
var localeNumObj = getNumericObject(context.locale);
return function (cell) {
if (!cell.format && cell.value && !isNumber(cell.value)) {
eventArgs.cell = cell;
eventArgs.value = cell.value;
context.notify(checkDateFormat, eventArgs);
if (!cell.format) {
var cellVal = cell.value.toString();
if (cellVal.includes(options.args.curSymbol) || cellVal.includes(localeNumObj.group) || cellVal.includes('%')) {
options.fResult = cellVal;
context.notify(checkNumberFormat, options);
}
else if (localeNumObj.decimal !== '.' && !isNumber(cellVal)) {
if (cellVal.includes(localeNumObj.decimal)) {
cellVal = cellVal.replace(localeNumObj.decimal, '.');
if (isNumber(cellVal)) {
cell.value = cellVal;
}
}
}
}
}
};
}
/**
*
* @param {DataManager} dataManager - Specifies the Datamanager.
* @param {Predicate[]} predicates - Specifies the predicates.
* @param {Predicate[]} equalOrPredicates - Specifies the equal or predicates.
* @returns {Object[]} - Returns apply predicates object.
* @hidden
*/
export function applyPredicates(dataManager, predicates, equalOrPredicates) {
var query = new Query();
if (predicates.length) {
query.where(Predicate.and(predicates));
}
var result = dataManager.executeLocal(query);
if (equalOrPredicates) {
for (var idx = 0, predicateCollLen = equalOrPredicates.length; idx < predicateCollLen; idx++) {
if (!result.length) {
break;
}
query = new Query();
if (equalOrPredicates[idx].length) {
query.where(Predicate.or(equalOrPredicates[idx]));
}
result = new DataManager(result).executeLocal(query);
}
}
return result;
}
/**
* Checks whether the cell is read-only or not.
*
* @param {CellModel} cell - The cell to check.
* @param {ColumnModel} column - The column associated with the cell.
* @param {RowModel} row - The row associated with the cell.
* @returns {boolean} - Returns true if the cell is read-only, otherwise false.
* @hidden
*/
export function isReadOnly(cell, column, row) {
return (cell && cell.isReadOnly) || (row && row.isReadOnly) || (column && column.isReadOnly);
}
/**
* Checks whether a specific range of cells is read-only or not.
*
* @param {Workbook} parent - The spreadsheet instance.
* @param {number[]} rangeIndexes - The range indexes to check.
* @param {boolean} [throwAlert] - Specifies whether to throw an alert if cells are read-only.
* @returns {boolean} - Returns true if any of the cells is read-only, otherwise false.
* @hidden
*/
export function isReadOnlyCells(parent, rangeIndexes, throwAlert) {
var sheet = parent.getActiveSheet();
var address = !isNullOrUndefined(rangeIndexes) ? rangeIndexes : getSwapRange(getRangeIndexes(sheet.selectedRange));
for (var row = address[0]; row <= address[2]; row++) {
for (var col = address[1]; col <= address[3]; col++) {
var cell = getCell(row, col, sheet);
if (isReadOnly(cell, getColumn(sheet, col), getRow(sheet, row))) {
if (throwAlert) {
parent.notify(workbookReadonlyAlert, null);
}
return true;
}
}
}
return false;
}
/**
* Checks whether the selected range in the sheet is an entire row or column and returns the updated range accordingly.
*
* @param {SheetModel} sheet -Specifies the sheet.
* @param {string} range - Specify the range that need to be updated.
* @returns {string} - Retruns updated range
* @hidden
*/
export function getUpdatedRange(sheet, range) {
var updateRange = range || sheet.selectedRange;
var indexes = getRangeIndexes(updateRange);
var maxColCount = sheet.colCount;
var maxRowCount = sheet.rowCount;
if (indexes[2] === maxRowCount - 1 && indexes[0] === 0) {
updateRange = updateRange.replace(/[0-9]/g, '');
}
else if (indexes[3] === maxColCount - 1 && indexes[2] === 0) {
updateRange = updateRange.replace(/\D/g, '');
}
return updateRange;
}
/**
* Calculating resolution based windows value
*
* @param {number} size - Specify the end column index.
* @returns {number} - get excluded column width.
* @hidden
*/
export function addDPRValue(size) {
if (window.devicePixelRatio % 1 > 0) {
var pointValue = (size * window.devicePixelRatio) % 1;
return size + (pointValue ? ((pointValue > 0.5 ? (1 - pointValue) : -1 * pointValue) / window.devicePixelRatio) : 0);
}
return size;
}
/**
* Updated the top border of the adjacent merged cells
*
* @param {Workbook} context - The spreadsheet instance.
* @param {number[]} rowIndexes - An array of row indexes that top border need to be updated.
* @param {number[]} colIndexes - An array of col indexes that top border need to be updated.
* @returns {void}
* @hidden
*/
export function updateMergeBorder(context, rowIndexes, colIndexes) {
if (!rowIndexes.length) {
return;
}
var sheet = context.getActiveSheet();
var style;
var parent = context;
var frozenCol = context.frozenColCount(sheet);
var startCol = (colIndexes && colIndexes[0]) ||
(frozenCol ? getCellIndexes(sheet.topLeftCell)[1] : parent.viewport.leftIndex);
var endCol = (colIndexes && colIndexes[1]) || parent.viewport.rightIndex;
rowIndexes.forEach(function (rowIdx) {
for (var col = startCol; col <= endCol; col++) {
if (col === frozenCol) {
col += parent.viewport.leftIndex;
}
var prevModel = getCell(rowIdx - 1, col, sheet, false, true);
if (((!prevModel.rowSpan || prevModel.rowSpan === 1)
|| (!prevModel.colSpan || prevModel.colSpan === 1)) &&
(!prevModel.style || !prevModel.style.borderBottom || prevModel.style.borderBottom === 'none')) {
style = getCell(rowIdx, col, sheet, false, true).style;
if (style && style.borderTop) {
var prevCell = context.getCell(rowIdx - 1, col);
if (prevCell && prevCell.style.borderBottom) {
var curCell = context.getCell(rowIdx, col);
if (curCell) {
prevCell.style.borderBottom = '';
curCell.style.borderTop = style.borderTop;
}
}
}
}
}
});
}