@visactor/vtable-sheet 1.20.0-alpha.3 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +173 -0
  2. package/cjs/components/vtable-sheet.d.ts +2 -0
  3. package/cjs/components/vtable-sheet.js +45 -13
  4. package/cjs/components/vtable-sheet.js.map +1 -1
  5. package/cjs/core/WorkSheet.d.ts +1 -0
  6. package/cjs/core/WorkSheet.js +32 -7
  7. package/cjs/core/WorkSheet.js.map +1 -1
  8. package/cjs/core/table-plugins.js.map +1 -1
  9. package/cjs/event/event-manager.d.ts +7 -8
  10. package/cjs/event/event-manager.js +2 -11
  11. package/cjs/event/event-manager.js.map +1 -1
  12. package/cjs/formula/formula-autocomplete.js.map +1 -1
  13. package/cjs/formula/formula-editor.js.map +1 -1
  14. package/cjs/formula/formula-engine.d.ts +102 -0
  15. package/cjs/formula/formula-engine.js +1028 -0
  16. package/cjs/formula/formula-engine.js.map +1 -0
  17. package/cjs/formula/formula-helper.js +1 -1
  18. package/cjs/formula/formula-helper.js.map +1 -1
  19. package/cjs/formula/formula-range-selector.d.ts +5 -2
  20. package/cjs/formula/formula-range-selector.js +70 -15
  21. package/cjs/formula/formula-range-selector.js.map +1 -1
  22. package/cjs/formula/formula-ui-manager.js +7 -4
  23. package/cjs/formula/formula-ui-manager.js.map +1 -1
  24. package/cjs/formula/index.js +1 -2
  25. package/cjs/index.d.ts +1 -1
  26. package/cjs/index.js +1 -1
  27. package/cjs/index.js.map +1 -1
  28. package/cjs/managers/formula-manager.d.ts +19 -16
  29. package/cjs/managers/formula-manager.js +71 -183
  30. package/cjs/managers/formula-manager.js.map +1 -1
  31. package/cjs/managers/menu-manager.js.map +1 -1
  32. package/cjs/tools/env.js.map +1 -1
  33. package/cjs/ts-types/event.d.ts +77 -68
  34. package/cjs/ts-types/event.js +6 -1
  35. package/cjs/ts-types/event.js.map +1 -1
  36. package/dist/vtable-sheet.js +73155 -109724
  37. package/dist/vtable-sheet.min.js +1 -853
  38. package/es/components/vtable-sheet.d.ts +2 -0
  39. package/es/components/vtable-sheet.js +46 -12
  40. package/es/components/vtable-sheet.js.map +1 -1
  41. package/es/core/WorkSheet.d.ts +1 -0
  42. package/es/core/WorkSheet.js +32 -5
  43. package/es/core/WorkSheet.js.map +1 -1
  44. package/es/core/table-plugins.js.map +1 -1
  45. package/es/event/event-manager.d.ts +7 -8
  46. package/es/event/event-manager.js +2 -11
  47. package/es/event/event-manager.js.map +1 -1
  48. package/es/formula/formula-autocomplete.js.map +1 -1
  49. package/es/formula/formula-editor.js.map +1 -1
  50. package/es/formula/formula-engine.d.ts +102 -0
  51. package/es/formula/formula-engine.js +1020 -0
  52. package/es/formula/formula-engine.js.map +1 -0
  53. package/es/formula/formula-helper.js +1 -1
  54. package/es/formula/formula-helper.js.map +1 -1
  55. package/es/formula/formula-range-selector.d.ts +5 -2
  56. package/es/formula/formula-range-selector.js +70 -15
  57. package/es/formula/formula-range-selector.js.map +1 -1
  58. package/es/formula/formula-ui-manager.js +7 -4
  59. package/es/formula/formula-ui-manager.js.map +1 -1
  60. package/es/formula/index.js +1 -2
  61. package/es/index.d.ts +1 -1
  62. package/es/index.js +1 -1
  63. package/es/index.js.map +1 -1
  64. package/es/managers/formula-manager.d.ts +19 -16
  65. package/es/managers/formula-manager.js +72 -184
  66. package/es/managers/formula-manager.js.map +1 -1
  67. package/es/managers/menu-manager.js.map +1 -1
  68. package/es/tools/env.js.map +1 -1
  69. package/es/ts-types/event.d.ts +77 -68
  70. package/es/ts-types/event.js +6 -1
  71. package/es/ts-types/event.js.map +1 -1
  72. package/package.json +6 -7
  73. package/cjs/test/formula-complete.test.d.ts +0 -1
  74. package/cjs/test/formula-complete.test.js +0 -42
  75. package/cjs/test/formula-complete.test.js.map +0 -1
  76. package/es/test/formula-complete.test.d.ts +0 -1
  77. package/es/test/formula-complete.test.js +0 -36
  78. package/es/test/formula-complete.test.js.map +0 -1
@@ -0,0 +1,1028 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: !0
5
+ }), exports.FormulaEngine = void 0;
6
+
7
+ const formula_helper_1 = require("./formula-helper");
8
+
9
+ class FormulaEngine {
10
+ constructor(_config = {}) {
11
+ this.sheets = new Map, this.reverseSheets = new Map, this.sheetData = new Map, this.formulaCells = new Map,
12
+ this.dependencies = new Map, this.dependents = new Map, this.nextSheetId = 0, this.activeSheetKey = null;
13
+ }
14
+ setActiveSheet(sheetKey) {
15
+ this.sheets.has(sheetKey) && (this.activeSheetKey = sheetKey);
16
+ }
17
+ getActiveSheet() {
18
+ return this.activeSheetKey;
19
+ }
20
+ addSheet(sheetKey, data) {
21
+ if (this.sheets.has(sheetKey)) return this.sheets.get(sheetKey);
22
+ const sheetId = this.nextSheetId++;
23
+ this.sheets.set(sheetKey, sheetId), this.reverseSheets.set(sheetId, sheetKey);
24
+ const sheetData = data || [ [ "" ] ];
25
+ return this.sheetData.set(sheetId, this.normalizeData(sheetData)), sheetId;
26
+ }
27
+ normalizeData(data) {
28
+ if (!Array.isArray(data) || 0 === data.length) return [ [ "" ] ];
29
+ const maxCols = Math.max(...data.map((row => Array.isArray(row) ? row.length : 0)));
30
+ return data.map((row => {
31
+ if (!Array.isArray(row)) return Array(maxCols).fill("");
32
+ const normalizedRow = row.map((cell => {
33
+ if ("string" == typeof cell) {
34
+ if (cell.startsWith("=")) return cell;
35
+ const num = Number(cell);
36
+ return isNaN(num) || "" === cell.trim() ? cell : num;
37
+ }
38
+ return null != cell ? cell : "";
39
+ }));
40
+ for (;normalizedRow.length < maxCols; ) normalizedRow.push("");
41
+ return normalizedRow;
42
+ }));
43
+ }
44
+ getSheetId(sheetKey) {
45
+ const sheetId = this.sheets.get(sheetKey);
46
+ return void 0 === sheetId ? this.addSheet(sheetKey) : sheetId;
47
+ }
48
+ setCellContent(cell, value) {
49
+ if (!cell || void 0 === cell.sheet || void 0 === cell.row || void 0 === cell.col) throw new Error("Invalid cell parameter");
50
+ if (cell.row < 0 || cell.col < 0) throw new Error(`Cell coordinates out of bounds: row=${cell.row}, col=${cell.col}`);
51
+ const sheetId = this.getSheetId(cell.sheet);
52
+ this.sheetData.has(sheetId) || this.sheetData.set(sheetId, [ [ "" ] ]);
53
+ const sheet = this.sheetData.get(sheetId);
54
+ if (!sheet) throw new Error(`Sheet data not found for ID: ${sheetId}`);
55
+ for (;sheet.length <= cell.row; ) sheet.push([]);
56
+ for (;sheet[cell.row].length <= cell.col; ) sheet[cell.row].push("");
57
+ let processedValue = value;
58
+ if (null == processedValue && (processedValue = ""), "string" == typeof processedValue && !processedValue.startsWith("=")) {
59
+ const numericValue = Number(processedValue);
60
+ isNaN(numericValue) || "" === processedValue.trim() || (processedValue = numericValue);
61
+ }
62
+ if (sheet[cell.row][cell.col] = processedValue, "string" == typeof processedValue && processedValue.startsWith("=")) {
63
+ const cellKey = this.getCellKey(cell);
64
+ this.formulaCells.set(cellKey, processedValue), this.updateDependencies(cellKey, processedValue);
65
+ }
66
+ this.recalculateDependents(cell);
67
+ }
68
+ getCellKey(cell) {
69
+ return `${cell.sheet}!${this.getA1Notation(cell.row, cell.col)}`;
70
+ }
71
+ getA1Notation(row, col) {
72
+ let columnStr = "", tempCol = col;
73
+ do {
74
+ columnStr = String.fromCharCode(65 + tempCol % 26) + columnStr, tempCol = Math.floor(tempCol / 26) - 1;
75
+ } while (tempCol >= 0);
76
+ return columnStr + (row + 1);
77
+ }
78
+ parseA1Notation(cellRef) {
79
+ const match = cellRef.match(/^([A-Z]+)([0-9]+)$/);
80
+ if (!match) throw new Error(`Invalid cell reference: ${cellRef}`);
81
+ const colLetters = match[1], rowNumber = parseInt(match[2], 10);
82
+ let col = 0;
83
+ for (let i = 0; i < colLetters.length; i++) col = 26 * col + (colLetters.charCodeAt(i) - 65);
84
+ return {
85
+ row: rowNumber - 1,
86
+ col: col
87
+ };
88
+ }
89
+ parseCellKey(cellKey) {
90
+ const parts = cellKey.split("!");
91
+ if (2 !== parts.length) return null;
92
+ const [sheet, a1Notation] = parts;
93
+ try {
94
+ const {row: row, col: col} = this.parseA1Notation(a1Notation);
95
+ return {
96
+ sheet: sheet,
97
+ row: row,
98
+ col: col
99
+ };
100
+ } catch (_a) {
101
+ return null;
102
+ }
103
+ }
104
+ calculateFormula(formula) {
105
+ try {
106
+ if (!formula.startsWith("=")) return {
107
+ value: formula,
108
+ error: void 0
109
+ };
110
+ const expression = formula.substring(1).trim();
111
+ return this.parseExpression(expression);
112
+ } catch (error) {
113
+ return {
114
+ value: null,
115
+ error: error instanceof Error ? error.message : "Calculation failed"
116
+ };
117
+ }
118
+ }
119
+ parseExpression(expr) {
120
+ expr = expr.trim();
121
+ const functionResult = this.tryParseFunction(expr);
122
+ if (functionResult) return functionResult;
123
+ const comparisonResult = this.tryParseComparison(expr);
124
+ return comparisonResult || this.parseArithmetic(expr);
125
+ }
126
+ tryParseComparison(expr) {
127
+ const operators = [ ">=", "<=", "<>", ">", "<", "=" ];
128
+ for (const op of operators) {
129
+ const index = expr.indexOf(op);
130
+ if (-1 !== index) {
131
+ const leftExpr = expr.substring(0, index).trim(), rightExpr = expr.substring(index + op.length).trim();
132
+ if (!leftExpr || !rightExpr) continue;
133
+ const leftResult = this.parseExpression(leftExpr), rightResult = this.parseExpression(rightExpr);
134
+ if (leftResult.error || rightResult.error) return {
135
+ value: null,
136
+ error: "Comparison operands error"
137
+ };
138
+ const leftVal = Number(leftResult.value), rightVal = Number(rightResult.value);
139
+ let result = !1;
140
+ if (isNaN(leftVal) || isNaN(rightVal)) {
141
+ const leftStr = String(leftResult.value), rightStr = String(rightResult.value);
142
+ switch (op) {
143
+ case ">=":
144
+ result = leftStr >= rightStr;
145
+ break;
146
+
147
+ case "<=":
148
+ result = leftStr <= rightStr;
149
+ break;
150
+
151
+ case "<>":
152
+ result = leftStr !== rightStr;
153
+ break;
154
+
155
+ case ">":
156
+ result = leftStr > rightStr;
157
+ break;
158
+
159
+ case "<":
160
+ result = leftStr < rightStr;
161
+ break;
162
+
163
+ case "=":
164
+ result = leftStr === rightStr;
165
+ }
166
+ } else switch (op) {
167
+ case ">=":
168
+ result = leftVal >= rightVal;
169
+ break;
170
+
171
+ case "<=":
172
+ result = leftVal <= rightVal;
173
+ break;
174
+
175
+ case "<>":
176
+ result = leftVal !== rightVal;
177
+ break;
178
+
179
+ case ">":
180
+ result = leftVal > rightVal;
181
+ break;
182
+
183
+ case "<":
184
+ result = leftVal < rightVal;
185
+ break;
186
+
187
+ case "=":
188
+ result = leftVal === rightVal;
189
+ }
190
+ return {
191
+ value: result,
192
+ error: void 0
193
+ };
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ parseArithmetic(expr) {
199
+ try {
200
+ const functionResult = this.tryParseFunction(expr);
201
+ if (functionResult) return functionResult;
202
+ if (expr.startsWith('"') && expr.endsWith('"')) return {
203
+ value: expr.slice(1, -1),
204
+ error: void 0
205
+ };
206
+ if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+$/.test(expr)) return {
207
+ value: this.getCellValueByA1(expr),
208
+ error: void 0
209
+ };
210
+ if (/^-?\d+(\.\d+)?$/.test(expr)) return {
211
+ value: Number(expr),
212
+ error: void 0
213
+ };
214
+ if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(expr)) {
215
+ return {
216
+ value: this.getRangeValuesFromExpr(expr),
217
+ error: void 0
218
+ };
219
+ }
220
+ return expr.includes("+") || expr.includes("-") || expr.includes("*") || expr.includes("/") ? this.evaluateArithmeticWithFunctions(expr) : {
221
+ value: expr,
222
+ error: void 0
223
+ };
224
+ } catch (error) {
225
+ return {
226
+ value: null,
227
+ error: error instanceof Error ? error.message : "Arithmetic parsing failed"
228
+ };
229
+ }
230
+ }
231
+ tryParseFunction(expr) {
232
+ const funcStartMatch = expr.match(/^([A-Z]+)\s*\(/i);
233
+ if (!funcStartMatch) return null;
234
+ const funcName = funcStartMatch[1].toUpperCase();
235
+ let depth = 1, argsEnd = funcStartMatch[0].length - 1;
236
+ for (let i = funcStartMatch[0].length; i < expr.length; i++) if ("(" === expr[i]) depth++; else if (")" === expr[i] && (depth--,
237
+ 0 === depth)) {
238
+ argsEnd = i;
239
+ break;
240
+ }
241
+ if (0 !== depth) return null;
242
+ if (argsEnd + 1 !== expr.length) return null;
243
+ const argsStr = expr.substring(funcStartMatch[0].length, argsEnd), args = this.parseArguments(argsStr), argValues = [];
244
+ for (const arg of args) {
245
+ const result = this.parseExpression(arg);
246
+ if (result.error) return {
247
+ value: null,
248
+ error: `Argument error in ${funcName}: ${result.error}`
249
+ };
250
+ argValues.push(result.value);
251
+ }
252
+ return this.executeFunction(funcName, argValues);
253
+ }
254
+ parseArguments(argsStr) {
255
+ const args = [];
256
+ let current = "", depth = 0, inQuotes = !1;
257
+ for (let i = 0; i < argsStr.length; i++) {
258
+ const char = argsStr[i];
259
+ '"' !== char || 0 !== i && "\\" === argsStr[i - 1] || (inQuotes = !inQuotes), inQuotes || ("(" === char && depth++,
260
+ ")" === char && depth--, "," !== char || 0 !== depth) ? current += char : (args.push(current.trim()),
261
+ current = "");
262
+ }
263
+ if (current.trim() && args.push(current.trim()), 1 === args.length && args[0].includes("(") && args[0].includes(")")) {
264
+ const singleArg = args[0], reArgs = [];
265
+ let reCurrent = "", reDepth = 0, reInQuotes = !1;
266
+ for (let i = 0; i < singleArg.length; i++) {
267
+ const char = singleArg[i];
268
+ '"' !== char || 0 !== i && "\\" === singleArg[i - 1] || (reInQuotes = !reInQuotes),
269
+ reInQuotes || ("(" === char && reDepth++, ")" === char && reDepth--, "," !== char || 0 !== reDepth) ? reCurrent += char : (reArgs.push(reCurrent.trim()),
270
+ reCurrent = "");
271
+ }
272
+ if (reCurrent.trim() && reArgs.push(reCurrent.trim()), reArgs.length > 1) return reArgs;
273
+ }
274
+ return args;
275
+ }
276
+ executeFunction(funcName, args) {
277
+ try {
278
+ switch (funcName) {
279
+ case "SUM":
280
+ return this.calculateSum(args);
281
+
282
+ case "AVERAGE":
283
+ return this.calculateAverage(args);
284
+
285
+ case "MAX":
286
+ return this.calculateMax(args);
287
+
288
+ case "MIN":
289
+ return this.calculateMin(args);
290
+
291
+ case "ABS":
292
+ return this.calculateAbs(args);
293
+
294
+ case "ROUND":
295
+ return this.calculateRound(args);
296
+
297
+ case "INT":
298
+ return this.calculateInt(args);
299
+
300
+ case "RAND":
301
+ return this.calculateRand(args);
302
+
303
+ case "COUNT":
304
+ return this.calculateCount(args);
305
+
306
+ case "COUNTA":
307
+ return this.calculateCountA(args);
308
+
309
+ case "STDEV":
310
+ return this.calculateStdev(args);
311
+
312
+ case "VAR":
313
+ return this.calculateVar(args);
314
+
315
+ case "MEDIAN":
316
+ return this.calculateMedian(args);
317
+
318
+ case "PRODUCT":
319
+ return this.calculateProduct(args);
320
+
321
+ case "IF":
322
+ return this.calculateIf(args);
323
+
324
+ case "AND":
325
+ return this.calculateAnd(args);
326
+
327
+ case "OR":
328
+ return this.calculateOr(args);
329
+
330
+ case "NOT":
331
+ return this.calculateNot(args);
332
+
333
+ case "TODAY":
334
+ return this.calculateToday(args);
335
+
336
+ case "NOW":
337
+ return this.calculateNow(args);
338
+
339
+ default:
340
+ return {
341
+ value: null,
342
+ error: `Unknown function: ${funcName}`
343
+ };
344
+ }
345
+ } catch (error) {
346
+ return {
347
+ value: null,
348
+ error: error instanceof Error ? error.message : "Function execution failed"
349
+ };
350
+ }
351
+ }
352
+ calculateSum(args) {
353
+ let total = 0;
354
+ for (const arg of this.flattenArgsWithRanges(args)) {
355
+ const num = Number(arg);
356
+ isNaN(num) || (total += num);
357
+ }
358
+ return {
359
+ value: total,
360
+ error: void 0
361
+ };
362
+ }
363
+ calculateAverage(args) {
364
+ const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg))));
365
+ if (0 === values.length) return {
366
+ value: 0,
367
+ error: void 0
368
+ };
369
+ const sum = values.reduce(((acc, val) => acc + Number(val)), 0);
370
+ return {
371
+ value: Number(sum) / Number(values.length),
372
+ error: void 0
373
+ };
374
+ }
375
+ calculateMax(args) {
376
+ const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg)))).map((arg => Number(arg)));
377
+ return 0 === values.length ? {
378
+ value: 0,
379
+ error: void 0
380
+ } : {
381
+ value: Math.max(...values),
382
+ error: void 0
383
+ };
384
+ }
385
+ calculateMin(args) {
386
+ const values = this.flattenArgsWithRanges(args).filter((arg => !isNaN(Number(arg)))).map((arg => Number(arg)));
387
+ return 0 === values.length ? {
388
+ value: 0,
389
+ error: void 0
390
+ } : {
391
+ value: Math.min(...values),
392
+ error: void 0
393
+ };
394
+ }
395
+ calculateAbs(args) {
396
+ if (1 !== args.length) return {
397
+ value: null,
398
+ error: "ABS requires exactly 1 argument"
399
+ };
400
+ const num = Number(args[0]);
401
+ return isNaN(num) ? {
402
+ value: null,
403
+ error: "ABS argument must be a number"
404
+ } : {
405
+ value: Math.abs(num),
406
+ error: void 0
407
+ };
408
+ }
409
+ calculateRound(args) {
410
+ if (args.length < 1 || args.length > 2) return {
411
+ value: null,
412
+ error: "ROUND requires 1 or 2 arguments"
413
+ };
414
+ const num = Number(args[0]);
415
+ if (isNaN(num)) return {
416
+ value: null,
417
+ error: "ROUND argument must be a number"
418
+ };
419
+ const digits = 2 === args.length ? Number(args[1]) : 0;
420
+ if (isNaN(digits)) return {
421
+ value: null,
422
+ error: "ROUND digits must be a number"
423
+ };
424
+ const factor = Math.pow(10, digits);
425
+ return {
426
+ value: Math.round(num * factor) / factor,
427
+ error: void 0
428
+ };
429
+ }
430
+ calculateInt(args) {
431
+ if (1 !== args.length) return {
432
+ value: null,
433
+ error: "INT requires exactly 1 argument"
434
+ };
435
+ const num = Number(args[0]);
436
+ return isNaN(num) ? {
437
+ value: null,
438
+ error: "INT argument must be a number"
439
+ } : {
440
+ value: Math.floor(num),
441
+ error: void 0
442
+ };
443
+ }
444
+ calculateRand(args) {
445
+ return args.length > 0 ? {
446
+ value: null,
447
+ error: "RAND requires no arguments"
448
+ } : {
449
+ value: Math.random(),
450
+ error: void 0
451
+ };
452
+ }
453
+ calculateCount(args) {
454
+ const values = this.flattenArgs(args);
455
+ let count = 0;
456
+ for (const value of values) if ("" !== value && null != value) {
457
+ const num = Number(value);
458
+ isNaN(num) || count++;
459
+ }
460
+ return {
461
+ value: count,
462
+ error: void 0
463
+ };
464
+ }
465
+ calculateCountA(args) {
466
+ const values = this.flattenArgs(args);
467
+ let count = 0;
468
+ for (const value of values) null != value && "" !== value && count++;
469
+ return {
470
+ value: count,
471
+ error: void 0
472
+ };
473
+ }
474
+ calculateStdev(args) {
475
+ const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
476
+ if (values.length < 2) return {
477
+ value: 0,
478
+ error: void 0
479
+ };
480
+ const mean = values.reduce(((sum, val) => sum + val), 0) / values.length, variance = values.reduce(((sum, val) => sum + Math.pow(val - mean, 2)), 0) / (values.length - 1);
481
+ return {
482
+ value: Math.sqrt(variance),
483
+ error: void 0
484
+ };
485
+ }
486
+ calculateVar(args) {
487
+ const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
488
+ if (values.length < 2) return {
489
+ value: 0,
490
+ error: void 0
491
+ };
492
+ const mean = values.reduce(((sum, val) => sum + val), 0) / values.length;
493
+ return {
494
+ value: values.reduce(((sum, val) => sum + Math.pow(val - mean, 2)), 0) / (values.length - 1),
495
+ error: void 0
496
+ };
497
+ }
498
+ calculateMedian(args) {
499
+ const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
500
+ if (0 === values.length) return {
501
+ value: 0,
502
+ error: void 0
503
+ };
504
+ const sortedValues = values.sort(((a, b) => a - b));
505
+ if (sortedValues.length % 2 == 0) {
506
+ return {
507
+ value: (sortedValues[sortedValues.length / 2 - 1] + sortedValues[sortedValues.length / 2]) / 2,
508
+ error: void 0
509
+ };
510
+ }
511
+ return {
512
+ value: sortedValues[Math.floor(sortedValues.length / 2)],
513
+ error: void 0
514
+ };
515
+ }
516
+ calculateProduct(args) {
517
+ let product = 1;
518
+ const values = this.flattenArgs(args).filter((arg => !isNaN(Number(arg)) && "" !== arg && null != arg)).map((arg => Number(arg)));
519
+ if (0 === values.length) return {
520
+ value: 0,
521
+ error: void 0
522
+ };
523
+ for (const value of values) product *= value;
524
+ return {
525
+ value: product,
526
+ error: void 0
527
+ };
528
+ }
529
+ calculateIf(args) {
530
+ if (3 !== args.length) return {
531
+ value: null,
532
+ error: "IF requires exactly 3 arguments"
533
+ };
534
+ return {
535
+ value: this.isTruthy(args[0]) ? args[1] : args[2],
536
+ error: void 0
537
+ };
538
+ }
539
+ calculateAnd(args) {
540
+ if (args.length < 1) return {
541
+ value: null,
542
+ error: "AND requires at least 1 argument"
543
+ };
544
+ for (const arg of args) if (!this.isTruthy(arg)) return {
545
+ value: !1,
546
+ error: void 0
547
+ };
548
+ return {
549
+ value: !0,
550
+ error: void 0
551
+ };
552
+ }
553
+ calculateOr(args) {
554
+ if (args.length < 1) return {
555
+ value: null,
556
+ error: "OR requires at least 1 argument"
557
+ };
558
+ for (const arg of args) if (this.isTruthy(arg)) return {
559
+ value: !0,
560
+ error: void 0
561
+ };
562
+ return {
563
+ value: !1,
564
+ error: void 0
565
+ };
566
+ }
567
+ calculateNot(args) {
568
+ return 1 !== args.length ? {
569
+ value: null,
570
+ error: "NOT requires exactly 1 argument"
571
+ } : {
572
+ value: !this.isTruthy(args[0]),
573
+ error: void 0
574
+ };
575
+ }
576
+ calculateToday(args) {
577
+ return args.length > 0 ? {
578
+ value: null,
579
+ error: "TODAY requires no arguments"
580
+ } : {
581
+ value: new Date,
582
+ error: void 0
583
+ };
584
+ }
585
+ calculateNow(args) {
586
+ return args.length > 0 ? {
587
+ value: null,
588
+ error: "NOW requires no arguments"
589
+ } : {
590
+ value: new Date,
591
+ error: void 0
592
+ };
593
+ }
594
+ flattenArgs(args) {
595
+ const result = [];
596
+ for (const arg of args) Array.isArray(arg) ? result.push(...arg) : result.push(arg);
597
+ return result;
598
+ }
599
+ flattenArgsWithRanges(args) {
600
+ const result = [];
601
+ for (const arg of args) if (Array.isArray(arg)) result.push(...arg); else if ("string" == typeof arg && arg.includes(":")) try {
602
+ const rangeValues = this.getRangeValuesFromExpr(arg);
603
+ result.push(...rangeValues);
604
+ } catch (_a) {
605
+ result.push(arg);
606
+ } else result.push(arg);
607
+ return result;
608
+ }
609
+ isTruthy(value) {
610
+ if (null == value) return !1;
611
+ if (!1 === value || 0 === value || "" === value) return !1;
612
+ if (!0 === value) return !0;
613
+ if ("string" == typeof value) {
614
+ if ("false" === value.toLowerCase()) return !1;
615
+ if ("true" === value.toLowerCase()) return !0;
616
+ }
617
+ const num = Number(value);
618
+ return !!isNaN(num) || 0 !== num;
619
+ }
620
+ evaluateArithmeticWithFunctions(expr) {
621
+ try {
622
+ const functionMatches = [], functionRegex = /[A-Z]+\([^)]*\)/g;
623
+ let match;
624
+ for (;null !== (match = functionRegex.exec(expr)); ) functionMatches.push({
625
+ match: match[0],
626
+ start: match.index,
627
+ end: match.index + match[0].length
628
+ });
629
+ let processedExpr = expr;
630
+ const functionValues = [];
631
+ for (const funcMatch of functionMatches) {
632
+ const funcResult = this.parseExpression(funcMatch.match);
633
+ if (funcResult.error) return {
634
+ value: null,
635
+ error: `Error in function ${funcMatch.match}: ${funcResult.error}`
636
+ };
637
+ functionValues.push(funcResult.value), processedExpr = processedExpr.replace(funcMatch.match, `__FUNC_${functionValues.length - 1}__`);
638
+ }
639
+ const cellRefs = processedExpr.match(/[A-Z]+[0-9]+/g) || [];
640
+ for (const cellRef of cellRefs) {
641
+ const value = this.getCellValueByA1(cellRef);
642
+ processedExpr = processedExpr.replace(cellRef, String(value));
643
+ }
644
+ for (let i = 0; i < functionValues.length; i++) processedExpr = processedExpr.replace(`__FUNC_${i}__`, String(functionValues[i]));
645
+ return {
646
+ value: Function('"use strict"; return (' + processedExpr + ")")(),
647
+ error: void 0
648
+ };
649
+ } catch (error) {
650
+ return {
651
+ value: null,
652
+ error: "Basic arithmetic evaluation failed"
653
+ };
654
+ }
655
+ }
656
+ evaluateBasicArithmetic(expr) {
657
+ try {
658
+ if (/[A-Z]+\s*\(/.test(expr)) return {
659
+ value: null,
660
+ error: "Expression contains function calls"
661
+ };
662
+ let processedExpr = expr;
663
+ const cellRefs = processedExpr.match(/[A-Z]+[0-9]+/g) || [];
664
+ for (const cellRef of cellRefs) {
665
+ const value = this.getCellValueByA1(cellRef);
666
+ processedExpr = processedExpr.replace(cellRef, String(value));
667
+ }
668
+ return {
669
+ value: Function('"use strict"; return (' + processedExpr + ")")(),
670
+ error: void 0
671
+ };
672
+ } catch (error) {
673
+ return {
674
+ value: null,
675
+ error: "Basic arithmetic evaluation failed"
676
+ };
677
+ }
678
+ }
679
+ getCellValueByA1(a1Notation) {
680
+ try {
681
+ let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", cellRef = a1Notation;
682
+ if (a1Notation.includes("!")) {
683
+ const parts = a1Notation.split("!");
684
+ 2 === parts.length && (sheetKey = parts[0], cellRef = parts[1]);
685
+ }
686
+ const {row: row, col: col} = this.parseA1Notation(cellRef), cell = {
687
+ sheet: sheetKey,
688
+ row: row,
689
+ col: col
690
+ };
691
+ return this.getCellValue(cell).value;
692
+ } catch (_a) {
693
+ return 0;
694
+ }
695
+ }
696
+ getRangeValuesFromExpr(expr) {
697
+ try {
698
+ if (!expr.includes(":")) return [ this.getCellValueByA1(expr) ];
699
+ let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", rangeExpr = expr;
700
+ if (expr.includes("!")) {
701
+ const parts = expr.split("!");
702
+ 2 === parts.length && (sheetKey = parts[0], rangeExpr = parts[1]);
703
+ }
704
+ const [start, end] = rangeExpr.split(":"), startCell = this.parseA1Notation(start.trim()), endCell = this.parseA1Notation(end.trim()), values = [];
705
+ for (let row = startCell.row; row <= endCell.row; row++) for (let col = startCell.col; col <= endCell.col; col++) {
706
+ const cell = {
707
+ sheet: sheetKey,
708
+ row: row,
709
+ col: col
710
+ };
711
+ values.push(this.getCellValue(cell).value);
712
+ }
713
+ return values;
714
+ } catch (_a) {
715
+ return [];
716
+ }
717
+ }
718
+ getCellValue(cell) {
719
+ try {
720
+ const sheetId = this.sheets.get(cell.sheet);
721
+ if (void 0 === sheetId) return {
722
+ value: "",
723
+ error: void 0
724
+ };
725
+ const sheet = this.sheetData.get(sheetId);
726
+ if (!sheet || !sheet[cell.row] || void 0 === sheet[cell.row][cell.col] || null === sheet[cell.row][cell.col]) return {
727
+ value: "",
728
+ error: void 0
729
+ };
730
+ const value = sheet[cell.row][cell.col];
731
+ if ("string" == typeof value && value.startsWith("=")) {
732
+ return this.calculateFormula(value);
733
+ }
734
+ return value instanceof FormulaError ? {
735
+ value: null,
736
+ error: value.message
737
+ } : {
738
+ value: value,
739
+ error: void 0
740
+ };
741
+ } catch (error) {
742
+ return {
743
+ value: null,
744
+ error: error instanceof Error ? error.message : "Unknown error"
745
+ };
746
+ }
747
+ }
748
+ getCellFormula(cell) {
749
+ const cellKey = this.getCellKey(cell);
750
+ return this.formulaCells.get(cellKey);
751
+ }
752
+ isCellFormula(cell) {
753
+ const cellKey = this.getCellKey(cell);
754
+ return this.formulaCells.has(cellKey);
755
+ }
756
+ getCellDependents(cell) {
757
+ const cellKey = this.getCellKey(cell), dependents = this.dependents.get(cellKey);
758
+ if (!dependents) return [];
759
+ const result = [];
760
+ for (const dependent of dependents) {
761
+ const parsed = this.parseCellKey(dependent);
762
+ parsed && result.push(parsed);
763
+ }
764
+ return result;
765
+ }
766
+ getCellPrecedents(cell) {
767
+ const cellKey = this.getCellKey(cell), dependencies = this.dependencies.get(cellKey);
768
+ if (!dependencies) return [];
769
+ const result = [];
770
+ for (const dependency of dependencies) {
771
+ const parsed = this.parseCellKey(dependency);
772
+ parsed && result.push(parsed);
773
+ }
774
+ return result;
775
+ }
776
+ getAvailableFunctions() {
777
+ return [ ...formula_helper_1.supportedFunctions ];
778
+ }
779
+ validateFormula(formula) {
780
+ try {
781
+ if (!formula.startsWith("=")) return {
782
+ isValid: !0
783
+ };
784
+ const expression = formula.substring(1).trim();
785
+ return expression ? (this.validateExpressionStructure(expression), this.validateFunctionNames(expression),
786
+ {
787
+ isValid: !0
788
+ }) : {
789
+ isValid: !1,
790
+ error: "Empty expression"
791
+ };
792
+ } catch (error) {
793
+ return {
794
+ isValid: !1,
795
+ error: error instanceof Error ? error.message : "Invalid formula syntax"
796
+ };
797
+ }
798
+ }
799
+ validateFunctionNames(expr) {
800
+ const functionPattern = /([A-Z]+)\s*\(/gi;
801
+ let match;
802
+ for (;null !== (match = functionPattern.exec(expr)); ) {
803
+ const funcName = match[1].toUpperCase();
804
+ if (!formula_helper_1.supportedFunctions.includes(funcName)) throw new Error(`Unknown function: ${funcName}`);
805
+ }
806
+ }
807
+ validateExpressionStructure(expr) {
808
+ if ((expr.match(/\(/g) || []).length !== (expr.match(/\)/g) || []).length) throw new Error("Unmatched parentheses");
809
+ if ((expr.match(/"/g) || []).length % 2 != 0) throw new Error("Unmatched quotes");
810
+ if (expr.match(/[+\-*/^&%<>=]$/)) throw new Error("Expression ends with operator");
811
+ if (expr.match(/,,/)) throw new Error("Consecutive commas");
812
+ if (expr.match(/\([^)]*,\s*\)/) || expr.match(/,\s*\)/) || expr.match(/\(\s*\)/)) throw new Error("Invalid function arguments");
813
+ if (expr.match(/[+\-*/^&%][+\-*/^&%]/)) throw new Error("Basic arithmetic evaluation failed");
814
+ }
815
+ release() {
816
+ this.sheets.clear(), this.reverseSheets.clear(), this.sheetData.clear(), this.formulaCells.clear(),
817
+ this.dependencies.clear(), this.dependents.clear(), this.nextSheetId = 0;
818
+ }
819
+ exportFormulas(sheetKey) {
820
+ const result = {};
821
+ if (void 0 === this.sheets.get(sheetKey)) return result;
822
+ for (const entry of Array.from(this.formulaCells.entries())) {
823
+ const [cellKey, formula] = entry, cell = this.parseCellKey(cellKey);
824
+ if (cell && cell.sheet === sheetKey) {
825
+ result[this.getA1Notation(cell.row, cell.col)] = formula;
826
+ }
827
+ }
828
+ return result;
829
+ }
830
+ getAllSheets() {
831
+ const result = [];
832
+ for (const entry of Array.from(this.sheets.entries())) {
833
+ const [sheetKey, sheetId] = entry;
834
+ result.push({
835
+ key: sheetKey,
836
+ id: sheetId,
837
+ name: sheetKey
838
+ });
839
+ }
840
+ return result;
841
+ }
842
+ sortFormulasByDependency(sheetKey, formulas) {
843
+ try {
844
+ const tempDependencies = new Map, tempDependents = new Map;
845
+ for (const [cellRef, formula] of Object.entries(formulas)) {
846
+ const cellKey = `${sheetKey}!${cellRef}`, dependencies = this.extractCellReferences(formula, sheetKey);
847
+ tempDependencies.set(cellKey, new Set(dependencies));
848
+ for (const dep of dependencies) {
849
+ const depDependents = tempDependents.get(dep) || new Set;
850
+ depDependents.add(cellKey), tempDependents.set(dep, depDependents);
851
+ }
852
+ }
853
+ const visited = new Set, tempVisited = new Set, result = [], visit = cellKey => {
854
+ if (visited.has(cellKey)) return;
855
+ if (tempVisited.has(cellKey)) return;
856
+ tempVisited.add(cellKey);
857
+ const deps = tempDependencies.get(cellKey) || new Set;
858
+ for (const dep of deps) tempDependencies.has(dep) && visit(dep);
859
+ tempVisited.delete(cellKey), visited.add(cellKey);
860
+ const cellRef = cellKey.substring(sheetKey.length + 1), formula = formulas[cellRef];
861
+ formula && result.push([ cellRef, formula ]);
862
+ };
863
+ for (const cellKey of tempDependencies.keys()) visited.has(cellKey) || visit(cellKey);
864
+ return result;
865
+ } catch (error) {
866
+ return Object.entries(formulas);
867
+ }
868
+ }
869
+ removeSheet(sheetKey) {
870
+ const sheetId = this.sheets.get(sheetKey);
871
+ if (void 0 === sheetId) return;
872
+ if (this.sheets.size <= 1) throw new Error("Cannot remove the last sheet");
873
+ this.sheetData.delete(sheetId), this.sheets.delete(sheetKey), this.reverseSheets.delete(sheetId);
874
+ const keysToRemove = [];
875
+ for (const entry of Array.from(this.formulaCells.entries())) {
876
+ const [cellKey] = entry;
877
+ cellKey.startsWith(`${sheetKey}!`) && keysToRemove.push(cellKey);
878
+ }
879
+ for (const cellKey of keysToRemove) this.formulaCells.delete(cellKey), this.dependencies.delete(cellKey),
880
+ this.dependents.delete(cellKey);
881
+ for (const entry of Array.from(this.dependencies.entries())) {
882
+ const [cellKey, deps] = entry, newDeps = new Set;
883
+ for (const dep of deps) dep.startsWith(`${sheetKey}!`) || newDeps.add(dep);
884
+ 0 === newDeps.size ? this.dependencies.delete(cellKey) : this.dependencies.set(cellKey, newDeps);
885
+ }
886
+ for (const entry of Array.from(this.dependents.entries())) {
887
+ const [cellKey, dependents] = entry, newDependents = new Set;
888
+ for (const dep of dependents) dep.startsWith(`${sheetKey}!`) || newDependents.add(dep);
889
+ 0 === newDependents.size ? this.dependents.delete(cellKey) : this.dependents.set(cellKey, newDependents);
890
+ }
891
+ }
892
+ renameSheet(oldKey, newKey) {
893
+ const sheetId = this.sheets.get(oldKey);
894
+ if (void 0 === sheetId) throw new Error(`Sheet not found: ${oldKey}`);
895
+ if (this.sheets.has(newKey)) throw new Error(`Sheet already exists: ${newKey}`);
896
+ this.sheets.delete(oldKey), this.sheets.set(newKey, sheetId), this.reverseSheets.set(sheetId, newKey);
897
+ const formulaEntriesToUpdate = [];
898
+ for (const entry of Array.from(this.formulaCells.entries())) {
899
+ const [cellKey, formula] = entry;
900
+ if (cellKey.startsWith(`${oldKey}!`)) {
901
+ const newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`);
902
+ formulaEntriesToUpdate.push([ newCellKey, formula ]), this.formulaCells.delete(cellKey);
903
+ }
904
+ }
905
+ for (const [newCellKey, formula] of formulaEntriesToUpdate) this.formulaCells.set(newCellKey, formula);
906
+ const dependencyEntriesToUpdate = [];
907
+ for (const entry of Array.from(this.dependencies.entries())) {
908
+ const [cellKey, deps] = entry;
909
+ let newCellKey = cellKey;
910
+ const newDeps = new Set;
911
+ cellKey.startsWith(`${oldKey}!`) && (newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`));
912
+ for (const dep of deps) dep.startsWith(`${oldKey}!`) ? newDeps.add(dep.replace(`${oldKey}!`, `${newKey}!`)) : newDeps.add(dep);
913
+ newCellKey === cellKey && this.areSetsEqual(newDeps, deps) || (dependencyEntriesToUpdate.push([ newCellKey, newDeps ]),
914
+ this.dependencies.delete(cellKey));
915
+ }
916
+ for (const [newCellKey, newDeps] of dependencyEntriesToUpdate) this.dependencies.set(newCellKey, newDeps);
917
+ const dependentEntriesToUpdate = [];
918
+ for (const entry of Array.from(this.dependents.entries())) {
919
+ const [cellKey, dependents] = entry;
920
+ let newCellKey = cellKey;
921
+ const newDependents = new Set;
922
+ cellKey.startsWith(`${oldKey}!`) && (newCellKey = cellKey.replace(`${oldKey}!`, `${newKey}!`));
923
+ for (const dep of dependents) dep.startsWith(`${oldKey}!`) ? newDependents.add(dep.replace(`${oldKey}!`, `${newKey}!`)) : newDependents.add(dep);
924
+ newCellKey === cellKey && this.areSetsEqual(newDependents, dependents) || (dependentEntriesToUpdate.push([ newCellKey, newDependents ]),
925
+ this.dependents.delete(cellKey));
926
+ }
927
+ for (const [newCellKey, newDependents] of dependentEntriesToUpdate) this.dependents.set(newCellKey, newDependents);
928
+ }
929
+ areSetsEqual(set1, set2) {
930
+ if (set1.size !== set2.size) return !1;
931
+ for (const item of set1) if (!set2.has(item)) return !1;
932
+ return !0;
933
+ }
934
+ updateDependencies(cellKey, formula) {
935
+ var _a;
936
+ const oldDeps = this.dependencies.get(cellKey) || new Set;
937
+ for (const dep of oldDeps) {
938
+ const depDependents = this.dependents.get(dep) || new Set;
939
+ depDependents.delete(cellKey), 0 === depDependents.size ? this.dependents.delete(dep) : this.dependents.set(dep, depDependents);
940
+ }
941
+ const currentSheet = (null === (_a = this.parseCellKey(cellKey)) || void 0 === _a ? void 0 : _a.sheet) || "Sheet1", dependencies = this.extractCellReferences(formula, currentSheet);
942
+ this.dependencies.set(cellKey, new Set(dependencies));
943
+ for (const dep of dependencies) {
944
+ const depDependents = this.dependents.get(dep) || new Set;
945
+ depDependents.add(cellKey), this.dependents.set(dep, depDependents);
946
+ }
947
+ }
948
+ extractCellReferences(formula, currentSheet = "Sheet1") {
949
+ if (!formula || "string" != typeof formula || !formula.startsWith("=")) return [];
950
+ const expression = formula.substring(1).trim(), references = [];
951
+ try {
952
+ this.extractReferencesFromExpression(expression, references, currentSheet);
953
+ } catch (error) {}
954
+ return [ ...new Set(references) ];
955
+ }
956
+ extractReferencesFromExpression(expr, references, currentSheet = "Sheet1") {
957
+ let cleanExpr = expr.replace(/"[^"]*"/g, "");
958
+ cleanExpr = cleanExpr.replace(/'[^']*'/g, "");
959
+ const cellRefPattern = /(?:([A-Za-z0-9_]+)!)?([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g;
960
+ let match;
961
+ for (;null !== (match = cellRefPattern.exec(cleanExpr)); ) {
962
+ const sheetName = match[1] || currentSheet, startCell = match[2], endCell = match[3];
963
+ if (endCell) {
964
+ const expandedRefs = this.expandRangeToCells(sheetName, startCell, endCell);
965
+ references.push(...expandedRefs);
966
+ } else references.push(`${sheetName}!${startCell}`);
967
+ }
968
+ const singleMatches = cleanExpr.match(/[A-Z]+[0-9]+/g) || [];
969
+ for (const match of singleMatches) references.some((ref => ref.endsWith(match))) || references.push(`${currentSheet}!${match}`);
970
+ }
971
+ expandRangeToCells(sheetName, startCell, endCell) {
972
+ try {
973
+ const start = this.parseA1Notation(startCell), end = this.parseA1Notation(endCell), cells = [], minRow = Math.min(start.row, end.row), maxRow = Math.max(start.row, end.row), minCol = Math.min(start.col, end.col), maxCol = Math.max(start.col, end.col);
974
+ for (let row = minRow; row <= maxRow; row++) for (let col = minCol; col <= maxCol; col++) cells.push(`${sheetName}!${this.getA1Notation(row, col)}`);
975
+ return cells;
976
+ } catch (_a) {
977
+ return [ `${sheetName}!${startCell}:${endCell}` ];
978
+ }
979
+ }
980
+ recalculateDependents(changedCell) {
981
+ this.recalculateDependentsWithTracking(changedCell, new Set);
982
+ }
983
+ recalculateDependentsWithTracking(changedCell, visited) {
984
+ const cellKey = this.getCellKey(changedCell);
985
+ if (visited.has(cellKey)) return;
986
+ const dependents = this.dependents.get(cellKey);
987
+ if (!dependents || 0 === dependents.size) return;
988
+ const sortedDependents = this.sortCellsByDependency([ ...dependents ]);
989
+ for (const dependentKey of sortedDependents) this.recalculateSingleCellWithTracking(dependentKey, visited);
990
+ }
991
+ recalculateSingleCellWithTracking(cellKey, visited) {
992
+ if (visited.has(cellKey)) return;
993
+ const formula = this.formulaCells.get(cellKey);
994
+ if (!formula) return;
995
+ const cell = this.parseCellKey(cellKey);
996
+ if (!cell) return;
997
+ const result = this.calculateFormula(formula);
998
+ if (result.error) return;
999
+ const sheetId = this.sheets.get(cell.sheet);
1000
+ if (void 0 === sheetId) return;
1001
+ const sheet = this.sheetData.get(sheetId);
1002
+ if (!sheet || !sheet[cell.row] || void 0 === sheet[cell.row][cell.col]) return;
1003
+ const oldValue = sheet[cell.row][cell.col], newValue = result.value;
1004
+ sheet[cell.row][cell.col] = newValue, oldValue !== newValue && (visited.add(cellKey),
1005
+ this.recalculateDependentsWithTracking(cell, visited), visited.delete(cellKey));
1006
+ }
1007
+ sortCellsByDependency(cells) {
1008
+ const visited = new Set, tempVisited = new Set, result = [], visit = cell => {
1009
+ if (visited.has(cell)) return;
1010
+ if (tempVisited.has(cell)) return;
1011
+ tempVisited.add(cell);
1012
+ const deps = this.dependencies.get(cell) || new Set;
1013
+ for (const dep of deps) cells.includes(dep) && visit(dep);
1014
+ tempVisited.delete(cell), visited.add(cell), result.push(cell);
1015
+ };
1016
+ for (const cell of cells) visited.has(cell) || visit(cell);
1017
+ return result;
1018
+ }
1019
+ }
1020
+
1021
+ exports.FormulaEngine = FormulaEngine;
1022
+
1023
+ class FormulaError {
1024
+ constructor(message) {
1025
+ this.message = message;
1026
+ }
1027
+ }
1028
+ //# sourceMappingURL=formula-engine.js.map