@devmm/puredocs-excel 1.0.6 → 1.0.7
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/index.cjs +909 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -2
- package/dist/index.d.ts +84 -2
- package/dist/index.js +910 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { unzipSync, zipSync } from 'fflate';
|
|
1
|
+
import { unzipSync, zipSync, deflateSync, deflate } from 'fflate';
|
|
2
2
|
|
|
3
3
|
var __typeError = (msg) => {
|
|
4
4
|
throw TypeError(msg);
|
|
@@ -194,6 +194,124 @@ function listEntries(entries, prefix) {
|
|
|
194
194
|
}
|
|
195
195
|
return results.sort();
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
// src/io/zip-container.ts
|
|
199
|
+
var LOCAL_HEADER = 67324752;
|
|
200
|
+
var CENTRAL_HEADER = 33639248;
|
|
201
|
+
var END_OF_CENTRAL_DIR = 101010256;
|
|
202
|
+
var DOS_DATE = 33;
|
|
203
|
+
var DOS_TIME = 0;
|
|
204
|
+
var MAX_ENTRIES = 65535;
|
|
205
|
+
var MAX_BYTES = 4294967295;
|
|
206
|
+
var CRC_TABLE = (() => {
|
|
207
|
+
const table = new Int32Array(256);
|
|
208
|
+
for (let i = 0; i < 256; i++) {
|
|
209
|
+
let c = i;
|
|
210
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
211
|
+
table[i] = c;
|
|
212
|
+
}
|
|
213
|
+
return table;
|
|
214
|
+
})();
|
|
215
|
+
function crc32(data) {
|
|
216
|
+
let c = -1;
|
|
217
|
+
for (let i = 0; i < data.length; i++) {
|
|
218
|
+
c = c >>> 8 ^ CRC_TABLE[(c ^ data[i]) & 255];
|
|
219
|
+
}
|
|
220
|
+
return (c ^ -1) >>> 0;
|
|
221
|
+
}
|
|
222
|
+
function exceedsZipLimits(parts) {
|
|
223
|
+
if (parts.length > MAX_ENTRIES) return true;
|
|
224
|
+
let total = 0;
|
|
225
|
+
for (const p of parts) {
|
|
226
|
+
total += 30 + utf8Length(p.name) * 2 + 46 + p.deflated.length;
|
|
227
|
+
if (total > MAX_BYTES || p.rawSize > MAX_BYTES) return true;
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
function buildZipContainer(parts) {
|
|
232
|
+
const names = parts.map((p) => encodeName(p.name));
|
|
233
|
+
let localSize = 0;
|
|
234
|
+
let centralSize = 0;
|
|
235
|
+
for (let i = 0; i < parts.length; i++) {
|
|
236
|
+
localSize += 30 + names[i].bytes.length + parts[i].deflated.length;
|
|
237
|
+
centralSize += 46 + names[i].bytes.length;
|
|
238
|
+
}
|
|
239
|
+
const out = new Uint8Array(localSize + centralSize + 22);
|
|
240
|
+
const view = new DataView(out.buffer);
|
|
241
|
+
const offsets = [];
|
|
242
|
+
let at = 0;
|
|
243
|
+
for (let i = 0; i < parts.length; i++) {
|
|
244
|
+
const part = parts[i];
|
|
245
|
+
const name = names[i];
|
|
246
|
+
offsets.push(at);
|
|
247
|
+
view.setUint32(at, LOCAL_HEADER, true);
|
|
248
|
+
view.setUint16(at + 4, 20, true);
|
|
249
|
+
view.setUint16(at + 6, name.utf8Flag, true);
|
|
250
|
+
view.setUint16(at + 8, 8, true);
|
|
251
|
+
view.setUint16(at + 10, DOS_TIME, true);
|
|
252
|
+
view.setUint16(at + 12, DOS_DATE, true);
|
|
253
|
+
view.setUint32(at + 14, part.crc, true);
|
|
254
|
+
view.setUint32(at + 18, part.deflated.length, true);
|
|
255
|
+
view.setUint32(at + 22, part.rawSize, true);
|
|
256
|
+
view.setUint16(at + 26, name.bytes.length, true);
|
|
257
|
+
view.setUint16(at + 28, 0, true);
|
|
258
|
+
at += 30;
|
|
259
|
+
out.set(name.bytes, at);
|
|
260
|
+
at += name.bytes.length;
|
|
261
|
+
out.set(part.deflated, at);
|
|
262
|
+
at += part.deflated.length;
|
|
263
|
+
}
|
|
264
|
+
const centralStart = at;
|
|
265
|
+
for (let i = 0; i < parts.length; i++) {
|
|
266
|
+
const part = parts[i];
|
|
267
|
+
const name = names[i];
|
|
268
|
+
view.setUint32(at, CENTRAL_HEADER, true);
|
|
269
|
+
view.setUint16(at + 4, 20, true);
|
|
270
|
+
view.setUint16(at + 6, 20, true);
|
|
271
|
+
view.setUint16(at + 8, name.utf8Flag, true);
|
|
272
|
+
view.setUint16(at + 10, 8, true);
|
|
273
|
+
view.setUint16(at + 12, DOS_TIME, true);
|
|
274
|
+
view.setUint16(at + 14, DOS_DATE, true);
|
|
275
|
+
view.setUint32(at + 16, part.crc, true);
|
|
276
|
+
view.setUint32(at + 20, part.deflated.length, true);
|
|
277
|
+
view.setUint32(at + 24, part.rawSize, true);
|
|
278
|
+
view.setUint16(at + 28, name.bytes.length, true);
|
|
279
|
+
view.setUint16(at + 30, 0, true);
|
|
280
|
+
view.setUint16(at + 32, 0, true);
|
|
281
|
+
view.setUint16(at + 34, 0, true);
|
|
282
|
+
view.setUint16(at + 36, 0, true);
|
|
283
|
+
view.setUint32(at + 38, 0, true);
|
|
284
|
+
view.setUint32(at + 42, offsets[i], true);
|
|
285
|
+
at += 46;
|
|
286
|
+
out.set(name.bytes, at);
|
|
287
|
+
at += name.bytes.length;
|
|
288
|
+
}
|
|
289
|
+
view.setUint32(at, END_OF_CENTRAL_DIR, true);
|
|
290
|
+
view.setUint16(at + 4, 0, true);
|
|
291
|
+
view.setUint16(at + 6, 0, true);
|
|
292
|
+
view.setUint16(at + 8, parts.length, true);
|
|
293
|
+
view.setUint16(at + 10, parts.length, true);
|
|
294
|
+
view.setUint32(at + 12, at - centralStart, true);
|
|
295
|
+
view.setUint32(at + 16, centralStart, true);
|
|
296
|
+
view.setUint16(at + 20, 0, true);
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
function encodeName(name) {
|
|
300
|
+
const bytes = new TextEncoder().encode(name);
|
|
301
|
+
let ascii = true;
|
|
302
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
303
|
+
if (bytes[i] > 127) {
|
|
304
|
+
ascii = false;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return { bytes, utf8Flag: ascii ? 0 : 2048 };
|
|
309
|
+
}
|
|
310
|
+
function utf8Length(name) {
|
|
311
|
+
return new TextEncoder().encode(name).length;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/io/zip-writer.ts
|
|
197
315
|
function zipXlsx(entries) {
|
|
198
316
|
const zippable = {};
|
|
199
317
|
for (const [path, content] of entries) {
|
|
@@ -208,6 +326,95 @@ function zipXlsx(entries) {
|
|
|
208
326
|
);
|
|
209
327
|
}
|
|
210
328
|
}
|
|
329
|
+
var ASYNC_MIN_BYTES = 512 * 1024;
|
|
330
|
+
function createDeflateCache() {
|
|
331
|
+
return /* @__PURE__ */ new Map();
|
|
332
|
+
}
|
|
333
|
+
function compress(name, source, bytes) {
|
|
334
|
+
return {
|
|
335
|
+
name,
|
|
336
|
+
source,
|
|
337
|
+
deflated: deflateSync(bytes, { level: 6 }),
|
|
338
|
+
crc: crc32(bytes),
|
|
339
|
+
rawSize: bytes.length
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function zipXlsxCached(entries, cache) {
|
|
343
|
+
const parts = collectParts(entries, cache);
|
|
344
|
+
pruneCache(entries, cache);
|
|
345
|
+
return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
|
|
346
|
+
}
|
|
347
|
+
async function zipXlsxCachedAsync(entries, cache) {
|
|
348
|
+
const order = [];
|
|
349
|
+
const ready = /* @__PURE__ */ new Map();
|
|
350
|
+
const jobs = [];
|
|
351
|
+
const todo = [];
|
|
352
|
+
let todoBytes = 0;
|
|
353
|
+
for (const [path, content] of entries) {
|
|
354
|
+
order.push(path);
|
|
355
|
+
const cached = cache.get(path);
|
|
356
|
+
if (cached && cached.source === content) {
|
|
357
|
+
ready.set(path, cached);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const bytes = typeof content === "string" ? encodeUtf8(content) : content;
|
|
361
|
+
todo.push({ path, content, bytes });
|
|
362
|
+
todoBytes += bytes.length;
|
|
363
|
+
}
|
|
364
|
+
if (todoBytes <= ASYNC_MIN_BYTES) {
|
|
365
|
+
for (const { path, content, bytes } of todo) {
|
|
366
|
+
const compressed = compress(path, content, bytes);
|
|
367
|
+
cache.set(path, compressed);
|
|
368
|
+
ready.set(path, compressed);
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
for (const { path, content, bytes } of todo) {
|
|
372
|
+
jobs.push(deflateAsync(bytes).then((deflated) => ({
|
|
373
|
+
name: path,
|
|
374
|
+
source: content,
|
|
375
|
+
deflated,
|
|
376
|
+
crc: crc32(bytes),
|
|
377
|
+
rawSize: bytes.length
|
|
378
|
+
})));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
for (const part of await Promise.all(jobs)) {
|
|
382
|
+
cache.set(part.name, part);
|
|
383
|
+
ready.set(part.name, part);
|
|
384
|
+
}
|
|
385
|
+
pruneCache(entries, cache);
|
|
386
|
+
const parts = order.map((path) => ready.get(path));
|
|
387
|
+
return exceedsZipLimits(parts) ? zipXlsx(entries) : buildZipContainer(parts);
|
|
388
|
+
}
|
|
389
|
+
function deflateAsync(bytes) {
|
|
390
|
+
return new Promise((resolve, reject) => {
|
|
391
|
+
deflate(bytes, { level: 6 }, (err, data) => {
|
|
392
|
+
if (err) reject(new Error(`Failed to create xlsx: ${err.message}`));
|
|
393
|
+
else resolve(data);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function collectParts(entries, cache) {
|
|
398
|
+
const parts = [];
|
|
399
|
+
for (const [path, content] of entries) {
|
|
400
|
+
const cached = cache.get(path);
|
|
401
|
+
if (cached && cached.source === content) {
|
|
402
|
+
parts.push(cached);
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const bytes = typeof content === "string" ? encodeUtf8(content) : content;
|
|
406
|
+
const part = compress(path, content, bytes);
|
|
407
|
+
cache.set(path, part);
|
|
408
|
+
parts.push(part);
|
|
409
|
+
}
|
|
410
|
+
return parts;
|
|
411
|
+
}
|
|
412
|
+
function pruneCache(entries, cache) {
|
|
413
|
+
if (cache.size === entries.size) return;
|
|
414
|
+
for (const path of [...cache.keys()]) {
|
|
415
|
+
if (!entries.has(path)) cache.delete(path);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
211
418
|
function setXmlEntry(entries, path, xml2) {
|
|
212
419
|
entries.set(path, xml2);
|
|
213
420
|
}
|
|
@@ -300,7 +507,8 @@ var REL_TYPES = {
|
|
|
300
507
|
drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
301
508
|
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
302
509
|
comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
|
303
|
-
vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
|
510
|
+
vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
|
|
511
|
+
calcChain: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain"
|
|
304
512
|
};
|
|
305
513
|
var CONTENT_TYPES = {
|
|
306
514
|
workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
@@ -312,7 +520,8 @@ var CONTENT_TYPES = {
|
|
|
312
520
|
relationships: "application/vnd.openxmlformats-package.relationships+xml",
|
|
313
521
|
coreProperties: "application/vnd.openxmlformats-package.core-properties+xml",
|
|
314
522
|
comments: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
|
315
|
-
vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
523
|
+
vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing",
|
|
524
|
+
calcChain: "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"
|
|
316
525
|
};
|
|
317
526
|
|
|
318
527
|
// src/io/ooxml-templates.ts
|
|
@@ -345,18 +554,20 @@ var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
|
345
554
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
346
555
|
<Relationship Id="rId1" Type="${REL_TYPES.officeDocument}" Target="xl/workbook.xml"/>
|
|
347
556
|
</Relationships>`;
|
|
348
|
-
function buildWorkbookRels(sheetCount) {
|
|
557
|
+
function buildWorkbookRels(sheetCount, includeCalcChain = false) {
|
|
349
558
|
const sheetRels = Array.from(
|
|
350
559
|
{ length: sheetCount },
|
|
351
560
|
(_, i) => ` <Relationship Id="rId${i + 1}" Type="${REL_TYPES.worksheet}" Target="worksheets/sheet${i + 1}.xml"/>`
|
|
352
561
|
).join("\n");
|
|
353
562
|
const stylesId = sheetCount + 1;
|
|
354
563
|
const sharedId = sheetCount + 2;
|
|
564
|
+
const calcChainRel = includeCalcChain ? `
|
|
565
|
+
<Relationship Id="rId${sheetCount + 3}" Type="${REL_TYPES.calcChain}" Target="calcChain.xml"/>` : "";
|
|
355
566
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
356
567
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
357
568
|
${sheetRels}
|
|
358
569
|
<Relationship Id="rId${stylesId}" Type="${REL_TYPES.styles}" Target="styles.xml"/>
|
|
359
|
-
<Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"
|
|
570
|
+
<Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"/>${calcChainRel}
|
|
360
571
|
</Relationships>`;
|
|
361
572
|
}
|
|
362
573
|
function buildWorkbookXml(sheets, definedNames = []) {
|
|
@@ -961,8 +1172,12 @@ var ExcelBorderEdge = class _ExcelBorderEdge {
|
|
|
961
1172
|
cacheKey() {
|
|
962
1173
|
return `${this.style}:${this.color?.toString() ?? "null"}`;
|
|
963
1174
|
}
|
|
1175
|
+
/** An independent copy. ExcelColor is immutable, so it is shared. */
|
|
1176
|
+
clone() {
|
|
1177
|
+
return new _ExcelBorderEdge(this.style, this.color);
|
|
1178
|
+
}
|
|
964
1179
|
};
|
|
965
|
-
var
|
|
1180
|
+
var ExcelBorder = class _ExcelBorder {
|
|
966
1181
|
constructor() {
|
|
967
1182
|
this.left = new ExcelBorderEdge();
|
|
968
1183
|
this.right = new ExcelBorderEdge();
|
|
@@ -1002,6 +1217,33 @@ var _ExcelBorder = class _ExcelBorder {
|
|
|
1002
1217
|
b.right = new ExcelBorderEdge(style, color);
|
|
1003
1218
|
return b;
|
|
1004
1219
|
}
|
|
1220
|
+
/**
|
|
1221
|
+
* A border with no edges set.
|
|
1222
|
+
*
|
|
1223
|
+
* A fresh instance per access, not a shared singleton. `ExcelBorder` is mutable
|
|
1224
|
+
* (`border.left.style = …` is the normal way to use it), so a shared instance
|
|
1225
|
+
* handed to two styles let an edit through one of them reach the other — and,
|
|
1226
|
+
* once anything had assigned `none` and then modified it, every later
|
|
1227
|
+
* `ExcelBorder.none` came back already carrying that edge.
|
|
1228
|
+
*/
|
|
1229
|
+
static get none() {
|
|
1230
|
+
return new _ExcelBorder();
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* A deep copy: the edges are new objects, so mutating the copy cannot reach the
|
|
1234
|
+
* original. {@link CellStyle.clone} relies on this.
|
|
1235
|
+
*/
|
|
1236
|
+
clone() {
|
|
1237
|
+
const b = new _ExcelBorder();
|
|
1238
|
+
b.left = this.left.clone();
|
|
1239
|
+
b.right = this.right.clone();
|
|
1240
|
+
b.top = this.top.clone();
|
|
1241
|
+
b.bottom = this.bottom.clone();
|
|
1242
|
+
b.diagonal = this.diagonal.clone();
|
|
1243
|
+
b.diagonalDown = this.diagonalDown;
|
|
1244
|
+
b.diagonalUp = this.diagonalUp;
|
|
1245
|
+
return b;
|
|
1246
|
+
}
|
|
1005
1247
|
// ── Serialisation ─────────────────────────────────────────────────────────
|
|
1006
1248
|
toXml() {
|
|
1007
1249
|
const dd = this.diagonalDown ? ' diagonalDown="1"' : "";
|
|
@@ -1047,8 +1289,6 @@ var _ExcelBorder = class _ExcelBorder {
|
|
|
1047
1289
|
].join("|");
|
|
1048
1290
|
}
|
|
1049
1291
|
};
|
|
1050
|
-
_ExcelBorder.none = new _ExcelBorder();
|
|
1051
|
-
var ExcelBorder = _ExcelBorder;
|
|
1052
1292
|
|
|
1053
1293
|
// src/style/excel-alignment.ts
|
|
1054
1294
|
var ExcelHorizontalAlignment = /* @__PURE__ */ ((ExcelHorizontalAlignment3) => {
|
|
@@ -1341,7 +1581,7 @@ var _CellStyle = class _CellStyle {
|
|
|
1341
1581
|
const s = new _CellStyle();
|
|
1342
1582
|
if (this.font) s.font = this.font.clone();
|
|
1343
1583
|
s.fill = this.fill;
|
|
1344
|
-
s.border = this.border;
|
|
1584
|
+
s.border = this.border?.clone();
|
|
1345
1585
|
s.alignment = this.alignment ? Object.assign(new ExcelAlignment(), this.alignment) : void 0;
|
|
1346
1586
|
s.numberFormat = this.numberFormat;
|
|
1347
1587
|
s.isLocked = this.isLocked;
|
|
@@ -1364,8 +1604,46 @@ ensureAlignment_fn = function() {
|
|
|
1364
1604
|
};
|
|
1365
1605
|
var CellStyle = _CellStyle;
|
|
1366
1606
|
|
|
1607
|
+
// src/model/oa-date.ts
|
|
1608
|
+
var OA_EPOCH_MS = Date.UTC(1899, 11, 30, 0, 0, 0, 0);
|
|
1609
|
+
var MS_PER_DAY = 864e5;
|
|
1610
|
+
function toOADate(date) {
|
|
1611
|
+
const utcMs = Date.UTC(
|
|
1612
|
+
date.getFullYear(),
|
|
1613
|
+
date.getMonth(),
|
|
1614
|
+
date.getDate(),
|
|
1615
|
+
date.getHours(),
|
|
1616
|
+
date.getMinutes(),
|
|
1617
|
+
date.getSeconds(),
|
|
1618
|
+
date.getMilliseconds()
|
|
1619
|
+
);
|
|
1620
|
+
return (utcMs - OA_EPOCH_MS) / MS_PER_DAY;
|
|
1621
|
+
}
|
|
1622
|
+
function fromOADate(oaDate) {
|
|
1623
|
+
const ms = OA_EPOCH_MS + oaDate * MS_PER_DAY;
|
|
1624
|
+
const d = new Date(ms);
|
|
1625
|
+
return new Date(
|
|
1626
|
+
d.getUTCFullYear(),
|
|
1627
|
+
d.getUTCMonth(),
|
|
1628
|
+
d.getUTCDate(),
|
|
1629
|
+
d.getUTCHours(),
|
|
1630
|
+
d.getUTCMinutes(),
|
|
1631
|
+
d.getUTCSeconds(),
|
|
1632
|
+
d.getUTCMilliseconds()
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
|
|
1636
|
+
function isDateFormatId(formatId) {
|
|
1637
|
+
return DATE_FORMAT_IDS.has(formatId);
|
|
1638
|
+
}
|
|
1639
|
+
function isDateFormatCode(formatCode) {
|
|
1640
|
+
const lower = formatCode.toLowerCase();
|
|
1641
|
+
return lower.includes("y") || lower.includes("d") || lower.includes("m") && !lower.includes("mm:") || // m = month unless part of mm:ss
|
|
1642
|
+
lower.includes("h") || lower.includes("am") || lower.includes("pm");
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1367
1645
|
// src/style/style-manager.ts
|
|
1368
|
-
var _StyleManager_instances, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
|
|
1646
|
+
var _numFmtMapCache, _dateStyleMemo, _StyleManager_instances, numFmtMap_fn, invalidateFormatCaches_fn, initDefaults_fn, getOrCreateFont_fn, getOrCreateFill_fn, getOrCreateBorder_fn, getOrCreateNumFmt_fn, cellFormatKey_fn, cfToXml_fn;
|
|
1369
1647
|
var StyleManager = class {
|
|
1370
1648
|
constructor() {
|
|
1371
1649
|
__privateAdd(this, _StyleManager_instances);
|
|
@@ -1384,6 +1662,21 @@ var StyleManager = class {
|
|
|
1384
1662
|
this.cellXfCache = /* @__PURE__ */ new Map();
|
|
1385
1663
|
this.nextCustomNumFmtId = 164;
|
|
1386
1664
|
this.isDirty = false;
|
|
1665
|
+
/**
|
|
1666
|
+
* `numFmtId` → format code for the custom (≥164) formats, materialised once.
|
|
1667
|
+
* Rebuilding it per lookup made every numeric cell read O(custom formats),
|
|
1668
|
+
* and reading a cell's value has to consult it to tell dates from numbers.
|
|
1669
|
+
* Invalidated whenever `numFmts` changes — see {@link #invalidateFormatCaches}.
|
|
1670
|
+
*/
|
|
1671
|
+
__privateAdd(this, _numFmtMapCache);
|
|
1672
|
+
/**
|
|
1673
|
+
* cellXf index → does its number format render a date? Memoised because
|
|
1674
|
+
* {@link Cell.getValue} asks this for every numeric cell, and answering it
|
|
1675
|
+
* from scratch reconstructs a whole CellStyle. Entries stay valid because
|
|
1676
|
+
* `cellXfs` and `numFmts` are append-only: an existing index never changes
|
|
1677
|
+
* meaning.
|
|
1678
|
+
*/
|
|
1679
|
+
__privateAdd(this, _dateStyleMemo, /* @__PURE__ */ new Map());
|
|
1387
1680
|
__privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1388
1681
|
}
|
|
1389
1682
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -1435,10 +1728,7 @@ var StyleManager = class {
|
|
|
1435
1728
|
const borderEntry = this.borders[cf.borderId];
|
|
1436
1729
|
if (borderEntry && cf.applyBorder) s.border = borderEntry.border;
|
|
1437
1730
|
if (cf.applyNumFmt) {
|
|
1438
|
-
|
|
1439
|
-
this.numFmts.map((n) => [n.id, n.code])
|
|
1440
|
-
);
|
|
1441
|
-
s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, customMap);
|
|
1731
|
+
s.numberFormat = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
|
|
1442
1732
|
}
|
|
1443
1733
|
if (cf.alignment && cf.applyAlign) s.alignment = cf.alignment;
|
|
1444
1734
|
if (cf.applyProt) {
|
|
@@ -1447,6 +1737,29 @@ var StyleManager = class {
|
|
|
1447
1737
|
}
|
|
1448
1738
|
return s;
|
|
1449
1739
|
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Whether the cellXf at `styleIndex` formats its value as a date. This is the
|
|
1742
|
+
* one style question the value-reading path has to ask, so it is answered
|
|
1743
|
+
* without reconstructing a CellStyle: the result is derived from the stored
|
|
1744
|
+
* format id/code and memoised per index.
|
|
1745
|
+
*
|
|
1746
|
+
* Equivalent to `getCellStyle(i).numberFormat` being date-like, by
|
|
1747
|
+
* construction — both read the same `applyNumFmt`/`numFmtId` pair through
|
|
1748
|
+
* {@link ExcelNumberFormat.fromFormatId}.
|
|
1749
|
+
*/
|
|
1750
|
+
isDateStyle(styleIndex) {
|
|
1751
|
+
const memo = __privateGet(this, _dateStyleMemo).get(styleIndex);
|
|
1752
|
+
if (memo !== void 0) return memo;
|
|
1753
|
+
const cf = this.cellXfs[styleIndex];
|
|
1754
|
+
if (!cf) return false;
|
|
1755
|
+
let isDate = false;
|
|
1756
|
+
if (cf.applyNumFmt) {
|
|
1757
|
+
const fmt = ExcelNumberFormat.fromFormatId(cf.numFmtId, __privateMethod(this, _StyleManager_instances, numFmtMap_fn).call(this));
|
|
1758
|
+
isDate = isDateFormatId(fmt.formatId) || isDateFormatCode(fmt.formatCode);
|
|
1759
|
+
}
|
|
1760
|
+
__privateGet(this, _dateStyleMemo).set(styleIndex, isDate);
|
|
1761
|
+
return isDate;
|
|
1762
|
+
}
|
|
1450
1763
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
1451
1764
|
/** Builds the complete xl/styles.xml string. */
|
|
1452
1765
|
buildStylesXml() {
|
|
@@ -1476,6 +1789,7 @@ ${cellXfsXml}
|
|
|
1476
1789
|
this.numFmtCache.clear();
|
|
1477
1790
|
this.cellXfCache.clear();
|
|
1478
1791
|
this.nextCustomNumFmtId = 164;
|
|
1792
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1479
1793
|
const root = parseXml(stylesXml);
|
|
1480
1794
|
for (const el of getElementsByTagName(root, "numFmt")) {
|
|
1481
1795
|
const id = parseInt(getAttr(el, "numFmtId") ?? "0", 10);
|
|
@@ -1534,9 +1848,23 @@ ${cellXfsXml}
|
|
|
1534
1848
|
}
|
|
1535
1849
|
if (this.fonts.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1536
1850
|
if (this.cellXfs.length === 0) __privateMethod(this, _StyleManager_instances, initDefaults_fn).call(this);
|
|
1851
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1537
1852
|
}
|
|
1538
1853
|
};
|
|
1854
|
+
_numFmtMapCache = new WeakMap();
|
|
1855
|
+
_dateStyleMemo = new WeakMap();
|
|
1539
1856
|
_StyleManager_instances = new WeakSet();
|
|
1857
|
+
numFmtMap_fn = function() {
|
|
1858
|
+
if (!__privateGet(this, _numFmtMapCache)) {
|
|
1859
|
+
__privateSet(this, _numFmtMapCache, new Map(this.numFmts.map((n) => [n.id, n.code])));
|
|
1860
|
+
}
|
|
1861
|
+
return __privateGet(this, _numFmtMapCache);
|
|
1862
|
+
};
|
|
1863
|
+
/** Drops the derived format caches after `numFmts` or `cellXfs` changed. */
|
|
1864
|
+
invalidateFormatCaches_fn = function() {
|
|
1865
|
+
__privateSet(this, _numFmtMapCache, void 0);
|
|
1866
|
+
__privateGet(this, _dateStyleMemo).clear();
|
|
1867
|
+
};
|
|
1540
1868
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
1541
1869
|
initDefaults_fn = function() {
|
|
1542
1870
|
const defaultFont = new ExcelFont({ name: "Calibri", size: 11 });
|
|
@@ -1609,6 +1937,7 @@ getOrCreateNumFmt_fn = function(fmt) {
|
|
|
1609
1937
|
const id = this.nextCustomNumFmtId++;
|
|
1610
1938
|
this.numFmts.push({ id, code: fmt.formatCode });
|
|
1611
1939
|
this.numFmtCache.set(fmt.formatCode, id);
|
|
1940
|
+
__privateMethod(this, _StyleManager_instances, invalidateFormatCaches_fn).call(this);
|
|
1612
1941
|
this.isDirty = true;
|
|
1613
1942
|
return id;
|
|
1614
1943
|
};
|
|
@@ -2021,56 +2350,54 @@ function parseRangeRef(rangeReference) {
|
|
|
2021
2350
|
};
|
|
2022
2351
|
}
|
|
2023
2352
|
|
|
2024
|
-
// src/model/
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
);
|
|
2051
|
-
}
|
|
2052
|
-
var DATE_FORMAT_IDS = /* @__PURE__ */ new Set([14, 15, 16, 17, 18, 19, 20, 21, 22, 45, 46, 47]);
|
|
2053
|
-
function isDateFormatId(formatId) {
|
|
2054
|
-
return DATE_FORMAT_IDS.has(formatId);
|
|
2353
|
+
// src/model/cell.ts
|
|
2354
|
+
function readCellValue(data, sharedStrings, styles) {
|
|
2355
|
+
const raw = data.rawValue;
|
|
2356
|
+
if (raw === void 0 || raw === "") return null;
|
|
2357
|
+
switch (data.dataType) {
|
|
2358
|
+
case "s": {
|
|
2359
|
+
const idx = parseInt(raw, 10);
|
|
2360
|
+
return sharedStrings.tryGetString(idx) ?? raw;
|
|
2361
|
+
}
|
|
2362
|
+
case "b":
|
|
2363
|
+
return raw === "1";
|
|
2364
|
+
case "str":
|
|
2365
|
+
return raw;
|
|
2366
|
+
default: {
|
|
2367
|
+
const num = parseFloat(raw);
|
|
2368
|
+
if (isNaN(num)) return raw;
|
|
2369
|
+
if (styles.isDateStyle(data.styleIndex)) {
|
|
2370
|
+
try {
|
|
2371
|
+
return fromOADate(num);
|
|
2372
|
+
} catch {
|
|
2373
|
+
return num;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
return num;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2055
2379
|
}
|
|
2056
|
-
function
|
|
2057
|
-
const
|
|
2058
|
-
|
|
2059
|
-
|
|
2380
|
+
function buildCellXml(data) {
|
|
2381
|
+
const style = data.styleIndex > 0 ? ` s="${data.styleIndex}"` : "";
|
|
2382
|
+
const formulaXml = data.formula ? data.arrayRef ? `<f t="array" ref="${data.arrayRef}" aca="1" ca="1">${escXml(data.formula)}</f>` : `<f>${escXml(data.formula)}</f>` : "";
|
|
2383
|
+
const valueXml = data.rawValue !== void 0 && data.rawValue !== "" ? `<v>${escXml(data.rawValue)}</v>` : "";
|
|
2384
|
+
const type = data.dataType && (formulaXml || valueXml) ? ` t="${data.dataType}"` : "";
|
|
2385
|
+
if (!formulaXml && !valueXml && data.styleIndex === 0) return "";
|
|
2386
|
+
return `<c r="${data.reference}"${style}${type}>${formulaXml}${valueXml}</c>`;
|
|
2060
2387
|
}
|
|
2061
|
-
|
|
2062
|
-
// src/model/cell.ts
|
|
2063
|
-
var _data, _sharedStrings, _styles, _Cell_instances, isDateStyle_fn;
|
|
2388
|
+
var _data, _sharedStrings, _styles, _version, _Cell_instances, touch_fn;
|
|
2064
2389
|
var Cell = class {
|
|
2065
2390
|
/** @internal */
|
|
2066
|
-
constructor(data, sharedStrings, styles) {
|
|
2391
|
+
constructor(data, sharedStrings, styles, version) {
|
|
2067
2392
|
__privateAdd(this, _Cell_instances);
|
|
2068
2393
|
__privateAdd(this, _data);
|
|
2069
2394
|
__privateAdd(this, _sharedStrings);
|
|
2070
2395
|
__privateAdd(this, _styles);
|
|
2396
|
+
__privateAdd(this, _version);
|
|
2071
2397
|
__privateSet(this, _data, data);
|
|
2072
2398
|
__privateSet(this, _sharedStrings, sharedStrings);
|
|
2073
2399
|
__privateSet(this, _styles, styles);
|
|
2400
|
+
__privateSet(this, _version, version);
|
|
2074
2401
|
}
|
|
2075
2402
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
2076
2403
|
/** Cell reference string e.g. "A1". */
|
|
@@ -2086,6 +2413,7 @@ var Cell = class {
|
|
|
2086
2413
|
return parseCellRef(__privateGet(this, _data).reference).column;
|
|
2087
2414
|
}
|
|
2088
2415
|
setValue(value) {
|
|
2416
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2089
2417
|
__privateGet(this, _data).formula = void 0;
|
|
2090
2418
|
if (value === null || value === void 0) {
|
|
2091
2419
|
__privateGet(this, _data).dataType = void 0;
|
|
@@ -2123,29 +2451,7 @@ var Cell = class {
|
|
|
2123
2451
|
* Date detection is based on the cell's number format.
|
|
2124
2452
|
*/
|
|
2125
2453
|
getValue() {
|
|
2126
|
-
|
|
2127
|
-
switch (__privateGet(this, _data).dataType) {
|
|
2128
|
-
case "s": {
|
|
2129
|
-
const idx = parseInt(__privateGet(this, _data).rawValue, 10);
|
|
2130
|
-
return __privateGet(this, _sharedStrings).tryGetString(idx) ?? __privateGet(this, _data).rawValue;
|
|
2131
|
-
}
|
|
2132
|
-
case "b":
|
|
2133
|
-
return __privateGet(this, _data).rawValue === "1";
|
|
2134
|
-
case "str":
|
|
2135
|
-
return __privateGet(this, _data).rawValue;
|
|
2136
|
-
default: {
|
|
2137
|
-
const num = parseFloat(__privateGet(this, _data).rawValue);
|
|
2138
|
-
if (isNaN(num)) return __privateGet(this, _data).rawValue;
|
|
2139
|
-
if (__privateMethod(this, _Cell_instances, isDateStyle_fn).call(this)) {
|
|
2140
|
-
try {
|
|
2141
|
-
return fromOADate(num);
|
|
2142
|
-
} catch {
|
|
2143
|
-
return num;
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
return num;
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2454
|
+
return readCellValue(__privateGet(this, _data), __privateGet(this, _sharedStrings), __privateGet(this, _styles));
|
|
2149
2455
|
}
|
|
2150
2456
|
/**
|
|
2151
2457
|
* Returns the display text of the cell value (always a string).
|
|
@@ -2183,6 +2489,7 @@ var Cell = class {
|
|
|
2183
2489
|
*/
|
|
2184
2490
|
setFormula(formula) {
|
|
2185
2491
|
if (!formula) throw new Error("Formula cannot be empty.");
|
|
2492
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2186
2493
|
__privateGet(this, _data).formula = formula.startsWith("=") ? formula.slice(1) : formula;
|
|
2187
2494
|
__privateGet(this, _data).rawValue = void 0;
|
|
2188
2495
|
__privateGet(this, _data).dataType = void 0;
|
|
@@ -2195,6 +2502,7 @@ var Cell = class {
|
|
|
2195
2502
|
* plain scalar formula. Serialises as `<f t="array" ref="…">`.
|
|
2196
2503
|
*/
|
|
2197
2504
|
setArrayRef(spillRef) {
|
|
2505
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2198
2506
|
__privateGet(this, _data).arrayRef = spillRef ?? void 0;
|
|
2199
2507
|
return this;
|
|
2200
2508
|
}
|
|
@@ -2212,6 +2520,7 @@ var Cell = class {
|
|
|
2212
2520
|
* added to the shared-string table.
|
|
2213
2521
|
*/
|
|
2214
2522
|
setComputedValue(value) {
|
|
2523
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2215
2524
|
if (value === null || value === void 0) {
|
|
2216
2525
|
__privateGet(this, _data).dataType = void 0;
|
|
2217
2526
|
__privateGet(this, _data).rawValue = void 0;
|
|
@@ -2251,6 +2560,7 @@ var Cell = class {
|
|
|
2251
2560
|
}
|
|
2252
2561
|
/** Replaces the entire cell style. */
|
|
2253
2562
|
set style(value) {
|
|
2563
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2254
2564
|
__privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(value);
|
|
2255
2565
|
}
|
|
2256
2566
|
/**
|
|
@@ -2261,6 +2571,7 @@ var Cell = class {
|
|
|
2261
2571
|
* cell.applyStyle(s => s.setBold(true).setFontColor(ExcelColor.red))
|
|
2262
2572
|
*/
|
|
2263
2573
|
applyStyle(action) {
|
|
2574
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2264
2575
|
const s = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex).clone();
|
|
2265
2576
|
action(s);
|
|
2266
2577
|
__privateGet(this, _data).styleIndex = __privateGet(this, _styles).getOrCreateCellFormatIndex(s);
|
|
@@ -2303,6 +2614,7 @@ var Cell = class {
|
|
|
2303
2614
|
// ── Clear ──────────────────────────────────────────────────────────────────
|
|
2304
2615
|
/** Clears the cell value and formula, preserving style. */
|
|
2305
2616
|
clear() {
|
|
2617
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2306
2618
|
__privateGet(this, _data).rawValue = void 0;
|
|
2307
2619
|
__privateGet(this, _data).dataType = void 0;
|
|
2308
2620
|
__privateGet(this, _data).formula = void 0;
|
|
@@ -2310,19 +2622,14 @@ var Cell = class {
|
|
|
2310
2622
|
}
|
|
2311
2623
|
/** Clears the cell value, formula, AND style. */
|
|
2312
2624
|
clearAll() {
|
|
2625
|
+
__privateMethod(this, _Cell_instances, touch_fn).call(this);
|
|
2313
2626
|
this.clear();
|
|
2314
2627
|
__privateGet(this, _data).styleIndex = 0;
|
|
2315
2628
|
}
|
|
2316
2629
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
2317
2630
|
/** @internal Builds the <c> XML element string for this cell. */
|
|
2318
2631
|
toXml() {
|
|
2319
|
-
|
|
2320
|
-
const style = __privateGet(this, _data).styleIndex > 0 ? ` s="${__privateGet(this, _data).styleIndex}"` : "";
|
|
2321
|
-
const formulaXml = __privateGet(this, _data).formula ? __privateGet(this, _data).arrayRef ? `<f t="array" ref="${__privateGet(this, _data).arrayRef}" aca="1" ca="1">${escXml(__privateGet(this, _data).formula)}</f>` : `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
|
|
2322
|
-
const valueXml = __privateGet(this, _data).rawValue !== void 0 && __privateGet(this, _data).rawValue !== "" ? `<v>${escXml(__privateGet(this, _data).rawValue)}</v>` : "";
|
|
2323
|
-
const type = __privateGet(this, _data).dataType && (formulaXml || valueXml) ? ` t="${__privateGet(this, _data).dataType}"` : "";
|
|
2324
|
-
if (!formulaXml && !valueXml && __privateGet(this, _data).styleIndex === 0) return "";
|
|
2325
|
-
return `<c r="${ref}"${style}${type}>${formulaXml}${valueXml}</c>`;
|
|
2632
|
+
return buildCellXml(__privateGet(this, _data));
|
|
2326
2633
|
}
|
|
2327
2634
|
/** @internal Parses a <c> XML element into CellData. */
|
|
2328
2635
|
static fromXmlElement(el, ref) {
|
|
@@ -2331,29 +2638,35 @@ var Cell = class {
|
|
|
2331
2638
|
let rawValue;
|
|
2332
2639
|
let formula;
|
|
2333
2640
|
let arrayRef;
|
|
2641
|
+
let sharedIndex;
|
|
2334
2642
|
for (const child of el.children) {
|
|
2335
2643
|
const tag = child.tagName.replace(/^.*:/, "");
|
|
2336
2644
|
if (tag === "v") rawValue = child.textContent;
|
|
2337
2645
|
if (tag === "f") {
|
|
2338
2646
|
formula = child.textContent;
|
|
2339
|
-
|
|
2340
|
-
|
|
2647
|
+
const attrs = child.attributes;
|
|
2648
|
+
const type = attrs?.["t"];
|
|
2649
|
+
if (type === "array" && attrs?.["ref"]) {
|
|
2650
|
+
arrayRef = attrs["ref"];
|
|
2651
|
+
}
|
|
2652
|
+
if (type === "shared" && attrs?.["si"] !== void 0) {
|
|
2653
|
+
const si = parseInt(attrs["si"], 10);
|
|
2654
|
+
if (!Number.isNaN(si)) sharedIndex = si;
|
|
2341
2655
|
}
|
|
2342
2656
|
}
|
|
2343
2657
|
}
|
|
2344
|
-
|
|
2658
|
+
if (formula === "") formula = void 0;
|
|
2659
|
+
return { reference: ref, dataType, rawValue, formula, arrayRef, styleIndex, sharedIndex };
|
|
2345
2660
|
}
|
|
2346
2661
|
};
|
|
2347
2662
|
_data = new WeakMap();
|
|
2348
2663
|
_sharedStrings = new WeakMap();
|
|
2349
2664
|
_styles = new WeakMap();
|
|
2665
|
+
_version = new WeakMap();
|
|
2350
2666
|
_Cell_instances = new WeakSet();
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
if (!style.numberFormat) return false;
|
|
2355
|
-
if (isDateFormatId(style.numberFormat.formatId)) return true;
|
|
2356
|
-
return isDateFormatCode(style.numberFormat.formatCode);
|
|
2667
|
+
/** Records that this cell's stored data changed. */
|
|
2668
|
+
touch_fn = function() {
|
|
2669
|
+
if (__privateGet(this, _version)) __privateGet(this, _version).v++;
|
|
2357
2670
|
};
|
|
2358
2671
|
function escXml(s) {
|
|
2359
2672
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -2620,6 +2933,219 @@ function shiftFormulaRefs(formula, shift, formulaSheet) {
|
|
|
2620
2933
|
return { text: out + formula.slice(flushed), changed: true, hasRefError };
|
|
2621
2934
|
}
|
|
2622
2935
|
|
|
2936
|
+
// src/model/formula-ref-translate.ts
|
|
2937
|
+
function isWordChar2(code) {
|
|
2938
|
+
return code >= 65 && code <= 90 || // A-Z
|
|
2939
|
+
code >= 97 && code <= 122 || // a-z
|
|
2940
|
+
code >= 48 && code <= 57 || // 0-9
|
|
2941
|
+
code === 95 || // _
|
|
2942
|
+
code === 46 || // .
|
|
2943
|
+
code === 36;
|
|
2944
|
+
}
|
|
2945
|
+
function parseCellComponent2(word) {
|
|
2946
|
+
const m = /^(\$?)([A-Za-z]{1,3})(\$?)(\d+)$/.exec(word);
|
|
2947
|
+
if (!m) return null;
|
|
2948
|
+
const col = columnNumber(m[2]);
|
|
2949
|
+
const row = parseInt(m[4], 10);
|
|
2950
|
+
if (col > EXCEL_MAX_COLUMNS || row < 1 || row > EXCEL_MAX_ROWS) return null;
|
|
2951
|
+
return { colAbs: m[1] === "$", col, rowAbs: m[3] === "$", row };
|
|
2952
|
+
}
|
|
2953
|
+
function parseColComponent2(word) {
|
|
2954
|
+
const m = /^(\$?)([A-Za-z]{1,3})$/.exec(word);
|
|
2955
|
+
if (!m) return null;
|
|
2956
|
+
const col = columnNumber(m[2]);
|
|
2957
|
+
return col > EXCEL_MAX_COLUMNS ? null : { abs: m[1] === "$", col };
|
|
2958
|
+
}
|
|
2959
|
+
function parseRowComponent2(word) {
|
|
2960
|
+
const m = /^(\$?)(\d+)$/.exec(word);
|
|
2961
|
+
if (!m) return null;
|
|
2962
|
+
const row = parseInt(m[2], 10);
|
|
2963
|
+
return row < 1 || row > EXCEL_MAX_ROWS ? null : { abs: m[1] === "$", row };
|
|
2964
|
+
}
|
|
2965
|
+
function move(index, delta, abs, max) {
|
|
2966
|
+
if (abs || delta === 0) return index;
|
|
2967
|
+
const moved = index + delta;
|
|
2968
|
+
return moved < 1 || moved > max ? null : moved;
|
|
2969
|
+
}
|
|
2970
|
+
function translateFormulaRefs(formula, dRow, dCol) {
|
|
2971
|
+
if (dRow === 0 && dCol === 0) return { text: formula, hasRefError: false };
|
|
2972
|
+
const n = formula.length;
|
|
2973
|
+
let out = "";
|
|
2974
|
+
let flushed = 0;
|
|
2975
|
+
let hasRefError = false;
|
|
2976
|
+
let i = 0;
|
|
2977
|
+
const replace = (start, text) => {
|
|
2978
|
+
if (text.length === i - start && formula.startsWith(text, start)) return;
|
|
2979
|
+
out += formula.slice(flushed, start) + text;
|
|
2980
|
+
flushed = i;
|
|
2981
|
+
};
|
|
2982
|
+
const readWord = () => {
|
|
2983
|
+
let j = i;
|
|
2984
|
+
while (j < n && isWordChar2(formula.charCodeAt(j))) j++;
|
|
2985
|
+
const w = formula.slice(i, j);
|
|
2986
|
+
i = j;
|
|
2987
|
+
return w;
|
|
2988
|
+
};
|
|
2989
|
+
const readQuoted = () => {
|
|
2990
|
+
let j = i + 1;
|
|
2991
|
+
while (j < n) {
|
|
2992
|
+
if (formula[j] === "'") {
|
|
2993
|
+
if (formula[j + 1] === "'") {
|
|
2994
|
+
j += 2;
|
|
2995
|
+
continue;
|
|
2996
|
+
}
|
|
2997
|
+
j++;
|
|
2998
|
+
break;
|
|
2999
|
+
}
|
|
3000
|
+
j++;
|
|
3001
|
+
}
|
|
3002
|
+
const q = formula.slice(i, j);
|
|
3003
|
+
i = j;
|
|
3004
|
+
return q;
|
|
3005
|
+
};
|
|
3006
|
+
const emitCell2 = (c, row, col) => `${c.colAbs ? "$" : ""}${columnLetter(col)}${c.rowAbs ? "$" : ""}${row}`;
|
|
3007
|
+
const consumeRef = (refStart, rewrite = true) => {
|
|
3008
|
+
const first = readWord();
|
|
3009
|
+
const firstCell = parseCellComponent2(first);
|
|
3010
|
+
const firstCol = firstCell ? null : parseColComponent2(first);
|
|
3011
|
+
const firstRow = firstCell || firstCol ? null : parseRowComponent2(first);
|
|
3012
|
+
if (!firstCell && !firstCol && !firstRow) return;
|
|
3013
|
+
let secondCell = null;
|
|
3014
|
+
let secondCol = null;
|
|
3015
|
+
let secondRow = null;
|
|
3016
|
+
let isRange = false;
|
|
3017
|
+
if (formula[i] === ":" && i + 1 < n && isWordChar2(formula.charCodeAt(i + 1))) {
|
|
3018
|
+
const save = i;
|
|
3019
|
+
i++;
|
|
3020
|
+
const w = readWord();
|
|
3021
|
+
if (firstCell) secondCell = parseCellComponent2(w);
|
|
3022
|
+
else if (firstCol) secondCol = parseColComponent2(w);
|
|
3023
|
+
else if (firstRow) secondRow = parseRowComponent2(w);
|
|
3024
|
+
if (secondCell || secondCol || secondRow) isRange = true;
|
|
3025
|
+
else i = save;
|
|
3026
|
+
}
|
|
3027
|
+
if (!isRange && formula[i] === "(") return;
|
|
3028
|
+
if (!rewrite) {
|
|
3029
|
+
if (formula[i] === "#") i++;
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
const dead = () => {
|
|
3033
|
+
if (formula[i] === "#") i++;
|
|
3034
|
+
replace(refStart, "#REF!");
|
|
3035
|
+
hasRefError = true;
|
|
3036
|
+
};
|
|
3037
|
+
if (isRange) {
|
|
3038
|
+
if (firstCell && secondCell) {
|
|
3039
|
+
const r1 = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3040
|
+
const c1 = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3041
|
+
const r2 = move(secondCell.row, dRow, secondCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3042
|
+
const c2 = move(secondCell.col, dCol, secondCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3043
|
+
if (r1 === null || c1 === null || r2 === null || c2 === null) {
|
|
3044
|
+
dead();
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
replace(refStart, `${emitCell2(firstCell, r1, c1)}:${emitCell2(secondCell, r2, c2)}`);
|
|
3048
|
+
return;
|
|
3049
|
+
}
|
|
3050
|
+
if (firstCol && secondCol) {
|
|
3051
|
+
const c1 = move(firstCol.col, dCol, firstCol.abs, EXCEL_MAX_COLUMNS);
|
|
3052
|
+
const c2 = move(secondCol.col, dCol, secondCol.abs, EXCEL_MAX_COLUMNS);
|
|
3053
|
+
if (c1 === null || c2 === null) {
|
|
3054
|
+
dead();
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
replace(refStart, `${firstCol.abs ? "$" : ""}${columnLetter(c1)}:${secondCol.abs ? "$" : ""}${columnLetter(c2)}`);
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
if (firstRow && secondRow) {
|
|
3061
|
+
const r1 = move(firstRow.row, dRow, firstRow.abs, EXCEL_MAX_ROWS);
|
|
3062
|
+
const r2 = move(secondRow.row, dRow, secondRow.abs, EXCEL_MAX_ROWS);
|
|
3063
|
+
if (r1 === null || r2 === null) {
|
|
3064
|
+
dead();
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
replace(refStart, `${firstRow.abs ? "$" : ""}${r1}:${secondRow.abs ? "$" : ""}${r2}`);
|
|
3068
|
+
return;
|
|
3069
|
+
}
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
if (!firstCell) return;
|
|
3073
|
+
const row = move(firstCell.row, dRow, firstCell.rowAbs, EXCEL_MAX_ROWS);
|
|
3074
|
+
const col = move(firstCell.col, dCol, firstCell.colAbs, EXCEL_MAX_COLUMNS);
|
|
3075
|
+
if (row === null || col === null) {
|
|
3076
|
+
dead();
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
3079
|
+
replace(refStart, emitCell2(firstCell, row, col));
|
|
3080
|
+
};
|
|
3081
|
+
while (i < n) {
|
|
3082
|
+
const ch = formula[i];
|
|
3083
|
+
if (ch === '"') {
|
|
3084
|
+
let j = i + 1;
|
|
3085
|
+
while (j < n) {
|
|
3086
|
+
if (formula[j] === '"') {
|
|
3087
|
+
if (formula[j + 1] === '"') {
|
|
3088
|
+
j += 2;
|
|
3089
|
+
continue;
|
|
3090
|
+
}
|
|
3091
|
+
j++;
|
|
3092
|
+
break;
|
|
3093
|
+
}
|
|
3094
|
+
j++;
|
|
3095
|
+
}
|
|
3096
|
+
i = j;
|
|
3097
|
+
continue;
|
|
3098
|
+
}
|
|
3099
|
+
if (ch === "'") {
|
|
3100
|
+
readQuoted();
|
|
3101
|
+
if (formula[i] === "!") {
|
|
3102
|
+
i++;
|
|
3103
|
+
consumeRef(i);
|
|
3104
|
+
}
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
if (isWordChar2(formula.charCodeAt(i))) {
|
|
3108
|
+
const start = i;
|
|
3109
|
+
readWord();
|
|
3110
|
+
if (formula[i] === "!") {
|
|
3111
|
+
i++;
|
|
3112
|
+
consumeRef(i);
|
|
3113
|
+
continue;
|
|
3114
|
+
}
|
|
3115
|
+
if (formula[i] === ":") {
|
|
3116
|
+
const save = i;
|
|
3117
|
+
i++;
|
|
3118
|
+
const w2 = i < n && isWordChar2(formula.charCodeAt(i)) ? readWord() : "";
|
|
3119
|
+
if (w2 && formula[i] === "!") {
|
|
3120
|
+
i++;
|
|
3121
|
+
consumeRef(i, false);
|
|
3122
|
+
continue;
|
|
3123
|
+
}
|
|
3124
|
+
i = save;
|
|
3125
|
+
}
|
|
3126
|
+
if (formula[i] === "[") {
|
|
3127
|
+
let depth = 0;
|
|
3128
|
+
let j = i;
|
|
3129
|
+
while (j < n) {
|
|
3130
|
+
if (formula[j] === "[") depth++;
|
|
3131
|
+
else if (formula[j] === "]" && --depth === 0) {
|
|
3132
|
+
j++;
|
|
3133
|
+
break;
|
|
3134
|
+
}
|
|
3135
|
+
j++;
|
|
3136
|
+
}
|
|
3137
|
+
i = j;
|
|
3138
|
+
continue;
|
|
3139
|
+
}
|
|
3140
|
+
i = start;
|
|
3141
|
+
consumeRef(start);
|
|
3142
|
+
continue;
|
|
3143
|
+
}
|
|
3144
|
+
i++;
|
|
3145
|
+
}
|
|
3146
|
+
return { text: out + formula.slice(flushed), hasRefError };
|
|
3147
|
+
}
|
|
3148
|
+
|
|
2623
3149
|
// src/model/range.ts
|
|
2624
3150
|
var _sheet, _startRow, _startColumn, _endRow, _endColumn, _address, _Range_instances, forEach_fn, forEachExisting_fn;
|
|
2625
3151
|
var Range = class {
|
|
@@ -2850,7 +3376,7 @@ var PROTECTION_ATTRS = [
|
|
|
2850
3376
|
["allowEditObjects", "objects"],
|
|
2851
3377
|
["allowEditScenarios", "scenarios"]
|
|
2852
3378
|
];
|
|
2853
|
-
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _Worksheet_instances, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
|
|
3379
|
+
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _protection, _protectionCredentials, _hyperlinks, _comments, _commentsDirty, _pendingHyperlinkRels, _preservedTail, _preservedRels, _version2, _usedBoundsMemo, _xmlMemo, _Worksheet_instances, touch_fn2, findCellData_fn, computeUsedBounds_fn, takeCellData_fn, takeCellDataAt_fn, deleteCellData_fn, applyStructural_fn, shiftRowsAndCells_fn, shiftRows_fn, shiftColumns_fn, maxPopulatedRow_fn, shiftSpillRanges_fn, shiftSpillRange_fn, rewriteFormulas_fn, shiftMerges_fn, shiftAutoFilter_fn, shiftValidations_fn, shiftHyperlinks_fn, shiftComments_fn, shiftColDefs_fn, relIdBase_fn, activeTail_fn, buildTailXml_fn, buildSheetProtectionXml_fn, buildHyperlinksXml_fn, resolveSharedFormulas_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, splitColSpan_fn, insertColDef_fn, findColDefIndex_fn, findColDef_fn, pruneColDefs_fn, getOrCreateRow_fn;
|
|
2854
3380
|
var Worksheet = class {
|
|
2855
3381
|
/** @internal */
|
|
2856
3382
|
constructor(name, sharedStrings, styles) {
|
|
@@ -2892,10 +3418,26 @@ var Worksheet = class {
|
|
|
2892
3418
|
// comment anchors survive open→save. See io/preserved-parts.ts.
|
|
2893
3419
|
__privateAdd(this, _preservedTail, []);
|
|
2894
3420
|
__privateAdd(this, _preservedRels, []);
|
|
3421
|
+
/**
|
|
3422
|
+
* Mutation counter for this sheet, shared with every {@link Cell} view it
|
|
3423
|
+
* hands out (a Cell writes directly into the stored CellData). Everything
|
|
3424
|
+
* derived from the sheet's contents — the used-range memo, the cached
|
|
3425
|
+
* worksheet XML — is keyed on it, so every mutator must bump it. The test
|
|
3426
|
+
* `worksheet-version.test.ts` walks the public surface to keep that true.
|
|
3427
|
+
*/
|
|
3428
|
+
__privateAdd(this, _version2, { v: 0 });
|
|
3429
|
+
/** Memoised {@link usedBounds}, valid while `#version.v` is unchanged. */
|
|
3430
|
+
__privateAdd(this, _usedBoundsMemo);
|
|
3431
|
+
/** Memoised {@link buildXml} output, valid while `#version.v` is unchanged. */
|
|
3432
|
+
__privateAdd(this, _xmlMemo);
|
|
2895
3433
|
__privateSet(this, _name, name);
|
|
2896
3434
|
__privateSet(this, _sharedStrings2, sharedStrings);
|
|
2897
3435
|
__privateSet(this, _styles2, styles);
|
|
2898
3436
|
}
|
|
3437
|
+
/** @internal Current mutation count — see {@link #version}. */
|
|
3438
|
+
get version() {
|
|
3439
|
+
return __privateGet(this, _version2).v;
|
|
3440
|
+
}
|
|
2899
3441
|
// ── Identity ───────────────────────────────────────────────────────────────
|
|
2900
3442
|
get name() {
|
|
2901
3443
|
return __privateGet(this, _name);
|
|
@@ -2917,10 +3459,11 @@ var Worksheet = class {
|
|
|
2917
3459
|
const rowData = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, row);
|
|
2918
3460
|
let cellData = rowData.cells.get(ref);
|
|
2919
3461
|
if (!cellData) {
|
|
3462
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
2920
3463
|
cellData = { reference: ref, styleIndex: 0 };
|
|
2921
3464
|
rowData.cells.set(ref, cellData);
|
|
2922
3465
|
}
|
|
2923
|
-
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
3466
|
+
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
|
|
2924
3467
|
}
|
|
2925
3468
|
tryGetCell(refOrRow, column) {
|
|
2926
3469
|
let rowData;
|
|
@@ -2936,7 +3479,7 @@ var Worksheet = class {
|
|
|
2936
3479
|
}
|
|
2937
3480
|
const cellData = rowData.cells.get(ref);
|
|
2938
3481
|
if (!cellData) return null;
|
|
2939
|
-
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
3482
|
+
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2));
|
|
2940
3483
|
}
|
|
2941
3484
|
cell(refOrRow, column) {
|
|
2942
3485
|
return typeof refOrRow === "string" ? this.getCell(refOrRow) : this.getCell(refOrRow, column);
|
|
@@ -2948,15 +3491,28 @@ var Worksheet = class {
|
|
|
2948
3491
|
}
|
|
2949
3492
|
/** Gets the raw value of a cell (for formula evaluation). */
|
|
2950
3493
|
getCellValue(reference) {
|
|
2951
|
-
|
|
3494
|
+
const cd = __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference);
|
|
3495
|
+
return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Value of a cell addressed by 1-based row/column. The reference-string
|
|
3499
|
+
* overload has to build (and the callee re-parse) a string per cell; a range
|
|
3500
|
+
* read walking a rectangle already knows the coordinates, and on a real
|
|
3501
|
+
* workbook that is tens of millions of strings per recalculation.
|
|
3502
|
+
*/
|
|
3503
|
+
getCellValueAt(row, column) {
|
|
3504
|
+
const rowData = __privateGet(this, _rows).get(row);
|
|
3505
|
+
if (!rowData) return null;
|
|
3506
|
+
const cd = rowData.cells.get(cellRefFromRowCol(row, column));
|
|
3507
|
+
return cd ? readCellValue(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)) : null;
|
|
2952
3508
|
}
|
|
2953
3509
|
/** Formula text (without leading '=') of a cell, or null if it has none. */
|
|
2954
3510
|
getCellFormula(reference) {
|
|
2955
|
-
return this.
|
|
3511
|
+
return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.formula ?? null;
|
|
2956
3512
|
}
|
|
2957
3513
|
/** Spill range (e.g. "A1:A3") of a dynamic-array anchor, or null. Supports A1#. */
|
|
2958
3514
|
getSpillRange(reference) {
|
|
2959
|
-
return this.
|
|
3515
|
+
return __privateMethod(this, _Worksheet_instances, findCellData_fn).call(this, reference)?.arrayRef ?? null;
|
|
2960
3516
|
}
|
|
2961
3517
|
/**
|
|
2962
3518
|
* Iterates the populated rows of the sheet in ascending row order, yielding
|
|
@@ -2972,7 +3528,7 @@ var Worksheet = class {
|
|
|
2972
3528
|
*rows() {
|
|
2973
3529
|
const populated = [...__privateGet(this, _rows).values()].filter((row) => row.cells.size > 0).sort((a, b) => a.rowIndex - b.rowIndex);
|
|
2974
3530
|
for (const row of populated) {
|
|
2975
|
-
const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)));
|
|
3531
|
+
const cells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col).map(({ cd }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)));
|
|
2976
3532
|
yield { rowIndex: row.rowIndex, cells };
|
|
2977
3533
|
}
|
|
2978
3534
|
}
|
|
@@ -2997,7 +3553,7 @@ var Worksheet = class {
|
|
|
2997
3553
|
for (const cd of row.cells.values()) {
|
|
2998
3554
|
const col = columnFromRef(cd.reference, refSplit(cd.reference));
|
|
2999
3555
|
if (col < startColumn || col > endColumn) continue;
|
|
3000
|
-
action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)), row.rowIndex, col);
|
|
3556
|
+
action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2), __privateGet(this, _version2)), row.rowIndex, col);
|
|
3001
3557
|
}
|
|
3002
3558
|
};
|
|
3003
3559
|
if (endRow - startRow + 1 <= __privateGet(this, _rows).size) {
|
|
@@ -3016,6 +3572,7 @@ var Worksheet = class {
|
|
|
3016
3572
|
* the public {@link moveCell} has to do. Used by bulk range moves.
|
|
3017
3573
|
*/
|
|
3018
3574
|
moveCellRC(fromRow, fromColumn, toRow, toColumn) {
|
|
3575
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3019
3576
|
if (fromRow === toRow && fromColumn === toColumn) return;
|
|
3020
3577
|
const fromRef = cellRefFromRowCol(fromRow, fromColumn);
|
|
3021
3578
|
const toRef = cellRefFromRowCol(toRow, toColumn);
|
|
@@ -3037,21 +3594,16 @@ var Worksheet = class {
|
|
|
3037
3594
|
* cheaper when a dense shape is not actually required.
|
|
3038
3595
|
*/
|
|
3039
3596
|
get usedBounds() {
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
for (const ref of row.cells.keys()) {
|
|
3046
|
-
const col = columnFromRef(ref, refSplit(ref));
|
|
3047
|
-
if (col > columnCount) columnCount = col;
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
return { rowCount, columnCount };
|
|
3597
|
+
const memo = __privateGet(this, _usedBoundsMemo);
|
|
3598
|
+
if (memo && memo.at === __privateGet(this, _version2).v) return memo.value;
|
|
3599
|
+
const value = __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this);
|
|
3600
|
+
__privateSet(this, _usedBoundsMemo, { at: __privateGet(this, _version2).v, value });
|
|
3601
|
+
return value;
|
|
3051
3602
|
}
|
|
3052
3603
|
// ── Layout ─────────────────────────────────────────────────────────────────
|
|
3053
3604
|
/** Sets the width of a column (1-based index). */
|
|
3054
3605
|
setColumnWidth(columnIndex, width) {
|
|
3606
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3055
3607
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3056
3608
|
if (width <= 0) throw new RangeError("Width must be > 0.");
|
|
3057
3609
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex) ?? __privateMethod(this, _Worksheet_instances, insertColDef_fn).call(this, { min: columnIndex, max: columnIndex, customWidth: false });
|
|
@@ -3064,6 +3616,7 @@ var Worksheet = class {
|
|
|
3064
3616
|
* {@link getColumnWidth} returns `undefined`. A hidden column stays hidden.
|
|
3065
3617
|
*/
|
|
3066
3618
|
resetColumnWidth(columnIndex) {
|
|
3619
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3067
3620
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3068
3621
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3069
3622
|
if (!def) return;
|
|
@@ -3076,6 +3629,7 @@ var Worksheet = class {
|
|
|
3076
3629
|
* file as `<col hidden="1">` and keep any explicit width.
|
|
3077
3630
|
*/
|
|
3078
3631
|
setColumnHidden(columnIndex, hidden = true) {
|
|
3632
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3079
3633
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3080
3634
|
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3081
3635
|
if (def) {
|
|
@@ -3102,6 +3656,7 @@ var Worksheet = class {
|
|
|
3102
3656
|
}
|
|
3103
3657
|
/** Auto-fits column width based on content (approximate). */
|
|
3104
3658
|
autoFitColumn(columnIndex) {
|
|
3659
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3105
3660
|
this.autoFitColumns(columnIndex, columnIndex);
|
|
3106
3661
|
}
|
|
3107
3662
|
/**
|
|
@@ -3109,6 +3664,7 @@ var Worksheet = class {
|
|
|
3109
3664
|
* populated cells, instead of re-scanning the sheet once per column.
|
|
3110
3665
|
*/
|
|
3111
3666
|
autoFitColumns(firstColumn, lastColumn) {
|
|
3667
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3112
3668
|
if (firstColumn < 1) throw new RangeError("Column index must be >= 1.");
|
|
3113
3669
|
if (lastColumn < firstColumn) return;
|
|
3114
3670
|
const maxLen = /* @__PURE__ */ new Map();
|
|
@@ -3123,6 +3679,7 @@ var Worksheet = class {
|
|
|
3123
3679
|
}
|
|
3124
3680
|
/** Sets the height of a row (1-based index). */
|
|
3125
3681
|
setRowHeight(rowIndex, height) {
|
|
3682
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3126
3683
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3127
3684
|
if (height <= 0) throw new RangeError("Height must be > 0.");
|
|
3128
3685
|
const row = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex);
|
|
@@ -3143,6 +3700,7 @@ var Worksheet = class {
|
|
|
3143
3700
|
* {@link getRowHeight} returns `undefined`. A hidden row stays hidden.
|
|
3144
3701
|
*/
|
|
3145
3702
|
resetRowHeight(rowIndex) {
|
|
3703
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3146
3704
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3147
3705
|
const row = __privateGet(this, _rows).get(rowIndex);
|
|
3148
3706
|
if (!row) return;
|
|
@@ -3152,6 +3710,7 @@ var Worksheet = class {
|
|
|
3152
3710
|
}
|
|
3153
3711
|
/** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
|
|
3154
3712
|
setRowHidden(rowIndex, hidden = true) {
|
|
3713
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3155
3714
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3156
3715
|
__privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex).hidden = hidden;
|
|
3157
3716
|
}
|
|
@@ -3202,6 +3761,7 @@ var Worksheet = class {
|
|
|
3202
3761
|
* ws.insertRows(3, 2); // pushes row 3 and below down by two rows
|
|
3203
3762
|
*/
|
|
3204
3763
|
insertRows(at, count = 1) {
|
|
3764
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3205
3765
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "insert", at, count);
|
|
3206
3766
|
}
|
|
3207
3767
|
/**
|
|
@@ -3211,6 +3771,7 @@ var Worksheet = class {
|
|
|
3211
3771
|
* for the shifting rules and known limitations.
|
|
3212
3772
|
*/
|
|
3213
3773
|
deleteRows(at, count = 1) {
|
|
3774
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3214
3775
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "delete", at, count);
|
|
3215
3776
|
}
|
|
3216
3777
|
/**
|
|
@@ -3219,6 +3780,7 @@ var Worksheet = class {
|
|
|
3219
3780
|
* shift with the columns.
|
|
3220
3781
|
*/
|
|
3221
3782
|
insertColumns(at, count = 1) {
|
|
3783
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3222
3784
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "insert", at, count);
|
|
3223
3785
|
}
|
|
3224
3786
|
/**
|
|
@@ -3226,6 +3788,7 @@ var Worksheet = class {
|
|
|
3226
3788
|
* Column-level counterpart to {@link deleteRows}.
|
|
3227
3789
|
*/
|
|
3228
3790
|
deleteColumns(at, count = 1) {
|
|
3791
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3229
3792
|
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "delete", at, count);
|
|
3230
3793
|
}
|
|
3231
3794
|
/**
|
|
@@ -3234,6 +3797,7 @@ var Worksheet = class {
|
|
|
3234
3797
|
* like `'Data'!A5` stay correct. Cell positions here do not change.
|
|
3235
3798
|
*/
|
|
3236
3799
|
applyExternalShift(shift) {
|
|
3800
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3237
3801
|
return { refErrors: __privateMethod(this, _Worksheet_instances, rewriteFormulas_fn).call(this, shift) };
|
|
3238
3802
|
}
|
|
3239
3803
|
/**
|
|
@@ -3246,6 +3810,7 @@ var Worksheet = class {
|
|
|
3246
3810
|
* ws.moveCell('A1', 'C5');
|
|
3247
3811
|
*/
|
|
3248
3812
|
moveCell(from, to) {
|
|
3813
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3249
3814
|
const fromRef = normalizeRef(from);
|
|
3250
3815
|
const toRef = normalizeRef(to);
|
|
3251
3816
|
if (fromRef === toRef) return;
|
|
@@ -3277,6 +3842,7 @@ var Worksheet = class {
|
|
|
3277
3842
|
* ws.copyCell('A1', 'C5');
|
|
3278
3843
|
*/
|
|
3279
3844
|
copyCell(from, to) {
|
|
3845
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3280
3846
|
const fromRef = normalizeRef(from);
|
|
3281
3847
|
const toRef = normalizeRef(to);
|
|
3282
3848
|
if (fromRef === toRef) return;
|
|
@@ -3292,11 +3858,13 @@ var Worksheet = class {
|
|
|
3292
3858
|
}
|
|
3293
3859
|
/** Merges cells in the given range (e.g. "A1:B2"). */
|
|
3294
3860
|
mergeCells(rangeReference) {
|
|
3861
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3295
3862
|
if (!rangeReference) throw new Error("Range reference cannot be empty.");
|
|
3296
3863
|
__privateGet(this, _mergeCells).push({ ref: rangeReference });
|
|
3297
3864
|
}
|
|
3298
3865
|
/** Removes a merge for the given range reference. */
|
|
3299
3866
|
unmergeCells(rangeReference) {
|
|
3867
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3300
3868
|
__privateSet(this, _mergeCells, __privateGet(this, _mergeCells).filter((m) => m.ref !== rangeReference));
|
|
3301
3869
|
}
|
|
3302
3870
|
/**
|
|
@@ -3313,12 +3881,14 @@ var Worksheet = class {
|
|
|
3313
3881
|
* @param column Number of columns to freeze (0 = none)
|
|
3314
3882
|
*/
|
|
3315
3883
|
freezePanes(row, column) {
|
|
3884
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3316
3885
|
if (row < 0) throw new RangeError("Row must be >= 0.");
|
|
3317
3886
|
if (column < 0) throw new RangeError("Column must be >= 0.");
|
|
3318
3887
|
__privateSet(this, _pane, row === 0 && column === 0 ? void 0 : { row, column });
|
|
3319
3888
|
}
|
|
3320
3889
|
/** Sets auto-filter on the given range. */
|
|
3321
3890
|
setAutoFilter(rangeReference) {
|
|
3891
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3322
3892
|
__privateSet(this, _autoFilter, { ref: rangeReference });
|
|
3323
3893
|
}
|
|
3324
3894
|
/**
|
|
@@ -3327,10 +3897,12 @@ var Worksheet = class {
|
|
|
3327
3897
|
* @param listFormula Formula for the dropdown list (e.g. "'Sheet2'!$A$1:$A$10")
|
|
3328
3898
|
*/
|
|
3329
3899
|
addDropdownValidation(cellRange, listFormula) {
|
|
3900
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3330
3901
|
__privateGet(this, _dataValidations).push({ sqref: cellRange, formula: listFormula });
|
|
3331
3902
|
}
|
|
3332
3903
|
/** Sets the visibility state of this worksheet. */
|
|
3333
3904
|
setVisibility(state) {
|
|
3905
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3334
3906
|
__privateSet(this, _visibility, state);
|
|
3335
3907
|
}
|
|
3336
3908
|
get visibility() {
|
|
@@ -3352,10 +3924,12 @@ var Worksheet = class {
|
|
|
3352
3924
|
* ws.protect({ allowSort: true, allowAutoFilter: true });
|
|
3353
3925
|
*/
|
|
3354
3926
|
protect(options) {
|
|
3927
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3355
3928
|
__privateSet(this, _protection, { ...PROTECTION_DEFAULTS, ...options });
|
|
3356
3929
|
}
|
|
3357
3930
|
/** Turns off sheet protection, discarding any password read from a file. */
|
|
3358
3931
|
unprotect() {
|
|
3932
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3359
3933
|
__privateSet(this, _protection, void 0);
|
|
3360
3934
|
__privateSet(this, _protectionCredentials, "");
|
|
3361
3935
|
}
|
|
@@ -3386,6 +3960,7 @@ var Worksheet = class {
|
|
|
3386
3960
|
if (!link.target && !link.location) {
|
|
3387
3961
|
throw new Error("A hyperlink needs a target (external) or a location (in-workbook).");
|
|
3388
3962
|
}
|
|
3963
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3389
3964
|
const normalized = normalizeRangeRef(ref);
|
|
3390
3965
|
__privateGet(this, _hyperlinks).set(normalized, { ref: normalized, ...link });
|
|
3391
3966
|
}
|
|
@@ -3395,6 +3970,7 @@ var Worksheet = class {
|
|
|
3395
3970
|
}
|
|
3396
3971
|
/** Removes the hyperlink on this ref. Returns true if one was there. */
|
|
3397
3972
|
removeHyperlink(ref) {
|
|
3973
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3398
3974
|
return __privateGet(this, _hyperlinks).delete(normalizeRangeRef(ref));
|
|
3399
3975
|
}
|
|
3400
3976
|
/** Every hyperlink on the sheet, including ones read from a file. */
|
|
@@ -3415,6 +3991,7 @@ var Worksheet = class {
|
|
|
3415
3991
|
* ws.setComment('B4', 'Check this figure', { author: 'Bill' });
|
|
3416
3992
|
*/
|
|
3417
3993
|
setComment(ref, text, options) {
|
|
3994
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3418
3995
|
const normalized = normalizeRef(ref);
|
|
3419
3996
|
__privateGet(this, _comments).set(normalized, {
|
|
3420
3997
|
ref: normalized,
|
|
@@ -3429,6 +4006,7 @@ var Worksheet = class {
|
|
|
3429
4006
|
}
|
|
3430
4007
|
/** Removes the comment on a cell. Returns true if one was there. */
|
|
3431
4008
|
removeComment(ref) {
|
|
4009
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3432
4010
|
const removed = __privateGet(this, _comments).delete(normalizeRef(ref));
|
|
3433
4011
|
if (removed) __privateSet(this, _commentsDirty, true);
|
|
3434
4012
|
return removed;
|
|
@@ -3456,6 +4034,7 @@ var Worksheet = class {
|
|
|
3456
4034
|
* save pipeline turns registered providers into valid OOXML drawing/chart parts.
|
|
3457
4035
|
*/
|
|
3458
4036
|
addDrawingProvider(provider) {
|
|
4037
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3459
4038
|
__privateGet(this, _drawings).push(provider);
|
|
3460
4039
|
}
|
|
3461
4040
|
/**
|
|
@@ -3467,6 +4046,7 @@ var Worksheet = class {
|
|
|
3467
4046
|
* deliberately drop a chart that came from the source file.
|
|
3468
4047
|
*/
|
|
3469
4048
|
clearDrawings() {
|
|
4049
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3470
4050
|
__privateSet(this, _drawings, []);
|
|
3471
4051
|
__privateSet(this, _preservedTail, __privateGet(this, _preservedTail).filter((t) => t.tag !== "drawing"));
|
|
3472
4052
|
}
|
|
@@ -3533,6 +4113,7 @@ var Worksheet = class {
|
|
|
3533
4113
|
}
|
|
3534
4114
|
/** @internal Captures round-trip state from the source package. */
|
|
3535
4115
|
loadPreservedState(worksheetXml, relationships) {
|
|
4116
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3536
4117
|
__privateSet(this, _preservedRels, relationships.filter((r) => r.type !== REL_TYPES.hyperlink));
|
|
3537
4118
|
const known = new Set(__privateGet(this, _preservedRels).map((r) => r.id));
|
|
3538
4119
|
__privateSet(this, _preservedTail, parseTailElements(worksheetXml).filter((t) => known.has(t.relId)));
|
|
@@ -3573,6 +4154,8 @@ var Worksheet = class {
|
|
|
3573
4154
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
3574
4155
|
/** Builds the xl/worksheets/sheetN.xml string. */
|
|
3575
4156
|
buildXml() {
|
|
4157
|
+
const memo = __privateGet(this, _xmlMemo);
|
|
4158
|
+
if (memo && memo.at === __privateGet(this, _version2).v) return memo.xml;
|
|
3576
4159
|
const rels = this.generatedRels();
|
|
3577
4160
|
const sheetViewXml = __privateMethod(this, _Worksheet_instances, buildSheetViewXml_fn).call(this);
|
|
3578
4161
|
const colsXml = __privateMethod(this, _Worksheet_instances, buildColsXml_fn).call(this);
|
|
@@ -3583,15 +4166,18 @@ var Worksheet = class {
|
|
|
3583
4166
|
const validXml = __privateMethod(this, _Worksheet_instances, buildDataValidationsXml_fn).call(this);
|
|
3584
4167
|
const linksXml = __privateMethod(this, _Worksheet_instances, buildHyperlinksXml_fn).call(this, rels.hyperlinks);
|
|
3585
4168
|
const tailXml = __privateMethod(this, _Worksheet_instances, buildTailXml_fn).call(this, rels);
|
|
3586
|
-
|
|
4169
|
+
const xml2 = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3587
4170
|
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
3588
4171
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
3589
4172
|
<sheetViews>${sheetViewXml}</sheetViews>
|
|
3590
4173
|
${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${linksXml}${tailXml}
|
|
3591
4174
|
</worksheet>`;
|
|
4175
|
+
__privateSet(this, _xmlMemo, { at: __privateGet(this, _version2).v, xml: xml2 });
|
|
4176
|
+
return xml2;
|
|
3592
4177
|
}
|
|
3593
4178
|
/** Parses xl/worksheets/sheetN.xml and loads cells into memory. */
|
|
3594
4179
|
loadFromXml(xml2) {
|
|
4180
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3595
4181
|
__privateGet(this, _rows).clear();
|
|
3596
4182
|
__privateSet(this, _cols, []);
|
|
3597
4183
|
__privateSet(this, _mergeCells, []);
|
|
@@ -3613,6 +4199,8 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3613
4199
|
hidden: getAttr(colEl, "hidden") === "1"
|
|
3614
4200
|
});
|
|
3615
4201
|
}
|
|
4202
|
+
const sharedMasters = /* @__PURE__ */ new Map();
|
|
4203
|
+
const sharedInstances = [];
|
|
3616
4204
|
for (const rowEl of getElementsByTagName(root, "row")) {
|
|
3617
4205
|
const rowIndex = parseInt(getAttr(rowEl, "r") ?? "0", 10);
|
|
3618
4206
|
if (rowIndex < 1) continue;
|
|
@@ -3633,9 +4221,17 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3633
4221
|
ref.toUpperCase()
|
|
3634
4222
|
);
|
|
3635
4223
|
rowData.cells.set(ref.toUpperCase(), cellData);
|
|
4224
|
+
if (cellData.sharedIndex !== void 0) {
|
|
4225
|
+
if (cellData.formula !== void 0) {
|
|
4226
|
+
sharedMasters.set(cellData.sharedIndex, { formula: cellData.formula, ref: cellData.reference });
|
|
4227
|
+
} else {
|
|
4228
|
+
sharedInstances.push(cellData);
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
3636
4231
|
}
|
|
3637
4232
|
__privateGet(this, _rows).set(rowIndex, rowData);
|
|
3638
4233
|
}
|
|
4234
|
+
__privateMethod(this, _Worksheet_instances, resolveSharedFormulas_fn).call(this, sharedMasters, sharedInstances);
|
|
3639
4235
|
for (const mcEl of getElementsByTagName(root, "mergeCell")) {
|
|
3640
4236
|
const ref = getAttr(mcEl, "ref");
|
|
3641
4237
|
if (ref) __privateGet(this, _mergeCells).push({ ref });
|
|
@@ -3691,6 +4287,7 @@ ${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${lin
|
|
|
3691
4287
|
}
|
|
3692
4288
|
/** @internal Parses an `xl/comments*.xml` part into this sheet's comments. */
|
|
3693
4289
|
loadCommentsXml(xml2) {
|
|
4290
|
+
__privateMethod(this, _Worksheet_instances, touch_fn2).call(this);
|
|
3694
4291
|
const root = parseXml(xml2);
|
|
3695
4292
|
const authors = getElementsByTagName(root, "author").map((a) => a.textContent);
|
|
3696
4293
|
for (const cEl of getElementsByTagName(root, "comment")) {
|
|
@@ -3750,7 +4347,47 @@ _commentsDirty = new WeakMap();
|
|
|
3750
4347
|
_pendingHyperlinkRels = new WeakMap();
|
|
3751
4348
|
_preservedTail = new WeakMap();
|
|
3752
4349
|
_preservedRels = new WeakMap();
|
|
4350
|
+
_version2 = new WeakMap();
|
|
4351
|
+
_usedBoundsMemo = new WeakMap();
|
|
4352
|
+
_xmlMemo = new WeakMap();
|
|
3753
4353
|
_Worksheet_instances = new WeakSet();
|
|
4354
|
+
/** Records a change to this sheet's contents or layout. */
|
|
4355
|
+
touch_fn2 = function() {
|
|
4356
|
+
__privateGet(this, _version2).v++;
|
|
4357
|
+
};
|
|
4358
|
+
/**
|
|
4359
|
+
* The stored record for a cell, without creating it — the read paths below
|
|
4360
|
+
* work on this directly instead of wrapping it in a {@link Cell}. Formula
|
|
4361
|
+
* evaluation reads far more cells than it writes, and one wrapper per read is
|
|
4362
|
+
* pure garbage.
|
|
4363
|
+
*/
|
|
4364
|
+
findCellData_fn = function(reference) {
|
|
4365
|
+
const ref = normalizeRefFast(reference);
|
|
4366
|
+
return __privateGet(this, _rows).get(parseCellRef(ref).row)?.cells.get(ref);
|
|
4367
|
+
};
|
|
4368
|
+
/**
|
|
4369
|
+
* The extent, computed without touching the memo.
|
|
4370
|
+
*
|
|
4371
|
+
* Internal callers must use this. A mutator bumps the version once, at its
|
|
4372
|
+
* start, and then does its work; if it read the memoised getter part-way
|
|
4373
|
+
* through, the half-finished extent would be cached against the *new* version
|
|
4374
|
+
* and served as current for the rest of that version's life. That is precisely
|
|
4375
|
+
* how `insertColumns` came to report the extent it had before the insert —
|
|
4376
|
+
* caught by the round-trip fuzz test, not by any of the hand-written ones.
|
|
4377
|
+
*/
|
|
4378
|
+
computeUsedBounds_fn = function() {
|
|
4379
|
+
let rowCount = 0;
|
|
4380
|
+
let columnCount = 0;
|
|
4381
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
4382
|
+
if (row.cells.size === 0) continue;
|
|
4383
|
+
if (row.rowIndex > rowCount) rowCount = row.rowIndex;
|
|
4384
|
+
for (const ref of row.cells.keys()) {
|
|
4385
|
+
const col = columnFromRef(ref, refSplit(ref));
|
|
4386
|
+
if (col > columnCount) columnCount = col;
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
return { rowCount, columnCount };
|
|
4390
|
+
};
|
|
3754
4391
|
/** Removes and returns a cell's data record, pruning an emptied bare row. */
|
|
3755
4392
|
takeCellData_fn = function(ref) {
|
|
3756
4393
|
return __privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, parseCellRef(ref).row, ref);
|
|
@@ -3776,7 +4413,7 @@ applyStructural_fn = function(dim, kind, at, count) {
|
|
|
3776
4413
|
const shift = { dim, kind, at, count, sheetName: __privateGet(this, _name) };
|
|
3777
4414
|
if (kind === "insert") {
|
|
3778
4415
|
const limit = dim === "row" ? EXCEL_MAX_ROWS : EXCEL_MAX_COLUMNS;
|
|
3779
|
-
const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : this.
|
|
4416
|
+
const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : __privateMethod(this, _Worksheet_instances, computeUsedBounds_fn).call(this).columnCount;
|
|
3780
4417
|
if (used >= at && used + count > limit) {
|
|
3781
4418
|
throw new RangeError(`Insert would push populated cells past the sheet limit of ${limit}.`);
|
|
3782
4419
|
}
|
|
@@ -3992,6 +4629,34 @@ buildHyperlinksXml_fn = function(relIds) {
|
|
|
3992
4629
|
}).join("");
|
|
3993
4630
|
return `<hyperlinks>${inner}</hyperlinks>`;
|
|
3994
4631
|
};
|
|
4632
|
+
/**
|
|
4633
|
+
* Materialises the formula of every shared-formula instance cell.
|
|
4634
|
+
*
|
|
4635
|
+
* Excel writes a fill-down once: the group's master carries the text, the rest
|
|
4636
|
+
* carry `<f t="shared" si="n"/>` and are understood to hold the master's
|
|
4637
|
+
* formula with its relative references moved by the offset between the cells.
|
|
4638
|
+
* Read literally — the way this used to be read — an instance cell has no
|
|
4639
|
+
* formula at all, and the next save turns it into the constant that happened to
|
|
4640
|
+
* be cached in `<v>`. On a real workbook that is tens of thousands of formulas
|
|
4641
|
+
* quietly replaced by numbers.
|
|
4642
|
+
*
|
|
4643
|
+
* Each cell ends up owning its own text, so nothing downstream (evaluation,
|
|
4644
|
+
* structural edits, serialisation) needs to know the group ever existed.
|
|
4645
|
+
*/
|
|
4646
|
+
resolveSharedFormulas_fn = function(masters, instances) {
|
|
4647
|
+
if (instances.length === 0) return;
|
|
4648
|
+
for (const cd of instances) {
|
|
4649
|
+
const master = masters.get(cd.sharedIndex);
|
|
4650
|
+
if (!master) continue;
|
|
4651
|
+
const from = parseCellRef(master.ref);
|
|
4652
|
+
const to = parseCellRef(cd.reference);
|
|
4653
|
+
cd.formula = translateFormulaRefs(
|
|
4654
|
+
master.formula,
|
|
4655
|
+
to.row - from.row,
|
|
4656
|
+
to.column - from.column
|
|
4657
|
+
).text;
|
|
4658
|
+
}
|
|
4659
|
+
};
|
|
3995
4660
|
// ── Private XML builders ───────────────────────────────────────────────────
|
|
3996
4661
|
buildColsXml_fn = function() {
|
|
3997
4662
|
if (__privateGet(this, _cols).length === 0) return "";
|
|
@@ -4009,7 +4674,7 @@ buildSheetDataXml_fn = function() {
|
|
|
4009
4674
|
const ht = row.height !== void 0 && row.customHeight ? ` ht="${row.height}" customHeight="1"` : "";
|
|
4010
4675
|
const hidden = row.hidden ? ' hidden="1"' : "";
|
|
4011
4676
|
const sortedCells = [...row.cells.values()].map((cd) => ({ cd, col: columnFromRef(cd.reference, refSplit(cd.reference)) })).sort((a, b) => a.col - b.col);
|
|
4012
|
-
const cellsXml = sortedCells.map(({ cd }) =>
|
|
4677
|
+
const cellsXml = sortedCells.map(({ cd }) => buildCellXml(cd)).filter(Boolean).join("");
|
|
4013
4678
|
if (!cellsXml && !ht && !hidden) return "";
|
|
4014
4679
|
return `<row r="${row.rowIndex}"${ht}${hidden}>${cellsXml}</row>`;
|
|
4015
4680
|
}).filter(Boolean).join("");
|
|
@@ -4223,7 +4888,7 @@ indexOf_fn = function(name, scope) {
|
|
|
4223
4888
|
};
|
|
4224
4889
|
|
|
4225
4890
|
// src/model/workbook.ts
|
|
4226
|
-
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
4891
|
+
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _calcChain, _calcChainFingerprint, _deflateCache, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, sheetFingerprint_fn, calcChainStillValid_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
4227
4892
|
var _Workbook = class _Workbook {
|
|
4228
4893
|
constructor(sharedStrings, styles) {
|
|
4229
4894
|
__privateAdd(this, _Workbook_instances);
|
|
@@ -4232,6 +4897,24 @@ var _Workbook = class _Workbook {
|
|
|
4232
4897
|
__privateAdd(this, _sheets, []);
|
|
4233
4898
|
__privateAdd(this, _definedNames, new DefinedNameManager());
|
|
4234
4899
|
__privateAdd(this, _closed, false);
|
|
4900
|
+
/**
|
|
4901
|
+
* The calculation order Excel stored in `xl/calcChain.xml`, in file order, or
|
|
4902
|
+
* undefined when the source had no such part. Read-only to consumers: it is a
|
|
4903
|
+
* record of what the source file said, not a live view of the model.
|
|
4904
|
+
*/
|
|
4905
|
+
__privateAdd(this, _calcChain);
|
|
4906
|
+
/**
|
|
4907
|
+
* Fingerprint of the sheet set taken right after the calc chain was parsed.
|
|
4908
|
+
* The chain is only written back out when this still matches — see
|
|
4909
|
+
* {@link #calcChainStillValid}.
|
|
4910
|
+
*/
|
|
4911
|
+
__privateAdd(this, _calcChainFingerprint);
|
|
4912
|
+
/**
|
|
4913
|
+
* Compressed form of each package part from the last save. Compression is where
|
|
4914
|
+
* a save spends its time, and an autosave changes one part, so the rest are
|
|
4915
|
+
* reused verbatim. Keyed on content, so a part rebuilt identically still hits.
|
|
4916
|
+
*/
|
|
4917
|
+
__privateAdd(this, _deflateCache, createDeflateCache());
|
|
4235
4918
|
/** Parts carried over verbatim from the file this workbook was opened from. */
|
|
4236
4919
|
__privateAdd(this, _preservedParts, /* @__PURE__ */ new Map());
|
|
4237
4920
|
__privateAdd(this, _preservedContentTypes);
|
|
@@ -4256,6 +4939,7 @@ var _Workbook = class _Workbook {
|
|
|
4256
4939
|
* Mirrors Workbook.Open(stream) in C#.
|
|
4257
4940
|
*/
|
|
4258
4941
|
static fromBuffer(buffer) {
|
|
4942
|
+
var _a;
|
|
4259
4943
|
const entries = unzipXlsx(buffer);
|
|
4260
4944
|
const ss = new SharedStringManager();
|
|
4261
4945
|
const sm = new StyleManager();
|
|
@@ -4292,6 +4976,11 @@ var _Workbook = class _Workbook {
|
|
|
4292
4976
|
}
|
|
4293
4977
|
__privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
4294
4978
|
}
|
|
4979
|
+
const calcChainXml = readXmlEntry(entries, CALC_CHAIN_PATH);
|
|
4980
|
+
if (calcChainXml) {
|
|
4981
|
+
__privateSet(wb, _calcChain, parseCalcChain(calcChainXml, __privateGet(wb, _sheets)));
|
|
4982
|
+
__privateSet(wb, _calcChainFingerprint, __privateMethod(_a = wb, _Workbook_instances, sheetFingerprint_fn).call(_a));
|
|
4983
|
+
}
|
|
4295
4984
|
__privateSet(wb, _preservedParts, collectPreservedParts(entries));
|
|
4296
4985
|
const ctXml = readXmlEntry(entries, "[Content_Types].xml");
|
|
4297
4986
|
if (ctXml) __privateSet(wb, _preservedContentTypes, parseContentTypes(ctXml));
|
|
@@ -4338,8 +5027,8 @@ var _Workbook = class _Workbook {
|
|
|
4338
5027
|
if (__privateGet(this, _sheets).some((e) => e.worksheet.name === name)) {
|
|
4339
5028
|
throw new Error(`Worksheet "${name}" already exists.`);
|
|
4340
5029
|
}
|
|
4341
|
-
const sheetId = __privateGet(this, _sheets).
|
|
4342
|
-
const relId = `rId${
|
|
5030
|
+
const sheetId = __privateGet(this, _sheets).reduce((max, e) => Math.max(max, e.sheetId), 0) + 1;
|
|
5031
|
+
const relId = `rId${__privateGet(this, _sheets).length + 1}`;
|
|
4343
5032
|
const ws = new Worksheet(name, __privateGet(this, _sharedStrings3), __privateGet(this, _styles3));
|
|
4344
5033
|
__privateGet(this, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
4345
5034
|
return ws;
|
|
@@ -4448,7 +5137,21 @@ var _Workbook = class _Workbook {
|
|
|
4448
5137
|
saveAsBuffer() {
|
|
4449
5138
|
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
4450
5139
|
const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
|
|
4451
|
-
return
|
|
5140
|
+
return zipXlsxCached(entries, __privateGet(this, _deflateCache));
|
|
5141
|
+
}
|
|
5142
|
+
/**
|
|
5143
|
+
* Same output as {@link saveAsBuffer}, without blocking the calling thread on
|
|
5144
|
+
* compression — for an editor that autosaves, this is the difference between a
|
|
5145
|
+
* visible stall on every save and none.
|
|
5146
|
+
*
|
|
5147
|
+
* The XML for sheets that have not changed since the last save is reused, so a
|
|
5148
|
+
* save after a one-cell edit re-serialises one sheet rather than all of them.
|
|
5149
|
+
* That reuse applies to the synchronous path too; only the compression differs.
|
|
5150
|
+
*/
|
|
5151
|
+
async saveAsBufferAsync() {
|
|
5152
|
+
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
5153
|
+
const entries = __privateMethod(this, _Workbook_instances, buildZipEntries_fn).call(this);
|
|
5154
|
+
return zipXlsxCachedAsync(entries, __privateGet(this, _deflateCache));
|
|
4452
5155
|
}
|
|
4453
5156
|
/**
|
|
4454
5157
|
* Saves the workbook to a file (Node.js only).
|
|
@@ -4456,7 +5159,7 @@ var _Workbook = class _Workbook {
|
|
|
4456
5159
|
*/
|
|
4457
5160
|
async saveToFile(filePath) {
|
|
4458
5161
|
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
4459
|
-
const buffer = this.
|
|
5162
|
+
const buffer = await this.saveAsBufferAsync();
|
|
4460
5163
|
const fs = await import('fs/promises');
|
|
4461
5164
|
const path = await import('path');
|
|
4462
5165
|
const dir = path.dirname(filePath);
|
|
@@ -4472,12 +5175,29 @@ var _Workbook = class _Workbook {
|
|
|
4472
5175
|
[Symbol.dispose]() {
|
|
4473
5176
|
this.close();
|
|
4474
5177
|
}
|
|
5178
|
+
// ── Calculation order ──────────────────────────────────────────────────────
|
|
5179
|
+
/**
|
|
5180
|
+
* The calculation order Excel recorded for this workbook, oldest-first as
|
|
5181
|
+
* stored in `xl/calcChain.xml`, or an empty array when the source file had
|
|
5182
|
+
* none (a freshly created workbook, or one saved by a tool that omits it).
|
|
5183
|
+
*
|
|
5184
|
+
* Excel has already done the topological sort that produced this order, so
|
|
5185
|
+
* evaluating in it avoids repeating that work. Treat it as a hint, not a
|
|
5186
|
+
* guarantee: it reflects the file as opened, and edits made since are not
|
|
5187
|
+
* reflected here.
|
|
5188
|
+
*/
|
|
5189
|
+
get calcChain() {
|
|
5190
|
+
return __privateGet(this, _calcChain) ?? EMPTY_CALC_CHAIN;
|
|
5191
|
+
}
|
|
4475
5192
|
};
|
|
4476
5193
|
_sharedStrings3 = new WeakMap();
|
|
4477
5194
|
_styles3 = new WeakMap();
|
|
4478
5195
|
_sheets = new WeakMap();
|
|
4479
5196
|
_definedNames = new WeakMap();
|
|
4480
5197
|
_closed = new WeakMap();
|
|
5198
|
+
_calcChain = new WeakMap();
|
|
5199
|
+
_calcChainFingerprint = new WeakMap();
|
|
5200
|
+
_deflateCache = new WeakMap();
|
|
4481
5201
|
_preservedParts = new WeakMap();
|
|
4482
5202
|
_preservedContentTypes = new WeakMap();
|
|
4483
5203
|
_orphanedTargets = new WeakMap();
|
|
@@ -4499,6 +5219,28 @@ structuralEdit_fn = function(sheetName, dim, kind, at, count) {
|
|
|
4499
5219
|
}
|
|
4500
5220
|
return { refErrors };
|
|
4501
5221
|
};
|
|
5222
|
+
/**
|
|
5223
|
+
* Identity of the sheet set: names, durable ids and per-sheet mutation counts.
|
|
5224
|
+
* The calc chain is a statement about exactly this set — which cells hold
|
|
5225
|
+
* formulas, on which sheet, under which id — so any change to it invalidates
|
|
5226
|
+
* the chain wholesale.
|
|
5227
|
+
*/
|
|
5228
|
+
sheetFingerprint_fn = function() {
|
|
5229
|
+
return __privateGet(this, _sheets).map((e) => `${e.sheetId}:${e.worksheet.name}:${e.worksheet.version}`).join("|");
|
|
5230
|
+
};
|
|
5231
|
+
/**
|
|
5232
|
+
* Whether the parsed calc chain may be written back out.
|
|
5233
|
+
*
|
|
5234
|
+
* Deliberately conservative: any edit at all (a value, a formula, a sheet
|
|
5235
|
+
* added or removed) and the chain is dropped instead of patched. An absent
|
|
5236
|
+
* calcChain is a documented no-op — Excel rebuilds it on open — whereas a
|
|
5237
|
+
* chain that disagrees with the sheets is the failure mode this whole change
|
|
5238
|
+
* exists to remove. Rebuilding a correct chain would mean redoing Excel's
|
|
5239
|
+
* dependency analysis, which belongs in the formula package, not here.
|
|
5240
|
+
*/
|
|
5241
|
+
calcChainStillValid_fn = function() {
|
|
5242
|
+
return __privateGet(this, _calcChain) !== void 0 && __privateGet(this, _calcChain).length > 0 && __privateGet(this, _calcChainFingerprint) === __privateMethod(this, _Workbook_instances, sheetFingerprint_fn).call(this);
|
|
5243
|
+
};
|
|
4502
5244
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
4503
5245
|
assertOpen_fn = function() {
|
|
4504
5246
|
if (__privateGet(this, _closed)) throw new Error("Workbook has been closed.");
|
|
@@ -4559,7 +5301,7 @@ buildZipEntries_fn = function() {
|
|
|
4559
5301
|
entries.set("_rels/.rels", ROOT_RELS);
|
|
4560
5302
|
const sheetMetas = __privateGet(this, _sheets).map((e, i) => ({
|
|
4561
5303
|
name: e.worksheet.name,
|
|
4562
|
-
sheetId:
|
|
5304
|
+
sheetId: e.sheetId,
|
|
4563
5305
|
relationshipId: `rId${i + 1}`,
|
|
4564
5306
|
state: e.worksheet.visibility !== "visible" ? e.worksheet.visibility : void 0
|
|
4565
5307
|
}));
|
|
@@ -4572,7 +5314,15 @@ buildZipEntries_fn = function() {
|
|
|
4572
5314
|
return { name: dn.name, refersTo: dn.refersTo, localSheetId };
|
|
4573
5315
|
});
|
|
4574
5316
|
entries.set("xl/workbook.xml", buildWorkbookXml(sheetMetas, definedNameMetas));
|
|
4575
|
-
|
|
5317
|
+
const keepCalcChain = __privateMethod(this, _Workbook_instances, calcChainStillValid_fn).call(this);
|
|
5318
|
+
if (keepCalcChain) {
|
|
5319
|
+
const idBySheet = new Map(__privateGet(this, _sheets).map((e) => [e.worksheet.name, e.sheetId]));
|
|
5320
|
+
entries.set(CALC_CHAIN_PATH, buildCalcChainXml(__privateGet(this, _calcChain), idBySheet));
|
|
5321
|
+
} else {
|
|
5322
|
+
entries.delete(CALC_CHAIN_PATH);
|
|
5323
|
+
preservedPaths.delete(CALC_CHAIN_PATH);
|
|
5324
|
+
}
|
|
5325
|
+
entries.set("xl/_rels/workbook.xml.rels", buildWorkbookRels(sheetCount, keepCalcChain));
|
|
4576
5326
|
entries.set("xl/styles.xml", __privateGet(this, _styles3).buildStylesXml());
|
|
4577
5327
|
entries.set("xl/sharedStrings.xml", __privateGet(this, _sharedStrings3).buildXml());
|
|
4578
5328
|
for (let i = 0; i < __privateGet(this, _sheets).length; i++) {
|
|
@@ -4580,6 +5330,9 @@ buildZipEntries_fn = function() {
|
|
|
4580
5330
|
}
|
|
4581
5331
|
const extraOverrides = [];
|
|
4582
5332
|
const extraDefaults = [];
|
|
5333
|
+
if (keepCalcChain) {
|
|
5334
|
+
extraOverrides.push({ partName: `/${CALC_CHAIN_PATH}`, contentType: CONTENT_TYPES.calcChain });
|
|
5335
|
+
}
|
|
4583
5336
|
let drawingCounter = 0;
|
|
4584
5337
|
let partCounter = 0;
|
|
4585
5338
|
let commentsCounter = 0;
|
|
@@ -4655,6 +5408,42 @@ buildZipEntries_fn = function() {
|
|
|
4655
5408
|
return entries;
|
|
4656
5409
|
};
|
|
4657
5410
|
var Workbook = _Workbook;
|
|
5411
|
+
var CALC_CHAIN_PATH = "xl/calcChain.xml";
|
|
5412
|
+
var EMPTY_CALC_CHAIN = [];
|
|
5413
|
+
function parseCalcChain(xml2, sheets) {
|
|
5414
|
+
const nameById = new Map(sheets.map((e) => [e.sheetId, e.worksheet.name]));
|
|
5415
|
+
const out = [];
|
|
5416
|
+
let currentId;
|
|
5417
|
+
try {
|
|
5418
|
+
const root = parseXml(xml2);
|
|
5419
|
+
for (const el of getElementsByTagName(root, "c")) {
|
|
5420
|
+
const ref = getAttr(el, "r");
|
|
5421
|
+
if (!ref) continue;
|
|
5422
|
+
const iAttr = getAttr(el, "i");
|
|
5423
|
+
if (iAttr !== void 0) {
|
|
5424
|
+
const parsed = parseInt(iAttr, 10);
|
|
5425
|
+
if (!Number.isNaN(parsed)) currentId = parsed;
|
|
5426
|
+
}
|
|
5427
|
+
if (currentId === void 0) continue;
|
|
5428
|
+
const sheet = nameById.get(currentId);
|
|
5429
|
+
if (sheet === void 0) continue;
|
|
5430
|
+
out.push({ sheet, ref: ref.toUpperCase() });
|
|
5431
|
+
}
|
|
5432
|
+
} catch {
|
|
5433
|
+
return [];
|
|
5434
|
+
}
|
|
5435
|
+
return out;
|
|
5436
|
+
}
|
|
5437
|
+
function buildCalcChainXml(chain, idBySheet) {
|
|
5438
|
+
const cells = [];
|
|
5439
|
+
for (const { sheet, ref } of chain) {
|
|
5440
|
+
const id = idBySheet.get(sheet);
|
|
5441
|
+
if (id === void 0) continue;
|
|
5442
|
+
cells.push(`<c r="${ref}" i="${id}"/>`);
|
|
5443
|
+
}
|
|
5444
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
5445
|
+
<calcChain xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">${cells.join("")}</calcChain>`;
|
|
5446
|
+
}
|
|
4658
5447
|
function resolveWorksheetPath(relsXml, relId) {
|
|
4659
5448
|
if (!relsXml) return void 0;
|
|
4660
5449
|
try {
|