@shbernal/ts-xlsx 2.0.0 → 2.1.0
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/README.md +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +26 -4
- package/dist/core/address.js +21 -9
- package/dist/core/autofilter.d.ts +11 -0
- package/dist/core/autofilter.js +35 -10
- package/dist/core/color-resolution.d.ts +1 -1
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/conditional-formatting-overlay.d.ts +5 -0
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +45 -5
- package/dist/core/conditional-formatting.js +88 -0
- package/dist/core/data-validation-overlay.d.ts +11 -0
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +9 -2
- package/dist/core/data-validation.js +34 -0
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +26 -8
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +2 -0
- package/dist/core/image.js +4 -0
- package/dist/core/merge.d.ts +12 -1
- package/dist/core/merge.js +36 -2
- package/dist/core/page-setup.d.ts +16 -2
- package/dist/core/page-setup.js +8 -1
- package/dist/core/pivot-table.d.ts +2 -0
- package/dist/core/pivot-table.js +25 -16
- package/dist/core/protection.d.ts +3 -0
- package/dist/core/protection.js +20 -14
- package/dist/core/row-input.d.ts +7 -0
- package/dist/core/row-input.js +10 -8
- package/dist/core/style.d.ts +27 -0
- package/dist/core/style.js +82 -67
- package/dist/core/table.d.ts +25 -11
- package/dist/core/table.js +67 -31
- package/dist/core/theme.d.ts +3 -1
- package/dist/core/theme.js +4 -0
- package/dist/core/workbook.d.ts +2 -2
- package/dist/core/worksheet-comments.d.ts +8 -0
- package/dist/core/worksheet-comments.js +18 -0
- package/dist/core/worksheet.d.ts +15 -2
- package/dist/core/worksheet.js +28 -48
- package/dist/customui/ribbon.js +24 -23
- package/dist/entries/core.d.ts +3 -3
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/xlsx.d.ts +0 -1
- package/dist/entries/xlsx.js +0 -1
- package/dist/io/csv/read.js +1 -1
- package/dist/io/csv/write.d.ts +12 -2
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/inflate.js +2 -12
- package/dist/io/opc/rels.d.ts +11 -0
- package/dist/io/opc/rels.js +3 -2
- package/dist/io/style/xf-style.d.ts +7 -2
- package/dist/io/style/xf-style.js +6 -1
- package/dist/io/xlsx/cell-accumulator.d.ts +1 -3
- package/dist/io/xlsx/cell-accumulator.js +20 -58
- package/dist/io/xlsx/cell-value.d.ts +1 -1
- package/dist/io/xlsx/cell-value.js +7 -4
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +7 -6
- package/dist/io/xlsx/comments.js +4 -2
- package/dist/io/xlsx/conditional-formatting.d.ts +1 -1
- package/dist/io/xlsx/conditional-formatting.js +60 -45
- package/dist/io/xlsx/data-validation.d.ts +1 -1
- package/dist/io/xlsx/data-validation.js +35 -33
- package/dist/io/xlsx/hyperlinks.js +6 -14
- package/dist/io/xlsx/images.js +19 -22
- package/dist/io/xlsx/read-pivot.js +2 -8
- package/dist/io/xlsx/read-shared-strings.js +8 -47
- package/dist/io/xlsx/read-styles.js +2 -1
- package/dist/io/xlsx/read-worksheet.js +14 -16
- package/dist/io/xlsx/read.d.ts +1 -1
- package/dist/io/xlsx/read.js +44 -74
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +94 -30
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +13 -9
- package/dist/io/xlsx/styles.d.ts +3 -15
- package/dist/io/xlsx/styles.js +63 -88
- package/dist/io/xlsx/tables.js +13 -13
- package/dist/io/xlsx/theme-xml.js +7 -4
- package/dist/io/xlsx/threaded-comments.js +19 -19
- package/dist/io/xlsx/workbook-xml.js +14 -8
- package/dist/io/xlsx/worksheet-xml.js +34 -37
- package/dist/io/xlsx/write-stream.d.ts +8 -1
- package/dist/io/xlsx/write-stream.js +10 -11
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/vba/bytes.d.ts +1 -2
- package/dist/vba/bytes.js +1 -12
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/project-editor.js +5 -34
- package/dist/vba/project.js +2 -18
- package/dist/xml/xml-read.d.ts +60 -0
- package/dist/xml/xml-read.js +47 -0
- package/dist/xml/xml.d.ts +43 -1
- package/dist/xml/xml.js +17 -5
- package/package.json +21 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, isCfTimePeriod, } from '../../core/conditional-formatting.js';
|
|
2
|
+
import { boolPresent, boolStrict, coerceNumericLiteral, enumToken, localName, numFinite, numInteger, parseXml, TextCapture, } from '../../xml/xml-read.js';
|
|
3
|
+
import { boolAttr, checkedToken, escapeAttr, escapeText, numberText, stripFormulaEquals, textAttr, } from '../../xml/xml.js';
|
|
3
4
|
import { colorAttrs, parseColor } from './color-xml.js';
|
|
4
5
|
import { CF_EXT_URI, DATABAR_LINK_EXT_URI, XM_NS } from './namespaces.js';
|
|
5
6
|
import { x14Ext } from './x14-ext.js';
|
|
@@ -61,7 +62,7 @@ function x14DataBarXml(ref, rule, guid) {
|
|
|
61
62
|
`</x14:cfRule><xm:sqref>${escapeText(ref)}</xm:sqref></x14:conditionalFormatting>`);
|
|
62
63
|
}
|
|
63
64
|
function x14CfvoXml(cfvo) {
|
|
64
|
-
const type =
|
|
65
|
+
const type = checkedToken(cfvo.type, isCfValueObjectType, 'conditional format value type');
|
|
65
66
|
if (cfvo.value === undefined)
|
|
66
67
|
return `<x14:cfvo type="${type}"/>`;
|
|
67
68
|
return `<x14:cfvo type="${type}"><xm:f>${escapeText(String(cfvo.value))}</xm:f></x14:cfvo>`;
|
|
@@ -71,12 +72,16 @@ function cfRuleExtLinkXml(guid) {
|
|
|
71
72
|
}
|
|
72
73
|
function blockXml(cf, styles, priority, extLinks) {
|
|
73
74
|
const rules = cf.rules.map((rule) => ruleXml(rule, styles, priority, extLinks)).join('');
|
|
75
|
+
if (rules === '')
|
|
76
|
+
return '';
|
|
74
77
|
return `<conditionalFormatting sqref="${escapeAttr(cf.ref)}">${rules}</conditionalFormatting>`;
|
|
75
78
|
}
|
|
76
79
|
function ruleXml(rule, styles, priority, extLinks) {
|
|
77
80
|
const p = rule.priority ?? priority.next;
|
|
78
81
|
priority.next = Math.max(priority.next, p) + 1;
|
|
79
|
-
const attrs = [
|
|
82
|
+
const attrs = [
|
|
83
|
+
`type="${checkedToken(rule.type, isConditionalFormattingType, 'conditional formatting type')}"`,
|
|
84
|
+
];
|
|
80
85
|
const dxfId = resolveDxfId(rule, styles);
|
|
81
86
|
if (dxfId !== undefined)
|
|
82
87
|
attrs.push(`dxfId="${dxfId}"`);
|
|
@@ -91,16 +96,18 @@ function ruleXml(rule, styles, priority, extLinks) {
|
|
|
91
96
|
attrs.push('bottom="1"');
|
|
92
97
|
if (rule.percent)
|
|
93
98
|
attrs.push('percent="1"');
|
|
94
|
-
if (rule.operator !== undefined)
|
|
95
|
-
attrs.push(`operator="${
|
|
99
|
+
if (rule.operator !== undefined) {
|
|
100
|
+
attrs.push(`operator="${checkedToken(rule.operator, isConditionalFormattingOperator, 'conditional formatting operator')}"`);
|
|
101
|
+
}
|
|
96
102
|
if (rule.text !== undefined)
|
|
97
103
|
attrs.push(`text="${escapeAttr(rule.text)}"`);
|
|
98
|
-
if (rule.timePeriod !== undefined)
|
|
99
|
-
attrs.push(`timePeriod="${
|
|
104
|
+
if (rule.timePeriod !== undefined) {
|
|
105
|
+
attrs.push(`timePeriod="${checkedToken(rule.timePeriod, isCfTimePeriod, 'time period')}"`);
|
|
106
|
+
}
|
|
100
107
|
if (rule.rank !== undefined)
|
|
101
|
-
attrs.push(`rank="${rule.rank}"`);
|
|
108
|
+
attrs.push(`rank="${numberText(rule.rank)}"`);
|
|
102
109
|
if (rule.stdDev !== undefined)
|
|
103
|
-
attrs.push(`stdDev="${rule.stdDev}"`);
|
|
110
|
+
attrs.push(`stdDev="${numberText(rule.stdDev)}"`);
|
|
104
111
|
let body = SCALE_TYPES.has(rule.type) ? scaleXml(rule) : formulaeXml(rule.formulae);
|
|
105
112
|
const extGuid = extLinks.get(rule);
|
|
106
113
|
if (extGuid !== undefined)
|
|
@@ -112,8 +119,9 @@ function ruleXml(rule, styles, priority, extLinks) {
|
|
|
112
119
|
function resolveDxfId(rule, styles) {
|
|
113
120
|
if (SCALE_TYPES.has(rule.type))
|
|
114
121
|
return undefined;
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
const preserved = dxfIndex(rule.dxfId);
|
|
123
|
+
if (preserved !== undefined)
|
|
124
|
+
return preserved;
|
|
117
125
|
if (rule.style !== undefined)
|
|
118
126
|
return styles.differentialStyleId(rule.style);
|
|
119
127
|
return undefined;
|
|
@@ -142,34 +150,34 @@ function colorScaleXml(rule) {
|
|
|
142
150
|
return `<colorScale>${anchors}${colors}</colorScale>`;
|
|
143
151
|
}
|
|
144
152
|
function iconSetXml(rule) {
|
|
145
|
-
const name = rule.iconSet
|
|
153
|
+
const name = rule.iconSet === undefined
|
|
154
|
+
? ''
|
|
155
|
+
: ` iconSet="${checkedToken(rule.iconSet, isIconSetType, 'icon set')}"`;
|
|
146
156
|
const anchors = (rule.cfvo ?? []).map(cfvoXml).join('');
|
|
147
157
|
return `<iconSet${name}>${anchors}</iconSet>`;
|
|
148
158
|
}
|
|
149
159
|
function cfvoXml(cfvo) {
|
|
150
|
-
const
|
|
151
|
-
|
|
160
|
+
const type = checkedToken(cfvo.type, isCfValueObjectType, 'conditional format value type');
|
|
161
|
+
const val = cfvo.value === undefined ? '' : textAttr('val', String(cfvo.value));
|
|
162
|
+
return `<cfvo type="${type}"${val}/>`;
|
|
152
163
|
}
|
|
153
164
|
export function parseConditionalFormattings(xml) {
|
|
154
165
|
const blocks = [];
|
|
155
166
|
let block;
|
|
156
167
|
let draft;
|
|
157
168
|
let scale;
|
|
158
|
-
|
|
159
|
-
let formulaText = '';
|
|
169
|
+
const formulaCapture = new TextCapture('formula');
|
|
160
170
|
const linked = [];
|
|
161
171
|
const extById = new Map();
|
|
162
172
|
let x14Ext;
|
|
163
173
|
let x14ExtId;
|
|
164
|
-
|
|
165
|
-
let x14IdText = '';
|
|
174
|
+
const x14IdCapture = new TextCapture('id');
|
|
166
175
|
parseXml(xml, {
|
|
167
176
|
onOpen(name, attrs, selfClosing) {
|
|
168
177
|
const ln = localName(name);
|
|
169
178
|
if (name.includes(':')) {
|
|
170
179
|
if (ln === 'id' && draft !== undefined) {
|
|
171
|
-
|
|
172
|
-
x14IdText = '';
|
|
180
|
+
x14IdCapture.open(ln, selfClosing);
|
|
173
181
|
}
|
|
174
182
|
else if (ln === 'cfRule') {
|
|
175
183
|
x14Ext = attrs.type === 'dataBar' && attrs.id !== undefined ? emptyExt() : undefined;
|
|
@@ -191,7 +199,9 @@ export function parseConditionalFormattings(xml) {
|
|
|
191
199
|
}
|
|
192
200
|
else if (ln === 'cfRule' && block !== undefined) {
|
|
193
201
|
if (selfClosing) {
|
|
194
|
-
|
|
202
|
+
const rule = finalizeRule(newDraft(attrs));
|
|
203
|
+
if (rule !== undefined)
|
|
204
|
+
block.rules.push(rule);
|
|
195
205
|
}
|
|
196
206
|
else {
|
|
197
207
|
draft = newDraft(attrs);
|
|
@@ -201,8 +211,8 @@ export function parseConditionalFormattings(xml) {
|
|
|
201
211
|
else if (draft !== undefined &&
|
|
202
212
|
(ln === 'dataBar' || ln === 'colorScale' || ln === 'iconSet')) {
|
|
203
213
|
scale = ln;
|
|
204
|
-
if (ln === 'iconSet'
|
|
205
|
-
draft.iconSet = attrs.iconSet;
|
|
214
|
+
if (ln === 'iconSet')
|
|
215
|
+
draft.iconSet = enumToken(attrs.iconSet, isIconSetType);
|
|
206
216
|
}
|
|
207
217
|
else if (draft !== undefined && ln === 'cfvo') {
|
|
208
218
|
draft.cfvo.push(parseCfvo(attrs));
|
|
@@ -215,23 +225,20 @@ export function parseConditionalFormattings(xml) {
|
|
|
215
225
|
draft.colors.push(color);
|
|
216
226
|
}
|
|
217
227
|
else if (draft !== undefined && ln === 'formula') {
|
|
218
|
-
|
|
219
|
-
formulaText = '';
|
|
228
|
+
formulaCapture.open(ln, selfClosing);
|
|
220
229
|
}
|
|
221
230
|
},
|
|
222
231
|
onText(chunk) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (capturingX14Id)
|
|
226
|
-
x14IdText += chunk;
|
|
232
|
+
formulaCapture.text(chunk);
|
|
233
|
+
x14IdCapture.text(chunk);
|
|
227
234
|
},
|
|
228
235
|
onClose(name) {
|
|
229
236
|
const ln = localName(name);
|
|
230
237
|
if (name.includes(':')) {
|
|
231
|
-
|
|
238
|
+
const id = x14IdCapture.close(ln);
|
|
239
|
+
if (id !== undefined) {
|
|
232
240
|
if (draft !== undefined)
|
|
233
|
-
draft.x14Id =
|
|
234
|
-
capturingX14Id = false;
|
|
241
|
+
draft.x14Id = id;
|
|
235
242
|
}
|
|
236
243
|
else if (ln === 'cfRule' && x14Ext !== undefined && x14ExtId !== undefined) {
|
|
237
244
|
extById.set(x14ExtId, x14Ext);
|
|
@@ -240,20 +247,22 @@ export function parseConditionalFormattings(xml) {
|
|
|
240
247
|
}
|
|
241
248
|
return;
|
|
242
249
|
}
|
|
243
|
-
|
|
250
|
+
const formula = formulaCapture.close(ln);
|
|
251
|
+
if (formula !== undefined) {
|
|
244
252
|
if (draft !== undefined)
|
|
245
|
-
draft.formulae.push(coerceNumericLiteral(
|
|
246
|
-
capturingFormula = false;
|
|
253
|
+
draft.formulae.push(coerceNumericLiteral(formula));
|
|
247
254
|
}
|
|
248
255
|
else if (ln === 'dataBar' || ln === 'colorScale' || ln === 'iconSet') {
|
|
249
256
|
scale = undefined;
|
|
250
257
|
}
|
|
251
258
|
else if (ln === 'cfRule' && draft !== undefined) {
|
|
252
259
|
const rule = finalizeRule(draft);
|
|
253
|
-
if (
|
|
254
|
-
block
|
|
255
|
-
|
|
256
|
-
|
|
260
|
+
if (rule !== undefined) {
|
|
261
|
+
if (block !== undefined)
|
|
262
|
+
block.rules.push(rule);
|
|
263
|
+
if (draft.x14Id !== undefined)
|
|
264
|
+
linked.push({ rule, id: draft.x14Id });
|
|
265
|
+
}
|
|
257
266
|
draft = undefined;
|
|
258
267
|
}
|
|
259
268
|
else if (ln === 'conditionalFormatting' && block !== undefined) {
|
|
@@ -287,12 +296,12 @@ export function parseDxfs(stylesXml) {
|
|
|
287
296
|
}
|
|
288
297
|
function newDraft(attrs) {
|
|
289
298
|
return {
|
|
290
|
-
type: attrs.type
|
|
299
|
+
type: enumToken(attrs.type, isConditionalFormattingType),
|
|
291
300
|
priority: numFinite(attrs.priority),
|
|
292
301
|
stopIfTrue: boolStrict(attrs.stopIfTrue),
|
|
293
|
-
operator: attrs.operator,
|
|
302
|
+
operator: enumToken(attrs.operator, isConditionalFormattingOperator),
|
|
294
303
|
text: attrs.text,
|
|
295
|
-
timePeriod: attrs.timePeriod,
|
|
304
|
+
timePeriod: enumToken(attrs.timePeriod, isCfTimePeriod),
|
|
296
305
|
rank: numFinite(attrs.rank),
|
|
297
306
|
stdDev: numFinite(attrs.stdDev),
|
|
298
307
|
percent: boolStrict(attrs.percent),
|
|
@@ -308,10 +317,15 @@ function newDraft(attrs) {
|
|
|
308
317
|
x14Id: undefined,
|
|
309
318
|
};
|
|
310
319
|
}
|
|
320
|
+
function dxfIndex(value) {
|
|
321
|
+
return numInteger(value, 0);
|
|
322
|
+
}
|
|
311
323
|
function parseIndexAttr(value) {
|
|
312
|
-
return
|
|
324
|
+
return dxfIndex(value) === undefined ? undefined : value;
|
|
313
325
|
}
|
|
314
326
|
function finalizeRule(draft) {
|
|
327
|
+
if (draft.type === undefined)
|
|
328
|
+
return undefined;
|
|
315
329
|
const rule = { type: draft.type };
|
|
316
330
|
if (draft.priority !== undefined)
|
|
317
331
|
rule.priority = draft.priority;
|
|
@@ -350,7 +364,8 @@ function finalizeRule(draft) {
|
|
|
350
364
|
return rule;
|
|
351
365
|
}
|
|
352
366
|
function parseCfvo(attrs) {
|
|
353
|
-
const
|
|
367
|
+
const raw = attrs.type;
|
|
368
|
+
const type = raw !== undefined && isCfValueObjectType(raw) ? raw : 'num';
|
|
354
369
|
const cfvo = { type };
|
|
355
370
|
if (attrs.val !== undefined) {
|
|
356
371
|
cfvo.value = type === 'formula' ? attrs.val : coerceNumericLiteral(attrs.val);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DataValidationEntry } from '../../core/data-validation.ts';
|
|
2
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
3
|
/** The standard `<dataValidations>` element for the rules stored in the legacy form, or '' when the
|
|
4
4
|
* sheet has none of them, so a sheet with only extended (or no) validations stays byte-clean here.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
|
|
2
|
+
import { decodeSqrefRects } from '../../core/merge.js';
|
|
3
|
+
import { boolStrict, coerceNumericLiteral, localName, parseXml, TextCapture, } from '../../xml/xml-read.js';
|
|
4
|
+
import { checkedToken, escapeAttr, escapeText, stripFormulaEquals, textAttr } from '../../xml/xml.js';
|
|
3
5
|
import { DATA_VALIDATION_EXT_URI, XM_NS } from './namespaces.js';
|
|
4
6
|
import { x14Ext } from './x14-ext.js';
|
|
5
7
|
const TYPED = new Set(['whole', 'decimal', 'date', 'time', 'textLength']);
|
|
@@ -18,16 +20,20 @@ export function dataValidationsExtXml(entries) {
|
|
|
18
20
|
return x14Ext(DATA_VALIDATION_EXT_URI, `<x14:dataValidations count="${extended.length}" xmlns:xm="${XM_NS}">${items}</x14:dataValidations>`);
|
|
19
21
|
}
|
|
20
22
|
function ruleAttrs(rule) {
|
|
21
|
-
return (` type="${rule.type}"` +
|
|
22
|
-
(rule.errorStyle
|
|
23
|
-
|
|
23
|
+
return (` type="${checkedToken(rule.type, isDataValidationType, 'data validation type')}"` +
|
|
24
|
+
(rule.errorStyle === undefined
|
|
25
|
+
? ''
|
|
26
|
+
: ` errorStyle="${checkedToken(rule.errorStyle, isDataValidationErrorStyle, 'data validation error style')}"`) +
|
|
27
|
+
(rule.operator === undefined
|
|
28
|
+
? ''
|
|
29
|
+
: ` operator="${checkedToken(rule.operator, isDataValidationOperator, 'data validation operator')}"`) +
|
|
24
30
|
(rule.allowBlank ? ' allowBlank="1"' : '') +
|
|
25
31
|
(rule.showInputMessage ? ' showInputMessage="1"' : '') +
|
|
26
32
|
(rule.showErrorMessage ? ' showErrorMessage="1"' : '') +
|
|
27
|
-
(
|
|
28
|
-
(
|
|
29
|
-
(
|
|
30
|
-
(
|
|
33
|
+
textAttr('errorTitle', rule.errorTitle) +
|
|
34
|
+
textAttr('error', rule.error) +
|
|
35
|
+
textAttr('promptTitle', rule.promptTitle) +
|
|
36
|
+
textAttr('prompt', rule.prompt));
|
|
31
37
|
}
|
|
32
38
|
function dataValidationXml(sqref, rule) {
|
|
33
39
|
const [f1, f2] = operands(rule);
|
|
@@ -100,11 +106,12 @@ function buildEntry(attrs, formulae) {
|
|
|
100
106
|
}
|
|
101
107
|
function buildRule(attrs, formulae) {
|
|
102
108
|
const { type } = attrs;
|
|
103
|
-
if (type === undefined)
|
|
109
|
+
if (type === undefined || !isDataValidationType(type))
|
|
104
110
|
return undefined;
|
|
105
|
-
const rule = { type
|
|
111
|
+
const rule = { type };
|
|
106
112
|
if (attrs.operator !== undefined) {
|
|
107
|
-
|
|
113
|
+
if (isDataValidationOperator(attrs.operator))
|
|
114
|
+
rule.operator = attrs.operator;
|
|
108
115
|
}
|
|
109
116
|
else if (TYPED.has(type)) {
|
|
110
117
|
rule.operator = 'between';
|
|
@@ -115,7 +122,7 @@ function buildRule(attrs, formulae) {
|
|
|
115
122
|
rule.showInputMessage = true;
|
|
116
123
|
if (boolStrict(attrs.showErrorMessage))
|
|
117
124
|
rule.showErrorMessage = true;
|
|
118
|
-
if (attrs.errorStyle !== undefined)
|
|
125
|
+
if (attrs.errorStyle !== undefined && isDataValidationErrorStyle(attrs.errorStyle))
|
|
119
126
|
rule.errorStyle = attrs.errorStyle;
|
|
120
127
|
if (attrs.error !== undefined)
|
|
121
128
|
rule.error = attrs.error;
|
|
@@ -136,10 +143,9 @@ export function parseExtendedDataValidations(xml) {
|
|
|
136
143
|
const entries = [];
|
|
137
144
|
let current;
|
|
138
145
|
let slot;
|
|
139
|
-
|
|
140
|
-
let text = '';
|
|
146
|
+
const capture = new TextCapture(['f', 'sqref']);
|
|
141
147
|
parseXml(xml, {
|
|
142
|
-
onOpen(name, attrs) {
|
|
148
|
+
onOpen(name, attrs, selfClosing) {
|
|
143
149
|
const ln = localName(name);
|
|
144
150
|
const prefixed = name.includes(':');
|
|
145
151
|
if (ln === 'dataValidation' && prefixed) {
|
|
@@ -151,30 +157,24 @@ export function parseExtendedDataValidations(xml) {
|
|
|
151
157
|
else if (current !== undefined && prefixed && ln === 'formula2') {
|
|
152
158
|
slot = 1;
|
|
153
159
|
}
|
|
154
|
-
else if (current !== undefined
|
|
155
|
-
capture
|
|
156
|
-
text = '';
|
|
157
|
-
}
|
|
158
|
-
else if (current !== undefined && ln === 'sqref') {
|
|
159
|
-
capture = 'sqref';
|
|
160
|
-
text = '';
|
|
160
|
+
else if (current !== undefined) {
|
|
161
|
+
capture.open(ln, selfClosing);
|
|
161
162
|
}
|
|
162
163
|
},
|
|
163
164
|
onText(chunk) {
|
|
164
|
-
|
|
165
|
-
text += chunk;
|
|
165
|
+
capture.text(chunk);
|
|
166
166
|
},
|
|
167
167
|
onClose(name) {
|
|
168
168
|
const ln = localName(name);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (current !== undefined)
|
|
169
|
+
const text = capture.close(ln);
|
|
170
|
+
if (text !== undefined) {
|
|
171
|
+
if (ln === 'f') {
|
|
172
|
+
if (current !== undefined && slot !== undefined)
|
|
173
|
+
current.formulae[slot] = text;
|
|
174
|
+
}
|
|
175
|
+
else if (current !== undefined) {
|
|
176
176
|
current.sqref = text;
|
|
177
|
-
|
|
177
|
+
}
|
|
178
178
|
}
|
|
179
179
|
else if ((ln === 'formula1' || ln === 'formula2') && name.includes(':')) {
|
|
180
180
|
slot = undefined;
|
|
@@ -197,6 +197,8 @@ function buildExtendedEntry(attrs, formulae, sqref) {
|
|
|
197
197
|
}
|
|
198
198
|
export function applyDataValidations(sheet, entries) {
|
|
199
199
|
for (const { sqref, rule, extended } of entries) {
|
|
200
|
+
if (decodeSqrefRects(sqref).length === 0)
|
|
201
|
+
continue;
|
|
200
202
|
sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
|
|
201
203
|
}
|
|
202
204
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tryDecodeRange } from '../../core/address.js';
|
|
2
2
|
import { isHyperlinkValue, isRichTextValue } from '../../core/value.js';
|
|
3
3
|
import { localName, parseXml } from '../../xml/xml-read.js';
|
|
4
|
-
import { escapeAttr } from '../../xml/xml.js';
|
|
4
|
+
import { escapeAttr, textAttr } from '../../xml/xml.js';
|
|
5
5
|
export function collectHyperlinks(sheet) {
|
|
6
6
|
const links = [];
|
|
7
7
|
for (const { cells } of sheet.rows()) {
|
|
@@ -33,8 +33,8 @@ export function hyperlinksXml(links) {
|
|
|
33
33
|
const items = links
|
|
34
34
|
.map((link) => {
|
|
35
35
|
const rid = link.relId !== undefined ? ` r:id="${link.relId}"` : '';
|
|
36
|
-
const location =
|
|
37
|
-
const tooltip =
|
|
36
|
+
const location = textAttr('location', link.location);
|
|
37
|
+
const tooltip = textAttr('tooltip', link.tooltip);
|
|
38
38
|
return `<hyperlink ref="${escapeAttr(link.ref)}"${rid}${location}${tooltip}/>`;
|
|
39
39
|
})
|
|
40
40
|
.join('');
|
|
@@ -64,8 +64,8 @@ export function applyHyperlinks(sheet, links, targetOf) {
|
|
|
64
64
|
const target = resolveTarget(link, targetOf);
|
|
65
65
|
if (target === undefined)
|
|
66
66
|
continue;
|
|
67
|
-
const decoded =
|
|
68
|
-
if (decoded === undefined)
|
|
67
|
+
const decoded = tryDecodeRange(link.ref);
|
|
68
|
+
if (decoded === undefined || decoded.tl.col === undefined || decoded.tl.row === undefined)
|
|
69
69
|
continue;
|
|
70
70
|
const cell = sheet.getCell(decoded.tl.address);
|
|
71
71
|
const cellValue = cell.value;
|
|
@@ -80,14 +80,6 @@ export function applyHyperlinks(sheet, links, targetOf) {
|
|
|
80
80
|
cell.value = value;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
function decodeRefSafe(ref) {
|
|
84
|
-
try {
|
|
85
|
-
return decodeRange(ref);
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
83
|
function resolveTarget(link, targetOf) {
|
|
92
84
|
if (link.rid !== undefined) {
|
|
93
85
|
const base = targetOf(link.rid);
|
package/dist/io/xlsx/images.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isOneCellAnchor, } from '../../core/image.js';
|
|
2
|
-
import { localName, numFinite, parseXml } from '../../xml/xml-read.js';
|
|
3
|
-
import { XML_DECLARATION } from '../../xml/xml.js';
|
|
1
|
+
import { isImageEditAs, isOneCellAnchor, } from '../../core/image.js';
|
|
2
|
+
import { enumToken, localName, numFinite, parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
3
|
+
import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
|
|
4
4
|
import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
|
|
5
5
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
6
6
|
import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
|
|
@@ -35,7 +35,7 @@ function anchorXml(image, id) {
|
|
|
35
35
|
: twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', anchor.rotation, image.embedId, id);
|
|
36
36
|
}
|
|
37
37
|
function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
|
|
38
|
-
return (`<xdr:twoCellAnchor editAs="${editAs}">` +
|
|
38
|
+
return (`<xdr:twoCellAnchor editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
|
|
39
39
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
40
40
|
`<xdr:to>${anchorPointXml(to)}</xdr:to>` +
|
|
41
41
|
picXml(embedId, id, rotation) +
|
|
@@ -45,13 +45,13 @@ function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
|
|
|
45
45
|
function oneCellAnchorXml(from, ext, rotation, embedId, id) {
|
|
46
46
|
return ('<xdr:oneCellAnchor>' +
|
|
47
47
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
48
|
-
`<xdr:ext
|
|
48
|
+
`<xdr:ext${numAttr('cx', ext.cx)}${numAttr('cy', ext.cy)}/>` +
|
|
49
49
|
picXml(embedId, id, rotation) +
|
|
50
50
|
'<xdr:clientData/>' +
|
|
51
51
|
'</xdr:oneCellAnchor>');
|
|
52
52
|
}
|
|
53
53
|
function picXml(embedId, id, rotation) {
|
|
54
|
-
const xfrm = rotation !== undefined ? `<a:xfrm
|
|
54
|
+
const xfrm = rotation !== undefined ? `<a:xfrm${numAttr('rot', rotation)}/>` : '';
|
|
55
55
|
return ('<xdr:pic>' +
|
|
56
56
|
`<xdr:nvPicPr><xdr:cNvPr id="${id}" name="Picture ${id}"/>` +
|
|
57
57
|
'<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>' +
|
|
@@ -61,8 +61,10 @@ function picXml(embedId, id, rotation) {
|
|
|
61
61
|
'</xdr:pic>');
|
|
62
62
|
}
|
|
63
63
|
function anchorPointXml(point) {
|
|
64
|
-
return (`<xdr:col>${point.col}</xdr:col
|
|
65
|
-
`<xdr:
|
|
64
|
+
return (`<xdr:col>${numberText(point.col)}</xdr:col>` +
|
|
65
|
+
`<xdr:colOff>${numberText(point.colOff ?? 0)}</xdr:colOff>` +
|
|
66
|
+
`<xdr:row>${numberText(point.row)}</xdr:row>` +
|
|
67
|
+
`<xdr:rowOff>${numberText(point.rowOff ?? 0)}</xdr:rowOff>`);
|
|
66
68
|
}
|
|
67
69
|
export function drawingRelsXml(mediaTargets) {
|
|
68
70
|
return relationshipsPart(mediaTargets.map((target, i) => relationship(`rId${i + 1}`, IMAGE_REL_TYPE, target)));
|
|
@@ -70,7 +72,6 @@ export function drawingRelsXml(mediaTargets) {
|
|
|
70
72
|
function blankPoint() {
|
|
71
73
|
return { col: 0, row: 0, colOff: 0, rowOff: 0 };
|
|
72
74
|
}
|
|
73
|
-
const EDIT_AS = new Set(['oneCell', 'twoCell', 'absolute']);
|
|
74
75
|
export function parseDrawing(xml) {
|
|
75
76
|
const anchors = [];
|
|
76
77
|
let from = null;
|
|
@@ -81,10 +82,9 @@ export function parseDrawing(xml) {
|
|
|
81
82
|
let embed;
|
|
82
83
|
let target = null;
|
|
83
84
|
let picDepth = 0;
|
|
84
|
-
|
|
85
|
-
let text = '';
|
|
85
|
+
const coord = new TextCapture(COORDINATES);
|
|
86
86
|
parseXml(xml, {
|
|
87
|
-
onOpen(name, attrs) {
|
|
87
|
+
onOpen(name, attrs, selfClosing) {
|
|
88
88
|
const local = localName(name);
|
|
89
89
|
if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
|
|
90
90
|
from = blankPoint();
|
|
@@ -92,8 +92,7 @@ export function parseDrawing(xml) {
|
|
|
92
92
|
ext = undefined;
|
|
93
93
|
rotation = undefined;
|
|
94
94
|
embed = undefined;
|
|
95
|
-
|
|
96
|
-
editAs = mode !== undefined && EDIT_AS.has(mode) ? mode : undefined;
|
|
95
|
+
editAs = enumToken(attrs.editAs, isImageEditAs);
|
|
97
96
|
}
|
|
98
97
|
else if (local === 'pic') {
|
|
99
98
|
picDepth++;
|
|
@@ -120,22 +119,20 @@ export function parseDrawing(xml) {
|
|
|
120
119
|
if (value !== undefined)
|
|
121
120
|
embed = value;
|
|
122
121
|
}
|
|
123
|
-
else if (target !== null
|
|
124
|
-
coord
|
|
125
|
-
text = '';
|
|
122
|
+
else if (target !== null) {
|
|
123
|
+
coord.open(local, selfClosing);
|
|
126
124
|
}
|
|
127
125
|
},
|
|
128
126
|
onText(chunk) {
|
|
129
|
-
|
|
130
|
-
text += chunk;
|
|
127
|
+
coord.text(chunk);
|
|
131
128
|
},
|
|
132
129
|
onClose(name) {
|
|
133
130
|
const local = localName(name);
|
|
134
|
-
|
|
131
|
+
const text = coord.close(local);
|
|
132
|
+
if (text !== undefined) {
|
|
135
133
|
const value = numFinite(text);
|
|
136
|
-
if (value !== undefined)
|
|
134
|
+
if (target !== null && value !== undefined)
|
|
137
135
|
setCoordinate(target, local, value);
|
|
138
|
-
coord = '';
|
|
139
136
|
}
|
|
140
137
|
else if (local === 'from' || local === 'to') {
|
|
141
138
|
target = null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
|
|
1
|
+
import { isDeclarablePivotSourceKind, pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
|
|
2
2
|
import { localName, numInteger, parseXml } from '../../xml/xml-read.js';
|
|
3
3
|
export function parsePivotTable(tableXml, cacheXml) {
|
|
4
4
|
const { fields, source } = parsePivotCacheDefinition(cacheXml);
|
|
@@ -85,16 +85,10 @@ function parsePivotTableDefinition(tableXml) {
|
|
|
85
85
|
});
|
|
86
86
|
return { name, cacheId, rowFields, columnFields, valueField, valueCaption, metric };
|
|
87
87
|
}
|
|
88
|
-
const SOURCE_KINDS = new Set([
|
|
89
|
-
'worksheet',
|
|
90
|
-
'external',
|
|
91
|
-
'consolidation',
|
|
92
|
-
'scenario',
|
|
93
|
-
]);
|
|
94
88
|
function sourceKind(type) {
|
|
95
89
|
if (type === undefined)
|
|
96
90
|
return 'worksheet';
|
|
97
|
-
return
|
|
91
|
+
return isDeclarablePivotSourceKind(type) ? type : 'unknown';
|
|
98
92
|
}
|
|
99
93
|
function toIndex(value) {
|
|
100
94
|
return numInteger(value, 0) ?? -1;
|
|
@@ -1,60 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { localName, parseXml } from '../../xml/xml-read.js';
|
|
2
2
|
import { RunAccumulator } from './rich-runs.js';
|
|
3
3
|
export function parseSharedStrings(xml) {
|
|
4
4
|
if (xml === '')
|
|
5
5
|
return [];
|
|
6
6
|
const strings = [];
|
|
7
|
-
|
|
8
|
-
const runs = new RunAccumulator();
|
|
9
|
-
let isRich = false;
|
|
10
|
-
let capture = false;
|
|
11
|
-
let text = '';
|
|
7
|
+
const runs = new RunAccumulator({ container: 'si', readRuns: true });
|
|
12
8
|
parseXml(xml, {
|
|
13
|
-
onOpen(name, attrs) {
|
|
14
|
-
|
|
15
|
-
switch (local) {
|
|
16
|
-
case 'si':
|
|
17
|
-
plain = '';
|
|
18
|
-
runs.beginContainer();
|
|
19
|
-
isRich = false;
|
|
20
|
-
break;
|
|
21
|
-
case 'r':
|
|
22
|
-
isRich = true;
|
|
23
|
-
runs.beginRun();
|
|
24
|
-
break;
|
|
25
|
-
case 'rPr':
|
|
26
|
-
runs.beginProperties();
|
|
27
|
-
break;
|
|
28
|
-
case 't':
|
|
29
|
-
capture = true;
|
|
30
|
-
text = '';
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
runs.applyProperty(local, attrs);
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
9
|
+
onOpen(name, attrs, selfClosing) {
|
|
10
|
+
runs.open(localName(name), attrs, selfClosing);
|
|
36
11
|
},
|
|
37
12
|
onText(chunk) {
|
|
38
|
-
|
|
39
|
-
text += chunk;
|
|
13
|
+
runs.text(chunk);
|
|
40
14
|
},
|
|
41
15
|
onClose(name) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const decoded = decodeSpreadsheetText(text);
|
|
46
|
-
if (!runs.appendText(decoded))
|
|
47
|
-
plain += decoded;
|
|
48
|
-
capture = false;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
case 'r':
|
|
52
|
-
runs.endRun();
|
|
53
|
-
break;
|
|
54
|
-
case 'si':
|
|
55
|
-
strings.push(isRich ? { richText: runs.runs } : plain);
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
16
|
+
if (runs.close(localName(name)) !== 'container')
|
|
17
|
+
return;
|
|
18
|
+
strings.push(runs.isRich ? { richText: runs.runs } : runs.plainText);
|
|
58
19
|
},
|
|
59
20
|
});
|
|
60
21
|
return strings;
|
|
@@ -4,6 +4,7 @@ import { numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
|
|
|
4
4
|
import { parseColor } from './color-xml.js';
|
|
5
5
|
const BORDER_EDGE_NAMES = ['left', 'right', 'top', 'bottom', 'diagonal'];
|
|
6
6
|
const BORDER_EDGES = new Set(BORDER_EDGE_NAMES);
|
|
7
|
+
const isBorderEdgeName = (name) => BORDER_EDGES.has(name);
|
|
7
8
|
const STYLE_EMPTY_CLOSES = new Set([
|
|
8
9
|
'font',
|
|
9
10
|
'border',
|
|
@@ -192,7 +193,7 @@ function parseBorders(events) {
|
|
|
192
193
|
borderDraft.diagonalDown = true;
|
|
193
194
|
}
|
|
194
195
|
else if (borderDraft !== null) {
|
|
195
|
-
if (
|
|
196
|
+
if (isBorderEdgeName(local)) {
|
|
196
197
|
if (attrs.style !== undefined && isBorderStyle(attrs.style)) {
|
|
197
198
|
currentEdge = local;
|
|
198
199
|
borderDraft[currentEdge] = { style: attrs.style };
|