@a3s-lab/office 0.2.2 → 0.4.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/COLLABORATION_ROADMAP.md +426 -0
- package/README.md +649 -10
- package/dist/0~1403.js +1 -1
- package/dist/0~2805.js +1279 -0
- package/dist/{0~use-office-draft.js → 0~345.js} +30 -1
- package/dist/0~3539.js +1 -1
- package/dist/0~3557.js +1 -1
- package/dist/0~3635.js +1 -1
- package/dist/0~4595.js +123 -12
- package/dist/0~4980.js +154 -0
- package/dist/0~5050.js +124 -113
- package/dist/{0~9512.js → 0~6090.js} +540 -3
- package/dist/0~7048.js +92 -66
- package/dist/0~7614.js +2 -1
- package/dist/0~7754.js +5653 -0
- package/dist/0~9445.js +1 -1
- package/dist/0~document-editor.js +4992 -3323
- package/dist/0~markdown-editor.js +250 -44
- package/dist/0~pdf-viewer.js +1016 -24
- package/dist/0~presentation-editor.js +179 -8
- package/dist/0~spreadsheet-editor.js +3371 -578
- package/dist/0~work-docx-export.js +6801 -658
- package/dist/0~work-docx-import.js +1388 -795
- package/dist/0~work-office-diagnostics.js +524 -30
- package/dist/0~work-pptx-export.js +2 -1
- package/dist/0~work-pptx-import.js +1 -2
- package/dist/0~work-presentation-charts.js +1 -1
- package/dist/2591.js +197 -0
- package/dist/3818.js +1090 -0
- package/dist/4104.js +970 -60
- package/dist/432.js +171 -14
- package/dist/4476.js +2008 -0
- package/dist/4560.js +674 -0
- package/dist/4650.js +70 -0
- package/dist/{1134.js → 5184.js} +183 -1
- package/dist/6489.js +18 -18
- package/dist/7060.js +132 -0
- package/dist/8715.js +2 -136
- package/dist/8928.js +16778 -0
- package/dist/9787.js +209 -0
- package/dist/core.d.ts +29 -9
- package/dist/core.js +10 -2
- package/dist/index.js +13 -3
- package/dist/internal/collaboration/office-collaboration-flat-json.d.ts +13 -0
- package/dist/internal/collaboration/office-collaboration-json.d.ts +4 -0
- package/dist/internal/collaboration/office-collaboration-presence.d.ts +86 -0
- package/dist/internal/collaboration/office-collaboration-transport.d.ts +42 -0
- package/dist/internal/collaboration/office-collaboration.d.ts +85 -0
- package/dist/internal/collaboration/office-document-collaboration-bibliography.d.ts +7 -0
- package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +9 -0
- package/dist/internal/collaboration/office-document-collaboration-comments.d.ts +7 -0
- package/dist/internal/collaboration/office-document-collaboration-sidecar-utils.d.ts +27 -0
- package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +16 -0
- package/dist/internal/collaboration/office-document-collaboration.d.ts +45 -0
- package/dist/internal/collaboration/office-markdown-collaboration.d.ts +31 -0
- package/dist/internal/collaboration/office-pdf-collaboration-claims.d.ts +4 -0
- package/dist/internal/collaboration/office-pdf-collaboration-conflicts.d.ts +2 -0
- package/dist/internal/collaboration/office-pdf-collaboration-model.d.ts +26 -0
- package/dist/internal/collaboration/office-pdf-collaboration-records.d.ts +21 -0
- package/dist/internal/collaboration/office-pdf-collaboration-types.d.ts +91 -0
- package/dist/internal/collaboration/office-pdf-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-pdf-collaboration.d.ts +35 -0
- package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +25 -0
- package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +22 -0
- package/dist/internal/collaboration/office-presentation-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-presentation-collaboration.d.ts +32 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-cells.d.ts +13 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-claims.d.ts +4 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-conflicts.d.ts +2 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-flat-json.d.ts +10 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-model.d.ts +31 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +17 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration.d.ts +32 -0
- package/dist/internal/design-system/primitives/navigation/tabs.d.ts +3 -1
- package/dist/internal/features/work/components/work-document-page-border.d.ts +6 -0
- package/dist/internal/features/work/editors/document-command-catalog.d.ts +536 -0
- package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -1
- package/dist/internal/features/work/editors/document-editor-support.d.ts +7 -3
- package/dist/internal/features/work/editors/document-editor.d.ts +8 -2
- package/dist/internal/features/work/editors/document-external-content.d.ts +14 -0
- package/dist/internal/features/work/editors/document-format-clipboard.d.ts +6 -0
- package/dist/internal/features/work/editors/document-format-tools.d.ts +4 -0
- package/dist/internal/features/work/editors/document-layout-panel.d.ts +4 -1
- package/dist/internal/features/work/editors/document-page-chrome-wps-shortcuts.d.ts +2 -0
- package/dist/internal/features/work/editors/document-page-layout-ribbon.d.ts +14 -0
- package/dist/internal/features/work/editors/document-page-stack.d.ts +10 -1
- package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +2 -0
- package/dist/internal/features/work/editors/document-pagination-run-coordinator.d.ts +26 -0
- package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +20 -1
- package/dist/internal/features/work/editors/document-review-navigation.d.ts +9 -0
- package/dist/internal/features/work/editors/document-statistics-dialog.d.ts +7 -0
- package/dist/internal/features/work/editors/document-status-bar.d.ts +2 -1
- package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +4 -1
- package/dist/internal/features/work/editors/document-toolbar.d.ts +19 -42
- package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +12 -0
- package/dist/internal/features/work/editors/document-zoom.d.ts +17 -0
- package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +1 -1
- package/dist/internal/features/work/editors/markdown-editor.d.ts +5 -1
- package/dist/internal/features/work/editors/markdown-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/office-collaboration-participants.d.ts +3 -0
- package/dist/internal/features/work/editors/office-collaboration-presence-context.d.ts +17 -0
- package/dist/internal/features/work/editors/office-collaboration-presence-ui.d.ts +25 -0
- package/dist/internal/features/work/editors/office-editor-focus-handoff.d.ts +27 -0
- package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +5 -0
- package/dist/internal/features/work/editors/pdf-collaboration-presence.d.ts +3 -0
- package/dist/internal/features/work/editors/pdf-collaboration-projection.d.ts +10 -0
- package/dist/internal/features/work/editors/pdf-evidence-contract.d.ts +36 -0
- package/dist/internal/features/work/editors/pdf-evidence-overlay.d.ts +37 -0
- package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +7 -1
- package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -1
- package/dist/internal/features/work/editors/presentation-collaboration-presence.d.ts +5 -0
- package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +3 -2
- package/dist/internal/features/work/editors/presentation-editor-types.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-editor.d.ts +1 -1
- package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-clear.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-merge.d.ts +12 -0
- package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +3 -7
- package/dist/internal/features/work/editors/spreadsheet-collaboration-presence.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +464 -0
- package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +45 -9
- package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +2 -0
- package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +9 -28
- package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +8 -2
- package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +27 -0
- package/dist/internal/features/work/editors/spreadsheet-freeze-panes.d.ts +14 -0
- package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -2
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -1
- package/dist/internal/features/work/editors/use-document-review-conflicts.d.ts +28 -0
- package/dist/internal/features/work/editors/use-spreadsheet-auto-filter.d.ts +21 -0
- package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +27 -0
- package/dist/internal/features/work/editors/use-spreadsheet-format-painter.d.ts +18 -0
- package/dist/internal/features/work/editors/work-office-chrome.d.ts +22 -3
- package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +2 -0
- package/dist/internal/features/work/work-document-bookmark-references.d.ts +37 -0
- package/dist/internal/features/work/work-document-bookmarks.d.ts +29 -0
- package/dist/internal/features/work/work-document-caption-nodes.d.ts +4 -1
- package/dist/internal/features/work/work-document-comments.d.ts +1 -1
- package/dist/internal/features/work/work-document-equations.d.ts +513 -0
- package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
- package/dist/internal/features/work/work-document-field-identity.d.ts +2 -0
- package/dist/internal/features/work/work-document-field-node.d.ts +2 -2
- package/dist/internal/features/work/work-document-fields.d.ts +7 -0
- package/dist/internal/features/work/work-document-image-identity.d.ts +30 -0
- package/dist/internal/features/work/work-document-image-layout.d.ts +46 -1
- package/dist/internal/features/work/work-document-image-wrap-contour.d.ts +16 -0
- package/dist/internal/features/work/work-document-incremental-layout.d.ts +2 -2
- package/dist/internal/features/work/work-document-model-codec.d.ts +4 -1
- package/dist/internal/features/work/work-document-note-integrity.d.ts +2 -0
- package/dist/internal/features/work/work-document-page-borders.d.ts +25 -0
- package/dist/internal/features/work/work-document-page-capture.d.ts +8 -0
- package/dist/internal/features/work/work-document-page-frames.d.ts +15 -0
- package/dist/internal/features/work/work-document-page-margins.d.ts +56 -0
- package/dist/internal/features/work/work-document-page-size.d.ts +47 -0
- package/dist/internal/features/work/work-document-pagination-measurement.d.ts +1 -2
- package/dist/internal/features/work/work-document-pagination-types.d.ts +7 -2
- package/dist/internal/features/work/work-document-pagination.d.ts +2 -2
- package/dist/internal/features/work/work-document-paragraph-borders.d.ts +32 -0
- package/dist/internal/features/work/work-document-paragraph-identity.d.ts +26 -0
- package/dist/internal/features/work/work-document-paragraph-shading.d.ts +18 -0
- package/dist/internal/features/work/work-document-review-conflicts.d.ts +35 -0
- package/dist/internal/features/work/work-document-section.d.ts +4 -0
- package/dist/internal/features/work/work-document-snapshot.d.ts +19 -0
- package/dist/internal/features/work/work-document-source.d.ts +23 -0
- package/dist/internal/features/work/work-document-table-column-widths.d.ts +7 -0
- package/dist/internal/features/work/work-document-table-property-changes.d.ts +3 -0
- package/dist/internal/features/work/work-document-table-row-identity.d.ts +15 -0
- package/dist/internal/features/work/work-document-table-sizing.d.ts +3 -0
- package/dist/internal/features/work/work-document-text-layout.d.ts +3 -1
- package/dist/internal/features/work/work-document-word-line-metrics.d.ts +2 -0
- package/dist/internal/features/work/work-docx-bookmark-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-bookmark-import.d.ts +24 -0
- package/dist/internal/features/work/work-docx-bookmarks.d.ts +17 -0
- package/dist/internal/features/work/work-docx-comment-durable-id-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-comment-export.d.ts +3 -0
- package/dist/internal/features/work/work-docx-comment-metadata.d.ts +2 -0
- package/dist/internal/features/work/work-docx-drawing-extension-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-equation-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-equation-export.d.ts +14 -0
- package/dist/internal/features/work/work-docx-equation-import.d.ts +30 -0
- package/dist/internal/features/work/work-docx-equation-story.d.ts +9 -0
- package/dist/internal/features/work/work-docx-export-formatting.d.ts +3 -1
- package/dist/internal/features/work/work-docx-export-image.d.ts +5 -1
- package/dist/internal/features/work/work-docx-export.d.ts +1 -1
- package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
- package/dist/internal/features/work/work-docx-font-table-preservation.d.ts +3 -0
- package/dist/internal/features/work/work-docx-ignorable-extension-preservation.d.ts +15 -0
- package/dist/internal/features/work/work-docx-image-crop.d.ts +12 -0
- package/dist/internal/features/work/work-docx-image-identity.d.ts +17 -0
- package/dist/internal/features/work/work-docx-image-layer.d.ts +12 -0
- package/dist/internal/features/work/work-docx-image-layout-import.d.ts +14 -2
- package/dist/internal/features/work/work-docx-image-wrap.d.ts +15 -0
- package/dist/internal/features/work/work-docx-import.d.ts +13 -3
- package/dist/internal/features/work/work-docx-list-export.d.ts +18 -0
- package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-block-preservation.d.ts +18 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-model.d.ts +47 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-properties.d.ts +17 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-table-model.d.ts +17 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-xml.d.ts +14 -0
- package/dist/internal/features/work/work-docx-note-comment-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-content.d.ts +40 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-preservation.d.ts +5 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-relationships.d.ts +35 -0
- package/dist/internal/features/work/work-docx-note-comment-identity.d.ts +15 -0
- package/dist/internal/features/work/work-docx-note-comment-run-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-note-comment-run-properties.d.ts +2 -0
- package/dist/internal/features/work/work-docx-note-image-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-note-image-relationships.d.ts +1 -0
- package/dist/internal/features/work/work-docx-numbering-extension-preservation.d.ts +6 -0
- package/dist/internal/features/work/work-docx-numbering.d.ts +1 -0
- package/dist/internal/features/work/work-docx-page-borders-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-borders-import.d.ts +10 -0
- package/dist/internal/features/work/work-docx-page-margins-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-margins-import.d.ts +18 -0
- package/dist/internal/features/work/work-docx-page-size-diagnostics.d.ts +2 -0
- package/dist/internal/features/work/work-docx-page-size-import.d.ts +17 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-export.d.ts +18 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +24 -0
- package/dist/internal/features/work/work-docx-paragraph-default-collapsed.d.ts +19 -0
- package/dist/internal/features/work/work-docx-paragraph-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-paragraph-identity-import.d.ts +10 -0
- package/dist/internal/features/work/work-docx-paragraph-identity.d.ts +18 -0
- package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +6 -1
- package/dist/internal/features/work/work-docx-paragraph-shading-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-paragraph-shading-export.d.ts +5 -0
- package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +18 -0
- package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +3 -0
- package/dist/internal/features/work/work-docx-settings-preservation.d.ts +10 -0
- package/dist/internal/features/work/work-docx-settings-xml.d.ts +16 -0
- package/dist/internal/features/work/work-docx-style-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-cell-export.d.ts +2 -1
- package/dist/internal/features/work/work-docx-table-cell-import.d.ts +3 -0
- package/dist/internal/features/work/work-docx-table-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-row-import.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-theme-reference.d.ts +23 -0
- package/dist/internal/features/work/work-docx-theme.d.ts +4 -1
- package/dist/internal/features/work/work-docx-twips.d.ts +12 -0
- package/dist/internal/features/work/work-file-contract.d.ts +1 -0
- package/dist/internal/features/work/work-file-io.d.ts +1 -1
- package/dist/internal/features/work/work-markdown-extensions.d.ts +4 -1
- package/dist/internal/features/work/work-ooxml-package-preservation.d.ts +12 -0
- package/dist/internal/features/work/work-ooxml-package-security.d.ts +6 -0
- package/dist/internal/features/work/work-ooxml-relationship-preservation.d.ts +10 -0
- package/dist/internal/features/work/work-ooxml-xml.d.ts +2 -0
- package/dist/internal/features/work/work-pdf-export.d.ts +10 -0
- package/dist/internal/features/work/work-repository.d.ts +2 -0
- package/dist/internal/features/work/work-types.d.ts +15 -2
- package/dist/internal/kernel/office-kernel-client-types.d.ts +1 -0
- package/dist/internal/kernel/office-kernel-protocol.d.ts +7 -0
- package/dist/internal/kernel/office-kernel-version.d.ts +1 -1
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +93 -41
- package/dist/react.d.ts +18 -12
- package/dist/react.js +1 -1
- package/dist/styles.css +1367 -16
- package/dist/vue.d.ts +48 -5
- package/dist/vue.js +42 -3
- package/dist/web-component.d.ts +24 -2
- package/dist/web-component.js +101 -2
- package/docs/latest/en/browser-editor-architecture.md +262 -47
- package/package.json +29 -4
- package/dist/0~152.js +0 -94
- package/dist/0~2582.js +0 -485
- package/dist/0~4750.js +0 -939
- package/dist/0~5861.js +0 -596
- package/dist/0~work-document-extensions.js +0 -3045
- package/dist/0~work-document-model-codec.js +0 -42
- package/dist/0~work-docx-bibliography.js +0 -347
- package/dist/0~work-docx-page-color.js +0 -42
- package/dist/2881.js +0 -1455
- package/dist/8728.js +0 -723
package/dist/4476.js
ADDED
|
@@ -0,0 +1,2008 @@
|
|
|
1
|
+
import { initializeWorkOfficeCollaborationMetadata, assertWorkOfficeCollaborationEditable, OfficeCollaborationError as WorkOfficeCollaborationError, readOfficeCollaborationMetadata as readWorkOfficeCollaborationMetadata, registerWorkOfficeCollaborationInitializer, markWorkOfficeCollaborationInitialized, assertWorkOfficeCollaborationOrigin } from "./9787.js";
|
|
2
|
+
import { OFFICE_KERNEL_SPREADSHEET_MAX_ROWS } from "./5184.js";
|
|
3
|
+
import { workOfficeCollaborationJsonEqual, canonicalWorkOfficeCollaborationJson, isWorkOfficeCollaborationRecord, cloneWorkOfficeCollaborationJson } from "./4650.js";
|
|
4
|
+
import { patchWorkOfficeCollaborationFlatJsonMap, readWorkOfficeCollaborationFlatJsonMap } from "./7060.js";
|
|
5
|
+
import { formatSpreadsheetCellRanges, parseSpreadsheetCellRanges } from "./8715.js";
|
|
6
|
+
import * as __rspack_external_yjs from "yjs";
|
|
7
|
+
const MAX_POPULATED_CELLS = 1000000;
|
|
8
|
+
const MAX_DENSE_MATRIX_CELLS = 1000000;
|
|
9
|
+
const TRANSIENT_SHEET_FIELDS = [
|
|
10
|
+
'calcChain',
|
|
11
|
+
'dynamicArray_compute',
|
|
12
|
+
'luckysheet_select_save',
|
|
13
|
+
'luckysheet_selection_range',
|
|
14
|
+
'status',
|
|
15
|
+
'zoomRatio'
|
|
16
|
+
];
|
|
17
|
+
function validateWorkOfficeSpreadsheetContent(content) {
|
|
18
|
+
if (!content || 'spreadsheet' !== content.type) invalidWorkOfficeSpreadsheetInput('a Spreadsheet content value');
|
|
19
|
+
if (!Array.isArray(content.sheets)) invalidWorkOfficeSpreadsheetInput('an array of sheets');
|
|
20
|
+
const sheets = validateSheets(content.sheets);
|
|
21
|
+
const result = {
|
|
22
|
+
type: 'spreadsheet',
|
|
23
|
+
sheets
|
|
24
|
+
};
|
|
25
|
+
copyOptionalJsonField(content, result, 'calculation', 'calculation settings');
|
|
26
|
+
if (void 0 !== content.namedRanges) result.namedRanges = validateIdRecords(content.namedRanges, 'named range');
|
|
27
|
+
copyOptionalSheetSidecars(content, result, 'printAreas', 'print area');
|
|
28
|
+
copyOptionalSheetSidecars(content, result, 'printTitles', 'print titles');
|
|
29
|
+
copyOptionalSheetSidecars(content, result, 'pageBreaks', 'page breaks');
|
|
30
|
+
copyOptionalSheetSidecars(content, result, 'pageSetups', 'page setup');
|
|
31
|
+
assertSpreadsheetReferences(result);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function validateSharedWorkOfficeSpreadsheetContent(content) {
|
|
35
|
+
try {
|
|
36
|
+
return validateWorkOfficeSpreadsheetContent(content);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error instanceof WorkOfficeCollaborationError) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The shared Spreadsheet collaboration content is invalid: ${error.message}`);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function invalidWorkOfficeSpreadsheetShared(label) {
|
|
43
|
+
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The shared Spreadsheet collaboration ${label} is invalid.`);
|
|
44
|
+
}
|
|
45
|
+
function validateSheets(value) {
|
|
46
|
+
const ids = new Set();
|
|
47
|
+
const names = new Set();
|
|
48
|
+
return value.map((candidate)=>{
|
|
49
|
+
const sheet = validateSheet(candidate);
|
|
50
|
+
const id = sheet.id;
|
|
51
|
+
if (ids.has(id)) invalidWorkOfficeSpreadsheetInput(`a unique sheet ID; '${id}' is repeated`);
|
|
52
|
+
ids.add(id);
|
|
53
|
+
const normalizedName = sheet.name.toLocaleLowerCase();
|
|
54
|
+
if (names.has(normalizedName)) invalidWorkOfficeSpreadsheetInput(`a unique sheet name; '${sheet.name}' is repeated`);
|
|
55
|
+
names.add(normalizedName);
|
|
56
|
+
return sheet;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function validateSheet(value) {
|
|
60
|
+
const source = requiredInputRecord(value, 'sheet');
|
|
61
|
+
const result = validateJsonRecord(source, 'sheet');
|
|
62
|
+
result.id = requiredIdentifier(source.id, 'sheet');
|
|
63
|
+
result.name = requiredNonEmptyString(source.name, 'sheet name');
|
|
64
|
+
for (const field of TRANSIENT_SHEET_FIELDS)delete result[field];
|
|
65
|
+
if (void 0 !== source.data) {
|
|
66
|
+
result.data = validateCellMatrix(source.data, result.id);
|
|
67
|
+
delete result.celldata;
|
|
68
|
+
} else if (void 0 !== source.celldata) {
|
|
69
|
+
result.celldata = validateSparseCells(source.celldata, result.id);
|
|
70
|
+
delete result.data;
|
|
71
|
+
}
|
|
72
|
+
validateOptionalDimension(result, 'row', OFFICE_KERNEL_SPREADSHEET_MAX_ROWS);
|
|
73
|
+
validateOptionalDimension(result, 'column', 16384);
|
|
74
|
+
if (void 0 !== source.images) result.images = validateIdRecords(source.images, `image in sheet '${result.id}'`).filter(({ id })=>!id.startsWith('work-chart-preview-'));
|
|
75
|
+
if (void 0 !== source.charts) result.charts = validateIdRecords(source.charts, `chart in sheet '${result.id}'`);
|
|
76
|
+
if (void 0 !== source.pivotTables) result.pivotTables = validateIdRecords(source.pivotTables, `pivot table in sheet '${result.id}'`);
|
|
77
|
+
if (void 0 !== source.formulaMetadata) result.formulaMetadata = validateJsonRecord(requiredInputRecord(source.formulaMetadata, 'formula metadata'), `formula metadata in sheet '${result.id}'`);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
function validateCellMatrix(value, sheetId) {
|
|
81
|
+
if (!Array.isArray(value) || value.length > OFFICE_KERNEL_SPREADSHEET_MAX_ROWS) invalidWorkOfficeSpreadsheetInput(`a cell matrix within the Spreadsheet row limit in sheet '${sheetId}'`);
|
|
82
|
+
let populated = 0;
|
|
83
|
+
let materialized = 0;
|
|
84
|
+
return value.map((candidate, row)=>{
|
|
85
|
+
if (!Array.isArray(candidate) || candidate.length > 16384) invalidWorkOfficeSpreadsheetInput(`a cell row within the Spreadsheet column limit at row ${row} in sheet '${sheetId}'`);
|
|
86
|
+
materialized += candidate.length;
|
|
87
|
+
if (materialized > MAX_DENSE_MATRIX_CELLS) invalidWorkOfficeSpreadsheetInput(`at most ${MAX_DENSE_MATRIX_CELLS.toLocaleString()} materialized dense cells in sheet '${sheetId}'; use celldata for larger sparse sheets`);
|
|
88
|
+
return candidate.map((cell, column)=>{
|
|
89
|
+
if (null == cell) return null;
|
|
90
|
+
populated += 1;
|
|
91
|
+
assertPopulatedCellLimit(populated, sheetId);
|
|
92
|
+
return validateCell(cell, row, column, sheetId);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function validateSparseCells(value, sheetId) {
|
|
97
|
+
if (!Array.isArray(value)) invalidWorkOfficeSpreadsheetInput(`an array of sparse cells in sheet '${sheetId}'`);
|
|
98
|
+
if (value.length > MAX_POPULATED_CELLS) assertPopulatedCellLimit(value.length, sheetId);
|
|
99
|
+
const coordinates = new Set();
|
|
100
|
+
const result = value.map((candidate)=>{
|
|
101
|
+
const record = requiredInputRecord(candidate, 'sparse cell');
|
|
102
|
+
const row = requiredCoordinate(record.r, OFFICE_KERNEL_SPREADSHEET_MAX_ROWS, 'row', sheetId);
|
|
103
|
+
const column = requiredCoordinate(record.c, 16384, 'column', sheetId);
|
|
104
|
+
const coordinate = `${row}:${column}`;
|
|
105
|
+
if (coordinates.has(coordinate)) invalidWorkOfficeSpreadsheetInput(`a unique sparse cell coordinate; '${coordinate}' is repeated in sheet '${sheetId}'`);
|
|
106
|
+
coordinates.add(coordinate);
|
|
107
|
+
return {
|
|
108
|
+
r: row,
|
|
109
|
+
c: column,
|
|
110
|
+
v: null === record.v || void 0 === record.v ? null : validateCell(record.v, row, column, sheetId)
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return result.sort((left, right)=>left.r - right.r || left.c - right.c);
|
|
114
|
+
}
|
|
115
|
+
function validateCell(value, row, column, sheetId) {
|
|
116
|
+
const record = requiredInputRecord(value, 'cell');
|
|
117
|
+
return validateJsonRecord(record, `cell ${row}:${column} in sheet '${sheetId}'`);
|
|
118
|
+
}
|
|
119
|
+
function validateIdRecords(value, label) {
|
|
120
|
+
if (!Array.isArray(value)) invalidWorkOfficeSpreadsheetInput(`an array of ${label}s`);
|
|
121
|
+
const ids = new Set();
|
|
122
|
+
return value.map((candidate)=>{
|
|
123
|
+
const source = requiredInputRecord(candidate, label);
|
|
124
|
+
const result = validateJsonRecord(source, label);
|
|
125
|
+
result.id = requiredIdentifier(source.id, label);
|
|
126
|
+
if (ids.has(result.id)) invalidWorkOfficeSpreadsheetInput(`a unique ${label} ID; '${result.id}' is repeated`);
|
|
127
|
+
ids.add(result.id);
|
|
128
|
+
return result;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function copyOptionalSheetSidecars(source, target, key, label) {
|
|
132
|
+
const value = source[key];
|
|
133
|
+
if (void 0 === value) return;
|
|
134
|
+
if (!Array.isArray(value)) invalidWorkOfficeSpreadsheetInput(`an array of ${label} records`);
|
|
135
|
+
const sheetIds = new Set();
|
|
136
|
+
const records = value.map((candidate)=>{
|
|
137
|
+
const input = requiredInputRecord(candidate, label);
|
|
138
|
+
const record = validateJsonRecord(input, label);
|
|
139
|
+
const sheetId = requiredIdentifier(input.sheetId, `${label} sheet`);
|
|
140
|
+
if (sheetIds.has(sheetId)) invalidWorkOfficeSpreadsheetInput(`at most one ${label} record for sheet '${sheetId}'`);
|
|
141
|
+
sheetIds.add(sheetId);
|
|
142
|
+
record.sheetId = sheetId;
|
|
143
|
+
return record;
|
|
144
|
+
});
|
|
145
|
+
target[key] = records;
|
|
146
|
+
}
|
|
147
|
+
function copyOptionalJsonField(source, target, key, label) {
|
|
148
|
+
const value = source[key];
|
|
149
|
+
if (void 0 === value) return;
|
|
150
|
+
target[key] = validateJsonRecord(requiredInputRecord(value, label), label);
|
|
151
|
+
}
|
|
152
|
+
function assertSpreadsheetReferences(content) {
|
|
153
|
+
const sheetIds = new Set(content.sheets.map((sheet)=>sheet.id));
|
|
154
|
+
for (const range of content.namedRanges ?? [])if (void 0 !== range.scopeSheetId && !sheetIds.has(range.scopeSheetId)) invalidWorkOfficeSpreadsheetInput(`named range '${range.id}' to reference an existing scope sheet`);
|
|
155
|
+
for (const sheet of content.sheets)for (const pivot of sheet.pivotTables ?? [])if (!sheetIds.has(pivot.sourceSheetId)) invalidWorkOfficeSpreadsheetInput(`pivot table '${pivot.id}' to reference an existing source sheet`);
|
|
156
|
+
for (const key of [
|
|
157
|
+
'printAreas',
|
|
158
|
+
'printTitles',
|
|
159
|
+
'pageBreaks',
|
|
160
|
+
'pageSetups'
|
|
161
|
+
])for (const record of content[key] ?? [])if (!sheetIds.has(record.sheetId)) invalidWorkOfficeSpreadsheetInput(`${key} to reference an existing sheet '${record.sheetId}'`);
|
|
162
|
+
}
|
|
163
|
+
function validateOptionalDimension(sheet, key, maximum) {
|
|
164
|
+
const value = sheet[key];
|
|
165
|
+
if (void 0 === value) return;
|
|
166
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > maximum) invalidWorkOfficeSpreadsheetInput(`a ${key} count between 0 and ${maximum} in sheet '${sheet.id}'`);
|
|
167
|
+
}
|
|
168
|
+
function requiredCoordinate(value, maximum, label, sheetId) {
|
|
169
|
+
if (!Number.isSafeInteger(value) || value < 0 || value >= maximum) invalidWorkOfficeSpreadsheetInput(`a valid ${label} coordinate in sheet '${sheetId}'`);
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function requiredInputRecord(value, label) {
|
|
173
|
+
if (!isWorkOfficeCollaborationRecord(value)) invalidWorkOfficeSpreadsheetInput(`a valid ${label} record`);
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
function requiredIdentifier(value, label) {
|
|
177
|
+
const result = requiredNonEmptyString(value, `${label} ID`);
|
|
178
|
+
if (result !== result.trim() || result.length > 256) invalidWorkOfficeSpreadsheetInput(`a ${label} ID containing 1 to 256 characters`);
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
function requiredNonEmptyString(value, label) {
|
|
182
|
+
if ('string' != typeof value || !value.trim() || value.length > 256) invalidWorkOfficeSpreadsheetInput(`a non-empty string of at most 256 characters for ${label}`);
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
function validateJsonRecord(value, label) {
|
|
186
|
+
try {
|
|
187
|
+
return cloneWorkOfficeCollaborationJson(value);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `Spreadsheet collaboration requires a JSON-compatible ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function assertPopulatedCellLimit(count, sheetId) {
|
|
193
|
+
if (count <= MAX_POPULATED_CELLS) return;
|
|
194
|
+
invalidWorkOfficeSpreadsheetInput(`at most ${MAX_POPULATED_CELLS.toLocaleString()} populated cells in sheet '${sheetId}'`);
|
|
195
|
+
}
|
|
196
|
+
function invalidWorkOfficeSpreadsheetInput(expected) {
|
|
197
|
+
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `Spreadsheet collaboration requires ${expected}.`);
|
|
198
|
+
}
|
|
199
|
+
function appendWorkOfficeSpreadsheetRecordClaims(claims, previous, next) {
|
|
200
|
+
const previousSheets = new Map((previous?.sheets ?? []).map((sheet)=>[
|
|
201
|
+
sheet.id,
|
|
202
|
+
sheet
|
|
203
|
+
]));
|
|
204
|
+
for (const sheet of next.sheets){
|
|
205
|
+
const before = previousSheets.get(sheet.id);
|
|
206
|
+
if (!before) appendClaim(claims, 'sheet', sheet.id, sheet);
|
|
207
|
+
appendChildClaims(claims, before, sheet, 'image', 'images');
|
|
208
|
+
appendChildClaims(claims, before, sheet, 'chart', 'charts');
|
|
209
|
+
appendChildClaims(claims, before, sheet, 'pivot-table', 'pivotTables');
|
|
210
|
+
}
|
|
211
|
+
const previousNamedRanges = new Set((previous?.namedRanges ?? []).map(({ id })=>id));
|
|
212
|
+
for (const range of next.namedRanges ?? [])if (!previousNamedRanges.has(range.id)) appendClaim(claims, 'named-range', range.id, range);
|
|
213
|
+
}
|
|
214
|
+
function assertWorkOfficeSpreadsheetRecordClaims(claims, content) {
|
|
215
|
+
const fingerprints = new Map();
|
|
216
|
+
for (const raw of claims.toArray()){
|
|
217
|
+
const claim = parsedClaim(raw);
|
|
218
|
+
const identity = claimIdentity(claim.kind, claim.id, claim.parentId);
|
|
219
|
+
const existing = fingerprints.get(identity);
|
|
220
|
+
if (void 0 !== existing && existing !== claim.fingerprint) throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The ${claimLabel(claim)} ID '${claim.id}' was concurrently assigned to different records.`);
|
|
221
|
+
fingerprints.set(identity, claim.fingerprint);
|
|
222
|
+
}
|
|
223
|
+
for (const sheet of content.sheets){
|
|
224
|
+
assertClaimExists(fingerprints, 'sheet', sheet.id);
|
|
225
|
+
for (const image of sheet.images ?? [])assertClaimExists(fingerprints, 'image', image.id, sheet.id);
|
|
226
|
+
for (const chart of sheet.charts ?? [])assertClaimExists(fingerprints, 'chart', chart.id, sheet.id);
|
|
227
|
+
for (const pivot of sheet.pivotTables ?? [])assertClaimExists(fingerprints, 'pivot-table', pivot.id, sheet.id);
|
|
228
|
+
}
|
|
229
|
+
for (const range of content.namedRanges ?? [])assertClaimExists(fingerprints, 'named-range', range.id);
|
|
230
|
+
}
|
|
231
|
+
function appendChildClaims(claims, previous, next, kind, key) {
|
|
232
|
+
const previousIds = new Set((previous?.[key] ?? []).map(({ id })=>id));
|
|
233
|
+
for (const value of next[key] ?? [])if (!previousIds.has(value.id)) appendClaim(claims, kind, value.id, value, next.id);
|
|
234
|
+
}
|
|
235
|
+
function appendClaim(claims, kind, id, value, parentId) {
|
|
236
|
+
const serialized = canonicalWorkOfficeCollaborationJson({
|
|
237
|
+
fingerprint: canonicalWorkOfficeCollaborationJson(value),
|
|
238
|
+
id,
|
|
239
|
+
kind,
|
|
240
|
+
...void 0 === parentId ? {} : {
|
|
241
|
+
parentId
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
if (!claims.toArray().includes(serialized)) claims.push([
|
|
245
|
+
serialized
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
function parsedClaim(raw) {
|
|
249
|
+
if ('string' != typeof raw) invalidWorkOfficeSpreadsheetShared('record claim');
|
|
250
|
+
let value;
|
|
251
|
+
try {
|
|
252
|
+
value = JSON.parse(raw);
|
|
253
|
+
} catch {
|
|
254
|
+
invalidWorkOfficeSpreadsheetShared('record claim');
|
|
255
|
+
}
|
|
256
|
+
if (!isWorkOfficeCollaborationRecord(value) || canonicalWorkOfficeCollaborationJson(value) !== raw) invalidWorkOfficeSpreadsheetShared('record claim');
|
|
257
|
+
const keys = Object.keys(value);
|
|
258
|
+
if (keys.some((key)=>'fingerprint' !== key && 'id' !== key && 'kind' !== key && 'parentId' !== key)) invalidWorkOfficeSpreadsheetShared('record claim');
|
|
259
|
+
const kind = requiredClaimKind(value.kind);
|
|
260
|
+
const id = requiredClaimIdentifier(value.id, 'record claim');
|
|
261
|
+
const fingerprint = requiredClaimString(value.fingerprint, 'record claim fingerprint');
|
|
262
|
+
const parentId = void 0 === value.parentId ? void 0 : requiredClaimIdentifier(value.parentId, 'record claim parent');
|
|
263
|
+
if (('sheet' === kind || 'named-range' === kind) === (void 0 !== parentId)) invalidWorkOfficeSpreadsheetShared('record claim parent');
|
|
264
|
+
try {
|
|
265
|
+
const fingerprintValue = JSON.parse(fingerprint);
|
|
266
|
+
if (canonicalWorkOfficeCollaborationJson(fingerprintValue) !== fingerprint) invalidWorkOfficeSpreadsheetShared('record claim fingerprint');
|
|
267
|
+
} catch {
|
|
268
|
+
invalidWorkOfficeSpreadsheetShared('record claim fingerprint');
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
fingerprint,
|
|
272
|
+
id,
|
|
273
|
+
kind,
|
|
274
|
+
parentId
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function requiredClaimKind(value) {
|
|
278
|
+
if ('chart' === value || 'image' === value || 'named-range' === value || 'pivot-table' === value || 'sheet' === value) return value;
|
|
279
|
+
invalidWorkOfficeSpreadsheetShared('record claim kind');
|
|
280
|
+
}
|
|
281
|
+
function requiredClaimIdentifier(value, label) {
|
|
282
|
+
const result = requiredClaimString(value, label);
|
|
283
|
+
if (!result || result !== result.trim() || result.length > 256) invalidWorkOfficeSpreadsheetShared(label);
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
function requiredClaimString(value, label) {
|
|
287
|
+
if ('string' != typeof value) invalidWorkOfficeSpreadsheetShared(label);
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
function assertClaimExists(claims, kind, id, parentId) {
|
|
291
|
+
if (claims.has(claimIdentity(kind, id, parentId))) return;
|
|
292
|
+
invalidWorkOfficeSpreadsheetShared(`${claimLabel({
|
|
293
|
+
kind,
|
|
294
|
+
parentId
|
|
295
|
+
})} record claim`);
|
|
296
|
+
}
|
|
297
|
+
function claimIdentity(kind, id, parentId) {
|
|
298
|
+
return canonicalWorkOfficeCollaborationJson([
|
|
299
|
+
kind,
|
|
300
|
+
parentId ?? null,
|
|
301
|
+
id
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
function claimLabel(claim) {
|
|
305
|
+
if ('named-range' === claim.kind) return 'named range';
|
|
306
|
+
if ('pivot-table' === claim.kind) return `pivot table in sheet '${claim.parentId}'`;
|
|
307
|
+
if ('sheet' === claim.kind) return 'sheet';
|
|
308
|
+
return `${claim.kind} in sheet '${claim.parentId}'`;
|
|
309
|
+
}
|
|
310
|
+
function assertWorkOfficeSpreadsheetPatchSafe(previous, next, shared) {
|
|
311
|
+
assertCompatibleValue(previous.calculation, next.calculation, shared.calculation, 'calculation settings');
|
|
312
|
+
assertRecordCollection(previous.sheets, next.sheets, shared.sheets, (sheet)=>sheet.id, 'sheet', assertCompatibleSheet);
|
|
313
|
+
assertRecordCollection(previous.namedRanges ?? [], next.namedRanges ?? [], shared.namedRanges ?? [], (range)=>range.id, 'named range');
|
|
314
|
+
assertSheetSidecars(previous, next, shared, 'printAreas', 'print area');
|
|
315
|
+
assertSheetSidecars(previous, next, shared, 'printTitles', 'print titles');
|
|
316
|
+
assertSheetSidecars(previous, next, shared, 'pageBreaks', 'page breaks');
|
|
317
|
+
assertSheetSidecars(previous, next, shared, 'pageSetups', 'page setup');
|
|
318
|
+
}
|
|
319
|
+
function assertSheetSidecars(previous, next, shared, key, label) {
|
|
320
|
+
assertRecordCollection(previous[key] ?? [], next[key] ?? [], shared[key] ?? [], (record)=>record.sheetId, label);
|
|
321
|
+
}
|
|
322
|
+
function assertCompatibleSheet(previous, next, shared, label) {
|
|
323
|
+
const omitted = new Set([
|
|
324
|
+
'data',
|
|
325
|
+
'celldata',
|
|
326
|
+
'config',
|
|
327
|
+
'formulaMetadata',
|
|
328
|
+
'images',
|
|
329
|
+
'charts',
|
|
330
|
+
'pivotTables'
|
|
331
|
+
]);
|
|
332
|
+
assertCompatibleValue(withoutKeys(previous, omitted), withoutKeys(next, omitted), withoutKeys(shared, omitted), label);
|
|
333
|
+
assertCompatibleValue(previous.config, next.config, shared.config, `${label} config`);
|
|
334
|
+
assertCompatibleValue(previous.formulaMetadata, next.formulaMetadata, shared.formulaMetadata, `${label} formula metadata`);
|
|
335
|
+
assertRecordCollection(previous.images ?? [], next.images ?? [], shared.images ?? [], (image)=>image.id, `${label} image`);
|
|
336
|
+
assertRecordCollection(previous.charts ?? [], next.charts ?? [], shared.charts ?? [], (chart)=>chart.id, `${label} chart`);
|
|
337
|
+
assertRecordCollection(previous.pivotTables ?? [], next.pivotTables ?? [], shared.pivotTables ?? [], (pivot)=>pivot.id, `${label} pivot table`);
|
|
338
|
+
assertCellCollection(spreadsheetCells(previous), spreadsheetCells(next), spreadsheetCells(shared), label);
|
|
339
|
+
}
|
|
340
|
+
function assertCellCollection(previous, next, shared, sheetLabel) {
|
|
341
|
+
for (const [coordinate, before] of previous){
|
|
342
|
+
const after = next.get(coordinate);
|
|
343
|
+
const current = shared.get(coordinate);
|
|
344
|
+
const label = `cell '${coordinate}' in ${sheetLabel}`;
|
|
345
|
+
if (void 0 === after) {
|
|
346
|
+
if (void 0 !== current && !workOfficeCollaborationJsonEqual(before, current)) conflict(label);
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (!workOfficeCollaborationJsonEqual(before, after)) {
|
|
350
|
+
if (void 0 === current) removedConflict(label);
|
|
351
|
+
assertCompatibleValue(before, after, current, label);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
for (const [coordinate, after] of next){
|
|
355
|
+
if (previous.has(coordinate)) continue;
|
|
356
|
+
const current = shared.get(coordinate);
|
|
357
|
+
if (void 0 !== current && !workOfficeCollaborationJsonEqual(after, current)) conflict(`cell '${coordinate}' in ${sheetLabel}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function assertRecordCollection(previous, next, shared, identity, label, compare = assertCompatibleValue) {
|
|
361
|
+
const nextById = new Map(next.map((value)=>[
|
|
362
|
+
identity(value),
|
|
363
|
+
value
|
|
364
|
+
]));
|
|
365
|
+
const sharedById = new Map(shared.map((value)=>[
|
|
366
|
+
identity(value),
|
|
367
|
+
value
|
|
368
|
+
]));
|
|
369
|
+
const previousIds = new Set();
|
|
370
|
+
for (const before of previous){
|
|
371
|
+
const id = identity(before);
|
|
372
|
+
previousIds.add(id);
|
|
373
|
+
const after = nextById.get(id);
|
|
374
|
+
const current = sharedById.get(id);
|
|
375
|
+
const recordLabel = `${label} '${id}'`;
|
|
376
|
+
if (void 0 === after) {
|
|
377
|
+
if (void 0 !== current && !workOfficeCollaborationJsonEqual(before, current)) conflict(recordLabel);
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (!workOfficeCollaborationJsonEqual(before, after)) {
|
|
381
|
+
if (void 0 === current) removedConflict(recordLabel);
|
|
382
|
+
compare(before, after, current, recordLabel);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
for (const after of next){
|
|
386
|
+
const id = identity(after);
|
|
387
|
+
if (previousIds.has(id)) continue;
|
|
388
|
+
const current = sharedById.get(id);
|
|
389
|
+
if (void 0 !== current && !workOfficeCollaborationJsonEqual(after, current)) conflict(`${label} '${id}'`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function assertCompatibleValue(previous, next, shared, label) {
|
|
393
|
+
if (workOfficeCollaborationJsonEqual(previous, next)) return;
|
|
394
|
+
if (isWorkOfficeCollaborationRecord(previous) && isWorkOfficeCollaborationRecord(next) && isWorkOfficeCollaborationRecord(shared)) {
|
|
395
|
+
const keys = new Set([
|
|
396
|
+
...Object.keys(previous),
|
|
397
|
+
...Object.keys(next)
|
|
398
|
+
]);
|
|
399
|
+
for (const key of keys){
|
|
400
|
+
const hadPrevious = Object.hasOwn(previous, key);
|
|
401
|
+
const hasNext = Object.hasOwn(next, key);
|
|
402
|
+
const hasShared = Object.hasOwn(shared, key);
|
|
403
|
+
const fieldLabel = `${label} field '${key}'`;
|
|
404
|
+
if (!hasNext) {
|
|
405
|
+
if (hadPrevious && hasShared && !workOfficeCollaborationJsonEqual(previous[key], shared[key])) conflict(fieldLabel);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (!hadPrevious) {
|
|
409
|
+
if (hasShared && !workOfficeCollaborationJsonEqual(next[key], shared[key])) conflict(fieldLabel);
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (!workOfficeCollaborationJsonEqual(previous[key], next[key])) {
|
|
413
|
+
if (!hasShared) removedConflict(fieldLabel);
|
|
414
|
+
assertCompatibleValue(previous[key], next[key], shared[key], fieldLabel);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (!workOfficeCollaborationJsonEqual(previous, shared) && !workOfficeCollaborationJsonEqual(next, shared)) conflict(label);
|
|
420
|
+
}
|
|
421
|
+
function spreadsheetCells(sheet) {
|
|
422
|
+
const result = new Map();
|
|
423
|
+
if (void 0 !== sheet.data) {
|
|
424
|
+
for (const [row, values] of sheet.data.entries())for (const [column, cell] of values.entries())if (null !== cell) result.set(`${row}:${column}`, cell);
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
for (const entry of sheet.celldata ?? [])if (null !== entry.v) result.set(`${entry.r}:${entry.c}`, entry.v);
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
function withoutKeys(value, omitted) {
|
|
431
|
+
return Object.fromEntries(Object.entries(value).filter(([key])=>!omitted.has(key)));
|
|
432
|
+
}
|
|
433
|
+
function removedConflict(label) {
|
|
434
|
+
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The ${label} was removed before this change could be applied. Refresh the shared snapshot before retrying.`);
|
|
435
|
+
}
|
|
436
|
+
function conflict(label) {
|
|
437
|
+
throw new WorkOfficeCollaborationError('office.collaboration.content_invalid', `The ${label} changed concurrently. Refresh the shared snapshot before retrying.`);
|
|
438
|
+
}
|
|
439
|
+
function patchSpreadsheetFlatJsonMap(target, previous, next, label) {
|
|
440
|
+
patchWorkOfficeCollaborationFlatJsonMap(target, previous, next, label);
|
|
441
|
+
}
|
|
442
|
+
function readSpreadsheetFlatJsonMap(target, label) {
|
|
443
|
+
return readWorkOfficeCollaborationFlatJsonMap(target, label, invalidWorkOfficeSpreadsheetShared);
|
|
444
|
+
}
|
|
445
|
+
const SPREADSHEET_RECORD_CELLS = 'cells';
|
|
446
|
+
const SPREADSHEET_RECORD_CELL_PRESENCE = 'cellPresence';
|
|
447
|
+
const SPREADSHEET_RECORD_CELL_MODE = 'cellMode';
|
|
448
|
+
const SPREADSHEET_RECORD_DATA_ROW_LENGTHS = 'dataRowLengths';
|
|
449
|
+
function patchSpreadsheetCells(record, previous, next) {
|
|
450
|
+
const fields = nestedSpreadsheetMap(record, SPREADSHEET_RECORD_CELLS, 'sheet cell fields');
|
|
451
|
+
const presence = nestedSpreadsheetMap(record, SPREADSHEET_RECORD_CELL_PRESENCE, 'sheet cell presence');
|
|
452
|
+
const rowLengths = nestedSpreadsheetArray(record, SPREADSHEET_RECORD_DATA_ROW_LENGTHS, 'sheet data row lengths');
|
|
453
|
+
const before = spreadsheetCellEntries(previous);
|
|
454
|
+
const after = spreadsheetCellEntries(next);
|
|
455
|
+
const beforeByCoordinate = new Map(before.map((entry)=>[
|
|
456
|
+
entry.coordinate,
|
|
457
|
+
entry
|
|
458
|
+
]));
|
|
459
|
+
const afterByCoordinate = new Map(after.map((entry)=>[
|
|
460
|
+
entry.coordinate,
|
|
461
|
+
entry
|
|
462
|
+
]));
|
|
463
|
+
for (const entry of before){
|
|
464
|
+
if (afterByCoordinate.has(entry.coordinate)) continue;
|
|
465
|
+
if (!presence.has(entry.coordinate)) continue;
|
|
466
|
+
const current = readCell(fields, entry.row, entry.column);
|
|
467
|
+
if (!workOfficeCollaborationJsonEqual(entry.cell, current)) throw staleSpreadsheetConflict(`Cell '${entry.coordinate}' in sheet '${next.id}' changed before it was deleted`);
|
|
468
|
+
presence.delete(entry.coordinate);
|
|
469
|
+
deleteCellFields(fields, entry.row, entry.column);
|
|
470
|
+
}
|
|
471
|
+
for (const entry of after){
|
|
472
|
+
const beforeEntry = beforeByCoordinate.get(entry.coordinate);
|
|
473
|
+
if (!(beforeEntry && workOfficeCollaborationJsonEqual(beforeEntry.cell, entry.cell))) {
|
|
474
|
+
if (beforeEntry && !presence.has(entry.coordinate)) throw staleSpreadsheetConflict(`Cell '${entry.coordinate}' in sheet '${next.id}' was removed before this change could be applied`);
|
|
475
|
+
presence.set(entry.coordinate, true);
|
|
476
|
+
patchCellFields(fields, beforeEntry?.cell, entry.cell, entry.row, entry.column, next.id);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
patchCellProjectionMetadata(record, rowLengths, previous, next);
|
|
480
|
+
}
|
|
481
|
+
function readSpreadsheetCells(record, sheet) {
|
|
482
|
+
const fields = requiredSpreadsheetMap(record, SPREADSHEET_RECORD_CELLS, 'sheet cell fields');
|
|
483
|
+
const presence = requiredSpreadsheetMap(record, SPREADSHEET_RECORD_CELL_PRESENCE, 'sheet cell presence');
|
|
484
|
+
const rowLengths = requiredSpreadsheetArray(record, SPREADSHEET_RECORD_DATA_ROW_LENGTHS, 'sheet data row lengths');
|
|
485
|
+
const grouped = new Map();
|
|
486
|
+
for (const [coordinate, value] of presence.entries()){
|
|
487
|
+
if (true !== value) invalidWorkOfficeSpreadsheetShared('sheet cell presence');
|
|
488
|
+
const decoded = decodedCoordinate(coordinate);
|
|
489
|
+
grouped.set(coordinate, {
|
|
490
|
+
...decoded,
|
|
491
|
+
cell: {},
|
|
492
|
+
coordinate
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
for (const [encoded, value] of fields.entries()){
|
|
496
|
+
const { column, row } = decodedCellField(encoded);
|
|
497
|
+
const coordinate = encodedCoordinate(row, column);
|
|
498
|
+
const entry = grouped.get(coordinate);
|
|
499
|
+
if (!entry || value instanceof __rspack_external_yjs.AbstractType) invalidWorkOfficeSpreadsheetShared(`cell field '${encoded}'`);
|
|
500
|
+
}
|
|
501
|
+
for (const entry of grouped.values())entry.cell = readCell(fields, entry.row, entry.column);
|
|
502
|
+
const entries = Array.from(grouped.values()).sort((left, right)=>left.row - right.row || left.column - right.column);
|
|
503
|
+
const mode = optionalCellMode(record.get(SPREADSHEET_RECORD_CELL_MODE));
|
|
504
|
+
if (void 0 === mode) {
|
|
505
|
+
if (entries.length > 0 || rowLengths.length > 0) invalidWorkOfficeSpreadsheetShared('cell projection mode');
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if ('celldata' === mode) {
|
|
509
|
+
if (rowLengths.length > 0) invalidWorkOfficeSpreadsheetShared('sparse cell projection dimensions');
|
|
510
|
+
sheet.celldata = entries.map(({ row, column, cell })=>({
|
|
511
|
+
r: row,
|
|
512
|
+
c: column,
|
|
513
|
+
v: cell
|
|
514
|
+
}));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const lengths = rowLengths.toArray().map((value, row)=>{
|
|
518
|
+
if (!Number.isSafeInteger(value) || value < 0) invalidWorkOfficeSpreadsheetShared(`data row length at row ${row}`);
|
|
519
|
+
return value;
|
|
520
|
+
});
|
|
521
|
+
const data = lengths.map((length)=>Array.from({
|
|
522
|
+
length
|
|
523
|
+
}, ()=>null));
|
|
524
|
+
for (const entry of entries){
|
|
525
|
+
if (entry.row >= data.length || entry.column >= (lengths[entry.row] ?? 0)) invalidWorkOfficeSpreadsheetShared(`cell '${entry.coordinate}' outside the stored data matrix`);
|
|
526
|
+
data[entry.row][entry.column] = entry.cell;
|
|
527
|
+
}
|
|
528
|
+
sheet.data = data;
|
|
529
|
+
}
|
|
530
|
+
function spreadsheetCellEntries(sheet) {
|
|
531
|
+
if (!sheet) return [];
|
|
532
|
+
const entries = [];
|
|
533
|
+
if (void 0 !== sheet.data) {
|
|
534
|
+
for (const [row, values] of sheet.data.entries())for (const [column, cell] of values.entries())if (null !== cell) entries.push({
|
|
535
|
+
cell,
|
|
536
|
+
column,
|
|
537
|
+
coordinate: encodedCoordinate(row, column),
|
|
538
|
+
row
|
|
539
|
+
});
|
|
540
|
+
return entries;
|
|
541
|
+
}
|
|
542
|
+
for (const entry of sheet.celldata ?? [])if (null !== entry.v) entries.push({
|
|
543
|
+
cell: entry.v,
|
|
544
|
+
column: entry.c,
|
|
545
|
+
coordinate: encodedCoordinate(entry.r, entry.c),
|
|
546
|
+
row: entry.r
|
|
547
|
+
});
|
|
548
|
+
return entries;
|
|
549
|
+
}
|
|
550
|
+
function patchCellFields(target, previous, next, row, column, sheetId) {
|
|
551
|
+
patchSpreadsheetFlatJsonMap(new SpreadsheetCellFieldView(target, row, column), previous, next, `cell '${row}:${column}' in sheet '${sheetId}'`);
|
|
552
|
+
}
|
|
553
|
+
function readCell(target, row, column) {
|
|
554
|
+
return readSpreadsheetFlatJsonMap(new SpreadsheetCellFieldView(target, row, column), `cell '${row}:${column}'`);
|
|
555
|
+
}
|
|
556
|
+
function patchCellProjectionMetadata(record, rowLengths, previous, next) {
|
|
557
|
+
const previousMode = cellMode(previous);
|
|
558
|
+
const nextMode = cellMode(next);
|
|
559
|
+
if (previousMode !== nextMode) if (void 0 === nextMode) record.delete(SPREADSHEET_RECORD_CELL_MODE);
|
|
560
|
+
else record.set(SPREADSHEET_RECORD_CELL_MODE, nextMode);
|
|
561
|
+
const before = dataRowLengths(previous);
|
|
562
|
+
const after = dataRowLengths(next);
|
|
563
|
+
if (!workOfficeCollaborationJsonEqual(before, after)) {
|
|
564
|
+
const shared = rowLengths.toArray();
|
|
565
|
+
const merged = mergeStaleRowLengths(before, after, shared);
|
|
566
|
+
if (rowLengths.length > 0) rowLengths.delete(0, rowLengths.length);
|
|
567
|
+
if (merged.length > 0) rowLengths.push(merged);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function mergeStaleRowLengths(previous, next, shared) {
|
|
571
|
+
const length = Math.max(next.length, shared.length);
|
|
572
|
+
const result = [];
|
|
573
|
+
for(let index = 0; index < length; index += 1){
|
|
574
|
+
const before = previous[index];
|
|
575
|
+
const after = next[index];
|
|
576
|
+
const current = shared[index];
|
|
577
|
+
if (after === before) result[index] = current ?? after ?? 0;
|
|
578
|
+
else if (current === before || void 0 === current) result[index] = after ?? 0;
|
|
579
|
+
else result[index] = Math.max(after ?? 0, current);
|
|
580
|
+
}
|
|
581
|
+
while(result.length > next.length && 0 === result.at(-1))result.pop();
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
function cellMode(sheet) {
|
|
585
|
+
if (sheet?.data !== void 0) return 'data';
|
|
586
|
+
if (sheet?.celldata !== void 0) return 'celldata';
|
|
587
|
+
}
|
|
588
|
+
function dataRowLengths(sheet) {
|
|
589
|
+
return sheet?.data?.map((row)=>row.length) ?? [];
|
|
590
|
+
}
|
|
591
|
+
function nestedSpreadsheetMap(record, key, label) {
|
|
592
|
+
const existing = record.get(key);
|
|
593
|
+
if (existing instanceof __rspack_external_yjs.Map) return existing;
|
|
594
|
+
if (void 0 !== existing) invalidWorkOfficeSpreadsheetShared(label);
|
|
595
|
+
const value = new __rspack_external_yjs.Map();
|
|
596
|
+
record.set(key, value);
|
|
597
|
+
return value;
|
|
598
|
+
}
|
|
599
|
+
function nestedSpreadsheetArray(record, key, label) {
|
|
600
|
+
const existing = record.get(key);
|
|
601
|
+
if (existing instanceof __rspack_external_yjs.Array) return existing;
|
|
602
|
+
if (void 0 !== existing) invalidWorkOfficeSpreadsheetShared(label);
|
|
603
|
+
const value = new __rspack_external_yjs.Array();
|
|
604
|
+
record.set(key, value);
|
|
605
|
+
return value;
|
|
606
|
+
}
|
|
607
|
+
function requiredSpreadsheetMap(record, key, label) {
|
|
608
|
+
const value = record.get(key);
|
|
609
|
+
if (!(value instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
610
|
+
return value;
|
|
611
|
+
}
|
|
612
|
+
function requiredSpreadsheetArray(record, key, label) {
|
|
613
|
+
const value = record.get(key);
|
|
614
|
+
if (!(value instanceof __rspack_external_yjs.Array)) invalidWorkOfficeSpreadsheetShared(label);
|
|
615
|
+
return value;
|
|
616
|
+
}
|
|
617
|
+
function optionalCellMode(value) {
|
|
618
|
+
if (void 0 === value) return;
|
|
619
|
+
if ('data' === value || 'celldata' === value) return value;
|
|
620
|
+
invalidWorkOfficeSpreadsheetShared('cell projection mode');
|
|
621
|
+
}
|
|
622
|
+
function encodedCoordinate(row, column) {
|
|
623
|
+
return `${row}:${column}`;
|
|
624
|
+
}
|
|
625
|
+
function decodedCoordinate(coordinate) {
|
|
626
|
+
const match = /^(0|[1-9]\d*):(0|[1-9]\d*)$/.exec(coordinate);
|
|
627
|
+
if (!match) invalidWorkOfficeSpreadsheetShared(`cell coordinate '${coordinate}'`);
|
|
628
|
+
const row = Number(match[1]);
|
|
629
|
+
const column = Number(match[2]);
|
|
630
|
+
if (!Number.isSafeInteger(row) || !Number.isSafeInteger(column) || row >= OFFICE_KERNEL_SPREADSHEET_MAX_ROWS || column >= 16384) invalidWorkOfficeSpreadsheetShared(`cell coordinate '${coordinate}'`);
|
|
631
|
+
return {
|
|
632
|
+
row,
|
|
633
|
+
column
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function encodedCellField(row, column, key) {
|
|
637
|
+
return JSON.stringify([
|
|
638
|
+
row,
|
|
639
|
+
column,
|
|
640
|
+
key
|
|
641
|
+
]);
|
|
642
|
+
}
|
|
643
|
+
class SpreadsheetCellFieldView {
|
|
644
|
+
#source;
|
|
645
|
+
#row;
|
|
646
|
+
#column;
|
|
647
|
+
constructor(source, row, column){
|
|
648
|
+
this.#source = source;
|
|
649
|
+
this.#row = row;
|
|
650
|
+
this.#column = column;
|
|
651
|
+
}
|
|
652
|
+
get(key) {
|
|
653
|
+
return this.#source.get(encodedCellField(this.#row, this.#column, key));
|
|
654
|
+
}
|
|
655
|
+
set(key, value) {
|
|
656
|
+
this.#source.set(encodedCellField(this.#row, this.#column, key), value);
|
|
657
|
+
return value;
|
|
658
|
+
}
|
|
659
|
+
delete(key) {
|
|
660
|
+
return this.#source.delete(encodedCellField(this.#row, this.#column, key));
|
|
661
|
+
}
|
|
662
|
+
*entries() {
|
|
663
|
+
for (const [encoded, value] of this.#source.entries()){
|
|
664
|
+
const identity = decodedCellField(encoded);
|
|
665
|
+
if (identity.row === this.#row && identity.column === this.#column) yield [
|
|
666
|
+
identity.key,
|
|
667
|
+
value
|
|
668
|
+
];
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function deleteCellFields(source, row, column) {
|
|
673
|
+
for (const encoded of Array.from(source.keys())){
|
|
674
|
+
const identity = decodedCellField(encoded);
|
|
675
|
+
if (identity.row === row && identity.column === column) source.delete(encoded);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function decodedCellField(encoded) {
|
|
679
|
+
let value;
|
|
680
|
+
try {
|
|
681
|
+
value = JSON.parse(encoded);
|
|
682
|
+
} catch {
|
|
683
|
+
invalidWorkOfficeSpreadsheetShared(`cell field identity '${encoded}'`);
|
|
684
|
+
}
|
|
685
|
+
if (!Array.isArray(value) || 3 !== value.length || 'string' != typeof value[2] || !value[2] || encodedCellField(value[0], value[1], value[2]) !== encoded) invalidWorkOfficeSpreadsheetShared(`cell field identity '${encoded}'`);
|
|
686
|
+
const coordinate = decodedCoordinate(`${value[0]}:${value[1]}`);
|
|
687
|
+
return {
|
|
688
|
+
...coordinate,
|
|
689
|
+
key: value[2]
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function staleSpreadsheetConflict(message) {
|
|
693
|
+
return new WorkOfficeCollaborationError('office.collaboration.content_invalid', `${message}. Refresh the shared snapshot before retrying.`);
|
|
694
|
+
}
|
|
695
|
+
const SPREADSHEET_RECORD_CONFIG = 'config';
|
|
696
|
+
const SPREADSHEET_RECORD_FORMULA_METADATA = 'formulaMetadata';
|
|
697
|
+
const SPREADSHEET_RECORD_IMAGES = 'images';
|
|
698
|
+
const SPREADSHEET_RECORD_IMAGE_ORDER = 'imageOrder';
|
|
699
|
+
const SPREADSHEET_RECORD_CHARTS = 'charts';
|
|
700
|
+
const SPREADSHEET_RECORD_CHART_ORDER = 'chartOrder';
|
|
701
|
+
const SPREADSHEET_RECORD_PIVOTS = 'pivotTables';
|
|
702
|
+
const SPREADSHEET_RECORD_PIVOT_ORDER = 'pivotOrder';
|
|
703
|
+
const NESTED_SHEET_FIELDS = new Set([
|
|
704
|
+
"cells",
|
|
705
|
+
SPREADSHEET_RECORD_CELL_PRESENCE,
|
|
706
|
+
SPREADSHEET_RECORD_CELL_MODE,
|
|
707
|
+
SPREADSHEET_RECORD_DATA_ROW_LENGTHS,
|
|
708
|
+
SPREADSHEET_RECORD_CONFIG,
|
|
709
|
+
SPREADSHEET_RECORD_FORMULA_METADATA,
|
|
710
|
+
SPREADSHEET_RECORD_IMAGES,
|
|
711
|
+
SPREADSHEET_RECORD_IMAGE_ORDER,
|
|
712
|
+
SPREADSHEET_RECORD_CHARTS,
|
|
713
|
+
SPREADSHEET_RECORD_CHART_ORDER,
|
|
714
|
+
SPREADSHEET_RECORD_PIVOTS,
|
|
715
|
+
SPREADSHEET_RECORD_PIVOT_ORDER
|
|
716
|
+
]);
|
|
717
|
+
function initializeSpreadsheetSheetRecord(record, sheet) {
|
|
718
|
+
office_spreadsheet_collaboration_records_nestedSpreadsheetMap(record, SPREADSHEET_RECORD_CONFIG, 'sheet config');
|
|
719
|
+
office_spreadsheet_collaboration_records_nestedSpreadsheetMap(record, SPREADSHEET_RECORD_FORMULA_METADATA, 'sheet formula metadata');
|
|
720
|
+
initializeIdCollection(record, SPREADSHEET_RECORD_IMAGES, SPREADSHEET_RECORD_IMAGE_ORDER);
|
|
721
|
+
initializeIdCollection(record, SPREADSHEET_RECORD_CHARTS, SPREADSHEET_RECORD_CHART_ORDER);
|
|
722
|
+
initializeIdCollection(record, SPREADSHEET_RECORD_PIVOTS, SPREADSHEET_RECORD_PIVOT_ORDER);
|
|
723
|
+
patchSpreadsheetSheetRecord(record, void 0, sheet);
|
|
724
|
+
}
|
|
725
|
+
function patchSpreadsheetSheetRecord(record, previous, next) {
|
|
726
|
+
const before = previous ?? {};
|
|
727
|
+
const after = next;
|
|
728
|
+
const keys = new Set([
|
|
729
|
+
...Object.keys(before),
|
|
730
|
+
...Object.keys(after)
|
|
731
|
+
]);
|
|
732
|
+
for (const key of keys)if ('data' !== key && 'celldata' !== key && 'config' !== key && 'formulaMetadata' !== key && 'images' !== key && 'charts' !== key && 'pivotTables' !== key) patchOptionalJson(record, key, before[key], after[key], void 0 !== previous);
|
|
733
|
+
patchJsonMap(office_spreadsheet_collaboration_records_nestedSpreadsheetMap(record, SPREADSHEET_RECORD_CONFIG, 'sheet config'), previous?.config, next.config);
|
|
734
|
+
patchJsonMap(office_spreadsheet_collaboration_records_nestedSpreadsheetMap(record, SPREADSHEET_RECORD_FORMULA_METADATA, 'sheet formula metadata'), previous?.formulaMetadata, next.formulaMetadata);
|
|
735
|
+
patchSpreadsheetCells(record, previous, next);
|
|
736
|
+
patchIdCollection(record, SPREADSHEET_RECORD_IMAGES, SPREADSHEET_RECORD_IMAGE_ORDER, previous?.images ?? [], next.images ?? [], `image in sheet '${next.id}'`);
|
|
737
|
+
patchIdCollection(record, SPREADSHEET_RECORD_CHARTS, SPREADSHEET_RECORD_CHART_ORDER, previous?.charts ?? [], next.charts ?? [], `chart in sheet '${next.id}'`);
|
|
738
|
+
patchIdCollection(record, SPREADSHEET_RECORD_PIVOTS, SPREADSHEET_RECORD_PIVOT_ORDER, previous?.pivotTables ?? [], next.pivotTables ?? [], `pivot table in sheet '${next.id}'`);
|
|
739
|
+
}
|
|
740
|
+
function readSpreadsheetSheetRecord(record, id) {
|
|
741
|
+
const result = {};
|
|
742
|
+
for (const [key, value] of record.entries())if (!NESTED_SHEET_FIELDS.has(key)) {
|
|
743
|
+
if (value instanceof __rspack_external_yjs.AbstractType) invalidWorkOfficeSpreadsheetShared(`sheet '${id}' field '${key}'`);
|
|
744
|
+
result[key] = cloneWorkOfficeCollaborationJson(value);
|
|
745
|
+
}
|
|
746
|
+
if (result.id !== id) invalidWorkOfficeSpreadsheetShared(`sheet '${id}' identity`);
|
|
747
|
+
const config = readJsonMap(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(record, SPREADSHEET_RECORD_CONFIG, 'sheet config'), 'sheet config');
|
|
748
|
+
if (Object.keys(config).length > 0) result.config = config;
|
|
749
|
+
const formulaMetadata = readJsonMap(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(record, SPREADSHEET_RECORD_FORMULA_METADATA, 'sheet formula metadata'), 'sheet formula metadata');
|
|
750
|
+
if (Object.keys(formulaMetadata).length > 0) result.formulaMetadata = formulaMetadata;
|
|
751
|
+
const sheet = result;
|
|
752
|
+
readSpreadsheetCells(record, sheet);
|
|
753
|
+
readOptionalCollection(record, SPREADSHEET_RECORD_IMAGES, SPREADSHEET_RECORD_IMAGE_ORDER, `image in sheet '${id}'`, 'images', result);
|
|
754
|
+
readOptionalCollection(record, SPREADSHEET_RECORD_CHARTS, SPREADSHEET_RECORD_CHART_ORDER, `chart in sheet '${id}'`, 'charts', result);
|
|
755
|
+
readOptionalCollection(record, SPREADSHEET_RECORD_PIVOTS, SPREADSHEET_RECORD_PIVOT_ORDER, `pivot table in sheet '${id}'`, 'pivotTables', result);
|
|
756
|
+
return sheet;
|
|
757
|
+
}
|
|
758
|
+
function patchSpreadsheetOrder(order, previous, next) {
|
|
759
|
+
if (workOfficeCollaborationJsonEqual(previous, next)) return;
|
|
760
|
+
const nextIds = new Set(next);
|
|
761
|
+
for (const id of previous)if (!nextIds.has(id)) removeAll(order, id);
|
|
762
|
+
const previousIds = new Set(previous);
|
|
763
|
+
for (const id of next)if (!previousIds.has(id) && !order.toArray().includes(id)) insertRelative(order, next, id);
|
|
764
|
+
if (!sameRelativeOrder(previous, next)) {
|
|
765
|
+
for (const id of next.filter((value)=>previousIds.has(value)))removeAll(order, id);
|
|
766
|
+
for (const id of next)if (!order.toArray().includes(id)) insertRelative(order, next, id);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function validatedSpreadsheetOrder(order, records, label) {
|
|
770
|
+
const seen = new Set();
|
|
771
|
+
const values = [];
|
|
772
|
+
for (const value of order.toArray()){
|
|
773
|
+
if ('string' != typeof value || !value.trim()) invalidWorkOfficeSpreadsheetShared(`${label} order`);
|
|
774
|
+
if (!seen.has(value)) {
|
|
775
|
+
seen.add(value);
|
|
776
|
+
values.push(value);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (seen.size !== records.size || Array.from(records.keys()).some((id)=>!seen.has(id))) invalidWorkOfficeSpreadsheetShared(`${label} order and record set`);
|
|
780
|
+
return values;
|
|
781
|
+
}
|
|
782
|
+
function office_spreadsheet_collaboration_records_nestedSpreadsheetMap(parent, key, label) {
|
|
783
|
+
const existing = parent.get(key);
|
|
784
|
+
if (existing instanceof __rspack_external_yjs.Map) return existing;
|
|
785
|
+
if (void 0 !== existing) invalidWorkOfficeSpreadsheetShared(label);
|
|
786
|
+
const value = new __rspack_external_yjs.Map();
|
|
787
|
+
parent.set(key, value);
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
function office_spreadsheet_collaboration_records_requiredSpreadsheetMap(parent, key, label) {
|
|
791
|
+
const value = parent.get(key);
|
|
792
|
+
if (!(value instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
793
|
+
return value;
|
|
794
|
+
}
|
|
795
|
+
function initializeIdCollection(record, recordsKey, orderKey) {
|
|
796
|
+
office_spreadsheet_collaboration_records_nestedSpreadsheetMap(record, recordsKey, recordsKey);
|
|
797
|
+
office_spreadsheet_collaboration_records_nestedSpreadsheetArray(record, orderKey, orderKey);
|
|
798
|
+
}
|
|
799
|
+
function patchIdCollection(parent, recordsKey, orderKey, previous, next, label) {
|
|
800
|
+
const records = office_spreadsheet_collaboration_records_nestedSpreadsheetMap(parent, recordsKey, label);
|
|
801
|
+
const order = office_spreadsheet_collaboration_records_nestedSpreadsheetArray(parent, orderKey, `${label} order`);
|
|
802
|
+
const previousById = new Map(previous.map((value)=>[
|
|
803
|
+
value.id,
|
|
804
|
+
value
|
|
805
|
+
]));
|
|
806
|
+
const nextById = new Map(next.map((value)=>[
|
|
807
|
+
value.id,
|
|
808
|
+
value
|
|
809
|
+
]));
|
|
810
|
+
for (const value of previous){
|
|
811
|
+
if (nextById.has(value.id)) continue;
|
|
812
|
+
const current = records.get(value.id);
|
|
813
|
+
if (!(current instanceof __rspack_external_yjs.Map)) {
|
|
814
|
+
if (void 0 === current) continue;
|
|
815
|
+
invalidWorkOfficeSpreadsheetShared(label);
|
|
816
|
+
}
|
|
817
|
+
if (!workOfficeCollaborationJsonEqual(value, readJsonMap(current, label))) throw staleConflict(`${label} '${value.id}' changed before it was deleted`);
|
|
818
|
+
records.delete(value.id);
|
|
819
|
+
removeAll(order, value.id);
|
|
820
|
+
}
|
|
821
|
+
for (const value of next){
|
|
822
|
+
const before = previousById.get(value.id);
|
|
823
|
+
if (before && workOfficeCollaborationJsonEqual(before, value)) continue;
|
|
824
|
+
let record = records.get(value.id);
|
|
825
|
+
if (void 0 === record) {
|
|
826
|
+
if (before) throw staleConflict(`${label} '${value.id}' was removed before this change`);
|
|
827
|
+
record = new __rspack_external_yjs.Map();
|
|
828
|
+
records.set(value.id, record);
|
|
829
|
+
}
|
|
830
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
831
|
+
patchJsonMap(record, before, value);
|
|
832
|
+
}
|
|
833
|
+
patchSpreadsheetOrder(order, previous.map(({ id })=>id), next.map(({ id })=>id));
|
|
834
|
+
}
|
|
835
|
+
function readOptionalCollection(parent, recordsKey, orderKey, label, outputKey, output) {
|
|
836
|
+
const records = office_spreadsheet_collaboration_records_requiredSpreadsheetMap(parent, recordsKey, label);
|
|
837
|
+
const order = office_spreadsheet_collaboration_records_requiredSpreadsheetArray(parent, orderKey, `${label} order`);
|
|
838
|
+
const result = validatedSpreadsheetOrder(order, records, label).map((id)=>{
|
|
839
|
+
const record = office_spreadsheet_collaboration_records_requiredSpreadsheetMap(records, id, label);
|
|
840
|
+
const value = readJsonMap(record, label);
|
|
841
|
+
if (value.id !== id) invalidWorkOfficeSpreadsheetShared(`${label} identity`);
|
|
842
|
+
return value;
|
|
843
|
+
});
|
|
844
|
+
if (result.length > 0) output[outputKey] = result;
|
|
845
|
+
}
|
|
846
|
+
function patchJsonMap(target, previous, next) {
|
|
847
|
+
patchSpreadsheetFlatJsonMap(target, previous, next, 'Spreadsheet record');
|
|
848
|
+
}
|
|
849
|
+
function readJsonMap(target, label) {
|
|
850
|
+
return readSpreadsheetFlatJsonMap(target, label);
|
|
851
|
+
}
|
|
852
|
+
function patchOptionalJson(target, key, previous, next, hadPrevious) {
|
|
853
|
+
if (hadPrevious && workOfficeCollaborationJsonEqual(previous, next)) return;
|
|
854
|
+
if (void 0 === next) {
|
|
855
|
+
if (hadPrevious && !workOfficeCollaborationJsonEqual(target.get(key), previous)) throw staleConflict(`Field '${key}' changed before it was removed`);
|
|
856
|
+
target.delete(key);
|
|
857
|
+
} else target.set(key, cloneWorkOfficeCollaborationJson(next));
|
|
858
|
+
}
|
|
859
|
+
function office_spreadsheet_collaboration_records_nestedSpreadsheetArray(parent, key, label) {
|
|
860
|
+
const existing = parent.get(key);
|
|
861
|
+
if (existing instanceof __rspack_external_yjs.Array) return existing;
|
|
862
|
+
if (void 0 !== existing) invalidWorkOfficeSpreadsheetShared(label);
|
|
863
|
+
const value = new __rspack_external_yjs.Array();
|
|
864
|
+
parent.set(key, value);
|
|
865
|
+
return value;
|
|
866
|
+
}
|
|
867
|
+
function office_spreadsheet_collaboration_records_requiredSpreadsheetArray(parent, key, label) {
|
|
868
|
+
const value = parent.get(key);
|
|
869
|
+
if (!(value instanceof __rspack_external_yjs.Array)) invalidWorkOfficeSpreadsheetShared(label);
|
|
870
|
+
return value;
|
|
871
|
+
}
|
|
872
|
+
function removeAll(order, id) {
|
|
873
|
+
for(let index = order.length - 1; index >= 0; index -= 1)if (order.get(index) === id) order.delete(index, 1);
|
|
874
|
+
}
|
|
875
|
+
function insertRelative(order, desired, id) {
|
|
876
|
+
const desiredIndex = desired.indexOf(id);
|
|
877
|
+
for(let index = desiredIndex - 1; index >= 0; index -= 1){
|
|
878
|
+
const sibling = desired[index];
|
|
879
|
+
const position = order.toArray().lastIndexOf(sibling);
|
|
880
|
+
if (position >= 0) return void order.insert(position + 1, [
|
|
881
|
+
id
|
|
882
|
+
]);
|
|
883
|
+
}
|
|
884
|
+
for(let index = desiredIndex + 1; index < desired.length; index += 1){
|
|
885
|
+
const sibling = desired[index];
|
|
886
|
+
const position = order.toArray().indexOf(sibling);
|
|
887
|
+
if (position >= 0) return void order.insert(position, [
|
|
888
|
+
id
|
|
889
|
+
]);
|
|
890
|
+
}
|
|
891
|
+
order.push([
|
|
892
|
+
id
|
|
893
|
+
]);
|
|
894
|
+
}
|
|
895
|
+
function sameRelativeOrder(previous, next) {
|
|
896
|
+
const nextIds = new Set(next);
|
|
897
|
+
const previousIds = new Set(previous);
|
|
898
|
+
return workOfficeCollaborationJsonEqual(previous.filter((id)=>nextIds.has(id)), next.filter((id)=>previousIds.has(id)));
|
|
899
|
+
}
|
|
900
|
+
function staleConflict(message) {
|
|
901
|
+
return new WorkOfficeCollaborationError('office.collaboration.content_invalid', `${message}. Refresh the shared snapshot before retrying.`);
|
|
902
|
+
}
|
|
903
|
+
const SPREADSHEET_OPTIONS_ROOT = 'spreadsheet.options';
|
|
904
|
+
const SPREADSHEET_SHEETS_ROOT = 'spreadsheet.sheets';
|
|
905
|
+
const SPREADSHEET_SHEET_ORDER_ROOT = 'spreadsheet.sheet-order';
|
|
906
|
+
const SPREADSHEET_NAMED_RANGES_ROOT = 'spreadsheet.named-ranges';
|
|
907
|
+
const SPREADSHEET_NAMED_RANGE_ORDER_ROOT = 'spreadsheet.named-range-order';
|
|
908
|
+
const SPREADSHEET_RECORD_CLAIMS_ROOT = 'spreadsheet.record-claims';
|
|
909
|
+
const SPREADSHEET_PRINT_AREAS_ROOT = 'spreadsheet.print-areas';
|
|
910
|
+
const SPREADSHEET_PRINT_TITLES_ROOT = 'spreadsheet.print-titles';
|
|
911
|
+
const SPREADSHEET_PAGE_BREAKS_ROOT = 'spreadsheet.page-breaks';
|
|
912
|
+
const SPREADSHEET_PAGE_SETUPS_ROOT = 'spreadsheet.page-setups';
|
|
913
|
+
function workOfficeSpreadsheetRoots(document, rootName) {
|
|
914
|
+
return {
|
|
915
|
+
options: document.getMap(rootName(SPREADSHEET_OPTIONS_ROOT)),
|
|
916
|
+
sheets: document.getMap(rootName(SPREADSHEET_SHEETS_ROOT)),
|
|
917
|
+
sheetOrder: document.getArray(rootName(SPREADSHEET_SHEET_ORDER_ROOT)),
|
|
918
|
+
namedRanges: document.getMap(rootName(SPREADSHEET_NAMED_RANGES_ROOT)),
|
|
919
|
+
namedRangeOrder: document.getArray(rootName(SPREADSHEET_NAMED_RANGE_ORDER_ROOT)),
|
|
920
|
+
recordClaims: document.getArray(rootName(SPREADSHEET_RECORD_CLAIMS_ROOT)),
|
|
921
|
+
printAreas: document.getMap(rootName(SPREADSHEET_PRINT_AREAS_ROOT)),
|
|
922
|
+
printTitles: document.getMap(rootName(SPREADSHEET_PRINT_TITLES_ROOT)),
|
|
923
|
+
pageBreaks: document.getMap(rootName(SPREADSHEET_PAGE_BREAKS_ROOT)),
|
|
924
|
+
pageSetups: document.getMap(rootName(SPREADSHEET_PAGE_SETUPS_ROOT))
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function initializeWorkOfficeSpreadsheetRoots(roots, content) {
|
|
928
|
+
appendWorkOfficeSpreadsheetRecordClaims(roots.recordClaims, void 0, content);
|
|
929
|
+
patchSpreadsheetFlatJsonMap(roots.options, void 0, content.calculation, 'calculation settings');
|
|
930
|
+
patchSheets(roots, void 0, content);
|
|
931
|
+
patchIdRecords(roots.namedRanges, roots.namedRangeOrder, [], content.namedRanges ?? [], 'named range');
|
|
932
|
+
patchSheetSidecars(roots.printAreas, [], content.printAreas ?? [], 'print area');
|
|
933
|
+
patchSheetSidecars(roots.printTitles, [], content.printTitles ?? [], 'print titles');
|
|
934
|
+
patchSheetSidecars(roots.pageBreaks, [], content.pageBreaks ?? [], 'page breaks');
|
|
935
|
+
patchSheetSidecars(roots.pageSetups, [], content.pageSetups ?? [], 'page setup');
|
|
936
|
+
}
|
|
937
|
+
function readWorkOfficeSpreadsheetRoots(roots) {
|
|
938
|
+
const result = {
|
|
939
|
+
type: 'spreadsheet',
|
|
940
|
+
sheets: validatedSpreadsheetOrder(roots.sheetOrder, roots.sheets, 'sheet').map((id)=>readSpreadsheetSheetRecord(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(roots.sheets, id, `sheet '${id}'`), id))
|
|
941
|
+
};
|
|
942
|
+
const calculation = readSpreadsheetFlatJsonMap(roots.options, 'calculation settings');
|
|
943
|
+
if (Object.keys(calculation).length > 0) result.calculation = calculation;
|
|
944
|
+
const namedRanges = readIdRecords(roots.namedRanges, roots.namedRangeOrder, 'named range');
|
|
945
|
+
if (namedRanges.length > 0) result.namedRanges = namedRanges;
|
|
946
|
+
readSheetSidecars(roots.printAreas, 'printAreas', 'print area', result);
|
|
947
|
+
readSheetSidecars(roots.printTitles, 'printTitles', 'print titles', result);
|
|
948
|
+
readSheetSidecars(roots.pageBreaks, 'pageBreaks', 'page breaks', result);
|
|
949
|
+
readSheetSidecars(roots.pageSetups, 'pageSetups', 'page setup', result);
|
|
950
|
+
const validated = validateSharedWorkOfficeSpreadsheetContent(result);
|
|
951
|
+
assertWorkOfficeSpreadsheetRecordClaims(roots.recordClaims, validated);
|
|
952
|
+
return validated;
|
|
953
|
+
}
|
|
954
|
+
function patchWorkOfficeSpreadsheetRoots(roots, previous, next) {
|
|
955
|
+
const shared = readWorkOfficeSpreadsheetRoots(roots);
|
|
956
|
+
assertWorkOfficeSpreadsheetPatchSafe(previous, next, shared);
|
|
957
|
+
appendWorkOfficeSpreadsheetRecordClaims(roots.recordClaims, previous, next);
|
|
958
|
+
patchSpreadsheetFlatJsonMap(roots.options, previous.calculation, next.calculation, 'calculation settings');
|
|
959
|
+
patchSheets(roots, previous, next);
|
|
960
|
+
patchIdRecords(roots.namedRanges, roots.namedRangeOrder, previous.namedRanges ?? [], next.namedRanges ?? [], 'named range');
|
|
961
|
+
patchSheetSidecars(roots.printAreas, previous.printAreas ?? [], next.printAreas ?? [], 'print area');
|
|
962
|
+
patchSheetSidecars(roots.printTitles, previous.printTitles ?? [], next.printTitles ?? [], 'print titles');
|
|
963
|
+
patchSheetSidecars(roots.pageBreaks, previous.pageBreaks ?? [], next.pageBreaks ?? [], 'page breaks');
|
|
964
|
+
patchSheetSidecars(roots.pageSetups, previous.pageSetups ?? [], next.pageSetups ?? [], 'page setup');
|
|
965
|
+
}
|
|
966
|
+
function assertWorkOfficeSpreadsheetRootsEmpty(roots) {
|
|
967
|
+
if (roots.options.size > 0 || roots.sheets.size > 0 || roots.sheetOrder.length > 0 || roots.namedRanges.size > 0 || roots.namedRangeOrder.length > 0 || roots.recordClaims.length > 0 || roots.printAreas.size > 0 || roots.printTitles.size > 0 || roots.pageBreaks.size > 0 || roots.pageSetups.size > 0) throw new WorkOfficeCollaborationError('office.collaboration.bootstrap_ambiguous', 'The Spreadsheet collaboration roots contain data without initialized metadata.');
|
|
968
|
+
}
|
|
969
|
+
function workOfficeSpreadsheetUndoScope(roots) {
|
|
970
|
+
const scope = [
|
|
971
|
+
roots.options,
|
|
972
|
+
roots.sheets,
|
|
973
|
+
roots.sheetOrder,
|
|
974
|
+
roots.namedRanges,
|
|
975
|
+
roots.namedRangeOrder,
|
|
976
|
+
roots.recordClaims,
|
|
977
|
+
roots.printAreas,
|
|
978
|
+
roots.printTitles,
|
|
979
|
+
roots.pageBreaks,
|
|
980
|
+
roots.pageSetups
|
|
981
|
+
];
|
|
982
|
+
return scope;
|
|
983
|
+
}
|
|
984
|
+
function patchSheets(roots, previous, next) {
|
|
985
|
+
const before = previous?.sheets ?? [];
|
|
986
|
+
const beforeById = new Map(before.map((sheet)=>[
|
|
987
|
+
sheet.id,
|
|
988
|
+
sheet
|
|
989
|
+
]));
|
|
990
|
+
const afterById = new Map(next.sheets.map((sheet)=>[
|
|
991
|
+
sheet.id,
|
|
992
|
+
sheet
|
|
993
|
+
]));
|
|
994
|
+
for (const sheet of before){
|
|
995
|
+
const id = sheet.id;
|
|
996
|
+
if (afterById.has(id)) continue;
|
|
997
|
+
const current = roots.sheets.get(id);
|
|
998
|
+
if (void 0 !== current) {
|
|
999
|
+
if (!(current instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(`sheet '${id}'`);
|
|
1000
|
+
if (!workOfficeCollaborationJsonEqual(sheet, readSpreadsheetSheetRecord(current, id))) throw office_spreadsheet_collaboration_model_staleConflict(`Sheet '${id}' changed before it was deleted`);
|
|
1001
|
+
roots.sheets.delete(id);
|
|
1002
|
+
office_spreadsheet_collaboration_model_removeAll(roots.sheetOrder, id);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
for (const sheet of next.sheets){
|
|
1006
|
+
const id = sheet.id;
|
|
1007
|
+
const beforeSheet = beforeById.get(id);
|
|
1008
|
+
if (beforeSheet && workOfficeCollaborationJsonEqual(beforeSheet, sheet)) continue;
|
|
1009
|
+
let record = roots.sheets.get(id);
|
|
1010
|
+
if (void 0 === record) {
|
|
1011
|
+
if (beforeSheet) throw office_spreadsheet_collaboration_model_staleConflict(`Sheet '${id}' was removed before this change`);
|
|
1012
|
+
record = new __rspack_external_yjs.Map();
|
|
1013
|
+
roots.sheets.set(id, record);
|
|
1014
|
+
initializeSpreadsheetSheetRecord(record, sheet);
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(`sheet '${id}'`);
|
|
1018
|
+
patchSpreadsheetSheetRecord(record, beforeSheet, sheet);
|
|
1019
|
+
}
|
|
1020
|
+
patchSpreadsheetOrder(roots.sheetOrder, before.map((sheet)=>sheet.id), next.sheets.map((sheet)=>sheet.id));
|
|
1021
|
+
}
|
|
1022
|
+
function patchIdRecords(records, order, previous, next, label) {
|
|
1023
|
+
const beforeById = new Map(previous.map((value)=>[
|
|
1024
|
+
value.id,
|
|
1025
|
+
value
|
|
1026
|
+
]));
|
|
1027
|
+
const afterById = new Map(next.map((value)=>[
|
|
1028
|
+
value.id,
|
|
1029
|
+
value
|
|
1030
|
+
]));
|
|
1031
|
+
for (const value of previous){
|
|
1032
|
+
if (afterById.has(value.id)) continue;
|
|
1033
|
+
const current = records.get(value.id);
|
|
1034
|
+
if (void 0 !== current) {
|
|
1035
|
+
if (!(current instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
1036
|
+
if (!workOfficeCollaborationJsonEqual(value, readSpreadsheetFlatJsonMap(current, label))) throw office_spreadsheet_collaboration_model_staleConflict(`${label} '${value.id}' changed before it was deleted`);
|
|
1037
|
+
records.delete(value.id);
|
|
1038
|
+
office_spreadsheet_collaboration_model_removeAll(order, value.id);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
for (const value of next){
|
|
1042
|
+
const before = beforeById.get(value.id);
|
|
1043
|
+
if (before && workOfficeCollaborationJsonEqual(before, value)) continue;
|
|
1044
|
+
let record = records.get(value.id);
|
|
1045
|
+
if (void 0 === record) {
|
|
1046
|
+
if (before) throw office_spreadsheet_collaboration_model_staleConflict(`${label} '${value.id}' was removed before this change`);
|
|
1047
|
+
record = new __rspack_external_yjs.Map();
|
|
1048
|
+
records.set(value.id, record);
|
|
1049
|
+
}
|
|
1050
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
1051
|
+
patchSpreadsheetFlatJsonMap(record, before, value, label);
|
|
1052
|
+
}
|
|
1053
|
+
patchSpreadsheetOrder(order, previous.map(({ id })=>id), next.map(({ id })=>id));
|
|
1054
|
+
}
|
|
1055
|
+
function readIdRecords(records, order, label) {
|
|
1056
|
+
return validatedSpreadsheetOrder(order, records, label).map((id)=>{
|
|
1057
|
+
const value = readSpreadsheetFlatJsonMap(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(records, id, label), label);
|
|
1058
|
+
if (value.id !== id) invalidWorkOfficeSpreadsheetShared(`${label} identity`);
|
|
1059
|
+
return value;
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
function patchSheetSidecars(records, previous, next, label) {
|
|
1063
|
+
const beforeBySheet = new Map(previous.map((value)=>[
|
|
1064
|
+
value.sheetId,
|
|
1065
|
+
value
|
|
1066
|
+
]));
|
|
1067
|
+
const afterBySheet = new Map(next.map((value)=>[
|
|
1068
|
+
value.sheetId,
|
|
1069
|
+
value
|
|
1070
|
+
]));
|
|
1071
|
+
for (const value of previous){
|
|
1072
|
+
if (afterBySheet.has(value.sheetId)) continue;
|
|
1073
|
+
const current = records.get(value.sheetId);
|
|
1074
|
+
if (void 0 !== current) {
|
|
1075
|
+
if (!(current instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
1076
|
+
if (!workOfficeCollaborationJsonEqual(value, readSpreadsheetFlatJsonMap(current, label))) throw office_spreadsheet_collaboration_model_staleConflict(`${label} for sheet '${value.sheetId}' changed before deletion`);
|
|
1077
|
+
records.delete(value.sheetId);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
for (const value of next){
|
|
1081
|
+
const before = beforeBySheet.get(value.sheetId);
|
|
1082
|
+
if (before && workOfficeCollaborationJsonEqual(before, value)) continue;
|
|
1083
|
+
let record = records.get(value.sheetId);
|
|
1084
|
+
if (void 0 === record) {
|
|
1085
|
+
if (before) throw office_spreadsheet_collaboration_model_staleConflict(`${label} for sheet '${value.sheetId}' was removed`);
|
|
1086
|
+
record = new __rspack_external_yjs.Map();
|
|
1087
|
+
records.set(value.sheetId, record);
|
|
1088
|
+
}
|
|
1089
|
+
if (!(record instanceof __rspack_external_yjs.Map)) invalidWorkOfficeSpreadsheetShared(label);
|
|
1090
|
+
patchSpreadsheetFlatJsonMap(record, before, value, label);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function readSheetSidecars(records, key, label, output) {
|
|
1094
|
+
const result = Array.from(records.keys()).sort().map((sheetId)=>{
|
|
1095
|
+
const value = readSpreadsheetFlatJsonMap(office_spreadsheet_collaboration_records_requiredSpreadsheetMap(records, sheetId, label), label);
|
|
1096
|
+
if (value.sheetId !== sheetId) invalidWorkOfficeSpreadsheetShared(`${label} identity`);
|
|
1097
|
+
return value;
|
|
1098
|
+
});
|
|
1099
|
+
if (result.length > 0) output[key] = result;
|
|
1100
|
+
}
|
|
1101
|
+
function office_spreadsheet_collaboration_model_removeAll(order, id) {
|
|
1102
|
+
for(let index = order.length - 1; index >= 0; index -= 1)if (order.get(index) === id) order.delete(index, 1);
|
|
1103
|
+
}
|
|
1104
|
+
function office_spreadsheet_collaboration_model_staleConflict(message) {
|
|
1105
|
+
return new WorkOfficeCollaborationError('office.collaboration.content_invalid', `${message}. Refresh the shared snapshot before retrying.`);
|
|
1106
|
+
}
|
|
1107
|
+
function initializeWorkOfficeSpreadsheetCollaboration(session, content) {
|
|
1108
|
+
assertSpreadsheetSession(session);
|
|
1109
|
+
assertWorkOfficeCollaborationEditable(session);
|
|
1110
|
+
const prepared = validateWorkOfficeSpreadsheetContent(content);
|
|
1111
|
+
const existing = readWorkOfficeCollaborationMetadata(session);
|
|
1112
|
+
if (existing?.initialized) return {
|
|
1113
|
+
initialized: false,
|
|
1114
|
+
content: readWorkOfficeSpreadsheetCollaboration(session)
|
|
1115
|
+
};
|
|
1116
|
+
const roots = spreadsheetRoots(session);
|
|
1117
|
+
assertWorkOfficeSpreadsheetRootsEmpty(roots);
|
|
1118
|
+
const origin = session.createOrigin('bootstrap');
|
|
1119
|
+
session.transact(()=>{
|
|
1120
|
+
const metadata = initializeWorkOfficeCollaborationMetadata(session);
|
|
1121
|
+
if (metadata.initialized) return;
|
|
1122
|
+
registerWorkOfficeCollaborationInitializer(session);
|
|
1123
|
+
initializeWorkOfficeSpreadsheetRoots(roots, prepared);
|
|
1124
|
+
markWorkOfficeCollaborationInitialized(session);
|
|
1125
|
+
}, origin);
|
|
1126
|
+
return {
|
|
1127
|
+
initialized: true,
|
|
1128
|
+
content: readWorkOfficeSpreadsheetCollaboration(session)
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
function readWorkOfficeSpreadsheetCollaboration(session) {
|
|
1132
|
+
assertInitializedSpreadsheetSession(session);
|
|
1133
|
+
return readWorkOfficeSpreadsheetRoots(spreadsheetRoots(session));
|
|
1134
|
+
}
|
|
1135
|
+
function replaceWorkOfficeSpreadsheetCollaboration(session, previous, next, origin = session.localOrigin) {
|
|
1136
|
+
assertInitializedSpreadsheetSession(session);
|
|
1137
|
+
assertWorkOfficeCollaborationEditable(session);
|
|
1138
|
+
const before = validateWorkOfficeSpreadsheetContent(previous);
|
|
1139
|
+
const after = validateWorkOfficeSpreadsheetContent(next);
|
|
1140
|
+
if (workOfficeCollaborationJsonEqual(before, after)) return false;
|
|
1141
|
+
session.transact(()=>{
|
|
1142
|
+
patchWorkOfficeSpreadsheetRoots(spreadsheetRoots(session), before, after);
|
|
1143
|
+
}, origin);
|
|
1144
|
+
return true;
|
|
1145
|
+
}
|
|
1146
|
+
function createWorkOfficeSpreadsheetCollaborationBinding(session, options = {}) {
|
|
1147
|
+
assertInitializedSpreadsheetSession(session);
|
|
1148
|
+
return new WorkOfficeSpreadsheetCollaborationBindingImpl(session, options);
|
|
1149
|
+
}
|
|
1150
|
+
class WorkOfficeSpreadsheetCollaborationBindingImpl {
|
|
1151
|
+
origin;
|
|
1152
|
+
#session;
|
|
1153
|
+
#undoManager;
|
|
1154
|
+
#roots;
|
|
1155
|
+
#undoScope;
|
|
1156
|
+
#listeners = new Set();
|
|
1157
|
+
#errorListeners = new Set();
|
|
1158
|
+
#historyListeners = new Set();
|
|
1159
|
+
#destroyed = false;
|
|
1160
|
+
#pendingChange = false;
|
|
1161
|
+
#pendingLocal = true;
|
|
1162
|
+
#pendingOrigin;
|
|
1163
|
+
constructor(session, options){
|
|
1164
|
+
this.#session = session;
|
|
1165
|
+
this.#roots = spreadsheetRoots(session);
|
|
1166
|
+
this.origin = options.origin ?? session.createOrigin(session.localOrigin.kind);
|
|
1167
|
+
assertWorkOfficeCollaborationOrigin(this.origin);
|
|
1168
|
+
this.#undoScope = workOfficeSpreadsheetUndoScope(this.#roots);
|
|
1169
|
+
this.#undoManager = new __rspack_external_yjs.UndoManager(this.#undoScope, {
|
|
1170
|
+
captureTimeout: options.captureTimeoutMs ?? 500,
|
|
1171
|
+
trackedOrigins: new Set([
|
|
1172
|
+
this.origin
|
|
1173
|
+
])
|
|
1174
|
+
});
|
|
1175
|
+
session.document.on('afterTransaction', this.#onTransaction);
|
|
1176
|
+
this.#undoManager.on('stack-item-added', this.#onHistoryChange);
|
|
1177
|
+
this.#undoManager.on('stack-item-popped', this.#onHistoryChange);
|
|
1178
|
+
this.#undoManager.on('stack-item-updated', this.#onHistoryChange);
|
|
1179
|
+
}
|
|
1180
|
+
content() {
|
|
1181
|
+
this.ensureActive();
|
|
1182
|
+
return readWorkOfficeSpreadsheetRoots(this.#roots);
|
|
1183
|
+
}
|
|
1184
|
+
replace(previous, next) {
|
|
1185
|
+
this.ensureActive();
|
|
1186
|
+
return replaceWorkOfficeSpreadsheetCollaboration(this.#session, previous, next, this.origin);
|
|
1187
|
+
}
|
|
1188
|
+
canUndo() {
|
|
1189
|
+
this.ensureActive();
|
|
1190
|
+
return this.#undoManager.canUndo();
|
|
1191
|
+
}
|
|
1192
|
+
canRedo() {
|
|
1193
|
+
this.ensureActive();
|
|
1194
|
+
return this.#undoManager.canRedo();
|
|
1195
|
+
}
|
|
1196
|
+
undo() {
|
|
1197
|
+
this.ensureActive();
|
|
1198
|
+
assertWorkOfficeCollaborationEditable(this.#session);
|
|
1199
|
+
return null !== this.#undoManager.undo();
|
|
1200
|
+
}
|
|
1201
|
+
redo() {
|
|
1202
|
+
this.ensureActive();
|
|
1203
|
+
assertWorkOfficeCollaborationEditable(this.#session);
|
|
1204
|
+
return null !== this.#undoManager.redo();
|
|
1205
|
+
}
|
|
1206
|
+
stopCapturing() {
|
|
1207
|
+
this.ensureActive();
|
|
1208
|
+
this.#undoManager.stopCapturing();
|
|
1209
|
+
}
|
|
1210
|
+
subscribe(listener) {
|
|
1211
|
+
this.ensureActive();
|
|
1212
|
+
this.#listeners.add(listener);
|
|
1213
|
+
return ()=>this.#listeners.delete(listener);
|
|
1214
|
+
}
|
|
1215
|
+
subscribeError(listener) {
|
|
1216
|
+
this.ensureActive();
|
|
1217
|
+
this.#errorListeners.add(listener);
|
|
1218
|
+
return ()=>this.#errorListeners.delete(listener);
|
|
1219
|
+
}
|
|
1220
|
+
subscribeHistory(listener) {
|
|
1221
|
+
this.ensureActive();
|
|
1222
|
+
this.#historyListeners.add(listener);
|
|
1223
|
+
return ()=>this.#historyListeners.delete(listener);
|
|
1224
|
+
}
|
|
1225
|
+
destroy() {
|
|
1226
|
+
if (this.#destroyed) return;
|
|
1227
|
+
this.#destroyed = true;
|
|
1228
|
+
this.#pendingChange = false;
|
|
1229
|
+
this.#session.document.off('afterTransaction', this.#onTransaction);
|
|
1230
|
+
this.#undoManager.off('stack-item-added', this.#onHistoryChange);
|
|
1231
|
+
this.#undoManager.off('stack-item-popped', this.#onHistoryChange);
|
|
1232
|
+
this.#undoManager.off('stack-item-updated', this.#onHistoryChange);
|
|
1233
|
+
this.#undoManager.destroy();
|
|
1234
|
+
this.#listeners.clear();
|
|
1235
|
+
this.#errorListeners.clear();
|
|
1236
|
+
this.#historyListeners.clear();
|
|
1237
|
+
}
|
|
1238
|
+
#onTransaction = (transaction)=>{
|
|
1239
|
+
const changedParents = new Set(transaction.changedParentTypes.keys());
|
|
1240
|
+
if (this.#destroyed || !this.#undoScope.some((root)=>changedParents.has(root))) return;
|
|
1241
|
+
const local = transaction.origin === this.origin || transaction.origin === this.#undoManager;
|
|
1242
|
+
this.#pendingLocal = this.#pendingChange ? this.#pendingLocal && local : local;
|
|
1243
|
+
this.#pendingOrigin = transaction.origin;
|
|
1244
|
+
if (this.#pendingChange) return;
|
|
1245
|
+
this.#pendingChange = true;
|
|
1246
|
+
queueMicrotask(this.#flushChange);
|
|
1247
|
+
};
|
|
1248
|
+
#flushChange = ()=>{
|
|
1249
|
+
if (this.#destroyed || !this.#pendingChange) return;
|
|
1250
|
+
const local = this.#pendingLocal;
|
|
1251
|
+
const origin = this.#pendingOrigin;
|
|
1252
|
+
this.#pendingChange = false;
|
|
1253
|
+
this.#pendingLocal = true;
|
|
1254
|
+
this.#pendingOrigin = void 0;
|
|
1255
|
+
try {
|
|
1256
|
+
const change = {
|
|
1257
|
+
content: this.content(),
|
|
1258
|
+
local,
|
|
1259
|
+
origin
|
|
1260
|
+
};
|
|
1261
|
+
for (const listener of this.#listeners)listener(change);
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
for (const listener of this.#errorListeners)listener(error);
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
#onHistoryChange = ()=>{
|
|
1267
|
+
for (const listener of this.#historyListeners)listener();
|
|
1268
|
+
};
|
|
1269
|
+
ensureActive() {
|
|
1270
|
+
if (!this.#destroyed) return;
|
|
1271
|
+
throw new WorkOfficeCollaborationError('office.collaboration.binding_destroyed', 'The Spreadsheet collaboration binding has been destroyed.');
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function spreadsheetRoots(session) {
|
|
1275
|
+
return workOfficeSpreadsheetRoots(session.document, session.rootName.bind(session));
|
|
1276
|
+
}
|
|
1277
|
+
function assertSpreadsheetSession(session) {
|
|
1278
|
+
if ('spreadsheet' === session.kind) return;
|
|
1279
|
+
throw new WorkOfficeCollaborationError('office.collaboration.kind_mismatch', `A Spreadsheet collaboration binding cannot use a '${session.kind}' session.`);
|
|
1280
|
+
}
|
|
1281
|
+
function assertInitializedSpreadsheetSession(session) {
|
|
1282
|
+
assertSpreadsheetSession(session);
|
|
1283
|
+
const metadata = readWorkOfficeCollaborationMetadata(session);
|
|
1284
|
+
if (metadata?.initialized) return;
|
|
1285
|
+
throw new WorkOfficeCollaborationError('office.collaboration.not_initialized', 'The Spreadsheet collaboration session has not been initialized.');
|
|
1286
|
+
}
|
|
1287
|
+
const DEFAULT_PROTECTION_HINT = '此工作表已受保护。若要更改锁定的单元格,请先取消工作表保护。';
|
|
1288
|
+
function defaultSheetProtectionAuthority(enabled = false) {
|
|
1289
|
+
return {
|
|
1290
|
+
sheet: enabled ? 1 : 0,
|
|
1291
|
+
selectLockedCells: 1,
|
|
1292
|
+
selectunLockedCells: 1,
|
|
1293
|
+
formatCells: 0,
|
|
1294
|
+
formatColumns: 0,
|
|
1295
|
+
formatRows: 0,
|
|
1296
|
+
insertColumns: 0,
|
|
1297
|
+
insertRows: 0,
|
|
1298
|
+
insertHyperlinks: 0,
|
|
1299
|
+
deleteColumns: 0,
|
|
1300
|
+
deleteRows: 0,
|
|
1301
|
+
sort: 0,
|
|
1302
|
+
filter: 0,
|
|
1303
|
+
usePivotTablereports: 0,
|
|
1304
|
+
editObjects: 0,
|
|
1305
|
+
editScenarios: 0,
|
|
1306
|
+
hintText: '',
|
|
1307
|
+
defaultSheetHintText: DEFAULT_PROTECTION_HINT,
|
|
1308
|
+
allowRangeList: []
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
function sheetProtectionAuthority(sheet) {
|
|
1312
|
+
return normalizeSheetProtectionAuthority(sheet.config?.authority);
|
|
1313
|
+
}
|
|
1314
|
+
function normalizeSheetProtectionAuthority(source) {
|
|
1315
|
+
const defaults = defaultSheetProtectionAuthority();
|
|
1316
|
+
if (!source || 'object' != typeof source) return defaults;
|
|
1317
|
+
const authority = source;
|
|
1318
|
+
return {
|
|
1319
|
+
sheet: flag(authority.sheet, defaults.sheet),
|
|
1320
|
+
selectLockedCells: flag(authority.selectLockedCells, defaults.selectLockedCells),
|
|
1321
|
+
selectunLockedCells: flag(authority.selectunLockedCells, defaults.selectunLockedCells),
|
|
1322
|
+
formatCells: flag(authority.formatCells, defaults.formatCells),
|
|
1323
|
+
formatColumns: flag(authority.formatColumns, defaults.formatColumns),
|
|
1324
|
+
formatRows: flag(authority.formatRows, defaults.formatRows),
|
|
1325
|
+
insertColumns: flag(authority.insertColumns, defaults.insertColumns),
|
|
1326
|
+
insertRows: flag(authority.insertRows, defaults.insertRows),
|
|
1327
|
+
insertHyperlinks: flag(authority.insertHyperlinks, defaults.insertHyperlinks),
|
|
1328
|
+
deleteColumns: flag(authority.deleteColumns, defaults.deleteColumns),
|
|
1329
|
+
deleteRows: flag(authority.deleteRows, defaults.deleteRows),
|
|
1330
|
+
sort: flag(authority.sort, defaults.sort),
|
|
1331
|
+
filter: flag(authority.filter, defaults.filter),
|
|
1332
|
+
usePivotTablereports: flag(authority.usePivotTablereports, defaults.usePivotTablereports),
|
|
1333
|
+
editObjects: flag(authority.editObjects, defaults.editObjects),
|
|
1334
|
+
editScenarios: flag(authority.editScenarios, defaults.editScenarios),
|
|
1335
|
+
hintText: stringValue(authority.hintText),
|
|
1336
|
+
defaultSheetHintText: stringValue(authority.defaultSheetHintText) || DEFAULT_PROTECTION_HINT,
|
|
1337
|
+
allowRangeList: editableRangeList(authority.allowRangeList),
|
|
1338
|
+
xlsxAttributes: stringRecord(authority.xlsxAttributes)
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
function withSheetProtection(sheet, enabled) {
|
|
1342
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1343
|
+
authority.sheet = enabled ? 1 : 0;
|
|
1344
|
+
return withAuthority(sheet, authority);
|
|
1345
|
+
}
|
|
1346
|
+
function withSheetSelectionPermissions(sheet, permissions) {
|
|
1347
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1348
|
+
if (void 0 !== permissions.selectLockedCells) authority.selectLockedCells = permissions.selectLockedCells ? 1 : 0;
|
|
1349
|
+
if (void 0 !== permissions.selectUnlockedCells) authority.selectunLockedCells = permissions.selectUnlockedCells ? 1 : 0;
|
|
1350
|
+
return withAuthority(sheet, authority);
|
|
1351
|
+
}
|
|
1352
|
+
function withEditableRange(sheet, index, editableRange) {
|
|
1353
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1354
|
+
const nextRange = {
|
|
1355
|
+
name: editableRange.name.trim(),
|
|
1356
|
+
sqref: canonicalRangeReference(editableRange.sqref),
|
|
1357
|
+
hintText: editableRange.hintText?.trim() || void 0,
|
|
1358
|
+
xlsxAttributes: editableRange.xlsxAttributes
|
|
1359
|
+
};
|
|
1360
|
+
if (null !== index && authority.allowRangeList[index]) authority.allowRangeList[index] = nextRange;
|
|
1361
|
+
else authority.allowRangeList.push(nextRange);
|
|
1362
|
+
const ranges = parseSpreadsheetCellRanges(nextRange.sqref) ?? [];
|
|
1363
|
+
return withAuthority(withCellProtection(sheet, ranges, false), authority);
|
|
1364
|
+
}
|
|
1365
|
+
function withoutEditableRange(sheet, index) {
|
|
1366
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1367
|
+
const removed = authority.allowRangeList[index];
|
|
1368
|
+
if (!removed) return sheet;
|
|
1369
|
+
authority.allowRangeList.splice(index, 1);
|
|
1370
|
+
const removedRanges = parseSpreadsheetCellRanges(removed.sqref) ?? [];
|
|
1371
|
+
let next = withCellProtection(sheet, removedRanges, true);
|
|
1372
|
+
for (const editableRange of authority.allowRangeList)if (!editableRangeRequiresCredentials(editableRange)) next = withCellProtection(next, parseSpreadsheetCellRanges(editableRange.sqref) ?? [], false);
|
|
1373
|
+
return withAuthority(next, authority);
|
|
1374
|
+
}
|
|
1375
|
+
function withCellProtection(sheet, ranges, locked, hidden = false) {
|
|
1376
|
+
if (!ranges.length) return sheet;
|
|
1377
|
+
const data = cloneMatrix(sheet.data);
|
|
1378
|
+
let rowCount = Math.max(sheet.row ?? 0, data.length);
|
|
1379
|
+
let columnCount = Math.max(sheet.column ?? 0, ...data.map((row)=>row.length));
|
|
1380
|
+
for (const range of ranges){
|
|
1381
|
+
rowCount = Math.max(rowCount, range.row[1] + 1);
|
|
1382
|
+
columnCount = Math.max(columnCount, range.column[1] + 1);
|
|
1383
|
+
for(let row = range.row[0]; row <= range.row[1]; row += 1){
|
|
1384
|
+
data[row] ??= [];
|
|
1385
|
+
for(let column = range.column[0]; column <= range.column[1]; column += 1){
|
|
1386
|
+
const cell = {
|
|
1387
|
+
...data[row][column] ?? {}
|
|
1388
|
+
};
|
|
1389
|
+
cell.lo = locked ? 1 : 0;
|
|
1390
|
+
if (hidden) cell.hi = 1;
|
|
1391
|
+
else if (void 0 !== cell.hi) delete cell.hi;
|
|
1392
|
+
data[row][column] = cell;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
normalizeMatrix(data, rowCount, columnCount);
|
|
1397
|
+
return {
|
|
1398
|
+
...sheet,
|
|
1399
|
+
row: rowCount,
|
|
1400
|
+
column: columnCount,
|
|
1401
|
+
data
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
function applySpreadsheetCellProtectionRanges(data, ranges, rowCount, columnCount) {
|
|
1405
|
+
for (const item of ranges){
|
|
1406
|
+
const lastRow = Math.min(item.range.row[1], rowCount - 1);
|
|
1407
|
+
const lastColumn = Math.min(item.range.column[1], columnCount - 1);
|
|
1408
|
+
for(let row = Math.max(0, item.range.row[0]); row <= lastRow; row += 1){
|
|
1409
|
+
data[row] ??= [];
|
|
1410
|
+
for(let column = Math.max(0, item.range.column[0]); column <= lastColumn; column += 1){
|
|
1411
|
+
const cell = {
|
|
1412
|
+
...data[row][column] ?? {}
|
|
1413
|
+
};
|
|
1414
|
+
cell.lo = item.locked ? 1 : 0;
|
|
1415
|
+
if (item.hidden) cell.hi = 1;
|
|
1416
|
+
else if (void 0 !== cell.hi) delete cell.hi;
|
|
1417
|
+
data[row][column] = cell;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function applyPasswordlessEditableRanges(data, ranges, rowCount, columnCount) {
|
|
1423
|
+
const protectionRanges = ranges.filter((range)=>!editableRangeRequiresCredentials(range)).flatMap((range)=>parseSpreadsheetCellRanges(range.sqref) ?? []).map((range)=>({
|
|
1424
|
+
range,
|
|
1425
|
+
locked: false,
|
|
1426
|
+
hidden: false
|
|
1427
|
+
}));
|
|
1428
|
+
applySpreadsheetCellProtectionRanges(data, protectionRanges, rowCount, columnCount);
|
|
1429
|
+
}
|
|
1430
|
+
function editableRangeRequiresCredentials(range) {
|
|
1431
|
+
const attributes = range.xlsxAttributes ?? {};
|
|
1432
|
+
return Boolean(attributes.password || attributes.hashValue || attributes.saltValue || attributes.securityDescriptor || attributes.securitydescriptor);
|
|
1433
|
+
}
|
|
1434
|
+
function sheetHasProtectionState(sheet) {
|
|
1435
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1436
|
+
if (1 === authority.sheet || authority.allowRangeList.length || Object.keys(authority.xlsxAttributes ?? {}).length) return true;
|
|
1437
|
+
return (sheet.data ?? []).some((row)=>row.some((cell)=>cell?.lo !== void 0 || cell?.hi !== void 0));
|
|
1438
|
+
}
|
|
1439
|
+
function protectedSheetCount(sheets) {
|
|
1440
|
+
return sheets.filter((sheet)=>1 === sheetProtectionAuthority(sheet).sheet).length;
|
|
1441
|
+
}
|
|
1442
|
+
function unlockedCellCount(sheet) {
|
|
1443
|
+
return (sheet.data ?? []).reduce((total, row)=>total + row.reduce((rowTotal, cell)=>rowTotal + (cell?.lo === 0 ? 1 : 0), 0), 0);
|
|
1444
|
+
}
|
|
1445
|
+
function spreadsheetProtectionKey(sheets) {
|
|
1446
|
+
return sheets.map((sheet)=>{
|
|
1447
|
+
const authority = sheetProtectionAuthority(sheet);
|
|
1448
|
+
const cells = [];
|
|
1449
|
+
for (const [row, values] of (sheet.data ?? []).entries())for (const [column, cell] of values.entries()){
|
|
1450
|
+
const hidden = cell?.hi;
|
|
1451
|
+
if (cell?.lo !== void 0 || void 0 !== hidden) cells.push(`${row}_${column}:${cell?.lo ?? ''}:${hidden ?? ''}`);
|
|
1452
|
+
}
|
|
1453
|
+
return `${sheet.id ?? sheet.name}:${JSON.stringify(authority)}:${cells.join(',')}`;
|
|
1454
|
+
}).join('|');
|
|
1455
|
+
}
|
|
1456
|
+
function editableRangeCellCount(ranges) {
|
|
1457
|
+
return ranges.reduce((total, range)=>total + (range.row[1] - range.row[0] + 1) * (range.column[1] - range.column[0] + 1), 0);
|
|
1458
|
+
}
|
|
1459
|
+
function withAuthority(sheet, authority) {
|
|
1460
|
+
return {
|
|
1461
|
+
...sheet,
|
|
1462
|
+
config: {
|
|
1463
|
+
...sheet.config ?? {},
|
|
1464
|
+
authority
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
function editableRangeList(value) {
|
|
1469
|
+
if (!Array.isArray(value)) return [];
|
|
1470
|
+
return value.flatMap((item, index)=>{
|
|
1471
|
+
if (!item || 'object' != typeof item) return [];
|
|
1472
|
+
const range = item;
|
|
1473
|
+
const sqref = stringValue(range.sqref);
|
|
1474
|
+
if (!sqref || !parseSpreadsheetCellRanges(sqref)) return [];
|
|
1475
|
+
return [
|
|
1476
|
+
{
|
|
1477
|
+
name: stringValue(range.name) || `Range ${index + 1}`,
|
|
1478
|
+
sqref: canonicalRangeReference(sqref),
|
|
1479
|
+
hintText: stringValue(range.hintText) || void 0,
|
|
1480
|
+
xlsxAttributes: stringRecord(range.xlsxAttributes)
|
|
1481
|
+
}
|
|
1482
|
+
];
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
function canonicalRangeReference(value) {
|
|
1486
|
+
const ranges = parseSpreadsheetCellRanges(value);
|
|
1487
|
+
return ranges ? formatSpreadsheetCellRanges(ranges) : value.trim();
|
|
1488
|
+
}
|
|
1489
|
+
function cloneMatrix(source) {
|
|
1490
|
+
return (source ?? []).map((row)=>[
|
|
1491
|
+
...row
|
|
1492
|
+
]);
|
|
1493
|
+
}
|
|
1494
|
+
function normalizeMatrix(data, rows, columns) {
|
|
1495
|
+
while(data.length < rows)data.push([]);
|
|
1496
|
+
for(let index = 0; index < data.length; index += 1){
|
|
1497
|
+
const row = data[index] ?? [];
|
|
1498
|
+
data[index] = row;
|
|
1499
|
+
while(row.length < columns)row.push(null);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
function flag(value, fallback) {
|
|
1503
|
+
if (1 === value || true === value || '1' === value) return 1;
|
|
1504
|
+
if (0 === value || false === value || '0' === value) return 0;
|
|
1505
|
+
return fallback;
|
|
1506
|
+
}
|
|
1507
|
+
function stringValue(value) {
|
|
1508
|
+
return 'string' == typeof value ? value : '';
|
|
1509
|
+
}
|
|
1510
|
+
function stringRecord(value) {
|
|
1511
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return;
|
|
1512
|
+
const entries = Object.entries(value).filter((entry)=>'string' == typeof entry[1]);
|
|
1513
|
+
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
1514
|
+
}
|
|
1515
|
+
const SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS = [
|
|
1516
|
+
'greaterThan',
|
|
1517
|
+
'greaterThanOrEqual',
|
|
1518
|
+
'lessThan',
|
|
1519
|
+
'lessThanOrEqual',
|
|
1520
|
+
'equal',
|
|
1521
|
+
'notEqual',
|
|
1522
|
+
'between',
|
|
1523
|
+
'notBetween'
|
|
1524
|
+
];
|
|
1525
|
+
function isSpreadsheetConditionalComparisonOperator(value) {
|
|
1526
|
+
return SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS.some((operator)=>operator === value);
|
|
1527
|
+
}
|
|
1528
|
+
function spreadsheetConditionalComparisonNeedsUpperValue(operator) {
|
|
1529
|
+
return 'between' === operator || 'notBetween' === operator;
|
|
1530
|
+
}
|
|
1531
|
+
const DEFAULT_DATA_BAR_MIN_LENGTH = 10;
|
|
1532
|
+
const DEFAULT_DATA_BAR_MAX_LENGTH = 90;
|
|
1533
|
+
function defaultSpreadsheetColorScaleThresholds(colorCount) {
|
|
1534
|
+
return 3 === colorCount ? [
|
|
1535
|
+
{
|
|
1536
|
+
type: 'min'
|
|
1537
|
+
},
|
|
1538
|
+
{
|
|
1539
|
+
type: 'percentile',
|
|
1540
|
+
value: 50
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
type: 'max'
|
|
1544
|
+
}
|
|
1545
|
+
] : [
|
|
1546
|
+
{
|
|
1547
|
+
type: 'min'
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
type: 'max'
|
|
1551
|
+
}
|
|
1552
|
+
];
|
|
1553
|
+
}
|
|
1554
|
+
function defaultSpreadsheetDataBarOptions() {
|
|
1555
|
+
return {
|
|
1556
|
+
thresholds: [
|
|
1557
|
+
{
|
|
1558
|
+
type: 'min'
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
type: 'max'
|
|
1562
|
+
}
|
|
1563
|
+
],
|
|
1564
|
+
showValue: true,
|
|
1565
|
+
minLength: DEFAULT_DATA_BAR_MIN_LENGTH,
|
|
1566
|
+
maxLength: DEFAULT_DATA_BAR_MAX_LENGTH
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
function normalizeSpreadsheetConditionalThreshold(value) {
|
|
1570
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return null;
|
|
1571
|
+
const source = value;
|
|
1572
|
+
if (![
|
|
1573
|
+
'min',
|
|
1574
|
+
'max',
|
|
1575
|
+
'num',
|
|
1576
|
+
'percent',
|
|
1577
|
+
'percentile'
|
|
1578
|
+
].includes(String(source.type))) return null;
|
|
1579
|
+
const type = source.type;
|
|
1580
|
+
if ('min' === type || 'max' === type) return {
|
|
1581
|
+
type
|
|
1582
|
+
};
|
|
1583
|
+
if ('number' != typeof source.value || !Number.isFinite(source.value)) return null;
|
|
1584
|
+
return {
|
|
1585
|
+
type,
|
|
1586
|
+
value: source.value
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function normalizeSpreadsheetConditionalVisualOptions(value, expectedThresholdCount, kind) {
|
|
1590
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return null;
|
|
1591
|
+
const source = value;
|
|
1592
|
+
if (!Array.isArray(source.thresholds) || source.thresholds.length !== expectedThresholdCount) return null;
|
|
1593
|
+
const thresholds = source.thresholds.map(normalizeSpreadsheetConditionalThreshold);
|
|
1594
|
+
if (thresholds.some((threshold)=>!threshold)) return null;
|
|
1595
|
+
if ('colorScale' === kind) return {
|
|
1596
|
+
thresholds: thresholds
|
|
1597
|
+
};
|
|
1598
|
+
const minLength = source.minLength ?? DEFAULT_DATA_BAR_MIN_LENGTH;
|
|
1599
|
+
const maxLength = source.maxLength ?? DEFAULT_DATA_BAR_MAX_LENGTH;
|
|
1600
|
+
if ('number' != typeof minLength || !Number.isFinite(minLength) || minLength < 0 || minLength > 100 || 'number' != typeof maxLength || !Number.isFinite(maxLength) || maxLength < 0 || maxLength > 100 || minLength > maxLength) return null;
|
|
1601
|
+
return {
|
|
1602
|
+
thresholds: thresholds,
|
|
1603
|
+
showValue: false !== source.showValue,
|
|
1604
|
+
minLength,
|
|
1605
|
+
maxLength
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function spreadsheetConditionalThresholdValue(threshold, sourceValues) {
|
|
1609
|
+
const values = sourceValues.filter(Number.isFinite).sort((left, right)=>left - right);
|
|
1610
|
+
if (!values.length) return null;
|
|
1611
|
+
const minimum = values[0];
|
|
1612
|
+
const maximum = values.at(-1);
|
|
1613
|
+
if ('min' === threshold.type) return minimum;
|
|
1614
|
+
if ('max' === threshold.type) return maximum;
|
|
1615
|
+
if ('num' === threshold.type) return threshold.value ?? null;
|
|
1616
|
+
const percentage = threshold.value;
|
|
1617
|
+
if (void 0 === percentage || !Number.isFinite(percentage)) return null;
|
|
1618
|
+
if ('percent' === threshold.type) return minimum + (maximum - minimum) * percentage / 100;
|
|
1619
|
+
const position = Math.max(0, Math.min(100, percentage)) / 100 * (values.length - 1);
|
|
1620
|
+
const lower = Math.floor(position);
|
|
1621
|
+
const upper = Math.ceil(position);
|
|
1622
|
+
if (lower === upper) return values[lower];
|
|
1623
|
+
return values[lower] + (values[upper] - values[lower]) * (position - lower);
|
|
1624
|
+
}
|
|
1625
|
+
function spreadsheetConditionalThresholdsEqual(left, right) {
|
|
1626
|
+
return left.length === right.length && left.every((threshold, index)=>threshold.type === right[index]?.type && threshold.value === right[index]?.value);
|
|
1627
|
+
}
|
|
1628
|
+
const SPREADSHEET_CONDITIONAL_ICON_SETS = [
|
|
1629
|
+
{
|
|
1630
|
+
name: '3Arrows',
|
|
1631
|
+
label: '三向彩色箭头',
|
|
1632
|
+
count: 3
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
name: '3ArrowsGray',
|
|
1636
|
+
label: '三向灰色箭头',
|
|
1637
|
+
count: 3
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
name: '3Flags',
|
|
1641
|
+
label: '三色旗帜',
|
|
1642
|
+
count: 3
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
name: '3TrafficLights1',
|
|
1646
|
+
label: '三色交通灯(实心)',
|
|
1647
|
+
count: 3
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
name: '3TrafficLights2',
|
|
1651
|
+
label: '三色交通灯(边框)',
|
|
1652
|
+
count: 3
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
name: '3Signs',
|
|
1656
|
+
label: '三色标志',
|
|
1657
|
+
count: 3
|
|
1658
|
+
},
|
|
1659
|
+
{
|
|
1660
|
+
name: '3Symbols',
|
|
1661
|
+
label: '三色符号(圆形)',
|
|
1662
|
+
count: 3
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
name: '3Symbols2',
|
|
1666
|
+
label: '三色符号',
|
|
1667
|
+
count: 3
|
|
1668
|
+
},
|
|
1669
|
+
{
|
|
1670
|
+
name: '4Arrows',
|
|
1671
|
+
label: '四向彩色箭头',
|
|
1672
|
+
count: 4
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
name: '4ArrowsGray',
|
|
1676
|
+
label: '四向灰色箭头',
|
|
1677
|
+
count: 4
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
name: '4RedToBlack',
|
|
1681
|
+
label: '红到黑圆点',
|
|
1682
|
+
count: 4
|
|
1683
|
+
},
|
|
1684
|
+
{
|
|
1685
|
+
name: '4Rating',
|
|
1686
|
+
label: '四级评分',
|
|
1687
|
+
count: 4
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
name: '4TrafficLights',
|
|
1691
|
+
label: '四色交通灯',
|
|
1692
|
+
count: 4
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
name: '5Arrows',
|
|
1696
|
+
label: '五向彩色箭头',
|
|
1697
|
+
count: 5
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
name: '5ArrowsGray',
|
|
1701
|
+
label: '五向灰色箭头',
|
|
1702
|
+
count: 5
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
name: '5Rating',
|
|
1706
|
+
label: '五级评分',
|
|
1707
|
+
count: 5
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
name: '5Quarters',
|
|
1711
|
+
label: '五级圆饼',
|
|
1712
|
+
count: 5
|
|
1713
|
+
}
|
|
1714
|
+
];
|
|
1715
|
+
const COLOR_3 = [
|
|
1716
|
+
'#c62828',
|
|
1717
|
+
'#f9a825',
|
|
1718
|
+
'#2e7d32'
|
|
1719
|
+
];
|
|
1720
|
+
const COLOR_4 = [
|
|
1721
|
+
'#c62828',
|
|
1722
|
+
'#ef6c00',
|
|
1723
|
+
'#7cb342',
|
|
1724
|
+
'#2e7d32'
|
|
1725
|
+
];
|
|
1726
|
+
const COLOR_5 = [
|
|
1727
|
+
'#c62828',
|
|
1728
|
+
'#ef6c00',
|
|
1729
|
+
'#f9a825',
|
|
1730
|
+
'#7cb342',
|
|
1731
|
+
'#2e7d32'
|
|
1732
|
+
];
|
|
1733
|
+
function isSpreadsheetConditionalIconSetName(value) {
|
|
1734
|
+
return SPREADSHEET_CONDITIONAL_ICON_SETS.some((item)=>item.name === value);
|
|
1735
|
+
}
|
|
1736
|
+
function spreadsheetConditionalIconSetCount(iconSet) {
|
|
1737
|
+
return SPREADSHEET_CONDITIONAL_ICON_SETS.find((item)=>item.name === iconSet).count;
|
|
1738
|
+
}
|
|
1739
|
+
function defaultSpreadsheetConditionalIconThresholds(iconSet) {
|
|
1740
|
+
const count = spreadsheetConditionalIconSetCount(iconSet);
|
|
1741
|
+
return Array.from({
|
|
1742
|
+
length: count
|
|
1743
|
+
}, (_, index)=>0 === index ? {
|
|
1744
|
+
type: 'min',
|
|
1745
|
+
gte: true
|
|
1746
|
+
} : {
|
|
1747
|
+
type: 'percent',
|
|
1748
|
+
value: Math.round(100 * index / count),
|
|
1749
|
+
gte: true
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
function normalizeSpreadsheetConditionalIconSetFormat(value) {
|
|
1753
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return null;
|
|
1754
|
+
const source = value;
|
|
1755
|
+
if (!isSpreadsheetConditionalIconSetName(source.iconSet) || !Array.isArray(source.thresholds)) return null;
|
|
1756
|
+
const count = spreadsheetConditionalIconSetCount(source.iconSet);
|
|
1757
|
+
if (source.thresholds.length !== count) return null;
|
|
1758
|
+
const thresholds = source.thresholds.map(normalizeThreshold);
|
|
1759
|
+
if (thresholds.some((threshold)=>!threshold)) return null;
|
|
1760
|
+
return {
|
|
1761
|
+
iconSet: source.iconSet,
|
|
1762
|
+
showValue: false !== source.showValue,
|
|
1763
|
+
reverse: true === source.reverse,
|
|
1764
|
+
percent: false !== source.percent,
|
|
1765
|
+
thresholds: thresholds
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
function spreadsheetConditionalIconForValue(sourceFormat, value, sourceValues) {
|
|
1769
|
+
const format = normalizeSpreadsheetConditionalIconSetFormat(sourceFormat);
|
|
1770
|
+
const values = sourceValues.filter(Number.isFinite);
|
|
1771
|
+
if (!format || !Number.isFinite(value) || !values.length) return null;
|
|
1772
|
+
const count = spreadsheetConditionalIconSetCount(format.iconSet);
|
|
1773
|
+
let level = 0;
|
|
1774
|
+
for(let index = 1; index < count; index += 1){
|
|
1775
|
+
const threshold = format.thresholds[index];
|
|
1776
|
+
const cutoff = spreadsheetConditionalThresholdValue(threshold, values);
|
|
1777
|
+
if (null === cutoff) return null;
|
|
1778
|
+
if (threshold.gte ? value >= cutoff : value > cutoff) level = index;
|
|
1779
|
+
}
|
|
1780
|
+
return {
|
|
1781
|
+
iconSet: format.iconSet,
|
|
1782
|
+
index: format.reverse ? count - 1 - level : level,
|
|
1783
|
+
count,
|
|
1784
|
+
showValue: format.showValue
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function spreadsheetConditionalIconAppearance(icon) {
|
|
1788
|
+
const index = Math.max(0, Math.min(icon.count - 1, icon.index));
|
|
1789
|
+
const definition = SPREADSHEET_CONDITIONAL_ICON_SETS.find((item)=>item.name === icon.iconSet);
|
|
1790
|
+
const label = `${definition.label} ${index + 1}/${icon.count}`;
|
|
1791
|
+
if (icon.iconSet.includes('Arrows')) {
|
|
1792
|
+
const glyphs = 3 === icon.count ? [
|
|
1793
|
+
'↓',
|
|
1794
|
+
'→',
|
|
1795
|
+
'↑'
|
|
1796
|
+
] : 4 === icon.count ? [
|
|
1797
|
+
'↓',
|
|
1798
|
+
'↘',
|
|
1799
|
+
'↗',
|
|
1800
|
+
'↑'
|
|
1801
|
+
] : [
|
|
1802
|
+
'↓',
|
|
1803
|
+
'↘',
|
|
1804
|
+
'→',
|
|
1805
|
+
'↗',
|
|
1806
|
+
'↑'
|
|
1807
|
+
];
|
|
1808
|
+
return {
|
|
1809
|
+
glyph: glyphs[index],
|
|
1810
|
+
color: icon.iconSet.includes('Gray') ? '#606a78' : palette(icon.count)[index],
|
|
1811
|
+
label
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
if ('3Flags' === icon.iconSet) return {
|
|
1815
|
+
glyph: '⚑',
|
|
1816
|
+
color: COLOR_3[index],
|
|
1817
|
+
label
|
|
1818
|
+
};
|
|
1819
|
+
if (icon.iconSet.includes('TrafficLights')) return {
|
|
1820
|
+
glyph: '3TrafficLights2' === icon.iconSet ? '◉' : '●',
|
|
1821
|
+
color: palette(icon.count)[index],
|
|
1822
|
+
label
|
|
1823
|
+
};
|
|
1824
|
+
if ('3Signs' === icon.iconSet) return {
|
|
1825
|
+
glyph: [
|
|
1826
|
+
'◆',
|
|
1827
|
+
'▲',
|
|
1828
|
+
'●'
|
|
1829
|
+
][index],
|
|
1830
|
+
color: COLOR_3[index],
|
|
1831
|
+
label
|
|
1832
|
+
};
|
|
1833
|
+
if ('3Symbols' === icon.iconSet || '3Symbols2' === icon.iconSet) return {
|
|
1834
|
+
glyph: [
|
|
1835
|
+
'✕',
|
|
1836
|
+
'!',
|
|
1837
|
+
'✓'
|
|
1838
|
+
][index],
|
|
1839
|
+
color: COLOR_3[index],
|
|
1840
|
+
label
|
|
1841
|
+
};
|
|
1842
|
+
if ('4RedToBlack' === icon.iconSet) return {
|
|
1843
|
+
glyph: '●',
|
|
1844
|
+
color: [
|
|
1845
|
+
'#c62828',
|
|
1846
|
+
'#e45b4f',
|
|
1847
|
+
'#697386',
|
|
1848
|
+
'#111827'
|
|
1849
|
+
][index],
|
|
1850
|
+
label
|
|
1851
|
+
};
|
|
1852
|
+
if ('4Rating' === icon.iconSet) return {
|
|
1853
|
+
glyph: [
|
|
1854
|
+
'▁',
|
|
1855
|
+
'▃',
|
|
1856
|
+
'▆',
|
|
1857
|
+
'█'
|
|
1858
|
+
][index],
|
|
1859
|
+
color: '#54708f',
|
|
1860
|
+
label
|
|
1861
|
+
};
|
|
1862
|
+
if ('5Rating' === icon.iconSet) return {
|
|
1863
|
+
glyph: [
|
|
1864
|
+
'▁',
|
|
1865
|
+
'▂',
|
|
1866
|
+
'▄',
|
|
1867
|
+
'▆',
|
|
1868
|
+
'█'
|
|
1869
|
+
][index],
|
|
1870
|
+
color: '#54708f',
|
|
1871
|
+
label
|
|
1872
|
+
};
|
|
1873
|
+
return {
|
|
1874
|
+
glyph: [
|
|
1875
|
+
'○',
|
|
1876
|
+
'◔',
|
|
1877
|
+
'◑',
|
|
1878
|
+
'◕',
|
|
1879
|
+
'●'
|
|
1880
|
+
][index],
|
|
1881
|
+
color: '#54708f',
|
|
1882
|
+
label
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
function drawSpreadsheetConditionalIcon(context, bounds, icon, background, maskValue = !icon.showValue) {
|
|
1886
|
+
const width = bounds.endX - bounds.startX;
|
|
1887
|
+
const height = bounds.endY - bounds.startY;
|
|
1888
|
+
if (width < 6 || height < 6) return;
|
|
1889
|
+
const appearance = spreadsheetConditionalIconAppearance(icon);
|
|
1890
|
+
context.save();
|
|
1891
|
+
context.beginPath();
|
|
1892
|
+
context.rect(bounds.startX + 1, bounds.startY + 1, Math.max(0, width - 3), Math.max(0, height - 3));
|
|
1893
|
+
context.clip();
|
|
1894
|
+
if (maskValue) {
|
|
1895
|
+
context.fillStyle = background;
|
|
1896
|
+
context.fillRect(bounds.startX + 1, bounds.startY + 1, Math.max(0, width - 3), Math.max(0, height - 3));
|
|
1897
|
+
}
|
|
1898
|
+
const size = Math.max(9, Math.min(16, height - 5));
|
|
1899
|
+
context.fillStyle = appearance.color;
|
|
1900
|
+
context.font = `700 ${size}px "Arial Unicode MS", "Segoe UI Symbol", sans-serif`;
|
|
1901
|
+
context.textAlign = 'left';
|
|
1902
|
+
context.textBaseline = 'middle';
|
|
1903
|
+
context.fillText(appearance.glyph, bounds.startX + 4, bounds.startY + height / 2);
|
|
1904
|
+
context.restore();
|
|
1905
|
+
}
|
|
1906
|
+
function normalizeThreshold(value) {
|
|
1907
|
+
if (!value || 'object' != typeof value || Array.isArray(value)) return null;
|
|
1908
|
+
const source = value;
|
|
1909
|
+
const threshold = normalizeSpreadsheetConditionalThreshold(source);
|
|
1910
|
+
return threshold ? {
|
|
1911
|
+
...threshold,
|
|
1912
|
+
gte: false !== source.gte
|
|
1913
|
+
} : null;
|
|
1914
|
+
}
|
|
1915
|
+
function palette(count) {
|
|
1916
|
+
if (3 === count) return COLOR_3;
|
|
1917
|
+
if (4 === count) return COLOR_4;
|
|
1918
|
+
return COLOR_5;
|
|
1919
|
+
}
|
|
1920
|
+
function effectiveSpreadsheetHeaderFooterSections(sections) {
|
|
1921
|
+
return {
|
|
1922
|
+
left: sections?.left ?? '',
|
|
1923
|
+
center: sections?.center ?? '',
|
|
1924
|
+
right: sections?.right ?? ''
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
const DEFAULT_MARGINS = {
|
|
1928
|
+
top: 19.05,
|
|
1929
|
+
right: 17.78,
|
|
1930
|
+
bottom: 19.05,
|
|
1931
|
+
left: 17.78,
|
|
1932
|
+
header: 7.62,
|
|
1933
|
+
footer: 7.62
|
|
1934
|
+
};
|
|
1935
|
+
const PAPER_DIMENSIONS = {
|
|
1936
|
+
a3: {
|
|
1937
|
+
width: 297,
|
|
1938
|
+
height: 420
|
|
1939
|
+
},
|
|
1940
|
+
a4: {
|
|
1941
|
+
width: 210,
|
|
1942
|
+
height: 297
|
|
1943
|
+
},
|
|
1944
|
+
a5: {
|
|
1945
|
+
width: 148,
|
|
1946
|
+
height: 210
|
|
1947
|
+
},
|
|
1948
|
+
letter: {
|
|
1949
|
+
width: 215.9,
|
|
1950
|
+
height: 279.4
|
|
1951
|
+
},
|
|
1952
|
+
legal: {
|
|
1953
|
+
width: 215.9,
|
|
1954
|
+
height: 355.6
|
|
1955
|
+
},
|
|
1956
|
+
tabloid: {
|
|
1957
|
+
width: 279.4,
|
|
1958
|
+
height: 431.8
|
|
1959
|
+
}
|
|
1960
|
+
};
|
|
1961
|
+
PAPER_DIMENSIONS.a4.height, DEFAULT_MARGINS.left, DEFAULT_MARGINS.right;
|
|
1962
|
+
PAPER_DIMENSIONS.a4.width, DEFAULT_MARGINS.top, DEFAULT_MARGINS.bottom;
|
|
1963
|
+
function effectiveSpreadsheetPageSetup(pageSetup) {
|
|
1964
|
+
return {
|
|
1965
|
+
paperSize: normalizeSpreadsheetPaperSize(pageSetup?.paperSize),
|
|
1966
|
+
orientation: pageSetup?.orientation === 'portrait' ? 'portrait' : 'landscape',
|
|
1967
|
+
scale: boundedInteger(pageSetup?.scale, 10, 400, 100),
|
|
1968
|
+
fitToPage: Boolean(pageSetup?.fitToPage),
|
|
1969
|
+
fitToWidth: boundedInteger(pageSetup?.fitToWidth, 0, 32767, 1),
|
|
1970
|
+
fitToHeight: boundedInteger(pageSetup?.fitToHeight, 0, 32767, 0),
|
|
1971
|
+
horizontalCentered: Boolean(pageSetup?.horizontalCentered),
|
|
1972
|
+
verticalCentered: Boolean(pageSetup?.verticalCentered),
|
|
1973
|
+
header: effectiveSpreadsheetHeaderFooterSections(pageSetup?.header),
|
|
1974
|
+
footer: effectiveSpreadsheetHeaderFooterSections(pageSetup?.footer),
|
|
1975
|
+
pageNumberStart: boundedInteger(pageSetup?.pageNumberStart, 1, 32767, 1),
|
|
1976
|
+
pageOrder: pageSetup?.pageOrder === 'downThenOver' ? 'downThenOver' : 'overThenDown',
|
|
1977
|
+
scaleWithDocument: pageSetup?.scaleWithDocument !== false,
|
|
1978
|
+
alignWithMargins: pageSetup?.alignWithMargins !== false,
|
|
1979
|
+
margins: {
|
|
1980
|
+
top: boundedNumber(pageSetup?.margins?.top, 0, 100, DEFAULT_MARGINS.top),
|
|
1981
|
+
right: boundedNumber(pageSetup?.margins?.right, 0, 100, DEFAULT_MARGINS.right),
|
|
1982
|
+
bottom: boundedNumber(pageSetup?.margins?.bottom, 0, 100, DEFAULT_MARGINS.bottom),
|
|
1983
|
+
left: boundedNumber(pageSetup?.margins?.left, 0, 100, DEFAULT_MARGINS.left),
|
|
1984
|
+
header: boundedNumber(pageSetup?.margins?.header, 0, 100, DEFAULT_MARGINS.header),
|
|
1985
|
+
footer: boundedNumber(pageSetup?.margins?.footer, 0, 100, DEFAULT_MARGINS.footer)
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
function normalizeSpreadsheetPaperSize(value) {
|
|
1990
|
+
switch(value){
|
|
1991
|
+
case 'a3':
|
|
1992
|
+
case 'a4':
|
|
1993
|
+
case 'a5':
|
|
1994
|
+
case 'letter':
|
|
1995
|
+
case 'legal':
|
|
1996
|
+
case 'tabloid':
|
|
1997
|
+
return value;
|
|
1998
|
+
default:
|
|
1999
|
+
return 'a4';
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
function boundedInteger(value, minimum, maximum, fallback) {
|
|
2003
|
+
return Math.trunc(boundedNumber(value, minimum, maximum, fallback));
|
|
2004
|
+
}
|
|
2005
|
+
function boundedNumber(value, minimum, maximum, fallback) {
|
|
2006
|
+
return 'number' == typeof value && Number.isFinite(value) && value >= minimum && value <= maximum ? value : fallback;
|
|
2007
|
+
}
|
|
2008
|
+
export { DEFAULT_PROTECTION_HINT, SPREADSHEET_CONDITIONAL_COMPARISON_OPERATORS, SPREADSHEET_CONDITIONAL_ICON_SETS, applyPasswordlessEditableRanges, applySpreadsheetCellProtectionRanges, createWorkOfficeSpreadsheetCollaborationBinding as createOfficeSpreadsheetCollaborationBinding, defaultSpreadsheetColorScaleThresholds, defaultSpreadsheetConditionalIconThresholds, defaultSpreadsheetDataBarOptions, drawSpreadsheetConditionalIcon, editableRangeCellCount, editableRangeRequiresCredentials, effectiveSpreadsheetPageSetup, initializeWorkOfficeSpreadsheetCollaboration as initializeOfficeSpreadsheetCollaboration, isSpreadsheetConditionalComparisonOperator, isSpreadsheetConditionalIconSetName, normalizeSheetProtectionAuthority, normalizeSpreadsheetConditionalIconSetFormat, normalizeSpreadsheetConditionalVisualOptions, normalizeSpreadsheetPaperSize, protectedSheetCount, readWorkOfficeSpreadsheetCollaboration as readOfficeSpreadsheetCollaboration, replaceWorkOfficeSpreadsheetCollaboration as replaceOfficeSpreadsheetCollaboration, sheetHasProtectionState, sheetProtectionAuthority, spreadsheetConditionalComparisonNeedsUpperValue, spreadsheetConditionalIconForValue, spreadsheetConditionalIconSetCount, spreadsheetConditionalThresholdValue, spreadsheetConditionalThresholdsEqual, spreadsheetProtectionKey, unlockedCellCount, withEditableRange, withSheetProtection, withSheetSelectionPermissions, withoutEditableRange };
|