@cj-tech-master/excelts 9.2.1 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/README_zh.md +29 -6
- package/dist/browser/index.browser.d.ts +1 -1
- package/dist/browser/index.browser.js +4 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.js +4 -0
- package/dist/browser/modules/excel/cell.d.ts +17 -3
- package/dist/browser/modules/excel/cell.js +170 -22
- package/dist/browser/modules/excel/defined-names.d.ts +96 -1
- package/dist/browser/modules/excel/defined-names.js +411 -21
- package/dist/browser/modules/excel/image.d.ts +11 -0
- package/dist/browser/modules/excel/image.js +24 -1
- package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/browser/modules/excel/table.js +15 -2
- package/dist/browser/modules/excel/types.d.ts +133 -2
- package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/browser/modules/excel/utils/col-cache.js +15 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
- package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/browser/modules/excel/workbook.browser.js +318 -34
- package/dist/browser/modules/excel/workbook.d.ts +1 -1
- package/dist/browser/modules/excel/worksheet.d.ts +3 -1
- package/dist/browser/modules/excel/worksheet.js +21 -2
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/browser/modules/formula/compile/address-utils.js +83 -0
- package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
- package/dist/browser/modules/formula/compile/binder.js +487 -0
- package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
- package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
- package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/browser/modules/formula/functions/_date-context.js +29 -0
- package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/browser/modules/formula/functions/_shared.js +381 -0
- package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/browser/modules/formula/functions/conditional.js +343 -0
- package/dist/browser/modules/formula/functions/database.d.ts +37 -0
- package/dist/browser/modules/formula/functions/database.js +274 -0
- package/dist/browser/modules/formula/functions/date.d.ts +61 -0
- package/dist/browser/modules/formula/functions/date.js +855 -0
- package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/browser/modules/formula/functions/engineering.js +1128 -0
- package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
- package/dist/browser/modules/formula/functions/financial.js +2296 -0
- package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/browser/modules/formula/functions/lookup.js +886 -0
- package/dist/browser/modules/formula/functions/math.d.ts +114 -0
- package/dist/browser/modules/formula/functions/math.js +1406 -0
- package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/browser/modules/formula/functions/statistical.js +3390 -0
- package/dist/browser/modules/formula/functions/text.d.ts +86 -0
- package/dist/browser/modules/formula/functions/text.js +1845 -0
- package/dist/browser/modules/formula/host-registry.d.ts +53 -0
- package/dist/browser/modules/formula/host-registry.js +69 -0
- package/dist/browser/modules/formula/index.d.ts +39 -0
- package/dist/browser/modules/formula/index.js +49 -0
- package/dist/browser/modules/formula/install.d.ts +62 -0
- package/dist/browser/modules/formula/install.js +88 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
- package/dist/browser/modules/formula/materialize/types.js +29 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
- package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
- package/dist/browser/modules/formula/runtime/values.js +385 -0
- package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/browser/modules/formula/syntax/ast.js +28 -0
- package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/browser/modules/formula/syntax/parser.js +439 -0
- package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/browser/modules/formula/syntax/token-types.js +59 -0
- package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/browser/modules/pdf/excel-bridge.js +9 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/modules/excel/cell.js +170 -22
- package/dist/cjs/modules/excel/defined-names.js +411 -21
- package/dist/cjs/modules/excel/image.js +24 -1
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/cjs/modules/excel/table.js +15 -2
- package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
- package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
- package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
- package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
- package/dist/cjs/modules/excel/workbook.browser.js +318 -34
- package/dist/cjs/modules/excel/worksheet.js +20 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
- package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
- package/dist/cjs/modules/formula/compile/binder.js +489 -0
- package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
- package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
- package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
- package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
- package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
- package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
- package/dist/cjs/modules/formula/functions/_shared.js +396 -0
- package/dist/cjs/modules/formula/functions/conditional.js +354 -0
- package/dist/cjs/modules/formula/functions/database.js +288 -0
- package/dist/cjs/modules/formula/functions/date.js +883 -0
- package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
- package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
- package/dist/cjs/modules/formula/functions/financial.js +2348 -0
- package/dist/cjs/modules/formula/functions/lookup.js +902 -0
- package/dist/cjs/modules/formula/functions/math.js +1487 -0
- package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
- package/dist/cjs/modules/formula/functions/text.js +1889 -0
- package/dist/cjs/modules/formula/host-registry.js +75 -0
- package/dist/cjs/modules/formula/index.js +58 -0
- package/dist/cjs/modules/formula/install.js +93 -0
- package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
- package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
- package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
- package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
- package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
- package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
- package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
- package/dist/cjs/modules/formula/materialize/types.js +32 -0
- package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
- package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
- package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
- package/dist/cjs/modules/formula/runtime/values.js +385 -0
- package/dist/cjs/modules/formula/syntax/ast.js +8 -0
- package/dist/cjs/modules/formula/syntax/parser.js +440 -0
- package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
- package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
- package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
- package/dist/esm/index.browser.js +4 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/modules/excel/cell.js +170 -22
- package/dist/esm/modules/excel/defined-names.js +411 -21
- package/dist/esm/modules/excel/image.js +24 -1
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
- package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
- package/dist/esm/modules/excel/table.js +15 -2
- package/dist/esm/modules/excel/utils/col-cache.js +15 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
- package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
- package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
- package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
- package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
- package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
- package/dist/esm/modules/excel/workbook.browser.js +318 -34
- package/dist/esm/modules/excel/worksheet.js +21 -2
- package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
- package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
- package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
- package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
- package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
- package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
- package/dist/esm/modules/formula/compile/address-utils.js +83 -0
- package/dist/esm/modules/formula/compile/binder.js +487 -0
- package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
- package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
- package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
- package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
- package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
- package/dist/esm/modules/formula/functions/_date-context.js +29 -0
- package/dist/esm/modules/formula/functions/_shared.js +381 -0
- package/dist/esm/modules/formula/functions/conditional.js +343 -0
- package/dist/esm/modules/formula/functions/database.js +274 -0
- package/dist/esm/modules/formula/functions/date.js +855 -0
- package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
- package/dist/esm/modules/formula/functions/engineering.js +1128 -0
- package/dist/esm/modules/formula/functions/financial.js +2296 -0
- package/dist/esm/modules/formula/functions/lookup.js +886 -0
- package/dist/esm/modules/formula/functions/math.js +1406 -0
- package/dist/esm/modules/formula/functions/statistical.js +3390 -0
- package/dist/esm/modules/formula/functions/text.js +1845 -0
- package/dist/esm/modules/formula/host-registry.js +69 -0
- package/dist/esm/modules/formula/index.js +49 -0
- package/dist/esm/modules/formula/install.js +88 -0
- package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
- package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
- package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
- package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
- package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
- package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
- package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
- package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
- package/dist/esm/modules/formula/materialize/types.js +29 -0
- package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
- package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
- package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
- package/dist/esm/modules/formula/runtime/values.js +385 -0
- package/dist/esm/modules/formula/syntax/ast.js +28 -0
- package/dist/esm/modules/formula/syntax/parser.js +439 -0
- package/dist/esm/modules/formula/syntax/token-types.js +59 -0
- package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
- package/dist/esm/modules/pdf/excel-bridge.js +9 -0
- package/dist/iife/excelts.iife.js +2302 -373
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +34 -34
- package/dist/types/index.browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/modules/excel/cell.d.ts +17 -3
- package/dist/types/modules/excel/defined-names.d.ts +96 -1
- package/dist/types/modules/excel/image.d.ts +11 -0
- package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
- package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
- package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
- package/dist/types/modules/excel/types.d.ts +133 -2
- package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
- package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
- package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
- package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
- package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
- package/dist/types/modules/excel/workbook.d.ts +1 -1
- package/dist/types/modules/excel/worksheet.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
- package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
- package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
- package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
- package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
- package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
- package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
- package/dist/types/modules/formula/compile/binder.d.ts +42 -0
- package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
- package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
- package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
- package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
- package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
- package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
- package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
- package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
- package/dist/types/modules/formula/functions/database.d.ts +37 -0
- package/dist/types/modules/formula/functions/date.d.ts +61 -0
- package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
- package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
- package/dist/types/modules/formula/functions/financial.d.ts +202 -0
- package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
- package/dist/types/modules/formula/functions/math.d.ts +114 -0
- package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
- package/dist/types/modules/formula/functions/text.d.ts +86 -0
- package/dist/types/modules/formula/host-registry.d.ts +53 -0
- package/dist/types/modules/formula/index.d.ts +39 -0
- package/dist/types/modules/formula/install.d.ts +62 -0
- package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
- package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
- package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
- package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
- package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
- package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
- package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
- package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
- package/dist/types/modules/formula/materialize/types.d.ts +179 -0
- package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
- package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
- package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
- package/dist/types/modules/formula/runtime/values.d.ts +211 -0
- package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
- package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
- package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
- package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
- package/package.json +28 -28
|
@@ -0,0 +1,886 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lookup / Reference Functions — Native RuntimeValue Implementation
|
|
3
|
+
*/
|
|
4
|
+
import { RVKind, ERRORS, BLANK, rvNumber, rvString, rvArray, toNumberRV, toBooleanRV, toStringRV, topLeft, scalarEquals, compareScalarsSameKind, isError, isArray } from "../runtime/values.js";
|
|
5
|
+
import { excelWildcardToRegex, getCell, hasUnescapedWildcard, unescapeExcelWildcard } from "./_shared.js";
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Helpers
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/** Compare two scalar values for same-type ordering. */
|
|
10
|
+
function sameType(a, b) {
|
|
11
|
+
return a.kind === b.kind;
|
|
12
|
+
}
|
|
13
|
+
function scalarIsNumber(v) {
|
|
14
|
+
return v.kind === RVKind.Number;
|
|
15
|
+
}
|
|
16
|
+
function scalarIsString(v) {
|
|
17
|
+
return v.kind === RVKind.String;
|
|
18
|
+
}
|
|
19
|
+
function scalarStringEquals(a, b) {
|
|
20
|
+
return scalarIsString(a) && scalarIsString(b) && a.value.toLowerCase() === b.value.toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Ordered comparison of two scalars. Numbers compared by value; strings by
|
|
24
|
+
* case-insensitive lexical order. Returns NaN when the two operands have
|
|
25
|
+
* incompatible types (e.g. number vs string) so callers can skip them.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Use `compareScalarsSameKind` from `runtime/values` directly —
|
|
29
|
+
* the two are identical. Retained only as a local alias to keep the diff
|
|
30
|
+
* small; callers inside this file are free to migrate.
|
|
31
|
+
*/
|
|
32
|
+
const compareScalar = compareScalarsSameKind;
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Functions
|
|
35
|
+
// ============================================================================
|
|
36
|
+
export function fnROW(args) {
|
|
37
|
+
// The reference-aware path is handled in evaluator.ts via tryEvaluateRefFunction.
|
|
38
|
+
// This fallback is only reached when the argument is not a reference (e.g.
|
|
39
|
+
// ROW(INDIRECT("A5")) or ROW({1,2,3})), which Excel rejects as #VALUE!.
|
|
40
|
+
return ERRORS.VALUE;
|
|
41
|
+
}
|
|
42
|
+
export function fnCOLUMN(args) {
|
|
43
|
+
// See fnROW. Non-reference argument → #VALUE!.
|
|
44
|
+
return ERRORS.VALUE;
|
|
45
|
+
}
|
|
46
|
+
export function fnROWS(args) {
|
|
47
|
+
const a = args[0];
|
|
48
|
+
if (a.kind === RVKind.Array) {
|
|
49
|
+
return rvNumber(a.height);
|
|
50
|
+
}
|
|
51
|
+
return rvNumber(1);
|
|
52
|
+
}
|
|
53
|
+
export function fnCOLUMNS(args) {
|
|
54
|
+
const a = args[0];
|
|
55
|
+
if (a.kind === RVKind.Array) {
|
|
56
|
+
return rvNumber(a.width);
|
|
57
|
+
}
|
|
58
|
+
return rvNumber(1);
|
|
59
|
+
}
|
|
60
|
+
export function fnINDEX(args) {
|
|
61
|
+
if (!isArray(args[0])) {
|
|
62
|
+
return topLeft(args[0]);
|
|
63
|
+
}
|
|
64
|
+
const arr = args[0];
|
|
65
|
+
const rowNumV = args.length > 1 ? toNumberRV(args[1]) : rvNumber(0);
|
|
66
|
+
if (isError(rowNumV)) {
|
|
67
|
+
return rowNumV;
|
|
68
|
+
}
|
|
69
|
+
// Excel truncates fractional indices toward zero before bounds checks.
|
|
70
|
+
// Without this, `INDEX(a, 1.5, 1)` would index into `arr.rows[0.5]`, which
|
|
71
|
+
// in V8 silently returns `undefined` and corrupts downstream values.
|
|
72
|
+
const rowNum = Math.trunc(rowNumV.value);
|
|
73
|
+
const colNumV = args.length > 2 ? toNumberRV(args[2]) : rvNumber(0);
|
|
74
|
+
if (isError(colNumV)) {
|
|
75
|
+
return colNumV;
|
|
76
|
+
}
|
|
77
|
+
const colNum = Math.trunc(colNumV.value);
|
|
78
|
+
if (rowNum < 0 || colNum < 0) {
|
|
79
|
+
return ERRORS.VALUE;
|
|
80
|
+
}
|
|
81
|
+
if (rowNum === 0 && colNum === 0) {
|
|
82
|
+
return arr;
|
|
83
|
+
}
|
|
84
|
+
// rowNum=0: return entire column as array
|
|
85
|
+
if (rowNum === 0) {
|
|
86
|
+
const c = colNum - 1;
|
|
87
|
+
if (c < 0 || c >= arr.width) {
|
|
88
|
+
return ERRORS.REF;
|
|
89
|
+
}
|
|
90
|
+
const rows = [];
|
|
91
|
+
for (let r = 0; r < arr.height; r++) {
|
|
92
|
+
rows.push([getCell(arr, r, c)]);
|
|
93
|
+
}
|
|
94
|
+
return rvArray(rows);
|
|
95
|
+
}
|
|
96
|
+
// colNum=0: return entire row as array
|
|
97
|
+
if (colNum === 0) {
|
|
98
|
+
const r = rowNum - 1;
|
|
99
|
+
if (r < 0 || r >= arr.height) {
|
|
100
|
+
return ERRORS.REF;
|
|
101
|
+
}
|
|
102
|
+
return rvArray([[...arr.rows[r]]]);
|
|
103
|
+
}
|
|
104
|
+
// Single cell
|
|
105
|
+
const r = rowNum - 1;
|
|
106
|
+
const c = colNum - 1;
|
|
107
|
+
if (r < 0 || r >= arr.height || c < 0 || c >= arr.width) {
|
|
108
|
+
return ERRORS.REF;
|
|
109
|
+
}
|
|
110
|
+
return arr.rows[r][c];
|
|
111
|
+
}
|
|
112
|
+
export function fnMATCH(args) {
|
|
113
|
+
const lookupValue = topLeft(args[0]);
|
|
114
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
115
|
+
return lookupValue;
|
|
116
|
+
}
|
|
117
|
+
if (!isArray(args[1])) {
|
|
118
|
+
return ERRORS.NA;
|
|
119
|
+
}
|
|
120
|
+
const lookupArr = args[1];
|
|
121
|
+
const matchTypeV = args.length > 2 ? toNumberRV(args[2]) : rvNumber(1);
|
|
122
|
+
if (isError(matchTypeV)) {
|
|
123
|
+
return matchTypeV;
|
|
124
|
+
}
|
|
125
|
+
const matchType = matchTypeV.value;
|
|
126
|
+
// Flatten to 1D
|
|
127
|
+
const flat = [];
|
|
128
|
+
for (let r = 0; r < lookupArr.height; r++) {
|
|
129
|
+
for (let c = 0; c < lookupArr.width; c++) {
|
|
130
|
+
flat.push(getCell(lookupArr, r, c));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (matchType === 0) {
|
|
134
|
+
// Exact match (with wildcard support for string lookups). The shared
|
|
135
|
+
// `excelWildcardToRegex` converter applies the same `~*`, `~?`, `~~`
|
|
136
|
+
// escape rules used by SEARCH, XLOOKUP, and SUMIF so behaviour is
|
|
137
|
+
// consistent across the engine.
|
|
138
|
+
const lookupStr = scalarIsString(lookupValue) ? lookupValue.value : null;
|
|
139
|
+
const hasWildcard = lookupStr !== null && hasUnescapedWildcard(lookupStr);
|
|
140
|
+
let wildcardRe = null;
|
|
141
|
+
if (hasWildcard && lookupStr !== null) {
|
|
142
|
+
try {
|
|
143
|
+
wildcardRe = new RegExp("^" + excelWildcardToRegex(lookupStr) + "$", "i");
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
wildcardRe = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (let i = 0; i < flat.length; i++) {
|
|
150
|
+
if (scalarEquals(flat[i], lookupValue)) {
|
|
151
|
+
return rvNumber(i + 1);
|
|
152
|
+
}
|
|
153
|
+
const fi = flat[i];
|
|
154
|
+
if (scalarIsString(fi) && scalarIsString(lookupValue)) {
|
|
155
|
+
if (wildcardRe) {
|
|
156
|
+
if (wildcardRe.test(fi.value)) {
|
|
157
|
+
return rvNumber(i + 1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// No unescaped wildcard — but the pattern may still contain
|
|
162
|
+
// `~*` / `~?` / `~~` escape sequences that should reduce to
|
|
163
|
+
// their literal character before comparison. Calling
|
|
164
|
+
// `unescapeExcelWildcard` here matches the treatment that
|
|
165
|
+
// SEARCH and the criteria predicate use; without it,
|
|
166
|
+
// `MATCH("a~*b", ...)` would literally look for `"a~*b"`
|
|
167
|
+
// instead of `"a*b"`.
|
|
168
|
+
const literal = unescapeExcelWildcard(lookupValue.value).toLowerCase();
|
|
169
|
+
if (fi.value.toLowerCase() === literal) {
|
|
170
|
+
return rvNumber(i + 1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return ERRORS.NA;
|
|
176
|
+
}
|
|
177
|
+
if (matchType === 1 || matchType > 0) {
|
|
178
|
+
// Sorted ascending. Find largest value <= lookupValue.
|
|
179
|
+
let bestIdx = -1;
|
|
180
|
+
for (let i = 0; i < flat.length; i++) {
|
|
181
|
+
const v = flat[i];
|
|
182
|
+
if (sameType(v, lookupValue)) {
|
|
183
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue)) {
|
|
184
|
+
if (v.value <= lookupValue.value) {
|
|
185
|
+
bestIdx = i;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else if (scalarIsString(v) && scalarIsString(lookupValue)) {
|
|
192
|
+
if (v.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
193
|
+
bestIdx = i;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return bestIdx >= 0 ? rvNumber(bestIdx + 1) : ERRORS.NA;
|
|
202
|
+
}
|
|
203
|
+
// matchType === -1: Sorted descending. Find smallest value >= lookupValue.
|
|
204
|
+
let bestIdx = -1;
|
|
205
|
+
for (let i = 0; i < flat.length; i++) {
|
|
206
|
+
const v = flat[i];
|
|
207
|
+
if (sameType(v, lookupValue)) {
|
|
208
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue)) {
|
|
209
|
+
if (v.value >= lookupValue.value) {
|
|
210
|
+
bestIdx = i;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (scalarIsString(v) && scalarIsString(lookupValue)) {
|
|
217
|
+
if (v.value.toLowerCase() >= lookupValue.value.toLowerCase()) {
|
|
218
|
+
bestIdx = i;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return bestIdx >= 0 ? rvNumber(bestIdx + 1) : ERRORS.NA;
|
|
227
|
+
}
|
|
228
|
+
export function fnVLOOKUP(args) {
|
|
229
|
+
const lookupValue = topLeft(args[0]);
|
|
230
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
231
|
+
return lookupValue;
|
|
232
|
+
}
|
|
233
|
+
if (!isArray(args[1])) {
|
|
234
|
+
return ERRORS.NA;
|
|
235
|
+
}
|
|
236
|
+
const table = args[1];
|
|
237
|
+
const colIndexV = toNumberRV(args[2]);
|
|
238
|
+
if (isError(colIndexV)) {
|
|
239
|
+
return colIndexV;
|
|
240
|
+
}
|
|
241
|
+
// VLOOKUP truncates the column index toward zero before bounds checks.
|
|
242
|
+
const colIndex = Math.trunc(colIndexV.value);
|
|
243
|
+
const rangeLookupV = args.length > 3 ? toBooleanRV(args[3]) : { kind: RVKind.Boolean, value: true };
|
|
244
|
+
if (isError(rangeLookupV)) {
|
|
245
|
+
return rangeLookupV;
|
|
246
|
+
}
|
|
247
|
+
const rangeLookup = rangeLookupV.value;
|
|
248
|
+
if (colIndex < 1 || colIndex > table.width) {
|
|
249
|
+
return ERRORS.REF;
|
|
250
|
+
}
|
|
251
|
+
if (!rangeLookup) {
|
|
252
|
+
// Exact match
|
|
253
|
+
for (let r = 0; r < table.height; r++) {
|
|
254
|
+
const cell = getCell(table, r, 0);
|
|
255
|
+
if (scalarEquals(cell, lookupValue)) {
|
|
256
|
+
return getCell(table, r, colIndex - 1);
|
|
257
|
+
}
|
|
258
|
+
if (scalarStringEquals(cell, lookupValue)) {
|
|
259
|
+
return getCell(table, r, colIndex - 1);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return ERRORS.NA;
|
|
263
|
+
}
|
|
264
|
+
// Approximate match: sorted ascending by first column.
|
|
265
|
+
let bestRow = -1;
|
|
266
|
+
for (let r = 0; r < table.height; r++) {
|
|
267
|
+
const v = getCell(table, r, 0);
|
|
268
|
+
if (sameType(v, lookupValue)) {
|
|
269
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue)) {
|
|
270
|
+
if (v.value <= lookupValue.value) {
|
|
271
|
+
bestRow = r;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else if (scalarIsString(v) && scalarIsString(lookupValue)) {
|
|
278
|
+
if (v.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
279
|
+
bestRow = r;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return bestRow >= 0 ? getCell(table, bestRow, colIndex - 1) : ERRORS.NA;
|
|
288
|
+
}
|
|
289
|
+
export function fnHLOOKUP(args) {
|
|
290
|
+
const lookupValue = topLeft(args[0]);
|
|
291
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
292
|
+
return lookupValue;
|
|
293
|
+
}
|
|
294
|
+
if (!isArray(args[1])) {
|
|
295
|
+
return ERRORS.NA;
|
|
296
|
+
}
|
|
297
|
+
const table = args[1];
|
|
298
|
+
const rowIndexV = toNumberRV(args[2]);
|
|
299
|
+
if (isError(rowIndexV)) {
|
|
300
|
+
return rowIndexV;
|
|
301
|
+
}
|
|
302
|
+
// HLOOKUP truncates the row index toward zero before bounds checks.
|
|
303
|
+
const rowIndex = Math.trunc(rowIndexV.value);
|
|
304
|
+
const rangeLookupV = args.length > 3 ? toBooleanRV(args[3]) : { kind: RVKind.Boolean, value: true };
|
|
305
|
+
if (isError(rangeLookupV)) {
|
|
306
|
+
return rangeLookupV;
|
|
307
|
+
}
|
|
308
|
+
const rangeLookup = rangeLookupV.value;
|
|
309
|
+
if (rowIndex < 1 || rowIndex > table.height) {
|
|
310
|
+
return ERRORS.REF;
|
|
311
|
+
}
|
|
312
|
+
if (!rangeLookup) {
|
|
313
|
+
for (let c = 0; c < table.width; c++) {
|
|
314
|
+
if (scalarEquals(getCell(table, 0, c), lookupValue)) {
|
|
315
|
+
return getCell(table, rowIndex - 1, c);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return ERRORS.NA;
|
|
319
|
+
}
|
|
320
|
+
let bestCol = -1;
|
|
321
|
+
for (let c = 0; c < table.width; c++) {
|
|
322
|
+
const hv = getCell(table, 0, c);
|
|
323
|
+
if (sameType(hv, lookupValue)) {
|
|
324
|
+
// For approximate match, find largest <= lookupValue
|
|
325
|
+
if (scalarIsNumber(hv) && scalarIsNumber(lookupValue)) {
|
|
326
|
+
if (hv.value <= lookupValue.value) {
|
|
327
|
+
bestCol = c;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else if (scalarIsString(hv) && scalarIsString(lookupValue)) {
|
|
331
|
+
if (hv.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
332
|
+
bestCol = c;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return bestCol >= 0 ? getCell(table, rowIndex - 1, bestCol) : ERRORS.NA;
|
|
338
|
+
}
|
|
339
|
+
export function fnXLOOKUP(args) {
|
|
340
|
+
const lookupValue = topLeft(args[0]);
|
|
341
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
342
|
+
return lookupValue;
|
|
343
|
+
}
|
|
344
|
+
if (!isArray(args[1])) {
|
|
345
|
+
return ERRORS.VALUE;
|
|
346
|
+
}
|
|
347
|
+
const lookupArr = args[1];
|
|
348
|
+
if (!isArray(args[2])) {
|
|
349
|
+
return ERRORS.VALUE;
|
|
350
|
+
}
|
|
351
|
+
const returnArr = args[2];
|
|
352
|
+
const ifNotFound = args.length > 3 ? topLeft(args[3]) : null;
|
|
353
|
+
const matchModeV = args.length > 4 ? toNumberRV(args[4]) : rvNumber(0);
|
|
354
|
+
if (isError(matchModeV)) {
|
|
355
|
+
return matchModeV;
|
|
356
|
+
}
|
|
357
|
+
const matchMode = matchModeV.value;
|
|
358
|
+
const searchModeV = args.length > 5 ? toNumberRV(args[5]) : rvNumber(1);
|
|
359
|
+
if (isError(searchModeV)) {
|
|
360
|
+
return searchModeV;
|
|
361
|
+
}
|
|
362
|
+
const searchMode = searchModeV.value;
|
|
363
|
+
// Flatten lookup array to 1D
|
|
364
|
+
const flat = [];
|
|
365
|
+
const isRow = lookupArr.height === 1;
|
|
366
|
+
if (isRow) {
|
|
367
|
+
for (let c = 0; c < lookupArr.width; c++) {
|
|
368
|
+
flat.push(getCell(lookupArr, 0, c));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
for (let r = 0; r < lookupArr.height; r++) {
|
|
373
|
+
flat.push(getCell(lookupArr, r, 0));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
let foundIdx = -1;
|
|
377
|
+
const doCompare = (a, b) => {
|
|
378
|
+
// Use `compareScalarsSameKind` (shared with sorting / linear match)
|
|
379
|
+
// instead of `localeCompare`. `localeCompare` is locale-sensitive and
|
|
380
|
+
// can disagree with the `toLowerCase+===` equality check used on the
|
|
381
|
+
// linear-search path — binary search would then skip the exact cell
|
|
382
|
+
// that linear search would find (e.g. Turkish dotless I, ß→ss, etc.).
|
|
383
|
+
// See R6-P1-11.
|
|
384
|
+
const cmp = compareScalarsSameKind(a, b);
|
|
385
|
+
return Number.isFinite(cmp) ? cmp : 0;
|
|
386
|
+
};
|
|
387
|
+
// ── Binary search for sorted data (searchMode = ±2) ──
|
|
388
|
+
// Excel assumes the data is sorted ascending (2) or descending (-2). The
|
|
389
|
+
// array must contain values of a single type compatible with `lookupValue`;
|
|
390
|
+
// cells of an incompatible type make the sort invalid and binary search
|
|
391
|
+
// cannot produce a meaningful result, so we fall back to #N/A in that
|
|
392
|
+
// scenario (matching Excel's behaviour when the data is "not sorted").
|
|
393
|
+
//
|
|
394
|
+
// Supports matchMode 0 (exact), -1 (exact or next smaller), 1 (exact or
|
|
395
|
+
// next larger). Wildcard matchMode (2) is incompatible with binary search
|
|
396
|
+
// by definition — Excel silently downgrades to linear scan, which we do
|
|
397
|
+
// by leaving `searchMode` as 1 below.
|
|
398
|
+
const isBinary = (searchMode === 2 || searchMode === -2) && matchMode !== 2;
|
|
399
|
+
if (isBinary) {
|
|
400
|
+
const ascending = searchMode === 2;
|
|
401
|
+
let lo = 0;
|
|
402
|
+
let hi = flat.length - 1;
|
|
403
|
+
let exact = -1;
|
|
404
|
+
let nextSmaller = -1; // largest index with value < lookupValue (ascending)
|
|
405
|
+
let nextLarger = -1; // smallest index with value > lookupValue (ascending)
|
|
406
|
+
while (lo <= hi) {
|
|
407
|
+
const mid = (lo + hi) >>> 1;
|
|
408
|
+
const v = flat[mid];
|
|
409
|
+
if (!sameType(v, lookupValue)) {
|
|
410
|
+
// Heterogeneous array — binary search preconditions violated.
|
|
411
|
+
exact = -1;
|
|
412
|
+
nextSmaller = -1;
|
|
413
|
+
nextLarger = -1;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
const cmp = doCompare(v, lookupValue);
|
|
417
|
+
if (cmp === 0) {
|
|
418
|
+
exact = mid;
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
// In descending order, the ordering is inverted: treat `cmp > 0` on
|
|
422
|
+
// the left half as "still greater than target" → search right.
|
|
423
|
+
const goLeft = ascending ? cmp > 0 : cmp < 0;
|
|
424
|
+
if (goLeft) {
|
|
425
|
+
// mid is larger (ascending) or smaller (descending) than target
|
|
426
|
+
if (ascending) {
|
|
427
|
+
nextLarger = nextLarger === -1 || mid < nextLarger ? mid : nextLarger;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
nextSmaller = nextSmaller === -1 || mid < nextSmaller ? mid : nextSmaller;
|
|
431
|
+
}
|
|
432
|
+
hi = mid - 1;
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
if (ascending) {
|
|
436
|
+
nextSmaller = nextSmaller === -1 || mid > nextSmaller ? mid : nextSmaller;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
nextLarger = nextLarger === -1 || mid > nextLarger ? mid : nextLarger;
|
|
440
|
+
}
|
|
441
|
+
lo = mid + 1;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (exact !== -1) {
|
|
445
|
+
foundIdx = exact;
|
|
446
|
+
}
|
|
447
|
+
else if (matchMode === -1) {
|
|
448
|
+
foundIdx = nextSmaller;
|
|
449
|
+
}
|
|
450
|
+
else if (matchMode === 1) {
|
|
451
|
+
foundIdx = nextLarger;
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
foundIdx = -1;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
else if (matchMode === 0) {
|
|
458
|
+
// Exact match
|
|
459
|
+
const start = searchMode === -1 ? flat.length - 1 : 0;
|
|
460
|
+
const end = searchMode === -1 ? -1 : flat.length;
|
|
461
|
+
const step = searchMode === -1 ? -1 : 1;
|
|
462
|
+
for (let i = start; i !== end; i += step) {
|
|
463
|
+
if (scalarEquals(flat[i], lookupValue)) {
|
|
464
|
+
foundIdx = i;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
if (scalarStringEquals(flat[i], lookupValue)) {
|
|
468
|
+
foundIdx = i;
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
else if (matchMode === -1) {
|
|
474
|
+
// Exact match or next smaller
|
|
475
|
+
let best = -1;
|
|
476
|
+
for (let i = 0; i < flat.length; i++) {
|
|
477
|
+
if (sameType(flat[i], lookupValue)) {
|
|
478
|
+
const cmp = doCompare(flat[i], lookupValue);
|
|
479
|
+
if (cmp === 0) {
|
|
480
|
+
best = i;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
if (cmp < 0 && (best === -1 || doCompare(flat[i], flat[best]) > 0)) {
|
|
484
|
+
best = i;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
foundIdx = best;
|
|
489
|
+
}
|
|
490
|
+
else if (matchMode === 1) {
|
|
491
|
+
// Exact match or next larger
|
|
492
|
+
let best = -1;
|
|
493
|
+
for (let i = 0; i < flat.length; i++) {
|
|
494
|
+
if (sameType(flat[i], lookupValue)) {
|
|
495
|
+
const cmp = doCompare(flat[i], lookupValue);
|
|
496
|
+
if (cmp === 0) {
|
|
497
|
+
best = i;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
if (cmp > 0 && (best === -1 || doCompare(flat[i], flat[best]) < 0)) {
|
|
501
|
+
best = i;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
foundIdx = best;
|
|
506
|
+
}
|
|
507
|
+
else if (matchMode === 2) {
|
|
508
|
+
// Wildcard match — uses the shared Excel wildcard converter so SEARCH,
|
|
509
|
+
// MATCH, XLOOKUP, and SUMIF/COUNTIF agree on `~*`, `~?`, `~~` escaping.
|
|
510
|
+
const lookupStr = toStringRV(lookupValue);
|
|
511
|
+
const pattern = excelWildcardToRegex(lookupStr);
|
|
512
|
+
try {
|
|
513
|
+
const re = new RegExp("^" + pattern + "$", "i");
|
|
514
|
+
for (let i = 0; i < flat.length; i++) {
|
|
515
|
+
if (re.test(toStringRV(flat[i]))) {
|
|
516
|
+
foundIdx = i;
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
for (let i = 0; i < flat.length; i++) {
|
|
523
|
+
if (toStringRV(flat[i]).toLowerCase() === lookupStr.toLowerCase()) {
|
|
524
|
+
foundIdx = i;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (foundIdx === -1) {
|
|
531
|
+
return ifNotFound !== null ? ifNotFound : ERRORS.NA;
|
|
532
|
+
}
|
|
533
|
+
// Return from return array
|
|
534
|
+
if (isRow) {
|
|
535
|
+
// Horizontal lookup: the lookup axis is the column axis of the return
|
|
536
|
+
// array, so `foundIdx` selects a column. A single-row return array
|
|
537
|
+
// yields a scalar; a multi-row return array yields a column vector.
|
|
538
|
+
if (foundIdx >= returnArr.width) {
|
|
539
|
+
return BLANK;
|
|
540
|
+
}
|
|
541
|
+
if (returnArr.height === 1) {
|
|
542
|
+
return getCell(returnArr, 0, foundIdx);
|
|
543
|
+
}
|
|
544
|
+
const col = [];
|
|
545
|
+
for (let r = 0; r < returnArr.height; r++) {
|
|
546
|
+
col.push([getCell(returnArr, r, foundIdx)]);
|
|
547
|
+
}
|
|
548
|
+
return rvArray(col);
|
|
549
|
+
}
|
|
550
|
+
// Vertical lookup: `foundIdx` selects a row; a single-column return
|
|
551
|
+
// array yields a scalar; a multi-column array yields a row vector.
|
|
552
|
+
if (foundIdx < returnArr.height) {
|
|
553
|
+
if (returnArr.width === 1) {
|
|
554
|
+
return getCell(returnArr, foundIdx, 0);
|
|
555
|
+
}
|
|
556
|
+
const row = [];
|
|
557
|
+
for (let c = 0; c < returnArr.width; c++) {
|
|
558
|
+
row.push(getCell(returnArr, foundIdx, c));
|
|
559
|
+
}
|
|
560
|
+
return rvArray([row]);
|
|
561
|
+
}
|
|
562
|
+
return BLANK;
|
|
563
|
+
}
|
|
564
|
+
export function fnXMATCH(args) {
|
|
565
|
+
const lookupValue = topLeft(args[0]);
|
|
566
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
567
|
+
return lookupValue;
|
|
568
|
+
}
|
|
569
|
+
if (!isArray(args[1])) {
|
|
570
|
+
return ERRORS.VALUE;
|
|
571
|
+
}
|
|
572
|
+
const lookupArr = args[1];
|
|
573
|
+
const matchModeV = args.length > 2 ? toNumberRV(args[2]) : rvNumber(0);
|
|
574
|
+
if (isError(matchModeV)) {
|
|
575
|
+
return matchModeV;
|
|
576
|
+
}
|
|
577
|
+
const matchMode = matchModeV.value;
|
|
578
|
+
const searchModeV = args.length > 3 ? toNumberRV(args[3]) : rvNumber(1);
|
|
579
|
+
if (isError(searchModeV)) {
|
|
580
|
+
return searchModeV;
|
|
581
|
+
}
|
|
582
|
+
const searchMode = searchModeV.value;
|
|
583
|
+
const flat = [];
|
|
584
|
+
if (lookupArr.height === 1) {
|
|
585
|
+
for (let c = 0; c < lookupArr.width; c++) {
|
|
586
|
+
flat.push(getCell(lookupArr, 0, c));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
for (let r = 0; r < lookupArr.height; r++) {
|
|
591
|
+
flat.push(getCell(lookupArr, r, 0));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (matchMode === 0) {
|
|
595
|
+
const start = searchMode === -1 ? flat.length - 1 : 0;
|
|
596
|
+
const end = searchMode === -1 ? -1 : flat.length;
|
|
597
|
+
const step = searchMode === -1 ? -1 : 1;
|
|
598
|
+
for (let i = start; i !== end; i += step) {
|
|
599
|
+
if (scalarEquals(flat[i], lookupValue)) {
|
|
600
|
+
return rvNumber(i + 1);
|
|
601
|
+
}
|
|
602
|
+
if (scalarStringEquals(flat[i], lookupValue)) {
|
|
603
|
+
return rvNumber(i + 1);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return ERRORS.NA;
|
|
607
|
+
}
|
|
608
|
+
if (matchMode === 2) {
|
|
609
|
+
// Wildcard matching — `*` and `?` (with `~` escape). Only meaningful
|
|
610
|
+
// when the lookup value is a string; for non-string lookup values
|
|
611
|
+
// Excel falls back to plain comparison.
|
|
612
|
+
if (lookupValue.kind !== RVKind.String) {
|
|
613
|
+
// Fall through to exact-match semantics for non-string lookups.
|
|
614
|
+
const start = searchMode === -1 ? flat.length - 1 : 0;
|
|
615
|
+
const end = searchMode === -1 ? -1 : flat.length;
|
|
616
|
+
const step = searchMode === -1 ? -1 : 1;
|
|
617
|
+
for (let i = start; i !== end; i += step) {
|
|
618
|
+
if (scalarEquals(flat[i], lookupValue)) {
|
|
619
|
+
return rvNumber(i + 1);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return ERRORS.NA;
|
|
623
|
+
}
|
|
624
|
+
const pattern = lookupValue.value;
|
|
625
|
+
const matcher = hasUnescapedWildcard(pattern)
|
|
626
|
+
? new RegExp(`^${excelWildcardToRegex(pattern)}$`, "iu")
|
|
627
|
+
: null;
|
|
628
|
+
const literal = matcher ? null : unescapeExcelWildcard(pattern).toLowerCase();
|
|
629
|
+
const start = searchMode === -1 ? flat.length - 1 : 0;
|
|
630
|
+
const end = searchMode === -1 ? -1 : flat.length;
|
|
631
|
+
const step = searchMode === -1 ? -1 : 1;
|
|
632
|
+
for (let i = start; i !== end; i += step) {
|
|
633
|
+
const cell = flat[i];
|
|
634
|
+
if (cell.kind !== RVKind.String) {
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (matcher) {
|
|
638
|
+
if (matcher.test(cell.value)) {
|
|
639
|
+
return rvNumber(i + 1);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
else if (cell.value.toLowerCase() === literal) {
|
|
643
|
+
return rvNumber(i + 1);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return ERRORS.NA;
|
|
647
|
+
}
|
|
648
|
+
if (matchMode === -1) {
|
|
649
|
+
// Next-smaller-or-equal: largest item <= lookupValue.
|
|
650
|
+
let best = -1;
|
|
651
|
+
for (let i = 0; i < flat.length; i++) {
|
|
652
|
+
const cmp = compareScalar(flat[i], lookupValue);
|
|
653
|
+
if (Number.isNaN(cmp)) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (cmp <= 0 && (best === -1 || compareScalar(flat[i], flat[best]) > 0)) {
|
|
657
|
+
best = i;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return best >= 0 ? rvNumber(best + 1) : ERRORS.NA;
|
|
661
|
+
}
|
|
662
|
+
if (matchMode === 1) {
|
|
663
|
+
// Next-larger-or-equal: smallest item >= lookupValue.
|
|
664
|
+
let best = -1;
|
|
665
|
+
for (let i = 0; i < flat.length; i++) {
|
|
666
|
+
const cmp = compareScalar(flat[i], lookupValue);
|
|
667
|
+
if (Number.isNaN(cmp)) {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (cmp >= 0 && (best === -1 || compareScalar(flat[i], flat[best]) < 0)) {
|
|
671
|
+
best = i;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return best >= 0 ? rvNumber(best + 1) : ERRORS.NA;
|
|
675
|
+
}
|
|
676
|
+
return ERRORS.NA;
|
|
677
|
+
}
|
|
678
|
+
export function fnADDRESS(args) {
|
|
679
|
+
const rowNumV = toNumberRV(args[0]);
|
|
680
|
+
if (isError(rowNumV)) {
|
|
681
|
+
return rowNumV;
|
|
682
|
+
}
|
|
683
|
+
const rowNum = Math.trunc(rowNumV.value);
|
|
684
|
+
const colNumV = toNumberRV(args[1]);
|
|
685
|
+
if (isError(colNumV)) {
|
|
686
|
+
return colNumV;
|
|
687
|
+
}
|
|
688
|
+
const colNum = Math.trunc(colNumV.value);
|
|
689
|
+
// ADDRESS rejects non-positive row/col with #VALUE!. Without this guard
|
|
690
|
+
// ADDRESS(0, 1) would silently return "$A$0" and ADDRESS(1, 0) would
|
|
691
|
+
// produce "$$1" (no column letter) — neither is a legal cell reference.
|
|
692
|
+
if (!Number.isFinite(rowNum) || !Number.isFinite(colNum) || rowNum < 1 || colNum < 1) {
|
|
693
|
+
return ERRORS.VALUE;
|
|
694
|
+
}
|
|
695
|
+
const absNumV = args.length > 2 ? toNumberRV(args[2]) : rvNumber(1);
|
|
696
|
+
if (isError(absNumV)) {
|
|
697
|
+
return absNumV;
|
|
698
|
+
}
|
|
699
|
+
const absNum = Math.trunc(absNumV.value);
|
|
700
|
+
// Excel only accepts abs_num ∈ {1, 2, 3, 4}; anything else is #VALUE!.
|
|
701
|
+
if (absNum < 1 || absNum > 4) {
|
|
702
|
+
return ERRORS.VALUE;
|
|
703
|
+
}
|
|
704
|
+
// a1 style (true/default) vs r1c1 (false)
|
|
705
|
+
const a1Arg = args.length > 3 ? topLeft(args[3]) : { kind: RVKind.Boolean, value: true };
|
|
706
|
+
const a1 = a1Arg.kind === RVKind.Boolean ? a1Arg.value : true;
|
|
707
|
+
const sheetText = args.length > 4 ? toStringRV(args[4]) : "";
|
|
708
|
+
if (!a1) {
|
|
709
|
+
// R1C1 style
|
|
710
|
+
const rPart = absNum === 1 || absNum === 2 ? `R${rowNum}` : `R[${rowNum}]`;
|
|
711
|
+
const cPart = absNum === 1 || absNum === 3 ? `C${colNum}` : `C[${colNum}]`;
|
|
712
|
+
const prefix = sheetText ? `${renderSheetPrefix(sheetText)}!` : "";
|
|
713
|
+
return rvString(prefix + rPart + cPart);
|
|
714
|
+
}
|
|
715
|
+
// Convert column number to letters
|
|
716
|
+
let col = "";
|
|
717
|
+
let cv = colNum;
|
|
718
|
+
while (cv > 0) {
|
|
719
|
+
cv--;
|
|
720
|
+
col = String.fromCharCode(65 + (cv % 26)) + col;
|
|
721
|
+
cv = Math.floor(cv / 26);
|
|
722
|
+
}
|
|
723
|
+
let result;
|
|
724
|
+
switch (absNum) {
|
|
725
|
+
case 1:
|
|
726
|
+
result = "$" + col + "$" + rowNum;
|
|
727
|
+
break;
|
|
728
|
+
case 2:
|
|
729
|
+
result = col + "$" + rowNum;
|
|
730
|
+
break;
|
|
731
|
+
case 3:
|
|
732
|
+
result = "$" + col + rowNum;
|
|
733
|
+
break;
|
|
734
|
+
case 4:
|
|
735
|
+
result = col + rowNum;
|
|
736
|
+
break;
|
|
737
|
+
default:
|
|
738
|
+
// Unreachable — `absNum` is already validated to {1, 2, 3, 4} above.
|
|
739
|
+
result = "$" + col + "$" + rowNum;
|
|
740
|
+
}
|
|
741
|
+
if (sheetText) {
|
|
742
|
+
result = renderSheetPrefix(sheetText) + "!" + result;
|
|
743
|
+
}
|
|
744
|
+
return rvString(result);
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Quote a sheet name for use in a reference prefix the way Excel does:
|
|
748
|
+
* - plain `Name` (letters, digits, underscore, leading non-digit) → as-is
|
|
749
|
+
* - anything else → wrapped in single quotes with embedded `'` doubled
|
|
750
|
+
*/
|
|
751
|
+
function renderSheetPrefix(name) {
|
|
752
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
753
|
+
return name;
|
|
754
|
+
}
|
|
755
|
+
return `'${name.replace(/'/g, "''")}'`;
|
|
756
|
+
}
|
|
757
|
+
export function fnLOOKUP(args) {
|
|
758
|
+
const lookupValue = topLeft(args[0]);
|
|
759
|
+
if (lookupValue.kind === RVKind.Error) {
|
|
760
|
+
return lookupValue;
|
|
761
|
+
}
|
|
762
|
+
if (!isArray(args[1])) {
|
|
763
|
+
return ERRORS.NA;
|
|
764
|
+
}
|
|
765
|
+
const lookupArr = args[1];
|
|
766
|
+
if (args.length > 2 && isArray(args[2])) {
|
|
767
|
+
const resultArr = args[2];
|
|
768
|
+
const flat = [];
|
|
769
|
+
const isRow = lookupArr.height === 1;
|
|
770
|
+
if (isRow) {
|
|
771
|
+
for (let c = 0; c < lookupArr.width; c++) {
|
|
772
|
+
flat.push(getCell(lookupArr, 0, c));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
for (let r = 0; r < lookupArr.height; r++) {
|
|
777
|
+
flat.push(getCell(lookupArr, r, 0));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
let bestIdx = -1;
|
|
781
|
+
for (let i = 0; i < flat.length; i++) {
|
|
782
|
+
const v = flat[i];
|
|
783
|
+
if (sameType(v, lookupValue)) {
|
|
784
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue) && v.value <= lookupValue.value) {
|
|
785
|
+
bestIdx = i;
|
|
786
|
+
}
|
|
787
|
+
else if (scalarIsString(v) &&
|
|
788
|
+
scalarIsString(lookupValue) &&
|
|
789
|
+
v.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
790
|
+
bestIdx = i;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (bestIdx === -1) {
|
|
795
|
+
return ERRORS.NA;
|
|
796
|
+
}
|
|
797
|
+
if (isRow) {
|
|
798
|
+
return resultArr.height === 1
|
|
799
|
+
? bestIdx < resultArr.width
|
|
800
|
+
? getCell(resultArr, 0, bestIdx)
|
|
801
|
+
: BLANK
|
|
802
|
+
: bestIdx < resultArr.height
|
|
803
|
+
? getCell(resultArr, bestIdx, 0)
|
|
804
|
+
: BLANK;
|
|
805
|
+
}
|
|
806
|
+
return bestIdx < resultArr.height ? getCell(resultArr, bestIdx, 0) : BLANK;
|
|
807
|
+
}
|
|
808
|
+
const rows = lookupArr.height;
|
|
809
|
+
const cols = lookupArr.width;
|
|
810
|
+
if (cols === 0) {
|
|
811
|
+
return ERRORS.NA;
|
|
812
|
+
}
|
|
813
|
+
if (cols >= rows) {
|
|
814
|
+
let bestIdx = -1;
|
|
815
|
+
for (let c = 0; c < cols; c++) {
|
|
816
|
+
const v = getCell(lookupArr, 0, c);
|
|
817
|
+
if (sameType(v, lookupValue)) {
|
|
818
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue) && v.value <= lookupValue.value) {
|
|
819
|
+
bestIdx = c;
|
|
820
|
+
}
|
|
821
|
+
else if (scalarIsString(v) &&
|
|
822
|
+
scalarIsString(lookupValue) &&
|
|
823
|
+
v.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
824
|
+
bestIdx = c;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return bestIdx >= 0 ? getCell(lookupArr, rows - 1, bestIdx) : ERRORS.NA;
|
|
829
|
+
}
|
|
830
|
+
let bestIdx = -1;
|
|
831
|
+
for (let r = 0; r < rows; r++) {
|
|
832
|
+
const v = getCell(lookupArr, r, 0);
|
|
833
|
+
if (sameType(v, lookupValue)) {
|
|
834
|
+
if (scalarIsNumber(v) && scalarIsNumber(lookupValue) && v.value <= lookupValue.value) {
|
|
835
|
+
bestIdx = r;
|
|
836
|
+
}
|
|
837
|
+
else if (scalarIsString(v) &&
|
|
838
|
+
scalarIsString(lookupValue) &&
|
|
839
|
+
v.value.toLowerCase() <= lookupValue.value.toLowerCase()) {
|
|
840
|
+
bestIdx = r;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return bestIdx >= 0 ? getCell(lookupArr, bestIdx, cols - 1) : ERRORS.NA;
|
|
845
|
+
}
|
|
846
|
+
export function fnTRANSPOSE(args) {
|
|
847
|
+
if (!isArray(args[0])) {
|
|
848
|
+
const sv = topLeft(args[0]);
|
|
849
|
+
// Excel propagates errors through TRANSPOSE rather than burying them
|
|
850
|
+
// inside a 1×1 array — callers that then aggregate the result (e.g.
|
|
851
|
+
// `SUM(TRANSPOSE(#N/A))`) expect the error to surface. R8-P1 fix.
|
|
852
|
+
if (sv.kind === RVKind.Error) {
|
|
853
|
+
return sv;
|
|
854
|
+
}
|
|
855
|
+
return rvArray([[sv]]);
|
|
856
|
+
}
|
|
857
|
+
const arr = args[0];
|
|
858
|
+
const rows = arr.height;
|
|
859
|
+
const cols = arr.width;
|
|
860
|
+
const result = [];
|
|
861
|
+
for (let c = 0; c < cols; c++) {
|
|
862
|
+
const row = [];
|
|
863
|
+
for (let r = 0; r < rows; r++) {
|
|
864
|
+
row.push(getCell(arr, r, c));
|
|
865
|
+
}
|
|
866
|
+
result.push(row);
|
|
867
|
+
}
|
|
868
|
+
return rvArray(result);
|
|
869
|
+
}
|
|
870
|
+
export function fnAREAS(args) {
|
|
871
|
+
if (args.length === 0) {
|
|
872
|
+
return ERRORS.VALUE;
|
|
873
|
+
}
|
|
874
|
+
// Error in the argument propagates (Excel parity). A reference value
|
|
875
|
+
// counts as one area; the engine does not yet build multi-area
|
|
876
|
+
// references via `(A1, B1)` union syntax — when that lands, the arity
|
|
877
|
+
// should be `v.areas.length`, not a hardcoded 1.
|
|
878
|
+
const a = args[0];
|
|
879
|
+
if (a.kind === RVKind.Error) {
|
|
880
|
+
return a;
|
|
881
|
+
}
|
|
882
|
+
if (a.kind === RVKind.Reference) {
|
|
883
|
+
return rvNumber(a.areas.length);
|
|
884
|
+
}
|
|
885
|
+
return rvNumber(1);
|
|
886
|
+
}
|