@cj-tech-master/excelts 1.5.0 → 1.6.1

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.
Files changed (45) hide show
  1. package/dist/browser/excelts.iife.js +1057 -201
  2. package/dist/browser/excelts.iife.js.map +1 -1
  3. package/dist/browser/excelts.iife.min.js +63 -33
  4. package/dist/cjs/doc/column.js +7 -3
  5. package/dist/cjs/doc/pivot-table.js +149 -61
  6. package/dist/cjs/doc/workbook.js +3 -1
  7. package/dist/cjs/doc/worksheet.js +0 -2
  8. package/dist/cjs/stream/xlsx/worksheet-writer.js +1 -1
  9. package/dist/cjs/utils/unzip/zip-parser.js +2 -5
  10. package/dist/cjs/xlsx/xform/book/workbook-xform.js +3 -0
  11. package/dist/cjs/xlsx/xform/core/content-types-xform.js +19 -14
  12. package/dist/cjs/xlsx/xform/pivot-table/cache-field-xform.js +135 -0
  13. package/dist/cjs/xlsx/xform/pivot-table/cache-field.js +7 -4
  14. package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +135 -13
  15. package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-records-xform.js +193 -45
  16. package/dist/cjs/xlsx/xform/pivot-table/pivot-table-xform.js +390 -39
  17. package/dist/cjs/xlsx/xform/sheet/cell-xform.js +6 -0
  18. package/dist/cjs/xlsx/xform/sheet/worksheet-xform.js +14 -3
  19. package/dist/cjs/xlsx/xlsx.js +261 -38
  20. package/dist/esm/doc/column.js +7 -3
  21. package/dist/esm/doc/pivot-table.js +150 -62
  22. package/dist/esm/doc/workbook.js +3 -1
  23. package/dist/esm/doc/worksheet.js +0 -2
  24. package/dist/esm/stream/xlsx/worksheet-writer.js +1 -1
  25. package/dist/esm/utils/unzip/zip-parser.js +2 -5
  26. package/dist/esm/xlsx/xform/book/workbook-xform.js +3 -0
  27. package/dist/esm/xlsx/xform/core/content-types-xform.js +19 -14
  28. package/dist/esm/xlsx/xform/pivot-table/cache-field-xform.js +132 -0
  29. package/dist/esm/xlsx/xform/pivot-table/cache-field.js +7 -4
  30. package/dist/esm/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +135 -13
  31. package/dist/esm/xlsx/xform/pivot-table/pivot-cache-records-xform.js +193 -45
  32. package/dist/esm/xlsx/xform/pivot-table/pivot-table-xform.js +390 -39
  33. package/dist/esm/xlsx/xform/sheet/cell-xform.js +6 -0
  34. package/dist/esm/xlsx/xform/sheet/worksheet-xform.js +14 -3
  35. package/dist/esm/xlsx/xlsx.js +261 -38
  36. package/dist/types/doc/column.d.ts +13 -6
  37. package/dist/types/doc/pivot-table.d.ts +135 -9
  38. package/dist/types/doc/workbook.d.ts +2 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/dist/types/xlsx/xform/pivot-table/cache-field-xform.d.ts +42 -0
  41. package/dist/types/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +45 -6
  42. package/dist/types/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +52 -5
  43. package/dist/types/xlsx/xform/pivot-table/pivot-table-xform.d.ts +98 -5
  44. package/dist/types/xlsx/xlsx.d.ts +27 -0
  45. package/package.json +17 -17
@@ -3,21 +3,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PivotCacheDefinitionXform = void 0;
4
4
  const base_xform_js_1 = require("../base-xform");
5
5
  const cache_field_js_1 = require("./cache-field");
6
+ const cache_field_xform_js_1 = require("./cache-field-xform");
6
7
  const xml_stream_js_1 = require("../../../utils/xml-stream");
7
8
  class PivotCacheDefinitionXform extends base_xform_js_1.BaseXform {
8
9
  constructor() {
9
10
  super();
10
11
  this.map = {};
12
+ this.model = null;
13
+ this.currentCacheField = null;
14
+ this.inCacheFields = false;
15
+ this.inCacheSource = false;
11
16
  }
12
17
  prepare(_model) {
13
- // TK
18
+ // No preparation needed for writing
14
19
  }
15
20
  get tag() {
16
21
  // http://www.datypic.com/sc/ooxml/e-ssml_pivotCacheDefinition.html
17
22
  return "pivotCacheDefinition";
18
23
  }
24
+ reset() {
25
+ this.model = null;
26
+ this.currentCacheField = null;
27
+ this.inCacheFields = false;
28
+ this.inCacheSource = false;
29
+ }
30
+ /**
31
+ * Render pivot cache definition XML.
32
+ * Supports both newly created models (with PivotTableSource) and loaded models.
33
+ */
19
34
  render(xmlStream, model) {
20
- const { sourceSheet, cacheFields } = model;
35
+ // Check if this is a loaded model (has isLoaded flag or no source property)
36
+ const isLoaded = model.isLoaded || !("source" in model);
37
+ if (isLoaded) {
38
+ this.renderLoaded(xmlStream, model);
39
+ }
40
+ else {
41
+ this.renderNew(xmlStream, model);
42
+ }
43
+ }
44
+ /**
45
+ * Render newly created pivot cache definition
46
+ */
47
+ renderNew(xmlStream, model) {
48
+ const { source, cacheFields } = model;
21
49
  xmlStream.openXml(xml_stream_js_1.XmlStream.StdDocAttributes);
22
50
  xmlStream.openNode(this.tag, {
23
51
  ...PivotCacheDefinitionXform.PIVOT_CACHE_DEFINITION_ATTRIBUTES,
@@ -32,8 +60,8 @@ class PivotCacheDefinitionXform extends base_xform_js_1.BaseXform {
32
60
  });
33
61
  xmlStream.openNode("cacheSource", { type: "worksheet" });
34
62
  xmlStream.leafNode("worksheetSource", {
35
- ref: sourceSheet.dimensions.shortRange,
36
- sheet: sourceSheet.name
63
+ ref: source.dimensions.shortRange,
64
+ sheet: source.name
37
65
  });
38
66
  xmlStream.closeNode();
39
67
  xmlStream.openNode("cacheFields", { count: cacheFields.length });
@@ -42,19 +70,113 @@ class PivotCacheDefinitionXform extends base_xform_js_1.BaseXform {
42
70
  xmlStream.closeNode();
43
71
  xmlStream.closeNode();
44
72
  }
45
- parseOpen(_node) {
46
- // TK
47
- return false;
73
+ /**
74
+ * Render loaded pivot cache definition (preserving original structure)
75
+ */
76
+ renderLoaded(xmlStream, model) {
77
+ const { cacheFields, sourceRef, sourceSheet, recordCount } = model;
78
+ xmlStream.openXml(xml_stream_js_1.XmlStream.StdDocAttributes);
79
+ xmlStream.openNode(this.tag, {
80
+ ...PivotCacheDefinitionXform.PIVOT_CACHE_DEFINITION_ATTRIBUTES,
81
+ "r:id": model.rId || "rId1",
82
+ refreshOnLoad: model.refreshOnLoad || "1",
83
+ refreshedBy: model.refreshedBy || "Author",
84
+ refreshedDate: model.refreshedDate || "45125.026046874998",
85
+ createdVersion: model.createdVersion || "8",
86
+ refreshedVersion: model.refreshedVersion || "8",
87
+ minRefreshableVersion: model.minRefreshableVersion || "3",
88
+ recordCount: recordCount || cacheFields.length + 1
89
+ });
90
+ xmlStream.openNode("cacheSource", { type: "worksheet" });
91
+ xmlStream.leafNode("worksheetSource", {
92
+ ref: sourceRef,
93
+ sheet: sourceSheet
94
+ });
95
+ xmlStream.closeNode();
96
+ xmlStream.openNode("cacheFields", { count: cacheFields.length });
97
+ xmlStream.writeXml(cacheFields
98
+ .map((cacheField) => new cache_field_js_1.CacheField(cacheField).render())
99
+ .join("\n "));
100
+ xmlStream.closeNode();
101
+ xmlStream.closeNode();
102
+ }
103
+ parseOpen(node) {
104
+ const { name, attributes } = node;
105
+ // Delegate to current cacheField parser if active
106
+ if (this.currentCacheField) {
107
+ this.currentCacheField.parseOpen(node);
108
+ return true;
109
+ }
110
+ switch (name) {
111
+ case this.tag:
112
+ // pivotCacheDefinition root element
113
+ this.reset();
114
+ this.model = {
115
+ cacheFields: [],
116
+ rId: attributes["r:id"],
117
+ refreshOnLoad: attributes.refreshOnLoad,
118
+ refreshedBy: attributes.refreshedBy,
119
+ refreshedDate: attributes.refreshedDate,
120
+ createdVersion: attributes.createdVersion,
121
+ refreshedVersion: attributes.refreshedVersion,
122
+ minRefreshableVersion: attributes.minRefreshableVersion,
123
+ recordCount: attributes.recordCount ? parseInt(attributes.recordCount, 10) : undefined,
124
+ isLoaded: true
125
+ };
126
+ break;
127
+ case "cacheSource":
128
+ this.inCacheSource = true;
129
+ break;
130
+ case "worksheetSource":
131
+ if (this.inCacheSource && this.model) {
132
+ this.model.sourceRef = attributes.ref;
133
+ this.model.sourceSheet = attributes.sheet;
134
+ }
135
+ break;
136
+ case "cacheFields":
137
+ this.inCacheFields = true;
138
+ break;
139
+ case "cacheField":
140
+ if (this.inCacheFields) {
141
+ this.currentCacheField = new cache_field_xform_js_1.CacheFieldXform();
142
+ this.currentCacheField.parseOpen(node);
143
+ }
144
+ break;
145
+ }
146
+ return true;
48
147
  }
49
- parseText(_text) {
50
- // TK
148
+ parseText(text) {
149
+ if (this.currentCacheField) {
150
+ this.currentCacheField.parseText(text);
151
+ }
51
152
  }
52
- parseClose(_name) {
53
- // TK
54
- return false;
153
+ parseClose(name) {
154
+ // Delegate to current cacheField parser if active
155
+ if (this.currentCacheField) {
156
+ if (!this.currentCacheField.parseClose(name)) {
157
+ // cacheField parsing complete, add to model
158
+ if (this.model && this.currentCacheField.model) {
159
+ this.model.cacheFields.push(this.currentCacheField.model);
160
+ }
161
+ this.currentCacheField = null;
162
+ }
163
+ return true;
164
+ }
165
+ switch (name) {
166
+ case this.tag:
167
+ // End of pivotCacheDefinition
168
+ return false;
169
+ case "cacheSource":
170
+ this.inCacheSource = false;
171
+ break;
172
+ case "cacheFields":
173
+ this.inCacheFields = false;
174
+ break;
175
+ }
176
+ return true;
55
177
  }
56
178
  reconcile(_model, _options) {
57
- // TK
179
+ // No reconciliation needed
58
180
  }
59
181
  }
60
182
  exports.PivotCacheDefinitionXform = PivotCacheDefinitionXform;
@@ -2,81 +2,229 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PivotCacheRecordsXform = void 0;
4
4
  const xml_stream_js_1 = require("../../../utils/xml-stream");
5
+ const utils_js_1 = require("../../../utils/utils");
5
6
  const base_xform_js_1 = require("../base-xform");
6
7
  class PivotCacheRecordsXform extends base_xform_js_1.BaseXform {
7
8
  constructor() {
8
9
  super();
9
10
  this.map = {};
11
+ this.model = null;
12
+ this.currentRecord = null;
10
13
  }
11
14
  prepare(_model) {
12
- // TK
15
+ // No preparation needed
13
16
  }
14
17
  get tag() {
15
18
  // http://www.datypic.com/sc/ooxml/e-ssml_pivotCacheRecords.html
16
19
  return "pivotCacheRecords";
17
20
  }
21
+ reset() {
22
+ this.model = null;
23
+ this.currentRecord = null;
24
+ }
25
+ /**
26
+ * Render pivot cache records XML.
27
+ * Supports both newly created models (with PivotTableSource) and loaded models.
28
+ */
18
29
  render(xmlStream, model) {
19
- const { sourceSheet, cacheFields } = model;
20
- const sourceBodyRows = sourceSheet.getSheetValues().slice(2);
30
+ // Check if this is a loaded model
31
+ const isLoaded = model.isLoaded || !("source" in model);
32
+ if (isLoaded) {
33
+ this.renderLoaded(xmlStream, model);
34
+ }
35
+ else {
36
+ this.renderNew(xmlStream, model);
37
+ }
38
+ }
39
+ /**
40
+ * Render newly created pivot cache records
41
+ */
42
+ renderNew(xmlStream, model) {
43
+ const { source, cacheFields } = model;
44
+ const sourceBodyRows = source.getSheetValues().slice(2);
21
45
  xmlStream.openXml(xml_stream_js_1.XmlStream.StdDocAttributes);
22
46
  xmlStream.openNode(this.tag, {
23
47
  ...PivotCacheRecordsXform.PIVOT_CACHE_RECORDS_ATTRIBUTES,
24
48
  count: sourceBodyRows.length
25
49
  });
26
- xmlStream.writeXml(renderTable());
50
+ xmlStream.writeXml(this.renderTableNew(sourceBodyRows, cacheFields));
27
51
  xmlStream.closeNode();
28
- // Helpers
29
- function renderTable() {
30
- const rowsInXML = sourceBodyRows.map((row) => {
31
- const realRow = row.slice(1);
32
- return [...renderRowLines(realRow)].join("");
33
- });
34
- return rowsInXML.join("");
35
- }
36
- function* renderRowLines(row) {
37
- // PivotCache Record: http://www.datypic.com/sc/ooxml/e-ssml_r-1.html
38
- // Note: pretty-printing this for now to ease debugging.
39
- yield "\n <r>";
40
- for (const [index, cellValue] of row.entries()) {
41
- yield "\n ";
42
- yield renderCell(cellValue, cacheFields[index].sharedItems);
52
+ }
53
+ /**
54
+ * Render loaded pivot cache records
55
+ */
56
+ renderLoaded(xmlStream, model) {
57
+ xmlStream.openXml(xml_stream_js_1.XmlStream.StdDocAttributes);
58
+ xmlStream.openNode(this.tag, {
59
+ ...PivotCacheRecordsXform.PIVOT_CACHE_RECORDS_ATTRIBUTES,
60
+ count: model.count
61
+ });
62
+ // Render each record
63
+ for (const record of model.records) {
64
+ xmlStream.writeXml("\n <r>");
65
+ for (const value of record) {
66
+ xmlStream.writeXml("\n ");
67
+ xmlStream.writeXml(this.renderRecordValue(value));
43
68
  }
44
- yield "\n </r>";
69
+ xmlStream.writeXml("\n </r>");
45
70
  }
46
- function renderCell(value, sharedItems) {
47
- // no shared items
48
- // --------------------------------------------------
49
- if (sharedItems === null) {
50
- if (Number.isFinite(value)) {
51
- // Numeric value: http://www.datypic.com/sc/ooxml/e-ssml_n-2.html
52
- return `<n v="${value}" />`;
53
- }
54
- // Character Value: http://www.datypic.com/sc/ooxml/e-ssml_s-2.html
55
- return `<s v="${value}" />`;
71
+ xmlStream.closeNode();
72
+ }
73
+ /**
74
+ * Render a single record value to XML
75
+ */
76
+ renderRecordValue(value) {
77
+ switch (value.type) {
78
+ case "x":
79
+ return `<x v="${value.value}" />`;
80
+ case "n":
81
+ return `<n v="${value.value}" />`;
82
+ case "s":
83
+ return `<s v="${(0, utils_js_1.xmlEncode)(String(value.value))}" />`;
84
+ case "b":
85
+ return `<b v="${value.value ? "1" : "0"}" />`;
86
+ case "m":
87
+ return "<m />";
88
+ case "d":
89
+ return `<d v="${value.value.toISOString()}" />`;
90
+ case "e":
91
+ return `<e v="${value.value}" />`;
92
+ default:
93
+ return "<m />";
94
+ }
95
+ }
96
+ // Helper methods for rendering new records
97
+ renderTableNew(sourceBodyRows, cacheFields) {
98
+ const parts = [];
99
+ for (const row of sourceBodyRows) {
100
+ const realRow = row.slice(1);
101
+ parts.push("\n <r>");
102
+ for (let i = 0; i < realRow.length; i++) {
103
+ parts.push("\n ");
104
+ parts.push(this.renderCellNew(realRow[i], cacheFields[i].sharedItems));
56
105
  }
57
- // shared items
58
- // --------------------------------------------------
59
- const sharedItemsIndex = sharedItems.indexOf(value);
60
- if (sharedItemsIndex < 0) {
61
- throw new Error(`${JSON.stringify(value)} not in sharedItems ${JSON.stringify(sharedItems)}`);
106
+ parts.push("\n </r>");
107
+ }
108
+ return parts.join("");
109
+ }
110
+ renderCellNew(value, sharedItems) {
111
+ // Handle null/undefined values first
112
+ if (value === null || value === undefined) {
113
+ return "<m />";
114
+ }
115
+ // no shared items
116
+ if (sharedItems === null) {
117
+ if (Number.isFinite(value)) {
118
+ return `<n v="${value}" />`;
62
119
  }
63
- // Shared Items Index: http://www.datypic.com/sc/ooxml/e-ssml_x-9.html
64
- return `<x v="${sharedItemsIndex}" />`;
120
+ return `<s v="${(0, utils_js_1.xmlEncode)(String(value))}" />`;
121
+ }
122
+ // shared items
123
+ const sharedItemsIndex = sharedItems.indexOf(value);
124
+ if (sharedItemsIndex < 0) {
125
+ throw new Error(`${JSON.stringify(value)} not in sharedItems ${JSON.stringify(sharedItems)}`);
65
126
  }
127
+ return `<x v="${sharedItemsIndex}" />`;
66
128
  }
67
- parseOpen(_node) {
68
- // TK
69
- return false;
129
+ parseOpen(node) {
130
+ const { name, attributes } = node;
131
+ switch (name) {
132
+ case this.tag:
133
+ // pivotCacheRecords root element
134
+ this.reset();
135
+ this.model = {
136
+ records: [],
137
+ count: parseInt(attributes.count || "0", 10),
138
+ isLoaded: true
139
+ };
140
+ break;
141
+ case "r":
142
+ // Start of a new record
143
+ this.currentRecord = [];
144
+ break;
145
+ case "x":
146
+ // Shared item index
147
+ if (this.currentRecord) {
148
+ this.currentRecord.push({
149
+ type: "x",
150
+ value: parseInt(attributes.v || "0", 10)
151
+ });
152
+ }
153
+ break;
154
+ case "n":
155
+ // Numeric value
156
+ if (this.currentRecord) {
157
+ this.currentRecord.push({
158
+ type: "n",
159
+ value: parseFloat(attributes.v || "0")
160
+ });
161
+ }
162
+ break;
163
+ case "s":
164
+ // String value
165
+ if (this.currentRecord) {
166
+ this.currentRecord.push({
167
+ type: "s",
168
+ value: (0, utils_js_1.xmlDecode)(attributes.v || "")
169
+ });
170
+ }
171
+ break;
172
+ case "b":
173
+ // Boolean value
174
+ if (this.currentRecord) {
175
+ this.currentRecord.push({
176
+ type: "b",
177
+ value: attributes.v === "1"
178
+ });
179
+ }
180
+ break;
181
+ case "m":
182
+ // Missing/null value
183
+ if (this.currentRecord) {
184
+ this.currentRecord.push({ type: "m" });
185
+ }
186
+ break;
187
+ case "d":
188
+ // Date value
189
+ if (this.currentRecord) {
190
+ this.currentRecord.push({
191
+ type: "d",
192
+ value: new Date(attributes.v || "")
193
+ });
194
+ }
195
+ break;
196
+ case "e":
197
+ // Error value
198
+ if (this.currentRecord) {
199
+ this.currentRecord.push({
200
+ type: "e",
201
+ value: attributes.v || ""
202
+ });
203
+ }
204
+ break;
205
+ }
206
+ return true;
70
207
  }
71
208
  parseText(_text) {
72
- // TK
209
+ // No text content in cache records elements
73
210
  }
74
- parseClose(_name) {
75
- // TK
76
- return false;
211
+ parseClose(name) {
212
+ switch (name) {
213
+ case this.tag:
214
+ // End of pivotCacheRecords
215
+ return false;
216
+ case "r":
217
+ // End of record - add to model
218
+ if (this.model && this.currentRecord) {
219
+ this.model.records.push(this.currentRecord);
220
+ this.currentRecord = null;
221
+ }
222
+ break;
223
+ }
224
+ return true;
77
225
  }
78
226
  reconcile(_model, _options) {
79
- // TK
227
+ // No reconciliation needed
80
228
  }
81
229
  }
82
230
  exports.PivotCacheRecordsXform = PivotCacheRecordsXform;