@devmm/puredocs-excel 1.0.3 → 1.0.5
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/LICENSE +21 -0
- package/dist/index.cjs +474 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -29
- package/dist/index.d.ts +142 -29
- package/dist/index.js +474 -38
- package/dist/index.js.map +1 -1
- package/package.json +55 -55
package/dist/index.cjs
CHANGED
|
@@ -310,19 +310,26 @@ var CONTENT_TYPES = {
|
|
|
310
310
|
};
|
|
311
311
|
|
|
312
312
|
// src/io/ooxml-templates.ts
|
|
313
|
-
function buildContentTypes(sheetCount, extraOverrides = []) {
|
|
313
|
+
function buildContentTypes(sheetCount, extraOverrides = [], extraDefaults = []) {
|
|
314
314
|
const overrides = Array.from(
|
|
315
315
|
{ length: sheetCount },
|
|
316
316
|
(_, i) => ` <Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="${CONTENT_TYPES.worksheet}"/>`
|
|
317
317
|
);
|
|
318
|
-
|
|
318
|
+
const seen = /* @__PURE__ */ new Set();
|
|
319
|
+
for (const { partName, contentType } of [...extraOverrides].reverse()) {
|
|
320
|
+
if (seen.has(partName)) continue;
|
|
321
|
+
seen.add(partName);
|
|
319
322
|
overrides.push(` <Override PartName="${partName}" ContentType="${contentType}"/>`);
|
|
320
323
|
}
|
|
324
|
+
const defaults = extraDefaults.map(
|
|
325
|
+
({ extension, contentType }) => ` <Default Extension="${extension}" ContentType="${contentType}"/>`
|
|
326
|
+
);
|
|
321
327
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
322
328
|
<Types xmlns="${"http://schemas.openxmlformats.org/package/2006/content-types"}">
|
|
323
329
|
<Default Extension="rels" ContentType="${CONTENT_TYPES.relationships}"/>
|
|
324
330
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
325
|
-
|
|
331
|
+
${defaults.length > 0 ? `${defaults.join("\n")}
|
|
332
|
+
` : ""} <Override PartName="/xl/workbook.xml" ContentType="${CONTENT_TYPES.workbook}"/>
|
|
326
333
|
<Override PartName="/xl/styles.xml" ContentType="${CONTENT_TYPES.styles}"/>
|
|
327
334
|
<Override PartName="/xl/sharedStrings.xml" ContentType="${CONTENT_TYPES.sharedStrings}"/>
|
|
328
335
|
${overrides.join("\n")}
|
|
@@ -401,6 +408,94 @@ ${relationships.join("\n")}
|
|
|
401
408
|
</Relationships>`;
|
|
402
409
|
}
|
|
403
410
|
|
|
411
|
+
// src/io/preserved-parts.ts
|
|
412
|
+
function isCoreOwned(path) {
|
|
413
|
+
return path === "[Content_Types].xml" || path === "_rels/.rels" || path === "xl/workbook.xml" || path === "xl/_rels/workbook.xml.rels" || path === "xl/styles.xml" || path === "xl/sharedStrings.xml" || path.startsWith("xl/worksheets/");
|
|
414
|
+
}
|
|
415
|
+
function collectPreservedParts(entries) {
|
|
416
|
+
const preserved = /* @__PURE__ */ new Map();
|
|
417
|
+
for (const [path, bytes] of entries) {
|
|
418
|
+
if (!isCoreOwned(path)) preserved.set(path, bytes);
|
|
419
|
+
}
|
|
420
|
+
return preserved;
|
|
421
|
+
}
|
|
422
|
+
function parseContentTypes(xml2) {
|
|
423
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
424
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
425
|
+
for (const m of xml2.matchAll(/<Default\s+[^>]*\/?>/g)) {
|
|
426
|
+
const ext = /Extension="([^"]*)"/.exec(m[0])?.[1];
|
|
427
|
+
const ct = /ContentType="([^"]*)"/.exec(m[0])?.[1];
|
|
428
|
+
if (ext && ct) defaults.set(ext.toLowerCase(), ct);
|
|
429
|
+
}
|
|
430
|
+
for (const m of xml2.matchAll(/<Override\s+[^>]*\/?>/g)) {
|
|
431
|
+
const part = /PartName="([^"]*)"/.exec(m[0])?.[1];
|
|
432
|
+
const ct = /ContentType="([^"]*)"/.exec(m[0])?.[1];
|
|
433
|
+
if (part && ct) overrides.set(part, ct);
|
|
434
|
+
}
|
|
435
|
+
return { defaults, overrides };
|
|
436
|
+
}
|
|
437
|
+
function resolveTarget(target, baseDir) {
|
|
438
|
+
if (target.startsWith("/")) return target.slice(1);
|
|
439
|
+
const segments = baseDir.split("/").filter(Boolean);
|
|
440
|
+
for (const seg of target.split("/")) {
|
|
441
|
+
if (seg === "." || seg === "") continue;
|
|
442
|
+
if (seg === "..") segments.pop();
|
|
443
|
+
else segments.push(seg);
|
|
444
|
+
}
|
|
445
|
+
return segments.join("/");
|
|
446
|
+
}
|
|
447
|
+
function parseRelationships(relsXml, ownerPath) {
|
|
448
|
+
const baseDir = ownerPath.slice(0, ownerPath.lastIndexOf("/"));
|
|
449
|
+
const rels = [];
|
|
450
|
+
for (const m of relsXml.matchAll(/<Relationship\s+[^>]*\/?>/g)) {
|
|
451
|
+
const tag = m[0];
|
|
452
|
+
const id = /\bId="([^"]*)"/.exec(tag)?.[1];
|
|
453
|
+
const type = /\bType="([^"]*)"/.exec(tag)?.[1];
|
|
454
|
+
const target = /\bTarget="([^"]*)"/.exec(tag)?.[1];
|
|
455
|
+
if (!id || !type || target === void 0) continue;
|
|
456
|
+
const external = /TargetMode="External"/.test(tag);
|
|
457
|
+
rels.push({
|
|
458
|
+
id,
|
|
459
|
+
type,
|
|
460
|
+
target,
|
|
461
|
+
resolved: external ? void 0 : resolveTarget(target, baseDir),
|
|
462
|
+
external
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
return rels;
|
|
466
|
+
}
|
|
467
|
+
function collectSubtree(parts, startPath) {
|
|
468
|
+
const seen = /* @__PURE__ */ new Set();
|
|
469
|
+
const queue = [startPath];
|
|
470
|
+
while (queue.length > 0) {
|
|
471
|
+
const path = queue.pop();
|
|
472
|
+
if (seen.has(path) || !parts.has(path)) continue;
|
|
473
|
+
seen.add(path);
|
|
474
|
+
const dir = path.slice(0, path.lastIndexOf("/"));
|
|
475
|
+
const file = path.slice(path.lastIndexOf("/") + 1);
|
|
476
|
+
const relsPath = `${dir}/_rels/${file}.rels`;
|
|
477
|
+
const relsBytes = parts.get(relsPath);
|
|
478
|
+
if (!relsBytes) continue;
|
|
479
|
+
seen.add(relsPath);
|
|
480
|
+
for (const rel of parseRelationships(decodeUtf8(relsBytes), path)) {
|
|
481
|
+
if (rel.resolved) queue.push(rel.resolved);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return seen;
|
|
485
|
+
}
|
|
486
|
+
var PRESERVED_TAIL_TAGS = ["drawing", "legacyDrawing", "legacyDrawingHF", "picture"];
|
|
487
|
+
function parseTailElements(worksheetXml) {
|
|
488
|
+
const found = [];
|
|
489
|
+
for (const tag of PRESERVED_TAIL_TAGS) {
|
|
490
|
+
const re = new RegExp(`<(?:\\w+:)?${tag}\\s[^>]*?/>|<(?:\\w+:)?${tag}\\s[^>]*?>\\s*</(?:\\w+:)?${tag}>`, "g");
|
|
491
|
+
for (const m of worksheetXml.matchAll(re)) {
|
|
492
|
+
const relId = /r:id="([^"]*)"/.exec(m[0])?.[1];
|
|
493
|
+
if (relId) found.push({ tag, relId, xml: m[0] });
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return found;
|
|
497
|
+
}
|
|
498
|
+
|
|
404
499
|
// src/model/shared-string-manager.ts
|
|
405
500
|
var _stringToIndex, _indexToString;
|
|
406
501
|
var SharedStringManager = class {
|
|
@@ -1550,6 +1645,129 @@ function escAttr(s) {
|
|
|
1550
1645
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1551
1646
|
}
|
|
1552
1647
|
|
|
1648
|
+
// src/style/number-format-renderer.ts
|
|
1649
|
+
function splitSections(code) {
|
|
1650
|
+
const sections = [];
|
|
1651
|
+
let current = "";
|
|
1652
|
+
let inQuote = false;
|
|
1653
|
+
for (let i = 0; i < code.length; i++) {
|
|
1654
|
+
const ch = code[i];
|
|
1655
|
+
if (ch === '"') {
|
|
1656
|
+
inQuote = !inQuote;
|
|
1657
|
+
current += ch;
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
if (ch === "\\" && i + 1 < code.length) {
|
|
1661
|
+
current += ch + code[++i];
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
if (ch === ";" && !inQuote) {
|
|
1665
|
+
sections.push(current);
|
|
1666
|
+
current = "";
|
|
1667
|
+
continue;
|
|
1668
|
+
}
|
|
1669
|
+
current += ch;
|
|
1670
|
+
}
|
|
1671
|
+
sections.push(current);
|
|
1672
|
+
return sections;
|
|
1673
|
+
}
|
|
1674
|
+
function isUnsupported(section) {
|
|
1675
|
+
if (/\[[<>=]/.test(section)) return true;
|
|
1676
|
+
if (/[?]/.test(section)) return true;
|
|
1677
|
+
if (/[Ee][+-]/.test(section)) return true;
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
function parseSection(section) {
|
|
1681
|
+
const stripped = section.replace(
|
|
1682
|
+
/\[(\$([^\]-]*)[^\]]*|[^\]]*)\]/g,
|
|
1683
|
+
(_m, _all, currency) => typeof currency === "string" ? currency : ""
|
|
1684
|
+
);
|
|
1685
|
+
let prefix = "";
|
|
1686
|
+
let suffix = "";
|
|
1687
|
+
let pattern = "";
|
|
1688
|
+
let seenPattern = false;
|
|
1689
|
+
let percentScale = 1;
|
|
1690
|
+
for (let i = 0; i < stripped.length; i++) {
|
|
1691
|
+
const ch = stripped[i];
|
|
1692
|
+
if (ch === '"') {
|
|
1693
|
+
const end = stripped.indexOf('"', i + 1);
|
|
1694
|
+
const literal = stripped.slice(i + 1, end < 0 ? void 0 : end);
|
|
1695
|
+
if (seenPattern) suffix += literal;
|
|
1696
|
+
else prefix += literal;
|
|
1697
|
+
i = end < 0 ? stripped.length : end;
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1700
|
+
if (ch === "\\" && i + 1 < stripped.length) {
|
|
1701
|
+
const literal = stripped[++i];
|
|
1702
|
+
if (seenPattern) suffix += literal;
|
|
1703
|
+
else prefix += literal;
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
if (ch === "_" || ch === "*") {
|
|
1707
|
+
i++;
|
|
1708
|
+
continue;
|
|
1709
|
+
}
|
|
1710
|
+
if (ch === "#" || ch === "0" || ch === "." || ch === ",") {
|
|
1711
|
+
pattern += ch;
|
|
1712
|
+
seenPattern = true;
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
if (ch === "%") {
|
|
1716
|
+
percentScale *= 100;
|
|
1717
|
+
if (seenPattern) suffix += ch;
|
|
1718
|
+
else prefix += ch;
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
if (seenPattern) suffix += ch;
|
|
1722
|
+
else prefix += ch;
|
|
1723
|
+
}
|
|
1724
|
+
const dot = pattern.indexOf(".");
|
|
1725
|
+
const intPart = dot < 0 ? pattern : pattern.slice(0, dot);
|
|
1726
|
+
const fracPart = dot < 0 ? "" : pattern.slice(dot + 1);
|
|
1727
|
+
return {
|
|
1728
|
+
literalOnly: !seenPattern,
|
|
1729
|
+
prefix,
|
|
1730
|
+
suffix,
|
|
1731
|
+
minIntDigits: (intPart.match(/0/g) ?? []).length,
|
|
1732
|
+
decimals: (fracPart.match(/[#0]/g) ?? []).length,
|
|
1733
|
+
useGrouping: intPart.includes(","),
|
|
1734
|
+
percentScale
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
function renderNumber(value, p) {
|
|
1738
|
+
if (p.literalOnly) return p.prefix + p.suffix;
|
|
1739
|
+
const scaled = value * p.percentScale;
|
|
1740
|
+
const fixed = scaled.toFixed(p.decimals);
|
|
1741
|
+
let [intDigits = "0", fracDigits = ""] = fixed.split(".");
|
|
1742
|
+
if (intDigits.length < p.minIntDigits) {
|
|
1743
|
+
intDigits = intDigits.padStart(p.minIntDigits, "0");
|
|
1744
|
+
}
|
|
1745
|
+
if (p.useGrouping) {
|
|
1746
|
+
intDigits = intDigits.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
1747
|
+
}
|
|
1748
|
+
return p.prefix + intDigits + (fracDigits ? `.${fracDigits}` : "") + p.suffix;
|
|
1749
|
+
}
|
|
1750
|
+
function formatNumberWithCode(value, formatCode) {
|
|
1751
|
+
if (!Number.isFinite(value)) return null;
|
|
1752
|
+
if (!formatCode || formatCode === "General") return null;
|
|
1753
|
+
if (formatCode === "@") return String(value);
|
|
1754
|
+
const sections = splitSections(formatCode);
|
|
1755
|
+
const negative = sections[1];
|
|
1756
|
+
const zero = sections[2];
|
|
1757
|
+
let section;
|
|
1758
|
+
let magnitude = value;
|
|
1759
|
+
if (value === 0 && zero !== void 0) {
|
|
1760
|
+
section = zero;
|
|
1761
|
+
} else if (value < 0 && negative !== void 0) {
|
|
1762
|
+
section = negative;
|
|
1763
|
+
magnitude = Math.abs(value);
|
|
1764
|
+
} else {
|
|
1765
|
+
section = sections[0];
|
|
1766
|
+
}
|
|
1767
|
+
if (section === void 0 || isUnsupported(section)) return null;
|
|
1768
|
+
return renderNumber(magnitude, parseSection(section));
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1553
1771
|
// src/model/cell-reference.ts
|
|
1554
1772
|
function parseCellRef(cellReference) {
|
|
1555
1773
|
if (!cellReference) {
|
|
@@ -1749,6 +1967,9 @@ var Cell = class {
|
|
|
1749
1967
|
}
|
|
1750
1968
|
/**
|
|
1751
1969
|
* Returns the display text of the cell value (always a string).
|
|
1970
|
+
*
|
|
1971
|
+
* Numbers are returned unformatted; use {@link getFormattedText} to apply the
|
|
1972
|
+
* cell's number format.
|
|
1752
1973
|
*/
|
|
1753
1974
|
getText() {
|
|
1754
1975
|
const val = this.getValue();
|
|
@@ -1756,6 +1977,21 @@ var Cell = class {
|
|
|
1756
1977
|
if (val instanceof Date) return val.toLocaleDateString();
|
|
1757
1978
|
return String(val);
|
|
1758
1979
|
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Returns the display text with the cell's number format applied, e.g. a
|
|
1982
|
+
* value of 4.5 under `$#,##0.00` renders as `$4.50`.
|
|
1983
|
+
*
|
|
1984
|
+
* Falls back to {@link getText} for non-numeric values and for format codes
|
|
1985
|
+
* outside the supported subset (fractions, scientific notation, conditional
|
|
1986
|
+
* sections) — so the result is never a misleading rendering.
|
|
1987
|
+
*/
|
|
1988
|
+
getFormattedText() {
|
|
1989
|
+
const val = this.getValue();
|
|
1990
|
+
if (typeof val !== "number") return this.getText();
|
|
1991
|
+
const format = __privateGet(this, _styles).getCellStyle(__privateGet(this, _data).styleIndex).numberFormat;
|
|
1992
|
+
if (!format) return this.getText();
|
|
1993
|
+
return formatNumberWithCode(val, format.formatCode) ?? this.getText();
|
|
1994
|
+
}
|
|
1759
1995
|
// ── Formula API ────────────────────────────────────────────────────────────
|
|
1760
1996
|
/**
|
|
1761
1997
|
* Sets a formula. The leading '=' is optional.
|
|
@@ -1898,9 +2134,9 @@ var Cell = class {
|
|
|
1898
2134
|
toXml() {
|
|
1899
2135
|
const ref = __privateGet(this, _data).reference;
|
|
1900
2136
|
const style = __privateGet(this, _data).styleIndex > 0 ? ` s="${__privateGet(this, _data).styleIndex}"` : "";
|
|
1901
|
-
const type = __privateGet(this, _data).dataType ? ` t="${__privateGet(this, _data).dataType}"` : "";
|
|
1902
2137
|
const formulaXml = __privateGet(this, _data).formula ? __privateGet(this, _data).arrayRef ? `<f t="array" ref="${__privateGet(this, _data).arrayRef}" aca="1" ca="1">${escXml(__privateGet(this, _data).formula)}</f>` : `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
|
|
1903
2138
|
const valueXml = __privateGet(this, _data).rawValue !== void 0 && __privateGet(this, _data).rawValue !== "" ? `<v>${escXml(__privateGet(this, _data).rawValue)}</v>` : "";
|
|
2139
|
+
const type = __privateGet(this, _data).dataType && (formulaXml || valueXml) ? ` t="${__privateGet(this, _data).dataType}"` : "";
|
|
1904
2140
|
if (!formulaXml && !valueXml && __privateGet(this, _data).styleIndex === 0) return "";
|
|
1905
2141
|
return `<c r="${ref}"${style}${type}>${formulaXml}${valueXml}</c>`;
|
|
1906
2142
|
}
|
|
@@ -2084,7 +2320,7 @@ forEach_fn = function(action) {
|
|
|
2084
2320
|
};
|
|
2085
2321
|
|
|
2086
2322
|
// src/model/worksheet.ts
|
|
2087
|
-
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _Worksheet_instances, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, getOrCreateRow_fn;
|
|
2323
|
+
var _sharedStrings2, _styles2, _name, _rows, _cols, _mergeCells, _pane, _autoFilter, _dataValidations, _visibility, _drawings, _preservedTail, _preservedRels, _Worksheet_instances, activeTail_fn, buildColsXml_fn, buildSheetDataXml_fn, buildMergeCellsXml_fn, buildDataValidationsXml_fn, buildSheetViewXml_fn, getOrCreateRow_fn;
|
|
2088
2324
|
var Worksheet = class {
|
|
2089
2325
|
/** @internal */
|
|
2090
2326
|
constructor(name, sharedStrings, styles) {
|
|
@@ -2100,6 +2336,12 @@ var Worksheet = class {
|
|
|
2100
2336
|
__privateAdd(this, _dataValidations, []);
|
|
2101
2337
|
__privateAdd(this, _visibility, "visible");
|
|
2102
2338
|
__privateAdd(this, _drawings, []);
|
|
2339
|
+
// Round-trip state captured when this sheet was loaded from a file: the
|
|
2340
|
+
// relationship-bearing leaf elements of the worksheet part, and the sheet's
|
|
2341
|
+
// original relationships. Re-emitted on save so drawings, images and legacy
|
|
2342
|
+
// comment anchors survive open→save. See io/preserved-parts.ts.
|
|
2343
|
+
__privateAdd(this, _preservedTail, []);
|
|
2344
|
+
__privateAdd(this, _preservedRels, []);
|
|
2103
2345
|
__privateSet(this, _name, name);
|
|
2104
2346
|
__privateSet(this, _sharedStrings2, sharedStrings);
|
|
2105
2347
|
__privateSet(this, _styles2, styles);
|
|
@@ -2179,6 +2421,16 @@ var Worksheet = class {
|
|
|
2179
2421
|
__privateGet(this, _cols).push({ min: columnIndex, max: columnIndex, width, customWidth: true });
|
|
2180
2422
|
__privateGet(this, _cols).sort((a, b) => a.min - b.min);
|
|
2181
2423
|
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Returns the explicit width of a column (1-based index), or `undefined` when
|
|
2426
|
+
* the column uses the sheet default. Counterpart to {@link setColumnWidth};
|
|
2427
|
+
* widths parsed from an existing file are visible here too, so column layout
|
|
2428
|
+
* can be round-tripped.
|
|
2429
|
+
*/
|
|
2430
|
+
getColumnWidth(columnIndex) {
|
|
2431
|
+
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
2432
|
+
return __privateGet(this, _cols).find((c) => c.min <= columnIndex && c.max >= columnIndex)?.width;
|
|
2433
|
+
}
|
|
2182
2434
|
/** Auto-fits column width based on content (approximate). */
|
|
2183
2435
|
autoFitColumn(columnIndex) {
|
|
2184
2436
|
if (columnIndex < 1) throw new RangeError("Column index must be >= 1.");
|
|
@@ -2202,6 +2454,14 @@ var Worksheet = class {
|
|
|
2202
2454
|
row.height = height;
|
|
2203
2455
|
row.customHeight = true;
|
|
2204
2456
|
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Returns the explicit height of a row (1-based index), or `undefined` when
|
|
2459
|
+
* the row uses the sheet default. Counterpart to {@link setRowHeight}.
|
|
2460
|
+
*/
|
|
2461
|
+
getRowHeight(rowIndex) {
|
|
2462
|
+
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
2463
|
+
return __privateGet(this, _rows).get(rowIndex)?.height;
|
|
2464
|
+
}
|
|
2205
2465
|
/** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
|
|
2206
2466
|
setRowHidden(rowIndex, hidden = true) {
|
|
2207
2467
|
if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
|
|
@@ -2220,6 +2480,14 @@ var Worksheet = class {
|
|
|
2220
2480
|
unmergeCells(rangeReference) {
|
|
2221
2481
|
__privateSet(this, _mergeCells, __privateGet(this, _mergeCells).filter((m) => m.ref !== rangeReference));
|
|
2222
2482
|
}
|
|
2483
|
+
/**
|
|
2484
|
+
* All merged ranges on this sheet, in the order they were added (or parsed).
|
|
2485
|
+
* Includes merges read from an existing file, so callers never need to track
|
|
2486
|
+
* their own copy of the list.
|
|
2487
|
+
*/
|
|
2488
|
+
get merges() {
|
|
2489
|
+
return __privateGet(this, _mergeCells).map((m) => m.ref);
|
|
2490
|
+
}
|
|
2223
2491
|
/**
|
|
2224
2492
|
* Freezes rows and/or columns.
|
|
2225
2493
|
* @param row Number of rows to freeze (0 = none)
|
|
@@ -2258,6 +2526,18 @@ var Worksheet = class {
|
|
|
2258
2526
|
addDrawingProvider(provider) {
|
|
2259
2527
|
__privateGet(this, _drawings).push(provider);
|
|
2260
2528
|
}
|
|
2529
|
+
/**
|
|
2530
|
+
* Detaches every drawing from this worksheet — both providers added in memory
|
|
2531
|
+
* and any drawing preserved from the file the workbook was opened from.
|
|
2532
|
+
*
|
|
2533
|
+
* Call before re-adding charts when exporting the same in-memory workbook
|
|
2534
|
+
* repeatedly, otherwise each export appends another copy; call on its own to
|
|
2535
|
+
* deliberately drop a chart that came from the source file.
|
|
2536
|
+
*/
|
|
2537
|
+
clearDrawings() {
|
|
2538
|
+
__privateSet(this, _drawings, []);
|
|
2539
|
+
__privateSet(this, _preservedTail, __privateGet(this, _preservedTail).filter((t) => t.tag !== "drawing"));
|
|
2540
|
+
}
|
|
2261
2541
|
/** True when this worksheet has at least one drawing to serialise. */
|
|
2262
2542
|
get hasDrawings() {
|
|
2263
2543
|
return __privateGet(this, _drawings).length > 0;
|
|
@@ -2266,6 +2546,44 @@ var Worksheet = class {
|
|
|
2266
2546
|
get drawingProviders() {
|
|
2267
2547
|
return __privateGet(this, _drawings);
|
|
2268
2548
|
}
|
|
2549
|
+
// ── Round-trip preservation (internal) ───────────────────────────────────────
|
|
2550
|
+
/**
|
|
2551
|
+
* @internal Relationships carried over from the source file, minus any whose
|
|
2552
|
+
* leaf element is no longer emitted (a drawing replaced by live providers, or
|
|
2553
|
+
* cleared outright). The save pipeline writes these into the sheet's rels.
|
|
2554
|
+
*/
|
|
2555
|
+
get preservedRelationships() {
|
|
2556
|
+
const liveIds = new Set(__privateMethod(this, _Worksheet_instances, activeTail_fn).call(this).map((t) => t.relId));
|
|
2557
|
+
return __privateGet(this, _preservedRels).filter((r) => liveIds.has(r.id));
|
|
2558
|
+
}
|
|
2559
|
+
/**
|
|
2560
|
+
* @internal Package paths of relationships that were preserved at load time
|
|
2561
|
+
* but are no longer referenced — a drawing replaced by live providers, or one
|
|
2562
|
+
* cleared outright. The save pipeline prunes these parts so the output has no
|
|
2563
|
+
* orphans.
|
|
2564
|
+
*/
|
|
2565
|
+
get droppedPreservedTargets() {
|
|
2566
|
+
const liveIds = new Set(__privateMethod(this, _Worksheet_instances, activeTail_fn).call(this).map((t) => t.relId));
|
|
2567
|
+
return __privateGet(this, _preservedRels).filter((r) => !liveIds.has(r.id) && r.resolved !== void 0).map((r) => r.resolved);
|
|
2568
|
+
}
|
|
2569
|
+
/**
|
|
2570
|
+
* @internal Relationship id for a drawing built from live providers. Chosen to
|
|
2571
|
+
* clear every preserved id so the two sets cannot collide.
|
|
2572
|
+
*/
|
|
2573
|
+
get drawingRelId() {
|
|
2574
|
+
let max = 0;
|
|
2575
|
+
for (const r of __privateGet(this, _preservedRels)) {
|
|
2576
|
+
const n = /^rId(\d+)$/.exec(r.id);
|
|
2577
|
+
if (n) max = Math.max(max, parseInt(n[1], 10));
|
|
2578
|
+
}
|
|
2579
|
+
return `rId${max + 1}`;
|
|
2580
|
+
}
|
|
2581
|
+
/** @internal Captures round-trip state from the source package. */
|
|
2582
|
+
loadPreservedState(worksheetXml, relationships) {
|
|
2583
|
+
__privateSet(this, _preservedRels, [...relationships]);
|
|
2584
|
+
const known = new Set(relationships.map((r) => r.id));
|
|
2585
|
+
__privateSet(this, _preservedTail, parseTailElements(worksheetXml).filter((t) => known.has(t.relId)));
|
|
2586
|
+
}
|
|
2269
2587
|
// ── XML serialisation ──────────────────────────────────────────────────────
|
|
2270
2588
|
/** Builds the xl/worksheets/sheetN.xml string. */
|
|
2271
2589
|
buildXml() {
|
|
@@ -2275,12 +2593,13 @@ var Worksheet = class {
|
|
|
2275
2593
|
const filterXml = __privateGet(this, _autoFilter) ? `<autoFilter ref="${__privateGet(this, _autoFilter).ref}"/>` : "";
|
|
2276
2594
|
const validXml = __privateMethod(this, _Worksheet_instances, buildDataValidationsXml_fn).call(this);
|
|
2277
2595
|
const sheetViewXml = __privateMethod(this, _Worksheet_instances, buildSheetViewXml_fn).call(this);
|
|
2278
|
-
const
|
|
2596
|
+
const preservedTailXml = __privateMethod(this, _Worksheet_instances, activeTail_fn).call(this).map((t) => t.xml).join("");
|
|
2597
|
+
const drawingXml = this.hasDrawings ? `<drawing r:id="${this.drawingRelId}"/>` : "";
|
|
2279
2598
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2280
2599
|
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
2281
2600
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
2282
2601
|
<sheetViews>${sheetViewXml}</sheetViews>
|
|
2283
|
-
${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${drawingXml}
|
|
2602
|
+
${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${preservedTailXml}${drawingXml}
|
|
2284
2603
|
</worksheet>`;
|
|
2285
2604
|
}
|
|
2286
2605
|
/** Parses xl/worksheets/sheetN.xml and loads cells into memory. */
|
|
@@ -2361,7 +2680,17 @@ _autoFilter = new WeakMap();
|
|
|
2361
2680
|
_dataValidations = new WeakMap();
|
|
2362
2681
|
_visibility = new WeakMap();
|
|
2363
2682
|
_drawings = new WeakMap();
|
|
2683
|
+
_preservedTail = new WeakMap();
|
|
2684
|
+
_preservedRels = new WeakMap();
|
|
2364
2685
|
_Worksheet_instances = new WeakSet();
|
|
2686
|
+
/**
|
|
2687
|
+
* Preserved leaf elements that will actually be emitted. A sheet may only have
|
|
2688
|
+
* one `<drawing>`, so live providers suppress a preserved one — every consumer
|
|
2689
|
+
* (serialisation, relationships, pruning) must agree on that, hence one helper.
|
|
2690
|
+
*/
|
|
2691
|
+
activeTail_fn = function() {
|
|
2692
|
+
return __privateGet(this, _preservedTail).filter((t) => !(t.tag === "drawing" && this.hasDrawings));
|
|
2693
|
+
};
|
|
2365
2694
|
// ── Private XML builders ───────────────────────────────────────────────────
|
|
2366
2695
|
buildColsXml_fn = function() {
|
|
2367
2696
|
if (__privateGet(this, _cols).length === 0) return "";
|
|
@@ -2486,7 +2815,7 @@ indexOf_fn = function(name, scope) {
|
|
|
2486
2815
|
};
|
|
2487
2816
|
|
|
2488
2817
|
// src/model/workbook.ts
|
|
2489
|
-
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _Workbook_instances, assertOpen_fn, buildZipEntries_fn;
|
|
2818
|
+
var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _preservedParts, _preservedContentTypes, _orphanedTargets, _Workbook_instances, assertOpen_fn, seedPreservedParts_fn, preservedContentTypeDeclarations_fn, buildZipEntries_fn;
|
|
2490
2819
|
var _Workbook = class _Workbook {
|
|
2491
2820
|
constructor(sharedStrings, styles) {
|
|
2492
2821
|
__privateAdd(this, _Workbook_instances);
|
|
@@ -2495,6 +2824,11 @@ var _Workbook = class _Workbook {
|
|
|
2495
2824
|
__privateAdd(this, _sheets, []);
|
|
2496
2825
|
__privateAdd(this, _definedNames, new DefinedNameManager());
|
|
2497
2826
|
__privateAdd(this, _closed, false);
|
|
2827
|
+
/** Parts carried over verbatim from the file this workbook was opened from. */
|
|
2828
|
+
__privateAdd(this, _preservedParts, /* @__PURE__ */ new Map());
|
|
2829
|
+
__privateAdd(this, _preservedContentTypes);
|
|
2830
|
+
/** Preserved targets whose referring worksheet has been removed. */
|
|
2831
|
+
__privateAdd(this, _orphanedTargets, []);
|
|
2498
2832
|
__privateSet(this, _sharedStrings3, sharedStrings);
|
|
2499
2833
|
__privateSet(this, _styles3, styles);
|
|
2500
2834
|
}
|
|
@@ -2535,8 +2869,19 @@ var _Workbook = class _Workbook {
|
|
|
2535
2869
|
const ws = new Worksheet(name, ss, sm);
|
|
2536
2870
|
if (state) ws.setVisibility(state);
|
|
2537
2871
|
ws.loadFromXml(wsXml);
|
|
2872
|
+
if (wsPath) {
|
|
2873
|
+
const dir = wsPath.slice(0, wsPath.lastIndexOf("/"));
|
|
2874
|
+
const file = wsPath.slice(wsPath.lastIndexOf("/") + 1);
|
|
2875
|
+
const wsRelsXml = readXmlEntry(entries, `${dir}/_rels/${file}.rels`);
|
|
2876
|
+
if (wsRelsXml) {
|
|
2877
|
+
ws.loadPreservedState(wsXml, parseRelationships(wsRelsXml, wsPath));
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2538
2880
|
__privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
2539
2881
|
}
|
|
2882
|
+
__privateSet(wb, _preservedParts, collectPreservedParts(entries));
|
|
2883
|
+
const ctXml = readXmlEntry(entries, "[Content_Types].xml");
|
|
2884
|
+
if (ctXml) __privateSet(wb, _preservedContentTypes, parseContentTypes(ctXml));
|
|
2540
2885
|
for (const dn of getElementsByTagName(wbRoot, "definedName")) {
|
|
2541
2886
|
const name = getAttr(dn, "name");
|
|
2542
2887
|
if (!name) continue;
|
|
@@ -2586,6 +2931,30 @@ var _Workbook = class _Workbook {
|
|
|
2586
2931
|
__privateGet(this, _sheets).push({ worksheet: ws, sheetId, relId });
|
|
2587
2932
|
return ws;
|
|
2588
2933
|
}
|
|
2934
|
+
/**
|
|
2935
|
+
* Removes the worksheet with the given name, along with any defined names
|
|
2936
|
+
* scoped to it. Throws if the sheet does not exist or is the last remaining
|
|
2937
|
+
* one — a workbook must always keep at least one sheet.
|
|
2938
|
+
*
|
|
2939
|
+
* Sheet ids and relationship ids are re-derived from document order on save,
|
|
2940
|
+
* so the remaining sheets stay consistent after a removal.
|
|
2941
|
+
*/
|
|
2942
|
+
removeWorksheet(name) {
|
|
2943
|
+
__privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
|
|
2944
|
+
const idx = __privateGet(this, _sheets).findIndex((e) => e.worksheet.name === name);
|
|
2945
|
+
if (idx < 0) throw new Error(`Worksheet "${name}" not found.`);
|
|
2946
|
+
if (__privateGet(this, _sheets).length === 1) {
|
|
2947
|
+
throw new Error("Cannot remove the last worksheet; a workbook must have at least one.");
|
|
2948
|
+
}
|
|
2949
|
+
const removed = __privateGet(this, _sheets)[idx].worksheet;
|
|
2950
|
+
for (const rel of removed.preservedRelationships) {
|
|
2951
|
+
if (rel.resolved) __privateGet(this, _orphanedTargets).push(rel.resolved);
|
|
2952
|
+
}
|
|
2953
|
+
__privateGet(this, _sheets).splice(idx, 1);
|
|
2954
|
+
for (const dn of [...__privateGet(this, _definedNames).all]) {
|
|
2955
|
+
if (dn.scope === name) __privateGet(this, _definedNames).remove(dn.name, dn.scope);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2589
2958
|
/**
|
|
2590
2959
|
* Returns a worksheet by name. Throws if not found.
|
|
2591
2960
|
*/
|
|
@@ -2670,19 +3039,72 @@ _styles3 = new WeakMap();
|
|
|
2670
3039
|
_sheets = new WeakMap();
|
|
2671
3040
|
_definedNames = new WeakMap();
|
|
2672
3041
|
_closed = new WeakMap();
|
|
3042
|
+
_preservedParts = new WeakMap();
|
|
3043
|
+
_preservedContentTypes = new WeakMap();
|
|
3044
|
+
_orphanedTargets = new WeakMap();
|
|
2673
3045
|
_Workbook_instances = new WeakSet();
|
|
2674
3046
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
2675
3047
|
assertOpen_fn = function() {
|
|
2676
3048
|
if (__privateGet(this, _closed)) throw new Error("Workbook has been closed.");
|
|
2677
3049
|
};
|
|
3050
|
+
/**
|
|
3051
|
+
* Copies preserved source parts into the output, minus those that are now
|
|
3052
|
+
* orphaned. A part is dropped when the only relationship pointing at it has
|
|
3053
|
+
* gone (a drawing replaced by live providers, a sheet removed, an explicit
|
|
3054
|
+
* `clearDrawings()`) and nothing else still reaches it.
|
|
3055
|
+
*
|
|
3056
|
+
* @returns the set of paths written, so later steps can avoid colliding with
|
|
3057
|
+
* them when naming newly generated parts.
|
|
3058
|
+
*/
|
|
3059
|
+
seedPreservedParts_fn = function(entries) {
|
|
3060
|
+
if (__privateGet(this, _preservedParts).size === 0) return /* @__PURE__ */ new Set();
|
|
3061
|
+
const retained = /* @__PURE__ */ new Set();
|
|
3062
|
+
for (const { worksheet } of __privateGet(this, _sheets)) {
|
|
3063
|
+
for (const rel of worksheet.preservedRelationships) {
|
|
3064
|
+
if (rel.resolved) {
|
|
3065
|
+
for (const p of collectSubtree(__privateGet(this, _preservedParts), rel.resolved)) retained.add(p);
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
const candidates = [
|
|
3070
|
+
...__privateGet(this, _sheets).flatMap((e) => [...e.worksheet.droppedPreservedTargets]),
|
|
3071
|
+
...__privateGet(this, _orphanedTargets)
|
|
3072
|
+
];
|
|
3073
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
3074
|
+
for (const target of candidates) {
|
|
3075
|
+
for (const p of collectSubtree(__privateGet(this, _preservedParts), target)) {
|
|
3076
|
+
if (!retained.has(p)) dropped.add(p);
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
const written = /* @__PURE__ */ new Set();
|
|
3080
|
+
for (const [path, bytes] of __privateGet(this, _preservedParts)) {
|
|
3081
|
+
if (dropped.has(path)) continue;
|
|
3082
|
+
entries.set(path, bytes);
|
|
3083
|
+
written.add(path);
|
|
3084
|
+
}
|
|
3085
|
+
return written;
|
|
3086
|
+
};
|
|
3087
|
+
/**
|
|
3088
|
+
* Content-type declarations for preserved parts. `<Default>` entries are kept
|
|
3089
|
+
* wholesale — they are extension-scoped and are what types binary parts such
|
|
3090
|
+
* as images — while `<Override>` entries are filtered to parts that survived.
|
|
3091
|
+
*/
|
|
3092
|
+
preservedContentTypeDeclarations_fn = function(preservedPaths) {
|
|
3093
|
+
const ct = __privateGet(this, _preservedContentTypes);
|
|
3094
|
+
if (!ct) return { defaults: [], overrides: [] };
|
|
3095
|
+
const defaults = [...ct.defaults].filter(([ext]) => ext !== "rels" && ext !== "xml").map(([extension, contentType]) => ({ extension, contentType }));
|
|
3096
|
+
const overrides = [...ct.overrides].filter(([partName]) => preservedPaths.has(partName.replace(/^\//, ""))).map(([partName, contentType]) => ({ partName, contentType }));
|
|
3097
|
+
return { defaults, overrides };
|
|
3098
|
+
};
|
|
2678
3099
|
buildZipEntries_fn = function() {
|
|
2679
3100
|
const entries = /* @__PURE__ */ new Map();
|
|
2680
3101
|
const sheetCount = __privateGet(this, _sheets).length;
|
|
3102
|
+
const preservedPaths = __privateMethod(this, _Workbook_instances, seedPreservedParts_fn).call(this, entries);
|
|
2681
3103
|
entries.set("_rels/.rels", ROOT_RELS);
|
|
2682
|
-
const sheetMetas = __privateGet(this, _sheets).map((e) => ({
|
|
3104
|
+
const sheetMetas = __privateGet(this, _sheets).map((e, i) => ({
|
|
2683
3105
|
name: e.worksheet.name,
|
|
2684
|
-
sheetId:
|
|
2685
|
-
relationshipId:
|
|
3106
|
+
sheetId: i + 1,
|
|
3107
|
+
relationshipId: `rId${i + 1}`,
|
|
2686
3108
|
state: e.worksheet.visibility !== "visible" ? e.worksheet.visibility : void 0
|
|
2687
3109
|
}));
|
|
2688
3110
|
const definedNameMetas = __privateGet(this, _definedNames).all.map((dn) => {
|
|
@@ -2705,34 +3127,48 @@ buildZipEntries_fn = function() {
|
|
|
2705
3127
|
let partCounter = 0;
|
|
2706
3128
|
for (let i = 0; i < __privateGet(this, _sheets).length; i++) {
|
|
2707
3129
|
const ws = __privateGet(this, _sheets)[i].worksheet;
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
const drawingPath = `xl/drawings/drawing${drawingCounter}.xml`;
|
|
2711
|
-
const anchors = [];
|
|
2712
|
-
const drawingRels = [];
|
|
2713
|
-
ws.drawingProviders.forEach((provider, idx) => {
|
|
2714
|
-
const relId = `rId${idx + 1}`;
|
|
2715
|
-
anchors.push(provider.buildAnchorXml(relId));
|
|
2716
|
-
partCounter++;
|
|
2717
|
-
const part = provider.buildPart(partCounter);
|
|
2718
|
-
entries.set(part.path, part.content);
|
|
2719
|
-
extraOverrides.push({ partName: `/${part.path}`, contentType: part.contentType });
|
|
2720
|
-
const target = `../${part.path.slice("xl/".length)}`;
|
|
2721
|
-
drawingRels.push(
|
|
2722
|
-
` <Relationship Id="${relId}" Type="${part.relType}" Target="${target}"/>`
|
|
2723
|
-
);
|
|
2724
|
-
});
|
|
2725
|
-
entries.set(drawingPath, buildDrawingXml(anchors.join("")));
|
|
2726
|
-
entries.set(`xl/drawings/_rels/drawing${drawingCounter}.xml.rels`, buildRelsXml(drawingRels));
|
|
2727
|
-
extraOverrides.push({ partName: `/${drawingPath}`, contentType: CONTENT_TYPES.drawing });
|
|
2728
|
-
entries.set(
|
|
2729
|
-
`xl/worksheets/_rels/sheet${i + 1}.xml.rels`,
|
|
2730
|
-
buildRelsXml([
|
|
2731
|
-
` <Relationship Id="${WORKSHEET_DRAWING_REL_ID}" Type="${REL_TYPES.drawing}" Target="../drawings/drawing${drawingCounter}.xml"/>`
|
|
2732
|
-
])
|
|
3130
|
+
const sheetRels = ws.preservedRelationships.map(
|
|
3131
|
+
(r) => ` <Relationship Id="${r.id}" Type="${r.type}" Target="${r.target}"${r.external ? ' TargetMode="External"' : ""}/>`
|
|
2733
3132
|
);
|
|
3133
|
+
if (ws.hasDrawings) {
|
|
3134
|
+
do {
|
|
3135
|
+
drawingCounter++;
|
|
3136
|
+
} while (preservedPaths.has(`xl/drawings/drawing${drawingCounter}.xml`));
|
|
3137
|
+
const drawingPath = `xl/drawings/drawing${drawingCounter}.xml`;
|
|
3138
|
+
const anchors = [];
|
|
3139
|
+
const drawingRels = [];
|
|
3140
|
+
ws.drawingProviders.forEach((provider, idx) => {
|
|
3141
|
+
const relId = `rId${idx + 1}`;
|
|
3142
|
+
anchors.push(provider.buildAnchorXml(relId));
|
|
3143
|
+
let part = provider.buildPart(++partCounter);
|
|
3144
|
+
while (preservedPaths.has(part.path)) part = provider.buildPart(++partCounter);
|
|
3145
|
+
entries.set(part.path, part.content);
|
|
3146
|
+
extraOverrides.push({ partName: `/${part.path}`, contentType: part.contentType });
|
|
3147
|
+
const target = `../${part.path.slice("xl/".length)}`;
|
|
3148
|
+
drawingRels.push(
|
|
3149
|
+
` <Relationship Id="${relId}" Type="${part.relType}" Target="${target}"/>`
|
|
3150
|
+
);
|
|
3151
|
+
});
|
|
3152
|
+
entries.set(drawingPath, buildDrawingXml(anchors.join("")));
|
|
3153
|
+
entries.set(`xl/drawings/_rels/drawing${drawingCounter}.xml.rels`, buildRelsXml(drawingRels));
|
|
3154
|
+
extraOverrides.push({ partName: `/${drawingPath}`, contentType: CONTENT_TYPES.drawing });
|
|
3155
|
+
sheetRels.push(
|
|
3156
|
+
` <Relationship Id="${ws.drawingRelId}" Type="${REL_TYPES.drawing}" Target="../drawings/drawing${drawingCounter}.xml"/>`
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
if (sheetRels.length > 0) {
|
|
3160
|
+
entries.set(`xl/worksheets/_rels/sheet${i + 1}.xml.rels`, buildRelsXml(sheetRels));
|
|
3161
|
+
}
|
|
2734
3162
|
}
|
|
2735
|
-
|
|
3163
|
+
const preservedCt = __privateMethod(this, _Workbook_instances, preservedContentTypeDeclarations_fn).call(this, preservedPaths);
|
|
3164
|
+
entries.set(
|
|
3165
|
+
"[Content_Types].xml",
|
|
3166
|
+
buildContentTypes(
|
|
3167
|
+
sheetCount,
|
|
3168
|
+
[...preservedCt.overrides, ...extraOverrides],
|
|
3169
|
+
preservedCt.defaults
|
|
3170
|
+
)
|
|
3171
|
+
);
|
|
2736
3172
|
return entries;
|
|
2737
3173
|
};
|
|
2738
3174
|
var Workbook = _Workbook;
|
|
@@ -2785,6 +3221,7 @@ exports.columnNumber = columnNumber;
|
|
|
2785
3221
|
exports.decodeUtf8 = decodeUtf8;
|
|
2786
3222
|
exports.encodeUtf8 = encodeUtf8;
|
|
2787
3223
|
exports.escapeXml = escapeXml;
|
|
3224
|
+
exports.formatNumberWithCode = formatNumberWithCode;
|
|
2788
3225
|
exports.fromOADate = fromOADate;
|
|
2789
3226
|
exports.getAttr = getAttr;
|
|
2790
3227
|
exports.getElementsByTagName = getElementsByTagName;
|