@rowsncolumns/utils 9.5.1 → 9.5.4
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/cjs/clipboard-html-sanitize.d.ts +15 -0
- package/dist/cjs/clipboard-html-sanitize.d.ts.map +1 -0
- package/dist/cjs/clipboard-html-sanitize.js +237 -0
- package/dist/cjs/condition-operand.d.ts +54 -0
- package/dist/cjs/condition-operand.d.ts.map +1 -0
- package/dist/cjs/condition-operand.js +223 -0
- package/dist/cjs/conditional-format.d.ts.map +1 -1
- package/dist/cjs/conditional-format.js +5 -59
- package/dist/cjs/data-validation.d.ts +9 -0
- package/dist/cjs/data-validation.d.ts.map +1 -1
- package/dist/cjs/data-validation.js +29 -60
- package/dist/cjs/date.d.ts +16 -0
- package/dist/cjs/date.d.ts.map +1 -1
- package/dist/cjs/date.js +33 -4
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/rich-text-html.d.ts +9 -0
- package/dist/cjs/rich-text-html.d.ts.map +1 -1
- package/dist/cjs/rich-text-html.js +52 -23
- package/dist/cjs/sheet-names.d.ts +60 -0
- package/dist/cjs/sheet-names.d.ts.map +1 -0
- package/dist/cjs/sheet-names.js +157 -0
- package/dist/cjs/sheet-protection.d.ts +9 -0
- package/dist/cjs/sheet-protection.d.ts.map +1 -0
- package/dist/cjs/sheet-protection.js +11 -0
- package/dist/cjs/string.d.ts +31 -0
- package/dist/cjs/string.d.ts.map +1 -1
- package/dist/cjs/string.js +73 -3
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/clipboard-html-sanitize.d.ts +15 -0
- package/dist/esm/clipboard-html-sanitize.d.ts.map +1 -0
- package/dist/esm/clipboard-html-sanitize.js +192 -0
- package/dist/esm/condition-operand.d.ts +54 -0
- package/dist/esm/condition-operand.d.ts.map +1 -0
- package/dist/esm/condition-operand.js +203 -0
- package/dist/esm/conditional-format.d.ts.map +1 -1
- package/dist/esm/conditional-format.js +5 -57
- package/dist/esm/data-validation.d.ts +9 -0
- package/dist/esm/data-validation.d.ts.map +1 -1
- package/dist/esm/data-validation.js +27 -57
- package/dist/esm/date.d.ts +16 -0
- package/dist/esm/date.d.ts.map +1 -1
- package/dist/esm/date.js +22 -1
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/rich-text-html.d.ts +9 -0
- package/dist/esm/rich-text-html.d.ts.map +1 -1
- package/dist/esm/rich-text-html.js +20 -4
- package/dist/esm/sheet-names.d.ts +60 -0
- package/dist/esm/sheet-names.d.ts.map +1 -0
- package/dist/esm/sheet-names.js +109 -0
- package/dist/esm/sheet-protection.d.ts +9 -0
- package/dist/esm/sheet-protection.d.ts.map +1 -0
- package/dist/esm/sheet-protection.js +8 -0
- package/dist/esm/string.d.ts +31 -0
- package/dist/esm/string.d.ts.map +1 -1
- package/dist/esm/string.js +62 -2
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Excel compares worksheet names case-insensitively: `SHEET1` beside `Sheet1` is refused with
|
|
4
|
+
* "That name is already taken. Try a different one." The calc engine's sheet registry folds names
|
|
5
|
+
* the same way, so two sheets differing only by case would alias one engine sheet — every write to
|
|
6
|
+
* the second lands in the first and `=SHEET1!A1` resolves to whichever was registered first
|
|
7
|
+
* (Athena-Intel/demo-app-monorepo#37167 UI2-1). Both engines' rename / create / duplicate paths and
|
|
8
|
+
* the sheet tabs compare titles through these helpers (the rnc store mirrors them in Rust and
|
|
9
|
+
* surfaces the same message through its `rejections` channel).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.validateSheetName = exports.SHEET_NAME_INVALID_MESSAGE = exports.SHEET_NAME_RESERVED_WORD = exports.SHEET_NAME_ILLEGAL_CHARACTERS = exports.SHEET_NAME_MAX_LENGTH = exports.uniqueSheetTitle = exports.isSheetTitleTaken = exports.isSameSheetTitle = exports.sheetTitleKey = exports.SHEET_NAME_TAKEN_MESSAGE = void 0;
|
|
13
|
+
var tslib_1 = require("tslib");
|
|
14
|
+
exports.SHEET_NAME_TAKEN_MESSAGE = "That name is already taken. Try a different one.";
|
|
15
|
+
/** The identity two worksheet titles are compared under: trimmed, Unicode-lowercased. */
|
|
16
|
+
var sheetTitleKey = function (title) {
|
|
17
|
+
return title.trim().toLowerCase();
|
|
18
|
+
};
|
|
19
|
+
exports.sheetTitleKey = sheetTitleKey;
|
|
20
|
+
/** Whether `a` and `b` name the same worksheet under Excel's comparison. */
|
|
21
|
+
var isSameSheetTitle = function (a, b) {
|
|
22
|
+
return (0, exports.sheetTitleKey)(a) === (0, exports.sheetTitleKey)(b);
|
|
23
|
+
};
|
|
24
|
+
exports.isSameSheetTitle = isSameSheetTitle;
|
|
25
|
+
/**
|
|
26
|
+
* Whether another sheet (any but `exceptSheetId`) already holds `title`, case-insensitively. A
|
|
27
|
+
* sheet may always re-case its own title (`Sheet1` → `SHEET1` in place is not a collision).
|
|
28
|
+
*/
|
|
29
|
+
var isSheetTitleTaken = function (title, sheets, exceptSheetId) {
|
|
30
|
+
var e_1, _a;
|
|
31
|
+
var key = (0, exports.sheetTitleKey)(title);
|
|
32
|
+
try {
|
|
33
|
+
for (var sheets_1 = tslib_1.__values(sheets), sheets_1_1 = sheets_1.next(); !sheets_1_1.done; sheets_1_1 = sheets_1.next()) {
|
|
34
|
+
var sheet = sheets_1_1.value;
|
|
35
|
+
if (sheet.sheetId !== exceptSheetId && (0, exports.sheetTitleKey)(sheet.title) === key) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
41
|
+
finally {
|
|
42
|
+
try {
|
|
43
|
+
if (sheets_1_1 && !sheets_1_1.done && (_a = sheets_1.return)) _a.call(sheets_1);
|
|
44
|
+
}
|
|
45
|
+
finally { if (e_1) throw e_1.error; }
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
};
|
|
49
|
+
exports.isSheetTitleTaken = isSheetTitleTaken;
|
|
50
|
+
/**
|
|
51
|
+
* `base` if no title in `existing` holds it (case-insensitively), else the first free
|
|
52
|
+
* `base 1`, `base 2`, … — the suffix scheme the JS engine uses for a new / duplicated sheet.
|
|
53
|
+
* Every candidate stays within `maxLength` (the suffix wins over the tail of a long base).
|
|
54
|
+
*/
|
|
55
|
+
var uniqueSheetTitle = function (base, existing, maxLength) {
|
|
56
|
+
var e_2, _a;
|
|
57
|
+
if (maxLength === void 0) { maxLength = Number.POSITIVE_INFINITY; }
|
|
58
|
+
var taken = new Set();
|
|
59
|
+
try {
|
|
60
|
+
for (var existing_1 = tslib_1.__values(existing), existing_1_1 = existing_1.next(); !existing_1_1.done; existing_1_1 = existing_1.next()) {
|
|
61
|
+
var title = existing_1_1.value;
|
|
62
|
+
taken.add((0, exports.sheetTitleKey)(title));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
66
|
+
finally {
|
|
67
|
+
try {
|
|
68
|
+
if (existing_1_1 && !existing_1_1.done && (_a = existing_1.return)) _a.call(existing_1);
|
|
69
|
+
}
|
|
70
|
+
finally { if (e_2) throw e_2.error; }
|
|
71
|
+
}
|
|
72
|
+
var root = base.slice(0, maxLength);
|
|
73
|
+
if (!taken.has((0, exports.sheetTitleKey)(root))) {
|
|
74
|
+
return root;
|
|
75
|
+
}
|
|
76
|
+
for (var counter = 1;; counter++) {
|
|
77
|
+
var suffix = " ".concat(counter);
|
|
78
|
+
var candidate = "".concat(root.slice(0, Math.max(0, maxLength - suffix.length))).concat(suffix);
|
|
79
|
+
if (!taken.has((0, exports.sheetTitleKey)(candidate))) {
|
|
80
|
+
return candidate;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
exports.uniqueSheetTitle = uniqueSheetTitle;
|
|
85
|
+
/** Excel's ceiling for a worksheet name, in UTF-16 code units (what `String#length` counts). */
|
|
86
|
+
exports.SHEET_NAME_MAX_LENGTH = 31;
|
|
87
|
+
/** The characters Excel never allows anywhere in a worksheet name: `\ / ? * [ ] :`. */
|
|
88
|
+
exports.SHEET_NAME_ILLEGAL_CHARACTERS = ["\\", "/", "?", "*", "[", "]", ":"];
|
|
89
|
+
var SHEET_NAME_ILLEGAL_CHARACTER = /[\\/?*[\]:]/;
|
|
90
|
+
/** Excel reserves this title for its change-history sheet (compared case-insensitively). */
|
|
91
|
+
exports.SHEET_NAME_RESERVED_WORD = "History";
|
|
92
|
+
/**
|
|
93
|
+
* Excel 365's rename-refusal dialog, verbatim — one message for every rule except the duplicate
|
|
94
|
+
* title, which has its own ({@link SHEET_NAME_TAKEN_MESSAGE}).
|
|
95
|
+
*/
|
|
96
|
+
exports.SHEET_NAME_INVALID_MESSAGE = [
|
|
97
|
+
"Sheet names cannot:",
|
|
98
|
+
"• Be blank.",
|
|
99
|
+
"\u2022 Contain more than ".concat(exports.SHEET_NAME_MAX_LENGTH, " characters."),
|
|
100
|
+
"• Contain any of the following characters: / \\ ? * : [ ]",
|
|
101
|
+
"• Begin or end with an apostrophe ('), but they can be used in between text or numbers in a name.",
|
|
102
|
+
"\u2022 Be named \"".concat(exports.SHEET_NAME_RESERVED_WORD, "\". This is a reserved word Excel uses internally."),
|
|
103
|
+
].join("\n");
|
|
104
|
+
/**
|
|
105
|
+
* Excel's worksheet-name rules, in the order its dialog lists them: not blank, at most 31 characters,
|
|
106
|
+
* none of `\ / ? * [ ] :`, no leading or trailing apostrophe, not `History`, and unique among
|
|
107
|
+
* `existing` compared case-insensitively (a sheet may always re-case its own title — pass its id as
|
|
108
|
+
* `exceptSheetId`). Both engines' rename / create paths and the sheet tabs validate through this one
|
|
109
|
+
* function (the rnc store mirrors it in Rust and surfaces the same messages via its `rejections`).
|
|
110
|
+
*/
|
|
111
|
+
var validateSheetName = function (name, existing, exceptSheetId) {
|
|
112
|
+
var e_3, _a;
|
|
113
|
+
if (existing === void 0) { existing = []; }
|
|
114
|
+
var invalid = function (reason) { return ({
|
|
115
|
+
ok: false,
|
|
116
|
+
reason: reason,
|
|
117
|
+
message: exports.SHEET_NAME_INVALID_MESSAGE,
|
|
118
|
+
}); };
|
|
119
|
+
if (name.trim().length === 0) {
|
|
120
|
+
return invalid("blank");
|
|
121
|
+
}
|
|
122
|
+
if (name.length > exports.SHEET_NAME_MAX_LENGTH) {
|
|
123
|
+
return invalid("too-long");
|
|
124
|
+
}
|
|
125
|
+
if (SHEET_NAME_ILLEGAL_CHARACTER.test(name)) {
|
|
126
|
+
return invalid("illegal-character");
|
|
127
|
+
}
|
|
128
|
+
if (name.startsWith("'") || name.endsWith("'")) {
|
|
129
|
+
return invalid("apostrophe");
|
|
130
|
+
}
|
|
131
|
+
if ((0, exports.isSameSheetTitle)(name, exports.SHEET_NAME_RESERVED_WORD)) {
|
|
132
|
+
return invalid("reserved");
|
|
133
|
+
}
|
|
134
|
+
var key = (0, exports.sheetTitleKey)(name);
|
|
135
|
+
try {
|
|
136
|
+
for (var existing_2 = tslib_1.__values(existing), existing_2_1 = existing_2.next(); !existing_2_1.done; existing_2_1 = existing_2.next()) {
|
|
137
|
+
var sheet = existing_2_1.value;
|
|
138
|
+
if (typeof sheet === "string") {
|
|
139
|
+
if ((0, exports.sheetTitleKey)(sheet) === key) {
|
|
140
|
+
return { ok: false, reason: "taken", message: exports.SHEET_NAME_TAKEN_MESSAGE };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (sheet.sheetId !== exceptSheetId && (0, exports.sheetTitleKey)(sheet.title) === key) {
|
|
144
|
+
return { ok: false, reason: "taken", message: exports.SHEET_NAME_TAKEN_MESSAGE };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
149
|
+
finally {
|
|
150
|
+
try {
|
|
151
|
+
if (existing_2_1 && !existing_2_1.done && (_a = existing_2.return)) _a.call(existing_2);
|
|
152
|
+
}
|
|
153
|
+
finally { if (e_3) throw e_3.error; }
|
|
154
|
+
}
|
|
155
|
+
return { ok: true };
|
|
156
|
+
};
|
|
157
|
+
exports.validateSheetName = validateSheetName;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel's refusal when an edit touches a locked cell of a protected sheet — the dialog every
|
|
3
|
+
* mutation path shows (typing, Delete / Backspace, fill, paste) on both engines
|
|
4
|
+
* (rowsncolumns/spreadsheet#939 U-35). The rnc store surfaces the same text through its
|
|
5
|
+
* `rejections` channel under the `protected-sheet` code.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SHEET_PROTECTION_ERROR_TITLE = "This sheet is protected";
|
|
8
|
+
export declare const PROTECTED_SHEET_EDIT_MESSAGE = "The cell or chart you're trying to change is on a protected sheet. To make a change, unprotect the sheet. You might be requested to enter a password.";
|
|
9
|
+
//# sourceMappingURL=sheet-protection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheet-protection.d.ts","sourceRoot":"","sources":["../../sheet-protection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE,eAAO,MAAM,4BAA4B,0JACgH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROTECTED_SHEET_EDIT_MESSAGE = exports.SHEET_PROTECTION_ERROR_TITLE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Excel's refusal when an edit touches a locked cell of a protected sheet — the dialog every
|
|
6
|
+
* mutation path shows (typing, Delete / Backspace, fill, paste) on both engines
|
|
7
|
+
* (rowsncolumns/spreadsheet#939 U-35). The rnc store surfaces the same text through its
|
|
8
|
+
* `rejections` channel under the `protected-sheet` code.
|
|
9
|
+
*/
|
|
10
|
+
exports.SHEET_PROTECTION_ERROR_TITLE = "This sheet is protected";
|
|
11
|
+
exports.PROTECTED_SHEET_EDIT_MESSAGE = "The cell or chart you're trying to change is on a protected sheet. To make a change, unprotect the sheet. You might be requested to enter a password.";
|
package/dist/cjs/string.d.ts
CHANGED
|
@@ -38,6 +38,20 @@ export declare const cellToAddress: (cell: CellInterface | null, isAbsoluteColum
|
|
|
38
38
|
* @returns
|
|
39
39
|
*/
|
|
40
40
|
export declare const cellRangeToAddress: (sheetName: string | undefined, range: GridRange) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Must `name` be single-quoted as a sheet qualifier (`'My Data'!A1`)? Excel writes it bare only when
|
|
43
|
+
* it is a plain identifier — ASCII letters and digits, starting with a letter — and cannot be read
|
|
44
|
+
* as something else: a cell reference (`A1`, `XFD1`, `R1C1`), or a boolean (`TRUE`). A leading digit
|
|
45
|
+
* (`2024`, `1Sheet`) always quotes. Deliberately a superset of Excel's minimum (a `_`, `.` or
|
|
46
|
+
* non-ASCII letter also quotes): over-quoting is always valid, under-quoting makes `=2024!A1` a
|
|
47
|
+
* formula Excel rejects (rowsncolumns/spreadsheet#939 K-10).
|
|
48
|
+
*/
|
|
49
|
+
export declare const sheetNameNeedsQuoting: (name: string) => boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Quote a sheet name for use as a formula qualifier when Excel requires it (see
|
|
52
|
+
* {@link sheetNameNeedsQuoting}); apostrophes inside are doubled (`'O''Neil'`). Rename, cross-sheet
|
|
53
|
+
* reference generation and formula-bar insertion all quote through here so they agree.
|
|
54
|
+
*/
|
|
41
55
|
export declare const sanitizeSheetName: (name: string | undefined) => string | undefined;
|
|
42
56
|
/**
|
|
43
57
|
* Remove single quotes from sheetname
|
|
@@ -76,6 +90,23 @@ export declare const matchesWildcardPattern: (pattern: string, text: string, anc
|
|
|
76
90
|
*/
|
|
77
91
|
export declare const selectionBoundsFromCells: (start: CellInterface, end?: CellInterface | null) => GridRange;
|
|
78
92
|
export declare const supplant: (s: string, o: Record<string, any>) => string;
|
|
93
|
+
/**
|
|
94
|
+
* Whether a hyperlink target read from untrusted markup (clipboard HTML) may
|
|
95
|
+
* be kept. A URL with an explicit scheme must use one of the safe schemes;
|
|
96
|
+
* scheme-less, protocol-relative, path and fragment references are allowed
|
|
97
|
+
* (they are normalized by {@link normalizeExternalUrl} before being opened).
|
|
98
|
+
* Applies the same tab/newline and C0-control stripping as the browser's URL
|
|
99
|
+
* canonicalization so `java\tscript:` cannot slip through.
|
|
100
|
+
*/
|
|
101
|
+
export declare const isSafeHyperlinkUri: (uri: string | null | undefined) => boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Escape text for interpolation into an HTML text node (`text/html`
|
|
104
|
+
* clipboard fragments): a cell whose value is literally `<b>x</b>` must paste
|
|
105
|
+
* back as that text, not as bold "x".
|
|
106
|
+
*/
|
|
107
|
+
export declare const escapeHtmlText: (value: string) => string;
|
|
108
|
+
/** Escape a value for interpolation into a quoted HTML attribute. */
|
|
109
|
+
export declare const escapeHtmlAttribute: (value: string) => string;
|
|
79
110
|
/**
|
|
80
111
|
* Normalize an external hyperlink target for the browser. A stored URL
|
|
81
112
|
* without a scheme ("paradigm.xyz") would otherwise be resolved relative to
|
package/dist/cjs/string.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;
|
|
1
|
+
{"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../string.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAK3E;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,aAAa,KAAG,SAKvD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,MAAM,EACf,UAAS;IACP,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,KACL,aAAa,GAAG,IAuDlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,uBAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,uBAErD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,MAAM,aAAa,GAAG,IAAI,EAC1B,0BAAwB,EACxB,uBAAqB,EACrB,sBAAoB,EACpB,mBAAiB,KAChB,MAAM,GAAG,IAiBX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAS,WAUjB,CAAC;AAOF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,KAAG,OAMrB,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,GAAG,SAAS,uBASzD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,GAAG,SAAS,uBAgB3D,CAAC;AAKF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,WAI3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,MAAM,KAAG,MAe/D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GACjC,SAAS,MAAM,EACf,MAAM,MAAM,EACZ,QAAQ,QAAQ,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,KACxD,OAWF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,EACpB,MAAK,aAAa,GAAG,IAAY,KAChC,SAQF,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAKzD,CAAC;AAUF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAgBnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MAC2B,CAAC;AAE3E,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAM1B,CAAC;AAE3B;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,WAoB/C,CAAC"}
|
package/dist/cjs/string.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeExternalUrl = exports.supplant = exports.selectionBoundsFromCells = exports.matchesWildcardPattern = exports.wildcardPatternToRegExpSource = exports.escapeCharacters = exports.desanitizeSheetName = exports.sanitizeSheetName = exports.cellRangeToAddress = exports.cellToAddress = exports.getRowIndexFromAddress = exports.getColumnNameFromAddress = exports.addressToCell = exports.convertCellToRange = void 0;
|
|
3
|
+
exports.normalizeExternalUrl = exports.escapeHtmlAttribute = exports.escapeHtmlText = exports.isSafeHyperlinkUri = exports.supplant = exports.selectionBoundsFromCells = exports.matchesWildcardPattern = exports.wildcardPatternToRegExpSource = exports.escapeCharacters = exports.desanitizeSheetName = exports.sanitizeSheetName = exports.sheetNameNeedsQuoting = exports.cellRangeToAddress = exports.cellToAddress = exports.getRowIndexFromAddress = exports.getColumnNameFromAddress = exports.addressToCell = exports.convertCellToRange = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var typeguards_1 = require("./typeguards");
|
|
6
6
|
var numbers_1 = require("./numbers");
|
|
@@ -135,13 +135,38 @@ var cellRangeToAddress = function (sheetName, range) {
|
|
|
135
135
|
}));
|
|
136
136
|
};
|
|
137
137
|
exports.cellRangeToAddress = cellRangeToAddress;
|
|
138
|
-
|
|
138
|
+
/** `A1`-style lookalike: 1–3 ASCII letters followed only by digits (`AB12`, `XFD1`). */
|
|
139
|
+
var SHEET_NAME_A1_LOOKALIKE = /^[A-Za-z]{1,3}\d+$/;
|
|
140
|
+
/** `R1C1`-style lookalike: an optional-digit `R` part and/or `C` part, nothing else (`R`, `C22`, `R1C1`). */
|
|
141
|
+
var SHEET_NAME_R1C1_LOOKALIKE = /^(?:[Rr]\d*)?(?:[Cc]\d*)?$/;
|
|
142
|
+
/**
|
|
143
|
+
* Must `name` be single-quoted as a sheet qualifier (`'My Data'!A1`)? Excel writes it bare only when
|
|
144
|
+
* it is a plain identifier — ASCII letters and digits, starting with a letter — and cannot be read
|
|
145
|
+
* as something else: a cell reference (`A1`, `XFD1`, `R1C1`), or a boolean (`TRUE`). A leading digit
|
|
146
|
+
* (`2024`, `1Sheet`) always quotes. Deliberately a superset of Excel's minimum (a `_`, `.` or
|
|
147
|
+
* non-ASCII letter also quotes): over-quoting is always valid, under-quoting makes `=2024!A1` a
|
|
148
|
+
* formula Excel rejects (rowsncolumns/spreadsheet#939 K-10).
|
|
149
|
+
*/
|
|
150
|
+
var sheetNameNeedsQuoting = function (name) {
|
|
151
|
+
return name.length === 0 ||
|
|
152
|
+
/[^A-Za-z0-9]/.test(name) ||
|
|
153
|
+
/^\d/.test(name) ||
|
|
154
|
+
SHEET_NAME_A1_LOOKALIKE.test(name) ||
|
|
155
|
+
SHEET_NAME_R1C1_LOOKALIKE.test(name) ||
|
|
156
|
+
/^(?:true|false)$/i.test(name);
|
|
157
|
+
};
|
|
158
|
+
exports.sheetNameNeedsQuoting = sheetNameNeedsQuoting;
|
|
159
|
+
/**
|
|
160
|
+
* Quote a sheet name for use as a formula qualifier when Excel requires it (see
|
|
161
|
+
* {@link sheetNameNeedsQuoting}); apostrophes inside are doubled (`'O''Neil'`). Rename, cross-sheet
|
|
162
|
+
* reference generation and formula-bar insertion all quote through here so they agree.
|
|
163
|
+
*/
|
|
139
164
|
var sanitizeSheetName = function (name) {
|
|
140
165
|
if (name === void 0)
|
|
141
166
|
return void 0;
|
|
142
167
|
if (typeof name === "number")
|
|
143
168
|
return name;
|
|
144
|
-
if (
|
|
169
|
+
if ((0, exports.sheetNameNeedsQuoting)(name)) {
|
|
145
170
|
// Escape single quotes by doubling them for valid sheet references.
|
|
146
171
|
var escaped = name.replace(/'/g, "''");
|
|
147
172
|
return "'".concat(escaped, "'");
|
|
@@ -263,6 +288,51 @@ var URL_SCHEME = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;
|
|
|
263
288
|
// explicit scheme (javascript:, data:, vbscript:, …) is a script/content
|
|
264
289
|
// injection vector and is rejected.
|
|
265
290
|
var SAFE_URL_SCHEMES = new Set(["http", "https", "mailto", "tel", "ftp"]);
|
|
291
|
+
/**
|
|
292
|
+
* Whether a hyperlink target read from untrusted markup (clipboard HTML) may
|
|
293
|
+
* be kept. A URL with an explicit scheme must use one of the safe schemes;
|
|
294
|
+
* scheme-less, protocol-relative, path and fragment references are allowed
|
|
295
|
+
* (they are normalized by {@link normalizeExternalUrl} before being opened).
|
|
296
|
+
* Applies the same tab/newline and C0-control stripping as the browser's URL
|
|
297
|
+
* canonicalization so `java\tscript:` cannot slip through.
|
|
298
|
+
*/
|
|
299
|
+
var isSafeHyperlinkUri = function (uri) {
|
|
300
|
+
if (uri == null) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
var trimmed = uri
|
|
304
|
+
.replace(/[\t\n\r]/g, "")
|
|
305
|
+
.replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, "")
|
|
306
|
+
.trim();
|
|
307
|
+
if (!trimmed) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
var schemeMatch = URL_SCHEME.exec(trimmed);
|
|
311
|
+
if (!schemeMatch) {
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
return SAFE_URL_SCHEMES.has(schemeMatch[1].toLowerCase());
|
|
315
|
+
};
|
|
316
|
+
exports.isSafeHyperlinkUri = isSafeHyperlinkUri;
|
|
317
|
+
/**
|
|
318
|
+
* Escape text for interpolation into an HTML text node (`text/html`
|
|
319
|
+
* clipboard fragments): a cell whose value is literally `<b>x</b>` must paste
|
|
320
|
+
* back as that text, not as bold "x".
|
|
321
|
+
*/
|
|
322
|
+
var escapeHtmlText = function (value) {
|
|
323
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
324
|
+
};
|
|
325
|
+
exports.escapeHtmlText = escapeHtmlText;
|
|
326
|
+
/** Escape a value for interpolation into a quoted HTML attribute. */
|
|
327
|
+
var escapeHtmlAttribute = function (value) {
|
|
328
|
+
return value
|
|
329
|
+
.replace(/&/g, "&")
|
|
330
|
+
.replace(/"/g, """)
|
|
331
|
+
.replace(/'/g, "'")
|
|
332
|
+
.replace(/</g, "<")
|
|
333
|
+
.replace(/>/g, ">");
|
|
334
|
+
};
|
|
335
|
+
exports.escapeHtmlAttribute = escapeHtmlAttribute;
|
|
266
336
|
/**
|
|
267
337
|
* Normalize an external hyperlink target for the browser. A stored URL
|
|
268
338
|
* without a scheme ("paradigm.xyz") would otherwise be resolved relative to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../../array.ts","../../cell-data-helpers.ts","../../color.ts","../../colors.ts","../../conditional-format.ts","../../content-hash.ts","../../data-validation.ts","../../date.ts","../../dom.ts","../../excel-number-format.ts","../../formatter.ts","../../formula-locale.ts","../../formula-refs.ts","../../formula.ts","../../index.ts","../../keyboard.ts","../../locale.ts","../../log.ts","../../memoize.ts","../../numbers.ts","../../rich-text-html.ts","../../rich-text-links.ts","../../selection.ts","../../shared-string-canonical.ts","../../sheet-span.ts","../../sort.ts","../../sparkline-groups.ts","../../string.ts","../../table-names.ts","../../table-styles.ts","../../table-totals.ts","../../typeguards.ts","../../uuid.ts","../../vitest.config.ts","../../formula-locale/formula-locale-data.generated.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["../../array.ts","../../cell-data-helpers.ts","../../clipboard-html-sanitize.ts","../../color.ts","../../colors.ts","../../condition-operand.ts","../../conditional-format.ts","../../content-hash.ts","../../data-validation.ts","../../date.ts","../../dom.ts","../../excel-number-format.ts","../../formatter.ts","../../formula-locale.ts","../../formula-refs.ts","../../formula.ts","../../index.ts","../../keyboard.ts","../../locale.ts","../../log.ts","../../memoize.ts","../../numbers.ts","../../rich-text-html.ts","../../rich-text-links.ts","../../selection.ts","../../shared-string-canonical.ts","../../sheet-names.ts","../../sheet-protection.ts","../../sheet-span.ts","../../sort.ts","../../sparkline-groups.ts","../../string.ts","../../table-names.ts","../../table-styles.ts","../../table-totals.ts","../../typeguards.ts","../../uuid.ts","../../vitest.config.ts","../../formula-locale/formula-locale-data.generated.ts"],"version":"5.8.3"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<img src>` may point at http(s) or an inline `data:image/*` (browsers copy
|
|
3
|
+
* inline images that way). Anything else — `javascript:`, `data:text/html`,
|
|
4
|
+
* `blob:` from another origin — is dropped.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isSafeImageSrc: (value: string | null | undefined) => boolean;
|
|
7
|
+
export declare const sanitizeInlineStyle: (value: string) => string;
|
|
8
|
+
export declare const sanitizeStylesheetText: (value: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Strip executable and network-reaching content from an inert (DOMParser)
|
|
11
|
+
* document in place. Safe to call on a document that was never attached; the
|
|
12
|
+
* caller imports the result into a live sandbox afterwards.
|
|
13
|
+
*/
|
|
14
|
+
export declare const sanitizeClipboardDocument: (doc: Document) => Document;
|
|
15
|
+
//# sourceMappingURL=clipboard-html-sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard-html-sanitize.d.ts","sourceRoot":"","sources":["../../clipboard-html-sanitize.ts"],"names":[],"mappings":"AA6FA;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,OAWjE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAanD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,KAAG,MACT,CAAC;AAmC/C;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,KAAK,QAAQ,KAAG,QAyCzD,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { isSafeHyperlinkUri } from "./string";
|
|
2
|
+
/**
|
|
3
|
+
* Sanitizer for clipboard `text/html` before any of it touches the live DOM.
|
|
4
|
+
*
|
|
5
|
+
* Clipboard HTML is attacker-controlled: a page the user copied from (or a
|
|
6
|
+
* synthetic `paste` event) can carry `<img onerror>`, `<svg onload>`,
|
|
7
|
+
* `<script>`, `<iframe srcdoc>` or `javascript:` links. The paste converters
|
|
8
|
+
* only ever need the document's structure (tables, rows, cells, inline
|
|
9
|
+
* formatting tags, `data-*` value hints), its inline `style` attributes and
|
|
10
|
+
* the payload's own `<style>` rules (Excel's `.fontN` / `.xlNN` classes), so
|
|
11
|
+
* everything else is dropped from the INERT `DOMParser` document before the
|
|
12
|
+
* sanitized nodes are imported into the mounted sandbox.
|
|
13
|
+
*
|
|
14
|
+
* This runs unconditionally; a consumer's `sanitizeHtml` prop runs on the raw
|
|
15
|
+
* string before this and can only tighten the result.
|
|
16
|
+
*/
|
|
17
|
+
/** Elements removed with their subtree. */
|
|
18
|
+
const FORBIDDEN_ELEMENTS = new Set([
|
|
19
|
+
"script",
|
|
20
|
+
"noscript",
|
|
21
|
+
"template",
|
|
22
|
+
"iframe",
|
|
23
|
+
"frame",
|
|
24
|
+
"frameset",
|
|
25
|
+
"object",
|
|
26
|
+
"embed",
|
|
27
|
+
"applet",
|
|
28
|
+
"link",
|
|
29
|
+
"base",
|
|
30
|
+
"svg",
|
|
31
|
+
"math",
|
|
32
|
+
"input",
|
|
33
|
+
"button",
|
|
34
|
+
"select",
|
|
35
|
+
"textarea",
|
|
36
|
+
"audio",
|
|
37
|
+
"video",
|
|
38
|
+
"source",
|
|
39
|
+
"track",
|
|
40
|
+
"canvas",
|
|
41
|
+
"portal",
|
|
42
|
+
"dialog",
|
|
43
|
+
]);
|
|
44
|
+
/** Elements removed but whose children are kept in place. */
|
|
45
|
+
const UNWRAPPED_ELEMENTS = new Set(["form", "fieldset", "label", "output"]);
|
|
46
|
+
/** URL-bearing attributes that never carry spreadsheet meaning. */
|
|
47
|
+
const FORBIDDEN_ATTRIBUTES = new Set([
|
|
48
|
+
"srcdoc",
|
|
49
|
+
"srcset",
|
|
50
|
+
"formaction",
|
|
51
|
+
"action",
|
|
52
|
+
"background",
|
|
53
|
+
"poster",
|
|
54
|
+
"ping",
|
|
55
|
+
"dynsrc",
|
|
56
|
+
"lowsrc",
|
|
57
|
+
"cite",
|
|
58
|
+
"longdesc",
|
|
59
|
+
"manifest",
|
|
60
|
+
"codebase",
|
|
61
|
+
"classid",
|
|
62
|
+
"data",
|
|
63
|
+
"archive",
|
|
64
|
+
"profile",
|
|
65
|
+
"usemap",
|
|
66
|
+
"xlink:href",
|
|
67
|
+
"xml:base",
|
|
68
|
+
]);
|
|
69
|
+
/** Attributes whose value must be a safe URL; otherwise the attribute is dropped. */
|
|
70
|
+
const URL_ATTRIBUTES = new Set(["href", "src"]);
|
|
71
|
+
/**
|
|
72
|
+
* CSS constructs that can reach the network or (in legacy engines) run code.
|
|
73
|
+
* Any declaration containing one of them is dropped; the rest of the `style`
|
|
74
|
+
* survives so legitimate `font-weight` / `color` / `mso-number-format` hints
|
|
75
|
+
* are kept.
|
|
76
|
+
*/
|
|
77
|
+
const DANGEROUS_CSS = /(expression\s*\(|url\s*\(|@import|behavior\s*:|-moz-binding|javascript:|vbscript:|data:)/i;
|
|
78
|
+
/**
|
|
79
|
+
* Stylesheet-level constructs that can reach the network. `<style>` text is
|
|
80
|
+
* needed for Excel's class rules but never for anything that fetches.
|
|
81
|
+
*/
|
|
82
|
+
const DANGEROUS_STYLESHEET_RULE = /@import[^;]*;?|url\s*\([^)]*\)|expression\s*\([^)]*\)|behavior\s*:[^;}]*|-moz-binding\s*:[^;}]*/gi;
|
|
83
|
+
const isEventHandlerAttribute = (name) => name.length > 2 && name.charCodeAt(0) === 111 /* o */ && name.charCodeAt(1) === 110 /* n */;
|
|
84
|
+
/**
|
|
85
|
+
* `<img src>` may point at http(s) or an inline `data:image/*` (browsers copy
|
|
86
|
+
* inline images that way). Anything else — `javascript:`, `data:text/html`,
|
|
87
|
+
* `blob:` from another origin — is dropped.
|
|
88
|
+
*/
|
|
89
|
+
export const isSafeImageSrc = (value) => {
|
|
90
|
+
if (!value) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const trimmed = value.replace(/[\t\n\r]/g, "").trim();
|
|
94
|
+
if (/^data:image\/(png|gif|jpe?g|webp|bmp|svg\+xml|x-icon|tiff);/i.test(trimmed)) {
|
|
95
|
+
// Inline SVG can carry scripts when navigated to, but as an `<img>` source
|
|
96
|
+
// it is rendered in an isolated, script-less document; allow it.
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return /^https?:\/\//i.test(trimmed) || /^\/\//.test(trimmed);
|
|
100
|
+
};
|
|
101
|
+
export const sanitizeInlineStyle = (value) => {
|
|
102
|
+
// Untouched styles are kept byte-for-byte: Excel's `mso-number-format`
|
|
103
|
+
// patterns carry escaped `\;` and significant spaces that a split/rejoin
|
|
104
|
+
// would mangle.
|
|
105
|
+
if (!DANGEROUS_CSS.test(value)) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
return value
|
|
109
|
+
.split(";")
|
|
110
|
+
.filter((declaration) => declaration.trim() && !DANGEROUS_CSS.test(declaration))
|
|
111
|
+
.join(";");
|
|
112
|
+
};
|
|
113
|
+
export const sanitizeStylesheetText = (value) => value.replace(DANGEROUS_STYLESHEET_RULE, "");
|
|
114
|
+
const sanitizeAttributes = (element) => {
|
|
115
|
+
const tag = element.tagName.toLowerCase();
|
|
116
|
+
// Copy first: removing while iterating a live NamedNodeMap skips entries.
|
|
117
|
+
const attributes = Array.from(element.attributes);
|
|
118
|
+
for (const attribute of attributes) {
|
|
119
|
+
const name = attribute.name.toLowerCase();
|
|
120
|
+
if (isEventHandlerAttribute(name) || FORBIDDEN_ATTRIBUTES.has(name)) {
|
|
121
|
+
element.removeAttribute(attribute.name);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (name === "style") {
|
|
125
|
+
const safe = sanitizeInlineStyle(attribute.value);
|
|
126
|
+
if (safe !== attribute.value) {
|
|
127
|
+
if (safe) {
|
|
128
|
+
element.setAttribute(attribute.name, safe);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
element.removeAttribute(attribute.name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (URL_ATTRIBUTES.has(name)) {
|
|
137
|
+
const safe = name === "src"
|
|
138
|
+
? tag === "img" && isSafeImageSrc(attribute.value)
|
|
139
|
+
: isSafeHyperlinkUri(attribute.value);
|
|
140
|
+
if (!safe) {
|
|
141
|
+
element.removeAttribute(attribute.name);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Strip executable and network-reaching content from an inert (DOMParser)
|
|
148
|
+
* document in place. Safe to call on a document that was never attached; the
|
|
149
|
+
* caller imports the result into a live sandbox afterwards.
|
|
150
|
+
*/
|
|
151
|
+
export const sanitizeClipboardDocument = (doc) => {
|
|
152
|
+
// A static snapshot: removals below detach subtrees, and detached
|
|
153
|
+
// descendants are skipped via `doc.contains`.
|
|
154
|
+
const elements = Array.from(doc.querySelectorAll("*"));
|
|
155
|
+
for (const element of elements) {
|
|
156
|
+
if (!doc.contains(element)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const tag = element.tagName.toLowerCase();
|
|
160
|
+
if (FORBIDDEN_ELEMENTS.has(tag)) {
|
|
161
|
+
element.remove();
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (UNWRAPPED_ELEMENTS.has(tag)) {
|
|
165
|
+
const parent = element.parentNode;
|
|
166
|
+
if (parent) {
|
|
167
|
+
while (element.firstChild) {
|
|
168
|
+
parent.insertBefore(element.firstChild, element);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
element.remove();
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (tag === "style") {
|
|
175
|
+
const text = element.textContent ?? "";
|
|
176
|
+
const safe = sanitizeStylesheetText(text);
|
|
177
|
+
if (safe !== text) {
|
|
178
|
+
element.textContent = safe;
|
|
179
|
+
}
|
|
180
|
+
// A stylesheet never needs attributes beyond `type`/`media`.
|
|
181
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
182
|
+
const name = attribute.name.toLowerCase();
|
|
183
|
+
if (name !== "type" && name !== "media") {
|
|
184
|
+
element.removeAttribute(attribute.name);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
sanitizeAttributes(element);
|
|
190
|
+
}
|
|
191
|
+
return doc;
|
|
192
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ConditionType } from "@rowsncolumns/common-types";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a condition type compares TEXT — its operand is always a string
|
|
4
|
+
* literal in Excel, even when it looks like a number or a boolean.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isTextConditionType: (type: ConditionType | string | undefined | null) => boolean;
|
|
7
|
+
export declare const escapeConditionFormulaText: (value: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* The formula fragment for an operand that is a typed LITERAL — a number
|
|
10
|
+
* (`5`, `1e3`, locale `6,5`), a percentage (`50%` → `0.5`), a boolean, or a
|
|
11
|
+
* date / time in the document locale (`2024-01-05`, `1/5/2024`, `10:30` → the
|
|
12
|
+
* serial). `undefined` for anything else (text, references, formulas).
|
|
13
|
+
*/
|
|
14
|
+
export declare const conditionOperandLiteral: (raw: string | undefined | null, locale?: string) => string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Whether an operand (with any leading `=` already removed) is an A1
|
|
17
|
+
* reference — a cell, a range, a whole column/row, with `$` anchors and an
|
|
18
|
+
* optional (quoted) sheet qualifier: `B1`, `$B$1`, `B$1`, `Sheet1!$B$1`,
|
|
19
|
+
* `'My Sheet'!$B$1:$C$9`, `$A:$A`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const isConditionOperandReference: (text: string) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether an operand (with any leading `=` already removed) is a function
|
|
24
|
+
* call — the form OOXML stores a formula bound in (`TODAY()`,
|
|
25
|
+
* `DATE(2024,1,1)`, `LEN($B$1)`): evaluated as a formula, never quoted.
|
|
26
|
+
*/
|
|
27
|
+
export declare const isConditionOperandFormulaCall: (text: string) => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Normalize one rule operand into the formula fragment the rule builders
|
|
30
|
+
* splice into `=<cell><op><fragment>` (and the xlsx exporters write inside
|
|
31
|
+
* `<formula>` / `<formula1>` — never with a leading `=`).
|
|
32
|
+
*
|
|
33
|
+
* - `=…` → the formula text (leading `=` stripped).
|
|
34
|
+
* - `TEXT_*` types → ALWAYS a quoted string literal (`"1"`, `"TRUE"`).
|
|
35
|
+
* - A complete `"…"` string literal → as is.
|
|
36
|
+
* - An A1 reference (`$B$1`, `B$1`, `Sheet1!$B$1`, `'My Sheet'!$B$1`) → as is.
|
|
37
|
+
* - A function call (`TODAY()`, `DATE(2024,1,1)`, `LEN(A1)`) → as is.
|
|
38
|
+
* - A typed literal (number, `50%`, boolean, date/time in `locale`) → its
|
|
39
|
+
* numeric form (`0.5`, `45296`, `TRUE`).
|
|
40
|
+
* - Anything else → a quoted string literal (`"abc"`).
|
|
41
|
+
*
|
|
42
|
+
* `""` for an empty operand.
|
|
43
|
+
*/
|
|
44
|
+
export declare const normalizeConditionOperand: (raw: string | undefined | null, type: ConditionType | string | undefined | null, locale?: string) => string;
|
|
45
|
+
/**
|
|
46
|
+
* The OOXML `<formula>` / `<formula1>` text for a number/date rule operand:
|
|
47
|
+
* the leading `=` is stripped (Excel never writes one), typed literals are
|
|
48
|
+
* normalized to what Excel itself stores (`50%` → `0.5`, `2024-01-05` → its
|
|
49
|
+
* serial `45296` rather than the arithmetic `2024-01-05` = 2022), and every
|
|
50
|
+
* other operand — references, defined names, function calls — is written
|
|
51
|
+
* verbatim.
|
|
52
|
+
*/
|
|
53
|
+
export declare const conditionOperandToFormulaText: (raw: string | undefined | null, locale?: string) => string;
|
|
54
|
+
//# sourceMappingURL=condition-operand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"condition-operand.d.ts","sourceRoot":"","sources":["../../condition-operand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAyChE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,KAC9C,OAA0D,CAAC;AAE9D,eAAO,MAAM,0BAA0B,GAAI,OAAO,MAAM,WAC7B,CAAC;AAsC5B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,SAAS,MAAM,KACd,MAAM,GAAG,SA2CX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,KAAG,OACU,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,KAAG,OAC5B,CAAC;AAElC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,yBAAyB,GACpC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,EAC/C,SAAS,MAAM,KACd,MA0BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,GACxC,KAAK,MAAM,GAAG,SAAS,GAAG,IAAI,EAC9B,SAAS,MAAM,KACd,MAQF,CAAC"}
|