@trebco/treb 32.14.0 → 36.1.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.
Files changed (267) hide show
  1. package/api-generator/api-generator-types.ts +3 -0
  2. package/api-generator/api-generator.ts +15 -1
  3. package/bun.lock +145 -99
  4. package/dist/chunk-43DLP2OX.mjs +11 -0
  5. package/dist/chunk-4CKS56PE.mjs +11 -0
  6. package/dist/chunk-75PARUQE.mjs +11 -0
  7. package/dist/chunk-7QD63AZS.mjs +24601 -0
  8. package/dist/chunk-A55ARVRD.mjs +11 -0
  9. package/dist/chunk-DESAKYW4.mjs +11 -0
  10. package/dist/chunk-EQ2R5W6P.mjs +24565 -0
  11. package/dist/chunk-IYJU2J6D.mjs +24601 -0
  12. package/dist/chunk-KSJFPGXT.mjs +11 -0
  13. package/dist/chunk-ORQFKLXM.mjs +24601 -0
  14. package/dist/chunk-SFDNNDHY.mjs +11 -0
  15. package/dist/chunk-T47DX5MI.mjs +11 -0
  16. package/dist/chunk-T6ILBVEX.mjs +11 -0
  17. package/dist/chunk-TPRCDYYG.mjs +11 -0
  18. package/dist/chunk-YAHNOOHO.mjs +11 -0
  19. package/dist/chunk-YLCFKX2G.mjs +24601 -0
  20. package/dist/treb-export-worker.mjs +9 -2
  21. package/dist/treb-spreadsheet.mjs +7 -19
  22. package/dist/treb.d.ts +144 -117
  23. package/esbuild-composite.mjs +18 -6
  24. package/esbuild-utils.mjs +62 -3
  25. package/i18n/languages/treb-i18n-da.mjs +1 -1
  26. package/i18n/languages/treb-i18n-de.mjs +1 -1
  27. package/i18n/languages/treb-i18n-es.mjs +1 -1
  28. package/i18n/languages/treb-i18n-fr.mjs +1 -1
  29. package/i18n/languages/treb-i18n-it.mjs +1 -1
  30. package/i18n/languages/treb-i18n-nl.mjs +1 -1
  31. package/i18n/languages/treb-i18n-no.mjs +1 -1
  32. package/i18n/languages/treb-i18n-pl.mjs +1 -1
  33. package/i18n/languages/treb-i18n-pt.mjs +1 -1
  34. package/i18n/languages/treb-i18n-sv.mjs +1 -1
  35. package/ooxml-types/README.md +141 -0
  36. package/ooxml-types/package.json +5 -0
  37. package/ooxml-types/src/types/drawingml/chart.ts +327 -0
  38. package/ooxml-types/src/types/drawingml/index.ts +63 -0
  39. package/ooxml-types/src/types/drawingml/spreadsheetDrawing.ts +105 -0
  40. package/ooxml-types/src/types/drawingml/theme.ts +104 -0
  41. package/ooxml-types/src/types/index.ts +3 -0
  42. package/ooxml-types/src/types/package/contentTypes.ts +49 -0
  43. package/ooxml-types/src/types/package/docProps.ts +46 -0
  44. package/ooxml-types/src/types/package/index.ts +17 -0
  45. package/ooxml-types/src/types/package/relationships.ts +37 -0
  46. package/ooxml-types/src/types/spreadsheetml/columns.ts +20 -0
  47. package/ooxml-types/src/types/spreadsheetml/comments.ts +30 -0
  48. package/ooxml-types/src/types/spreadsheetml/dataFeatures.ts +261 -0
  49. package/ooxml-types/src/types/spreadsheetml/enums.ts +175 -0
  50. package/ooxml-types/src/types/spreadsheetml/index.ts +186 -0
  51. package/ooxml-types/src/types/spreadsheetml/metadata.ts +90 -0
  52. package/ooxml-types/src/types/spreadsheetml/misc.ts +35 -0
  53. package/ooxml-types/src/types/spreadsheetml/pageLayout.ts +83 -0
  54. package/ooxml-types/src/types/spreadsheetml/sharedStrings.ts +33 -0
  55. package/ooxml-types/src/types/spreadsheetml/sheetData.ts +70 -0
  56. package/ooxml-types/src/types/spreadsheetml/sheetProperties.ts +86 -0
  57. package/ooxml-types/src/types/spreadsheetml/sheetViews.ts +51 -0
  58. package/ooxml-types/src/types/spreadsheetml/sparkline.ts +46 -0
  59. package/ooxml-types/src/types/spreadsheetml/styles.ts +274 -0
  60. package/ooxml-types/src/types/spreadsheetml/table.ts +106 -0
  61. package/ooxml-types/src/types/spreadsheetml/util.ts +15 -0
  62. package/ooxml-types/src/types/spreadsheetml/workbook.ts +165 -0
  63. package/ooxml-types/src/types/spreadsheetml/worksheet.ts +60 -0
  64. package/package.json +13 -11
  65. package/treb-base-types/src/api_types.ts +1 -1
  66. package/treb-base-types/src/area-utils.ts +1 -1
  67. package/treb-base-types/src/area.ts +1 -1
  68. package/treb-base-types/src/basic_types.ts +1 -1
  69. package/treb-base-types/src/cell.ts +1 -1
  70. package/treb-base-types/src/cells.ts +1 -1
  71. package/treb-base-types/src/color.ts +1 -1
  72. package/treb-base-types/src/dom-utilities.ts +1 -1
  73. package/treb-base-types/src/evaluate-options.ts +1 -1
  74. package/treb-base-types/src/font-stack.ts +1 -1
  75. package/treb-base-types/src/gradient.ts +1 -1
  76. package/treb-base-types/src/import.ts +1 -1
  77. package/treb-base-types/src/index-standalone.ts +1 -1
  78. package/treb-base-types/src/index.ts +2 -1
  79. package/treb-base-types/src/layout.ts +1 -1
  80. package/treb-base-types/src/localization.ts +1 -1
  81. package/treb-base-types/src/rectangle.ts +1 -1
  82. package/treb-base-types/src/render_text.ts +7 -1
  83. package/treb-base-types/src/style.ts +1 -1
  84. package/treb-base-types/src/table.ts +1 -1
  85. package/treb-base-types/src/text_part.ts +1 -1
  86. package/treb-base-types/src/theme.ts +1 -1
  87. package/treb-base-types/src/union.ts +4 -1
  88. package/treb-base-types/src/value-type.ts +1 -1
  89. package/treb-base-types/src/worker-proxy.ts +294 -0
  90. package/treb-base-types/style/resizable.css +1 -1
  91. package/treb-calculator/src/calculator.ts +133 -14
  92. package/treb-calculator/src/complex-math.ts +1 -1
  93. package/treb-calculator/src/dag/array-vertex.ts +1 -1
  94. package/treb-calculator/src/dag/calculation_leaf_vertex.ts +1 -1
  95. package/treb-calculator/src/dag/graph.ts +1 -1
  96. package/treb-calculator/src/dag/spreadsheet_vertex.ts +1 -1
  97. package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +1 -1
  98. package/treb-calculator/src/dag/state_leaf_vertex.ts +1 -1
  99. package/treb-calculator/src/dag/vertex.ts +1 -1
  100. package/treb-calculator/src/descriptors.ts +9 -1
  101. package/treb-calculator/src/expression-calculator.ts +1 -1
  102. package/treb-calculator/src/function-error.ts +1 -1
  103. package/treb-calculator/src/function-library.ts +1 -1
  104. package/treb-calculator/src/functions/base-functions.ts +8 -4
  105. package/treb-calculator/src/functions/beta.ts +1 -1
  106. package/treb-calculator/src/functions/checkbox.ts +1 -1
  107. package/treb-calculator/src/functions/complex-functions.ts +1 -1
  108. package/treb-calculator/src/functions/date-utils.ts +1 -1
  109. package/treb-calculator/src/functions/finance-functions.ts +2 -4
  110. package/treb-calculator/src/functions/fp.ts +1 -1
  111. package/treb-calculator/src/functions/function-utilities.ts +1 -1
  112. package/treb-calculator/src/functions/gamma.ts +1 -1
  113. package/treb-calculator/src/functions/information-functions.ts +1 -1
  114. package/treb-calculator/src/functions/lambda-functions.ts +4 -1
  115. package/treb-calculator/src/functions/matrix-functions.ts +1 -1
  116. package/treb-calculator/src/functions/normal.ts +1 -1
  117. package/treb-calculator/src/functions/regex-functions.ts +13 -4
  118. package/treb-calculator/src/functions/sparkline.ts +1 -1
  119. package/treb-calculator/src/functions/statistics-functions.ts +1 -1
  120. package/treb-calculator/src/functions/students-t.ts +1 -1
  121. package/treb-calculator/src/functions/text-functions.ts +5 -1
  122. package/treb-calculator/src/index.ts +1 -1
  123. package/treb-calculator/src/notifier-types.ts +1 -1
  124. package/treb-calculator/src/primitives.ts +1 -1
  125. package/treb-calculator/src/utilities.ts +1 -1
  126. package/treb-charts/src/chart-functions.ts +1 -1
  127. package/treb-charts/src/chart-types.ts +1 -1
  128. package/treb-charts/src/chart-utils.ts +1 -1
  129. package/treb-charts/src/chart.ts +1 -1
  130. package/treb-charts/src/default-chart-renderer.ts +1 -1
  131. package/treb-charts/src/index.ts +1 -1
  132. package/treb-charts/src/main.ts +1 -1
  133. package/treb-charts/src/quicksort.ts +1 -1
  134. package/treb-charts/src/rectangle.ts +1 -1
  135. package/treb-charts/src/renderer-type.ts +1 -1
  136. package/treb-charts/src/renderer.ts +1 -1
  137. package/treb-charts/src/util.ts +1 -1
  138. package/treb-charts/style/charts.scss +1 -1
  139. package/treb-data-model/src/annotation.ts +1 -1
  140. package/treb-data-model/src/conditional_format.ts +1 -1
  141. package/treb-data-model/src/data-validation.ts +1 -1
  142. package/treb-data-model/src/data_model.ts +32 -5
  143. package/treb-data-model/src/index.ts +1 -1
  144. package/treb-data-model/src/language-model.ts +1 -1
  145. package/treb-data-model/src/named.ts +1 -1
  146. package/treb-data-model/src/serialize_options.ts +1 -1
  147. package/treb-data-model/src/sheet.ts +1 -1
  148. package/treb-data-model/src/sheet_collection.ts +1 -1
  149. package/treb-data-model/src/sheet_selection.ts +1 -1
  150. package/treb-data-model/src/sheet_types.ts +1 -1
  151. package/treb-data-model/src/types.ts +1 -1
  152. package/treb-embed/src/content-types.d.ts +1 -1
  153. package/treb-embed/src/custom-element/global.d.ts +1 -1
  154. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1 -1
  155. package/treb-embed/src/custom-element/treb-global.ts +1 -1
  156. package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +1 -1
  157. package/treb-embed/src/embedded-spreadsheet.ts +357 -154
  158. package/treb-embed/src/index.ts +1 -1
  159. package/treb-embed/src/options.ts +4 -2
  160. package/treb-embed/src/plugin.ts +1 -1
  161. package/treb-embed/src/progress-dialog.ts +1 -1
  162. package/treb-embed/src/selection-state.ts +1 -1
  163. package/treb-embed/src/spinner.ts +1 -1
  164. package/treb-embed/src/toolbar-message.ts +6 -1
  165. package/treb-embed/src/types.ts +13 -1
  166. package/treb-embed/style/autocomplete.scss +1 -1
  167. package/treb-embed/style/dark-theme.scss +1 -1
  168. package/treb-embed/style/defaults.scss +1 -1
  169. package/treb-embed/style/dialog.scss +1 -1
  170. package/treb-embed/style/dropdown-select.scss +1 -1
  171. package/treb-embed/style/font-stacks.scss +1 -1
  172. package/treb-embed/style/formula-bar.scss +1 -1
  173. package/treb-embed/style/grid.scss +1 -1
  174. package/treb-embed/style/layout.scss +1 -1
  175. package/treb-embed/style/mouse-mask.scss +1 -1
  176. package/treb-embed/style/note.scss +1 -1
  177. package/treb-embed/style/overlay-editor.scss +1 -1
  178. package/treb-embed/style/spinner.scss +1 -1
  179. package/treb-embed/style/tab-bar.scss +1 -1
  180. package/treb-embed/style/table.scss +1 -1
  181. package/treb-embed/style/theme-defaults.scss +1 -1
  182. package/treb-embed/style/toolbar.scss +1 -1
  183. package/treb-embed/style/tooltip.scss +1 -1
  184. package/treb-embed/style/treb-icons.scss +1 -1
  185. package/treb-embed/style/treb-spreadsheet-element.scss +1 -1
  186. package/treb-embed/style/z-index.scss +1 -1
  187. package/treb-export/src/address-type.ts +1 -1
  188. package/treb-export/src/base-template.ts +1 -1
  189. package/treb-export/src/column-width.ts +1 -1
  190. package/treb-export/src/drawing/bubble-chart-template.ts +1 -1
  191. package/treb-export/src/drawing/chart-template-components2.ts +1 -1
  192. package/treb-export/src/drawing/chart.ts +1 -1
  193. package/treb-export/src/drawing/column-chart-template2.ts +1 -1
  194. package/treb-export/src/drawing/donut-chart-template2.ts +1 -1
  195. package/treb-export/src/drawing/drawing.ts +1 -1
  196. package/treb-export/src/drawing/embedded-image.ts +1 -1
  197. package/treb-export/src/drawing/scatter-chart-template2.ts +1 -1
  198. package/treb-export/src/export.ts +10 -6
  199. package/treb-export/src/import-export-messages.ts +61 -0
  200. package/treb-export/src/import.ts +318 -301
  201. package/treb-export/src/index.worker.ts +85 -53
  202. package/treb-export/src/metadata.ts +71 -3
  203. package/treb-export/src/ooxml.ts +47 -0
  204. package/treb-export/src/relationship.ts +1 -1
  205. package/treb-export/src/shared-strings.ts +19 -15
  206. package/treb-export/src/template-2.ts +1 -1
  207. package/treb-export/src/unescape_xml.ts +1 -1
  208. package/treb-export/src/workbook-sheet.ts +11 -6
  209. package/treb-export/src/workbook-style.ts +137 -25
  210. package/treb-export/src/workbook-theme.ts +20 -4
  211. package/treb-export/src/workbook.ts +85 -88
  212. package/treb-export/src/xml-test.ts +1 -1
  213. package/treb-export/src/xml-utils.ts +1 -1
  214. package/treb-export/src/zip-wrapper.ts +1 -1
  215. package/treb-export/tsconfig.json +2 -1
  216. package/treb-format/src/format.test.ts +1 -1
  217. package/treb-format/src/format.ts +12 -5
  218. package/treb-format/src/format_cache.ts +3 -3
  219. package/treb-format/src/format_parser.ts +1 -1
  220. package/treb-format/src/index.ts +1 -1
  221. package/treb-format/src/number_format_section.ts +1 -1
  222. package/treb-format/src/value_parser.ts +1 -1
  223. package/treb-grid/src/editors/autocomplete.ts +1 -1
  224. package/treb-grid/src/editors/autocomplete_matcher.ts +1 -1
  225. package/treb-grid/src/editors/editor.ts +15 -6
  226. package/treb-grid/src/editors/external_editor.ts +33 -8
  227. package/treb-grid/src/editors/formula_bar.ts +12 -1
  228. package/treb-grid/src/editors/overlay_editor.ts +4 -1
  229. package/treb-grid/src/index.ts +1 -1
  230. package/treb-grid/src/layout/base_layout.ts +1 -1
  231. package/treb-grid/src/layout/grid_layout.ts +1 -1
  232. package/treb-grid/src/layout/mock-layout.ts +1 -1
  233. package/treb-grid/src/render/selection-renderer.ts +1 -1
  234. package/treb-grid/src/render/svg_header_overlay.ts +1 -1
  235. package/treb-grid/src/render/svg_selection_block.ts +1 -1
  236. package/treb-grid/src/render/tile_renderer.ts +36 -7
  237. package/treb-grid/src/types/border_constants.ts +1 -1
  238. package/treb-grid/src/types/clipboard_data.ts +1 -1
  239. package/treb-grid/src/types/clipboard_data2.ts +1 -1
  240. package/treb-grid/src/types/drag_mask.ts +1 -1
  241. package/treb-grid/src/types/external_editor_config.ts +1 -1
  242. package/treb-grid/src/types/grid.ts +181 -40
  243. package/treb-grid/src/types/grid_base.ts +7 -4
  244. package/treb-grid/src/types/grid_command.ts +7 -1
  245. package/treb-grid/src/types/grid_events.ts +2 -1
  246. package/treb-grid/src/types/grid_options.ts +1 -1
  247. package/treb-grid/src/types/scale-control.ts +1 -1
  248. package/treb-grid/src/types/set_range_options.ts +1 -1
  249. package/treb-grid/src/types/tab_bar.ts +1 -1
  250. package/treb-grid/src/types/tile.ts +1 -1
  251. package/treb-grid/src/types/update_flags.ts +1 -1
  252. package/treb-grid/src/util/fontmetrics.ts +1 -1
  253. package/treb-grid/src/util/ua.ts +1 -1
  254. package/treb-parser/src/csv-parser.ts +1 -1
  255. package/treb-parser/src/index.ts +1 -1
  256. package/treb-parser/src/md-parser.ts +1 -1
  257. package/treb-parser/src/parser-types.ts +1 -1
  258. package/treb-parser/src/parser.ts +1 -1
  259. package/treb-utils/src/event_source.ts +1 -1
  260. package/treb-utils/src/ievent_source.ts +1 -1
  261. package/treb-utils/src/index.ts +1 -1
  262. package/treb-utils/src/measurement.ts +1 -1
  263. package/treb-utils/src/scale.ts +1 -1
  264. package/treb-utils/src/serialize_html.ts +1 -1
  265. package/treb-utils/src/validate_uri.ts +1 -1
  266. package/tsproject.json +2 -3
  267. package/treb-embed/src/export-worker.ts +0 -44
@@ -14,7 +14,7 @@
14
14
  * You should have received a copy of the GNU General Public License along
15
15
  * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
16
  *
17
- * Copyright 2022-2025 trebco, llc.
17
+ * Copyright 2022-2026 trebco, llc.
18
18
  * info@treb.app
19
19
  *
20
20
  */
@@ -35,7 +35,7 @@ import type { SerializedValueType } from 'treb-base-types';
35
35
  import type { Sheet} from './workbook-sheet';
36
36
  import { VisibleState } from './workbook-sheet';
37
37
  import type { CellAnchor } from './drawing/drawing';
38
- import { type GenericDOMElement, XMLUtils } from './xml-utils';
38
+ // import { type GenericDOMElement, XMLUtils } from './xml-utils';
39
39
 
40
40
  // import { one_hundred_pixels } from './constants';
41
41
  import { ColumnWidthToPixels } from './column-width';
@@ -44,6 +44,10 @@ import { ZipWrapper } from './zip-wrapper';
44
44
  import type { ConditionalFormat } from 'treb-data-model';
45
45
  import { LookupMetadata, type MetadataFlags } from './metadata';
46
46
 
47
+ import * as OOXML from 'ooxml-types';
48
+ import { EnsureArray, FirstTag, IterateTags } from './ooxml';
49
+
50
+
47
51
  interface SharedFormula {
48
52
  row: number;
49
53
  column: number;
@@ -53,6 +57,7 @@ interface SharedFormula {
53
57
 
54
58
  interface SharedFormulaMap { [index: string]: SharedFormula }
55
59
 
60
+ /*
56
61
  interface CellElementType {
57
62
  a$: {
58
63
  r?: string;
@@ -121,6 +126,7 @@ interface ConditionalFormatRule {
121
126
  const ElementHasTextNode = (test: unknown): test is {t$: string} => {
122
127
  return typeof test === 'object' && typeof (test as {$t: string}).$t !== 'undefined';
123
128
  }
129
+ */
124
130
 
125
131
  export class Importer {
126
132
 
@@ -146,7 +152,7 @@ export class Importer {
146
152
 
147
153
  public ParseCell(
148
154
  sheet: Sheet,
149
- element: CellElementType,
155
+ element: OOXML.Cell, // CellElementType,
150
156
  shared_formulae: SharedFormulaMap,
151
157
  arrays: RangeType[],
152
158
  dynamic_arrays: RangeType[],
@@ -156,8 +162,8 @@ export class Importer {
156
162
  ): CellParseResult | undefined {
157
163
 
158
164
  // must have, at minimum, an address (must be a single cell? FIXME)
159
- const address_attr = element.a$?.r;
160
- if (!address_attr) {
165
+ const address_attr = element.$attributes?.r;
166
+ if (address_attr === undefined) {
161
167
  console.warn('cell missing address');
162
168
  return undefined;
163
169
  }
@@ -168,10 +174,10 @@ export class Importer {
168
174
  return undefined;
169
175
  }
170
176
 
171
- // new (to us) metadata
177
+ // metadata
172
178
  let metadata_flags: MetadataFlags = {};
173
- if (element.a$?.cm) {
174
- const cm_index = Number(element.a$?.cm);
179
+ if (element.$attributes?.cm !== undefined) {
180
+ const cm_index = element.$attributes.cm;
175
181
  if (this.workbook?.metadata) {
176
182
  metadata_flags = LookupMetadata(this.workbook.metadata, 'cell', cm_index).flags;
177
183
  }
@@ -207,10 +213,10 @@ export class Importer {
207
213
 
208
214
  // console.info(address, 'e', element, 'm', mapped);
209
215
 
210
- if (element.a$?.t && element.a$.t === 's') {
211
- type = 'string'; // ValueType.string;
212
- if (typeof element.v !== 'undefined') {
213
- const index = Number(element.v);
216
+ if (element.$attributes?.t === 's') {
217
+ type = 'string';
218
+ if (element.v?.$text !== undefined) {
219
+ const index = Number(element.v.$text);
214
220
  if (!isNaN(index) && sheet.shared_strings) {
215
221
  value = sheet.shared_strings.Get(index) || '';
216
222
  if (value[0] === '=') { value = '\'' + value; }
@@ -218,15 +224,11 @@ export class Importer {
218
224
  }
219
225
  }
220
226
  else {
221
- if (typeof element.f !== 'undefined') {
222
- type = 'formula'; // ValueType.formula;
223
-
224
- const formula = (typeof element.f === 'string' ? element.f : element.f.t$) || '';
225
-
227
+ if (element.f !== undefined) {
228
+ type = 'formula';
229
+ const formula = element.f.$text || '';
226
230
  if (formula) {
227
231
 
228
- // console.info("F", formula);
229
-
230
232
  // doing it like this is sloppy (also does not work properly).
231
233
  value = '=' + formula.replace(/^_xll\./g, '');
232
234
 
@@ -245,11 +247,15 @@ export class Importer {
245
247
  name = name.substring(5);
246
248
  }
247
249
  if (/^_xlfn\./.test(name)) {
248
- console.info("xlfn:", name);
250
+ if (process.env.NODE_ENV !== 'production') {
251
+ console.info("xlfn:", name);
252
+ }
249
253
  name = name.substring(6);
250
254
  }
251
255
  if (/^_xlws\./.test(name)) {
252
- console.info("xlws:", name);
256
+ if (process.env.NODE_ENV !== 'production') {
257
+ console.info("xlws:", name);
258
+ }
253
259
  name = name.substring(6);
254
260
  }
255
261
 
@@ -294,19 +300,26 @@ export class Importer {
294
300
 
295
301
  unit.name = TrimPrefixes(unit.name);
296
302
 
297
- /*
298
- if (/^_xll\./.test(unit.name)) {
299
- unit.name = unit.name.substring(5);
300
- }
301
- if (/^_xlfn\./.test(unit.name)) {
302
- console.info("xlfn:", unit.name);
303
- unit.name = unit.name.substring(6);
304
- }
305
- if (/^_xlws\./.test(unit.name)) {
306
- console.info("xlws:", unit.name);
307
- unit.name = unit.name.substring(6);
303
+ // excel export may be translating dynamic range references
304
+ // (e.g D2#) to `ANCHORARRAY(D2)`. this is for compatibility
305
+ // with older versions of excel, I guess?
306
+
307
+ // we can translate these but let's be conservative here and
308
+ // start with just ANCHORARRAY taking a single address --
309
+ // that we know we can handle.
310
+
311
+ // so for example, if the formula is `=SUM(ANCHORARRAY(D2))`
312
+ // we can translate that to explicitly `=SUM(D2#)`. in our
313
+ // scheme that's just the address plus a flag bit indicating
314
+ // "take the full dynamic array range".
315
+
316
+ if (unit.name === 'ANCHORARRAY') {
317
+ if (unit.args.length === 1 && unit.args[0].type === 'address') {
318
+ return {
319
+ ...(unit.args[0]), spill: true,
320
+ }
321
+ }
308
322
  }
309
- */
310
323
 
311
324
  }
312
325
  return true;
@@ -319,20 +332,18 @@ export class Importer {
319
332
  }
320
333
  }
321
334
 
322
- if (typeof element.f !== 'string') {
323
- if (element.f.a$?.t === 'shared' && element.f.a$.si) {
324
- shared_formulae[element.f.a$.si] = {
325
- row: address.row - 1,
326
- column: address.col - 1,
327
- formula: value,
328
- parse_result: this.parser.Parse(value),
329
- };
330
- }
335
+ if (element.f.$attributes?.t === 'shared' && element.f.$attributes.si !== undefined) {
336
+ shared_formulae[element.f.$attributes.si] = {
337
+ row: address.row - 1,
338
+ column: address.col - 1,
339
+ formula: value,
340
+ parse_result: this.parser.Parse(value),
341
+ };
331
342
  }
332
343
 
333
344
  }
334
- else if ((typeof element.f !== 'string') && element.f.a$?.t === 'shared' && element.f.a$.si) {
335
- const f = shared_formulae[element.f.a$.si];
345
+ else if (element.f.$attributes?.t === 'shared' && element.f.$attributes.si !== undefined) {
346
+ const f = shared_formulae[element.f.$attributes.si];
336
347
  if (f) {
337
348
  if (f.parse_result.expression) {
338
349
  value = '=' + this.parser.Render(f.parse_result.expression, {
@@ -354,8 +365,8 @@ export class Importer {
354
365
  // arrays and spill/dynamic arrays
355
366
  //
356
367
 
357
- if (typeof element.f !== 'string' && element.f.a$?.t === 'array') {
358
- const translated = sheet.TranslateAddress(element.f.a$.ref || '');
368
+ if (element.f.$attributes?.t === 'array') {
369
+ const translated = sheet.TranslateAddress(element.f.$attributes.ref ?? '');
359
370
 
360
371
  // why are we checking "is_range" here? this should be valid
361
372
  // even if the ref attribute is one cell, if it explicitly
@@ -385,31 +396,37 @@ export class Importer {
385
396
  }
386
397
  }
387
398
 
388
- if (typeof element.v !== 'undefined') {
399
+ if (element.v !== undefined) {
400
+
401
+ const V = element.v?.$text ?? '';
389
402
 
390
- const V = (typeof element.v === 'object') ? element.v?.t$ : element.v;
403
+ // FIXME: use parser?
391
404
 
392
- const num = Number(V.toString());
405
+ const num = Number(V);
393
406
  if (!isNaN(num)) {
394
- calculated_type = 'number'; // ValueType.number;
407
+ calculated_type = 'number';
395
408
  calculated_value = num;
396
409
  }
397
410
  else {
398
- calculated_type = 'string'; // ValueType.string;
399
- calculated_value = V.toString();
411
+ calculated_type = 'string';
412
+ calculated_value = V;
400
413
  }
414
+
401
415
  }
402
416
 
403
417
  }
404
- else if (typeof element.v !== 'undefined') {
405
- const num = Number(element.v.toString());
418
+ else if (element.v !== undefined) {
419
+
420
+ // FIXME: use parser?
421
+
422
+ const num = Number(element.v.$text || '');
406
423
  if (!isNaN(num)) {
407
- type = 'number'; // ValueType.number;
424
+ type = 'number';
408
425
  value = num;
409
426
  }
410
427
  else {
411
- type = 'string'; // ValueType.string;
412
- value = element.v.toString();
428
+ type = 'string';
429
+ value = element.v.$text || '';
413
430
  }
414
431
  }
415
432
  }
@@ -441,8 +458,8 @@ export class Importer {
441
458
  result.calculated = calculated_value;
442
459
  }
443
460
 
444
- if (element.a$?.s) {
445
- result.style_ref = Number(element.a$.s);
461
+ if (element.$attributes?.s !== undefined) {
462
+ result.style_ref = element.$attributes.s;
446
463
  }
447
464
 
448
465
  for (const link of links) {
@@ -522,68 +539,70 @@ export class Importer {
522
539
 
523
540
  }
524
541
 
525
- public ParseConditionalFormat(address: RangeType|AddressType, rule: ConditionalFormatRule, extensions?: any[]): ConditionalFormat|ConditionalFormat[]|undefined {
542
+ public ParseConditionalFormat(
543
+ address: RangeType|AddressType,
544
+ rule: OOXML.CfRule, // ConditionalFormatRule,
545
+ extensions: OOXML.X14ConditionalFormatting[] = []): ConditionalFormat|ConditionalFormat[]|undefined {
526
546
 
527
547
  const area = this.AddressToArea(address);
528
548
  const operators = ConditionalFormatOperators;
529
549
 
530
550
  // console.info({rule});
531
551
 
532
- switch (rule.a$.type) {
552
+ switch (rule.$attributes?.type) {
533
553
  case 'duplicateValues':
534
554
  case 'uniqueValues':
535
555
 
536
556
  {
537
557
  let style = {};
538
558
 
539
- if (rule.a$.dxfId) {
540
- const index = Number(rule.a$.dxfId);
541
- if (!isNaN(index)) {
542
- style = this.workbook?.style_cache.dxf_styles[index] || {};
543
- }
559
+ if (rule.$attributes.dxfId !== undefined) {
560
+ style = this.workbook?.style_cache.dxf_styles[rule.$attributes.dxfId] || {};
544
561
  }
545
562
 
546
563
  return {
547
564
  type: 'duplicate-values',
548
565
  area,
549
566
  style,
550
- unique: (rule.a$.type === 'uniqueValues'),
551
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
552
-
567
+ unique: (rule.$attributes.type === 'uniqueValues'),
568
+ priority: rule.$attributes.priority,
553
569
  };
570
+
554
571
  }
555
572
 
556
573
  case 'cellIs':
557
- if (rule.a$.operator && (rule.formula || typeof rule.formula === 'number')) {
574
+ if (rule.$attributes.operator && rule.formula) {
558
575
  let style = {};
559
576
 
560
- if (rule.a$.dxfId) {
561
- const index = Number(rule.a$.dxfId);
562
- if (!isNaN(index)) {
563
- style = this.workbook?.style_cache.dxf_styles[index] || {};
564
- }
577
+ if (rule.$attributes.dxfId !== undefined) {
578
+ style = this.workbook?.style_cache.dxf_styles[rule.$attributes.dxfId] || {};
565
579
  }
566
580
 
567
- if (rule.a$.operator === 'between') {
568
- if (Array.isArray(rule.formula) && rule.formula.length === 2
569
- && typeof rule.formula[0] === 'number' && typeof rule.formula[1] === 'number') {
581
+ if (rule.$attributes.operator === 'between') {
582
+ if (Array.isArray(rule.formula) && rule.formula.length === 2) {
583
+ // && typeof rule.formula[0] === 'number' && typeof rule.formula[1] === 'number') {
584
+
585
+ const between: [number, number] = [
586
+ Number(rule.formula[0]?.$text || ''),
587
+ Number(rule.formula[1]?.$text || ''),
588
+ ];
570
589
 
571
590
  return {
572
591
  type: 'cell-match',
573
592
  expression: '',
574
- between: rule.formula, // special case? ugh
593
+ between, // : rule.formula, // special case? ugh
575
594
  area,
576
595
  style,
577
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
596
+ priority: rule.$attributes.priority,
578
597
  };
579
598
 
580
599
  }
581
600
  }
582
601
 
583
- const operator = operators[rule.a$.operator || ''];
602
+ const operator = operators[rule.$attributes.operator || ''];
584
603
 
585
604
  if (!operator) {
586
- console.info('unhandled cellIs operator:', rule.a$.operator, {rule});
605
+ console.info('unhandled cellIs operator:', rule.$attributes.operator, {rule});
587
606
  }
588
607
  else {
589
608
  return {
@@ -591,7 +610,7 @@ export class Importer {
591
610
  expression: operator + ' ' + rule.formula,
592
611
  area,
593
612
  style,
594
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
613
+ priority: rule.$attributes.priority,
595
614
  };
596
615
  }
597
616
 
@@ -607,6 +626,10 @@ export class Importer {
607
626
 
608
627
  if (rule.formula) {
609
628
 
629
+ const first = FirstTag(rule.formula);
630
+ const formula = first?.$text || '';
631
+
632
+ /*
610
633
  if (typeof rule.formula !== 'string') {
611
634
  if (ElementHasTextNode(rule.formula)) {
612
635
 
@@ -622,17 +645,15 @@ export class Importer {
622
645
  rule.formula = '';
623
646
  }
624
647
  }
648
+ */
625
649
 
626
650
  let style = {};
627
651
 
628
- if (rule.a$.dxfId) {
629
- const index = Number(rule.a$.dxfId);
630
- if (!isNaN(index)) {
631
- style = this.workbook?.style_cache.dxf_styles[index] || {};
632
- }
652
+ if (rule.$attributes.dxfId !== undefined) {
653
+ style = this.workbook?.style_cache.dxf_styles[rule.$attributes.dxfId] || {};
633
654
  }
634
655
 
635
- if (rule.a$.type === 'expression' && (area.start.row !== area.end.row || area.start.column !== area.end.column)) {
656
+ if (rule.$attributes.type === 'expression' && (area.start.row !== area.end.row || area.start.column !== area.end.column)) {
636
657
 
637
658
  // (1) this is only required if there are relative references
638
659
  // in the formula. so we could check and short-circuit.
@@ -646,7 +667,7 @@ export class Importer {
646
667
  const list: ConditionalFormat[] = [];
647
668
  const a2 = new Area(area.start, area.end);
648
669
 
649
- const parse_result = this.parser.Parse(rule.formula);
670
+ const parse_result = this.parser.Parse(formula);
650
671
  if (parse_result.expression) {
651
672
  for (const cell of a2) {
652
673
  const f = this.parser.Render(parse_result.expression, {
@@ -659,7 +680,7 @@ export class Importer {
659
680
  expression: f,
660
681
  style,
661
682
  area: { start: cell, end: cell },
662
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
683
+ priority: rule.$attributes.priority,
663
684
  })
664
685
 
665
686
  // console.info(f);
@@ -673,10 +694,10 @@ export class Importer {
673
694
 
674
695
  return {
675
696
  type: 'expression',
676
- expression: rule.formula,
697
+ expression: formula,
677
698
  area,
678
699
  style,
679
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
700
+ priority: rule.$attributes.priority,
680
701
  };
681
702
 
682
703
  }
@@ -684,38 +705,49 @@ export class Importer {
684
705
 
685
706
  case 'dataBar':
686
707
  {
687
- const hide_values = (rule.dataBar?.a$?.showValue === '0');
688
- let extension: any = undefined;
708
+ const show_value = rule.dataBar?.$attributes?.showValue ?? true; // default true
689
709
 
690
- if (rule.extLst?.ext?.['x14:id']) {
691
- for (const test of (extensions || [])) {
692
- if (test['x14:cfRule']?.a$?.id === rule.extLst.ext['x14:id']) {
693
- extension = test;
694
- break;
710
+ // const hide_values = !rule.dataBar?.$attributes?.showValue;
711
+ // let extension: any = undefined;
712
+
713
+ let extension: OOXML.X14ConditionalFormatting|undefined;
714
+
715
+ IterateTags(rule.extLst?.ext, ext => {
716
+ if (ext.id !== undefined) {
717
+ for (const test of extensions) {
718
+ return IterateTags(test.cfRule, cfRule => {
719
+ if (cfRule.$attributes?.id === ext.id) {
720
+ extension = test;
721
+ return false;
722
+ }
723
+ });
695
724
  }
696
725
  }
697
- if (!extension) {
698
- console.info("conditional format extension not found");
699
- }
726
+ });
727
+
728
+ if (!extension) {
729
+ console.info("conditional format extension not found");
700
730
  }
701
731
 
702
- if (rule.dataBar?.color?.a$?.rgb) {
732
+ if (rule.dataBar?.color?.$attributes?.rgb) {
703
733
 
704
734
  let negative: Color|undefined = undefined;
735
+ const first = FirstTag(extension?.cfRule);
705
736
 
706
- if (extension?.['x14:cfRule']?.['x14:dataBar']?.['x14:negativeFillColor']?.a$?.rgb) {
707
- const rgb = extension['x14:cfRule']['x14:dataBar']['x14:negativeFillColor'].a$.rgb;
737
+ const rgb = first?.dataBar?.negativeFillColor?.$attributes?.rgb;
738
+ if (rgb !== undefined) {
708
739
  negative = { text: '#' + rgb.toString().substring(2) };
709
740
  }
710
741
 
711
- const fill: Color = { text: '#' + rule.dataBar.color.a$.rgb.substring(2) };
742
+ const fill: Color = { text: '#' + rule.dataBar.color.$attributes.rgb.substring(2) };
712
743
  return {
713
744
  type: 'data-bar',
714
745
  area,
715
746
  fill,
716
- hide_values,
747
+ hide_values: !show_value,
717
748
  negative,
718
749
  };
750
+
719
751
  }
720
752
  }
721
753
  break;
@@ -729,17 +761,17 @@ export class Importer {
729
761
  const color: Color = {};
730
762
 
731
763
  const color_element = rule.colorScale.color[index];
732
- if (color_element.a$.rgb) {
733
- (color as HTMLColor).text = '#' + color_element.a$.rgb.substring(2);
764
+ if (color_element.$attributes?.rgb) {
765
+ (color as HTMLColor).text = '#' + color_element.$attributes.rgb.substring(2);
734
766
  }
735
- else if (color_element.a$.theme) {
736
- (color as ThemeColor).theme = Number(color_element.a$.theme) || 0;
737
- if (color_element.a$.tint) {
738
- (color as ThemeColor).tint = Math.round(Number(color_element.a$.tint) * 1000) / 1000;
767
+ else if (color_element.$attributes?.theme) {
768
+ (color as ThemeColor).theme = Number(color_element.$attributes.theme) || 0;
769
+ if (color_element.$attributes.tint) {
770
+ (color as ThemeColor).tint = Math.round(Number(color_element.$attributes.tint) * 1000) / 1000;
739
771
  }
740
772
  }
741
773
 
742
- switch (entry.a$.type) {
774
+ switch (entry.$attributes?.type) {
743
775
  case 'min':
744
776
  value = 0;
745
777
  break;
@@ -749,7 +781,7 @@ export class Importer {
749
781
  break;
750
782
 
751
783
  case 'percentile':
752
- value = (Number(entry.a$.val) || 0) / 100;
784
+ value = (Number(entry.$attributes.val) || 0) / 100;
753
785
  break;
754
786
  }
755
787
 
@@ -762,8 +794,7 @@ export class Importer {
762
794
  stops,
763
795
  color_space: 'RGB',
764
796
  area,
765
- priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
766
-
797
+ priority: rule.$attributes.priority,
767
798
  };
768
799
 
769
800
  }
@@ -811,25 +842,25 @@ export class Importer {
811
842
 
812
843
  const annotations: AnchoredAnnotation[] = [];
813
844
 
814
- const FindAll: <T = GenericDOMElement>(path: string) => T[] = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
845
+ // const FindAll: <T = GenericDOMElement>(path: string) => T[] = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
815
846
 
816
847
  // tab color
817
848
 
818
- const tab_color_element = FindAll('worksheet/sheetPr/tabColor');
849
+ // const tab_color_element = FindAll('worksheet/sheetPr/tabColor');
819
850
 
851
+ const tab_color_element = sheet.root.sheetPr?.tabColor;
820
852
  let tab_color: Color|undefined;
821
853
 
822
- if (tab_color_element?.[0]) {
854
+ if (tab_color_element) {
823
855
 
824
- const element = tab_color_element[0];
825
- if (element.a$?.theme) {
826
- tab_color = { theme: Number(element.a$.theme) };
827
- if (element.a$?.tint) {
828
- tab_color.tint = Number(element.a$.tint);
856
+ if (tab_color_element.$attributes?.theme !== undefined) {
857
+ tab_color = { theme: tab_color_element.$attributes.theme };
858
+ if (tab_color_element.$attributes.tint !== undefined) {
859
+ tab_color.tint = tab_color_element.$attributes.tint;
829
860
  }
830
861
  }
831
- if (element.a$?.rgb) {
832
- const argb = element.a$.rgb;
862
+ if (tab_color_element.$attributes?.rgb !== undefined) {
863
+ const argb = tab_color_element.$attributes.rgb;
833
864
  tab_color = {
834
865
  text: '#' + (
835
866
  argb.length > 6 ?
@@ -842,26 +873,31 @@ export class Importer {
842
873
 
843
874
  // conditionals
844
875
 
845
- const conditional_formatting = FindAll('worksheet/conditionalFormatting');
876
+ // const conditional_formatting = // FindAll('worksheet/conditionalFormatting');
877
+ // sheet.root.conditionalFormatting;
846
878
 
847
879
  // we might need extensions as well? TODO
880
+ // const conditional_formattings = FindAll('worksheet/extLst/ext/x14:conditionalFormattings/x14:conditionalFormatting');
881
+
882
+ const extensions: OOXML.X14ConditionalFormatting[] = [];
883
+ IterateTags(sheet.root.extLst?.ext, ext => {
884
+ extensions.push(...EnsureArray(ext.conditionalFormattings?.conditionalFormatting));
885
+ });
848
886
 
849
- const conditional_formattings = FindAll('worksheet/extLst/ext/x14:conditionalFormattings/x14:conditionalFormatting');
887
+ IterateTags(sheet.root.conditionalFormatting, element => {
850
888
 
851
- for (const element of conditional_formatting) {
852
- if (element.a$?.sqref ){
889
+ if (element.$attributes?.sqref ){
853
890
 
854
891
  // FIXME: this attribute might include multiple ranges? e.g.:
855
892
  //
856
893
  // <conditionalFormatting sqref="B31:I31 B10:E30 G10:I30 F14:F15">
857
894
 
858
- const parts = element.a$.sqref.split(/\s+/);
895
+ const parts = element.$attributes.sqref.split(/\s+/);
859
896
  for (const part of parts) {
860
897
  const area = sheet.TranslateAddress(part);
861
898
  if (element.cfRule) {
862
- const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
863
- for (const rule of rules) {
864
- const format = this.ParseConditionalFormat(area, rule as unknown as ConditionalFormatRule, conditional_formattings);
899
+ IterateTags(element.cfRule, rule => {
900
+ const format = this.ParseConditionalFormat(area, rule, extensions);
865
901
  if (format) {
866
902
  if (Array.isArray(format)) {
867
903
  conditional_formats.push(...format);
@@ -870,36 +906,37 @@ export class Importer {
870
906
  conditional_formats.push(format);
871
907
  }
872
908
  }
873
- }
909
+ });
874
910
  }
875
911
  }
876
912
 
877
913
  }
878
- }
914
+
915
+ });
879
916
 
880
917
  // merges
881
918
 
882
- const merge_cells = FindAll('worksheet/mergeCells/mergeCell');
919
+ // const merge_cells = FindAll('worksheet/mergeCells/mergeCell');
883
920
 
884
- for (const element of merge_cells) {
885
- if (element.a$?.ref) {
886
- const merge = sheet.TranslateAddress(element.a$.ref);
921
+ IterateTags(sheet.root.mergeCells?.mergeCell, element => {
922
+ if (element.$attributes?.ref) {
923
+ const merge = sheet.TranslateAddress(element.$attributes.ref);
887
924
  if (is_range(merge)) {
888
925
  merges.push(ShiftRange(merge, -1, -1));
889
926
  }
890
927
  }
891
- }
928
+ });
892
929
 
893
930
  // validation
894
931
 
895
- const validation_entries = FindAll('worksheet/dataValidations/dataValidation');
896
- for (const entry of validation_entries) {
897
- const type = entry.a$?.type;
898
- const ref = entry.a$?.sqref;
899
- const formula = entry.formula1;
932
+ IterateTags(sheet.root.dataValidations?.dataValidation, entry => {
933
+
934
+ const type = entry.$attributes?.type;
935
+ const ref = entry.$attributes?.sqref;
936
+ const formula = entry.formula1?.$text || '';
937
+
938
+ if (ref && formula && type === 'list') {
900
939
 
901
- if (ref && formula && typeof formula === 'string' && type === 'list') {
902
- // let address: ICellAddress|undefined;
903
940
  let validation: DataValidation|undefined;
904
941
  let parse_result = this.parser.Parse(ref);
905
942
  const target: IArea[] = [];
@@ -912,11 +949,9 @@ export class Importer {
912
949
 
913
950
  if (parse_result.expression) {
914
951
  if (parse_result.expression.type === 'address') {
915
- // address = parse_result.expression;
916
952
  target.push({start: parse_result.expression, end: parse_result.expression});
917
953
  }
918
954
  else if (parse_result.expression.type === 'range') {
919
- // address = parse_result.expression.start;
920
955
  target.push(parse_result.expression);
921
956
  }
922
957
  }
@@ -991,15 +1026,15 @@ export class Importer {
991
1026
 
992
1027
  }
993
1028
 
994
- }
1029
+ });
995
1030
 
996
1031
  // links
997
1032
 
998
- const hyperlinks = FindAll('worksheet/hyperlinks/hyperlink');
1033
+ // const hyperlinks = FindAll('worksheet/hyperlinks/hyperlink');
1034
+ // for (const child of hyperlinks) {
1035
+ IterateTags(sheet.root.hyperlinks?.hyperlink, child => {
999
1036
 
1000
- for (const child of hyperlinks) {
1001
-
1002
- let address = sheet.TranslateAddress(child.a$?.ref || '');
1037
+ let address = sheet.TranslateAddress(child.$attributes?.ref || '');
1003
1038
  if (is_range(address)) {
1004
1039
  address = address.from;
1005
1040
  }
@@ -1007,42 +1042,46 @@ export class Importer {
1007
1042
  let text = '';
1008
1043
  let reference = '';
1009
1044
 
1010
- if (child.a$ && child.a$['r:id']) {
1045
+ if (child.$attributes?.id !== undefined) {
1011
1046
 
1012
1047
  text = 'remote link';
1013
- const relationship = sheet.rels[child.a$['r:id']];
1048
+ const relationship = sheet.rels[child.$attributes.id];
1014
1049
  if (relationship) {
1015
1050
  reference = relationship.target || '';
1016
1051
  }
1017
1052
 
1018
1053
  }
1019
1054
  else {
1055
+
1056
+ // what's up with these weird attributes? did we change this at
1057
+ // some point and not update this block? (probably)
1058
+
1059
+ /*
1020
1060
  reference = typeof child.__location === 'string' ? child.__location : '';
1021
1061
  text = typeof child.__display === 'string' ? child.__display : '';
1062
+ */
1063
+
1064
+ reference = child.$attributes?.location || '';
1065
+ text = child.$attributes?.display || '';
1066
+
1022
1067
  }
1023
1068
 
1024
1069
  links.push({ address, reference, text });
1025
- }
1070
+
1071
+ });
1026
1072
 
1027
1073
  // base
1028
1074
 
1029
1075
  let default_row_height = 21;
1030
1076
  let default_column_width = 100; // ?
1031
1077
 
1032
- const sheet_format = sheet.sheet_data.worksheet?.sheetFormatPr;
1078
+ const sheet_format = sheet.root.sheetFormatPr;
1033
1079
  if (sheet_format) {
1034
- if (sheet_format.a$?.defaultColWidth) {
1035
- const width = Number(sheet_format.a$.defaultColWidth);
1036
- if (!isNaN(width)) {
1037
- // default_column_width = Math.round(width / one_hundred_pixels * 100);
1038
- default_column_width = ColumnWidthToPixels(width);
1039
- }
1080
+ if (sheet_format.$attributes?.defaultColWidth !== undefined) {
1081
+ default_column_width = ColumnWidthToPixels(sheet_format.$attributes.defaultColWidth);
1040
1082
  }
1041
- if (sheet_format.a$?.defaultRowHeight) {
1042
- const height = Number(sheet_format.a$.defaultRowHeight);
1043
- if (!isNaN(height)) {
1044
- default_row_height = Math.round(height * 4 / 3); // ??
1045
- }
1083
+ if (sheet_format.$attributes?.defaultRowHeight) {
1084
+ default_row_height = Math.round((sheet_format.$attributes.defaultRowHeight) * 4 / 3); // ??
1046
1085
  }
1047
1086
  }
1048
1087
 
@@ -1051,30 +1090,24 @@ export class Importer {
1051
1090
  const row_heights: number[] = [];
1052
1091
  const outline: number[] = [];
1053
1092
 
1054
- const rows = FindAll('worksheet/sheetData/row');
1093
+ // const rows = FindAll('worksheet/sheetData/row');
1094
+ // for (const row of rows) {
1095
+
1096
+ IterateTags(sheet.root.sheetData.row, row => {
1055
1097
 
1056
- for (const row of rows) {
1057
- const row_index = row.a$?.r ? Number(row.a$.r) : 1;
1098
+ const row_index = row.$attributes?.r ?? 1;
1058
1099
 
1059
1100
  let height = default_row_height;
1060
- if (row.a$?.ht) {
1061
- const num = Number(row.a$.ht);
1062
- if (!isNaN(num)) {
1063
- height = Math.round(num * 4 / 3); // seems to be the excel unit -> pixel ratio
1064
- }
1101
+ if (row.$attributes?.ht !== undefined) {
1102
+ height = Math.round((row.$attributes.ht) * 4 / 3); // seems to be the excel unit -> pixel ratio
1065
1103
  }
1066
- if (row.a$?.outlineLevel) {
1067
- const num = Number(row.a$.outlineLevel);
1068
- if (!isNaN(num)) {
1069
- outline[row_index - 1] = num;
1070
- }
1104
+
1105
+ if (row.$attributes?.outlineLevel !== undefined) {
1106
+ outline[row_index - 1] = row.$attributes?.outlineLevel;
1071
1107
  }
1072
1108
 
1073
- if (row.a$?.s) {
1074
- const style_reference = Number(row.a$?.s);
1075
- if (!isNaN(style_reference)) {
1076
- row_styles[row_index - 1] = style_reference;
1077
- }
1109
+ if (row.$attributes?.s !== undefined) {
1110
+ row_styles[row_index - 1] = row.$attributes.s;
1078
1111
  }
1079
1112
 
1080
1113
  // if there's a height which is not === default height, but
@@ -1085,32 +1118,30 @@ export class Importer {
1085
1118
  row_heights[row_index - 1] = height;
1086
1119
  }
1087
1120
 
1088
- const cells = row.c ? Array.isArray(row.c) ? row.c : [row.c] : [];
1089
-
1090
- for (const element of cells) {
1091
- const cell = this.ParseCell(sheet, element as unknown as CellElementType, shared_formulae, arrays, dynamic_arrays, merges, links); // , validations);
1121
+ // const cells = row.c ? Array.isArray(row.c) ? row.c : [row.c] : [];
1122
+ // for (const element of cells) {
1123
+ IterateTags(row.c, element => {
1124
+ const cell = this.ParseCell(sheet, element, shared_formulae, arrays, dynamic_arrays, merges, links);
1092
1125
  if (cell) {
1093
1126
  data.push(cell);
1094
1127
  }
1095
- }
1096
- }
1128
+ });
1129
+
1130
+ });
1097
1131
 
1098
1132
  const column_styles: number[] = [];
1099
1133
  let default_column_style = -1;
1100
1134
  const column_widths: number[] = [];
1101
1135
 
1102
- const columns = FindAll('worksheet/cols/col');
1103
-
1104
- for (const child of columns) {
1105
-
1106
- const min = Number(child.a$?.min);
1107
- const max = Number(child.a$?.max);
1108
-
1109
- if (child.a$?.style) {
1136
+ IterateTags(sheet.root.cols, cols => {
1137
+ IterateTags(cols.col, child => {
1138
+
1139
+ const min = child.$attributes?.min ?? 0;
1140
+ const max = child.$attributes?.max ?? 0;
1110
1141
 
1111
- const style = Number(child.a$.style);
1142
+ if (child.$attributes?.style !== undefined) {
1112
1143
 
1113
- if (!isNaN(min) && !isNaN(max) && !isNaN(style)) {
1144
+ const style = child.$attributes.style;
1114
1145
 
1115
1146
  // this is not the way to do this? for the time being
1116
1147
  // it's OK because style doesn't need to extend past
@@ -1126,13 +1157,9 @@ export class Importer {
1126
1157
  }
1127
1158
 
1128
1159
  }
1160
+ if (child.$attributes?.customWidth) {
1129
1161
 
1130
- }
1131
- if (child.a$?.customWidth) {
1132
-
1133
- let width = Number(child.a$.width);
1134
-
1135
- if (!isNaN(min) && !isNaN(max) && !isNaN(width)) {
1162
+ let width = child.$attributes.width ?? 0;
1136
1163
 
1137
1164
  if (max === 16384) {
1138
1165
 
@@ -1152,97 +1179,103 @@ export class Importer {
1152
1179
  }
1153
1180
  }
1154
1181
 
1155
- }
1156
- }
1182
+ });
1183
+ });
1157
1184
 
1158
1185
  // --- import tables -------------------------------------------------------
1159
1186
 
1160
- const table_references = FindAll('worksheet/tableParts/tablePart')
1161
- for (const child of table_references) {
1162
- const rel = child.a$ ? child.a$['r:id'] : undefined;
1163
- if (rel) {
1164
- let reference = '';
1187
+ IterateTags(sheet.root.tableParts, tablePart => {
1188
+ IterateTags(tablePart.tablePart, child => {
1165
1189
 
1166
- const relationship = sheet.rels[rel];
1167
- if (relationship) {
1168
- reference = relationship.target || '';
1169
- const description = this.workbook.ReadTable(reference);
1170
- if (description) {
1171
-
1172
- // console.info({description});
1173
-
1174
- const ref = sheet.TranslateAddress(description.ref);
1175
- const area: IArea = is_address(ref) ? {
1176
- start: { row: ref.row - 1, column: ref.col - 1},
1177
- end: { row: ref.row - 1, column: ref.col - 1},
1178
- } : {
1179
- start: { row: ref.from.row - 1, column: ref.from.col - 1},
1180
- end: { row: ref.to.row - 1, column: ref.to.col - 1},
1181
- };
1190
+ const rel = child.$attributes?.id;
1191
+ if (rel !== undefined) {
1192
+ let reference = '';
1182
1193
 
1183
- for (const cell of data) {
1184
- if (cell.row === area.start.row && cell.column === area.start.column) {
1185
- cell.table = {
1186
- area,
1187
- name: description.name,
1188
- totals_row: (!!description.totals_row_count),
1194
+ const relationship = sheet.rels[rel];
1195
+ if (relationship) {
1196
+ reference = relationship.target || '';
1197
+ const description = this.workbook?.ReadTable(reference);
1198
+ if (description) {
1189
1199
 
1190
- // NOTE: column headers are added on first load, we don't
1191
- // read them from here. not super efficient but we do it
1192
- // that way for regular loads as well
1200
+ // console.info({description});
1193
1201
 
1202
+ const ref = sheet.TranslateAddress(description.ref);
1203
+ const area: IArea = is_address(ref) ? {
1204
+ start: { row: ref.row - 1, column: ref.col - 1},
1205
+ end: { row: ref.row - 1, column: ref.col - 1},
1206
+ } : {
1207
+ start: { row: ref.from.row - 1, column: ref.from.col - 1},
1208
+ end: { row: ref.to.row - 1, column: ref.to.col - 1},
1194
1209
  };
1195
- break;
1210
+
1211
+ for (const cell of data) {
1212
+ if (cell.row === area.start.row && cell.column === area.start.column) {
1213
+ cell.table = {
1214
+ area,
1215
+ name: description.name,
1216
+ totals_row: (!!description.totals_row_count),
1217
+
1218
+ // NOTE: column headers are added on first load, we don't
1219
+ // read them from here. not super efficient but we do it
1220
+ // that way for regular loads as well
1221
+
1222
+ };
1223
+ break;
1224
+ }
1196
1225
  }
1226
+
1197
1227
  }
1198
1228
 
1199
1229
  }
1200
-
1201
1230
  }
1202
- }
1203
- }
1231
+
1232
+ });
1233
+ });
1204
1234
 
1205
1235
  // --- import drawings -----------------------------------------------------
1206
1236
 
1207
1237
  // wip...
1208
1238
 
1209
- const drawings = FindAll('worksheet/drawing');
1239
+ // const drawings = FindAll('worksheet/drawing');
1210
1240
  const chart_descriptors: AnchoredChartDescription[] = [];
1211
1241
  const image_descriptors: AnchoredImageDescription[] = [];
1212
1242
  const textbox_descriptors: AnchoredTextBoxDescription[] = [];
1213
1243
 
1214
- for (const child of drawings) {
1244
+ if (this.workbook) {
1245
+ const workbook = this.workbook;
1246
+ IterateTags(sheet.root.drawing, child => {
1215
1247
 
1216
- const rel = child.a$ ? child.a$['r:id'] : undefined;
1217
- if (rel) {
1248
+ const rel = child.$attributes?.id;
1249
+ if (rel !== undefined) {
1218
1250
 
1219
- let reference = '';
1251
+ let reference = '';
1220
1252
 
1221
- const relationship = sheet.rels[rel];
1222
- if (relationship) {
1223
- reference = relationship.target || '';
1224
- }
1253
+ const relationship = sheet.rels[rel];
1254
+ if (relationship) {
1255
+ reference = relationship.target || '';
1256
+ }
1225
1257
 
1226
- if (reference) {
1227
- const drawing = this.workbook.ReadDrawing(reference);
1228
- if (drawing && drawing.length) {
1229
- for (const entry of drawing) {
1230
- switch (entry.type) {
1231
- case 'chart':
1232
- chart_descriptors.push(entry);
1233
- break;
1234
- case 'image':
1235
- image_descriptors.push(entry);
1236
- break;
1237
- case 'textbox':
1238
- textbox_descriptors.push(entry);
1239
- break;
1258
+ if (reference) {
1259
+ const drawing = workbook.ReadDrawing(reference);
1260
+ if (drawing && drawing.length) {
1261
+ for (const entry of drawing) {
1262
+ switch (entry.type) {
1263
+ case 'chart':
1264
+ chart_descriptors.push(entry);
1265
+ break;
1266
+ case 'image':
1267
+ image_descriptors.push(entry);
1268
+ break;
1269
+ case 'textbox':
1270
+ textbox_descriptors.push(entry);
1271
+ break;
1272
+ }
1240
1273
  }
1241
1274
  }
1242
1275
  }
1243
- }
1244
1276
 
1245
- }
1277
+ }
1278
+ });
1246
1279
  }
1247
1280
 
1248
1281
  const AnchorToCorner = (anchor: CellAnchor): LayoutCorner => {
@@ -1470,43 +1503,27 @@ export class Importer {
1470
1503
 
1471
1504
  // /wip
1472
1505
 
1473
- const ext = FindAll('worksheet/extLst/ext');
1474
- for (const entry of ext) {
1475
-
1476
- // find the prefix
1477
- let prefix = '';
1478
- for (const key of Object.keys(entry?.a$ || {})) {
1479
- const match = key.match(/^xmlns:(.*)$/);
1480
- if (match) {
1481
- prefix = match[1];
1482
- break;
1483
- }
1484
- }
1506
+ IterateTags(sheet.root.extLst?.ext, entry => {
1507
+ IterateTags(entry.sparklineGroups?.sparklineGroup, group => {
1508
+
1509
+ // const groups = XMLUtils.FindAll(entry, `${prefix}:sparklineGroups/${prefix}:sparklineGroup`);
1510
+ // for (const group of groups) {
1485
1511
 
1486
- const groups = XMLUtils.FindAll(entry, `${prefix}:sparklineGroups/${prefix}:sparklineGroup`);
1487
- for (const group of groups) {
1488
1512
  let func = 'Sparkline.line';
1489
1513
  let reference = '';
1490
1514
  let source = '';
1491
1515
 
1492
- if (group.a$?.type === 'column') {
1516
+ if (group.$attributes?.type === 'column') {
1493
1517
  func = 'Sparkline.column';
1494
1518
  }
1495
1519
 
1496
1520
  // TODO: gap optional
1497
1521
  // TODO: colors
1498
1522
 
1499
- const sparklines = XMLUtils.FindAll(group, `${prefix}:sparklines/${prefix}:sparkline`);
1500
- for (const sparkline of sparklines) {
1501
- for (const key of Object.keys(sparkline)) {
1502
- if (/:f$/.test(key)) {
1503
- source = sparkline[key];
1504
- }
1505
- else if (/:sqref$/.test(key)) {
1506
- reference = sparkline[key];
1507
- }
1508
- }
1509
- }
1523
+ IterateTags(group.sparklines.sparkline, sparkline => {
1524
+ source = sparkline.f?.$text ?? '';
1525
+ reference = sparkline.sqref?.$text ?? '';
1526
+ });
1510
1527
 
1511
1528
  //
1512
1529
 
@@ -1552,9 +1569,9 @@ export class Importer {
1552
1569
 
1553
1570
  //
1554
1571
 
1555
- }
1572
+ });
1556
1573
 
1557
- }
1574
+ });
1558
1575
 
1559
1576
  const result: ImportedSheetData = {
1560
1577
  name: sheet.options.name,