@cj-tech-master/excelts 5.0.6 → 5.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/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/modules/archive/unzip/stream.base.js +19 -19
- package/dist/browser/modules/archive/unzip/stream.browser.js +3 -3
- package/dist/browser/modules/csv/csv-core.js +6 -3
- package/dist/browser/modules/csv/csv.browser.js +2 -2
- package/dist/browser/modules/csv/csv.js +1 -1
- package/dist/browser/modules/excel/anchor.js +4 -4
- package/dist/browser/modules/excel/cell.js +5 -5
- package/dist/browser/modules/excel/column.js +4 -4
- package/dist/browser/modules/excel/defined-names.js +1 -1
- package/dist/browser/modules/excel/form-control.js +1 -1
- package/dist/browser/modules/excel/pivot-table.d.ts +168 -17
- package/dist/browser/modules/excel/pivot-table.js +278 -70
- package/dist/browser/modules/excel/row.js +4 -4
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +4 -4
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/browser/modules/excel/stream/worksheet-reader.js +1 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.js +4 -4
- package/dist/browser/modules/excel/table.js +2 -2
- package/dist/browser/modules/excel/types.d.ts +0 -4
- package/dist/browser/modules/excel/utils/cell-format.js +3 -3
- package/dist/browser/modules/excel/utils/shared-formula.js +1 -1
- package/dist/browser/modules/excel/utils/stream-buf.js +2 -2
- package/dist/browser/modules/excel/utils/string-buf.js +1 -1
- package/dist/browser/modules/excel/workbook.d.ts +0 -2
- package/dist/browser/modules/excel/workbook.js +4 -5
- package/dist/browser/modules/excel/worksheet.js +9 -9
- package/dist/browser/modules/excel/xlsx/xform/base-xform.d.ts +5 -5
- package/dist/browser/modules/excel/xlsx/xform/base-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
- package/dist/browser/modules/excel/xlsx/xform/comment/comment-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/comment/comments-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-position-xform.d.ts +3 -4
- package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-client-data-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-notes-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-textbox-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/composite-xform.d.ts +1 -1
- package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/cell-position-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/ext-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/list-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/list-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field-xform.d.ts +5 -15
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +134 -52
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field.d.ts +14 -15
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/cache-field.js +244 -70
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +13 -29
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +213 -37
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +7 -34
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +143 -41
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +101 -27
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.d.ts +78 -0
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +149 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
- package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
- package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
- package/dist/browser/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
- package/dist/browser/modules/excel/xlsx/xform/style/color-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +5 -5
- package/dist/browser/modules/excel/xlsx/xform/table/auto-filter-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/custom-filter-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/filter-column-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/filter-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/table-column-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/table-style-info-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xform/table/table-xform.d.ts +1 -2
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +5 -2
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +88 -54
- package/dist/browser/utils/env.d.ts +0 -5
- package/dist/browser/utils/env.js +0 -7
- package/dist/browser/utils/utils.base.d.ts +8 -13
- package/dist/browser/utils/utils.base.js +40 -47
- package/dist/browser/utils/utils.browser.d.ts +1 -1
- package/dist/browser/utils/utils.browser.js +1 -1
- package/dist/browser/utils/utils.d.ts +1 -1
- package/dist/browser/utils/utils.js +1 -1
- package/dist/cjs/modules/archive/unzip/stream.base.js +19 -19
- package/dist/cjs/modules/archive/unzip/stream.browser.js +3 -3
- package/dist/cjs/modules/csv/csv-core.js +6 -3
- package/dist/cjs/modules/csv/csv.browser.js +2 -2
- package/dist/cjs/modules/csv/csv.js +1 -1
- package/dist/cjs/modules/excel/anchor.js +4 -4
- package/dist/cjs/modules/excel/cell.js +5 -5
- package/dist/cjs/modules/excel/column.js +4 -4
- package/dist/cjs/modules/excel/defined-names.js +1 -1
- package/dist/cjs/modules/excel/form-control.js +1 -1
- package/dist/cjs/modules/excel/pivot-table.js +280 -70
- package/dist/cjs/modules/excel/row.js +4 -4
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +4 -4
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +1 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +4 -4
- package/dist/cjs/modules/excel/table.js +2 -2
- package/dist/cjs/modules/excel/utils/cell-format.js +3 -3
- package/dist/cjs/modules/excel/utils/shared-formula.js +1 -1
- package/dist/cjs/modules/excel/utils/stream-buf.js +2 -2
- package/dist/cjs/modules/excel/utils/string-buf.js +1 -1
- package/dist/cjs/modules/excel/workbook.js +4 -5
- package/dist/cjs/modules/excel/worksheet.js +9 -9
- package/dist/cjs/modules/excel/xlsx/xform/base-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
- package/dist/cjs/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +133 -51
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/cache-field.js +245 -71
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +212 -36
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +142 -40
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +153 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
- package/dist/cjs/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
- package/dist/cjs/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
- package/dist/cjs/modules/excel/xlsx/xform/style/color-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +5 -5
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +88 -54
- package/dist/cjs/utils/env.js +0 -8
- package/dist/cjs/utils/utils.base.js +41 -54
- package/dist/cjs/utils/utils.browser.js +2 -7
- package/dist/cjs/utils/utils.js +2 -7
- package/dist/esm/modules/archive/unzip/stream.base.js +19 -19
- package/dist/esm/modules/archive/unzip/stream.browser.js +3 -3
- package/dist/esm/modules/csv/csv-core.js +6 -3
- package/dist/esm/modules/csv/csv.browser.js +2 -2
- package/dist/esm/modules/csv/csv.js +1 -1
- package/dist/esm/modules/excel/anchor.js +4 -4
- package/dist/esm/modules/excel/cell.js +5 -5
- package/dist/esm/modules/excel/column.js +4 -4
- package/dist/esm/modules/excel/defined-names.js +1 -1
- package/dist/esm/modules/excel/form-control.js +1 -1
- package/dist/esm/modules/excel/pivot-table.js +278 -70
- package/dist/esm/modules/excel/row.js +4 -4
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +4 -4
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/esm/modules/excel/stream/worksheet-reader.js +1 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +4 -4
- package/dist/esm/modules/excel/table.js +2 -2
- package/dist/esm/modules/excel/utils/cell-format.js +3 -3
- package/dist/esm/modules/excel/utils/shared-formula.js +1 -1
- package/dist/esm/modules/excel/utils/stream-buf.js +2 -2
- package/dist/esm/modules/excel/utils/string-buf.js +1 -1
- package/dist/esm/modules/excel/workbook.js +4 -5
- package/dist/esm/modules/excel/worksheet.js +9 -9
- package/dist/esm/modules/excel/xlsx/xform/base-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +2 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-view-xform.js +4 -4
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +16 -4
- package/dist/esm/modules/excel/xlsx/xform/comment/style/vml-position-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/comment/style/vml-protection-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/comment/vml-textbox-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +24 -11
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/ext-xform.js +2 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/list-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/cache-field-xform.js +134 -52
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/cache-field.js +244 -70
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +213 -37
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.js +143 -41
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +793 -408
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.js +149 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/cf/cf-rule-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/cf/conditional-formattings-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/col-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/sheet/header-footer-xform.js +6 -6
- package/dist/esm/modules/excel/xlsx/xform/sheet/page-setup-xform.js +11 -11
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +10 -10
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +12 -12
- package/dist/esm/modules/excel/xlsx/xform/strings/phonetic-text-xform.js +2 -2
- package/dist/esm/modules/excel/xlsx/xform/style/color-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +5 -5
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +88 -54
- package/dist/esm/utils/env.js +0 -7
- package/dist/esm/utils/utils.base.js +40 -47
- package/dist/esm/utils/utils.browser.js +1 -1
- package/dist/esm/utils/utils.js +1 -1
- package/dist/iife/excelts.iife.js +1553 -718
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +36 -105
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/modules/excel/pivot-table.d.ts +168 -17
- package/dist/types/modules/excel/types.d.ts +0 -4
- package/dist/types/modules/excel/workbook.d.ts +0 -2
- package/dist/types/modules/excel/xlsx/xform/base-xform.d.ts +5 -5
- package/dist/types/modules/excel/xlsx/xform/comment/comment-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/comment/comments-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/comment/style/vml-position-xform.d.ts +3 -4
- package/dist/types/modules/excel/xlsx/xform/comment/vml-client-data-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/comment/vml-notes-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/comment/vml-textbox-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/composite-xform.d.ts +1 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/drawing/cell-position-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/drawing/ext-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/list-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/pivot-table/cache-field-xform.d.ts +5 -15
- package/dist/types/modules/excel/xlsx/xform/pivot-table/cache-field.d.ts +14 -15
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +13 -29
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +7 -34
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +101 -27
- package/dist/types/modules/excel/xlsx/xform/pivot-table/raw-xml-collector.d.ts +78 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/sheet/sheet-view-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/auto-filter-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/custom-filter-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/filter-column-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/filter-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/table-column-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/table-style-info-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xform/table/table-xform.d.ts +1 -2
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +5 -2
- package/dist/types/utils/env.d.ts +0 -5
- package/dist/types/utils/utils.base.d.ts +8 -13
- package/dist/types/utils/utils.browser.d.ts +1 -1
- package/dist/types/utils/utils.d.ts +1 -1
- package/package.json +1 -1
|
@@ -2,25 +2,50 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PivotTableXform = void 0;
|
|
4
4
|
const xml_stream_1 = require("../../../utils/xml-stream.js");
|
|
5
|
-
const
|
|
5
|
+
const col_cache_1 = require("../../../utils/col-cache.js");
|
|
6
6
|
const base_xform_1 = require("../base-xform.js");
|
|
7
|
+
const pivot_table_1 = require("../../../pivot-table.js");
|
|
8
|
+
const raw_xml_collector_1 = require("./raw-xml-collector.js");
|
|
9
|
+
/** OOXML sentinel field index meaning "data values" pseudo-field (used in pivotArea references) */
|
|
10
|
+
const FIELD_INDEX_DATA_VALUES = 4294967294; // 0xFFFFFFFE
|
|
11
|
+
/**
|
|
12
|
+
* Signed sentinel for the "Values" pseudo-field in colFields/rowFields.
|
|
13
|
+
* OOXML represents this as x="-2" (signed int32 of 0xFFFFFFFE).
|
|
14
|
+
*/
|
|
15
|
+
const VALUES_FIELD_INDEX = -2;
|
|
16
|
+
/** Default pivot table style name */
|
|
17
|
+
const DEFAULT_PIVOT_STYLE = "PivotStyleLight16";
|
|
18
|
+
/** Valid OOXML axis values for pivot fields */
|
|
19
|
+
const VALID_PIVOT_AXES = new Set(["axisRow", "axisCol", "axisPage", "axisValues"]);
|
|
20
|
+
/** PivotFieldItem attribute keys — used to build the attrs object for rendering */
|
|
21
|
+
const PIVOT_FIELD_ITEM_KEYS = ["x", "t", "h", "sd", "f", "m", "c", "d"];
|
|
22
|
+
/** Factory for default ParserState values */
|
|
23
|
+
function createDefaultParserState() {
|
|
24
|
+
return {
|
|
25
|
+
currentSection: null,
|
|
26
|
+
inPivotArea: false,
|
|
27
|
+
inAutoSortScope: false
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Known pivotField attributes that we parse individually (hoisted to module scope) */
|
|
31
|
+
const KNOWN_PIVOT_FIELD_KEYS = new Set([
|
|
32
|
+
"axis",
|
|
33
|
+
"dataField",
|
|
34
|
+
"compact",
|
|
35
|
+
"outline",
|
|
36
|
+
"showAll",
|
|
37
|
+
"defaultSubtotal",
|
|
38
|
+
"numFmtId",
|
|
39
|
+
"sortType",
|
|
40
|
+
"subtotalTop",
|
|
41
|
+
"insertBlankRow",
|
|
42
|
+
"multipleItemSelectionAllowed"
|
|
43
|
+
]);
|
|
7
44
|
class PivotTableXform extends base_xform_1.BaseXform {
|
|
8
45
|
constructor() {
|
|
9
46
|
super();
|
|
10
47
|
// Parser state consolidated into object for easier reset
|
|
11
|
-
this.state =
|
|
12
|
-
inPivotFields: false,
|
|
13
|
-
inRowFields: false,
|
|
14
|
-
inColFields: false,
|
|
15
|
-
inDataFields: false,
|
|
16
|
-
inRowItems: false,
|
|
17
|
-
inColItems: false,
|
|
18
|
-
inLocation: false,
|
|
19
|
-
inItems: false,
|
|
20
|
-
inPivotTableStyleInfo: false,
|
|
21
|
-
inChartFormats: false,
|
|
22
|
-
inPivotArea: false
|
|
23
|
-
};
|
|
48
|
+
this.state = createDefaultParserState();
|
|
24
49
|
// Current parsing context
|
|
25
50
|
this.currentPivotField = null;
|
|
26
51
|
this.currentRowItem = null;
|
|
@@ -28,37 +53,46 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
28
53
|
this.currentChartFormat = null;
|
|
29
54
|
// Buffer for collecting pivotArea XML
|
|
30
55
|
this.pivotAreaXmlBuffer = [];
|
|
31
|
-
|
|
32
|
-
this.
|
|
56
|
+
// Buffer for collecting autoSortScope XML
|
|
57
|
+
this.autoSortScopeXmlBuffer = [];
|
|
58
|
+
// Raw XML collectors (replacing manual in/depth/buffer triples)
|
|
59
|
+
this.extLstCollector = new raw_xml_collector_1.RawXmlCollector("extLst");
|
|
60
|
+
this.formatsCollector = new raw_xml_collector_1.RawXmlCollector("formats");
|
|
61
|
+
this.conditionalFormatsCollector = new raw_xml_collector_1.RawXmlCollector("conditionalFormats");
|
|
62
|
+
this.filtersCollector = new raw_xml_collector_1.RawXmlCollector("filters");
|
|
63
|
+
this.unknownCollector = new raw_xml_collector_1.RawXmlCollector("");
|
|
64
|
+
// Accumulated unknown elements XML strings (one per element)
|
|
65
|
+
this.unknownElementsXmlParts = [];
|
|
33
66
|
this.model = null;
|
|
34
67
|
}
|
|
35
|
-
prepare(_model) {
|
|
36
|
-
// No preparation needed
|
|
37
|
-
}
|
|
38
68
|
get tag() {
|
|
39
69
|
// http://www.datypic.com/sc/ooxml/e-ssml_pivotTableDefinition.html
|
|
40
70
|
return "pivotTableDefinition";
|
|
41
71
|
}
|
|
42
72
|
reset() {
|
|
43
73
|
this.model = null;
|
|
44
|
-
// Reset all parser state flags
|
|
45
|
-
|
|
46
|
-
this.state[key] = false;
|
|
47
|
-
});
|
|
74
|
+
// Reset all parser state flags
|
|
75
|
+
this.state = createDefaultParserState();
|
|
48
76
|
// Reset current context
|
|
49
77
|
this.currentPivotField = null;
|
|
50
78
|
this.currentRowItem = null;
|
|
51
79
|
this.currentColItem = null;
|
|
52
80
|
this.currentChartFormat = null;
|
|
53
81
|
this.pivotAreaXmlBuffer = [];
|
|
54
|
-
this.
|
|
82
|
+
this.autoSortScopeXmlBuffer = [];
|
|
83
|
+
this.extLstCollector.reset();
|
|
84
|
+
this.formatsCollector.reset();
|
|
85
|
+
this.conditionalFormatsCollector.reset();
|
|
86
|
+
this.filtersCollector.reset();
|
|
87
|
+
this.unknownCollector.reset();
|
|
88
|
+
this.unknownElementsXmlParts = [];
|
|
55
89
|
}
|
|
56
90
|
/**
|
|
57
91
|
* Render pivot table XML.
|
|
58
92
|
* Supports both newly created models and loaded models.
|
|
59
93
|
*/
|
|
60
94
|
render(xmlStream, model) {
|
|
61
|
-
const isLoaded = model.isLoaded;
|
|
95
|
+
const isLoaded = "isLoaded" in model && model.isLoaded;
|
|
62
96
|
if (isLoaded) {
|
|
63
97
|
this.renderLoaded(xmlStream, model);
|
|
64
98
|
}
|
|
@@ -70,33 +104,26 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
70
104
|
* Render newly created pivot table
|
|
71
105
|
*/
|
|
72
106
|
renderNew(xmlStream, model) {
|
|
73
|
-
const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
|
|
74
|
-
//
|
|
75
|
-
const
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// - Grand total row: 1
|
|
90
|
-
// endRow = 3 + 2 + rowFieldItemCount + 1 - 1 = 5 + rowFieldItemCount
|
|
91
|
-
// Or simplified: startRow (3) + 1 (column labels) + rowFieldItemCount (data) + 1 (grand total)
|
|
92
|
-
const endRow = 3 + 1 + rowFieldItemCount + 1; // = 5 + rowFieldItemCount
|
|
93
|
-
const endCol = 1 + colFieldItemCount + 1; // start col + data cols + grand total
|
|
94
|
-
const endColLetter = String.fromCharCode(64 + endCol);
|
|
95
|
-
const locationRef = `A3:${endColLetter}${endRow}`;
|
|
107
|
+
const { rows, columns, values, pages = [], cacheFields, cacheId, tableNumber, applyWidthHeightFormats } = model;
|
|
108
|
+
// Multi-value with no explicit columns: the "Values" pseudo-field occupies the column axis
|
|
109
|
+
const isMultiValueNoCol = columns.length === 0 && values.length > 1;
|
|
110
|
+
// Page fields offset: each page field adds 1 row above the pivot table,
|
|
111
|
+
// plus 1 blank separator row when any page fields are present.
|
|
112
|
+
const pageCount = pages.length;
|
|
113
|
+
const pageOffset = pageCount > 0 ? pageCount + 1 : 0;
|
|
114
|
+
// Location ref: firstDataCol = number of row fields (row label columns),
|
|
115
|
+
// endCol = row fields + data columns.
|
|
116
|
+
const firstDataCol = rows.length;
|
|
117
|
+
const startRow = 3 + pageOffset;
|
|
118
|
+
const endRow = startRow + 1; // header + 1 data row placeholder
|
|
119
|
+
const dataColCount = isMultiValueNoCol ? values.length : 1;
|
|
120
|
+
const endCol = firstDataCol + dataColCount;
|
|
121
|
+
const endColLetter = col_cache_1.colCache.n2l(endCol);
|
|
122
|
+
const locationRef = `A${startRow}:${endColLetter}${endRow}`;
|
|
96
123
|
xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
|
|
97
124
|
xmlStream.openNode(this.tag, {
|
|
98
125
|
...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
|
|
99
|
-
name:
|
|
126
|
+
name: `PivotTable${tableNumber}`,
|
|
100
127
|
cacheId,
|
|
101
128
|
applyNumberFormats: "0",
|
|
102
129
|
applyBorderFormats: "0",
|
|
@@ -115,110 +142,121 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
115
142
|
compactData: "0",
|
|
116
143
|
multipleFieldFilters: "0"
|
|
117
144
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
${rows.map(rowIndex => `<field x="${rowIndex}" />`).join("\n ")}
|
|
125
|
-
</rowFields>
|
|
126
|
-
<rowItems count="${rowItems.count}">
|
|
127
|
-
${rowItems.xml}
|
|
128
|
-
</rowItems>
|
|
129
|
-
<colFields count="${columns.length === 0 ? 1 : columns.length}">
|
|
130
|
-
${columns.length === 0
|
|
131
|
-
? '<field x="-2" />'
|
|
132
|
-
: columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
|
|
133
|
-
</colFields>
|
|
134
|
-
<colItems count="${colItems.count}">
|
|
135
|
-
${colItems.xml}
|
|
136
|
-
</colItems>
|
|
137
|
-
<dataFields count="${values.length}">
|
|
138
|
-
${buildDataFields(cacheFields, values, model.metric)}
|
|
139
|
-
</dataFields>
|
|
140
|
-
<pivotTableStyleInfo
|
|
141
|
-
name="PivotStyleLight16"
|
|
142
|
-
showRowHeaders="1"
|
|
143
|
-
showColHeaders="1"
|
|
144
|
-
showRowStripes="0"
|
|
145
|
-
showColStripes="0"
|
|
146
|
-
showLastColumn="1"
|
|
147
|
-
/>
|
|
148
|
-
<extLst>
|
|
149
|
-
<ext
|
|
150
|
-
uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
|
|
151
|
-
xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
|
|
152
|
-
>
|
|
153
|
-
<x14:pivotTableDefinition
|
|
154
|
-
hideValuesRow="1"
|
|
155
|
-
xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
|
|
156
|
-
/>
|
|
157
|
-
</ext>
|
|
158
|
-
<ext
|
|
159
|
-
uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
|
|
160
|
-
xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
|
|
161
|
-
>
|
|
162
|
-
<xpdl:pivotTableDefinition16
|
|
163
|
-
EnabledSubtotalsDefault="0"
|
|
164
|
-
SubtotalsOnTopDefault="0"
|
|
165
|
-
/>
|
|
166
|
-
</ext>
|
|
167
|
-
</extLst>
|
|
168
|
-
`);
|
|
169
|
-
xmlStream.closeNode();
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Render loaded pivot table (preserving original structure)
|
|
173
|
-
*/
|
|
174
|
-
renderLoaded(xmlStream, model) {
|
|
175
|
-
const attrs = {
|
|
176
|
-
...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
|
|
177
|
-
name: model.name || "PivotTable1",
|
|
178
|
-
cacheId: model.cacheId,
|
|
179
|
-
applyNumberFormats: model.applyNumberFormats || "0",
|
|
180
|
-
applyBorderFormats: model.applyBorderFormats || "0",
|
|
181
|
-
applyFontFormats: model.applyFontFormats || "0",
|
|
182
|
-
applyPatternFormats: model.applyPatternFormats || "0",
|
|
183
|
-
applyAlignmentFormats: model.applyAlignmentFormats || "0",
|
|
184
|
-
// Preserve original value when present; default to Excel's typical "0".
|
|
185
|
-
applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
|
|
186
|
-
dataCaption: model.dataCaption || "Values",
|
|
187
|
-
updatedVersion: model.updatedVersion || "8",
|
|
188
|
-
minRefreshableVersion: model.minRefreshableVersion || "3",
|
|
189
|
-
useAutoFormatting: model.useAutoFormatting ? "1" : "0",
|
|
190
|
-
itemPrintTitles: model.itemPrintTitles ? "1" : "0",
|
|
191
|
-
createdVersion: model.createdVersion || "8",
|
|
192
|
-
indent: model.indent !== undefined ? String(model.indent) : "0",
|
|
193
|
-
multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
|
|
145
|
+
// Location
|
|
146
|
+
const locAttrs = {
|
|
147
|
+
ref: locationRef,
|
|
148
|
+
firstHeaderRow: 1,
|
|
149
|
+
firstDataRow: 1,
|
|
150
|
+
firstDataCol
|
|
194
151
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
152
|
+
if (pageCount > 0) {
|
|
153
|
+
locAttrs.rowPageCount = pageCount;
|
|
154
|
+
locAttrs.colPageCount = 1;
|
|
198
155
|
}
|
|
199
|
-
|
|
200
|
-
|
|
156
|
+
xmlStream.leafNode("location", locAttrs);
|
|
157
|
+
// Pivot fields
|
|
158
|
+
renderPivotFields(xmlStream, model);
|
|
159
|
+
// Row fields
|
|
160
|
+
xmlStream.openNode("rowFields", { count: rows.length });
|
|
161
|
+
for (const rowIndex of rows) {
|
|
162
|
+
xmlStream.leafNode("field", { x: rowIndex });
|
|
201
163
|
}
|
|
202
|
-
|
|
203
|
-
|
|
164
|
+
xmlStream.closeNode();
|
|
165
|
+
// Row items: minimal grand total row. refreshOnLoad="1" causes Excel
|
|
166
|
+
// to rebuild the full row expansion on open.
|
|
167
|
+
xmlStream.openNode("rowItems", { count: 1 });
|
|
168
|
+
xmlStream.openNode("i", { t: "grand" });
|
|
169
|
+
xmlStream.leafNode("x");
|
|
170
|
+
xmlStream.closeNode(); // i
|
|
171
|
+
xmlStream.closeNode(); // rowItems
|
|
172
|
+
// colFields: lists the field indices on the column axis.
|
|
173
|
+
// When columns is non-empty, list those field indices.
|
|
174
|
+
// When columns is empty but there are multiple values, emit the synthetic
|
|
175
|
+
// "Values" pseudo-field (field x="-2") so Excel knows where to position
|
|
176
|
+
// the data field labels on the column axis.
|
|
177
|
+
// When columns is empty and there is only one value, omit colFields entirely.
|
|
178
|
+
if (columns.length > 0) {
|
|
179
|
+
const fieldCount = values.length > 1 ? columns.length + 1 : columns.length;
|
|
180
|
+
xmlStream.openNode("colFields", { count: fieldCount });
|
|
181
|
+
for (const colIndex of columns) {
|
|
182
|
+
xmlStream.leafNode("field", { x: colIndex });
|
|
183
|
+
}
|
|
184
|
+
if (values.length > 1) {
|
|
185
|
+
xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
|
|
186
|
+
}
|
|
187
|
+
xmlStream.closeNode();
|
|
188
|
+
}
|
|
189
|
+
else if (isMultiValueNoCol) {
|
|
190
|
+
xmlStream.openNode("colFields", { count: 1 });
|
|
191
|
+
xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
|
|
192
|
+
xmlStream.closeNode();
|
|
193
|
+
}
|
|
194
|
+
// colItems: for multi-value no-column pivots, one <i> per value field (referencing
|
|
195
|
+
// its index in dataFields via <x v="N"/>) plus a grand total <i>.
|
|
196
|
+
// For single-value or explicit-columns pivots, a single empty <i/>.
|
|
197
|
+
// These are required by Excel — omitting them causes "Repaired Records" errors.
|
|
198
|
+
if (isMultiValueNoCol) {
|
|
199
|
+
xmlStream.openNode("colItems", { count: values.length + 1 });
|
|
200
|
+
for (let idx = 0; idx < values.length; idx++) {
|
|
201
|
+
xmlStream.openNode("i");
|
|
202
|
+
xmlStream.leafNode("x", idx === 0 ? undefined : { v: idx });
|
|
203
|
+
xmlStream.closeNode(); // i
|
|
204
|
+
}
|
|
205
|
+
xmlStream.openNode("i", { t: "grand" });
|
|
206
|
+
xmlStream.leafNode("x");
|
|
207
|
+
xmlStream.closeNode(); // i
|
|
208
|
+
xmlStream.closeNode(); // colItems
|
|
204
209
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
else {
|
|
211
|
+
xmlStream.openNode("colItems", { count: 1 });
|
|
212
|
+
xmlStream.leafNode("i");
|
|
213
|
+
xmlStream.closeNode();
|
|
208
214
|
}
|
|
209
|
-
|
|
210
|
-
|
|
215
|
+
// Page fields (between colItems and dataFields per OOXML spec)
|
|
216
|
+
if (pageCount > 0) {
|
|
217
|
+
xmlStream.openNode("pageFields", { count: pageCount });
|
|
218
|
+
for (const fld of pages) {
|
|
219
|
+
xmlStream.leafNode("pageField", { fld, hier: -1 });
|
|
220
|
+
}
|
|
221
|
+
xmlStream.closeNode();
|
|
211
222
|
}
|
|
223
|
+
// Data fields
|
|
224
|
+
renderDataFields(xmlStream, cacheFields, values, model.valueMetrics);
|
|
225
|
+
// Pivot table style info
|
|
226
|
+
xmlStream.leafNode("pivotTableStyleInfo", {
|
|
227
|
+
name: DEFAULT_PIVOT_STYLE,
|
|
228
|
+
showRowHeaders: "1",
|
|
229
|
+
showColHeaders: "1",
|
|
230
|
+
showRowStripes: "0",
|
|
231
|
+
showColStripes: "0",
|
|
232
|
+
showLastColumn: "1"
|
|
233
|
+
});
|
|
234
|
+
// Extensions
|
|
235
|
+
xmlStream.writeXml(PivotTableXform.EXTLST_XML);
|
|
236
|
+
xmlStream.closeNode();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Render loaded pivot table (preserving original structure)
|
|
240
|
+
*/
|
|
241
|
+
renderLoaded(xmlStream, model) {
|
|
242
|
+
const attrs = this.buildLoadedRootAttributes(model);
|
|
212
243
|
xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
|
|
213
244
|
xmlStream.openNode(this.tag, attrs);
|
|
214
245
|
// Location
|
|
215
246
|
if (model.location) {
|
|
216
|
-
|
|
247
|
+
const locAttrs = {
|
|
217
248
|
ref: model.location.ref,
|
|
218
249
|
firstHeaderRow: model.location.firstHeaderRow,
|
|
219
250
|
firstDataRow: model.location.firstDataRow,
|
|
220
251
|
firstDataCol: model.location.firstDataCol
|
|
221
|
-
}
|
|
252
|
+
};
|
|
253
|
+
if (model.location.rowPageCount !== undefined) {
|
|
254
|
+
locAttrs.rowPageCount = model.location.rowPageCount;
|
|
255
|
+
}
|
|
256
|
+
if (model.location.colPageCount !== undefined) {
|
|
257
|
+
locAttrs.colPageCount = model.location.colPageCount;
|
|
258
|
+
}
|
|
259
|
+
xmlStream.leafNode("location", locAttrs);
|
|
222
260
|
}
|
|
223
261
|
// Pivot fields
|
|
224
262
|
if (model.pivotFields.length > 0) {
|
|
@@ -244,24 +282,30 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
244
282
|
}
|
|
245
283
|
xmlStream.closeNode();
|
|
246
284
|
}
|
|
247
|
-
else {
|
|
285
|
+
else if (model.hasRowItems) {
|
|
248
286
|
xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
|
|
249
287
|
}
|
|
250
288
|
// Col fields
|
|
251
289
|
// Only render colFields if it was present in the original file or if there are actual column fields
|
|
252
290
|
// Some pivot tables don't have colFields element at all
|
|
253
291
|
if (model.hasColFields || model.colFields.length > 0) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
xmlStream.leafNode("field", { x: -2 });
|
|
292
|
+
if (model.colFields.length === 0 && model.dataFields.length <= 1) {
|
|
293
|
+
// Empty colFields with no multi-value need — preserve as empty element
|
|
294
|
+
xmlStream.leafNode("colFields", { count: 0 });
|
|
258
295
|
}
|
|
259
296
|
else {
|
|
260
|
-
|
|
261
|
-
|
|
297
|
+
const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
|
|
298
|
+
xmlStream.openNode("colFields", { count: colFieldCount });
|
|
299
|
+
if (model.colFields.length === 0) {
|
|
300
|
+
xmlStream.leafNode("field", { x: VALUES_FIELD_INDEX });
|
|
262
301
|
}
|
|
302
|
+
else {
|
|
303
|
+
for (const fieldIndex of model.colFields) {
|
|
304
|
+
xmlStream.leafNode("field", { x: fieldIndex });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
xmlStream.closeNode();
|
|
263
308
|
}
|
|
264
|
-
xmlStream.closeNode();
|
|
265
309
|
}
|
|
266
310
|
// Col items - use parsed items if available
|
|
267
311
|
if (model.colItems && model.colItems.length > 0) {
|
|
@@ -271,58 +315,194 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
271
315
|
}
|
|
272
316
|
xmlStream.closeNode();
|
|
273
317
|
}
|
|
274
|
-
else {
|
|
318
|
+
else if (model.hasColItems) {
|
|
275
319
|
xmlStream.writeXml('<colItems count="1"><i t="grand"><x/></i></colItems>');
|
|
276
320
|
}
|
|
321
|
+
// Page fields (report filters)
|
|
322
|
+
if (model.pageFields && model.pageFields.length > 0) {
|
|
323
|
+
xmlStream.openNode("pageFields", { count: model.pageFields.length });
|
|
324
|
+
for (const pf of model.pageFields) {
|
|
325
|
+
const pfAttrs = { fld: pf.fld };
|
|
326
|
+
if (pf.item !== undefined) {
|
|
327
|
+
pfAttrs.item = pf.item;
|
|
328
|
+
}
|
|
329
|
+
if (pf.hier !== undefined) {
|
|
330
|
+
pfAttrs.hier = pf.hier;
|
|
331
|
+
}
|
|
332
|
+
if (pf.name !== undefined) {
|
|
333
|
+
pfAttrs.name = pf.name;
|
|
334
|
+
}
|
|
335
|
+
xmlStream.leafNode("pageField", pfAttrs);
|
|
336
|
+
}
|
|
337
|
+
xmlStream.closeNode();
|
|
338
|
+
}
|
|
277
339
|
// Data fields
|
|
278
340
|
if (model.dataFields.length > 0) {
|
|
279
341
|
xmlStream.openNode("dataFields", { count: model.dataFields.length });
|
|
280
342
|
for (const dataField of model.dataFields) {
|
|
281
343
|
const dfAttrs = {
|
|
282
344
|
name: dataField.name,
|
|
283
|
-
fld: dataField.fld
|
|
284
|
-
baseField: dataField.baseField ?? 0,
|
|
285
|
-
baseItem: dataField.baseItem ?? 0
|
|
345
|
+
fld: dataField.fld
|
|
286
346
|
};
|
|
287
|
-
if (dataField.
|
|
347
|
+
if (dataField.baseField !== undefined) {
|
|
348
|
+
dfAttrs.baseField = dataField.baseField;
|
|
349
|
+
}
|
|
350
|
+
if (dataField.baseItem !== undefined) {
|
|
351
|
+
dfAttrs.baseItem = dataField.baseItem;
|
|
352
|
+
}
|
|
353
|
+
if (dataField.subtotal !== undefined && dataField.subtotal !== "sum") {
|
|
288
354
|
dfAttrs.subtotal = dataField.subtotal;
|
|
289
355
|
}
|
|
356
|
+
if (dataField.numFmtId !== undefined) {
|
|
357
|
+
dfAttrs.numFmtId = dataField.numFmtId;
|
|
358
|
+
}
|
|
290
359
|
xmlStream.leafNode("dataField", dfAttrs);
|
|
291
360
|
}
|
|
292
361
|
xmlStream.closeNode();
|
|
293
362
|
}
|
|
363
|
+
// Formats — preserved raw XML from loaded file
|
|
364
|
+
if (model.formatsXml) {
|
|
365
|
+
xmlStream.writeXml(model.formatsXml);
|
|
366
|
+
}
|
|
367
|
+
// Conditional formats — preserved raw XML from loaded file
|
|
368
|
+
// OOXML order: formats → conditionalFormats → chartFormats
|
|
369
|
+
if (model.conditionalFormatsXml) {
|
|
370
|
+
xmlStream.writeXml(model.conditionalFormatsXml);
|
|
371
|
+
}
|
|
294
372
|
// Chart formats (for pivot charts) - preserve original pivotArea XML
|
|
295
373
|
if (model.chartFormats && model.chartFormats.length > 0) {
|
|
296
|
-
|
|
297
|
-
for (const cf of model.chartFormats) {
|
|
298
|
-
xmlStream.openNode("chartFormat", {
|
|
299
|
-
chart: cf.chart,
|
|
300
|
-
format: cf.format,
|
|
301
|
-
series: cf.series ? "1" : undefined
|
|
302
|
-
});
|
|
303
|
-
// Use preserved pivotArea XML or fallback to default
|
|
304
|
-
if (cf.pivotAreaXml) {
|
|
305
|
-
xmlStream.writeXml(cf.pivotAreaXml);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
// Fallback for newly created chart formats (shouldn't happen for loaded models)
|
|
309
|
-
xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="4294967294" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
|
|
310
|
-
}
|
|
311
|
-
xmlStream.closeNode();
|
|
312
|
-
}
|
|
313
|
-
xmlStream.closeNode();
|
|
374
|
+
this.renderChartFormats(xmlStream, model.chartFormats);
|
|
314
375
|
}
|
|
315
376
|
// Style info
|
|
377
|
+
const si = model.styleInfo;
|
|
316
378
|
xmlStream.leafNode("pivotTableStyleInfo", {
|
|
317
|
-
name: model.styleName
|
|
318
|
-
showRowHeaders: "1",
|
|
319
|
-
showColHeaders: "1",
|
|
320
|
-
showRowStripes: "0",
|
|
321
|
-
showColStripes: "0",
|
|
322
|
-
showLastColumn: "1"
|
|
379
|
+
name: si?.name ?? model.styleName ?? DEFAULT_PIVOT_STYLE,
|
|
380
|
+
showRowHeaders: si?.showRowHeaders ?? "1",
|
|
381
|
+
showColHeaders: si?.showColHeaders ?? "1",
|
|
382
|
+
showRowStripes: si?.showRowStripes ?? "0",
|
|
383
|
+
showColStripes: si?.showColStripes ?? "0",
|
|
384
|
+
showLastColumn: si?.showLastColumn ?? "1"
|
|
323
385
|
});
|
|
324
|
-
//
|
|
325
|
-
|
|
386
|
+
// Filters — preserved raw XML from loaded file
|
|
387
|
+
// <filters> appears between pivotTableStyleInfo and extLst per OOXML schema
|
|
388
|
+
if (model.filtersXml) {
|
|
389
|
+
xmlStream.writeXml(model.filtersXml);
|
|
390
|
+
}
|
|
391
|
+
// Unknown top-level elements — preserved raw XML for roundtrip
|
|
392
|
+
if (model.unknownElementsXml) {
|
|
393
|
+
xmlStream.writeXml(model.unknownElementsXml);
|
|
394
|
+
}
|
|
395
|
+
// Extensions — use preserved XML from loaded file; only inject default for new tables
|
|
396
|
+
const extLstXml = model.extLstXml ?? (model.isLoaded ? "" : PivotTableXform.EXTLST_XML);
|
|
397
|
+
if (extLstXml) {
|
|
398
|
+
xmlStream.writeXml(extLstXml);
|
|
399
|
+
}
|
|
400
|
+
xmlStream.closeNode();
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Build the root `<pivotTableDefinition>` attributes for a loaded (roundtrip) model.
|
|
404
|
+
* Extracted from renderLoaded to keep the render method focused on element structure.
|
|
405
|
+
*/
|
|
406
|
+
buildLoadedRootAttributes(model) {
|
|
407
|
+
const attrs = {
|
|
408
|
+
...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
|
|
409
|
+
name: model.name ?? "PivotTable1",
|
|
410
|
+
cacheId: String(model.cacheId),
|
|
411
|
+
applyNumberFormats: model.applyNumberFormats ?? "0",
|
|
412
|
+
applyBorderFormats: model.applyBorderFormats ?? "0",
|
|
413
|
+
applyFontFormats: model.applyFontFormats ?? "0",
|
|
414
|
+
applyPatternFormats: model.applyPatternFormats ?? "0",
|
|
415
|
+
applyAlignmentFormats: model.applyAlignmentFormats ?? "0",
|
|
416
|
+
applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
|
|
417
|
+
dataCaption: model.dataCaption ?? "Values",
|
|
418
|
+
updatedVersion: model.updatedVersion ?? "8",
|
|
419
|
+
minRefreshableVersion: model.minRefreshableVersion ?? "3"
|
|
420
|
+
};
|
|
421
|
+
// Only emit these boolean-style attributes when they were present in the original.
|
|
422
|
+
// Absent means the OOXML default applies; emitting "0" explicitly changes semantics.
|
|
423
|
+
// Placed before createdVersion to match Excel's attribute ordering.
|
|
424
|
+
if (model.useAutoFormatting !== undefined) {
|
|
425
|
+
attrs.useAutoFormatting = model.useAutoFormatting;
|
|
426
|
+
}
|
|
427
|
+
if (model.itemPrintTitles !== undefined) {
|
|
428
|
+
attrs.itemPrintTitles = model.itemPrintTitles;
|
|
429
|
+
}
|
|
430
|
+
if (model.multipleFieldFilters !== undefined) {
|
|
431
|
+
attrs.multipleFieldFilters = model.multipleFieldFilters;
|
|
432
|
+
}
|
|
433
|
+
attrs.createdVersion = model.createdVersion ?? "8";
|
|
434
|
+
if (model.indent !== undefined) {
|
|
435
|
+
attrs.indent = String(model.indent);
|
|
436
|
+
}
|
|
437
|
+
// Preserve xr:uid on roundtrip
|
|
438
|
+
if (model.uid) {
|
|
439
|
+
attrs["xmlns:xr"] = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
|
|
440
|
+
attrs["xr:uid"] = model.uid;
|
|
441
|
+
}
|
|
442
|
+
// Add outline attributes if present
|
|
443
|
+
if (model.outline) {
|
|
444
|
+
attrs.outline = "1";
|
|
445
|
+
}
|
|
446
|
+
if (model.outlineData) {
|
|
447
|
+
attrs.outlineData = "1";
|
|
448
|
+
}
|
|
449
|
+
if (model.chartFormat !== undefined) {
|
|
450
|
+
attrs.chartFormat = String(model.chartFormat);
|
|
451
|
+
}
|
|
452
|
+
// Grand totals and display option attributes — only emit when present in original
|
|
453
|
+
if (model.colGrandTotals !== undefined) {
|
|
454
|
+
attrs.colGrandTotals = model.colGrandTotals;
|
|
455
|
+
}
|
|
456
|
+
if (model.rowGrandTotals !== undefined) {
|
|
457
|
+
attrs.rowGrandTotals = model.rowGrandTotals;
|
|
458
|
+
}
|
|
459
|
+
if (model.showError !== undefined) {
|
|
460
|
+
attrs.showError = model.showError;
|
|
461
|
+
}
|
|
462
|
+
if (model.errorCaption !== undefined) {
|
|
463
|
+
attrs.errorCaption = model.errorCaption;
|
|
464
|
+
}
|
|
465
|
+
if (model.showMissing !== undefined) {
|
|
466
|
+
attrs.showMissing = model.showMissing;
|
|
467
|
+
}
|
|
468
|
+
if (model.missingCaption !== undefined) {
|
|
469
|
+
attrs.missingCaption = model.missingCaption;
|
|
470
|
+
}
|
|
471
|
+
if (model.grandTotalCaption !== undefined) {
|
|
472
|
+
attrs.grandTotalCaption = model.grandTotalCaption;
|
|
473
|
+
}
|
|
474
|
+
// Only write compact/compactData when false (non-default).
|
|
475
|
+
// OOXML spec: absent = true (default). So if the original file had compact="0",
|
|
476
|
+
// we must preserve it; omitting it would change semantics from false to true.
|
|
477
|
+
if (model.compact === false) {
|
|
478
|
+
attrs.compact = "0";
|
|
479
|
+
}
|
|
480
|
+
if (model.compactData === false) {
|
|
481
|
+
attrs.compactData = "0";
|
|
482
|
+
}
|
|
483
|
+
return attrs;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Render `<chartFormats>` with preserved pivotArea XML for pivot chart roundtrip.
|
|
487
|
+
*/
|
|
488
|
+
renderChartFormats(xmlStream, chartFormats) {
|
|
489
|
+
xmlStream.openNode("chartFormats", { count: chartFormats.length });
|
|
490
|
+
for (const cf of chartFormats) {
|
|
491
|
+
xmlStream.openNode("chartFormat", {
|
|
492
|
+
chart: cf.chart,
|
|
493
|
+
format: cf.format,
|
|
494
|
+
series: cf.series === true ? "1" : cf.series === false ? "0" : undefined
|
|
495
|
+
});
|
|
496
|
+
// Use preserved pivotArea XML or fallback to default
|
|
497
|
+
if (cf.pivotAreaXml) {
|
|
498
|
+
xmlStream.writeXml(cf.pivotAreaXml);
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
// Fallback for newly created chart formats (shouldn't happen for loaded models)
|
|
502
|
+
xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="${FIELD_INDEX_DATA_VALUES}" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
|
|
503
|
+
}
|
|
504
|
+
xmlStream.closeNode();
|
|
505
|
+
}
|
|
326
506
|
xmlStream.closeNode();
|
|
327
507
|
}
|
|
328
508
|
/**
|
|
@@ -330,13 +510,19 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
330
510
|
*/
|
|
331
511
|
renderRowColItem(xmlStream, item) {
|
|
332
512
|
const attrs = {};
|
|
333
|
-
if (item.t) {
|
|
513
|
+
if (item.t !== undefined) {
|
|
334
514
|
attrs.t = item.t;
|
|
335
515
|
}
|
|
336
|
-
if (item.
|
|
516
|
+
if (item.r !== undefined) {
|
|
517
|
+
attrs.r = item.r;
|
|
518
|
+
}
|
|
519
|
+
if (item.i !== undefined) {
|
|
520
|
+
attrs.i = item.i;
|
|
521
|
+
}
|
|
522
|
+
if (item.x.length > 0) {
|
|
337
523
|
xmlStream.openNode("i", attrs);
|
|
338
524
|
for (const x of item.x) {
|
|
339
|
-
if (x.v
|
|
525
|
+
if (x.v !== 0) {
|
|
340
526
|
xmlStream.leafNode("x", { v: x.v });
|
|
341
527
|
}
|
|
342
528
|
else {
|
|
@@ -362,36 +548,103 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
362
548
|
if (field.dataField) {
|
|
363
549
|
attrs.dataField = "1";
|
|
364
550
|
}
|
|
365
|
-
|
|
551
|
+
if (field.numFmtId !== undefined) {
|
|
552
|
+
attrs.numFmtId = String(field.numFmtId);
|
|
553
|
+
}
|
|
554
|
+
if (field.sortType) {
|
|
555
|
+
attrs.sortType = field.sortType;
|
|
556
|
+
}
|
|
557
|
+
// OOXML defaults: compact=true, outline=true, defaultSubtotal=true when absent.
|
|
558
|
+
// Only write the attribute when false (non-default) to preserve round-trip fidelity.
|
|
559
|
+
if (field.compact === false) {
|
|
560
|
+
attrs.compact = "0";
|
|
561
|
+
}
|
|
562
|
+
if (field.outline === false) {
|
|
563
|
+
attrs.outline = "0";
|
|
564
|
+
}
|
|
565
|
+
// showAll is typically always present — placed before defaultSubtotal to match Excel's ordering
|
|
366
566
|
attrs.showAll = field.showAll ? "1" : "0";
|
|
367
|
-
if (field.
|
|
567
|
+
if (field.defaultSubtotal === false) {
|
|
568
|
+
attrs.defaultSubtotal = "0";
|
|
569
|
+
}
|
|
570
|
+
if (field.subtotalTop === false) {
|
|
571
|
+
attrs.subtotalTop = "0";
|
|
572
|
+
}
|
|
573
|
+
if (field.insertBlankRow === true) {
|
|
574
|
+
attrs.insertBlankRow = "1";
|
|
575
|
+
}
|
|
576
|
+
if (field.multipleItemSelectionAllowed === true) {
|
|
577
|
+
attrs.multipleItemSelectionAllowed = "1";
|
|
578
|
+
}
|
|
579
|
+
// Spread extra unknown attributes for roundtrip preservation
|
|
580
|
+
if (field.extraAttrs) {
|
|
581
|
+
for (const [k, v] of Object.entries(field.extraAttrs)) {
|
|
582
|
+
attrs[k] = v;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const hasChildren = (field.items !== undefined && field.items.length > 0) || field.autoSortScopeXml !== undefined;
|
|
586
|
+
if (hasChildren) {
|
|
368
587
|
xmlStream.openNode("pivotField", attrs);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
588
|
+
if (field.items !== undefined && field.items.length > 0) {
|
|
589
|
+
xmlStream.openNode("items", { count: field.items.length });
|
|
590
|
+
for (const item of field.items) {
|
|
591
|
+
const itemAttrs = {};
|
|
592
|
+
for (const key of PIVOT_FIELD_ITEM_KEYS) {
|
|
593
|
+
if (item[key] !== undefined) {
|
|
594
|
+
itemAttrs[key] = item[key];
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
xmlStream.leafNode("item", itemAttrs);
|
|
598
|
+
}
|
|
599
|
+
xmlStream.closeNode(); // items
|
|
600
|
+
}
|
|
601
|
+
if (field.autoSortScopeXml) {
|
|
602
|
+
xmlStream.writeXml(field.autoSortScopeXml);
|
|
372
603
|
}
|
|
373
|
-
// Grand total item
|
|
374
|
-
xmlStream.writeXml('<item t="default"/>');
|
|
375
|
-
xmlStream.closeNode(); // items
|
|
376
604
|
xmlStream.closeNode(); // pivotField
|
|
377
605
|
}
|
|
378
606
|
else {
|
|
379
607
|
xmlStream.leafNode("pivotField", attrs);
|
|
380
608
|
}
|
|
381
609
|
}
|
|
610
|
+
// TODO: Consider migrating to map-based child xform delegation (like table-xform.ts)
|
|
611
|
+
// to replace this large manual switch. Currently kept as-is because the manual SAX
|
|
612
|
+
// approach, while verbose, handles all OOXML edge cases correctly.
|
|
382
613
|
parseOpen(node) {
|
|
383
614
|
const { name, attributes } = node;
|
|
615
|
+
// Collect raw XML verbatim for roundtrip preservation (5 collectors)
|
|
616
|
+
if (this.extLstCollector.active) {
|
|
617
|
+
this.extLstCollector.feedOpen(name, attributes);
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
if (this.formatsCollector.active) {
|
|
621
|
+
this.formatsCollector.feedOpen(name, attributes);
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
if (this.conditionalFormatsCollector.active) {
|
|
625
|
+
this.conditionalFormatsCollector.feedOpen(name, attributes);
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
if (this.filtersCollector.active) {
|
|
629
|
+
this.filtersCollector.feedOpen(name, attributes);
|
|
630
|
+
return true;
|
|
631
|
+
}
|
|
632
|
+
if (this.unknownCollector.active) {
|
|
633
|
+
this.unknownCollector.feedOpen(name, attributes);
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
384
636
|
switch (name) {
|
|
385
637
|
case this.tag:
|
|
386
638
|
// pivotTableDefinition root element
|
|
387
639
|
this.reset();
|
|
388
640
|
this.model = {
|
|
389
641
|
name: attributes.name,
|
|
390
|
-
cacheId: parseInt(attributes.cacheId
|
|
642
|
+
cacheId: parseInt(attributes.cacheId ?? "0", 10),
|
|
391
643
|
uid: attributes["xr:uid"],
|
|
392
644
|
pivotFields: [],
|
|
393
645
|
rowFields: [],
|
|
394
646
|
colFields: [],
|
|
647
|
+
pageFields: [],
|
|
395
648
|
dataFields: [],
|
|
396
649
|
applyNumberFormats: attributes.applyNumberFormats,
|
|
397
650
|
applyBorderFormats: attributes.applyBorderFormats,
|
|
@@ -403,15 +656,22 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
403
656
|
updatedVersion: attributes.updatedVersion,
|
|
404
657
|
minRefreshableVersion: attributes.minRefreshableVersion,
|
|
405
658
|
createdVersion: attributes.createdVersion,
|
|
406
|
-
useAutoFormatting: attributes.useAutoFormatting
|
|
407
|
-
itemPrintTitles: attributes.itemPrintTitles
|
|
408
|
-
indent: attributes.indent ? parseInt(attributes.indent, 10) :
|
|
409
|
-
compact: attributes.compact
|
|
410
|
-
compactData: attributes.compactData
|
|
411
|
-
multipleFieldFilters: attributes.multipleFieldFilters
|
|
659
|
+
useAutoFormatting: attributes.useAutoFormatting,
|
|
660
|
+
itemPrintTitles: attributes.itemPrintTitles,
|
|
661
|
+
indent: attributes.indent !== undefined ? parseInt(attributes.indent, 10) : undefined,
|
|
662
|
+
compact: attributes.compact !== "0",
|
|
663
|
+
compactData: attributes.compactData !== "0",
|
|
664
|
+
multipleFieldFilters: attributes.multipleFieldFilters,
|
|
412
665
|
outline: attributes.outline === "1",
|
|
413
666
|
outlineData: attributes.outlineData === "1",
|
|
414
|
-
chartFormat: attributes.chartFormat ? parseInt(attributes.chartFormat, 10) : undefined,
|
|
667
|
+
chartFormat: attributes.chartFormat !== undefined ? parseInt(attributes.chartFormat, 10) : undefined,
|
|
668
|
+
colGrandTotals: attributes.colGrandTotals,
|
|
669
|
+
rowGrandTotals: attributes.rowGrandTotals,
|
|
670
|
+
showError: attributes.showError,
|
|
671
|
+
errorCaption: attributes.errorCaption,
|
|
672
|
+
showMissing: attributes.showMissing,
|
|
673
|
+
missingCaption: attributes.missingCaption,
|
|
674
|
+
grandTotalCaption: attributes.grandTotalCaption,
|
|
415
675
|
rowItems: [],
|
|
416
676
|
colItems: [],
|
|
417
677
|
chartFormats: [],
|
|
@@ -422,171 +682,346 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
422
682
|
if (this.model) {
|
|
423
683
|
this.model.location = {
|
|
424
684
|
ref: attributes.ref,
|
|
425
|
-
firstHeaderRow: attributes.firstHeaderRow
|
|
685
|
+
firstHeaderRow: attributes.firstHeaderRow !== undefined
|
|
426
686
|
? parseInt(attributes.firstHeaderRow, 10)
|
|
427
687
|
: undefined,
|
|
428
|
-
firstDataRow: attributes.firstDataRow
|
|
688
|
+
firstDataRow: attributes.firstDataRow !== undefined
|
|
429
689
|
? parseInt(attributes.firstDataRow, 10)
|
|
430
690
|
: undefined,
|
|
431
|
-
firstDataCol: attributes.firstDataCol
|
|
691
|
+
firstDataCol: attributes.firstDataCol !== undefined
|
|
432
692
|
? parseInt(attributes.firstDataCol, 10)
|
|
693
|
+
: undefined,
|
|
694
|
+
rowPageCount: attributes.rowPageCount !== undefined
|
|
695
|
+
? parseInt(attributes.rowPageCount, 10)
|
|
696
|
+
: undefined,
|
|
697
|
+
colPageCount: attributes.colPageCount !== undefined
|
|
698
|
+
? parseInt(attributes.colPageCount, 10)
|
|
433
699
|
: undefined
|
|
434
700
|
};
|
|
435
701
|
}
|
|
436
702
|
break;
|
|
437
703
|
case "pivotFields":
|
|
438
|
-
this.state.
|
|
704
|
+
this.state.currentSection = "pivotFields";
|
|
439
705
|
break;
|
|
440
706
|
case "pivotField":
|
|
441
|
-
if (this.state.
|
|
707
|
+
if (this.state.currentSection === "pivotFields") {
|
|
708
|
+
// Collect unknown attributes into extraAttrs bag for roundtrip preservation
|
|
709
|
+
const extraAttrs = {};
|
|
710
|
+
for (const [k, v] of Object.entries(attributes)) {
|
|
711
|
+
if (!KNOWN_PIVOT_FIELD_KEYS.has(k)) {
|
|
712
|
+
extraAttrs[k] = String(v);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
442
715
|
this.currentPivotField = {
|
|
443
|
-
axis: attributes.axis
|
|
716
|
+
axis: VALID_PIVOT_AXES.has(attributes.axis)
|
|
717
|
+
? attributes.axis
|
|
718
|
+
: undefined,
|
|
444
719
|
dataField: attributes.dataField === "1",
|
|
445
720
|
items: [],
|
|
446
|
-
compact: attributes.compact
|
|
447
|
-
outline: attributes.outline
|
|
448
|
-
showAll: attributes.showAll
|
|
449
|
-
defaultSubtotal: attributes.defaultSubtotal
|
|
721
|
+
compact: attributes.compact !== "0",
|
|
722
|
+
outline: attributes.outline !== "0",
|
|
723
|
+
showAll: attributes.showAll !== "0",
|
|
724
|
+
defaultSubtotal: attributes.defaultSubtotal !== "0",
|
|
725
|
+
numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined,
|
|
726
|
+
sortType: attributes.sortType,
|
|
727
|
+
subtotalTop: attributes.subtotalTop !== undefined ? attributes.subtotalTop === "1" : undefined,
|
|
728
|
+
insertBlankRow: attributes.insertBlankRow === "1" ? true : undefined,
|
|
729
|
+
multipleItemSelectionAllowed: attributes.multipleItemSelectionAllowed === "1" ? true : undefined,
|
|
730
|
+
extraAttrs: Object.keys(extraAttrs).length > 0 ? extraAttrs : undefined
|
|
450
731
|
};
|
|
451
732
|
}
|
|
452
733
|
break;
|
|
453
734
|
case "items":
|
|
735
|
+
// No state needed — item parsing is guarded by currentPivotField
|
|
736
|
+
break;
|
|
737
|
+
case "item":
|
|
454
738
|
if (this.currentPivotField) {
|
|
455
|
-
|
|
739
|
+
// R8-O1: Parse item attributes using a loop over PIVOT_FIELD_ITEM_KEYS
|
|
740
|
+
const item = {};
|
|
741
|
+
if (attributes.x !== undefined) {
|
|
742
|
+
item.x = parseInt(attributes.x, 10);
|
|
743
|
+
}
|
|
744
|
+
for (const key of PIVOT_FIELD_ITEM_KEYS) {
|
|
745
|
+
if (key !== "x" && attributes[key] !== undefined) {
|
|
746
|
+
item[key] = attributes[key];
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
// items is always initialized as [] when currentPivotField is created (see "pivotField" case)
|
|
750
|
+
this.currentPivotField.items.push(item);
|
|
456
751
|
}
|
|
457
752
|
break;
|
|
458
|
-
case "
|
|
459
|
-
|
|
460
|
-
|
|
753
|
+
case "autoSortScope":
|
|
754
|
+
// Start collecting autoSortScope XML for the current pivotField
|
|
755
|
+
if (this.currentPivotField) {
|
|
756
|
+
this.state.inAutoSortScope = true;
|
|
757
|
+
this.autoSortScopeXmlBuffer = ["<autoSortScope>"];
|
|
461
758
|
}
|
|
462
759
|
break;
|
|
463
760
|
case "rowFields":
|
|
464
|
-
this.state.
|
|
761
|
+
this.state.currentSection = "rowFields";
|
|
465
762
|
break;
|
|
466
763
|
case "colFields":
|
|
467
|
-
this.state.
|
|
764
|
+
this.state.currentSection = "colFields";
|
|
468
765
|
// Track that colFields element was present in original file
|
|
469
766
|
if (this.model) {
|
|
470
767
|
this.model.hasColFields = true;
|
|
471
768
|
}
|
|
472
769
|
break;
|
|
473
770
|
case "dataFields":
|
|
474
|
-
this.state.
|
|
771
|
+
this.state.currentSection = "dataFields";
|
|
772
|
+
break;
|
|
773
|
+
case "pageFields":
|
|
774
|
+
this.state.currentSection = "pageFields";
|
|
775
|
+
break;
|
|
776
|
+
case "pageField":
|
|
777
|
+
if (this.state.currentSection === "pageFields" && this.model) {
|
|
778
|
+
this.model.pageFields.push({
|
|
779
|
+
fld: parseInt(attributes.fld ?? "0", 10),
|
|
780
|
+
item: attributes.item !== undefined ? parseInt(attributes.item, 10) : undefined,
|
|
781
|
+
hier: attributes.hier !== undefined ? parseInt(attributes.hier, 10) : undefined,
|
|
782
|
+
name: attributes.name
|
|
783
|
+
});
|
|
784
|
+
}
|
|
475
785
|
break;
|
|
476
786
|
case "rowItems":
|
|
477
|
-
this.state.
|
|
787
|
+
this.state.currentSection = "rowItems";
|
|
788
|
+
if (this.model) {
|
|
789
|
+
this.model.hasRowItems = true;
|
|
790
|
+
}
|
|
478
791
|
break;
|
|
479
792
|
case "colItems":
|
|
480
|
-
this.state.
|
|
793
|
+
this.state.currentSection = "colItems";
|
|
794
|
+
if (this.model) {
|
|
795
|
+
this.model.hasColItems = true;
|
|
796
|
+
}
|
|
481
797
|
break;
|
|
482
798
|
case "i":
|
|
483
799
|
// Handle row/col item element
|
|
484
|
-
if (this.
|
|
485
|
-
this.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
this.
|
|
800
|
+
if (this.model) {
|
|
801
|
+
const rowColItem = this.state.currentSection === "rowItems" || this.state.currentSection === "colItems"
|
|
802
|
+
? parseRowColItem(attributes)
|
|
803
|
+
: null;
|
|
804
|
+
if (this.state.currentSection === "rowItems") {
|
|
805
|
+
this.currentRowItem = rowColItem;
|
|
806
|
+
}
|
|
807
|
+
else if (this.state.currentSection === "colItems") {
|
|
808
|
+
this.currentColItem = rowColItem;
|
|
809
|
+
}
|
|
489
810
|
}
|
|
490
811
|
break;
|
|
491
812
|
case "x":
|
|
492
813
|
// Handle x element inside row/col items or pivotArea
|
|
493
814
|
if (this.state.inPivotArea) {
|
|
494
|
-
// Collect x element for pivotArea XML
|
|
495
|
-
const xAttrs =
|
|
496
|
-
|
|
497
|
-
.
|
|
498
|
-
|
|
815
|
+
// Collect x element for pivotArea XML (re-encode attribute values for XML safety)
|
|
816
|
+
const xAttrs = (0, raw_xml_collector_1.serializeAttributes)(attributes);
|
|
817
|
+
if (this.state.inAutoSortScope) {
|
|
818
|
+
this.autoSortScopeXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
|
|
822
|
+
}
|
|
499
823
|
}
|
|
500
824
|
else if (this.currentRowItem) {
|
|
501
|
-
this.currentRowItem.x.push({ v: attributes.v
|
|
825
|
+
this.currentRowItem.x.push({ v: parseInt(attributes.v ?? "0", 10) });
|
|
502
826
|
}
|
|
503
827
|
else if (this.currentColItem) {
|
|
504
|
-
this.currentColItem.x.push({ v: attributes.v
|
|
828
|
+
this.currentColItem.x.push({ v: parseInt(attributes.v ?? "0", 10) });
|
|
505
829
|
}
|
|
506
830
|
break;
|
|
507
831
|
case "chartFormats":
|
|
508
|
-
this.state.
|
|
832
|
+
this.state.currentSection = "chartFormats";
|
|
509
833
|
break;
|
|
510
834
|
case "chartFormat":
|
|
511
|
-
if (this.state.
|
|
835
|
+
if (this.state.currentSection === "chartFormats" && this.model) {
|
|
512
836
|
this.currentChartFormat = {
|
|
513
|
-
chart: attributes.chart
|
|
514
|
-
format: attributes.format
|
|
515
|
-
series: attributes.series === "1"
|
|
837
|
+
chart: parseInt(attributes.chart ?? "0", 10),
|
|
838
|
+
format: parseInt(attributes.format ?? "0", 10),
|
|
839
|
+
series: attributes.series !== undefined ? attributes.series === "1" : undefined
|
|
516
840
|
};
|
|
517
841
|
}
|
|
518
842
|
break;
|
|
519
843
|
case "pivotArea":
|
|
520
|
-
// Start collecting pivotArea XML for chartFormat
|
|
844
|
+
// Start collecting pivotArea XML for chartFormat or autoSortScope
|
|
521
845
|
if (this.currentChartFormat) {
|
|
522
846
|
this.state.inPivotArea = true;
|
|
523
|
-
const attrsStr =
|
|
524
|
-
.map(([k, v]) => `${k}="${v}"`)
|
|
525
|
-
.join(" ");
|
|
847
|
+
const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
|
|
526
848
|
this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
|
|
527
849
|
}
|
|
850
|
+
else if (this.state.inAutoSortScope) {
|
|
851
|
+
this.state.inPivotArea = true;
|
|
852
|
+
const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
|
|
853
|
+
this.autoSortScopeXmlBuffer.push(attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>");
|
|
854
|
+
}
|
|
528
855
|
break;
|
|
529
856
|
case "references":
|
|
530
857
|
case "reference":
|
|
531
858
|
// Collect nested elements in pivotArea
|
|
532
859
|
if (this.state.inPivotArea) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
.
|
|
536
|
-
|
|
537
|
-
|
|
860
|
+
const attrsStr = (0, raw_xml_collector_1.serializeAttributes)(attributes);
|
|
861
|
+
if (this.state.inAutoSortScope) {
|
|
862
|
+
this.autoSortScopeXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
|
|
866
|
+
}
|
|
538
867
|
}
|
|
539
868
|
break;
|
|
540
869
|
case "field":
|
|
541
870
|
// Handle field element (used in rowFields, colFields)
|
|
542
871
|
if (this.model) {
|
|
543
|
-
const fieldIndex = parseInt(attributes.x
|
|
544
|
-
if (this.state.
|
|
872
|
+
const fieldIndex = parseInt(attributes.x ?? "0", 10);
|
|
873
|
+
if (this.state.currentSection === "rowFields") {
|
|
545
874
|
this.model.rowFields.push(fieldIndex);
|
|
546
875
|
}
|
|
547
|
-
else if (this.state.
|
|
876
|
+
else if (this.state.currentSection === "colFields") {
|
|
548
877
|
this.model.colFields.push(fieldIndex);
|
|
549
878
|
}
|
|
550
879
|
}
|
|
551
880
|
break;
|
|
552
881
|
case "dataField":
|
|
553
|
-
if (this.state.
|
|
882
|
+
if (this.state.currentSection === "dataFields" && this.model) {
|
|
554
883
|
this.model.dataFields.push({
|
|
555
|
-
name:
|
|
556
|
-
fld: parseInt(attributes.fld
|
|
557
|
-
baseField: attributes.baseField ? parseInt(attributes.baseField, 10) :
|
|
558
|
-
baseItem: attributes.baseItem ? parseInt(attributes.baseItem, 10) :
|
|
559
|
-
subtotal: attributes.subtotal
|
|
884
|
+
name: attributes.name ?? "",
|
|
885
|
+
fld: parseInt(attributes.fld ?? "0", 10),
|
|
886
|
+
baseField: attributes.baseField !== undefined ? parseInt(attributes.baseField, 10) : undefined,
|
|
887
|
+
baseItem: attributes.baseItem !== undefined ? parseInt(attributes.baseItem, 10) : undefined,
|
|
888
|
+
subtotal: pivot_table_1.VALID_SUBTOTALS.has(attributes.subtotal)
|
|
889
|
+
? attributes.subtotal
|
|
890
|
+
: undefined,
|
|
891
|
+
numFmtId: attributes.numFmtId !== undefined ? parseInt(attributes.numFmtId, 10) : undefined
|
|
560
892
|
});
|
|
561
893
|
}
|
|
562
894
|
break;
|
|
563
895
|
case "pivotTableStyleInfo":
|
|
564
896
|
if (this.model) {
|
|
565
897
|
this.model.styleName = attributes.name;
|
|
898
|
+
this.model.styleInfo = {
|
|
899
|
+
name: attributes.name,
|
|
900
|
+
showRowHeaders: attributes.showRowHeaders,
|
|
901
|
+
showColHeaders: attributes.showColHeaders,
|
|
902
|
+
showRowStripes: attributes.showRowStripes,
|
|
903
|
+
showColStripes: attributes.showColStripes,
|
|
904
|
+
showLastColumn: attributes.showLastColumn
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
break;
|
|
908
|
+
case "extLst":
|
|
909
|
+
// Start collecting extLst XML for roundtrip preservation
|
|
910
|
+
if (this.model) {
|
|
911
|
+
this.extLstCollector.start(attributes);
|
|
912
|
+
}
|
|
913
|
+
break;
|
|
914
|
+
case "formats":
|
|
915
|
+
// Start collecting formats XML for roundtrip preservation
|
|
916
|
+
if (this.model) {
|
|
917
|
+
this.formatsCollector.start(attributes);
|
|
918
|
+
}
|
|
919
|
+
break;
|
|
920
|
+
case "conditionalFormats":
|
|
921
|
+
// Start collecting conditionalFormats XML for roundtrip preservation
|
|
922
|
+
if (this.model) {
|
|
923
|
+
this.conditionalFormatsCollector.start(attributes);
|
|
924
|
+
}
|
|
925
|
+
break;
|
|
926
|
+
case "filters":
|
|
927
|
+
// Start collecting filters XML for roundtrip preservation
|
|
928
|
+
// <filters> appears between pivotTableStyleInfo and extLst per OOXML schema
|
|
929
|
+
if (this.model) {
|
|
930
|
+
this.filtersCollector.start(attributes);
|
|
931
|
+
}
|
|
932
|
+
break;
|
|
933
|
+
default:
|
|
934
|
+
// Catch-all: collect any unhandled top-level child element as raw XML.
|
|
935
|
+
// This preserves elements like pivotHierarchies, rowHierarchiesUsage,
|
|
936
|
+
// colHierarchiesUsage, etc. that we don't individually model.
|
|
937
|
+
// R8-B1: Only activate at the top level of pivotTableDefinition — NOT inside
|
|
938
|
+
// known sections (pivotFields, rowFields, etc.) or pivotArea/autoSortScope,
|
|
939
|
+
// otherwise the collector would steal subsequent tags from normal parsing.
|
|
940
|
+
if (this.model &&
|
|
941
|
+
this.state.currentSection === null &&
|
|
942
|
+
!this.state.inPivotArea &&
|
|
943
|
+
!this.state.inAutoSortScope) {
|
|
944
|
+
this.unknownCollector.startAs(name, attributes);
|
|
566
945
|
}
|
|
567
946
|
break;
|
|
568
947
|
}
|
|
569
948
|
return true;
|
|
570
949
|
}
|
|
571
|
-
parseText(
|
|
572
|
-
//
|
|
950
|
+
parseText(text) {
|
|
951
|
+
// Forward text nodes to whichever raw-XML collector is active (B3 fix)
|
|
952
|
+
if (this.extLstCollector.active) {
|
|
953
|
+
this.extLstCollector.feedText(text);
|
|
954
|
+
}
|
|
955
|
+
else if (this.formatsCollector.active) {
|
|
956
|
+
this.formatsCollector.feedText(text);
|
|
957
|
+
}
|
|
958
|
+
else if (this.conditionalFormatsCollector.active) {
|
|
959
|
+
this.conditionalFormatsCollector.feedText(text);
|
|
960
|
+
}
|
|
961
|
+
else if (this.filtersCollector.active) {
|
|
962
|
+
this.filtersCollector.feedText(text);
|
|
963
|
+
}
|
|
964
|
+
else if (this.unknownCollector.active) {
|
|
965
|
+
this.unknownCollector.feedText(text);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/** Feed a close-tag to a collector; if it completes, store the result on the model. */
|
|
969
|
+
tryCloseCollector(collector, name, modelKey) {
|
|
970
|
+
if (collector.feedClose(name)) {
|
|
971
|
+
if (this.model) {
|
|
972
|
+
this.model[modelKey] = collector.result;
|
|
973
|
+
}
|
|
974
|
+
collector.reset();
|
|
975
|
+
}
|
|
573
976
|
}
|
|
574
977
|
parseClose(name) {
|
|
978
|
+
// Handle raw-XML collectors — close tags
|
|
979
|
+
if (this.extLstCollector.active) {
|
|
980
|
+
this.tryCloseCollector(this.extLstCollector, name, "extLstXml");
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
if (this.formatsCollector.active) {
|
|
984
|
+
this.tryCloseCollector(this.formatsCollector, name, "formatsXml");
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
if (this.conditionalFormatsCollector.active) {
|
|
988
|
+
this.tryCloseCollector(this.conditionalFormatsCollector, name, "conditionalFormatsXml");
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
if (this.filtersCollector.active) {
|
|
992
|
+
this.tryCloseCollector(this.filtersCollector, name, "filtersXml");
|
|
993
|
+
return true;
|
|
994
|
+
}
|
|
995
|
+
if (this.unknownCollector.active) {
|
|
996
|
+
if (this.unknownCollector.feedClose(name)) {
|
|
997
|
+
this.unknownElementsXmlParts.push(this.unknownCollector.result);
|
|
998
|
+
this.unknownCollector.reset();
|
|
999
|
+
}
|
|
1000
|
+
return true;
|
|
1001
|
+
}
|
|
575
1002
|
// Handle pivotArea nested elements - close tags
|
|
576
1003
|
if (this.state.inPivotArea) {
|
|
577
1004
|
if (name === "pivotArea") {
|
|
578
|
-
this.
|
|
579
|
-
|
|
580
|
-
|
|
1005
|
+
if (this.state.inAutoSortScope) {
|
|
1006
|
+
this.autoSortScopeXmlBuffer.push("</pivotArea>");
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
this.pivotAreaXmlBuffer.push("</pivotArea>");
|
|
1010
|
+
if (this.currentChartFormat) {
|
|
1011
|
+
this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
|
|
1012
|
+
}
|
|
1013
|
+
this.pivotAreaXmlBuffer = [];
|
|
581
1014
|
}
|
|
582
1015
|
this.state.inPivotArea = false;
|
|
583
|
-
this.pivotAreaXmlBuffer = [];
|
|
584
|
-
this.pivotAreaDepth = 0;
|
|
585
1016
|
return true;
|
|
586
1017
|
}
|
|
587
1018
|
else if (name === "references" || name === "reference") {
|
|
588
|
-
this.
|
|
589
|
-
|
|
1019
|
+
if (this.state.inAutoSortScope) {
|
|
1020
|
+
this.autoSortScopeXmlBuffer.push(`</${name}>`);
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
this.pivotAreaXmlBuffer.push(`</${name}>`);
|
|
1024
|
+
}
|
|
590
1025
|
return true;
|
|
591
1026
|
}
|
|
592
1027
|
// x elements are self-closing, no need to handle close
|
|
@@ -594,10 +1029,20 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
594
1029
|
}
|
|
595
1030
|
switch (name) {
|
|
596
1031
|
case this.tag:
|
|
597
|
-
// End of pivotTableDefinition
|
|
1032
|
+
// End of pivotTableDefinition — store any collected unknown elements
|
|
1033
|
+
if (this.model && this.unknownElementsXmlParts.length > 0) {
|
|
1034
|
+
this.model.unknownElementsXml = this.unknownElementsXmlParts.join("");
|
|
1035
|
+
}
|
|
598
1036
|
return false;
|
|
599
1037
|
case "pivotFields":
|
|
600
|
-
|
|
1038
|
+
case "rowFields":
|
|
1039
|
+
case "colFields":
|
|
1040
|
+
case "dataFields":
|
|
1041
|
+
case "pageFields":
|
|
1042
|
+
case "rowItems":
|
|
1043
|
+
case "colItems":
|
|
1044
|
+
case "chartFormats":
|
|
1045
|
+
this.state.currentSection = null;
|
|
601
1046
|
break;
|
|
602
1047
|
case "pivotField":
|
|
603
1048
|
if (this.currentPivotField && this.model) {
|
|
@@ -606,199 +1051,139 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
606
1051
|
}
|
|
607
1052
|
break;
|
|
608
1053
|
case "items":
|
|
609
|
-
|
|
610
|
-
break;
|
|
611
|
-
case "rowFields":
|
|
612
|
-
this.state.inRowFields = false;
|
|
613
|
-
break;
|
|
614
|
-
case "colFields":
|
|
615
|
-
this.state.inColFields = false;
|
|
616
|
-
break;
|
|
617
|
-
case "dataFields":
|
|
618
|
-
this.state.inDataFields = false;
|
|
619
|
-
break;
|
|
620
|
-
case "rowItems":
|
|
621
|
-
this.state.inRowItems = false;
|
|
1054
|
+
// No close handling needed — item parsing guarded by currentPivotField
|
|
622
1055
|
break;
|
|
623
|
-
case "
|
|
624
|
-
|
|
1056
|
+
case "autoSortScope":
|
|
1057
|
+
// Finish collecting autoSortScope XML
|
|
1058
|
+
if (this.state.inAutoSortScope && this.currentPivotField) {
|
|
1059
|
+
this.autoSortScopeXmlBuffer.push("</autoSortScope>");
|
|
1060
|
+
this.currentPivotField.autoSortScopeXml = this.autoSortScopeXmlBuffer.join("");
|
|
1061
|
+
this.autoSortScopeXmlBuffer = [];
|
|
1062
|
+
this.state.inAutoSortScope = false;
|
|
1063
|
+
}
|
|
625
1064
|
break;
|
|
626
1065
|
case "i":
|
|
627
1066
|
// Finish row/col item
|
|
628
1067
|
if (this.currentRowItem && this.model) {
|
|
629
|
-
this.model.rowItems
|
|
1068
|
+
this.model.rowItems?.push(this.currentRowItem);
|
|
630
1069
|
this.currentRowItem = null;
|
|
631
1070
|
}
|
|
632
1071
|
else if (this.currentColItem && this.model) {
|
|
633
|
-
this.model.colItems
|
|
1072
|
+
this.model.colItems?.push(this.currentColItem);
|
|
634
1073
|
this.currentColItem = null;
|
|
635
1074
|
}
|
|
636
1075
|
break;
|
|
637
|
-
case "chartFormats":
|
|
638
|
-
this.state.inChartFormats = false;
|
|
639
|
-
break;
|
|
640
1076
|
case "chartFormat":
|
|
641
1077
|
if (this.currentChartFormat && this.model) {
|
|
642
|
-
this.model.chartFormats
|
|
1078
|
+
this.model.chartFormats?.push(this.currentChartFormat);
|
|
643
1079
|
this.currentChartFormat = null;
|
|
644
1080
|
}
|
|
645
1081
|
break;
|
|
646
1082
|
}
|
|
647
1083
|
return true;
|
|
648
1084
|
}
|
|
649
|
-
reconcile(_model, _options) {
|
|
650
|
-
// No reconciliation needed
|
|
651
|
-
}
|
|
652
1085
|
}
|
|
653
1086
|
exports.PivotTableXform = PivotTableXform;
|
|
654
1087
|
PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
|
|
655
1088
|
xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
|
656
1089
|
};
|
|
1090
|
+
PivotTableXform.EXTLST_XML = "<extLst>" +
|
|
1091
|
+
'<ext uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">' +
|
|
1092
|
+
'<x14:pivotTableDefinition hideValuesRow="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"/>' +
|
|
1093
|
+
"</ext>" +
|
|
1094
|
+
'<ext uri="{747A6164-185A-40DC-8AA5-F01512510D54}" xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout">' +
|
|
1095
|
+
'<xpdl:pivotTableDefinition16 EnabledSubtotalsDefault="0" SubtotalsOnTopDefault="0"/>' +
|
|
1096
|
+
"</ext>" +
|
|
1097
|
+
"</extLst>";
|
|
657
1098
|
// Helpers
|
|
658
|
-
/**
|
|
659
|
-
|
|
660
|
-
* Each <i> represents a row in the pivot table.
|
|
661
|
-
* - Regular items: <i><x/></i> for index 0, <i><x v="index"/></i> for index > 0
|
|
662
|
-
* - Grand total: <i t="grand"><x/></i>
|
|
663
|
-
* Note: When v=0, the v attribute should be omitted (Excel convention)
|
|
664
|
-
*/
|
|
665
|
-
function buildRowItems(rows, cacheFields) {
|
|
666
|
-
if (rows.length === 0) {
|
|
667
|
-
// No row fields - just grand total
|
|
668
|
-
return { count: 1, xml: '<i t="grand"><x /></i>' };
|
|
669
|
-
}
|
|
670
|
-
// Get unique values count from the first row field
|
|
671
|
-
const rowFieldIndex = rows[0];
|
|
672
|
-
const sharedItems = cacheFields[rowFieldIndex]?.sharedItems || [];
|
|
673
|
-
const itemCount = sharedItems.length;
|
|
674
|
-
// Build items: one for each unique value + grand total
|
|
675
|
-
const items = [];
|
|
676
|
-
// Regular items - reference each unique value by index
|
|
677
|
-
// Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
|
|
678
|
-
for (let i = 0; i < itemCount; i++) {
|
|
679
|
-
if (i === 0) {
|
|
680
|
-
items.push("<i><x /></i>");
|
|
681
|
-
}
|
|
682
|
-
else {
|
|
683
|
-
items.push(`<i><x v="${i}" /></i>`);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
// Grand total row
|
|
687
|
-
items.push('<i t="grand"><x /></i>');
|
|
1099
|
+
/** Parse attributes of a row/col `<i>` element into a RowColItem. */
|
|
1100
|
+
function parseRowColItem(attributes) {
|
|
688
1101
|
return {
|
|
689
|
-
|
|
690
|
-
|
|
1102
|
+
t: attributes.t,
|
|
1103
|
+
r: attributes.r !== undefined ? parseInt(attributes.r, 10) : undefined,
|
|
1104
|
+
i: attributes.i !== undefined ? parseInt(attributes.i, 10) : undefined,
|
|
1105
|
+
x: []
|
|
691
1106
|
};
|
|
692
1107
|
}
|
|
693
1108
|
/**
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
* Note: When v=0, the v attribute should be omitted (Excel convention)
|
|
1109
|
+
* Render dataField XML elements for all values in the pivot table.
|
|
1110
|
+
* Each value field gets its own metric from the `valueMetrics` array.
|
|
697
1111
|
*/
|
|
698
|
-
function
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
items.push(`<i><x v="${i}" /></i>`);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
items.push('<i t="grand"><x /></i>');
|
|
713
|
-
return { count: items.length, xml: items.join("\n ") };
|
|
1112
|
+
function renderDataFields(xmlStream, cacheFields, values, valueMetrics) {
|
|
1113
|
+
xmlStream.openNode("dataFields", { count: values.length });
|
|
1114
|
+
for (let i = 0; i < values.length; i++) {
|
|
1115
|
+
const valueIndex = values[i];
|
|
1116
|
+
const metric = valueMetrics[i] ?? "sum";
|
|
1117
|
+
const metricName = pivot_table_1.METRIC_DISPLAY_NAMES[metric];
|
|
1118
|
+
const field = cacheFields[valueIndex];
|
|
1119
|
+
if (!field) {
|
|
1120
|
+
throw new Error(`Value field index ${valueIndex} is out of bounds (cacheFields has ${cacheFields.length} entries)`);
|
|
714
1121
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
// Regular items - reference each unique value by index
|
|
725
|
-
// Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
|
|
726
|
-
for (let i = 0; i < itemCount; i++) {
|
|
727
|
-
if (i === 0) {
|
|
728
|
-
items.push("<i><x /></i>");
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
items.push(`<i><x v="${i}" /></i>`);
|
|
1122
|
+
const attrs = {
|
|
1123
|
+
name: `${metricName} of ${field.name}`,
|
|
1124
|
+
fld: valueIndex,
|
|
1125
|
+
baseField: 0,
|
|
1126
|
+
baseItem: 0
|
|
1127
|
+
};
|
|
1128
|
+
// OOXML default is "sum", so omit subtotal attribute for sum
|
|
1129
|
+
if (metric !== "sum") {
|
|
1130
|
+
attrs.subtotal = metric;
|
|
732
1131
|
}
|
|
1132
|
+
xmlStream.leafNode("dataField", attrs);
|
|
733
1133
|
}
|
|
734
|
-
|
|
735
|
-
items.push('<i t="grand"><x /></i>');
|
|
736
|
-
return {
|
|
737
|
-
count: items.length,
|
|
738
|
-
xml: items.join("\n ")
|
|
739
|
-
};
|
|
1134
|
+
xmlStream.closeNode();
|
|
740
1135
|
}
|
|
741
|
-
|
|
742
|
-
* Build dataField XML elements for all values in the pivot table.
|
|
743
|
-
* Supports multiple values when columns is empty.
|
|
744
|
-
*/
|
|
745
|
-
function buildDataFields(cacheFields, values, metric) {
|
|
746
|
-
const metricName = metric === "count" ? "Count" : "Sum";
|
|
747
|
-
// For 'count' metric, Excel requires subtotal="count" attribute
|
|
748
|
-
const subtotalAttr = metric === "count" ? ' subtotal="count"' : "";
|
|
749
|
-
return values
|
|
750
|
-
.map(valueIndex => `<dataField
|
|
751
|
-
name="${metricName} of ${(0, utils_1.xmlEncode)(cacheFields[valueIndex].name)}"
|
|
752
|
-
fld="${valueIndex}"
|
|
753
|
-
baseField="0"
|
|
754
|
-
baseItem="0"${subtotalAttr}
|
|
755
|
-
/>`)
|
|
756
|
-
.join("");
|
|
757
|
-
}
|
|
758
|
-
function renderPivotFields(pivotTable) {
|
|
1136
|
+
function renderPivotFields(xmlStream, pivotTable) {
|
|
759
1137
|
// Pre-compute field type lookup for O(1) access
|
|
760
1138
|
const rowSet = new Set(pivotTable.rows);
|
|
761
1139
|
const colSet = new Set(pivotTable.columns);
|
|
762
1140
|
const valueSet = new Set(pivotTable.values);
|
|
763
|
-
|
|
764
|
-
|
|
1141
|
+
const pageSet = new Set(pivotTable.pages ?? []);
|
|
1142
|
+
xmlStream.openNode("pivotFields", { count: pivotTable.cacheFields.length });
|
|
1143
|
+
for (let fieldIndex = 0; fieldIndex < pivotTable.cacheFields.length; fieldIndex++) {
|
|
1144
|
+
const cacheField = pivotTable.cacheFields[fieldIndex];
|
|
765
1145
|
const isRow = rowSet.has(fieldIndex);
|
|
766
1146
|
const isCol = colSet.has(fieldIndex);
|
|
767
1147
|
const isValue = valueSet.has(fieldIndex);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1148
|
+
const isPage = pageSet.has(fieldIndex);
|
|
1149
|
+
renderPivotField(xmlStream, isRow, isCol, isValue, isPage, cacheField.sharedItems);
|
|
1150
|
+
}
|
|
1151
|
+
xmlStream.closeNode();
|
|
771
1152
|
}
|
|
772
|
-
function renderPivotField(isRow, isCol, isValue, sharedItems) {
|
|
1153
|
+
function renderPivotField(xmlStream, isRow, isCol, isValue, isPage, sharedItems) {
|
|
773
1154
|
// A field can be both a row/column field AND a value field
|
|
774
1155
|
// In this case, it needs both axis attribute AND dataField="1"
|
|
775
|
-
if (isRow || isCol) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1156
|
+
if (isRow || isCol || isPage) {
|
|
1157
|
+
if (!sharedItems) {
|
|
1158
|
+
throw new Error("sharedItems is required for axis field (row/column/page)");
|
|
1159
|
+
}
|
|
1160
|
+
const axis = isRow ? "axisRow" : isCol ? "axisCol" : "axisPage";
|
|
1161
|
+
const attrs = { axis };
|
|
780
1162
|
if (isValue) {
|
|
781
|
-
|
|
1163
|
+
attrs.dataField = "1";
|
|
782
1164
|
}
|
|
1165
|
+
attrs.compact = "0";
|
|
1166
|
+
attrs.outline = "0";
|
|
1167
|
+
attrs.showAll = "0";
|
|
1168
|
+
xmlStream.openNode("pivotField", attrs);
|
|
783
1169
|
// items = one for each shared item + one default item
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
</items>
|
|
793
|
-
</pivotField>
|
|
794
|
-
`;
|
|
1170
|
+
xmlStream.openNode("items", { count: sharedItems.length + 1 });
|
|
1171
|
+
for (let i = 0; i < sharedItems.length; i++) {
|
|
1172
|
+
xmlStream.leafNode("item", { x: i });
|
|
1173
|
+
}
|
|
1174
|
+
xmlStream.leafNode("item", { t: "default" }); // Required default item for subtotals/grand totals
|
|
1175
|
+
xmlStream.closeNode(); // items
|
|
1176
|
+
xmlStream.closeNode(); // pivotField
|
|
1177
|
+
return;
|
|
795
1178
|
}
|
|
796
1179
|
// Value fields and non-axis fields should have defaultSubtotal="0"
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1180
|
+
const attrs = {};
|
|
1181
|
+
if (isValue) {
|
|
1182
|
+
attrs.dataField = "1";
|
|
1183
|
+
}
|
|
1184
|
+
attrs.compact = "0";
|
|
1185
|
+
attrs.outline = "0";
|
|
1186
|
+
attrs.showAll = "0";
|
|
1187
|
+
attrs.defaultSubtotal = "0";
|
|
1188
|
+
xmlStream.leafNode("pivotField", attrs);
|
|
804
1189
|
}
|