@a3s-lab/office 0.35.0 → 0.36.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.
package/dist/8715.js CHANGED
@@ -1,4 +1,269 @@
1
1
  import { createOfficeId as createWorkId, directChild, attribute } from "./4121.js";
2
+ function sparseArrayIndexes(values) {
3
+ if (!values) return [];
4
+ return Object.keys(values).flatMap((key)=>{
5
+ const index = Number(key);
6
+ return Number.isSafeInteger(index) && index >= 0 && index < values.length ? [
7
+ index
8
+ ] : [];
9
+ });
10
+ }
11
+ function sparseArrayEntries(values) {
12
+ if (!values) return [];
13
+ return sparseArrayIndexes(values).flatMap((index)=>{
14
+ const value = values[index];
15
+ return void 0 === value ? [] : [
16
+ [
17
+ index,
18
+ value
19
+ ]
20
+ ];
21
+ });
22
+ }
23
+ function sparseMatrixColumnCount(matrix) {
24
+ let maximum = 0;
25
+ for (const [, row] of sparseArrayEntries(matrix))maximum = Math.max(maximum, row.length);
26
+ return maximum;
27
+ }
28
+ function spreadsheetGridSize(sheet) {
29
+ if (!sheet) return null;
30
+ return {
31
+ rowCount: Math.max(sheet.row ?? 0, sheet.data?.length ?? 0),
32
+ columnCount: Math.max(sheet.column ?? 0, sparseMatrixColumnCount(sheet.data))
33
+ };
34
+ }
35
+ function cloneSparseMatrix(source) {
36
+ const clone = [];
37
+ if (!source) return clone;
38
+ clone.length = source.length;
39
+ for (const [rowIndex, sourceRow] of sparseArrayEntries(source)){
40
+ const row = [];
41
+ row.length = sourceRow.length;
42
+ for (const columnIndex of sparseArrayIndexes(sourceRow))row[columnIndex] = sourceRow[columnIndex];
43
+ clone[rowIndex] = row;
44
+ }
45
+ return clone;
46
+ }
47
+ const DEFAULT_SPREADSHEET_CALCULATION_SETTINGS = {
48
+ mode: 'automatic',
49
+ fullCalculationOnLoad: false,
50
+ forceFullCalculation: false,
51
+ iterativeCalculation: false,
52
+ maximumIterations: 100,
53
+ maximumChange: 0.001,
54
+ fullPrecision: true
55
+ };
56
+ const VOLATILE_FUNCTIONS = new Set([
57
+ 'CELL',
58
+ 'INFO',
59
+ 'INDIRECT',
60
+ 'NOW',
61
+ 'OFFSET',
62
+ 'RAND',
63
+ 'RANDBETWEEN',
64
+ 'TODAY'
65
+ ]);
66
+ const FUTURE_FUNCTIONS = new Set([
67
+ 'ARRAYTOTEXT',
68
+ 'BYCOL',
69
+ 'BYROW',
70
+ 'CHOOSECOLS',
71
+ 'CHOOSEROWS',
72
+ 'DROP',
73
+ 'EXPAND',
74
+ 'FIELDVALUE',
75
+ 'FILTER',
76
+ 'HSTACK',
77
+ 'IMAGE',
78
+ 'ISOMITTED',
79
+ 'LAMBDA',
80
+ 'LET',
81
+ 'MAKEARRAY',
82
+ 'MAP',
83
+ 'RANDARRAY',
84
+ 'REDUCE',
85
+ 'SCAN',
86
+ 'SEQUENCE',
87
+ 'SORT',
88
+ 'SORTBY',
89
+ 'STOCKHISTORY',
90
+ 'TAKE',
91
+ 'TEXTAFTER',
92
+ 'TEXTBEFORE',
93
+ 'TEXTSPLIT',
94
+ 'TOCOL',
95
+ 'TOROW',
96
+ 'UNIQUE',
97
+ 'VALUETOTEXT',
98
+ 'VSTACK',
99
+ 'WRAPCOLS',
100
+ 'WRAPROWS',
101
+ 'XLOOKUP',
102
+ 'XMATCH'
103
+ ]);
104
+ const FUTURE_FUNCTION_PREFIXES = new Map([
105
+ [
106
+ 'FILTER',
107
+ '_xlfn._xlws.'
108
+ ],
109
+ [
110
+ 'SORT',
111
+ '_xlfn._xlws.'
112
+ ],
113
+ [
114
+ 'SORTBY',
115
+ '_xlfn._xlws.'
116
+ ],
117
+ [
118
+ 'UNIQUE',
119
+ '_xlfn.'
120
+ ],
121
+ ...Array.from(FUTURE_FUNCTIONS).filter((name)=>![
122
+ 'FILTER',
123
+ 'SORT',
124
+ 'SORTBY',
125
+ 'UNIQUE'
126
+ ].includes(name)).map((name)=>[
127
+ name,
128
+ '_xlfn.'
129
+ ])
130
+ ]);
131
+ function effectiveSpreadsheetCalculationSettings(settings) {
132
+ if (!settings) return {
133
+ ...DEFAULT_SPREADSHEET_CALCULATION_SETTINGS
134
+ };
135
+ return {
136
+ mode: settings.mode,
137
+ fullCalculationOnLoad: Boolean(settings.fullCalculationOnLoad),
138
+ forceFullCalculation: Boolean(settings.forceFullCalculation),
139
+ iterativeCalculation: Boolean(settings.iterativeCalculation),
140
+ maximumIterations: clampedInteger(settings.maximumIterations, 1, 10000, 100),
141
+ maximumChange: positiveNumber(settings.maximumChange, 0.001),
142
+ fullPrecision: false !== settings.fullPrecision
143
+ };
144
+ }
145
+ function editableSpreadsheetFormula(source) {
146
+ return transformFormulaOutsideStrings(source, (segment)=>segment.replace(/_xlfn\._xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlfn\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, ''));
147
+ }
148
+ function spreadsheetFormulaForXlsx(currentFormula, sourceFormula) {
149
+ const current = currentFormula.trim().replace(/^=/, '');
150
+ const source = sourceFormula?.trim().replace(/^=/, '');
151
+ if (source && comparableFormula(source) === comparableFormula(current)) return source;
152
+ return futureFunctionPrefixes(current, source);
153
+ }
154
+ function spreadsheetFormulaRangeForCell(sheet, row, column) {
155
+ const address = spreadsheetCellAddress(row, column);
156
+ return (sheet.formulaMetadata?.ranges ?? []).find((range)=>range.anchor.toUpperCase().replaceAll('$', '') === address);
157
+ }
158
+ function spreadsheetFormulaRangesForSelection(sheet, selection) {
159
+ return (sheet.formulaMetadata?.ranges ?? []).filter((range)=>rangesOverlap(selection, parseSpreadsheetFormulaRange(range.reference)));
160
+ }
161
+ function spreadsheetFormulaRangeConflict(sheet, range) {
162
+ const bounds = parseSpreadsheetFormulaRange(range.reference);
163
+ if (!bounds) return '引用范围无效';
164
+ const anchor = parseSpreadsheetCellAddress(range.anchor);
165
+ if (!anchor || !containsCell(bounds, anchor.row, anchor.column)) return '锚点不在引用范围内';
166
+ const anchorCell = sheet.data?.[anchor.row]?.[anchor.column];
167
+ if (!anchorCell) return '锚点单元格不存在';
168
+ const overlaps = (sheet.formulaMetadata?.ranges ?? []).filter((candidate)=>candidate !== range && rangesOverlap(bounds, parseSpreadsheetFormulaRange(candidate.reference)));
169
+ if (overlaps.length) return '与其他公式范围重叠';
170
+ for (const [row, cells] of sparseArrayEntries(sheet.data))if (!(row < bounds.startRow) && !(row > bounds.endRow)) {
171
+ for (const [column, cell] of sparseArrayEntries(cells))if (!(column < bounds.startColumn) && !(column > bounds.endColumn)) {
172
+ if (row !== anchor.row || column !== anchor.column) {
173
+ if (cell?.f) return `${spreadsheetCellAddress(row, column)} 包含独立公式`;
174
+ }
175
+ }
176
+ }
177
+ if (Object.values(sheet.config?.merge ?? {}).some((merge)=>rangesOverlap(bounds, {
178
+ startRow: merge.r,
179
+ endRow: merge.r + merge.rs - 1,
180
+ startColumn: merge.c,
181
+ endColumn: merge.c + merge.cs - 1
182
+ }))) return '范围内包含合并单元格';
183
+ if ('data-table' !== range.type && !anchorCell.f) return '锚点公式已被删除';
184
+ return null;
185
+ }
186
+ function spreadsheetFormulaFunctions(formula) {
187
+ const withoutStrings = formula.replace(/"(?:[^"]|"")*"/g, '""');
188
+ const functions = new Set();
189
+ for (const match of withoutStrings.matchAll(/(?:_xlfn\.)?(?:_xlws\.)?([A-Z][A-Z0-9_.]*)\s*\(/gi))if (match[1]) functions.add(match[1].toUpperCase());
190
+ return Array.from(functions);
191
+ }
192
+ function formulaHasExternalReference(formula) {
193
+ return /\[[^\]]+\][^!]*!/i.test(formula.replace(/"(?:[^"]|"")*"/g, '""'));
194
+ }
195
+ function formulaHasStructuredReference(formula) {
196
+ const withoutExternalReferences = formula.replace(/"(?:[^"]|"")*"/g, '""').replace(/\[[^\]]+\][^!]*!/gi, '');
197
+ return /\[(?:@|#)[^\]]+\]/i.test(withoutExternalReferences) || /\b[A-Z_\\][A-Z0-9_.]*\s*\[[^\]]+\]/i.test(withoutExternalReferences);
198
+ }
199
+ function volatileSpreadsheetFormulaFunctions(formula) {
200
+ return spreadsheetFormulaFunctions(formula).filter((name)=>VOLATILE_FUNCTIONS.has(name));
201
+ }
202
+ function spreadsheetCellAddress(row, column) {
203
+ let value = column + 1;
204
+ let label = '';
205
+ while(value > 0){
206
+ value -= 1;
207
+ label = String.fromCharCode(65 + value % 26) + label;
208
+ value = Math.floor(value / 26);
209
+ }
210
+ return `${label}${row + 1}`;
211
+ }
212
+ function parseSpreadsheetFormulaRange(reference) {
213
+ const normalized = reference.trim().replaceAll('$', '').split('!').at(-1) ?? '';
214
+ const [startText, endText = startText] = normalized.split(':');
215
+ const start = parseSpreadsheetCellAddress(startText);
216
+ const end = parseSpreadsheetCellAddress(endText);
217
+ if (!start || !end) return null;
218
+ return {
219
+ startRow: Math.min(start.row, end.row),
220
+ endRow: Math.max(start.row, end.row),
221
+ startColumn: Math.min(start.column, end.column),
222
+ endColumn: Math.max(start.column, end.column)
223
+ };
224
+ }
225
+ function parseSpreadsheetCellAddress(address) {
226
+ const match = /^([A-Z]{1,3})([1-9]\d*)$/i.exec(address.trim().replaceAll('$', ''));
227
+ if (!match) return null;
228
+ let column = 0;
229
+ for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
230
+ const row = Number(match[2]) - 1;
231
+ if (row > 1048575 || column > 16384) return null;
232
+ return {
233
+ row,
234
+ column: column - 1
235
+ };
236
+ }
237
+ function containsCell(bounds, row, column) {
238
+ return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
239
+ }
240
+ function rangesOverlap(first, second) {
241
+ if (!second) return false;
242
+ return first.startRow <= second.endRow && first.endRow >= second.startRow && first.startColumn <= second.endColumn && first.endColumn >= second.startColumn;
243
+ }
244
+ function comparableFormula(formula) {
245
+ return editableSpreadsheetFormula(formula).trim();
246
+ }
247
+ function futureFunctionPrefixes(formula, sourceFormula) {
248
+ const sourcePrefixes = new Map();
249
+ const sourceFunctions = (sourceFormula ?? '').replace(/"(?:[^"]|"")*"/g, '""');
250
+ for (const match of sourceFunctions.matchAll(/(_xlfn\.(?:_xlws\.)?)([A-Z][A-Z0-9_.]*)\s*(?=\()/gi))if (match[1] && match[2]) sourcePrefixes.set(match[2].toUpperCase(), match[1]);
251
+ return transformFormulaOutsideStrings(formula, (segment)=>segment.replace(/(?<![A-Z0-9_.])([A-Z][A-Z0-9_.]*)\s*(?=\()/gi, (match, functionName)=>{
252
+ const normalized = functionName.toUpperCase();
253
+ const prefix = sourcePrefixes.get(normalized) ?? FUTURE_FUNCTION_PREFIXES.get(normalized);
254
+ return prefix ? `${prefix}${match}` : match;
255
+ }));
256
+ }
257
+ function clampedInteger(value, minimum, maximum, fallback) {
258
+ if (!Number.isFinite(value)) return fallback;
259
+ return Math.min(maximum, Math.max(minimum, Math.round(value)));
260
+ }
261
+ function positiveNumber(value, fallback) {
262
+ return Number.isFinite(value) && value > 0 ? value : fallback;
263
+ }
264
+ function transformFormulaOutsideStrings(formula, transform) {
265
+ return formula.split(/("(?:[^"]|"")*")/).map((segment, index)=>index % 2 ? segment : transform(segment)).join('');
266
+ }
2
267
  const CELL_REFERENCE = /\$?([A-Z]{1,3})\$?([1-9]\d*)/i;
3
268
  const CELL_OR_RANGE = new RegExp(`^${CELL_REFERENCE.source}(?::${CELL_REFERENCE.source})?$`, 'i');
4
269
  const COLUMN_RANGE = /^\$?([A-Z]{1,3}):\$?([A-Z]{1,3})$/i;
@@ -126,79 +391,34 @@ function splitRangeList(value) {
126
391
  if (current.trim()) parts.push(current.trim());
127
392
  return parts;
128
393
  }
129
- function unqualifiedReference(value) {
130
- const reference = value.trim().replace(/^=/, '');
131
- const separator = reference.lastIndexOf('!');
132
- return separator >= 0 ? reference.slice(separator + 1) : reference;
133
- }
134
- function decodeCell(column, row) {
135
- return {
136
- row: Math.max(0, Number(row) - 1),
137
- column: decodeColumn(column)
138
- };
139
- }
140
- function decodeColumn(value) {
141
- let result = 0;
142
- for (const character of value.toUpperCase())result = 26 * result + character.charCodeAt(0) - 64;
143
- return Math.max(0, result - 1);
144
- }
145
- function encodeCell(row, column) {
146
- return `${encodeColumn(column)}${Math.max(0, row) + 1}`;
147
- }
148
- function encodeColumn(column) {
149
- let value = Math.max(0, column) + 1;
150
- let label = '';
151
- while(value > 0){
152
- value -= 1;
153
- label = String.fromCharCode(65 + value % 26) + label;
154
- value = Math.floor(value / 26);
155
- }
156
- return label;
157
- }
158
- function sparseArrayIndexes(values) {
159
- if (!values) return [];
160
- return Object.keys(values).flatMap((key)=>{
161
- const index = Number(key);
162
- return Number.isSafeInteger(index) && index >= 0 && index < values.length ? [
163
- index
164
- ] : [];
165
- });
166
- }
167
- function sparseArrayEntries(values) {
168
- if (!values) return [];
169
- return sparseArrayIndexes(values).flatMap((index)=>{
170
- const value = values[index];
171
- return void 0 === value ? [] : [
172
- [
173
- index,
174
- value
175
- ]
176
- ];
177
- });
178
- }
179
- function sparseMatrixColumnCount(matrix) {
180
- let maximum = 0;
181
- for (const [, row] of sparseArrayEntries(matrix))maximum = Math.max(maximum, row.length);
182
- return maximum;
394
+ function unqualifiedReference(value) {
395
+ const reference = value.trim().replace(/^=/, '');
396
+ const separator = reference.lastIndexOf('!');
397
+ return separator >= 0 ? reference.slice(separator + 1) : reference;
183
398
  }
184
- function spreadsheetGridSize(sheet) {
185
- if (!sheet) return null;
399
+ function decodeCell(column, row) {
186
400
  return {
187
- rowCount: Math.max(sheet.row ?? 0, sheet.data?.length ?? 0),
188
- columnCount: Math.max(sheet.column ?? 0, sparseMatrixColumnCount(sheet.data))
401
+ row: Math.max(0, Number(row) - 1),
402
+ column: decodeColumn(column)
189
403
  };
190
404
  }
191
- function cloneSparseMatrix(source) {
192
- const clone = [];
193
- if (!source) return clone;
194
- clone.length = source.length;
195
- for (const [rowIndex, sourceRow] of sparseArrayEntries(source)){
196
- const row = [];
197
- row.length = sourceRow.length;
198
- for (const columnIndex of sparseArrayIndexes(sourceRow))row[columnIndex] = sourceRow[columnIndex];
199
- clone[rowIndex] = row;
405
+ function decodeColumn(value) {
406
+ let result = 0;
407
+ for (const character of value.toUpperCase())result = 26 * result + character.charCodeAt(0) - 64;
408
+ return Math.max(0, result - 1);
409
+ }
410
+ function encodeCell(row, column) {
411
+ return `${encodeColumn(column)}${Math.max(0, row) + 1}`;
412
+ }
413
+ function encodeColumn(column) {
414
+ let value = Math.max(0, column) + 1;
415
+ let label = '';
416
+ while(value > 0){
417
+ value -= 1;
418
+ label = String.fromCharCode(65 + value % 26) + label;
419
+ value = Math.floor(value / 26);
200
420
  }
201
- return clone;
421
+ return label;
202
422
  }
203
423
  function spreadsheetPivotCellValue(cell) {
204
424
  return cell?.v ?? cell?.m ?? null;
@@ -723,13 +943,13 @@ function spreadsheetPivotValidation(content, ownerSheetId, pivot) {
723
943
  function spreadsheetPivotOutputContains(sheet, row, column) {
724
944
  return (sheet.pivotTables ?? []).some((pivot)=>{
725
945
  const bounds = singleRange(pivot.outputReference ?? '');
726
- return Boolean(bounds && containsCell(bounds, row, column));
946
+ return Boolean(bounds && work_spreadsheet_pivots_containsCell(bounds, row, column));
727
947
  });
728
948
  }
729
949
  function spreadsheetPivotIntersects(sheet, range) {
730
950
  return (sheet.pivotTables ?? []).filter((pivot)=>{
731
951
  const bounds = singleRange(pivot.outputReference ?? '');
732
- return Boolean(bounds && rangesOverlap(bounds, range));
952
+ return Boolean(bounds && work_spreadsheet_pivots_rangesOverlap(bounds, range));
733
953
  });
734
954
  }
735
955
  function resolvePivot(content, ownerSheetId, pivot) {
@@ -751,14 +971,14 @@ function resolvePivot(content, ownerSheetId, pivot) {
751
971
  const outputReference = formatBounds(outputBounds);
752
972
  if (output.length * output[0].length > MAXIMUM_OUTPUT_CELLS) return null;
753
973
  if (outputBounds.endRow > MAXIMUM_XLSX_ROW || outputBounds.endColumn > MAXIMUM_XLSX_COLUMN) return null;
754
- if (ownerSheet.id === sourceSheet.id && rangesOverlap(sourceBounds, outputBounds)) return null;
974
+ if (ownerSheet.id === sourceSheet.id && work_spreadsheet_pivots_rangesOverlap(sourceBounds, outputBounds)) return null;
755
975
  const oldOutput = singleRange(pivot.outputReference ?? '');
756
976
  for (const other of ownerSheet.pivotTables ?? []){
757
977
  if (other.id === pivot.id) continue;
758
978
  const otherOutput = singleRange(other.outputReference ?? '');
759
- if (otherOutput && rangesOverlap(otherOutput, outputBounds)) return null;
979
+ if (otherOutput && work_spreadsheet_pivots_rangesOverlap(otherOutput, outputBounds)) return null;
760
980
  }
761
- for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && containsCell(oldOutput, row, column))) {
981
+ for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && work_spreadsheet_pivots_containsCell(oldOutput, row, column))) {
762
982
  if (ownerSheet.data?.[row]?.[column]) return null;
763
983
  }
764
984
  return {
@@ -805,8 +1025,8 @@ function pivotFailure(content, ownerSheetId, pivot) {
805
1025
  endColumn: anchor.startColumn + output[0].length - 1
806
1026
  };
807
1027
  if (outputBounds.endRow > MAXIMUM_XLSX_ROW || outputBounds.endColumn > MAXIMUM_XLSX_COLUMN) return invalid('pivot.output-out-of-bounds', '透视表结果超出 XLSX 工作表边界。');
808
- if (ownerSheet.id === sourceSheet.id && rangesOverlap(sourceBounds, outputBounds)) return invalid('pivot.output-overlaps-source', '透视表输出区域与源数据重叠,请改用其他位置或工作表。');
809
- if (Object.values(ownerSheet.config?.merge ?? {}).some((merge)=>rangesOverlap(outputBounds, {
1028
+ if (ownerSheet.id === sourceSheet.id && work_spreadsheet_pivots_rangesOverlap(sourceBounds, outputBounds)) return invalid('pivot.output-overlaps-source', '透视表输出区域与源数据重叠,请改用其他位置或工作表。');
1029
+ if (Object.values(ownerSheet.config?.merge ?? {}).some((merge)=>work_spreadsheet_pivots_rangesOverlap(outputBounds, {
810
1030
  startRow: merge.r,
811
1031
  endRow: merge.r + merge.rs - 1,
812
1032
  startColumn: merge.c,
@@ -816,9 +1036,9 @@ function pivotFailure(content, ownerSheetId, pivot) {
816
1036
  for (const other of ownerSheet.pivotTables ?? []){
817
1037
  if (other.id === pivot.id) continue;
818
1038
  const otherOutput = singleRange(other.outputReference ?? '');
819
- if (otherOutput && rangesOverlap(otherOutput, outputBounds)) return invalid('pivot.output-overlaps-pivot', '透视表输出区域与另一个透视表重叠。');
1039
+ if (otherOutput && work_spreadsheet_pivots_rangesOverlap(otherOutput, outputBounds)) return invalid('pivot.output-overlaps-pivot', '透视表输出区域与另一个透视表重叠。');
820
1040
  }
821
- for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && containsCell(oldOutput, row, column))) {
1041
+ for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && work_spreadsheet_pivots_containsCell(oldOutput, row, column))) {
822
1042
  if (ownerSheet.data?.[row]?.[column]) return invalid('pivot.output-not-empty', '透视表输出区域包含现有内容,请选择空白位置。');
823
1043
  }
824
1044
  return null;
@@ -846,10 +1066,10 @@ function selectionBounds(selection) {
846
1066
  endColumn: Math.max(0, Math.max(selection.column[0], selection.column[1]))
847
1067
  };
848
1068
  }
849
- function containsCell(bounds, row, column) {
1069
+ function work_spreadsheet_pivots_containsCell(bounds, row, column) {
850
1070
  return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
851
1071
  }
852
- function rangesOverlap(left, right) {
1072
+ function work_spreadsheet_pivots_rangesOverlap(left, right) {
853
1073
  return left.startRow <= right.endRow && left.endRow >= right.startRow && left.startColumn <= right.endColumn && left.endColumn >= right.startColumn;
854
1074
  }
855
1075
  function formatBounds(bounds) {
@@ -905,226 +1125,6 @@ function invalid(code, message) {
905
1125
  message
906
1126
  };
907
1127
  }
908
- const DEFAULT_SPREADSHEET_CALCULATION_SETTINGS = {
909
- mode: 'automatic',
910
- fullCalculationOnLoad: false,
911
- forceFullCalculation: false,
912
- iterativeCalculation: false,
913
- maximumIterations: 100,
914
- maximumChange: 0.001,
915
- fullPrecision: true
916
- };
917
- const VOLATILE_FUNCTIONS = new Set([
918
- 'CELL',
919
- 'INFO',
920
- 'INDIRECT',
921
- 'NOW',
922
- 'OFFSET',
923
- 'RAND',
924
- 'RANDBETWEEN',
925
- 'TODAY'
926
- ]);
927
- const FUTURE_FUNCTIONS = new Set([
928
- 'ARRAYTOTEXT',
929
- 'BYCOL',
930
- 'BYROW',
931
- 'CHOOSECOLS',
932
- 'CHOOSEROWS',
933
- 'DROP',
934
- 'EXPAND',
935
- 'FIELDVALUE',
936
- 'FILTER',
937
- 'HSTACK',
938
- 'IMAGE',
939
- 'ISOMITTED',
940
- 'LAMBDA',
941
- 'LET',
942
- 'MAKEARRAY',
943
- 'MAP',
944
- 'RANDARRAY',
945
- 'REDUCE',
946
- 'SCAN',
947
- 'SEQUENCE',
948
- 'SORT',
949
- 'SORTBY',
950
- 'STOCKHISTORY',
951
- 'TAKE',
952
- 'TEXTAFTER',
953
- 'TEXTBEFORE',
954
- 'TEXTSPLIT',
955
- 'TOCOL',
956
- 'TOROW',
957
- 'UNIQUE',
958
- 'VALUETOTEXT',
959
- 'VSTACK',
960
- 'WRAPCOLS',
961
- 'WRAPROWS',
962
- 'XLOOKUP',
963
- 'XMATCH'
964
- ]);
965
- const FUTURE_FUNCTION_PREFIXES = new Map([
966
- [
967
- 'FILTER',
968
- '_xlfn._xlws.'
969
- ],
970
- [
971
- 'SORT',
972
- '_xlfn._xlws.'
973
- ],
974
- [
975
- 'SORTBY',
976
- '_xlfn._xlws.'
977
- ],
978
- [
979
- 'UNIQUE',
980
- '_xlfn.'
981
- ],
982
- ...Array.from(FUTURE_FUNCTIONS).filter((name)=>![
983
- 'FILTER',
984
- 'SORT',
985
- 'SORTBY',
986
- 'UNIQUE'
987
- ].includes(name)).map((name)=>[
988
- name,
989
- '_xlfn.'
990
- ])
991
- ]);
992
- function effectiveSpreadsheetCalculationSettings(settings) {
993
- if (!settings) return {
994
- ...DEFAULT_SPREADSHEET_CALCULATION_SETTINGS
995
- };
996
- return {
997
- mode: settings.mode,
998
- fullCalculationOnLoad: Boolean(settings.fullCalculationOnLoad),
999
- forceFullCalculation: Boolean(settings.forceFullCalculation),
1000
- iterativeCalculation: Boolean(settings.iterativeCalculation),
1001
- maximumIterations: clampedInteger(settings.maximumIterations, 1, 10000, 100),
1002
- maximumChange: positiveNumber(settings.maximumChange, 0.001),
1003
- fullPrecision: false !== settings.fullPrecision
1004
- };
1005
- }
1006
- function editableSpreadsheetFormula(source) {
1007
- return transformFormulaOutsideStrings(source, (segment)=>segment.replace(/_xlfn\._xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlfn\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, ''));
1008
- }
1009
- function spreadsheetFormulaForXlsx(currentFormula, sourceFormula) {
1010
- const current = currentFormula.trim().replace(/^=/, '');
1011
- const source = sourceFormula?.trim().replace(/^=/, '');
1012
- if (source && comparableFormula(source) === comparableFormula(current)) return source;
1013
- return futureFunctionPrefixes(current, source);
1014
- }
1015
- function spreadsheetFormulaRangeForCell(sheet, row, column) {
1016
- const address = spreadsheetCellAddress(row, column);
1017
- return (sheet.formulaMetadata?.ranges ?? []).find((range)=>range.anchor.toUpperCase().replaceAll('$', '') === address);
1018
- }
1019
- function spreadsheetFormulaRangesForSelection(sheet, selection) {
1020
- return (sheet.formulaMetadata?.ranges ?? []).filter((range)=>work_spreadsheet_formulas_rangesOverlap(selection, parseSpreadsheetFormulaRange(range.reference)));
1021
- }
1022
- function spreadsheetFormulaRangeConflict(sheet, range) {
1023
- const bounds = parseSpreadsheetFormulaRange(range.reference);
1024
- if (!bounds) return '引用范围无效';
1025
- const anchor = parseSpreadsheetCellAddress(range.anchor);
1026
- if (!anchor || !work_spreadsheet_formulas_containsCell(bounds, anchor.row, anchor.column)) return '锚点不在引用范围内';
1027
- const anchorCell = sheet.data?.[anchor.row]?.[anchor.column];
1028
- if (!anchorCell) return '锚点单元格不存在';
1029
- const overlaps = (sheet.formulaMetadata?.ranges ?? []).filter((candidate)=>candidate !== range && work_spreadsheet_formulas_rangesOverlap(bounds, parseSpreadsheetFormulaRange(candidate.reference)));
1030
- if (overlaps.length) return '与其他公式范围重叠';
1031
- for (const [row, cells] of sparseArrayEntries(sheet.data))if (!(row < bounds.startRow) && !(row > bounds.endRow)) {
1032
- for (const [column, cell] of sparseArrayEntries(cells))if (!(column < bounds.startColumn) && !(column > bounds.endColumn)) {
1033
- if (row !== anchor.row || column !== anchor.column) {
1034
- if (cell?.f) return `${spreadsheetCellAddress(row, column)} 包含独立公式`;
1035
- }
1036
- }
1037
- }
1038
- if (Object.values(sheet.config?.merge ?? {}).some((merge)=>work_spreadsheet_formulas_rangesOverlap(bounds, {
1039
- startRow: merge.r,
1040
- endRow: merge.r + merge.rs - 1,
1041
- startColumn: merge.c,
1042
- endColumn: merge.c + merge.cs - 1
1043
- }))) return '范围内包含合并单元格';
1044
- if ('data-table' !== range.type && !anchorCell.f) return '锚点公式已被删除';
1045
- return null;
1046
- }
1047
- function spreadsheetFormulaFunctions(formula) {
1048
- const withoutStrings = formula.replace(/"(?:[^"]|"")*"/g, '""');
1049
- const functions = new Set();
1050
- for (const match of withoutStrings.matchAll(/(?:_xlfn\.)?(?:_xlws\.)?([A-Z][A-Z0-9_.]*)\s*\(/gi))if (match[1]) functions.add(match[1].toUpperCase());
1051
- return Array.from(functions);
1052
- }
1053
- function formulaHasExternalReference(formula) {
1054
- return /\[[^\]]+\][^!]*!/i.test(formula.replace(/"(?:[^"]|"")*"/g, '""'));
1055
- }
1056
- function formulaHasStructuredReference(formula) {
1057
- const withoutExternalReferences = formula.replace(/"(?:[^"]|"")*"/g, '""').replace(/\[[^\]]+\][^!]*!/gi, '');
1058
- return /\[(?:@|#)[^\]]+\]/i.test(withoutExternalReferences) || /\b[A-Z_\\][A-Z0-9_.]*\s*\[[^\]]+\]/i.test(withoutExternalReferences);
1059
- }
1060
- function volatileSpreadsheetFormulaFunctions(formula) {
1061
- return spreadsheetFormulaFunctions(formula).filter((name)=>VOLATILE_FUNCTIONS.has(name));
1062
- }
1063
- function spreadsheetCellAddress(row, column) {
1064
- let value = column + 1;
1065
- let label = '';
1066
- while(value > 0){
1067
- value -= 1;
1068
- label = String.fromCharCode(65 + value % 26) + label;
1069
- value = Math.floor(value / 26);
1070
- }
1071
- return `${label}${row + 1}`;
1072
- }
1073
- function parseSpreadsheetFormulaRange(reference) {
1074
- const normalized = reference.trim().replaceAll('$', '').split('!').at(-1) ?? '';
1075
- const [startText, endText = startText] = normalized.split(':');
1076
- const start = parseSpreadsheetCellAddress(startText);
1077
- const end = parseSpreadsheetCellAddress(endText);
1078
- if (!start || !end) return null;
1079
- return {
1080
- startRow: Math.min(start.row, end.row),
1081
- endRow: Math.max(start.row, end.row),
1082
- startColumn: Math.min(start.column, end.column),
1083
- endColumn: Math.max(start.column, end.column)
1084
- };
1085
- }
1086
- function parseSpreadsheetCellAddress(address) {
1087
- const match = /^([A-Z]{1,3})([1-9]\d*)$/i.exec(address.trim().replaceAll('$', ''));
1088
- if (!match) return null;
1089
- let column = 0;
1090
- for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
1091
- const row = Number(match[2]) - 1;
1092
- if (row > 1048575 || column > 16384) return null;
1093
- return {
1094
- row,
1095
- column: column - 1
1096
- };
1097
- }
1098
- function work_spreadsheet_formulas_containsCell(bounds, row, column) {
1099
- return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
1100
- }
1101
- function work_spreadsheet_formulas_rangesOverlap(first, second) {
1102
- if (!second) return false;
1103
- return first.startRow <= second.endRow && first.endRow >= second.startRow && first.startColumn <= second.endColumn && first.endColumn >= second.startColumn;
1104
- }
1105
- function comparableFormula(formula) {
1106
- return editableSpreadsheetFormula(formula).trim();
1107
- }
1108
- function futureFunctionPrefixes(formula, sourceFormula) {
1109
- const sourcePrefixes = new Map();
1110
- const sourceFunctions = (sourceFormula ?? '').replace(/"(?:[^"]|"")*"/g, '""');
1111
- for (const match of sourceFunctions.matchAll(/(_xlfn\.(?:_xlws\.)?)([A-Z][A-Z0-9_.]*)\s*(?=\()/gi))if (match[1] && match[2]) sourcePrefixes.set(match[2].toUpperCase(), match[1]);
1112
- return transformFormulaOutsideStrings(formula, (segment)=>segment.replace(/(?<![A-Z0-9_.])([A-Z][A-Z0-9_.]*)\s*(?=\()/gi, (match, functionName)=>{
1113
- const normalized = functionName.toUpperCase();
1114
- const prefix = sourcePrefixes.get(normalized) ?? FUTURE_FUNCTION_PREFIXES.get(normalized);
1115
- return prefix ? `${prefix}${match}` : match;
1116
- }));
1117
- }
1118
- function clampedInteger(value, minimum, maximum, fallback) {
1119
- if (!Number.isFinite(value)) return fallback;
1120
- return Math.min(maximum, Math.max(minimum, Math.round(value)));
1121
- }
1122
- function positiveNumber(value, fallback) {
1123
- return Number.isFinite(value) && value > 0 ? value : fallback;
1124
- }
1125
- function transformFormulaOutsideStrings(formula, transform) {
1126
- return formula.split(/("(?:[^"]|"")*")/).map((segment, index)=>index % 2 ? segment : transform(segment)).join('');
1127
- }
1128
1128
  const XLSX_EMU_PER_PIXEL = 9525;
1129
1129
  const DEFAULT_COLUMN_WIDTH = 96;
1130
1130
  const DEFAULT_ROW_HEIGHT = 24;