@devmm/puredocs-excel 1.0.5 → 1.0.6
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 +1572 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +446 -9
- package/dist/index.d.ts +446 -9
- package/dist/index.js +1563 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,10 +48,10 @@ function parseMinimal(xmlString) {
|
|
|
48
48
|
}
|
|
49
49
|
function parseAttributes(raw) {
|
|
50
50
|
const attrs = {};
|
|
51
|
-
const re = /(
|
|
51
|
+
const re = /([^\s=/>]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
52
52
|
let m;
|
|
53
53
|
while ((m = re.exec(raw)) !== null) {
|
|
54
|
-
attrs[m[1]] = m[2]
|
|
54
|
+
attrs[m[1]] = decodeXmlEntities(m[2] ?? m[3] ?? "");
|
|
55
55
|
}
|
|
56
56
|
return attrs;
|
|
57
57
|
}
|
|
@@ -105,7 +105,7 @@ function parseMinimal(xmlString) {
|
|
|
105
105
|
} else {
|
|
106
106
|
const textStart = pos;
|
|
107
107
|
skipUntil("<");
|
|
108
|
-
const text = xmlString.slice(textStart, pos)
|
|
108
|
+
const text = decodeXmlEntities(xmlString.slice(textStart, pos));
|
|
109
109
|
if (stack.length > 0 && text.trim()) {
|
|
110
110
|
stack[stack.length - 1].textContent += text;
|
|
111
111
|
}
|
|
@@ -114,6 +114,10 @@ function parseMinimal(xmlString) {
|
|
|
114
114
|
if (!root) throw new Error("XML parse error: empty document.");
|
|
115
115
|
return root;
|
|
116
116
|
}
|
|
117
|
+
function decodeXmlEntities(value) {
|
|
118
|
+
if (value.indexOf("&") === -1) return value;
|
|
119
|
+
return value.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
120
|
+
}
|
|
117
121
|
function parseXml(xmlString) {
|
|
118
122
|
if (typeof DOMParser !== "undefined") {
|
|
119
123
|
return parseDom(xmlString);
|
|
@@ -294,7 +298,9 @@ var REL_TYPES = {
|
|
|
294
298
|
styles: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
295
299
|
chart: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
|
|
296
300
|
drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
297
|
-
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
|
301
|
+
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
302
|
+
comments: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
|
303
|
+
vmlDrawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
|
298
304
|
};
|
|
299
305
|
var CONTENT_TYPES = {
|
|
300
306
|
workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
@@ -304,7 +310,9 @@ var CONTENT_TYPES = {
|
|
|
304
310
|
drawing: "application/vnd.openxmlformats-officedocument.drawing+xml",
|
|
305
311
|
chart: "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
|
|
306
312
|
relationships: "application/vnd.openxmlformats-package.relationships+xml",
|
|
307
|
-
coreProperties: "application/vnd.openxmlformats-package.core-properties+xml"
|
|
313
|
+
coreProperties: "application/vnd.openxmlformats-package.core-properties+xml",
|
|
314
|
+
comments: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
|
315
|
+
vmlDrawing: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
308
316
|
};
|
|
309
317
|
|
|
310
318
|
// src/io/ooxml-templates.ts
|
|
@@ -449,8 +457,9 @@ function parseRelationships(relsXml, ownerPath) {
|
|
|
449
457
|
const tag = m[0];
|
|
450
458
|
const id = /\bId="([^"]*)"/.exec(tag)?.[1];
|
|
451
459
|
const type = /\bType="([^"]*)"/.exec(tag)?.[1];
|
|
452
|
-
const
|
|
453
|
-
if (!id || !type ||
|
|
460
|
+
const rawTarget = /\bTarget="([^"]*)"/.exec(tag)?.[1];
|
|
461
|
+
if (!id || !type || rawTarget === void 0) continue;
|
|
462
|
+
const target = decodeXmlEntities(rawTarget);
|
|
454
463
|
const external = /TargetMode="External"/.test(tag);
|
|
455
464
|
rels.push({
|
|
456
465
|
id,
|
|
@@ -1669,10 +1678,15 @@ function splitSections(code) {
|
|
|
1669
1678
|
sections.push(current);
|
|
1670
1679
|
return sections;
|
|
1671
1680
|
}
|
|
1681
|
+
function hasDateTokens(section) {
|
|
1682
|
+
const bare = section.replace(/"[^"]*"/g, "").replace(/\\./g, "").replace(/\[[^\]]*\]/g, "");
|
|
1683
|
+
return /[ymdhsYMDHS]/.test(bare);
|
|
1684
|
+
}
|
|
1672
1685
|
function isUnsupported(section) {
|
|
1673
1686
|
if (/\[[<>=]/.test(section)) return true;
|
|
1674
1687
|
if (/[?]/.test(section)) return true;
|
|
1675
1688
|
if (/[Ee][+-]/.test(section)) return true;
|
|
1689
|
+
if (hasDateTokens(section)) return true;
|
|
1676
1690
|
return false;
|
|
1677
1691
|
}
|
|
1678
1692
|
function parseSection(section) {
|
|
@@ -1766,12 +1780,177 @@ function formatNumberWithCode(value, formatCode) {
|
|
|
1766
1780
|
return renderNumber(magnitude, parseSection(section));
|
|
1767
1781
|
}
|
|
1768
1782
|
|
|
1783
|
+
// src/style/date-format-renderer.ts
|
|
1784
|
+
var MONTHS = [
|
|
1785
|
+
"January",
|
|
1786
|
+
"February",
|
|
1787
|
+
"March",
|
|
1788
|
+
"April",
|
|
1789
|
+
"May",
|
|
1790
|
+
"June",
|
|
1791
|
+
"July",
|
|
1792
|
+
"August",
|
|
1793
|
+
"September",
|
|
1794
|
+
"October",
|
|
1795
|
+
"November",
|
|
1796
|
+
"December"
|
|
1797
|
+
];
|
|
1798
|
+
var DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
1799
|
+
function firstSection(code) {
|
|
1800
|
+
let inQuote = false;
|
|
1801
|
+
for (let i = 0; i < code.length; i++) {
|
|
1802
|
+
const ch = code[i];
|
|
1803
|
+
if (ch === '"') inQuote = !inQuote;
|
|
1804
|
+
else if (ch === ";" && !inQuote) return code.slice(0, i);
|
|
1805
|
+
}
|
|
1806
|
+
return code;
|
|
1807
|
+
}
|
|
1808
|
+
function tokenize(section) {
|
|
1809
|
+
const tokens = [];
|
|
1810
|
+
let i = 0;
|
|
1811
|
+
const n = section.length;
|
|
1812
|
+
while (i < n) {
|
|
1813
|
+
const ch = section[i];
|
|
1814
|
+
if (ch === '"') {
|
|
1815
|
+
let j = i + 1;
|
|
1816
|
+
let text = "";
|
|
1817
|
+
while (j < n && section[j] !== '"') text += section[j++];
|
|
1818
|
+
tokens.push({ kind: "literal", text });
|
|
1819
|
+
i = j + 1;
|
|
1820
|
+
continue;
|
|
1821
|
+
}
|
|
1822
|
+
if (ch === "\\") {
|
|
1823
|
+
tokens.push({ kind: "literal", text: section[i + 1] ?? "" });
|
|
1824
|
+
i += 2;
|
|
1825
|
+
continue;
|
|
1826
|
+
}
|
|
1827
|
+
if (ch === "[") {
|
|
1828
|
+
const close = section.indexOf("]", i);
|
|
1829
|
+
if (close === -1) return null;
|
|
1830
|
+
const inner = section.slice(i + 1, close);
|
|
1831
|
+
if (/^[hmsHMS]+$/.test(inner)) return null;
|
|
1832
|
+
i = close + 1;
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
const rest = section.slice(i);
|
|
1836
|
+
const ampm = /^(AM\/PM|A\/P|am\/pm|a\/p)/.exec(rest);
|
|
1837
|
+
if (ampm) {
|
|
1838
|
+
tokens.push({ kind: "ampm", text: ampm[1] });
|
|
1839
|
+
i += ampm[1].length;
|
|
1840
|
+
continue;
|
|
1841
|
+
}
|
|
1842
|
+
const lower = ch.toLowerCase();
|
|
1843
|
+
if ("ymdhs".includes(lower)) {
|
|
1844
|
+
let j = i;
|
|
1845
|
+
while (j < n && section[j].toLowerCase() === lower) j++;
|
|
1846
|
+
tokens.push({ kind: lower, text: section.slice(i, j) });
|
|
1847
|
+
i = j;
|
|
1848
|
+
if (lower === "s" && section[i] === "." && /\d|0/.test(section[i + 1] ?? "")) return null;
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1851
|
+
if (/[a-zA-Z]/.test(ch)) return null;
|
|
1852
|
+
tokens.push({ kind: "literal", text: ch });
|
|
1853
|
+
i++;
|
|
1854
|
+
}
|
|
1855
|
+
return tokens;
|
|
1856
|
+
}
|
|
1857
|
+
function resolveMonthVsMinute(tokens) {
|
|
1858
|
+
const dateKinds = /* @__PURE__ */ new Set(["y", "m", "d", "h", "s"]);
|
|
1859
|
+
for (let t = 0; t < tokens.length; t++) {
|
|
1860
|
+
const token = tokens[t];
|
|
1861
|
+
if (token.kind !== "m" || token.text.length > 2) continue;
|
|
1862
|
+
let prev;
|
|
1863
|
+
for (let p = t - 1; p >= 0; p--) {
|
|
1864
|
+
if (dateKinds.has(tokens[p].kind)) {
|
|
1865
|
+
prev = tokens[p];
|
|
1866
|
+
break;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
let next;
|
|
1870
|
+
for (let q = t + 1; q < tokens.length; q++) {
|
|
1871
|
+
if (dateKinds.has(tokens[q].kind)) {
|
|
1872
|
+
next = tokens[q];
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
if (prev?.kind === "h" || next?.kind === "s") token.kind = "minute";
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
var pad = (v, len) => String(v).padStart(len, "0");
|
|
1880
|
+
function formatDateCode(date, formatCode) {
|
|
1881
|
+
if (!formatCode || formatCode === "General") return null;
|
|
1882
|
+
const tokens = tokenize(firstSection(formatCode));
|
|
1883
|
+
if (!tokens) return null;
|
|
1884
|
+
if (!tokens.some((t) => "ymdhs".includes(t.kind))) return null;
|
|
1885
|
+
resolveMonthVsMinute(tokens);
|
|
1886
|
+
const twelveHour = tokens.some((t) => t.kind === "ampm");
|
|
1887
|
+
const hours24 = date.getHours();
|
|
1888
|
+
const hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
|
1889
|
+
let out = "";
|
|
1890
|
+
for (const t of tokens) {
|
|
1891
|
+
switch (t.kind) {
|
|
1892
|
+
case "literal":
|
|
1893
|
+
out += t.text;
|
|
1894
|
+
break;
|
|
1895
|
+
case "y":
|
|
1896
|
+
out += t.text.length <= 2 ? pad(date.getFullYear() % 100, 2) : String(date.getFullYear());
|
|
1897
|
+
break;
|
|
1898
|
+
case "m": {
|
|
1899
|
+
const month = date.getMonth();
|
|
1900
|
+
if (t.text.length === 1) out += String(month + 1);
|
|
1901
|
+
else if (t.text.length === 2) out += pad(month + 1, 2);
|
|
1902
|
+
else if (t.text.length === 3) out += MONTHS[month].slice(0, 3);
|
|
1903
|
+
else if (t.text.length === 5) out += MONTHS[month][0];
|
|
1904
|
+
else out += MONTHS[month];
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
case "minute":
|
|
1908
|
+
out += t.text.length === 1 ? String(date.getMinutes()) : pad(date.getMinutes(), 2);
|
|
1909
|
+
break;
|
|
1910
|
+
case "d": {
|
|
1911
|
+
const day = date.getDate();
|
|
1912
|
+
if (t.text.length === 1) out += String(day);
|
|
1913
|
+
else if (t.text.length === 2) out += pad(day, 2);
|
|
1914
|
+
else if (t.text.length === 3) out += DAYS[date.getDay()].slice(0, 3);
|
|
1915
|
+
else out += DAYS[date.getDay()];
|
|
1916
|
+
break;
|
|
1917
|
+
}
|
|
1918
|
+
case "h": {
|
|
1919
|
+
const h = twelveHour ? hours12 : hours24;
|
|
1920
|
+
out += t.text.length === 1 ? String(h) : pad(h, 2);
|
|
1921
|
+
break;
|
|
1922
|
+
}
|
|
1923
|
+
case "s":
|
|
1924
|
+
out += t.text.length === 1 ? String(date.getSeconds()) : pad(date.getSeconds(), 2);
|
|
1925
|
+
break;
|
|
1926
|
+
case "ampm": {
|
|
1927
|
+
const isAm = hours24 < 12;
|
|
1928
|
+
if (t.text === "a/p") out += isAm ? "a" : "p";
|
|
1929
|
+
else if (t.text === "A/P") out += isAm ? "A" : "P";
|
|
1930
|
+
else if (t.text === "am/pm") out += isAm ? "am" : "pm";
|
|
1931
|
+
else out += isAm ? "AM" : "PM";
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return out;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1769
1939
|
// src/model/cell-reference.ts
|
|
1940
|
+
var EXCEL_MAX_ROWS = 1048576;
|
|
1941
|
+
var EXCEL_MAX_COLUMNS = 16384;
|
|
1942
|
+
function needsNormalizing(ref) {
|
|
1943
|
+
for (let i = 0; i < ref.length; i++) {
|
|
1944
|
+
const code = ref.charCodeAt(i);
|
|
1945
|
+
if (code === 36 || code >= 97 && code <= 122) return true;
|
|
1946
|
+
}
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1770
1949
|
function parseCellRef(cellReference) {
|
|
1771
1950
|
if (!cellReference) {
|
|
1772
1951
|
throw new Error("Cell reference cannot be null or empty.");
|
|
1773
1952
|
}
|
|
1774
|
-
const ref = cellReference.replace(/\$/g, "").toUpperCase();
|
|
1953
|
+
const ref = needsNormalizing(cellReference) ? cellReference.replace(/\$/g, "").toUpperCase() : cellReference;
|
|
1775
1954
|
let i = 0;
|
|
1776
1955
|
let column = 0;
|
|
1777
1956
|
while (i < ref.length && ref.charCodeAt(i) >= 65 && ref.charCodeAt(i) <= 90) {
|
|
@@ -1817,6 +1996,11 @@ function columnNumber(letters) {
|
|
|
1817
1996
|
}
|
|
1818
1997
|
return col;
|
|
1819
1998
|
}
|
|
1999
|
+
function formatRangeRef(startRow, startColumn, endRow, endColumn) {
|
|
2000
|
+
const start = cellRefFromRowCol(startRow, startColumn);
|
|
2001
|
+
if (startRow === endRow && startColumn === endColumn) return start;
|
|
2002
|
+
return `${start}:${cellRefFromRowCol(endRow, endColumn)}`;
|
|
2003
|
+
}
|
|
1820
2004
|
function parseRangeRef(rangeReference) {
|
|
1821
2005
|
const parts = rangeReference.split(":");
|
|
1822
2006
|
if (parts.length !== 2) {
|
|
@@ -1977,18 +2161,20 @@ var Cell = class {
|
|
|
1977
2161
|
}
|
|
1978
2162
|
/**
|
|
1979
2163
|
* Returns the display text with the cell's number format applied, e.g. a
|
|
1980
|
-
* value of 4.5 under `$#,##0.00` renders as `$4.50
|
|
2164
|
+
* value of 4.5 under `$#,##0.00` renders as `$4.50`, and a date under
|
|
2165
|
+
* `dd/mm/yyyy` as `31/01/2025`.
|
|
1981
2166
|
*
|
|
1982
|
-
* Falls back to {@link getText} for
|
|
1983
|
-
* outside the supported subset (fractions, scientific notation,
|
|
1984
|
-
* sections) — so the result is never a misleading
|
|
2167
|
+
* Falls back to {@link getText} for values with no format and for format
|
|
2168
|
+
* codes outside the supported subset (fractions, scientific notation,
|
|
2169
|
+
* conditional sections, elapsed time) — so the result is never a misleading
|
|
2170
|
+
* rendering.
|
|
1985
2171
|
*/
|
|
1986
2172
|
getFormattedText() {
|
|
1987
2173
|
const val = this.getValue();
|
|
1988
|
-
if (typeof val !== "number") return this.getText();
|
|
2174
|
+
if (typeof val !== "number" && !(val instanceof Date)) return this.getText();
|
|
1989
2175
|
const format = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex).numberFormat;
|
|
1990
2176
|
if (!format) return this.getText();
|
|
1991
|
-
return formatNumberWithCode(val, format.formatCode) ?? this.getText();
|
|
2177
|
+
return val instanceof Date ? formatDateCode(val, format.formatCode) ?? this.getText() : formatNumberWithCode(val, format.formatCode) ?? this.getText();
|
|
1992
2178
|
}
|
|
1993
2179
|
// ── Formula API ────────────────────────────────────────────────────────────
|
|
1994
2180
|
/**
|
|
@@ -2173,8 +2359,269 @@ function escXml(s) {
|
|
|
2173
2359
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2174
2360
|
}
|
|
2175
2361
|
|
|
2362
|
+
// src/model/ref-shift.ts
|
|
2363
|
+
function shiftIndex(index, shift) {
|
|
2364
|
+
if (shift.kind === "insert") {
|
|
2365
|
+
return index >= shift.at ? index + shift.count : index;
|
|
2366
|
+
}
|
|
2367
|
+
const bandEnd = shift.at + shift.count - 1;
|
|
2368
|
+
if (index < shift.at) return index;
|
|
2369
|
+
if (index <= bandEnd) return null;
|
|
2370
|
+
return index - shift.count;
|
|
2371
|
+
}
|
|
2372
|
+
function shiftSpan(start, end, shift) {
|
|
2373
|
+
if (shift.kind === "insert") {
|
|
2374
|
+
return {
|
|
2375
|
+
start: start >= shift.at ? start + shift.count : start,
|
|
2376
|
+
end: end >= shift.at ? end + shift.count : end
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
const bandEnd = shift.at + shift.count - 1;
|
|
2380
|
+
const newStart = start < shift.at ? start : start <= bandEnd ? shift.at : start - shift.count;
|
|
2381
|
+
const newEnd = end < shift.at ? end : end <= bandEnd ? shift.at - 1 : end - shift.count;
|
|
2382
|
+
return newEnd < newStart ? null : { start: newStart, end: newEnd };
|
|
2383
|
+
}
|
|
2384
|
+
function shiftRangeRef(ref, shift) {
|
|
2385
|
+
const [startRef, endRef] = ref.split(":");
|
|
2386
|
+
const start = parseCellRef(startRef);
|
|
2387
|
+
const end = endRef !== void 0 ? parseCellRef(endRef) : start;
|
|
2388
|
+
const rows = shift.dim === "row" ? shiftSpan(start.row, end.row, shift) : { start: start.row, end: end.row };
|
|
2389
|
+
const cols = shift.dim === "col" ? shiftSpan(start.column, end.column, shift) : { start: start.column, end: end.column };
|
|
2390
|
+
if (!rows || !cols) return null;
|
|
2391
|
+
const startOut = cellRefFromRowCol(rows.start, cols.start);
|
|
2392
|
+
if (endRef === void 0) return startOut;
|
|
2393
|
+
return `${startOut}:${cellRefFromRowCol(rows.end, cols.end)}`;
|
|
2394
|
+
}
|
|
2395
|
+
function shiftSqref(sqref, shift) {
|
|
2396
|
+
const surviving = sqref.split(/\s+/).filter(Boolean).map((part) => shiftRangeRef(part, shift)).filter((part) => part !== null);
|
|
2397
|
+
return surviving.length > 0 ? surviving.join(" ") : null;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
// src/model/formula-ref-shift.ts
|
|
2401
|
+
var MAX_ROW = EXCEL_MAX_ROWS;
|
|
2402
|
+
var MAX_COL = EXCEL_MAX_COLUMNS;
|
|
2403
|
+
function isWordChar(code) {
|
|
2404
|
+
return code >= 65 && code <= 90 || // A-Z
|
|
2405
|
+
code >= 97 && code <= 122 || // a-z
|
|
2406
|
+
code >= 48 && code <= 57 || // 0-9
|
|
2407
|
+
code === 95 || // _
|
|
2408
|
+
code === 46 || // .
|
|
2409
|
+
code === 36;
|
|
2410
|
+
}
|
|
2411
|
+
function parseCellComponent(word) {
|
|
2412
|
+
const m = /^(\$?)([A-Za-z]{1,3})(\$?)(\d+)$/.exec(word);
|
|
2413
|
+
if (!m) return null;
|
|
2414
|
+
const col = columnNumber(m[2]);
|
|
2415
|
+
const row = parseInt(m[4], 10);
|
|
2416
|
+
if (col > MAX_COL || row < 1 || row > MAX_ROW) return null;
|
|
2417
|
+
return { colAbs: m[1] === "$", col, rowAbs: m[3] === "$", row };
|
|
2418
|
+
}
|
|
2419
|
+
function parseColComponent(word) {
|
|
2420
|
+
const m = /^(\$?)([A-Za-z]{1,3})$/.exec(word);
|
|
2421
|
+
if (!m) return null;
|
|
2422
|
+
const col = columnNumber(m[2]);
|
|
2423
|
+
if (col > MAX_COL) return null;
|
|
2424
|
+
return { abs: m[1] === "$", col };
|
|
2425
|
+
}
|
|
2426
|
+
function parseRowComponent(word) {
|
|
2427
|
+
const m = /^(\$?)(\d+)$/.exec(word);
|
|
2428
|
+
if (!m) return null;
|
|
2429
|
+
const row = parseInt(m[2], 10);
|
|
2430
|
+
if (row < 1 || row > MAX_ROW) return null;
|
|
2431
|
+
return { abs: m[1] === "$", row };
|
|
2432
|
+
}
|
|
2433
|
+
function emitCell(c, row, col) {
|
|
2434
|
+
return `${c.colAbs ? "$" : ""}${columnLetter(col)}${c.rowAbs ? "$" : ""}${row}`;
|
|
2435
|
+
}
|
|
2436
|
+
function shiftFormulaRefs(formula, shift, formulaSheet) {
|
|
2437
|
+
const n = formula.length;
|
|
2438
|
+
const editedSheet = shift.sheetName.toUpperCase();
|
|
2439
|
+
const unqualifiedRewritable = formulaSheet.toUpperCase() === editedSheet;
|
|
2440
|
+
const isRowShift = shift.dim === "row";
|
|
2441
|
+
let out = "";
|
|
2442
|
+
let flushed = 0;
|
|
2443
|
+
let changed = false;
|
|
2444
|
+
let hasRefError = false;
|
|
2445
|
+
let i = 0;
|
|
2446
|
+
const replace = (start, text) => {
|
|
2447
|
+
if (text.length === i - start && formula.startsWith(text, start)) return;
|
|
2448
|
+
out += formula.slice(flushed, start) + text;
|
|
2449
|
+
flushed = i;
|
|
2450
|
+
changed = true;
|
|
2451
|
+
};
|
|
2452
|
+
const readWord = () => {
|
|
2453
|
+
let j = i;
|
|
2454
|
+
while (j < n && isWordChar(formula.charCodeAt(j))) j++;
|
|
2455
|
+
const w = formula.slice(i, j);
|
|
2456
|
+
i = j;
|
|
2457
|
+
return w;
|
|
2458
|
+
};
|
|
2459
|
+
const readQuoted = () => {
|
|
2460
|
+
let j = i + 1;
|
|
2461
|
+
while (j < n) {
|
|
2462
|
+
if (formula[j] === "'") {
|
|
2463
|
+
if (formula[j + 1] === "'") {
|
|
2464
|
+
j += 2;
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
j++;
|
|
2468
|
+
break;
|
|
2469
|
+
}
|
|
2470
|
+
j++;
|
|
2471
|
+
}
|
|
2472
|
+
const q = formula.slice(i, j);
|
|
2473
|
+
i = j;
|
|
2474
|
+
return q;
|
|
2475
|
+
};
|
|
2476
|
+
const consumeRef = (refStart, rewritable) => {
|
|
2477
|
+
const first = readWord();
|
|
2478
|
+
let firstCell = parseCellComponent(first);
|
|
2479
|
+
let firstCol = firstCell ? null : parseColComponent(first);
|
|
2480
|
+
let firstRow = firstCell || firstCol ? null : parseRowComponent(first);
|
|
2481
|
+
let second = null;
|
|
2482
|
+
let secondCell = null;
|
|
2483
|
+
let secondCol = null;
|
|
2484
|
+
let secondRow = null;
|
|
2485
|
+
if (formula[i] === ":" && i + 1 < n && isWordChar(formula.charCodeAt(i + 1))) {
|
|
2486
|
+
const save = i;
|
|
2487
|
+
i++;
|
|
2488
|
+
const w = readWord();
|
|
2489
|
+
if (firstCell) secondCell = parseCellComponent(w);
|
|
2490
|
+
else if (firstCol) secondCol = parseColComponent(w);
|
|
2491
|
+
else if (firstRow) secondRow = parseRowComponent(w);
|
|
2492
|
+
if (secondCell || secondCol || secondRow) {
|
|
2493
|
+
second = w;
|
|
2494
|
+
} else {
|
|
2495
|
+
i = save;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
if (second === null && formula[i] === "(") return;
|
|
2499
|
+
const dead = () => {
|
|
2500
|
+
if (formula[i] === "#") i++;
|
|
2501
|
+
replace(refStart, "#REF!");
|
|
2502
|
+
hasRefError = true;
|
|
2503
|
+
};
|
|
2504
|
+
if (second !== null) {
|
|
2505
|
+
if (!rewritable) return;
|
|
2506
|
+
if (firstCell && secondCell) {
|
|
2507
|
+
const a = firstCell, b = secondCell;
|
|
2508
|
+
const rows = isRowShift ? shiftSpan(a.row, b.row, shift) : { start: a.row, end: b.row };
|
|
2509
|
+
const cols = isRowShift ? { start: a.col, end: b.col } : shiftSpan(a.col, b.col, shift);
|
|
2510
|
+
if (!rows || !cols) {
|
|
2511
|
+
dead();
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
replace(refStart, `${emitCell(a, rows.start, cols.start)}:${emitCell(b, rows.end, cols.end)}`);
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
if (firstCol && secondCol) {
|
|
2518
|
+
if (isRowShift) return;
|
|
2519
|
+
const span = shiftSpan(firstCol.col, secondCol.col, shift);
|
|
2520
|
+
if (!span) {
|
|
2521
|
+
dead();
|
|
2522
|
+
return;
|
|
2523
|
+
}
|
|
2524
|
+
const a = firstCol.abs ? "$" : "";
|
|
2525
|
+
const b = secondCol.abs ? "$" : "";
|
|
2526
|
+
replace(refStart, `${a}${columnLetter(span.start)}:${b}${columnLetter(span.end)}`);
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
if (firstRow && secondRow) {
|
|
2530
|
+
if (!isRowShift) return;
|
|
2531
|
+
const span = shiftSpan(firstRow.row, secondRow.row, shift);
|
|
2532
|
+
if (!span) {
|
|
2533
|
+
dead();
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const a = firstRow.abs ? "$" : "";
|
|
2537
|
+
const b = secondRow.abs ? "$" : "";
|
|
2538
|
+
replace(refStart, `${a}${span.start}:${b}${span.end}`);
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
if (!firstCell || !rewritable) return;
|
|
2544
|
+
const row = isRowShift ? shiftIndex(firstCell.row, shift) : firstCell.row;
|
|
2545
|
+
const col = isRowShift ? firstCell.col : shiftIndex(firstCell.col, shift);
|
|
2546
|
+
if (row === null || col === null) {
|
|
2547
|
+
dead();
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
replace(refStart, emitCell(firstCell, row, col));
|
|
2551
|
+
};
|
|
2552
|
+
while (i < n) {
|
|
2553
|
+
const ch = formula[i];
|
|
2554
|
+
if (ch === '"') {
|
|
2555
|
+
let j = i + 1;
|
|
2556
|
+
while (j < n) {
|
|
2557
|
+
if (formula[j] === '"') {
|
|
2558
|
+
if (formula[j + 1] === '"') {
|
|
2559
|
+
j += 2;
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
j++;
|
|
2563
|
+
break;
|
|
2564
|
+
}
|
|
2565
|
+
j++;
|
|
2566
|
+
}
|
|
2567
|
+
i = j;
|
|
2568
|
+
continue;
|
|
2569
|
+
}
|
|
2570
|
+
if (ch === "'") {
|
|
2571
|
+
const quoted = readQuoted();
|
|
2572
|
+
if (formula[i] === "!") {
|
|
2573
|
+
i++;
|
|
2574
|
+
const name = quoted.slice(1, -1);
|
|
2575
|
+
const rewritable = name.indexOf(":") === -1 && (name.indexOf("''") === -1 ? name : name.replace(/''/g, "'")).toUpperCase() === editedSheet;
|
|
2576
|
+
consumeRef(i, rewritable);
|
|
2577
|
+
}
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
if (isWordChar(formula.charCodeAt(i))) {
|
|
2581
|
+
const start = i;
|
|
2582
|
+
const word = readWord();
|
|
2583
|
+
if (formula[i] === "!") {
|
|
2584
|
+
i++;
|
|
2585
|
+
consumeRef(i, word.toUpperCase() === editedSheet);
|
|
2586
|
+
continue;
|
|
2587
|
+
}
|
|
2588
|
+
if (formula[i] === ":") {
|
|
2589
|
+
const save = i;
|
|
2590
|
+
i++;
|
|
2591
|
+
const w2 = i < n && isWordChar(formula.charCodeAt(i)) ? readWord() : "";
|
|
2592
|
+
if (w2 && formula[i] === "!") {
|
|
2593
|
+
i++;
|
|
2594
|
+
consumeRef(i, false);
|
|
2595
|
+
continue;
|
|
2596
|
+
}
|
|
2597
|
+
i = save;
|
|
2598
|
+
}
|
|
2599
|
+
if (formula[i] === "[") {
|
|
2600
|
+
let depth = 0;
|
|
2601
|
+
let j = i;
|
|
2602
|
+
while (j < n) {
|
|
2603
|
+
if (formula[j] === "[") depth++;
|
|
2604
|
+
else if (formula[j] === "]" && --depth === 0) {
|
|
2605
|
+
j++;
|
|
2606
|
+
break;
|
|
2607
|
+
}
|
|
2608
|
+
j++;
|
|
2609
|
+
}
|
|
2610
|
+
i = j;
|
|
2611
|
+
continue;
|
|
2612
|
+
}
|
|
2613
|
+
i = start;
|
|
2614
|
+
consumeRef(start, unqualifiedRewritable);
|
|
2615
|
+
continue;
|
|
2616
|
+
}
|
|
2617
|
+
i++;
|
|
2618
|
+
}
|
|
2619
|
+
if (!changed) return { text: formula, changed: false, hasRefError };
|
|
2620
|
+
return { text: out + formula.slice(flushed), changed: true, hasRefError };
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2176
2623
|
// src/model/range.ts
|
|
2177
|
-
var _sheet, _startRow, _startColumn, _endRow, _endColumn, _address, _Range_instances, forEach_fn;
|
|
2624
|
+
var _sheet, _startRow, _startColumn, _endRow, _endColumn, _address, _Range_instances, forEach_fn, forEachExisting_fn;
|
|
2178
2625
|
var Range = class {
|
|
2179
2626
|
constructor(sheet, rangeReference) {
|
|
2180
2627
|
__privateAdd(this, _Range_instances);
|
|
@@ -2223,26 +2670,63 @@ var Range = class {
|
|
|
2223
2670
|
* Gets all cell values as a 2D array (row-major order).
|
|
2224
2671
|
*/
|
|
2225
2672
|
getValues() {
|
|
2226
|
-
const
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2673
|
+
const rowCount = this.rowCount;
|
|
2674
|
+
const columnCount = this.columnCount;
|
|
2675
|
+
const result = new Array(rowCount);
|
|
2676
|
+
for (let r = 0; r < rowCount; r++) result[r] = new Array(columnCount).fill(null);
|
|
2677
|
+
const startRow = __privateGet(this, _startRow);
|
|
2678
|
+
const startColumn = __privateGet(this, _startColumn);
|
|
2679
|
+
__privateGet(this, _sheet).forEachCellIn(
|
|
2680
|
+
startRow,
|
|
2681
|
+
startColumn,
|
|
2682
|
+
__privateGet(this, _endRow),
|
|
2683
|
+
__privateGet(this, _endColumn),
|
|
2684
|
+
(cell, row, column) => {
|
|
2685
|
+
result[row - startRow][column - startColumn] = cell.getValue();
|
|
2232
2686
|
}
|
|
2233
|
-
|
|
2234
|
-
}
|
|
2687
|
+
);
|
|
2235
2688
|
return result;
|
|
2236
2689
|
}
|
|
2237
2690
|
/** Clears all cells in the range (values only, preserves style). */
|
|
2238
2691
|
clear() {
|
|
2239
|
-
__privateMethod(this, _Range_instances,
|
|
2692
|
+
__privateMethod(this, _Range_instances, forEachExisting_fn).call(this, (cell) => cell.clear());
|
|
2240
2693
|
}
|
|
2241
|
-
/**
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2694
|
+
/** Clears values AND styles of all cells in the range. */
|
|
2695
|
+
clearAll() {
|
|
2696
|
+
__privateMethod(this, _Range_instances, forEachExisting_fn).call(this, (cell) => cell.clearAll());
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Moves the whole range so its top-left cell lands on `destTopLeft`,
|
|
2700
|
+
* carrying values, formulas, styles and spill anchors. The vacated cells
|
|
2701
|
+
* are cleared and the destination block is fully replaced (empty source
|
|
2702
|
+
* cells clear their destination). Overlapping source/destination is safe.
|
|
2703
|
+
* Formula text is NOT adjusted for the new position.
|
|
2704
|
+
*
|
|
2705
|
+
* @example
|
|
2706
|
+
* ws.getRange('A1:B3').moveTo('D1');
|
|
2707
|
+
*/
|
|
2708
|
+
moveTo(destTopLeft) {
|
|
2709
|
+
const dest = parseCellRef(destTopLeft.replace(/\$/g, "").toUpperCase());
|
|
2710
|
+
const deltaRow = dest.row - __privateGet(this, _startRow);
|
|
2711
|
+
const deltaCol = dest.column - __privateGet(this, _startColumn);
|
|
2712
|
+
if (deltaRow === 0 && deltaCol === 0) return;
|
|
2713
|
+
if (__privateGet(this, _endRow) + deltaRow > EXCEL_MAX_ROWS || __privateGet(this, _endColumn) + deltaCol > EXCEL_MAX_COLUMNS) {
|
|
2714
|
+
throw new RangeError("Destination extends past the sheet limits.");
|
|
2245
2715
|
}
|
|
2716
|
+
const rowOrder = deltaRow > 0 ? { from: __privateGet(this, _endRow), to: __privateGet(this, _startRow), step: -1 } : { from: __privateGet(this, _startRow), to: __privateGet(this, _endRow), step: 1 };
|
|
2717
|
+
const colOrder = deltaCol > 0 ? { from: __privateGet(this, _endColumn), to: __privateGet(this, _startColumn), step: -1 } : { from: __privateGet(this, _startColumn), to: __privateGet(this, _endColumn), step: 1 };
|
|
2718
|
+
for (let r = rowOrder.from; deltaRow > 0 ? r >= rowOrder.to : r <= rowOrder.to; r += rowOrder.step) {
|
|
2719
|
+
for (let c = colOrder.from; deltaCol > 0 ? c >= colOrder.to : c <= colOrder.to; c += colOrder.step) {
|
|
2720
|
+
__privateGet(this, _sheet).moveCellRC(r, c, r + deltaRow, c + deltaCol);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Auto-fits all columns in the range. Measures every column in one sparse
|
|
2726
|
+
* sweep rather than re-scanning the sheet per column.
|
|
2727
|
+
*/
|
|
2728
|
+
autoFit() {
|
|
2729
|
+
__privateGet(this, _sheet).autoFitColumns(__privateGet(this, _startColumn), __privateGet(this, _endColumn));
|
|
2246
2730
|
}
|
|
2247
2731
|
// ── Style bulk operations ──────────────────────────────────────────────────
|
|
2248
2732
|
/** Applies a CellStyle to all cells in the range. Returns this for chaining. */
|
|
@@ -2316,9 +2800,57 @@ forEach_fn = function(action) {
|
|
|
2316
2800
|
}
|
|
2317
2801
|
}
|
|
2318
2802
|
};
|
|
2803
|
+
/**
|
|
2804
|
+
* Like #forEach but only visits cells that already exist — used by the
|
|
2805
|
+
* clearing operations so they never materialise empty cells, and so the cost
|
|
2806
|
+
* tracks the populated cells rather than the area of the range.
|
|
2807
|
+
*/
|
|
2808
|
+
forEachExisting_fn = function(action) {
|
|
2809
|
+
__privateGet(this, _sheet).forEachCellIn(
|
|
2810
|
+
__privateGet(this, _startRow),
|
|
2811
|
+
__privateGet(this, _startColumn),
|
|
2812
|
+
__privateGet(this, _endRow),
|
|
2813
|
+
__privateGet(this, _endColumn),
|
|
2814
|
+
(cell) => action(cell)
|
|
2815
|
+
);
|
|
2816
|
+
};
|
|
2319
2817
|
|
|
2320
2818
|
// src/model/worksheet.ts
|
|
2321
|
-
var
|
|
2819
|
+
var PROTECTION_DEFAULTS = {
|
|
2820
|
+
allowSelectLockedCells: true,
|
|
2821
|
+
allowSelectUnlockedCells: true,
|
|
2822
|
+
allowFormatCells: false,
|
|
2823
|
+
allowFormatColumns: false,
|
|
2824
|
+
allowFormatRows: false,
|
|
2825
|
+
allowInsertRows: false,
|
|
2826
|
+
allowInsertColumns: false,
|
|
2827
|
+
allowInsertHyperlinks: false,
|
|
2828
|
+
allowDeleteRows: false,
|
|
2829
|
+
allowDeleteColumns: false,
|
|
2830
|
+
allowSort: false,
|
|
2831
|
+
allowAutoFilter: false,
|
|
2832
|
+
allowPivotTables: false,
|
|
2833
|
+
allowEditObjects: false,
|
|
2834
|
+
allowEditScenarios: false
|
|
2835
|
+
};
|
|
2836
|
+
var PROTECTION_ATTRS = [
|
|
2837
|
+
["allowSelectLockedCells", "selectLockedCells"],
|
|
2838
|
+
["allowSelectUnlockedCells", "selectUnlockedCells"],
|
|
2839
|
+
["allowFormatCells", "formatCells"],
|
|
2840
|
+
["allowFormatColumns", "formatColumns"],
|
|
2841
|
+
["allowFormatRows", "formatRows"],
|
|
2842
|
+
["allowInsertRows", "insertRows"],
|
|
2843
|
+
["allowInsertColumns", "insertColumns"],
|
|
2844
|
+
["allowInsertHyperlinks", "insertHyperlinks"],
|
|
2845
|
+
["allowDeleteRows", "deleteRows"],
|
|
2846
|
+
["allowDeleteColumns", "deleteColumns"],
|
|
2847
|
+
["allowSort", "sort"],
|
|
2848
|
+
["allowAutoFilter", "autoFilter"],
|
|
2849
|
+
["allowPivotTables", "pivotTables"],
|
|
2850
|
+
["allowEditObjects", "objects"],
|
|
2851
|
+
["allowEditScenarios", "scenarios"]
|
|
2852
|
+
];
|
|
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;
|
|
2322
2854
|
var Worksheet = class {
|
|
2323
2855
|
/** @internal */
|
|
2324
2856
|
constructor(name, sharedStrings, styles) {
|
|
@@ -2334,6 +2866,26 @@ var Worksheet = class {
|
|
|
2334
2866
|
__privateAdd(this, _dataValidations, []);
|
|
2335
2867
|
__privateAdd(this, _visibility, "visible");
|
|
2336
2868
|
__privateAdd(this, _drawings, []);
|
|
2869
|
+
__privateAdd(this, _protection);
|
|
2870
|
+
/**
|
|
2871
|
+
* Password/hash attributes of a protection element read from a file, kept
|
|
2872
|
+
* verbatim so a protected sheet round-trips with its password intact. This
|
|
2873
|
+
* library never creates them: see {@link protect}.
|
|
2874
|
+
*/
|
|
2875
|
+
__privateAdd(this, _protectionCredentials, "");
|
|
2876
|
+
/** Hyperlinks keyed by their normalised ref, in insertion order. */
|
|
2877
|
+
__privateAdd(this, _hyperlinks, /* @__PURE__ */ new Map());
|
|
2878
|
+
/** Comments keyed by their normalised cell ref, in insertion order. */
|
|
2879
|
+
__privateAdd(this, _comments, /* @__PURE__ */ new Map());
|
|
2880
|
+
/**
|
|
2881
|
+
* True once the comment set has been modified through the API. Until then a
|
|
2882
|
+
* sheet loaded from a file keeps its original comments and VML parts verbatim,
|
|
2883
|
+
* so untouched files round-trip byte-for-byte; from then on this sheet owns
|
|
2884
|
+
* them and regenerates both. See {@link setComment}.
|
|
2885
|
+
*/
|
|
2886
|
+
__privateAdd(this, _commentsDirty, false);
|
|
2887
|
+
/** Hyperlink ref → relationship id, awaiting resolution against the rels part. */
|
|
2888
|
+
__privateAdd(this, _pendingHyperlinkRels, /* @__PURE__ */ new Map());
|
|
2337
2889
|
// Round-trip state captured when this sheet was loaded from a file: the
|
|
2338
2890
|
// relationship-bearing leaf elements of the worksheet part, and the sheet's
|
|
2339
2891
|
// original relationships. Re-emitted on save so drawings, images and legacy
|
|
@@ -2353,8 +2905,15 @@ var Worksheet = class {
|
|
|
2353
2905
|
__privateSet(this, _name, value);
|
|
2354
2906
|
}
|
|
2355
2907
|
getCell(refOrRow, column) {
|
|
2356
|
-
|
|
2357
|
-
|
|
2908
|
+
let ref;
|
|
2909
|
+
let row;
|
|
2910
|
+
if (typeof refOrRow === "string") {
|
|
2911
|
+
ref = normalizeRefFast(refOrRow);
|
|
2912
|
+
row = parseCellRef(ref).row;
|
|
2913
|
+
} else {
|
|
2914
|
+
ref = cellRefFromRowCol(refOrRow, column);
|
|
2915
|
+
row = refOrRow;
|
|
2916
|
+
}
|
|
2358
2917
|
const rowData = __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, row);
|
|
2359
2918
|
let cellData = rowData.cells.get(ref);
|
|
2360
2919
|
if (!cellData) {
|
|
@@ -2364,10 +2923,17 @@ var Worksheet = class {
|
|
|
2364
2923
|
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
2365
2924
|
}
|
|
2366
2925
|
tryGetCell(refOrRow, column) {
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2926
|
+
let rowData;
|
|
2927
|
+
let ref;
|
|
2928
|
+
if (typeof refOrRow === "string") {
|
|
2929
|
+
ref = normalizeRefFast(refOrRow);
|
|
2930
|
+
rowData = __privateGet(this, _rows).get(parseCellRef(ref).row);
|
|
2931
|
+
if (!rowData) return null;
|
|
2932
|
+
} else {
|
|
2933
|
+
rowData = __privateGet(this, _rows).get(refOrRow);
|
|
2934
|
+
if (!rowData) return null;
|
|
2935
|
+
ref = cellRefFromRowCol(refOrRow, column);
|
|
2936
|
+
}
|
|
2371
2937
|
const cellData = rowData.cells.get(ref);
|
|
2372
2938
|
if (!cellData) return null;
|
|
2373
2939
|
return new Cell(cellData, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2));
|
|
@@ -2392,10 +2958,83 @@ var Worksheet = class {
|
|
|
2392
2958
|
getSpillRange(reference) {
|
|
2393
2959
|
return this.tryGetCell(reference)?.getArrayRef() ?? null;
|
|
2394
2960
|
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Iterates the populated rows of the sheet in ascending row order, yielding
|
|
2963
|
+
* each row's 1-based index and its cells (left to right). Rows and cells
|
|
2964
|
+
* that were never written are skipped, so a sparse sheet iterates in
|
|
2965
|
+
* O(populated cells) regardless of its bounds.
|
|
2966
|
+
*
|
|
2967
|
+
* @example
|
|
2968
|
+
* for (const { rowIndex, cells } of ws.rows()) {
|
|
2969
|
+
* for (const cell of cells) console.log(rowIndex, cell.reference, cell.getValue());
|
|
2970
|
+
* }
|
|
2971
|
+
*/
|
|
2972
|
+
*rows() {
|
|
2973
|
+
const populated = [...__privateGet(this, _rows).values()].filter((row) => row.cells.size > 0).sort((a, b) => a.rowIndex - b.rowIndex);
|
|
2974
|
+
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)));
|
|
2976
|
+
yield { rowIndex: row.rowIndex, cells };
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
/**
|
|
2980
|
+
* Iterates every populated cell of the sheet in row-major order. Sparse
|
|
2981
|
+
* counterpart to scanning {@link usedBounds} with {@link tryGetCell}.
|
|
2982
|
+
*
|
|
2983
|
+
* @example
|
|
2984
|
+
* for (const cell of ws.cells()) console.log(cell.reference, cell.getValue());
|
|
2985
|
+
*/
|
|
2986
|
+
*cells() {
|
|
2987
|
+
for (const { cells } of this.rows()) yield* cells;
|
|
2988
|
+
}
|
|
2989
|
+
/**
|
|
2990
|
+
* @internal Visits the populated cells inside a rectangle, passing the
|
|
2991
|
+
* coordinates it already knows so the callback never has to parse a
|
|
2992
|
+
* reference. Backs the Range operations that must not create cells; probing
|
|
2993
|
+
* every coordinate would cost O(area) even on an empty sheet.
|
|
2994
|
+
*/
|
|
2995
|
+
forEachCellIn(startRow, startColumn, endRow, endColumn, action) {
|
|
2996
|
+
const visit = (row) => {
|
|
2997
|
+
for (const cd of row.cells.values()) {
|
|
2998
|
+
const col = columnFromRef(cd.reference, refSplit(cd.reference));
|
|
2999
|
+
if (col < startColumn || col > endColumn) continue;
|
|
3000
|
+
action(new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)), row.rowIndex, col);
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
if (endRow - startRow + 1 <= __privateGet(this, _rows).size) {
|
|
3004
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
3005
|
+
const row = __privateGet(this, _rows).get(r);
|
|
3006
|
+
if (row) visit(row);
|
|
3007
|
+
}
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3011
|
+
if (row.rowIndex >= startRow && row.rowIndex <= endRow) visit(row);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* @internal Moves a cell by coordinates, skipping the reference parsing that
|
|
3016
|
+
* the public {@link moveCell} has to do. Used by bulk range moves.
|
|
3017
|
+
*/
|
|
3018
|
+
moveCellRC(fromRow, fromColumn, toRow, toColumn) {
|
|
3019
|
+
if (fromRow === toRow && fromColumn === toColumn) return;
|
|
3020
|
+
const fromRef = cellRefFromRowCol(fromRow, fromColumn);
|
|
3021
|
+
const toRef = cellRefFromRowCol(toRow, toColumn);
|
|
3022
|
+
const src = __privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, fromRow, fromRef);
|
|
3023
|
+
if (!src) {
|
|
3024
|
+
__privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, toRow, toRef);
|
|
3025
|
+
return;
|
|
3026
|
+
}
|
|
3027
|
+
const moved = { ...src, reference: toRef };
|
|
3028
|
+
if (moved.arrayRef) {
|
|
3029
|
+
moved.arrayRef = translateRangeRef(moved.arrayRef, toRow - fromRow, toColumn - fromColumn);
|
|
3030
|
+
}
|
|
3031
|
+
__privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, toRow).cells.set(toRef, moved);
|
|
3032
|
+
}
|
|
2395
3033
|
/**
|
|
2396
3034
|
* Returns the 1-based extent of populated cells (largest row and column that
|
|
2397
3035
|
* contain data). Returns zeros for an empty sheet. Useful for snapshotting a
|
|
2398
|
-
* sheet into a dense 2D array
|
|
3036
|
+
* sheet into a dense 2D array — though {@link rows} and {@link cells} are
|
|
3037
|
+
* cheaper when a dense shape is not actually required.
|
|
2399
3038
|
*/
|
|
2400
3039
|
get usedBounds() {
|
|
2401
3040
|
let rowCount = 0;
|
|
@@ -2404,7 +3043,7 @@ var Worksheet = class {
|
|
|
2404
3043
|
if (row.cells.size === 0) continue;
|
|
2405
3044
|
if (row.rowIndex > rowCount) rowCount = row.rowIndex;
|
|
2406
3045
|
for (const ref of row.cells.keys()) {
|
|
2407
|
-
const col =
|
|
3046
|
+
const col = columnFromRef(ref, refSplit(ref));
|
|
2408
3047
|
if (col > columnCount) columnCount = col;
|
|
2409
3048
|
}
|
|
2410
3049
|
}
|
|
@@ -2415,9 +3054,41 @@ var Worksheet = class {
|
|
|
2415
3054
|
setColumnWidth(columnIndex, width) {
|
|
2416
3055
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
2417
3056
|
if (width <= 0) throw new RangeError("Width must be > 0.");
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
3057
|
+
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 });
|
|
3058
|
+
def.width = width;
|
|
3059
|
+
def.customWidth = true;
|
|
3060
|
+
}
|
|
3061
|
+
/**
|
|
3062
|
+
* Removes the explicit width of a column so it falls back to the sheet
|
|
3063
|
+
* default. Counterpart to {@link setColumnWidth}; afterwards
|
|
3064
|
+
* {@link getColumnWidth} returns `undefined`. A hidden column stays hidden.
|
|
3065
|
+
*/
|
|
3066
|
+
resetColumnWidth(columnIndex) {
|
|
3067
|
+
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3068
|
+
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3069
|
+
if (!def) return;
|
|
3070
|
+
def.width = void 0;
|
|
3071
|
+
def.customWidth = false;
|
|
3072
|
+
__privateMethod(this, _Worksheet_instances, pruneColDefs_fn).call(this);
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Shows or hides a column (1-based index). Hidden columns round-trip to the
|
|
3076
|
+
* file as `<col hidden="1">` and keep any explicit width.
|
|
3077
|
+
*/
|
|
3078
|
+
setColumnHidden(columnIndex, hidden = true) {
|
|
3079
|
+
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3080
|
+
const def = __privateMethod(this, _Worksheet_instances, splitColSpan_fn).call(this, columnIndex);
|
|
3081
|
+
if (def) {
|
|
3082
|
+
def.hidden = hidden;
|
|
3083
|
+
__privateMethod(this, _Worksheet_instances, pruneColDefs_fn).call(this);
|
|
3084
|
+
} else if (hidden) {
|
|
3085
|
+
__privateMethod(this, _Worksheet_instances, insertColDef_fn).call(this, { min: columnIndex, max: columnIndex, customWidth: false, hidden: true });
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
/** True if the given 1-based column is hidden. */
|
|
3089
|
+
isColumnHidden(columnIndex) {
|
|
3090
|
+
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
3091
|
+
return __privateMethod(this, _Worksheet_instances, findColDef_fn).call(this, columnIndex)?.hidden === true;
|
|
2421
3092
|
}
|
|
2422
3093
|
/**
|
|
2423
3094
|
* Returns the explicit width of a column (1-based index), or `undefined` when
|
|
@@ -2427,22 +3098,28 @@ var Worksheet = class {
|
|
|
2427
3098
|
*/
|
|
2428
3099
|
getColumnWidth(columnIndex) {
|
|
2429
3100
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
2430
|
-
return
|
|
3101
|
+
return __privateMethod(this, _Worksheet_instances, findColDef_fn).call(this, columnIndex)?.width;
|
|
2431
3102
|
}
|
|
2432
3103
|
/** Auto-fits column width based on content (approximate). */
|
|
2433
3104
|
autoFitColumn(columnIndex) {
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
3105
|
+
this.autoFitColumns(columnIndex, columnIndex);
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Auto-fits an inclusive span of columns (1-based) in a single pass over the
|
|
3109
|
+
* populated cells, instead of re-scanning the sheet once per column.
|
|
3110
|
+
*/
|
|
3111
|
+
autoFitColumns(firstColumn, lastColumn) {
|
|
3112
|
+
if (firstColumn < 1) throw new RangeError("Column index must be >= 1.");
|
|
3113
|
+
if (lastColumn < firstColumn) return;
|
|
3114
|
+
const maxLen = /* @__PURE__ */ new Map();
|
|
3115
|
+
this.forEachCellIn(1, firstColumn, EXCEL_MAX_ROWS, lastColumn, (cell, _row, column) => {
|
|
3116
|
+
const len = cell.getText().length;
|
|
3117
|
+
if (len > (maxLen.get(column) ?? 0)) maxLen.set(column, len);
|
|
3118
|
+
});
|
|
3119
|
+
for (let col = firstColumn; col <= lastColumn; col++) {
|
|
3120
|
+
const len = Math.max(maxLen.get(col) ?? 0, 8);
|
|
3121
|
+
this.setColumnWidth(col, Math.min(len * 1.2 + 2, 255));
|
|
2444
3122
|
}
|
|
2445
|
-
this.setColumnWidth(columnIndex, Math.min(maxLen * 1.2 + 2, 255));
|
|
2446
3123
|
}
|
|
2447
3124
|
/** Sets the height of a row (1-based index). */
|
|
2448
3125
|
setRowHeight(rowIndex, height) {
|
|
@@ -2460,6 +3137,19 @@ var Worksheet = class {
|
|
|
2460
3137
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
2461
3138
|
return __privateGet(this, _rows).get(rowIndex)?.height;
|
|
2462
3139
|
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Removes the explicit height of a row so it falls back to the sheet
|
|
3142
|
+
* default. Counterpart to {@link setRowHeight}; afterwards
|
|
3143
|
+
* {@link getRowHeight} returns `undefined`. A hidden row stays hidden.
|
|
3144
|
+
*/
|
|
3145
|
+
resetRowHeight(rowIndex) {
|
|
3146
|
+
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
3147
|
+
const row = __privateGet(this, _rows).get(rowIndex);
|
|
3148
|
+
if (!row) return;
|
|
3149
|
+
row.height = void 0;
|
|
3150
|
+
row.customHeight = void 0;
|
|
3151
|
+
if (row.cells.size === 0 && !row.hidden) __privateGet(this, _rows).delete(rowIndex);
|
|
3152
|
+
}
|
|
2463
3153
|
/** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
|
|
2464
3154
|
setRowHidden(rowIndex, hidden = true) {
|
|
2465
3155
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
@@ -2469,6 +3159,137 @@ var Worksheet = class {
|
|
|
2469
3159
|
isRowHidden(rowIndex) {
|
|
2470
3160
|
return __privateGet(this, _rows).get(rowIndex)?.hidden === true;
|
|
2471
3161
|
}
|
|
3162
|
+
/**
|
|
3163
|
+
* The 1-based indices of every hidden row, ascending. Sparse counterpart to
|
|
3164
|
+
* probing {@link isRowHidden} across {@link usedBounds}.
|
|
3165
|
+
*/
|
|
3166
|
+
get hiddenRows() {
|
|
3167
|
+
const result = [];
|
|
3168
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3169
|
+
if (row.hidden) result.push(row.rowIndex);
|
|
3170
|
+
}
|
|
3171
|
+
return result.sort((a, b) => a - b);
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* The 1-based indices of every hidden column, ascending. Counterpart to
|
|
3175
|
+
* {@link isColumnHidden}, in the same spirit as {@link hiddenRows}.
|
|
3176
|
+
*/
|
|
3177
|
+
get hiddenColumns() {
|
|
3178
|
+
const result = [];
|
|
3179
|
+
for (const def of __privateGet(this, _cols)) {
|
|
3180
|
+
if (!def.hidden) continue;
|
|
3181
|
+
for (let c = def.min; c <= def.max; c++) result.push(c);
|
|
3182
|
+
}
|
|
3183
|
+
return result.sort((a, b) => a - b);
|
|
3184
|
+
}
|
|
3185
|
+
// ── Structural editing ─────────────────────────────────────────────────────
|
|
3186
|
+
/**
|
|
3187
|
+
* Inserts `count` empty rows starting at row `at` (1-based). Everything at
|
|
3188
|
+
* or below `at` moves down: cell values, formulas (references are rewritten
|
|
3189
|
+
* the way Excel does), styles, row heights, hidden flags, merges,
|
|
3190
|
+
* autofilter and data-validation ranges.
|
|
3191
|
+
*
|
|
3192
|
+
* Frozen panes are a view property and stay put. Drawing/chart anchors
|
|
3193
|
+
* preserved from a loaded file are opaque XML and do NOT shift — images
|
|
3194
|
+
* keep their absolute position (known limitation).
|
|
3195
|
+
*
|
|
3196
|
+
* When formulas on OTHER sheets reference this sheet, use the
|
|
3197
|
+
* Workbook-level counterpart so they are rewritten too, or call
|
|
3198
|
+
* `applyExternalShift` on each sheet yourself. If a RecalcEngine is
|
|
3199
|
+
* attached, notify it afterwards (e.g. `engine.onRowsInserted(...)`).
|
|
3200
|
+
*
|
|
3201
|
+
* @example
|
|
3202
|
+
* ws.insertRows(3, 2); // pushes row 3 and below down by two rows
|
|
3203
|
+
*/
|
|
3204
|
+
insertRows(at, count = 1) {
|
|
3205
|
+
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "insert", at, count);
|
|
3206
|
+
}
|
|
3207
|
+
/**
|
|
3208
|
+
* Deletes `count` rows starting at row `at` (1-based). Rows below move up;
|
|
3209
|
+
* formulas referencing the deleted band get `#REF!` (fully inside) or
|
|
3210
|
+
* shrink (partially inside), exactly like Excel. See {@link insertRows}
|
|
3211
|
+
* for the shifting rules and known limitations.
|
|
3212
|
+
*/
|
|
3213
|
+
deleteRows(at, count = 1) {
|
|
3214
|
+
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "row", "delete", at, count);
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Inserts `count` empty columns starting at column `at` (1-based).
|
|
3218
|
+
* Column-level counterpart to {@link insertRows}; widths and hidden flags
|
|
3219
|
+
* shift with the columns.
|
|
3220
|
+
*/
|
|
3221
|
+
insertColumns(at, count = 1) {
|
|
3222
|
+
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "insert", at, count);
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Deletes `count` columns starting at column `at` (1-based).
|
|
3226
|
+
* Column-level counterpart to {@link deleteRows}.
|
|
3227
|
+
*/
|
|
3228
|
+
deleteColumns(at, count = 1) {
|
|
3229
|
+
return __privateMethod(this, _Worksheet_instances, applyStructural_fn).call(this, "col", "delete", at, count);
|
|
3230
|
+
}
|
|
3231
|
+
/**
|
|
3232
|
+
* @internal Rewrites formulas on THIS sheet after a structural edit on a
|
|
3233
|
+
* DIFFERENT sheet (named by `shift.sheetName`), so qualified references
|
|
3234
|
+
* like `'Data'!A5` stay correct. Cell positions here do not change.
|
|
3235
|
+
*/
|
|
3236
|
+
applyExternalShift(shift) {
|
|
3237
|
+
return { refErrors: __privateMethod(this, _Worksheet_instances, rewriteFormulas_fn).call(this, shift) };
|
|
3238
|
+
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Moves a cell's full contents — value, formula, style and spill anchor —
|
|
3241
|
+
* to another cell, overwriting the destination and clearing the source.
|
|
3242
|
+
* A missing source clears the destination. This is a literal move: formula
|
|
3243
|
+
* text is NOT adjusted for the new position.
|
|
3244
|
+
*
|
|
3245
|
+
* @example
|
|
3246
|
+
* ws.moveCell('A1', 'C5');
|
|
3247
|
+
*/
|
|
3248
|
+
moveCell(from, to) {
|
|
3249
|
+
const fromRef = normalizeRef(from);
|
|
3250
|
+
const toRef = normalizeRef(to);
|
|
3251
|
+
if (fromRef === toRef) return;
|
|
3252
|
+
const src = __privateMethod(this, _Worksheet_instances, takeCellData_fn).call(this, fromRef);
|
|
3253
|
+
if (!src) {
|
|
3254
|
+
__privateMethod(this, _Worksheet_instances, deleteCellData_fn).call(this, toRef);
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
const fromPos = parseCellRef(fromRef);
|
|
3258
|
+
const toPos = parseCellRef(toRef);
|
|
3259
|
+
const moved = { ...src, reference: toRef };
|
|
3260
|
+
if (moved.arrayRef) {
|
|
3261
|
+
moved.arrayRef = translateRangeRef(
|
|
3262
|
+
moved.arrayRef,
|
|
3263
|
+
toPos.row - fromPos.row,
|
|
3264
|
+
toPos.column - fromPos.column
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3267
|
+
__privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, toPos.row).cells.set(toRef, moved);
|
|
3268
|
+
}
|
|
3269
|
+
/**
|
|
3270
|
+
* Copies a cell's value, formula and style to another cell, overwriting the
|
|
3271
|
+
* destination and leaving the source untouched. A missing source clears the
|
|
3272
|
+
* destination. This is a literal copy: formula references are NOT adjusted,
|
|
3273
|
+
* and the spill anchor (`arrayRef`) is not copied — a recalc engine
|
|
3274
|
+
* re-establishes it.
|
|
3275
|
+
*
|
|
3276
|
+
* @example
|
|
3277
|
+
* ws.copyCell('A1', 'C5');
|
|
3278
|
+
*/
|
|
3279
|
+
copyCell(from, to) {
|
|
3280
|
+
const fromRef = normalizeRef(from);
|
|
3281
|
+
const toRef = normalizeRef(to);
|
|
3282
|
+
if (fromRef === toRef) return;
|
|
3283
|
+
const srcRow = __privateGet(this, _rows).get(parseCellRef(fromRef).row);
|
|
3284
|
+
const src = srcRow?.cells.get(fromRef);
|
|
3285
|
+
if (!src) {
|
|
3286
|
+
__privateMethod(this, _Worksheet_instances, deleteCellData_fn).call(this, toRef);
|
|
3287
|
+
return;
|
|
3288
|
+
}
|
|
3289
|
+
const toPos = parseCellRef(toRef);
|
|
3290
|
+
const copied = { ...src, reference: toRef, arrayRef: void 0 };
|
|
3291
|
+
__privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, toPos.row).cells.set(toRef, copied);
|
|
3292
|
+
}
|
|
2472
3293
|
/** Merges cells in the given range (e.g. "A1:B2"). */
|
|
2473
3294
|
mergeCells(rangeReference) {
|
|
2474
3295
|
if (!rangeReference) throw new Error("Range reference cannot be empty.");
|
|
@@ -2515,6 +3336,119 @@ var Worksheet = class {
|
|
|
2515
3336
|
get visibility() {
|
|
2516
3337
|
return __privateGet(this, _visibility);
|
|
2517
3338
|
}
|
|
3339
|
+
// ── Sheet protection ───────────────────────────────────────────────────────
|
|
3340
|
+
/**
|
|
3341
|
+
* Turns on sheet protection, optionally relaxing individual permissions.
|
|
3342
|
+
* Cells are locked by default in Excel, so protecting a sheet makes all of
|
|
3343
|
+
* them read-only; clear a cell style's `locked` flag to leave a cell editable.
|
|
3344
|
+
*
|
|
3345
|
+
* Excel's sheet protection is a UI guard, **not** security: the contents stay
|
|
3346
|
+
* readable to anything that opens the file. This library therefore never
|
|
3347
|
+
* creates a protection password. A password read from an existing file is
|
|
3348
|
+
* preserved so the sheet round-trips unchanged.
|
|
3349
|
+
*
|
|
3350
|
+
* @example
|
|
3351
|
+
* ws.protect(); // lock everything
|
|
3352
|
+
* ws.protect({ allowSort: true, allowAutoFilter: true });
|
|
3353
|
+
*/
|
|
3354
|
+
protect(options) {
|
|
3355
|
+
__privateSet(this, _protection, { ...PROTECTION_DEFAULTS, ...options });
|
|
3356
|
+
}
|
|
3357
|
+
/** Turns off sheet protection, discarding any password read from a file. */
|
|
3358
|
+
unprotect() {
|
|
3359
|
+
__privateSet(this, _protection, void 0);
|
|
3360
|
+
__privateSet(this, _protectionCredentials, "");
|
|
3361
|
+
}
|
|
3362
|
+
/** True when this sheet is protected. */
|
|
3363
|
+
get isProtected() {
|
|
3364
|
+
return __privateGet(this, _protection) !== void 0;
|
|
3365
|
+
}
|
|
3366
|
+
/** The active permissions, or `undefined` when the sheet is not protected. */
|
|
3367
|
+
get protection() {
|
|
3368
|
+
return __privateGet(this, _protection) ? { ...__privateGet(this, _protection) } : void 0;
|
|
3369
|
+
}
|
|
3370
|
+
// ── Hyperlinks ─────────────────────────────────────────────────────────────
|
|
3371
|
+
/**
|
|
3372
|
+
* Attaches a hyperlink to a cell or range. Give `target` for an external
|
|
3373
|
+
* destination or `location` for one inside the workbook; passing both keeps
|
|
3374
|
+
* the external target and uses the location as its fragment, which is how
|
|
3375
|
+
* Excel links to an anchor within a document.
|
|
3376
|
+
*
|
|
3377
|
+
* A cell's displayed text still comes from its own value — set that
|
|
3378
|
+
* separately; `display` only overrides what Excel shows in the edit bar.
|
|
3379
|
+
*
|
|
3380
|
+
* @example
|
|
3381
|
+
* ws.getCell('A1').setValue('Anthropic');
|
|
3382
|
+
* ws.setHyperlink('A1', { target: 'https://anthropic.com', tooltip: 'Open site' });
|
|
3383
|
+
* ws.setHyperlink('B1', { location: "'Sheet2'!A1" });
|
|
3384
|
+
*/
|
|
3385
|
+
setHyperlink(ref, link) {
|
|
3386
|
+
if (!link.target && !link.location) {
|
|
3387
|
+
throw new Error("A hyperlink needs a target (external) or a location (in-workbook).");
|
|
3388
|
+
}
|
|
3389
|
+
const normalized = normalizeRangeRef(ref);
|
|
3390
|
+
__privateGet(this, _hyperlinks).set(normalized, { ref: normalized, ...link });
|
|
3391
|
+
}
|
|
3392
|
+
/** The hyperlink covering exactly this ref, or undefined. */
|
|
3393
|
+
getHyperlink(ref) {
|
|
3394
|
+
return __privateGet(this, _hyperlinks).get(normalizeRangeRef(ref));
|
|
3395
|
+
}
|
|
3396
|
+
/** Removes the hyperlink on this ref. Returns true if one was there. */
|
|
3397
|
+
removeHyperlink(ref) {
|
|
3398
|
+
return __privateGet(this, _hyperlinks).delete(normalizeRangeRef(ref));
|
|
3399
|
+
}
|
|
3400
|
+
/** Every hyperlink on the sheet, including ones read from a file. */
|
|
3401
|
+
get hyperlinks() {
|
|
3402
|
+
return [...__privateGet(this, _hyperlinks).values()];
|
|
3403
|
+
}
|
|
3404
|
+
// ── Comments (notes) ───────────────────────────────────────────────────────
|
|
3405
|
+
/**
|
|
3406
|
+
* Attaches a comment (a "note" in current Excel versions) to a cell,
|
|
3407
|
+
* replacing any comment already there.
|
|
3408
|
+
*
|
|
3409
|
+
* Note that writing comments makes this sheet regenerate its comment and
|
|
3410
|
+
* legacy-drawing parts on save. If the file was opened with other legacy
|
|
3411
|
+
* drawing content on the same sheet — form controls, for instance — that
|
|
3412
|
+
* content is not reproduced. Reading comments has no such effect.
|
|
3413
|
+
*
|
|
3414
|
+
* @example
|
|
3415
|
+
* ws.setComment('B4', 'Check this figure', { author: 'Bill' });
|
|
3416
|
+
*/
|
|
3417
|
+
setComment(ref, text, options) {
|
|
3418
|
+
const normalized = normalizeRef(ref);
|
|
3419
|
+
__privateGet(this, _comments).set(normalized, {
|
|
3420
|
+
ref: normalized,
|
|
3421
|
+
text,
|
|
3422
|
+
author: options?.author ?? ""
|
|
3423
|
+
});
|
|
3424
|
+
__privateSet(this, _commentsDirty, true);
|
|
3425
|
+
}
|
|
3426
|
+
/** The comment on a cell, or undefined. */
|
|
3427
|
+
getComment(ref) {
|
|
3428
|
+
return __privateGet(this, _comments).get(normalizeRef(ref));
|
|
3429
|
+
}
|
|
3430
|
+
/** Removes the comment on a cell. Returns true if one was there. */
|
|
3431
|
+
removeComment(ref) {
|
|
3432
|
+
const removed = __privateGet(this, _comments).delete(normalizeRef(ref));
|
|
3433
|
+
if (removed) __privateSet(this, _commentsDirty, true);
|
|
3434
|
+
return removed;
|
|
3435
|
+
}
|
|
3436
|
+
/** Every comment on the sheet, including ones read from a file. */
|
|
3437
|
+
get comments() {
|
|
3438
|
+
return [...__privateGet(this, _comments).values()];
|
|
3439
|
+
}
|
|
3440
|
+
/** @internal True when this sheet must write its own comment parts. */
|
|
3441
|
+
get ownsCommentParts() {
|
|
3442
|
+
return __privateGet(this, _commentsDirty) && __privateGet(this, _comments).size > 0;
|
|
3443
|
+
}
|
|
3444
|
+
/**
|
|
3445
|
+
* @internal True when the sheet took over its comment parts and any preserved
|
|
3446
|
+
* originals must be dropped — including the case where every comment was
|
|
3447
|
+
* deleted and nothing replaces them.
|
|
3448
|
+
*/
|
|
3449
|
+
get commentPartsReplaced() {
|
|
3450
|
+
return __privateGet(this, _commentsDirty);
|
|
3451
|
+
}
|
|
2518
3452
|
// ── Drawings (charts, images …) ──────────────────────────────────────────────
|
|
2519
3453
|
/**
|
|
2520
3454
|
* Attaches a drawing provider (e.g. a chart) to this worksheet.
|
|
@@ -2552,7 +3486,12 @@ var Worksheet = class {
|
|
|
2552
3486
|
*/
|
|
2553
3487
|
get preservedRelationships() {
|
|
2554
3488
|
const liveIds = new Set(__privateMethod(this, _Worksheet_instances, activeTail_fn).call(this).map((t) => t.relId));
|
|
2555
|
-
return __privateGet(this, _preservedRels).filter((r) =>
|
|
3489
|
+
return __privateGet(this, _preservedRels).filter((r) => {
|
|
3490
|
+
if (r.type === REL_TYPES.comments) {
|
|
3491
|
+
return !this.commentPartsReplaced && __privateGet(this, _comments).size > 0;
|
|
3492
|
+
}
|
|
3493
|
+
return liveIds.has(r.id);
|
|
3494
|
+
});
|
|
2556
3495
|
}
|
|
2557
3496
|
/**
|
|
2558
3497
|
* @internal Package paths of relationships that were preserved at load time
|
|
@@ -2569,35 +3508,86 @@ var Worksheet = class {
|
|
|
2569
3508
|
* clear every preserved id so the two sets cannot collide.
|
|
2570
3509
|
*/
|
|
2571
3510
|
get drawingRelId() {
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
3511
|
+
return this.generatedRels().drawing ?? `rId${__privateMethod(this, _Worksheet_instances, relIdBase_fn).call(this) + 1}`;
|
|
3512
|
+
}
|
|
3513
|
+
/**
|
|
3514
|
+
* @internal Relationship ids for the parts this sheet generates itself.
|
|
3515
|
+
*
|
|
3516
|
+
* Derived purely from the model so the worksheet XML and the sheet's rels part
|
|
3517
|
+
* — which are written by different code — cannot disagree about an id. Order is
|
|
3518
|
+
* fixed: drawing, then external hyperlinks in insertion order, then the
|
|
3519
|
+
* comments part and its legacy VML drawing.
|
|
3520
|
+
*/
|
|
3521
|
+
generatedRels() {
|
|
3522
|
+
let next = __privateMethod(this, _Worksheet_instances, relIdBase_fn).call(this);
|
|
3523
|
+
const result = { hyperlinks: /* @__PURE__ */ new Map() };
|
|
3524
|
+
if (this.hasDrawings) result.drawing = `rId${++next}`;
|
|
3525
|
+
for (const link of __privateGet(this, _hyperlinks).values()) {
|
|
3526
|
+
if (link.target) result.hyperlinks.set(link.ref, `rId${++next}`);
|
|
2576
3527
|
}
|
|
2577
|
-
|
|
3528
|
+
if (this.ownsCommentParts) {
|
|
3529
|
+
result.comments = `rId${++next}`;
|
|
3530
|
+
result.vmlDrawing = `rId${++next}`;
|
|
3531
|
+
}
|
|
3532
|
+
return result;
|
|
2578
3533
|
}
|
|
2579
3534
|
/** @internal Captures round-trip state from the source package. */
|
|
2580
3535
|
loadPreservedState(worksheetXml, relationships) {
|
|
2581
|
-
__privateSet(this, _preservedRels,
|
|
2582
|
-
const known = new Set(
|
|
3536
|
+
__privateSet(this, _preservedRels, relationships.filter((r) => r.type !== REL_TYPES.hyperlink));
|
|
3537
|
+
const known = new Set(__privateGet(this, _preservedRels).map((r) => r.id));
|
|
2583
3538
|
__privateSet(this, _preservedTail, parseTailElements(worksheetXml).filter((t) => known.has(t.relId)));
|
|
3539
|
+
const byId = new Map(relationships.map((r) => [r.id, r]));
|
|
3540
|
+
for (const [ref, relId] of __privateGet(this, _pendingHyperlinkRels)) {
|
|
3541
|
+
const rel = byId.get(relId);
|
|
3542
|
+
const link = __privateGet(this, _hyperlinks).get(ref);
|
|
3543
|
+
if (!rel || !link) continue;
|
|
3544
|
+
__privateGet(this, _hyperlinks).set(ref, { ...link, target: rel.target });
|
|
3545
|
+
}
|
|
3546
|
+
__privateGet(this, _pendingHyperlinkRels).clear();
|
|
3547
|
+
for (const [ref, link] of [...__privateGet(this, _hyperlinks)]) {
|
|
3548
|
+
if (!link.target && !link.location) __privateGet(this, _hyperlinks).delete(ref);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
/**
|
|
3552
|
+
* @internal Package paths of the comment parts this sheet read at load time.
|
|
3553
|
+
* The save pipeline drops them from the preserved set once the sheet takes
|
|
3554
|
+
* ownership, so they are not written twice.
|
|
3555
|
+
*/
|
|
3556
|
+
commentPartPaths() {
|
|
3557
|
+
const paths = [];
|
|
3558
|
+
for (const rel of __privateGet(this, _preservedRels)) {
|
|
3559
|
+
if (!rel.resolved) continue;
|
|
3560
|
+
if (rel.type === REL_TYPES.comments || rel.type === REL_TYPES.vmlDrawing) {
|
|
3561
|
+
paths.push(rel.resolved);
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
return paths;
|
|
3565
|
+
}
|
|
3566
|
+
/**
|
|
3567
|
+
* @internal The relationship pointing at this sheet's comments part, if the
|
|
3568
|
+
* sheet was loaded from a file that had one.
|
|
3569
|
+
*/
|
|
3570
|
+
commentsPartPath() {
|
|
3571
|
+
return __privateGet(this, _preservedRels).find((r) => r.type === REL_TYPES.comments)?.resolved;
|
|
2584
3572
|
}
|
|
2585
3573
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
2586
3574
|
/** Builds the xl/worksheets/sheetN.xml string. */
|
|
2587
3575
|
buildXml() {
|
|
3576
|
+
const rels = this.generatedRels();
|
|
3577
|
+
const sheetViewXml = __privateMethod(this, _Worksheet_instances, buildSheetViewXml_fn).call(this);
|
|
2588
3578
|
const colsXml = __privateMethod(this, _Worksheet_instances, buildColsXml_fn).call(this);
|
|
2589
3579
|
const sheetDataXml = __privateMethod(this, _Worksheet_instances, buildSheetDataXml_fn).call(this);
|
|
2590
|
-
const
|
|
3580
|
+
const protectionXml = __privateMethod(this, _Worksheet_instances, buildSheetProtectionXml_fn).call(this);
|
|
2591
3581
|
const filterXml = __privateGet(this, _autoFilter) ? `<autoFilter ref="${__privateGet(this, _autoFilter).ref}"/>` : "";
|
|
3582
|
+
const mergeXml = __privateMethod(this, _Worksheet_instances, buildMergeCellsXml_fn).call(this);
|
|
2592
3583
|
const validXml = __privateMethod(this, _Worksheet_instances, buildDataValidationsXml_fn).call(this);
|
|
2593
|
-
const
|
|
2594
|
-
const
|
|
2595
|
-
const drawingXml = this.hasDrawings ? `<drawing r:id="${this.drawingRelId}"/>` : "";
|
|
3584
|
+
const linksXml = __privateMethod(this, _Worksheet_instances, buildHyperlinksXml_fn).call(this, rels.hyperlinks);
|
|
3585
|
+
const tailXml = __privateMethod(this, _Worksheet_instances, buildTailXml_fn).call(this, rels);
|
|
2596
3586
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2597
3587
|
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
2598
3588
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
2599
3589
|
<sheetViews>${sheetViewXml}</sheetViews>
|
|
2600
|
-
${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${
|
|
3590
|
+
${colsXml}${sheetDataXml}${protectionXml}${filterXml}${mergeXml}${validXml}${linksXml}${tailXml}
|
|
2601
3591
|
</worksheet>`;
|
|
2602
3592
|
}
|
|
2603
3593
|
/** Parses xl/worksheets/sheetN.xml and loads cells into memory. */
|
|
@@ -2608,12 +3598,17 @@ ${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${preservedTailXml}${
|
|
|
2608
3598
|
__privateSet(this, _pane, void 0);
|
|
2609
3599
|
__privateSet(this, _autoFilter, void 0);
|
|
2610
3600
|
__privateSet(this, _dataValidations, []);
|
|
3601
|
+
__privateSet(this, _protection, void 0);
|
|
3602
|
+
__privateSet(this, _protectionCredentials, "");
|
|
3603
|
+
__privateGet(this, _hyperlinks).clear();
|
|
3604
|
+
__privateGet(this, _pendingHyperlinkRels).clear();
|
|
2611
3605
|
const root = parseXml(xml2);
|
|
2612
3606
|
for (const colEl of getElementsByTagName(root, "col")) {
|
|
3607
|
+
const widthAttr = getAttr(colEl, "width");
|
|
2613
3608
|
__privateGet(this, _cols).push({
|
|
2614
3609
|
min: parseInt(getAttr(colEl, "min") ?? "1", 10),
|
|
2615
3610
|
max: parseInt(getAttr(colEl, "max") ?? "1", 10),
|
|
2616
|
-
width: parseFloat(
|
|
3611
|
+
width: widthAttr !== void 0 ? parseFloat(widthAttr) : void 0,
|
|
2617
3612
|
customWidth: getAttr(colEl, "customWidth") === "1",
|
|
2618
3613
|
hidden: getAttr(colEl, "hidden") === "1"
|
|
2619
3614
|
});
|
|
@@ -2665,6 +3660,75 @@ ${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${preservedTailXml}${
|
|
|
2665
3660
|
const formula = f1Els[0]?.textContent ?? "";
|
|
2666
3661
|
if (sqref && formula) __privateGet(this, _dataValidations).push({ sqref, formula });
|
|
2667
3662
|
}
|
|
3663
|
+
const protEl = getElementsByTagName(root, "sheetProtection")[0];
|
|
3664
|
+
if (protEl && getAttr(protEl, "sheet") === "1") {
|
|
3665
|
+
const protection = { ...PROTECTION_DEFAULTS };
|
|
3666
|
+
for (const [key, attr] of PROTECTION_ATTRS) {
|
|
3667
|
+
const raw = getAttr(protEl, attr);
|
|
3668
|
+
if (raw !== void 0) protection[key] = raw !== "1";
|
|
3669
|
+
}
|
|
3670
|
+
__privateSet(this, _protection, protection);
|
|
3671
|
+
let credentials = "";
|
|
3672
|
+
for (const attr of ["password", "algorithmName", "hashValue", "saltValue", "spinCount"]) {
|
|
3673
|
+
const raw = getAttr(protEl, attr);
|
|
3674
|
+
if (raw !== void 0) credentials += ` ${attr}="${escXmlAttr(raw)}"`;
|
|
3675
|
+
}
|
|
3676
|
+
__privateSet(this, _protectionCredentials, credentials);
|
|
3677
|
+
}
|
|
3678
|
+
for (const hlEl of getElementsByTagName(root, "hyperlink")) {
|
|
3679
|
+
const ref = getAttr(hlEl, "ref");
|
|
3680
|
+
if (!ref) continue;
|
|
3681
|
+
const normalized = normalizeRangeRef(ref);
|
|
3682
|
+
__privateGet(this, _hyperlinks).set(normalized, {
|
|
3683
|
+
ref: normalized,
|
|
3684
|
+
location: getAttr(hlEl, "location"),
|
|
3685
|
+
display: getAttr(hlEl, "display"),
|
|
3686
|
+
tooltip: getAttr(hlEl, "tooltip")
|
|
3687
|
+
});
|
|
3688
|
+
const relId = getAttr(hlEl, "r:id") ?? getAttr(hlEl, "id");
|
|
3689
|
+
if (relId) __privateGet(this, _pendingHyperlinkRels).set(normalized, relId);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
/** @internal Parses an `xl/comments*.xml` part into this sheet's comments. */
|
|
3693
|
+
loadCommentsXml(xml2) {
|
|
3694
|
+
const root = parseXml(xml2);
|
|
3695
|
+
const authors = getElementsByTagName(root, "author").map((a) => a.textContent);
|
|
3696
|
+
for (const cEl of getElementsByTagName(root, "comment")) {
|
|
3697
|
+
const ref = getAttr(cEl, "ref");
|
|
3698
|
+
if (!ref) continue;
|
|
3699
|
+
const authorId = parseInt(getAttr(cEl, "authorId") ?? "0", 10);
|
|
3700
|
+
const text = getElementsByTagName(cEl, "t").map((t) => t.textContent).join("");
|
|
3701
|
+
const normalized = normalizeRef(ref);
|
|
3702
|
+
__privateGet(this, _comments).set(normalized, {
|
|
3703
|
+
ref: normalized,
|
|
3704
|
+
text,
|
|
3705
|
+
author: authors[authorId] ?? ""
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
__privateSet(this, _commentsDirty, false);
|
|
3709
|
+
}
|
|
3710
|
+
/** @internal The comments part content, when this sheet owns it. */
|
|
3711
|
+
buildCommentsXml() {
|
|
3712
|
+
const authors = [...new Set([...__privateGet(this, _comments).values()].map((c) => c.author))];
|
|
3713
|
+
const authorsXml = authors.map((a) => `<author>${escXml2(a)}</author>`).join("");
|
|
3714
|
+
const listXml = [...__privateGet(this, _comments).values()].map(
|
|
3715
|
+
(c) => `<comment ref="${c.ref}" authorId="${authors.indexOf(c.author)}"><text><r><t xml:space="preserve">${escXml2(c.text)}</t></r></text></comment>`
|
|
3716
|
+
).join("");
|
|
3717
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
3718
|
+
<comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><authors>${authorsXml}</authors><commentList>${listXml}</commentList></comments>`;
|
|
3719
|
+
}
|
|
3720
|
+
/**
|
|
3721
|
+
* @internal The legacy VML drawing that positions the comment boxes. Excel
|
|
3722
|
+
* needs it to draw the note indicator and popup; the comments part alone
|
|
3723
|
+
* carries only the text.
|
|
3724
|
+
*/
|
|
3725
|
+
buildCommentsVml() {
|
|
3726
|
+
const shapes = [...__privateGet(this, _comments).values()].map((c, idx) => {
|
|
3727
|
+
const { row, column } = parseCellRef(c.ref);
|
|
3728
|
+
const anchor = `${column}, 15, ${row - 1}, 10, ${column + 2}, 15, ${row + 3}, 4`;
|
|
3729
|
+
return `<v:shape id="_x0000_s${1025 + idx}" type="#_x0000_t202" style="position:absolute;margin-left:60pt;margin-top:5pt;width:108pt;height:60pt;z-index:${idx + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto"><v:fill color2="#ffffe1"/><v:shadow on="t" color="black" obscured="t"/><v:path o:connecttype="none"/><v:textbox style="mso-direction-alt:auto"><div style="text-align:left"/></v:textbox><x:ClientData ObjectType="Note"><x:MoveWithCells/><x:SizeWithCells/><x:Anchor>${anchor}</x:Anchor><x:AutoFill>False</x:AutoFill><x:Row>${row - 1}</x:Row><x:Column>${column - 1}</x:Column></x:ClientData></v:shape>`;
|
|
3730
|
+
}).join("");
|
|
3731
|
+
return `<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"><o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout><v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/><v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>${shapes}</xml>`;
|
|
2668
3732
|
}
|
|
2669
3733
|
};
|
|
2670
3734
|
_sharedStrings2 = new WeakMap();
|
|
@@ -2678,23 +3742,263 @@ _autoFilter = new WeakMap();
|
|
|
2678
3742
|
_dataValidations = new WeakMap();
|
|
2679
3743
|
_visibility = new WeakMap();
|
|
2680
3744
|
_drawings = new WeakMap();
|
|
3745
|
+
_protection = new WeakMap();
|
|
3746
|
+
_protectionCredentials = new WeakMap();
|
|
3747
|
+
_hyperlinks = new WeakMap();
|
|
3748
|
+
_comments = new WeakMap();
|
|
3749
|
+
_commentsDirty = new WeakMap();
|
|
3750
|
+
_pendingHyperlinkRels = new WeakMap();
|
|
2681
3751
|
_preservedTail = new WeakMap();
|
|
2682
3752
|
_preservedRels = new WeakMap();
|
|
2683
3753
|
_Worksheet_instances = new WeakSet();
|
|
3754
|
+
/** Removes and returns a cell's data record, pruning an emptied bare row. */
|
|
3755
|
+
takeCellData_fn = function(ref) {
|
|
3756
|
+
return __privateMethod(this, _Worksheet_instances, takeCellDataAt_fn).call(this, parseCellRef(ref).row, ref);
|
|
3757
|
+
};
|
|
3758
|
+
takeCellDataAt_fn = function(row, ref) {
|
|
3759
|
+
const rowData = __privateGet(this, _rows).get(row);
|
|
3760
|
+
const data = rowData?.cells.get(ref);
|
|
3761
|
+
if (rowData && data) {
|
|
3762
|
+
rowData.cells.delete(ref);
|
|
3763
|
+
if (rowData.cells.size === 0 && !rowData.hidden && rowData.height === void 0) {
|
|
3764
|
+
__privateGet(this, _rows).delete(row);
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
return data;
|
|
3768
|
+
};
|
|
3769
|
+
deleteCellData_fn = function(ref) {
|
|
3770
|
+
__privateMethod(this, _Worksheet_instances, takeCellData_fn).call(this, ref);
|
|
3771
|
+
};
|
|
3772
|
+
applyStructural_fn = function(dim, kind, at, count) {
|
|
3773
|
+
const what = dim === "row" ? "Row" : "Column";
|
|
3774
|
+
if (!Number.isInteger(at) || at < 1) throw new RangeError(`${what} index must be an integer >= 1.`);
|
|
3775
|
+
if (!Number.isInteger(count) || count < 1) throw new RangeError("Count must be an integer >= 1.");
|
|
3776
|
+
const shift = { dim, kind, at, count, sheetName: __privateGet(this, _name) };
|
|
3777
|
+
if (kind === "insert") {
|
|
3778
|
+
const limit = dim === "row" ? EXCEL_MAX_ROWS : EXCEL_MAX_COLUMNS;
|
|
3779
|
+
const used = dim === "row" ? __privateMethod(this, _Worksheet_instances, maxPopulatedRow_fn).call(this) : this.usedBounds.columnCount;
|
|
3780
|
+
if (used >= at && used + count > limit) {
|
|
3781
|
+
throw new RangeError(`Insert would push populated cells past the sheet limit of ${limit}.`);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
__privateMethod(this, _Worksheet_instances, shiftRowsAndCells_fn).call(this, shift);
|
|
3785
|
+
const refErrors = __privateMethod(this, _Worksheet_instances, rewriteFormulas_fn).call(this, shift);
|
|
3786
|
+
__privateMethod(this, _Worksheet_instances, shiftMerges_fn).call(this, shift);
|
|
3787
|
+
__privateMethod(this, _Worksheet_instances, shiftAutoFilter_fn).call(this, shift);
|
|
3788
|
+
__privateMethod(this, _Worksheet_instances, shiftValidations_fn).call(this, shift);
|
|
3789
|
+
__privateMethod(this, _Worksheet_instances, shiftHyperlinks_fn).call(this, shift);
|
|
3790
|
+
__privateMethod(this, _Worksheet_instances, shiftComments_fn).call(this, shift);
|
|
3791
|
+
if (dim === "col") __privateMethod(this, _Worksheet_instances, shiftColDefs_fn).call(this, shift);
|
|
3792
|
+
return { refErrors };
|
|
3793
|
+
};
|
|
3794
|
+
/**
|
|
3795
|
+
* Re-keys the row map / per-row cell maps and shifts every cell's spill range.
|
|
3796
|
+
*
|
|
3797
|
+
* Rows and columns before the shift boundary keep their keys, their reference
|
|
3798
|
+
* strings and their identity, so an edit near the end of a sheet costs
|
|
3799
|
+
* proportionally little rather than rebuilding the whole model. Spill ranges
|
|
3800
|
+
* are the one thing that must be visited everywhere, since a range belonging
|
|
3801
|
+
* to an unmoved anchor can still span the boundary — but that pass is a bare
|
|
3802
|
+
* property test per cell.
|
|
3803
|
+
*/
|
|
3804
|
+
shiftRowsAndCells_fn = function(shift) {
|
|
3805
|
+
if (shift.dim === "row") __privateMethod(this, _Worksheet_instances, shiftRows_fn).call(this, shift);
|
|
3806
|
+
else __privateMethod(this, _Worksheet_instances, shiftColumns_fn).call(this, shift);
|
|
3807
|
+
};
|
|
3808
|
+
shiftRows_fn = function(shift) {
|
|
3809
|
+
const moved = [];
|
|
3810
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3811
|
+
if (row.rowIndex < shift.at) {
|
|
3812
|
+
__privateMethod(this, _Worksheet_instances, shiftSpillRanges_fn).call(this, row, shift);
|
|
3813
|
+
continue;
|
|
3814
|
+
}
|
|
3815
|
+
const shifted = shiftIndex(row.rowIndex, shift);
|
|
3816
|
+
__privateGet(this, _rows).delete(row.rowIndex);
|
|
3817
|
+
if (shifted === null) continue;
|
|
3818
|
+
row.rowIndex = shifted;
|
|
3819
|
+
moved.push(row);
|
|
3820
|
+
}
|
|
3821
|
+
for (const row of moved) {
|
|
3822
|
+
const suffix = String(row.rowIndex);
|
|
3823
|
+
const newCells = /* @__PURE__ */ new Map();
|
|
3824
|
+
for (const cd of row.cells.values()) {
|
|
3825
|
+
const ref = cd.reference.slice(0, refSplit(cd.reference)) + suffix;
|
|
3826
|
+
cd.reference = ref;
|
|
3827
|
+
if (cd.arrayRef) cd.arrayRef = shiftRangeRef(cd.arrayRef, shift) ?? void 0;
|
|
3828
|
+
newCells.set(ref, cd);
|
|
3829
|
+
}
|
|
3830
|
+
row.cells = newCells;
|
|
3831
|
+
__privateGet(this, _rows).set(row.rowIndex, row);
|
|
3832
|
+
}
|
|
3833
|
+
};
|
|
3834
|
+
shiftColumns_fn = function(shift) {
|
|
3835
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3836
|
+
let affected;
|
|
3837
|
+
for (const cd of row.cells.values()) {
|
|
3838
|
+
const ref = cd.reference;
|
|
3839
|
+
if (columnFromRef(ref, refSplit(ref)) < shift.at) {
|
|
3840
|
+
__privateMethod(this, _Worksheet_instances, shiftSpillRange_fn).call(this, cd, shift);
|
|
3841
|
+
continue;
|
|
3842
|
+
}
|
|
3843
|
+
(affected ?? (affected = [])).push(cd);
|
|
3844
|
+
}
|
|
3845
|
+
if (!affected) continue;
|
|
3846
|
+
for (const cd of affected) row.cells.delete(cd.reference);
|
|
3847
|
+
for (const cd of affected) {
|
|
3848
|
+
const split = refSplit(cd.reference);
|
|
3849
|
+
const shifted = shiftIndex(columnFromRef(cd.reference, split), shift);
|
|
3850
|
+
if (shifted === null) continue;
|
|
3851
|
+
const ref = columnLetter(shifted) + cd.reference.slice(split);
|
|
3852
|
+
cd.reference = ref;
|
|
3853
|
+
__privateMethod(this, _Worksheet_instances, shiftSpillRange_fn).call(this, cd, shift);
|
|
3854
|
+
row.cells.set(ref, cd);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
};
|
|
3858
|
+
/** Largest 1-based row index holding a cell, or 0. O(rows), no ref parsing. */
|
|
3859
|
+
maxPopulatedRow_fn = function() {
|
|
3860
|
+
let max = 0;
|
|
3861
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3862
|
+
if (row.cells.size > 0 && row.rowIndex > max) max = row.rowIndex;
|
|
3863
|
+
}
|
|
3864
|
+
return max;
|
|
3865
|
+
};
|
|
3866
|
+
shiftSpillRanges_fn = function(row, shift) {
|
|
3867
|
+
for (const cd of row.cells.values()) __privateMethod(this, _Worksheet_instances, shiftSpillRange_fn).call(this, cd, shift);
|
|
3868
|
+
};
|
|
3869
|
+
shiftSpillRange_fn = function(cd, shift) {
|
|
3870
|
+
if (cd.arrayRef) cd.arrayRef = shiftRangeRef(cd.arrayRef, shift) ?? void 0;
|
|
3871
|
+
};
|
|
3872
|
+
/** Rewrites every formula on the sheet; returns refs that now hold #REF!. */
|
|
3873
|
+
rewriteFormulas_fn = function(shift) {
|
|
3874
|
+
const refErrors = [];
|
|
3875
|
+
for (const row of __privateGet(this, _rows).values()) {
|
|
3876
|
+
for (const cd of row.cells.values()) {
|
|
3877
|
+
if (!cd.formula) continue;
|
|
3878
|
+
const result = shiftFormulaRefs(cd.formula, shift, __privateGet(this, _name));
|
|
3879
|
+
if (result.changed) cd.formula = result.text;
|
|
3880
|
+
if (result.hasRefError) refErrors.push(cd.reference);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
return refErrors;
|
|
3884
|
+
};
|
|
3885
|
+
shiftMerges_fn = function(shift) {
|
|
3886
|
+
__privateSet(this, _mergeCells, __privateGet(this, _mergeCells).flatMap((m) => {
|
|
3887
|
+
const ref = shiftRangeRef(m.ref, shift);
|
|
3888
|
+
if (!ref) return [];
|
|
3889
|
+
const [a, b] = ref.split(":");
|
|
3890
|
+
if (!b || a === b) return [];
|
|
3891
|
+
return [{ ref }];
|
|
3892
|
+
}));
|
|
3893
|
+
};
|
|
3894
|
+
shiftAutoFilter_fn = function(shift) {
|
|
3895
|
+
if (!__privateGet(this, _autoFilter)) return;
|
|
3896
|
+
const ref = shiftRangeRef(__privateGet(this, _autoFilter).ref, shift);
|
|
3897
|
+
__privateSet(this, _autoFilter, ref ? { ref } : void 0);
|
|
3898
|
+
};
|
|
3899
|
+
shiftValidations_fn = function(shift) {
|
|
3900
|
+
__privateSet(this, _dataValidations, __privateGet(this, _dataValidations).flatMap((dv) => {
|
|
3901
|
+
const sqref = shiftSqref(dv.sqref, shift);
|
|
3902
|
+
if (!sqref) return [];
|
|
3903
|
+
const formula = shiftFormulaRefs(dv.formula, shift, __privateGet(this, _name)).text;
|
|
3904
|
+
return [{ sqref, formula }];
|
|
3905
|
+
}));
|
|
3906
|
+
};
|
|
3907
|
+
shiftHyperlinks_fn = function(shift) {
|
|
3908
|
+
const moved = /* @__PURE__ */ new Map();
|
|
3909
|
+
for (const link of __privateGet(this, _hyperlinks).values()) {
|
|
3910
|
+
const ref = shiftRangeRef(link.ref, shift);
|
|
3911
|
+
if (!ref) continue;
|
|
3912
|
+
moved.set(ref, { ...link, ref });
|
|
3913
|
+
}
|
|
3914
|
+
__privateSet(this, _hyperlinks, moved);
|
|
3915
|
+
};
|
|
3916
|
+
shiftComments_fn = function(shift) {
|
|
3917
|
+
const moved = /* @__PURE__ */ new Map();
|
|
3918
|
+
let changed = false;
|
|
3919
|
+
for (const comment of __privateGet(this, _comments).values()) {
|
|
3920
|
+
const ref = shiftRangeRef(comment.ref, shift);
|
|
3921
|
+
if (!ref) {
|
|
3922
|
+
changed = true;
|
|
3923
|
+
continue;
|
|
3924
|
+
}
|
|
3925
|
+
if (ref !== comment.ref) changed = true;
|
|
3926
|
+
moved.set(ref, { ...comment, ref });
|
|
3927
|
+
}
|
|
3928
|
+
__privateSet(this, _comments, moved);
|
|
3929
|
+
if (changed) __privateSet(this, _commentsDirty, true);
|
|
3930
|
+
};
|
|
3931
|
+
shiftColDefs_fn = function(shift) {
|
|
3932
|
+
__privateSet(this, _cols, __privateGet(this, _cols).flatMap((c) => {
|
|
3933
|
+
const span = shiftSpan(c.min, c.max, shift);
|
|
3934
|
+
return span ? [{ ...c, min: span.start, max: span.end }] : [];
|
|
3935
|
+
}));
|
|
3936
|
+
};
|
|
3937
|
+
/** Highest numeric preserved relationship id, so generated ids clear them all. */
|
|
3938
|
+
relIdBase_fn = function() {
|
|
3939
|
+
let max = 0;
|
|
3940
|
+
for (const r of __privateGet(this, _preservedRels)) {
|
|
3941
|
+
const n = RID_PATTERN.exec(r.id);
|
|
3942
|
+
if (n) max = Math.max(max, parseInt(n[1], 10));
|
|
3943
|
+
}
|
|
3944
|
+
return max;
|
|
3945
|
+
};
|
|
2684
3946
|
/**
|
|
2685
3947
|
* Preserved leaf elements that will actually be emitted. A sheet may only have
|
|
2686
3948
|
* one `<drawing>`, so live providers suppress a preserved one — every consumer
|
|
2687
3949
|
* (serialisation, relationships, pruning) must agree on that, hence one helper.
|
|
2688
3950
|
*/
|
|
2689
3951
|
activeTail_fn = function() {
|
|
2690
|
-
return __privateGet(this, _preservedTail).filter((t) =>
|
|
3952
|
+
return __privateGet(this, _preservedTail).filter((t) => {
|
|
3953
|
+
if (t.tag === "drawing") return !this.hasDrawings;
|
|
3954
|
+
if (t.tag === "legacyDrawing") return !this.commentPartsReplaced;
|
|
3955
|
+
return true;
|
|
3956
|
+
});
|
|
3957
|
+
};
|
|
3958
|
+
/**
|
|
3959
|
+
* The relationship-bearing leaf elements, in schema order: `drawing`,
|
|
3960
|
+
* `legacyDrawing`, `legacyDrawingHF`, `picture`. A sheet may hold only one of
|
|
3961
|
+
* each, so generated content replaces the preserved element of the same tag —
|
|
3962
|
+
* live drawing providers replace a preserved `<drawing>`, and comments this
|
|
3963
|
+
* sheet now owns replace a preserved `<legacyDrawing>`.
|
|
3964
|
+
*/
|
|
3965
|
+
buildTailXml_fn = function(rels) {
|
|
3966
|
+
const preserved = __privateMethod(this, _Worksheet_instances, activeTail_fn).call(this);
|
|
3967
|
+
const byTag = (tag) => preserved.filter((t) => t.tag === tag).map((t) => t.xml).join("");
|
|
3968
|
+
const drawing = rels.drawing ? `<drawing r:id="${rels.drawing}"/>` : byTag("drawing");
|
|
3969
|
+
const legacy = rels.vmlDrawing ? `<legacyDrawing r:id="${rels.vmlDrawing}"/>` : byTag("legacyDrawing");
|
|
3970
|
+
return drawing + legacy + byTag("legacyDrawingHF") + byTag("picture");
|
|
3971
|
+
};
|
|
3972
|
+
buildSheetProtectionXml_fn = function() {
|
|
3973
|
+
if (!__privateGet(this, _protection)) return "";
|
|
3974
|
+
let attrs = ' sheet="1"';
|
|
3975
|
+
for (const [key, attr] of PROTECTION_ATTRS) {
|
|
3976
|
+
const blocked = !__privateGet(this, _protection)[key];
|
|
3977
|
+
const defaultBlocked = !PROTECTION_DEFAULTS[key];
|
|
3978
|
+
if (blocked !== defaultBlocked) attrs += ` ${attr}="${blocked ? 1 : 0}"`;
|
|
3979
|
+
}
|
|
3980
|
+
return `<sheetProtection${__privateGet(this, _protectionCredentials)}${attrs}/>`;
|
|
3981
|
+
};
|
|
3982
|
+
buildHyperlinksXml_fn = function(relIds) {
|
|
3983
|
+
if (__privateGet(this, _hyperlinks).size === 0) return "";
|
|
3984
|
+
const inner = [...__privateGet(this, _hyperlinks).values()].map((link) => {
|
|
3985
|
+
const relId = relIds.get(link.ref);
|
|
3986
|
+
const parts = [`ref="${link.ref}"`];
|
|
3987
|
+
if (relId) parts.push(`r:id="${relId}"`);
|
|
3988
|
+
if (link.location) parts.push(`location="${escXmlAttr(link.location)}"`);
|
|
3989
|
+
if (link.display) parts.push(`display="${escXmlAttr(link.display)}"`);
|
|
3990
|
+
if (link.tooltip) parts.push(`tooltip="${escXmlAttr(link.tooltip)}"`);
|
|
3991
|
+
return `<hyperlink ${parts.join(" ")}/>`;
|
|
3992
|
+
}).join("");
|
|
3993
|
+
return `<hyperlinks>${inner}</hyperlinks>`;
|
|
2691
3994
|
};
|
|
2692
3995
|
// ── Private XML builders ───────────────────────────────────────────────────
|
|
2693
3996
|
buildColsXml_fn = function() {
|
|
2694
3997
|
if (__privateGet(this, _cols).length === 0) return "";
|
|
2695
3998
|
const inner = __privateGet(this, _cols).map((c) => {
|
|
3999
|
+
const width = c.width !== void 0 ? ` width="${c.width.toFixed(2)}" customWidth="${c.customWidth ? 1 : 0}"` : "";
|
|
2696
4000
|
const hidden = c.hidden ? ' hidden="1"' : "";
|
|
2697
|
-
return `<col min="${c.min}" max="${c.max}"
|
|
4001
|
+
return `<col min="${c.min}" max="${c.max}"${width}${hidden}/>`;
|
|
2698
4002
|
}).join("");
|
|
2699
4003
|
return `<cols>${inner}</cols>`;
|
|
2700
4004
|
};
|
|
@@ -2704,12 +4008,8 @@ buildSheetDataXml_fn = function() {
|
|
|
2704
4008
|
const rowsXml = sortedRows.map((row) => {
|
|
2705
4009
|
const ht = row.height !== void 0 && row.customHeight ? ` ht="${row.height}" customHeight="1"` : "";
|
|
2706
4010
|
const hidden = row.hidden ? ' hidden="1"' : "";
|
|
2707
|
-
const sortedCells = [...row.cells.values()].sort((a, b) =>
|
|
2708
|
-
|
|
2709
|
-
const rb = parseCellRef(b.reference);
|
|
2710
|
-
return ra.column - rb.column;
|
|
2711
|
-
});
|
|
2712
|
-
const cellsXml = sortedCells.map((cd) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)).toXml()).filter(Boolean).join("");
|
|
4011
|
+
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 }) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)).toXml()).filter(Boolean).join("");
|
|
2713
4013
|
if (!cellsXml && !ht && !hidden) return "";
|
|
2714
4014
|
return `<row r="${row.rowIndex}"${ht}${hidden}>${cellsXml}</row>`;
|
|
2715
4015
|
}).filter(Boolean).join("");
|
|
@@ -2738,6 +4038,61 @@ buildSheetViewXml_fn = function() {
|
|
|
2738
4038
|
const ySplit = row > 0 ? ` ySplit="${row}"` : "";
|
|
2739
4039
|
return `<sheetView workbookViewId="0"><pane${xSplit}${ySplit} topLeftCell="${topLeft}" activePane="${activePane}" state="frozen"/></sheetView>`;
|
|
2740
4040
|
};
|
|
4041
|
+
/**
|
|
4042
|
+
* Returns the single-column ColDef covering `columnIndex`, splitting a wider
|
|
4043
|
+
* span into up to three pieces so the other columns keep their width/hidden
|
|
4044
|
+
* state. Returns undefined when no definition covers the column.
|
|
4045
|
+
*/
|
|
4046
|
+
splitColSpan_fn = function(columnIndex) {
|
|
4047
|
+
const idx = __privateMethod(this, _Worksheet_instances, findColDefIndex_fn).call(this, columnIndex);
|
|
4048
|
+
if (idx === -1) return void 0;
|
|
4049
|
+
const span = __privateGet(this, _cols)[idx];
|
|
4050
|
+
if (span.min === span.max) return span;
|
|
4051
|
+
const pieces = [];
|
|
4052
|
+
if (span.min < columnIndex) pieces.push({ ...span, max: columnIndex - 1 });
|
|
4053
|
+
const target = { ...span, min: columnIndex, max: columnIndex };
|
|
4054
|
+
pieces.push(target);
|
|
4055
|
+
if (span.max > columnIndex) pieces.push({ ...span, min: columnIndex + 1 });
|
|
4056
|
+
__privateGet(this, _cols).splice(idx, 1, ...pieces);
|
|
4057
|
+
return target;
|
|
4058
|
+
};
|
|
4059
|
+
/**
|
|
4060
|
+
* Inserts a definition keeping `#cols` sorted by `min`. Binary insertion,
|
|
4061
|
+
* because re-sorting the whole array per call makes setting widths for every
|
|
4062
|
+
* column of a wide sheet quadratic.
|
|
4063
|
+
*/
|
|
4064
|
+
insertColDef_fn = function(def) {
|
|
4065
|
+
let lo = 0;
|
|
4066
|
+
let hi = __privateGet(this, _cols).length;
|
|
4067
|
+
while (lo < hi) {
|
|
4068
|
+
const mid = lo + hi >>> 1;
|
|
4069
|
+
if (__privateGet(this, _cols)[mid].min < def.min) lo = mid + 1;
|
|
4070
|
+
else hi = mid;
|
|
4071
|
+
}
|
|
4072
|
+
__privateGet(this, _cols).splice(lo, 0, def);
|
|
4073
|
+
return def;
|
|
4074
|
+
};
|
|
4075
|
+
/** Index of the definition covering `columnIndex`, or -1. Binary search. */
|
|
4076
|
+
findColDefIndex_fn = function(columnIndex) {
|
|
4077
|
+
let lo = 0;
|
|
4078
|
+
let hi = __privateGet(this, _cols).length - 1;
|
|
4079
|
+
while (lo <= hi) {
|
|
4080
|
+
const mid = lo + hi >>> 1;
|
|
4081
|
+
const c = __privateGet(this, _cols)[mid];
|
|
4082
|
+
if (columnIndex < c.min) hi = mid - 1;
|
|
4083
|
+
else if (columnIndex > c.max) lo = mid + 1;
|
|
4084
|
+
else return mid;
|
|
4085
|
+
}
|
|
4086
|
+
return -1;
|
|
4087
|
+
};
|
|
4088
|
+
findColDef_fn = function(columnIndex) {
|
|
4089
|
+
const idx = __privateMethod(this, _Worksheet_instances, findColDefIndex_fn).call(this, columnIndex);
|
|
4090
|
+
return idx === -1 ? void 0 : __privateGet(this, _cols)[idx];
|
|
4091
|
+
};
|
|
4092
|
+
/** Drops definitions that no longer carry any information. */
|
|
4093
|
+
pruneColDefs_fn = function() {
|
|
4094
|
+
__privateSet(this, _cols, __privateGet(this, _cols).filter((c) => c.width !== void 0 || c.customWidth || c.hidden));
|
|
4095
|
+
};
|
|
2741
4096
|
getOrCreateRow_fn = function(rowIndex) {
|
|
2742
4097
|
let row = __privateGet(this, _rows).get(rowIndex);
|
|
2743
4098
|
if (!row) {
|
|
@@ -2746,9 +4101,64 @@ getOrCreateRow_fn = function(rowIndex) {
|
|
|
2746
4101
|
}
|
|
2747
4102
|
return row;
|
|
2748
4103
|
};
|
|
4104
|
+
var RID_PATTERN = /^rId(\d+)$/;
|
|
2749
4105
|
function escXml2(s) {
|
|
2750
4106
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2751
4107
|
}
|
|
4108
|
+
function escXmlAttr(s) {
|
|
4109
|
+
return escXml2(s).replace(/"/g, """);
|
|
4110
|
+
}
|
|
4111
|
+
function normalizeRef(ref) {
|
|
4112
|
+
const normalized = normalizeRefFast(ref);
|
|
4113
|
+
parseCellRef(normalized);
|
|
4114
|
+
return normalized;
|
|
4115
|
+
}
|
|
4116
|
+
function normalizeRangeRef(ref) {
|
|
4117
|
+
const normalized = normalizeRefFast(ref);
|
|
4118
|
+
const colon = normalized.indexOf(":");
|
|
4119
|
+
if (colon === -1) {
|
|
4120
|
+
parseCellRef(normalized);
|
|
4121
|
+
return normalized;
|
|
4122
|
+
}
|
|
4123
|
+
parseCellRef(normalized.slice(0, colon));
|
|
4124
|
+
parseCellRef(normalized.slice(colon + 1));
|
|
4125
|
+
return normalized;
|
|
4126
|
+
}
|
|
4127
|
+
function normalizeRefFast(ref) {
|
|
4128
|
+
return ref.indexOf("$") === -1 ? upperFast(ref) : ref.replace(/\$/g, "").toUpperCase();
|
|
4129
|
+
}
|
|
4130
|
+
function upperFast(s) {
|
|
4131
|
+
for (let i = 0; i < s.length; i++) {
|
|
4132
|
+
const code = s.charCodeAt(i);
|
|
4133
|
+
if (code >= 97 && code <= 122) return s.toUpperCase();
|
|
4134
|
+
}
|
|
4135
|
+
return s;
|
|
4136
|
+
}
|
|
4137
|
+
function refSplit(ref) {
|
|
4138
|
+
let i = 0;
|
|
4139
|
+
while (i < ref.length) {
|
|
4140
|
+
const code = ref.charCodeAt(i);
|
|
4141
|
+
if (code < 65 || code > 90) break;
|
|
4142
|
+
i++;
|
|
4143
|
+
}
|
|
4144
|
+
return i;
|
|
4145
|
+
}
|
|
4146
|
+
function columnFromRef(ref, split) {
|
|
4147
|
+
let col = 0;
|
|
4148
|
+
for (let i = 0; i < split; i++) col = col * 26 + (ref.charCodeAt(i) - 64);
|
|
4149
|
+
return col;
|
|
4150
|
+
}
|
|
4151
|
+
function translateRangeRef(ref, deltaRow, deltaCol) {
|
|
4152
|
+
const parts = ref.split(":").map((p) => {
|
|
4153
|
+
const { row, column } = parseCellRef(p);
|
|
4154
|
+
const r = row + deltaRow;
|
|
4155
|
+
const c = column + deltaCol;
|
|
4156
|
+
if (r < 1 || c < 1 || r > EXCEL_MAX_ROWS || c > EXCEL_MAX_COLUMNS) return null;
|
|
4157
|
+
return cellRefFromRowCol(r, c);
|
|
4158
|
+
});
|
|
4159
|
+
if (parts.some((p) => p === null)) return void 0;
|
|
4160
|
+
return parts.join(":");
|
|
4161
|
+
}
|
|
2752
4162
|
|
|
2753
4163
|
// src/model/defined-name.ts
|
|
2754
4164
|
var _names, _DefinedNameManager_instances, indexOf_fn;
|
|
@@ -2813,7 +4223,7 @@ indexOf_fn = function(name, scope) {
|
|
|
2813
4223
|
};
|
|
2814
4224
|
|
|
2815
4225
|
// src/model/workbook.ts
|
|
2816
|
-
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
4226
|
+
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, structuralEdit_fn, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
2817
4227
|
var _Workbook = class _Workbook {
|
|
2818
4228
|
constructor(sharedStrings, styles) {
|
|
2819
4229
|
__privateAdd(this, _Workbook_instances);
|
|
@@ -2873,6 +4283,11 @@ var _Workbook = class _Workbook {
|
|
|
2873
4283
|
const wsRelsXml = readXmlEntry(entries, `${dir}/_rels/${file}.rels`);
|
|
2874
4284
|
if (wsRelsXml) {
|
|
2875
4285
|
ws.loadPreservedState(wsXml, parseRelationships(wsRelsXml, wsPath));
|
|
4286
|
+
const commentsPath = ws.commentsPartPath();
|
|
4287
|
+
if (commentsPath) {
|
|
4288
|
+
const commentsXml = readXmlEntry(entries, commentsPath);
|
|
4289
|
+
if (commentsXml) ws.loadCommentsXml(commentsXml);
|
|
4290
|
+
}
|
|
2876
4291
|
}
|
|
2877
4292
|
}
|
|
2878
4293
|
__privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
@@ -2961,6 +4376,32 @@ var _Workbook = class _Workbook {
|
|
|
2961
4376
|
if (!entry) throw new Error(`Worksheet "${name}" not found.`);
|
|
2962
4377
|
return entry.worksheet;
|
|
2963
4378
|
}
|
|
4379
|
+
// ── Structural editing (workbook-wide) ───────────────────────────────────────
|
|
4380
|
+
/**
|
|
4381
|
+
* Inserts rows on one sheet and keeps the whole workbook consistent:
|
|
4382
|
+
* formulas on OTHER sheets that reference the edited sheet, and defined
|
|
4383
|
+
* names, are rewritten too. Prefer this over `Worksheet.insertRows` in a
|
|
4384
|
+
* multi-sheet workbook.
|
|
4385
|
+
*
|
|
4386
|
+
* @returns The edited sheet's result; `refErrors` additionally includes
|
|
4387
|
+
* sheet-qualified refs (e.g. `"Other!B2"`) for affected formula
|
|
4388
|
+
* cells on other sheets.
|
|
4389
|
+
*/
|
|
4390
|
+
insertRows(sheetName, at, count = 1) {
|
|
4391
|
+
return __privateMethod(this, _Workbook_instances, structuralEdit_fn).call(this, sheetName, "row", "insert", at, count);
|
|
4392
|
+
}
|
|
4393
|
+
/** Workbook-wide counterpart to `Worksheet.deleteRows`. See {@link insertRows}. */
|
|
4394
|
+
deleteRows(sheetName, at, count = 1) {
|
|
4395
|
+
return __privateMethod(this, _Workbook_instances, structuralEdit_fn).call(this, sheetName, "row", "delete", at, count);
|
|
4396
|
+
}
|
|
4397
|
+
/** Workbook-wide counterpart to `Worksheet.insertColumns`. See {@link insertRows}. */
|
|
4398
|
+
insertColumns(sheetName, at, count = 1) {
|
|
4399
|
+
return __privateMethod(this, _Workbook_instances, structuralEdit_fn).call(this, sheetName, "col", "insert", at, count);
|
|
4400
|
+
}
|
|
4401
|
+
/** Workbook-wide counterpart to `Worksheet.deleteColumns`. See {@link insertRows}. */
|
|
4402
|
+
deleteColumns(sheetName, at, count = 1) {
|
|
4403
|
+
return __privateMethod(this, _Workbook_instances, structuralEdit_fn).call(this, sheetName, "col", "delete", at, count);
|
|
4404
|
+
}
|
|
2964
4405
|
// ── Defined names (named ranges) ─────────────────────────────────────────────
|
|
2965
4406
|
/**
|
|
2966
4407
|
* Defines (or replaces) a named range.
|
|
@@ -3041,6 +4482,23 @@ _preservedParts = new WeakMap();
|
|
|
3041
4482
|
_preservedContentTypes = new WeakMap();
|
|
3042
4483
|
_orphanedTargets = new WeakMap();
|
|
3043
4484
|
_Workbook_instances = new WeakSet();
|
|
4485
|
+
structuralEdit_fn = function(sheetName, dim, kind, at, count) {
|
|
4486
|
+
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
4487
|
+
const target = this.getWorksheet(sheetName);
|
|
4488
|
+
const result = dim === "row" ? kind === "insert" ? target.insertRows(at, count) : target.deleteRows(at, count) : kind === "insert" ? target.insertColumns(at, count) : target.deleteColumns(at, count);
|
|
4489
|
+
const shift = { dim, kind, at, count, sheetName: target.name };
|
|
4490
|
+
const refErrors = [...result.refErrors];
|
|
4491
|
+
for (const ws of this.worksheets) {
|
|
4492
|
+
if (ws === target) continue;
|
|
4493
|
+
const external = ws.applyExternalShift(shift);
|
|
4494
|
+
for (const ref of external.refErrors) refErrors.push(`${ws.name}!${ref}`);
|
|
4495
|
+
}
|
|
4496
|
+
for (const dn of [...__privateGet(this, _definedNames).all]) {
|
|
4497
|
+
const rewritten = shiftFormulaRefs(dn.refersTo, shift, dn.scope ?? "");
|
|
4498
|
+
if (rewritten.changed) __privateGet(this, _definedNames).define(dn.name, rewritten.text, dn.scope);
|
|
4499
|
+
}
|
|
4500
|
+
return { refErrors };
|
|
4501
|
+
};
|
|
3044
4502
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
3045
4503
|
assertOpen_fn = function() {
|
|
3046
4504
|
if (__privateGet(this, _closed)) throw new Error("Workbook has been closed.");
|
|
@@ -3121,13 +4579,40 @@ buildZipEntries_fn = function() {
|
|
|
3121
4579
|
entries.set(`xl/worksheets/sheet${i + 1}.xml`, __privateGet(this, _sheets)[i].worksheet.buildXml());
|
|
3122
4580
|
}
|
|
3123
4581
|
const extraOverrides = [];
|
|
4582
|
+
const extraDefaults = [];
|
|
3124
4583
|
let drawingCounter = 0;
|
|
3125
4584
|
let partCounter = 0;
|
|
4585
|
+
let commentsCounter = 0;
|
|
3126
4586
|
for (let i = 0; i < __privateGet(this, _sheets).length; i++) {
|
|
3127
4587
|
const ws = __privateGet(this, _sheets)[i].worksheet;
|
|
3128
4588
|
const sheetRels = ws.preservedRelationships.map(
|
|
3129
|
-
(r) => ` <Relationship Id="${r.id}" Type="${r.type}" Target="${r.target}"${r.external ? ' TargetMode="External"' : ""}/>`
|
|
4589
|
+
(r) => ` <Relationship Id="${r.id}" Type="${r.type}" Target="${escapeXml(r.target)}"${r.external ? ' TargetMode="External"' : ""}/>`
|
|
3130
4590
|
);
|
|
4591
|
+
const generated = ws.generatedRels();
|
|
4592
|
+
for (const link of ws.hyperlinks) {
|
|
4593
|
+
const relId = generated.hyperlinks.get(link.ref);
|
|
4594
|
+
if (!relId || !link.target) continue;
|
|
4595
|
+
sheetRels.push(
|
|
4596
|
+
` <Relationship Id="${relId}" Type="${REL_TYPES.hyperlink}" Target="${escapeXml(link.target)}" TargetMode="External"/>`
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
4599
|
+
if (generated.comments && generated.vmlDrawing) {
|
|
4600
|
+
do {
|
|
4601
|
+
commentsCounter++;
|
|
4602
|
+
} while (preservedPaths.has(`xl/comments${commentsCounter}.xml`) || preservedPaths.has(`xl/drawings/vmlDrawing${commentsCounter}.vml`));
|
|
4603
|
+
const commentsPath = `xl/comments${commentsCounter}.xml`;
|
|
4604
|
+
const vmlPath = `xl/drawings/vmlDrawing${commentsCounter}.vml`;
|
|
4605
|
+
entries.set(commentsPath, ws.buildCommentsXml());
|
|
4606
|
+
entries.set(vmlPath, ws.buildCommentsVml());
|
|
4607
|
+
extraOverrides.push({ partName: `/${commentsPath}`, contentType: CONTENT_TYPES.comments });
|
|
4608
|
+
if (!extraDefaults.some((d) => d.extension === "vml")) {
|
|
4609
|
+
extraDefaults.push({ extension: "vml", contentType: CONTENT_TYPES.vmlDrawing });
|
|
4610
|
+
}
|
|
4611
|
+
sheetRels.push(
|
|
4612
|
+
` <Relationship Id="${generated.comments}" Type="${REL_TYPES.comments}" Target="../comments${commentsCounter}.xml"/>`,
|
|
4613
|
+
` <Relationship Id="${generated.vmlDrawing}" Type="${REL_TYPES.vmlDrawing}" Target="../drawings/vmlDrawing${commentsCounter}.vml"/>`
|
|
4614
|
+
);
|
|
4615
|
+
}
|
|
3131
4616
|
if (ws.hasDrawings) {
|
|
3132
4617
|
do {
|
|
3133
4618
|
drawingCounter++;
|
|
@@ -3164,7 +4649,7 @@ buildZipEntries_fn = function() {
|
|
|
3164
4649
|
buildContentTypes(
|
|
3165
4650
|
sheetCount,
|
|
3166
4651
|
[...preservedCt.overrides, ...extraOverrides],
|
|
3167
|
-
preservedCt.defaults
|
|
4652
|
+
[...preservedCt.defaults, ...extraDefaults]
|
|
3168
4653
|
)
|
|
3169
4654
|
);
|
|
3170
4655
|
return entries;
|
|
@@ -3185,6 +4670,6 @@ function resolveWorksheetPath(relsXml, relId) {
|
|
|
3185
4670
|
return void 0;
|
|
3186
4671
|
}
|
|
3187
4672
|
|
|
3188
|
-
export { CONTENT_TYPES, Cell, CellStyle, DefinedNameManager, ExcelAlignment, ExcelBorder, ExcelBorderEdge, ExcelBorderStyle, ExcelColor, ExcelFill, ExcelFont, ExcelHorizontalAlignment, ExcelNumberFormat, ExcelPatternType, ExcelReadingOrder, ExcelUnderline, ExcelVerticalAlignRun, ExcelVerticalAlignment, NS, REL_TYPES, Range, StyleManager, WORKSHEET_DRAWING_REL_ID, Workbook, Worksheet, XmlBuilder, buildDrawingXml, buildRelsXml, cellRefFromRowCol, columnLetter, columnNumber, decodeUtf8, encodeUtf8, escapeXml, formatNumberWithCode, fromOADate, getAttr, getElementsByTagName, isDateFormatCode, isDateFormatId, listEntries, parseCellRef, parseRangeRef, parseXml, readXmlEntry, requireXmlEntry, setBinaryEntry, setXmlEntry, toOADate, unzipXlsx, xml, zipXlsx };
|
|
4673
|
+
export { CONTENT_TYPES, Cell, CellStyle, DefinedNameManager, EXCEL_MAX_COLUMNS, EXCEL_MAX_ROWS, ExcelAlignment, ExcelBorder, ExcelBorderEdge, ExcelBorderStyle, ExcelColor, ExcelFill, ExcelFont, ExcelHorizontalAlignment, ExcelNumberFormat, ExcelPatternType, ExcelReadingOrder, ExcelUnderline, ExcelVerticalAlignRun, ExcelVerticalAlignment, NS, REL_TYPES, Range, StyleManager, WORKSHEET_DRAWING_REL_ID, Workbook, Worksheet, XmlBuilder, buildDrawingXml, buildRelsXml, cellRefFromRowCol, columnLetter, columnNumber, decodeUtf8, decodeXmlEntities, encodeUtf8, escapeXml, formatDateCode, formatNumberWithCode, formatRangeRef, fromOADate, getAttr, getElementsByTagName, isDateFormatCode, isDateFormatId, listEntries, parseCellRef, parseRangeRef, parseXml, readXmlEntry, requireXmlEntry, setBinaryEntry, setXmlEntry, shiftFormulaRefs, shiftIndex, shiftRangeRef, shiftSpan, shiftSqref, toOADate, unzipXlsx, xml, zipXlsx };
|
|
3189
4674
|
//# sourceMappingURL=index.js.map
|
|
3190
4675
|
//# sourceMappingURL=index.js.map
|