@rowsncolumns/utils 9.5.1 → 9.5.3
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/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/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
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildDataValidationExpression = exports.isDataValidationFormulaRule = exports.dataValidationOptionMatchesCellValue = exports.stripDataValidationTextPrefix = void 0;
|
|
4
|
-
var
|
|
4
|
+
var condition_operand_1 = require("./condition-operand");
|
|
5
5
|
var FORMULA_PREDICATE_BASE_CELL_REF = "A1";
|
|
6
|
-
var NUMBER_LITERAL_PATTERN = /^-?\d+(?:\.\d+)?$/;
|
|
7
|
-
var BOOLEAN_LITERAL_PATTERN = /^(TRUE|FALSE)$/i;
|
|
8
|
-
var escapeFormulaText = function (value) { return value.replace(/"/g, '""'); };
|
|
9
6
|
/**
|
|
10
7
|
* A leading apostrophe in a data-validation / conditional-format list entry is
|
|
11
8
|
* Excel's text prefix — `'000s` is the text "000s" and a bare `'` is empty
|
|
@@ -29,57 +26,6 @@ var dataValidationOptionMatchesCellValue = function (option, cellValue) {
|
|
|
29
26
|
return normalizedOption.toLowerCase() === normalizedValue.toLowerCase();
|
|
30
27
|
};
|
|
31
28
|
exports.dataValidationOptionMatchesCellValue = dataValidationOptionMatchesCellValue;
|
|
32
|
-
/**
|
|
33
|
-
* Parse a locale-formatted numeric string (e.g., "6,5" in fr-FR) into a JS
|
|
34
|
-
* number. Uses `Intl.NumberFormat` to detect decimal/group separators.
|
|
35
|
-
* Returns NaN on failure.
|
|
36
|
-
*/
|
|
37
|
-
var parseLocaleNumberLoose = function (value, locale) {
|
|
38
|
-
var _a, _b, _c, _d;
|
|
39
|
-
try {
|
|
40
|
-
var parts = new Intl.NumberFormat(locale).formatToParts(1234.5);
|
|
41
|
-
var decimal = (_b = (_a = parts.find(function (part) { return part.type === "decimal"; })) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ".";
|
|
42
|
-
var group = (_d = (_c = parts.find(function (part) { return part.type === "group"; })) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : ",";
|
|
43
|
-
var cleaned = value.split(group).join("").replace(decimal, ".");
|
|
44
|
-
return Number(cleaned);
|
|
45
|
-
}
|
|
46
|
-
catch (_e) {
|
|
47
|
-
return NaN;
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Normalize a condition value for use in a formula expression.
|
|
52
|
-
* Handles locale-specific number formats (e.g., "6,5" -> "6.5").
|
|
53
|
-
*/
|
|
54
|
-
var normalizeConditionValue = function (raw, allowStringLiteral, locale) {
|
|
55
|
-
if (allowStringLiteral === void 0) { allowStringLiteral = false; }
|
|
56
|
-
if (!raw)
|
|
57
|
-
return "";
|
|
58
|
-
var trimmed = raw.trim();
|
|
59
|
-
if (!trimmed)
|
|
60
|
-
return "";
|
|
61
|
-
if (trimmed.startsWith("=")) {
|
|
62
|
-
return trimmed.slice(1);
|
|
63
|
-
}
|
|
64
|
-
if ((0, selection_1.checkIfStringIsReference)(trimmed)) {
|
|
65
|
-
return trimmed;
|
|
66
|
-
}
|
|
67
|
-
if (NUMBER_LITERAL_PATTERN.test(trimmed) ||
|
|
68
|
-
BOOLEAN_LITERAL_PATTERN.test(trimmed)) {
|
|
69
|
-
return trimmed;
|
|
70
|
-
}
|
|
71
|
-
// Try parsing as locale-aware number
|
|
72
|
-
if (locale) {
|
|
73
|
-
var parsed = parseLocaleNumberLoose(trimmed, locale);
|
|
74
|
-
if (!isNaN(parsed)) {
|
|
75
|
-
return String(parsed);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (allowStringLiteral) {
|
|
79
|
-
return "\"".concat(escapeFormulaText(trimmed), "\"");
|
|
80
|
-
}
|
|
81
|
-
return trimmed;
|
|
82
|
-
};
|
|
83
29
|
/**
|
|
84
30
|
* Whether a data validation rule is evaluated via the calculator (formula path)
|
|
85
31
|
* instead of the synchronous `checkBooleanRule` path used in
|
|
@@ -120,7 +66,29 @@ var isDataValidationFormulaRule = function (rule) {
|
|
|
120
66
|
return Boolean((0, exports.buildDataValidationExpression)(rule, FORMULA_PREDICATE_BASE_CELL_REF));
|
|
121
67
|
};
|
|
122
68
|
exports.isDataValidationFormulaRule = isDataValidationFormulaRule;
|
|
69
|
+
/**
|
|
70
|
+
* Excel's *Whole number* validation kind rejects any non-integer (`5.5` fails
|
|
71
|
+
* "whole between 1 and 10"). An imported `type="whole"` rule carries a plain
|
|
72
|
+
* `NUMBER_*` comparison as its `condition` (the bounds) — which alone accepts
|
|
73
|
+
* `5.5` — so when the rule's `xlsxSource.type` is `whole` the comparison is
|
|
74
|
+
* wrapped in the same integer guard `buildExcelValidationRule` composes for
|
|
75
|
+
* an authored Whole-number rule: `AND(ISNUMBER(x), x=INT(x), <comparison>)`.
|
|
76
|
+
* Rules of other kinds, and non-`NUMBER_*` conditions, are returned as built.
|
|
77
|
+
*/
|
|
123
78
|
var buildDataValidationExpression = function (rule, baseCellRef, locale) {
|
|
79
|
+
var _a, _b, _c, _d;
|
|
80
|
+
var expression = buildDataValidationConditionExpression(rule, baseCellRef, locale);
|
|
81
|
+
if (!expression)
|
|
82
|
+
return undefined;
|
|
83
|
+
var isWholeSource = ((_b = (_a = rule === null || rule === void 0 ? void 0 : rule.xlsxSource) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.trim().toLowerCase()) === "whole";
|
|
84
|
+
var isNumberCondition = Boolean((_d = (_c = rule === null || rule === void 0 ? void 0 : rule.condition) === null || _c === void 0 ? void 0 : _c.type) === null || _d === void 0 ? void 0 : _d.startsWith("NUMBER_"));
|
|
85
|
+
if (!isWholeSource || !isNumberCondition)
|
|
86
|
+
return expression;
|
|
87
|
+
var body = expression.startsWith("=") ? expression.slice(1) : expression;
|
|
88
|
+
return "=AND(ISNUMBER(".concat(baseCellRef, "),").concat(baseCellRef, "=INT(").concat(baseCellRef, "),").concat(body, ")");
|
|
89
|
+
};
|
|
90
|
+
exports.buildDataValidationExpression = buildDataValidationExpression;
|
|
91
|
+
var buildDataValidationConditionExpression = function (rule, baseCellRef, locale) {
|
|
124
92
|
var _a, _b, _c;
|
|
125
93
|
var condition = rule === null || rule === void 0 ? void 0 : rule.condition;
|
|
126
94
|
if (!(condition === null || condition === void 0 ? void 0 : condition.type))
|
|
@@ -135,8 +103,8 @@ var buildDataValidationExpression = function (rule, baseCellRef, locale) {
|
|
|
135
103
|
return undefined;
|
|
136
104
|
return trimmed.startsWith("=") ? trimmed : "=".concat(trimmed);
|
|
137
105
|
}
|
|
138
|
-
var fromExpr =
|
|
139
|
-
var toExpr =
|
|
106
|
+
var fromExpr = (0, condition_operand_1.normalizeConditionOperand)(fromRaw, condition.type, locale);
|
|
107
|
+
var toExpr = (0, condition_operand_1.normalizeConditionOperand)(toRaw, condition.type, locale);
|
|
140
108
|
if (!fromExpr && !toExpr)
|
|
141
109
|
return undefined;
|
|
142
110
|
var comparisonOperatorMap = {
|
|
@@ -217,7 +185,7 @@ var buildDataValidationExpression = function (rule, baseCellRef, locale) {
|
|
|
217
185
|
.flatMap(function (entry) {
|
|
218
186
|
if (entry.startsWith("'")) {
|
|
219
187
|
var stripped = (0, exports.stripDataValidationTextPrefix)(entry);
|
|
220
|
-
var textCondition = "".concat(baseCellRef, "=\"").concat(
|
|
188
|
+
var textCondition = "".concat(baseCellRef, "=\"").concat((0, condition_operand_1.escapeConditionFormulaText)(stripped), "\"");
|
|
221
189
|
// The bare `'` entry means empty text, and a cell storing the lone
|
|
222
190
|
// text-prefix apostrophe is equivalent to a blank cell — accept it too.
|
|
223
191
|
return stripped === ""
|
|
@@ -225,7 +193,9 @@ var buildDataValidationExpression = function (rule, baseCellRef, locale) {
|
|
|
225
193
|
: [textCondition];
|
|
226
194
|
}
|
|
227
195
|
return entry
|
|
228
|
-
? [
|
|
196
|
+
? [
|
|
197
|
+
"".concat(baseCellRef, "=").concat((0, condition_operand_1.normalizeConditionOperand)(entry, condition.type, locale)),
|
|
198
|
+
]
|
|
229
199
|
: [];
|
|
230
200
|
})
|
|
231
201
|
.filter(function (entry) { return Boolean(entry); });
|
|
@@ -246,4 +216,3 @@ var buildDataValidationExpression = function (rule, baseCellRef, locale) {
|
|
|
246
216
|
}
|
|
247
217
|
return undefined;
|
|
248
218
|
};
|
|
249
|
-
exports.buildDataValidationExpression = buildDataValidationExpression;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./colors";
|
|
|
18
18
|
export * from "./formula-refs";
|
|
19
19
|
export * from "./sort";
|
|
20
20
|
export * from "./table-styles";
|
|
21
|
+
export * from "./condition-operand";
|
|
21
22
|
export * from "./conditional-format";
|
|
22
23
|
export * from "./data-validation";
|
|
23
24
|
export * from "./content-hash";
|
|
@@ -26,8 +27,11 @@ export * from "./keyboard";
|
|
|
26
27
|
export * from "./sheet-span";
|
|
27
28
|
export * from "./formula-locale";
|
|
28
29
|
export * from "./table-names";
|
|
30
|
+
export * from "./sheet-names";
|
|
31
|
+
export * from "./sheet-protection";
|
|
29
32
|
export * from "./table-totals";
|
|
30
33
|
export * from "./sparkline-groups";
|
|
31
34
|
export * from "./rich-text-html";
|
|
35
|
+
export * from "./clipboard-html-sanitize";
|
|
32
36
|
export * from "./rich-text-links";
|
|
33
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC"}
|
package/dist/cjs/index.js
CHANGED
|
@@ -21,6 +21,7 @@ tslib_1.__exportStar(require("./colors"), exports);
|
|
|
21
21
|
tslib_1.__exportStar(require("./formula-refs"), exports);
|
|
22
22
|
tslib_1.__exportStar(require("./sort"), exports);
|
|
23
23
|
tslib_1.__exportStar(require("./table-styles"), exports);
|
|
24
|
+
tslib_1.__exportStar(require("./condition-operand"), exports);
|
|
24
25
|
tslib_1.__exportStar(require("./conditional-format"), exports);
|
|
25
26
|
tslib_1.__exportStar(require("./data-validation"), exports);
|
|
26
27
|
tslib_1.__exportStar(require("./content-hash"), exports);
|
|
@@ -29,7 +30,10 @@ tslib_1.__exportStar(require("./keyboard"), exports);
|
|
|
29
30
|
tslib_1.__exportStar(require("./sheet-span"), exports);
|
|
30
31
|
tslib_1.__exportStar(require("./formula-locale"), exports);
|
|
31
32
|
tslib_1.__exportStar(require("./table-names"), exports);
|
|
33
|
+
tslib_1.__exportStar(require("./sheet-names"), exports);
|
|
34
|
+
tslib_1.__exportStar(require("./sheet-protection"), exports);
|
|
32
35
|
tslib_1.__exportStar(require("./table-totals"), exports);
|
|
33
36
|
tslib_1.__exportStar(require("./sparkline-groups"), exports);
|
|
34
37
|
tslib_1.__exportStar(require("./rich-text-html"), exports);
|
|
38
|
+
tslib_1.__exportStar(require("./clipboard-html-sanitize"), exports);
|
|
35
39
|
tslib_1.__exportStar(require("./rich-text-links"), exports);
|
|
@@ -52,6 +52,15 @@ export type ParsedRichText = {
|
|
|
52
52
|
* Parse an HTML clipboard payload into a hidden, mounted sandbox so
|
|
53
53
|
* `getComputedStyle` resolves the payload's own `<style>` rules (Excel's
|
|
54
54
|
* `.fontN` / `.xlNN` classes). Callers must `cleanup()` when done.
|
|
55
|
+
*
|
|
56
|
+
* The payload is untrusted. It is parsed with `DOMParser` (an inert document:
|
|
57
|
+
* no scripts run, no handlers fire, nothing loads), stripped by
|
|
58
|
+
* {@link sanitizeClipboardDocument} — scripts, frames, embeds, SVG/MathML,
|
|
59
|
+
* every `on*` handler, `javascript:`/`data:` links and network-reaching CSS —
|
|
60
|
+
* and only then are the surviving nodes imported into the live sandbox. The
|
|
61
|
+
* import is node-by-node (`importNode`), never a serialize + `innerHTML`
|
|
62
|
+
* round trip, so the parser cannot re-interpret the sanitized tree (mXSS).
|
|
63
|
+
* This sanitization is not optional and runs for every clipboard consumer.
|
|
55
64
|
*/
|
|
56
65
|
export declare const mountClipboardHtml: (clipboardValue: string) => {
|
|
57
66
|
root: HTMLDivElement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-html.d.ts","sourceRoot":"","sources":["../../rich-text-html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"rich-text-html.d.ts","sourceRoot":"","sources":["../../rich-text-html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAO5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,yFAAyF;IACzF,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAgGF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAI,gBAAgB,MAAM;;;CA0BxD,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAC/B,MAAM,GAAG,SAkBX,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,GAAG,IAAI,GAAG,SAAS,KAC/B,MAAM,GAAG,SAoBX,CAAC;AAiTF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,WAAW,KAAG,cA+DzD,CAAC;AAgBF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,WAAW,KAChB,WAAW,GAAG,SAShB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,MAAM,MAAM,KACX,cAAc,GAAG,SAkBnB,CAAC"}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.clipboardHtmlToRichText = exports.findSingleClipboardCell = exports.htmlElementToRichText = exports.cssFontSizeToPt = exports.cssColorToHex = exports.mountClipboardHtml = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
|
+
var clipboard_html_sanitize_1 = require("./clipboard-html-sanitize");
|
|
5
6
|
var color_1 = require("./color");
|
|
6
7
|
var dom_1 = require("./dom");
|
|
8
|
+
var string_1 = require("./string");
|
|
7
9
|
var BLOCK_TAGS = new Set([
|
|
8
10
|
"p",
|
|
9
11
|
"div",
|
|
@@ -90,17 +92,42 @@ var NON_COLORS = new Set([
|
|
|
90
92
|
* Parse an HTML clipboard payload into a hidden, mounted sandbox so
|
|
91
93
|
* `getComputedStyle` resolves the payload's own `<style>` rules (Excel's
|
|
92
94
|
* `.fontN` / `.xlNN` classes). Callers must `cleanup()` when done.
|
|
95
|
+
*
|
|
96
|
+
* The payload is untrusted. It is parsed with `DOMParser` (an inert document:
|
|
97
|
+
* no scripts run, no handlers fire, nothing loads), stripped by
|
|
98
|
+
* {@link sanitizeClipboardDocument} — scripts, frames, embeds, SVG/MathML,
|
|
99
|
+
* every `on*` handler, `javascript:`/`data:` links and network-reaching CSS —
|
|
100
|
+
* and only then are the surviving nodes imported into the live sandbox. The
|
|
101
|
+
* import is node-by-node (`importNode`), never a serialize + `innerHTML`
|
|
102
|
+
* round trip, so the parser cannot re-interpret the sanitized tree (mXSS).
|
|
103
|
+
* This sanitization is not optional and runs for every clipboard consumer.
|
|
93
104
|
*/
|
|
94
105
|
var mountClipboardHtml = function (clipboardValue) {
|
|
95
|
-
var
|
|
106
|
+
var e_1, _a;
|
|
107
|
+
var _b, _c;
|
|
96
108
|
var parser = new DOMParser();
|
|
97
|
-
var doc = parser.parseFromString(clipboardValue, "text/html");
|
|
109
|
+
var doc = (0, clipboard_html_sanitize_1.sanitizeClipboardDocument)(parser.parseFromString(clipboardValue, "text/html"));
|
|
98
110
|
var sandbox = document.createElement("div");
|
|
99
111
|
sandbox.style.cssText =
|
|
100
112
|
"position:fixed;left:-10000px;top:-10000px;width:0;height:0;overflow:hidden;opacity:0;pointer-events:none;font-size:10pt;";
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
var bodyChildren = doc.body ? Array.from(doc.body.childNodes) : [];
|
|
114
|
+
try {
|
|
115
|
+
for (var bodyChildren_1 = tslib_1.__values(bodyChildren), bodyChildren_1_1 = bodyChildren_1.next(); !bodyChildren_1_1.done; bodyChildren_1_1 = bodyChildren_1.next()) {
|
|
116
|
+
var child = bodyChildren_1_1.value;
|
|
117
|
+
sandbox.appendChild(document.importNode(child, true));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
121
|
+
finally {
|
|
122
|
+
try {
|
|
123
|
+
if (bodyChildren_1_1 && !bodyChildren_1_1.done && (_a = bodyChildren_1.return)) _a.call(bodyChildren_1);
|
|
124
|
+
}
|
|
125
|
+
finally { if (e_1) throw e_1.error; }
|
|
126
|
+
}
|
|
127
|
+
var styles = (_c = (_b = doc.head) === null || _b === void 0 ? void 0 : _b.querySelectorAll("style")) !== null && _c !== void 0 ? _c : [];
|
|
128
|
+
styles.forEach(function (style) {
|
|
129
|
+
return sandbox.appendChild(document.importNode(style, true));
|
|
130
|
+
});
|
|
104
131
|
document.body.appendChild(sandbox);
|
|
105
132
|
return {
|
|
106
133
|
root: sandbox,
|
|
@@ -244,8 +271,10 @@ var readElementFormat = function (element, inherited) {
|
|
|
244
271
|
break;
|
|
245
272
|
}
|
|
246
273
|
case "a": {
|
|
274
|
+
// The sanitizer already drops unsafe hrefs from clipboard payloads;
|
|
275
|
+
// this converter also serves non-clipboard callers, so re-check here.
|
|
247
276
|
var href = element.getAttribute("href");
|
|
248
|
-
if (href && href.trim()) {
|
|
277
|
+
if (href && href.trim() && (0, string_1.isSafeHyperlinkUri)(href)) {
|
|
249
278
|
format.link = { uri: href.trim() };
|
|
250
279
|
}
|
|
251
280
|
break;
|
|
@@ -318,7 +347,7 @@ var isBlockElement = function (element) {
|
|
|
318
347
|
return display === "block" || display === "list-item";
|
|
319
348
|
};
|
|
320
349
|
var collectSegments = function (node, inherited, segments) {
|
|
321
|
-
var
|
|
350
|
+
var e_2, _a;
|
|
322
351
|
var _b;
|
|
323
352
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
324
353
|
var text = normalizeWhitespace((_b = node.textContent) !== null && _b !== void 0 ? _b : "");
|
|
@@ -350,12 +379,12 @@ var collectSegments = function (node, inherited, segments) {
|
|
|
350
379
|
collectSegments(child, format, segments);
|
|
351
380
|
}
|
|
352
381
|
}
|
|
353
|
-
catch (
|
|
382
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
354
383
|
finally {
|
|
355
384
|
try {
|
|
356
385
|
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
357
386
|
}
|
|
358
|
-
finally { if (
|
|
387
|
+
finally { if (e_2) throw e_2.error; }
|
|
359
388
|
}
|
|
360
389
|
if (block && segments.length && !endsWithNewline(segments)) {
|
|
361
390
|
segments.push({ text: "\n", format: inherited });
|
|
@@ -377,7 +406,7 @@ var TEXT_KEYS = [
|
|
|
377
406
|
];
|
|
378
407
|
/** The properties of `format` that differ from `base` (the run's own contribution). */
|
|
379
408
|
var diffFormat = function (format, base) {
|
|
380
|
-
var
|
|
409
|
+
var e_3, _a;
|
|
381
410
|
var diff = {};
|
|
382
411
|
try {
|
|
383
412
|
for (var TEXT_KEYS_1 = tslib_1.__values(TEXT_KEYS), TEXT_KEYS_1_1 = TEXT_KEYS_1.next(); !TEXT_KEYS_1_1.done; TEXT_KEYS_1_1 = TEXT_KEYS_1.next()) {
|
|
@@ -407,17 +436,17 @@ var diffFormat = function (format, base) {
|
|
|
407
436
|
diff[key] = normalizedValue;
|
|
408
437
|
}
|
|
409
438
|
}
|
|
410
|
-
catch (
|
|
439
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
411
440
|
finally {
|
|
412
441
|
try {
|
|
413
442
|
if (TEXT_KEYS_1_1 && !TEXT_KEYS_1_1.done && (_a = TEXT_KEYS_1.return)) _a.call(TEXT_KEYS_1);
|
|
414
443
|
}
|
|
415
|
-
finally { if (
|
|
444
|
+
finally { if (e_3) throw e_3.error; }
|
|
416
445
|
}
|
|
417
446
|
return diff;
|
|
418
447
|
};
|
|
419
448
|
var sameFormat = function (a, b) {
|
|
420
|
-
var
|
|
449
|
+
var e_4, _a;
|
|
421
450
|
var keys = new Set(tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(Object.keys(a)), false), tslib_1.__read(Object.keys(b)), false));
|
|
422
451
|
try {
|
|
423
452
|
for (var keys_1 = tslib_1.__values(keys), keys_1_1 = keys_1.next(); !keys_1_1.done; keys_1_1 = keys_1.next()) {
|
|
@@ -435,12 +464,12 @@ var sameFormat = function (a, b) {
|
|
|
435
464
|
}
|
|
436
465
|
}
|
|
437
466
|
}
|
|
438
|
-
catch (
|
|
467
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
439
468
|
finally {
|
|
440
469
|
try {
|
|
441
470
|
if (keys_1_1 && !keys_1_1.done && (_a = keys_1.return)) _a.call(keys_1);
|
|
442
471
|
}
|
|
443
|
-
finally { if (
|
|
472
|
+
finally { if (e_4) throw e_4.error; }
|
|
444
473
|
}
|
|
445
474
|
return true;
|
|
446
475
|
};
|
|
@@ -452,7 +481,7 @@ var isEmptyFormat = function (format) {
|
|
|
452
481
|
* table-less HTML) into the engine's rich-text shape. See the module doc.
|
|
453
482
|
*/
|
|
454
483
|
var htmlElementToRichText = function (root) {
|
|
455
|
-
var
|
|
484
|
+
var e_5, _a, e_6, _b;
|
|
456
485
|
var _c, _d, _e;
|
|
457
486
|
var baseFormat = normalizeBase(readElementFormat(root, {}));
|
|
458
487
|
var segments = [];
|
|
@@ -462,12 +491,12 @@ var htmlElementToRichText = function (root) {
|
|
|
462
491
|
collectSegments(child, baseFormat, segments);
|
|
463
492
|
}
|
|
464
493
|
}
|
|
465
|
-
catch (
|
|
494
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
466
495
|
finally {
|
|
467
496
|
try {
|
|
468
497
|
if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
|
|
469
498
|
}
|
|
470
|
-
finally { if (
|
|
499
|
+
finally { if (e_5) throw e_5.error; }
|
|
471
500
|
}
|
|
472
501
|
// Trim outer whitespace (leading/trailing spaces and line breaks) so the
|
|
473
502
|
// text matches `extractTextContent`, shifting the segments with it.
|
|
@@ -512,12 +541,12 @@ var htmlElementToRichText = function (root) {
|
|
|
512
541
|
runs.push({ startIndex: start, endIndex: end, format: format, nodeType: "text" });
|
|
513
542
|
}
|
|
514
543
|
}
|
|
515
|
-
catch (
|
|
544
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
516
545
|
finally {
|
|
517
546
|
try {
|
|
518
547
|
if (segments_1_1 && !segments_1_1.done && (_b = segments_1.return)) _b.call(segments_1);
|
|
519
548
|
}
|
|
520
|
-
finally { if (
|
|
549
|
+
finally { if (e_6) throw e_6.error; }
|
|
521
550
|
}
|
|
522
551
|
var wholeTextLink = links.find(function (link) { return link.startIndex === 0 && link.endIndex === text.length; });
|
|
523
552
|
var hyperlink = (_d = wholeTextLink === null || wholeTextLink === void 0 ? void 0 : wholeTextLink.uri) !== null && _d !== void 0 ? _d : (_e = links[0]) === null || _e === void 0 ? void 0 : _e.uri;
|
|
@@ -534,7 +563,7 @@ exports.htmlElementToRichText = htmlElementToRichText;
|
|
|
534
563
|
// The base drops the segment-only `link` key and `false`/`null` values — a
|
|
535
564
|
// container can't be "not bold" more than the default already is.
|
|
536
565
|
var normalizeBase = function (format) {
|
|
537
|
-
var
|
|
566
|
+
var e_7, _a;
|
|
538
567
|
var base = {};
|
|
539
568
|
try {
|
|
540
569
|
for (var TEXT_KEYS_2 = tslib_1.__values(TEXT_KEYS), TEXT_KEYS_2_1 = TEXT_KEYS_2.next(); !TEXT_KEYS_2_1.done; TEXT_KEYS_2_1 = TEXT_KEYS_2.next()) {
|
|
@@ -546,12 +575,12 @@ var normalizeBase = function (format) {
|
|
|
546
575
|
base[key] = value;
|
|
547
576
|
}
|
|
548
577
|
}
|
|
549
|
-
catch (
|
|
578
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
550
579
|
finally {
|
|
551
580
|
try {
|
|
552
581
|
if (TEXT_KEYS_2_1 && !TEXT_KEYS_2_1.done && (_a = TEXT_KEYS_2.return)) _a.call(TEXT_KEYS_2);
|
|
553
582
|
}
|
|
554
|
-
finally { if (
|
|
583
|
+
finally { if (e_7) throw e_7.error; }
|
|
555
584
|
}
|
|
556
585
|
return base;
|
|
557
586
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel compares worksheet names case-insensitively: `SHEET1` beside `Sheet1` is refused with
|
|
3
|
+
* "That name is already taken. Try a different one." The calc engine's sheet registry folds names
|
|
4
|
+
* the same way, so two sheets differing only by case would alias one engine sheet — every write to
|
|
5
|
+
* the second lands in the first and `=SHEET1!A1` resolves to whichever was registered first
|
|
6
|
+
* (Athena-Intel/demo-app-monorepo#37167 UI2-1). Both engines' rename / create / duplicate paths and
|
|
7
|
+
* the sheet tabs compare titles through these helpers (the rnc store mirrors them in Rust and
|
|
8
|
+
* surfaces the same message through its `rejections` channel).
|
|
9
|
+
*/
|
|
10
|
+
export declare const SHEET_NAME_TAKEN_MESSAGE = "That name is already taken. Try a different one.";
|
|
11
|
+
/** The identity two worksheet titles are compared under: trimmed, Unicode-lowercased. */
|
|
12
|
+
export declare const sheetTitleKey: (title: string) => string;
|
|
13
|
+
/** Whether `a` and `b` name the same worksheet under Excel's comparison. */
|
|
14
|
+
export declare const isSameSheetTitle: (a: string, b: string) => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Whether another sheet (any but `exceptSheetId`) already holds `title`, case-insensitively. A
|
|
17
|
+
* sheet may always re-case its own title (`Sheet1` → `SHEET1` in place is not a collision).
|
|
18
|
+
*/
|
|
19
|
+
export declare const isSheetTitleTaken: (title: string, sheets: Iterable<{
|
|
20
|
+
sheetId: number;
|
|
21
|
+
title: string;
|
|
22
|
+
}>, exceptSheetId?: number) => boolean;
|
|
23
|
+
/**
|
|
24
|
+
* `base` if no title in `existing` holds it (case-insensitively), else the first free
|
|
25
|
+
* `base 1`, `base 2`, … — the suffix scheme the JS engine uses for a new / duplicated sheet.
|
|
26
|
+
* Every candidate stays within `maxLength` (the suffix wins over the tail of a long base).
|
|
27
|
+
*/
|
|
28
|
+
export declare const uniqueSheetTitle: (base: string, existing: Iterable<string>, maxLength?: number) => string;
|
|
29
|
+
/** Excel's ceiling for a worksheet name, in UTF-16 code units (what `String#length` counts). */
|
|
30
|
+
export declare const SHEET_NAME_MAX_LENGTH = 31;
|
|
31
|
+
/** The characters Excel never allows anywhere in a worksheet name: `\ / ? * [ ] :`. */
|
|
32
|
+
export declare const SHEET_NAME_ILLEGAL_CHARACTERS: readonly ["\\", "/", "?", "*", "[", "]", ":"];
|
|
33
|
+
/** Excel reserves this title for its change-history sheet (compared case-insensitively). */
|
|
34
|
+
export declare const SHEET_NAME_RESERVED_WORD = "History";
|
|
35
|
+
/**
|
|
36
|
+
* Excel 365's rename-refusal dialog, verbatim — one message for every rule except the duplicate
|
|
37
|
+
* title, which has its own ({@link SHEET_NAME_TAKEN_MESSAGE}).
|
|
38
|
+
*/
|
|
39
|
+
export declare const SHEET_NAME_INVALID_MESSAGE: string;
|
|
40
|
+
/** Why a worksheet name was refused — a stable key hosts may branch on; `message` is the dialog text. */
|
|
41
|
+
export type SheetNameRejectionReason = "blank" | "too-long" | "illegal-character" | "apostrophe" | "reserved" | "taken";
|
|
42
|
+
export type SheetNameValidation = {
|
|
43
|
+
ok: true;
|
|
44
|
+
} | {
|
|
45
|
+
ok: false;
|
|
46
|
+
reason: SheetNameRejectionReason;
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Excel's worksheet-name rules, in the order its dialog lists them: not blank, at most 31 characters,
|
|
51
|
+
* none of `\ / ? * [ ] :`, no leading or trailing apostrophe, not `History`, and unique among
|
|
52
|
+
* `existing` compared case-insensitively (a sheet may always re-case its own title — pass its id as
|
|
53
|
+
* `exceptSheetId`). Both engines' rename / create paths and the sheet tabs validate through this one
|
|
54
|
+
* function (the rnc store mirrors it in Rust and surfaces the same messages via its `rejections`).
|
|
55
|
+
*/
|
|
56
|
+
export declare const validateSheetName: (name: string, existing?: Iterable<{
|
|
57
|
+
sheetId: number;
|
|
58
|
+
title: string;
|
|
59
|
+
} | string>, exceptSheetId?: number) => SheetNameValidation;
|
|
60
|
+
//# sourceMappingURL=sheet-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheet-names.d.ts","sourceRoot":"","sources":["../../sheet-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,wBAAwB,qDACe,CAAC;AAErD,yFAAyF;AACzF,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MAClB,CAAC;AAE7B,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OACjB,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,QAAQ,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EACpD,gBAAgB,MAAM,KACrB,OAQF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,UAAU,QAAQ,CAAC,MAAM,CAAC,EAC1B,kBAAoC,KACnC,MAgBF,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,uFAAuF;AACvF,eAAO,MAAM,6BAA6B,+CAAgD,CAAC;AAG3F,4FAA4F;AAC5F,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAO3B,CAAC;AAEb,yGAAyG;AACzG,MAAM,MAAM,wBAAwB,GAChC,OAAO,GACP,UAAU,GACV,mBAAmB,GACnB,YAAY,GACZ,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,WAAU,QAAQ,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAM,EACpE,gBAAgB,MAAM,KACrB,mBAgCF,CAAC"}
|
|
@@ -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"}
|