docxmlater 11.0.10 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -13
- package/dist/core/Document.d.ts +29 -3
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +733 -170
- package/dist/core/Document.js.map +1 -1
- package/dist/core/DocumentGenerator.d.ts +1 -0
- package/dist/core/DocumentGenerator.d.ts.map +1 -1
- package/dist/core/DocumentGenerator.js +77 -25
- package/dist/core/DocumentGenerator.js.map +1 -1
- package/dist/core/DocumentParser.d.ts +7 -3
- package/dist/core/DocumentParser.d.ts.map +1 -1
- package/dist/core/DocumentParser.js +427 -81
- package/dist/core/DocumentParser.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +2 -2
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/elements/Comment.d.ts +15 -1
- package/dist/elements/Comment.d.ts.map +1 -1
- package/dist/elements/Comment.js +67 -12
- package/dist/elements/Comment.js.map +1 -1
- package/dist/elements/CommentManager.d.ts +4 -0
- package/dist/elements/CommentManager.d.ts.map +1 -1
- package/dist/elements/CommentManager.js +74 -16
- package/dist/elements/CommentManager.js.map +1 -1
- package/dist/elements/EndnoteManager.d.ts.map +1 -1
- package/dist/elements/EndnoteManager.js.map +1 -1
- package/dist/elements/Field.d.ts +10 -1
- package/dist/elements/Field.d.ts.map +1 -1
- package/dist/elements/Field.js +76 -34
- package/dist/elements/Field.js.map +1 -1
- package/dist/elements/FieldHelpers.d.ts +1 -0
- package/dist/elements/FieldHelpers.d.ts.map +1 -1
- package/dist/elements/FieldHelpers.js +8 -4
- package/dist/elements/FieldHelpers.js.map +1 -1
- package/dist/elements/Footer.d.ts +3 -1
- package/dist/elements/Footer.d.ts.map +1 -1
- package/dist/elements/Footer.js +39 -53
- package/dist/elements/Footer.js.map +1 -1
- package/dist/elements/FootnoteManager.d.ts.map +1 -1
- package/dist/elements/FootnoteManager.js.map +1 -1
- package/dist/elements/Header.d.ts +3 -1
- package/dist/elements/Header.d.ts.map +1 -1
- package/dist/elements/Header.js +39 -53
- package/dist/elements/Header.js.map +1 -1
- package/dist/elements/HeaderFooterManager.d.ts +4 -2
- package/dist/elements/HeaderFooterManager.d.ts.map +1 -1
- package/dist/elements/HeaderFooterManager.js +36 -6
- package/dist/elements/HeaderFooterManager.js.map +1 -1
- package/dist/elements/Hyperlink.d.ts +6 -1
- package/dist/elements/Hyperlink.d.ts.map +1 -1
- package/dist/elements/Hyperlink.js +69 -44
- package/dist/elements/Hyperlink.js.map +1 -1
- package/dist/elements/Image.d.ts.map +1 -1
- package/dist/elements/Image.js +33 -6
- package/dist/elements/Image.js.map +1 -1
- package/dist/elements/ImageManager.d.ts +1 -0
- package/dist/elements/ImageManager.d.ts.map +1 -1
- package/dist/elements/ImageManager.js +11 -0
- package/dist/elements/ImageManager.js.map +1 -1
- package/dist/elements/Paragraph.d.ts +3 -0
- package/dist/elements/Paragraph.d.ts.map +1 -1
- package/dist/elements/Paragraph.js +91 -26
- package/dist/elements/Paragraph.js.map +1 -1
- package/dist/elements/Revision.d.ts +4 -0
- package/dist/elements/Revision.d.ts.map +1 -1
- package/dist/elements/Revision.js +369 -29
- package/dist/elements/Revision.js.map +1 -1
- package/dist/elements/Run.d.ts +1 -0
- package/dist/elements/Run.d.ts.map +1 -1
- package/dist/elements/Run.js +24 -4
- package/dist/elements/Run.js.map +1 -1
- package/dist/elements/Section.d.ts.map +1 -1
- package/dist/elements/Section.js +17 -7
- package/dist/elements/Section.js.map +1 -1
- package/dist/elements/Shape.d.ts.map +1 -1
- package/dist/elements/Shape.js +50 -42
- package/dist/elements/Shape.js.map +1 -1
- package/dist/elements/StructuredDocumentTag.d.ts +9 -1
- package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
- package/dist/elements/StructuredDocumentTag.js +46 -6
- package/dist/elements/StructuredDocumentTag.js.map +1 -1
- package/dist/elements/Table.d.ts +2 -0
- package/dist/elements/Table.d.ts.map +1 -1
- package/dist/elements/Table.js +90 -42
- package/dist/elements/Table.js.map +1 -1
- package/dist/elements/TableCell.d.ts +1 -0
- package/dist/elements/TableCell.d.ts.map +1 -1
- package/dist/elements/TableCell.js +32 -13
- package/dist/elements/TableCell.js.map +1 -1
- package/dist/elements/TableOfContents.d.ts.map +1 -1
- package/dist/elements/TableOfContents.js +5 -7
- package/dist/elements/TableOfContents.js.map +1 -1
- package/dist/elements/TableRow.d.ts +1 -0
- package/dist/elements/TableRow.d.ts.map +1 -1
- package/dist/elements/TableRow.js +16 -1
- package/dist/elements/TableRow.js.map +1 -1
- package/dist/elements/TextBox.d.ts.map +1 -1
- package/dist/elements/TextBox.js +55 -42
- package/dist/elements/TextBox.js.map +1 -1
- package/dist/esm/core/Document.js +735 -172
- package/dist/esm/core/Document.js.map +1 -1
- package/dist/esm/core/DocumentGenerator.js +77 -25
- package/dist/esm/core/DocumentGenerator.js.map +1 -1
- package/dist/esm/core/DocumentParser.js +427 -81
- package/dist/esm/core/DocumentParser.js.map +1 -1
- package/dist/esm/core/RelationshipManager.js +2 -2
- package/dist/esm/core/RelationshipManager.js.map +1 -1
- package/dist/esm/elements/Comment.js +67 -12
- package/dist/esm/elements/Comment.js.map +1 -1
- package/dist/esm/elements/CommentManager.js +74 -16
- package/dist/esm/elements/CommentManager.js.map +1 -1
- package/dist/esm/elements/EndnoteManager.js.map +1 -1
- package/dist/esm/elements/Field.js +77 -35
- package/dist/esm/elements/Field.js.map +1 -1
- package/dist/esm/elements/FieldHelpers.js +7 -4
- package/dist/esm/elements/FieldHelpers.js.map +1 -1
- package/dist/esm/elements/Footer.js +39 -53
- package/dist/esm/elements/Footer.js.map +1 -1
- package/dist/esm/elements/FootnoteManager.js.map +1 -1
- package/dist/esm/elements/Header.js +39 -53
- package/dist/esm/elements/Header.js.map +1 -1
- package/dist/esm/elements/HeaderFooterManager.js +36 -6
- package/dist/esm/elements/HeaderFooterManager.js.map +1 -1
- package/dist/esm/elements/Hyperlink.js +69 -44
- package/dist/esm/elements/Hyperlink.js.map +1 -1
- package/dist/esm/elements/Image.js +33 -6
- package/dist/esm/elements/Image.js.map +1 -1
- package/dist/esm/elements/ImageManager.js +11 -0
- package/dist/esm/elements/ImageManager.js.map +1 -1
- package/dist/esm/elements/Paragraph.js +91 -26
- package/dist/esm/elements/Paragraph.js.map +1 -1
- package/dist/esm/elements/Revision.js +369 -29
- package/dist/esm/elements/Revision.js.map +1 -1
- package/dist/esm/elements/Run.js +24 -4
- package/dist/esm/elements/Run.js.map +1 -1
- package/dist/esm/elements/Section.js +17 -7
- package/dist/esm/elements/Section.js.map +1 -1
- package/dist/esm/elements/Shape.js +50 -42
- package/dist/esm/elements/Shape.js.map +1 -1
- package/dist/esm/elements/StructuredDocumentTag.js +46 -6
- package/dist/esm/elements/StructuredDocumentTag.js.map +1 -1
- package/dist/esm/elements/Table.js +90 -42
- package/dist/esm/elements/Table.js.map +1 -1
- package/dist/esm/elements/TableCell.js +32 -13
- package/dist/esm/elements/TableCell.js.map +1 -1
- package/dist/esm/elements/TableOfContents.js +5 -7
- package/dist/esm/elements/TableOfContents.js.map +1 -1
- package/dist/esm/elements/TableRow.js +16 -1
- package/dist/esm/elements/TableRow.js.map +1 -1
- package/dist/esm/elements/TextBox.js +55 -42
- package/dist/esm/elements/TextBox.js.map +1 -1
- package/dist/esm/formatting/AbstractNumbering.js +1 -1
- package/dist/esm/formatting/AbstractNumbering.js.map +1 -1
- package/dist/esm/formatting/NumberingLevel.js +8 -6
- package/dist/esm/formatting/NumberingLevel.js.map +1 -1
- package/dist/esm/formatting/NumberingManager.js +16 -1
- package/dist/esm/formatting/NumberingManager.js.map +1 -1
- package/dist/esm/formatting/Style.js +14 -0
- package/dist/esm/formatting/Style.js.map +1 -1
- package/dist/esm/formatting/StylesManager.js +14 -2
- package/dist/esm/formatting/StylesManager.js.map +1 -1
- package/dist/esm/helpers/CleanupHelper.js +103 -26
- package/dist/esm/helpers/CleanupHelper.js.map +1 -1
- package/dist/esm/images/ImageOptimizer.js +52 -4
- package/dist/esm/images/ImageOptimizer.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal.js +1 -1
- package/dist/esm/internal.js.map +1 -1
- package/dist/esm/processors/CompatibilityUpgrader.js +7 -1
- package/dist/esm/processors/CompatibilityUpgrader.js.map +1 -1
- package/dist/esm/processors/InMemoryRevisionAcceptor.js +134 -13
- package/dist/esm/processors/InMemoryRevisionAcceptor.js.map +1 -1
- package/dist/esm/processors/RevisionWalker.js +223 -39
- package/dist/esm/processors/RevisionWalker.js.map +1 -1
- package/dist/esm/processors/SelectiveRevisionAcceptor.js +116 -17
- package/dist/esm/processors/SelectiveRevisionAcceptor.js.map +1 -1
- package/dist/esm/processors/acceptRevisions.js +20 -11
- package/dist/esm/processors/acceptRevisions.js.map +1 -1
- package/dist/esm/processors/cnfStyleDecoder.js +7 -7
- package/dist/esm/processors/cnfStyleDecoder.js.map +1 -1
- package/dist/esm/processors/stripTrackedChanges.js +16 -5
- package/dist/esm/processors/stripTrackedChanges.js.map +1 -1
- package/dist/esm/utils/deepClone.js +24 -11
- package/dist/esm/utils/deepClone.js.map +1 -1
- package/dist/esm/utils/units.js +1 -1
- package/dist/esm/utils/units.js.map +1 -1
- package/dist/esm/utils/validation.js +11 -23
- package/dist/esm/utils/validation.js.map +1 -1
- package/dist/esm/utils/xmlSanitization.js +131 -7
- package/dist/esm/utils/xmlSanitization.js.map +1 -1
- package/dist/esm/validation/RevisionAutoFixer.js +69 -55
- package/dist/esm/validation/RevisionAutoFixer.js.map +1 -1
- package/dist/esm/validation/RevisionValidator.js +24 -20
- package/dist/esm/validation/RevisionValidator.js.map +1 -1
- package/dist/esm/xml/XMLBuilder.js +52 -20
- package/dist/esm/xml/XMLBuilder.js.map +1 -1
- package/dist/esm/xml/XMLParser.js +55 -13
- package/dist/esm/xml/XMLParser.js.map +1 -1
- package/dist/esm/zip/ZipHandler.js +37 -13
- package/dist/esm/zip/ZipHandler.js.map +1 -1
- package/dist/esm/zip/ZipReader.js +53 -6
- package/dist/esm/zip/ZipReader.js.map +1 -1
- package/dist/esm/zip/errors.js +7 -0
- package/dist/esm/zip/errors.js.map +1 -1
- package/dist/esm/zip/types.js +4 -0
- package/dist/esm/zip/types.js.map +1 -1
- package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
- package/dist/formatting/AbstractNumbering.js +1 -1
- package/dist/formatting/AbstractNumbering.js.map +1 -1
- package/dist/formatting/NumberingLevel.d.ts +1 -1
- package/dist/formatting/NumberingLevel.d.ts.map +1 -1
- package/dist/formatting/NumberingLevel.js +8 -6
- package/dist/formatting/NumberingLevel.js.map +1 -1
- package/dist/formatting/NumberingManager.d.ts.map +1 -1
- package/dist/formatting/NumberingManager.js +16 -1
- package/dist/formatting/NumberingManager.js.map +1 -1
- package/dist/formatting/Style.d.ts.map +1 -1
- package/dist/formatting/Style.js +14 -0
- package/dist/formatting/Style.js.map +1 -1
- package/dist/formatting/StylesManager.d.ts +2 -0
- package/dist/formatting/StylesManager.d.ts.map +1 -1
- package/dist/formatting/StylesManager.js +14 -2
- package/dist/formatting/StylesManager.js.map +1 -1
- package/dist/helpers/CleanupHelper.d.ts.map +1 -1
- package/dist/helpers/CleanupHelper.js +103 -26
- package/dist/helpers/CleanupHelper.js.map +1 -1
- package/dist/images/ImageOptimizer.d.ts.map +1 -1
- package/dist/images/ImageOptimizer.js +52 -4
- package/dist/images/ImageOptimizer.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -1
- package/dist/internal.js.map +1 -1
- package/dist/processors/CompatibilityUpgrader.d.ts.map +1 -1
- package/dist/processors/CompatibilityUpgrader.js +7 -1
- package/dist/processors/CompatibilityUpgrader.js.map +1 -1
- package/dist/processors/InMemoryRevisionAcceptor.d.ts +1 -0
- package/dist/processors/InMemoryRevisionAcceptor.d.ts.map +1 -1
- package/dist/processors/InMemoryRevisionAcceptor.js +135 -13
- package/dist/processors/InMemoryRevisionAcceptor.js.map +1 -1
- package/dist/processors/RevisionWalker.d.ts +9 -3
- package/dist/processors/RevisionWalker.d.ts.map +1 -1
- package/dist/processors/RevisionWalker.js +223 -39
- package/dist/processors/RevisionWalker.js.map +1 -1
- package/dist/processors/SelectiveRevisionAcceptor.d.ts.map +1 -1
- package/dist/processors/SelectiveRevisionAcceptor.js +116 -17
- package/dist/processors/SelectiveRevisionAcceptor.js.map +1 -1
- package/dist/processors/acceptRevisions.d.ts +1 -0
- package/dist/processors/acceptRevisions.d.ts.map +1 -1
- package/dist/processors/acceptRevisions.js +21 -11
- package/dist/processors/acceptRevisions.js.map +1 -1
- package/dist/processors/cnfStyleDecoder.d.ts.map +1 -1
- package/dist/processors/cnfStyleDecoder.js +7 -7
- package/dist/processors/cnfStyleDecoder.js.map +1 -1
- package/dist/processors/stripTrackedChanges.d.ts.map +1 -1
- package/dist/processors/stripTrackedChanges.js +16 -5
- package/dist/processors/stripTrackedChanges.js.map +1 -1
- package/dist/utils/deepClone.d.ts.map +1 -1
- package/dist/utils/deepClone.js +24 -11
- package/dist/utils/deepClone.js.map +1 -1
- package/dist/utils/units.d.ts.map +1 -1
- package/dist/utils/units.js +1 -1
- package/dist/utils/units.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +11 -23
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/xmlSanitization.d.ts +2 -0
- package/dist/utils/xmlSanitization.d.ts.map +1 -1
- package/dist/utils/xmlSanitization.js +133 -7
- package/dist/utils/xmlSanitization.js.map +1 -1
- package/dist/validation/RevisionAutoFixer.d.ts +6 -2
- package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
- package/dist/validation/RevisionAutoFixer.js +68 -54
- package/dist/validation/RevisionAutoFixer.js.map +1 -1
- package/dist/validation/RevisionValidator.d.ts +2 -0
- package/dist/validation/RevisionValidator.d.ts.map +1 -1
- package/dist/validation/RevisionValidator.js +26 -21
- package/dist/validation/RevisionValidator.js.map +1 -1
- package/dist/xml/XMLBuilder.d.ts +1 -0
- package/dist/xml/XMLBuilder.d.ts.map +1 -1
- package/dist/xml/XMLBuilder.js +52 -20
- package/dist/xml/XMLBuilder.js.map +1 -1
- package/dist/xml/XMLParser.d.ts +1 -0
- package/dist/xml/XMLParser.d.ts.map +1 -1
- package/dist/xml/XMLParser.js +55 -13
- package/dist/xml/XMLParser.js.map +1 -1
- package/dist/zip/ZipHandler.d.ts.map +1 -1
- package/dist/zip/ZipHandler.js +37 -13
- package/dist/zip/ZipHandler.js.map +1 -1
- package/dist/zip/ZipReader.d.ts.map +1 -1
- package/dist/zip/ZipReader.js +52 -5
- package/dist/zip/ZipReader.js.map +1 -1
- package/dist/zip/errors.d.ts +3 -0
- package/dist/zip/errors.d.ts.map +1 -1
- package/dist/zip/errors.js +9 -1
- package/dist/zip/errors.js.map +1 -1
- package/dist/zip/types.d.ts +4 -0
- package/dist/zip/types.d.ts.map +1 -1
- package/dist/zip/types.js +4 -0
- package/dist/zip/types.js.map +1 -1
- package/package.json +2 -2
- package/src/core/Document.ts +1184 -242
- package/src/core/DocumentGenerator.ts +106 -26
- package/src/core/DocumentParser.ts +681 -114
- package/src/core/RelationshipManager.ts +446 -442
- package/src/elements/Comment.ts +123 -18
- package/src/elements/CommentManager.ts +121 -22
- package/src/elements/EndnoteManager.ts +4 -1
- package/src/elements/Field.ts +134 -43
- package/src/elements/FieldHelpers.ts +20 -4
- package/src/elements/Footer.ts +62 -67
- package/src/elements/FootnoteManager.ts +4 -1
- package/src/elements/Header.ts +62 -67
- package/src/elements/HeaderFooterManager.ts +56 -8
- package/src/elements/Hyperlink.ts +150 -55
- package/src/elements/Image.ts +51 -7
- package/src/elements/ImageManager.ts +24 -0
- package/src/elements/Paragraph.ts +161 -40
- package/src/elements/Revision.ts +446 -47
- package/src/elements/Run.ts +44 -6
- package/src/elements/Section.ts +25 -9
- package/src/elements/Shape.ts +52 -44
- package/src/elements/StructuredDocumentTag.ts +83 -8
- package/src/elements/Table.ts +151 -50
- package/src/elements/TableCell.ts +57 -17
- package/src/elements/TableOfContents.ts +10 -9
- package/src/elements/TableRow.ts +35 -3
- package/src/elements/TextBox.ts +58 -43
- package/src/formatting/AbstractNumbering.ts +4 -2
- package/src/formatting/NumberingLevel.ts +50 -12
- package/src/formatting/NumberingManager.ts +32 -3
- package/src/formatting/Style.ts +14 -0
- package/src/formatting/StylesManager.ts +29 -2
- package/src/helpers/CleanupHelper.ts +122 -30
- package/src/images/ImageOptimizer.ts +64 -5
- package/src/index.ts +7 -0
- package/src/internal.ts +1 -0
- package/src/processors/CompatibilityUpgrader.ts +15 -2
- package/src/processors/InMemoryRevisionAcceptor.ts +276 -17
- package/src/processors/RevisionWalker.ts +390 -46
- package/src/processors/SelectiveRevisionAcceptor.ts +164 -28
- package/src/processors/acceptRevisions.ts +67 -17
- package/src/processors/cnfStyleDecoder.ts +18 -12
- package/src/processors/stripTrackedChanges.ts +47 -10
- package/src/utils/deepClone.ts +48 -14
- package/src/utils/units.ts +3 -1
- package/src/utils/validation.ts +30 -32
- package/src/utils/xmlSanitization.ts +218 -22
- package/src/validation/RevisionAutoFixer.ts +109 -65
- package/src/validation/RevisionValidator.ts +48 -23
- package/src/xml/XMLBuilder.ts +82 -22
- package/src/xml/XMLParser.ts +87 -20
- package/src/zip/ZipHandler.ts +61 -21
- package/src/zip/ZipReader.ts +135 -13
- package/src/zip/errors.ts +15 -0
- package/src/zip/types.ts +44 -2
|
@@ -11,19 +11,43 @@
|
|
|
11
11
|
import { ParsedXMLObject } from '../xml/XMLParser.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Options for controlling which revision types to
|
|
14
|
+
* Options for controlling which revision types to process
|
|
15
15
|
*/
|
|
16
16
|
export interface RevisionWalkerOptions {
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Direction of processing (default: 'accept').
|
|
19
|
+
*
|
|
20
|
+
* - 'accept': keep inserted content, discard deleted content (the post-edit
|
|
21
|
+
* document).
|
|
22
|
+
* - 'reject': discard inserted content, restore deleted content and previous
|
|
23
|
+
* formatting (the original pre-edit document — the exact inverse of accept).
|
|
24
|
+
*/
|
|
25
|
+
mode?: 'accept' | 'reject';
|
|
26
|
+
/**
|
|
27
|
+
* Process insertions. In 'accept' mode w:ins is unwrapped (content kept); in
|
|
28
|
+
* 'reject' mode w:ins is removed (content discarded). Inserted rows/tables are
|
|
29
|
+
* removed under 'reject'. (default: true)
|
|
30
|
+
*/
|
|
18
31
|
acceptInsertions?: boolean;
|
|
19
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Process deletions. In 'accept' mode w:del is removed (content discarded); in
|
|
34
|
+
* 'reject' mode w:del is unwrapped and w:delText restored to w:t. Deleted
|
|
35
|
+
* rows/tables are removed under 'accept'. (default: true)
|
|
36
|
+
*/
|
|
20
37
|
acceptDeletions?: boolean;
|
|
21
38
|
/** Handle w:moveFrom/w:moveTo (default: true) */
|
|
22
39
|
acceptMoves?: boolean;
|
|
23
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Process *Change elements. In 'accept' mode they are removed (current
|
|
42
|
+
* formatting kept); in 'reject' mode the previous formatting stored inside the
|
|
43
|
+
* change is restored. (default: true)
|
|
44
|
+
*/
|
|
24
45
|
acceptPropertyChanges?: boolean;
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
/** Action to take on a revision element at a given level. */
|
|
49
|
+
type RevisionAction = 'unwrap' | 'remove' | 'restore' | 'none';
|
|
50
|
+
|
|
27
51
|
/**
|
|
28
52
|
* Structure for tracking element order
|
|
29
53
|
*/
|
|
@@ -72,6 +96,34 @@ const REVISION_ELEMENTS = {
|
|
|
72
96
|
],
|
|
73
97
|
};
|
|
74
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Children that are tracked independently of a property-change snapshot and
|
|
101
|
+
* must be carried over (not wiped) when rejecting that change restores the
|
|
102
|
+
* previous properties. Keyed by the wrapper element; `position` is where they
|
|
103
|
+
* sit in the wrapper's ECMA-376 child order relative to the restored snapshot:
|
|
104
|
+
*
|
|
105
|
+
* - CT_PPr (§17.3.1.26): the paragraph-mark run properties (`w:rPr`) and the
|
|
106
|
+
* section properties (`w:sectPr`) follow the base paragraph properties, and a
|
|
107
|
+
* `w:pPrChange` snapshot is a CT_PPrBase that contains neither.
|
|
108
|
+
* - CT_SectPr (§17.6.17): the header/footer references PRECEDE the section base
|
|
109
|
+
* contents, and a `w:sectPrChange` snapshot is a CT_SectPrBase that contains
|
|
110
|
+
* neither — so they must be prepended or the section loses its headers/footers.
|
|
111
|
+
*/
|
|
112
|
+
const PRESERVED_ON_RESTORE: Record<string, { keys: string[]; position: 'before' | 'after' }> = {
|
|
113
|
+
'w:pPr': { keys: ['w:rPr', 'w:sectPr'], position: 'after' },
|
|
114
|
+
'w:sectPr': { keys: ['w:headerReference', 'w:footerReference'], position: 'before' },
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Deleted run-content elements and the live elements they restore to when a
|
|
119
|
+
* deletion is rejected (§17.3.3.7 w:delText is structurally CT_Text, identical
|
|
120
|
+
* to w:t; w:delInstrText likewise mirrors w:instrText).
|
|
121
|
+
*/
|
|
122
|
+
const DELETED_TEXT_RENAMES: Record<string, string> = {
|
|
123
|
+
'w:delText': 'w:t',
|
|
124
|
+
'w:delInstrText': 'w:instrText',
|
|
125
|
+
};
|
|
126
|
+
|
|
75
127
|
/**
|
|
76
128
|
* DOM-based tree walker for accepting Word document revisions
|
|
77
129
|
*
|
|
@@ -100,6 +152,7 @@ export class RevisionWalker {
|
|
|
100
152
|
*/
|
|
101
153
|
static processTree(obj: ParsedXMLObject, options?: RevisionWalkerOptions): ParsedXMLObject {
|
|
102
154
|
const opts: Required<RevisionWalkerOptions> = {
|
|
155
|
+
mode: options?.mode ?? 'accept',
|
|
103
156
|
acceptInsertions: options?.acceptInsertions ?? true,
|
|
104
157
|
acceptDeletions: options?.acceptDeletions ?? true,
|
|
105
158
|
acceptMoves: options?.acceptMoves ?? true,
|
|
@@ -112,6 +165,14 @@ export class RevisionWalker {
|
|
|
112
165
|
// Walk and transform the tree
|
|
113
166
|
RevisionWalker.walkAndTransform(clone, opts);
|
|
114
167
|
|
|
168
|
+
// Reject restores deleted content by unwrapping w:del; the runs inside a
|
|
169
|
+
// deletion carry their text in w:delText (and field codes in
|
|
170
|
+
// w:delInstrText). Once unwrapped they are live content again, so convert
|
|
171
|
+
// those back to the normal w:t / w:instrText elements. (§17.3.3.7)
|
|
172
|
+
if (opts.mode === 'reject' && opts.acceptDeletions) {
|
|
173
|
+
RevisionWalker.convertDeletedTextToNormal(clone);
|
|
174
|
+
}
|
|
175
|
+
|
|
115
176
|
return clone;
|
|
116
177
|
}
|
|
117
178
|
|
|
@@ -149,8 +210,29 @@ export class RevisionWalker {
|
|
|
149
210
|
// detect it at the table level. The default `w:del` removal in
|
|
150
211
|
// processRevisions only strips the marker itself, leaving a zombie
|
|
151
212
|
// empty row; per spec, accepting the deletion removes the entire row.
|
|
152
|
-
|
|
153
|
-
|
|
213
|
+
// Tables are checked from the parent first: a table whose rows are ALL
|
|
214
|
+
// tracked-deleted must be removed entirely (a row-less w:tbl violates
|
|
215
|
+
// §17.4.38's at-least-one-row requirement and Word's Accept All drops
|
|
216
|
+
// the whole table), and only the parent can remove the w:tbl itself.
|
|
217
|
+
// Reject is the mirror image: a row whose w:trPr carries a self-closing
|
|
218
|
+
// w:ins marker was added by the edit and must be removed to revert to the
|
|
219
|
+
// original (gated on acceptInsertions, since this undoes an insertion). A
|
|
220
|
+
// table whose rows are ALL insert-marked is removed entirely, mirroring the
|
|
221
|
+
// accept-side at-least-one-row handling.
|
|
222
|
+
if (options.mode === 'reject') {
|
|
223
|
+
if (options.acceptInsertions && obj['w:tbl']) {
|
|
224
|
+
RevisionWalker.removeFullyMarkedTables(obj, 'w:ins');
|
|
225
|
+
}
|
|
226
|
+
if (options.acceptInsertions && obj['w:tr']) {
|
|
227
|
+
RevisionWalker.filterMarkedRows(obj, 'w:ins');
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
if (options.acceptDeletions && obj['w:tbl']) {
|
|
231
|
+
RevisionWalker.removeFullyMarkedTables(obj, 'w:del');
|
|
232
|
+
}
|
|
233
|
+
if (options.acceptDeletions && obj['w:tr']) {
|
|
234
|
+
RevisionWalker.filterMarkedRows(obj, 'w:del');
|
|
235
|
+
}
|
|
154
236
|
}
|
|
155
237
|
|
|
156
238
|
// Get keys to process (excluding metadata keys)
|
|
@@ -176,36 +258,112 @@ export class RevisionWalker {
|
|
|
176
258
|
}
|
|
177
259
|
|
|
178
260
|
/**
|
|
179
|
-
* Remove `<w:tr>` entries whose `<w:trPr>` contains a self-closing
|
|
180
|
-
*
|
|
261
|
+
* Remove `<w:tr>` entries whose `<w:trPr>` contains a self-closing row-level
|
|
262
|
+
* tracking marker. `marker` is `w:del` when accepting (drop deleted rows) or
|
|
263
|
+
* `w:ins` when rejecting (drop inserted rows). Operates on the parsed-object
|
|
181
264
|
* representation (arrays when multiple, single object otherwise).
|
|
182
265
|
*/
|
|
183
|
-
private static
|
|
266
|
+
private static filterMarkedRows(tbl: any, marker: 'w:del' | 'w:ins'): void {
|
|
184
267
|
const rows = tbl['w:tr'];
|
|
185
|
-
const
|
|
268
|
+
const isRowMarked = (row: any): boolean => {
|
|
186
269
|
if (!row || typeof row !== 'object') return false;
|
|
187
270
|
const trPr = row['w:trPr'];
|
|
188
271
|
if (!trPr || typeof trPr !== 'object') return false;
|
|
189
|
-
//
|
|
272
|
+
// The row-level marker is a CT_TrackChange (no children). When absent
|
|
190
273
|
// the parser leaves no key; when present it's a self-closing tag
|
|
191
274
|
// parsed as an object with only @_w:id/@_w:author/@_w:date keys.
|
|
192
|
-
return !!trPr[
|
|
275
|
+
return !!trPr[marker];
|
|
193
276
|
};
|
|
194
277
|
|
|
195
278
|
if (Array.isArray(rows)) {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
delete tbl['w:tr'];
|
|
200
|
-
} else {
|
|
201
|
-
tbl['w:tr'] = kept;
|
|
202
|
-
}
|
|
279
|
+
const removedIndices = new Set<number>();
|
|
280
|
+
for (let i = 0; i < rows.length; i++) {
|
|
281
|
+
if (isRowMarked(rows[i])) removedIndices.add(i);
|
|
203
282
|
}
|
|
204
|
-
|
|
283
|
+
if (removedIndices.size === 0) return;
|
|
284
|
+
const kept = rows.filter((_, i) => !removedIndices.has(i));
|
|
285
|
+
if (kept.length === 0) {
|
|
286
|
+
delete tbl['w:tr'];
|
|
287
|
+
} else {
|
|
288
|
+
tbl['w:tr'] = kept;
|
|
289
|
+
}
|
|
290
|
+
RevisionWalker.dropOrderedChildEntries(tbl, 'w:tr', removedIndices);
|
|
291
|
+
} else if (isRowMarked(rows)) {
|
|
205
292
|
delete tbl['w:tr'];
|
|
293
|
+
RevisionWalker.dropOrderedChildEntries(tbl, 'w:tr', new Set([0]));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Remove `<w:tbl>` children whose rows are ALL marked with the given
|
|
299
|
+
* row-level tracking marker (`w:del` when accepting, `w:ins` when rejecting).
|
|
300
|
+
*
|
|
301
|
+
* Runs at the table's parent because filterMarkedRows (invoked on the table
|
|
302
|
+
* itself) has no reference back to the container holding the `w:tbl` key.
|
|
303
|
+
* Skips tables that still carry potential row containers — the wrappers that
|
|
304
|
+
* SURVIVE this direction (w:ins/w:moveTo when accepting deletions,
|
|
305
|
+
* w:del/w:moveFrom when rejecting insertions), plus w:sdt / w:customXml —
|
|
306
|
+
* since their rows only materialize as direct `w:tr` children later, when the
|
|
307
|
+
* wrappers are unwrapped during recursion.
|
|
308
|
+
*/
|
|
309
|
+
private static removeFullyMarkedTables(parent: any, marker: 'w:del' | 'w:ins'): void {
|
|
310
|
+
const tables = parent['w:tbl'];
|
|
311
|
+
// Wrappers whose rows survive in this direction must not be treated as
|
|
312
|
+
// "rowless": accept keeps w:ins/w:moveTo content, reject keeps w:del/
|
|
313
|
+
// w:moveFrom content.
|
|
314
|
+
const survivingWrappers: string[] =
|
|
315
|
+
marker === 'w:del' ? ['w:ins', 'w:moveTo'] : ['w:del', 'w:moveFrom'];
|
|
316
|
+
const becomesRowless = (tbl: any): boolean => {
|
|
317
|
+
// Tables with no rows at all are pre-existing structures, not the
|
|
318
|
+
// result of a tracked change — leave them untouched.
|
|
319
|
+
if (!tbl || typeof tbl !== 'object' || tbl['w:tr'] === undefined) return false;
|
|
320
|
+
RevisionWalker.filterMarkedRows(tbl, marker);
|
|
321
|
+
return (
|
|
322
|
+
tbl['w:tr'] === undefined &&
|
|
323
|
+
!survivingWrappers.some((w) => tbl[w]) &&
|
|
324
|
+
!tbl['w:sdt'] &&
|
|
325
|
+
!tbl['w:customXml']
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
if (Array.isArray(tables)) {
|
|
330
|
+
const removedIndices = new Set<number>();
|
|
331
|
+
for (let i = 0; i < tables.length; i++) {
|
|
332
|
+
if (becomesRowless(tables[i])) removedIndices.add(i);
|
|
333
|
+
}
|
|
334
|
+
if (removedIndices.size === 0) return;
|
|
335
|
+
const kept = tables.filter((_: any, i: number) => !removedIndices.has(i));
|
|
336
|
+
if (kept.length === 0) {
|
|
337
|
+
delete parent['w:tbl'];
|
|
338
|
+
} else {
|
|
339
|
+
parent['w:tbl'] = kept;
|
|
340
|
+
}
|
|
341
|
+
RevisionWalker.dropOrderedChildEntries(parent, 'w:tbl', removedIndices);
|
|
342
|
+
} else if (becomesRowless(tables)) {
|
|
343
|
+
delete parent['w:tbl'];
|
|
344
|
+
RevisionWalker.dropOrderedChildEntries(parent, 'w:tbl', new Set([0]));
|
|
206
345
|
}
|
|
207
346
|
}
|
|
208
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Drop specific {type, index} entries from `_orderedChildren` and
|
|
350
|
+
* re-index the survivors. The serializer maps each entry onto the
|
|
351
|
+
* element array by index, so stale entries left after filtering would
|
|
352
|
+
* shift surviving elements relative to inter-element siblings (e.g. a
|
|
353
|
+
* w:bookmarkEnd between table rows) or silently drop the tail.
|
|
354
|
+
*/
|
|
355
|
+
private static dropOrderedChildEntries(
|
|
356
|
+
parent: any,
|
|
357
|
+
type: string,
|
|
358
|
+
removedIndices: Set<number>
|
|
359
|
+
): void {
|
|
360
|
+
if (!parent._orderedChildren) return;
|
|
361
|
+
parent._orderedChildren = parent._orderedChildren.filter(
|
|
362
|
+
(c: OrderedChildInfo) => c.type !== type || !removedIndices.has(c.index)
|
|
363
|
+
);
|
|
364
|
+
RevisionWalker.reindexOrderedChildren(parent._orderedChildren);
|
|
365
|
+
}
|
|
366
|
+
|
|
209
367
|
/**
|
|
210
368
|
* Process revision elements at the current level
|
|
211
369
|
*/
|
|
@@ -219,43 +377,63 @@ export class RevisionWalker {
|
|
|
219
377
|
);
|
|
220
378
|
|
|
221
379
|
for (const key of keysToProcess) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
380
|
+
switch (RevisionWalker.getActionForKey(key, options)) {
|
|
381
|
+
case 'unwrap':
|
|
382
|
+
RevisionWalker.unwrapAllElements(parent, key);
|
|
383
|
+
break;
|
|
384
|
+
case 'remove':
|
|
385
|
+
RevisionWalker.removeAllElements(parent, key);
|
|
386
|
+
break;
|
|
387
|
+
case 'restore':
|
|
388
|
+
RevisionWalker.restorePropertyChange(parent, key);
|
|
389
|
+
break;
|
|
390
|
+
case 'none':
|
|
391
|
+
break;
|
|
227
392
|
}
|
|
228
393
|
}
|
|
229
394
|
}
|
|
230
395
|
|
|
231
396
|
/**
|
|
232
|
-
*
|
|
397
|
+
* Resolve what to do with a revision element of the given key, honouring the
|
|
398
|
+
* processing direction. Accept and reject are exact inverses for content
|
|
399
|
+
* revisions; property changes are removed on accept and restored on reject.
|
|
233
400
|
*/
|
|
234
|
-
private static
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
401
|
+
private static getActionForKey(
|
|
402
|
+
key: string,
|
|
403
|
+
options: Required<RevisionWalkerOptions>
|
|
404
|
+
): RevisionAction {
|
|
405
|
+
const reject = options.mode === 'reject';
|
|
406
|
+
|
|
407
|
+
// Content revisions are driven off REVISION_ELEMENTS so membership stays a
|
|
408
|
+
// single source of truth (shared with isRevisionElement). On accept, UNWRAP
|
|
409
|
+
// keeps content and REMOVE discards it; reject is the exact inverse.
|
|
410
|
+
const isUnwrap = REVISION_ELEMENTS.UNWRAP.includes(key);
|
|
411
|
+
const isRemove = REVISION_ELEMENTS.REMOVE.includes(key);
|
|
412
|
+
if (isUnwrap || isRemove) {
|
|
413
|
+
// Per-key gating: w:ins ↔ insertions, w:del ↔ deletions, w:moveTo/
|
|
414
|
+
// w:moveFrom ↔ moves.
|
|
415
|
+
const gate =
|
|
416
|
+
key === 'w:ins'
|
|
417
|
+
? options.acceptInsertions
|
|
418
|
+
: key === 'w:del'
|
|
419
|
+
? options.acceptDeletions
|
|
420
|
+
: options.acceptMoves;
|
|
421
|
+
if (!gate) return 'none';
|
|
422
|
+
const keepContent = reject ? isRemove : isUnwrap;
|
|
423
|
+
return keepContent ? 'unwrap' : 'remove';
|
|
251
424
|
}
|
|
252
425
|
if (REVISION_ELEMENTS.PROPERTY_CHANGES.includes(key)) {
|
|
253
|
-
|
|
426
|
+
if (!options.acceptPropertyChanges) return 'none';
|
|
427
|
+
// w:numberingChange (CT_TrackChangeNumbering) is a legacy attribute-only
|
|
428
|
+
// marker with no embedded previous-properties element, so there is
|
|
429
|
+
// nothing to restore — drop it in both directions.
|
|
430
|
+
if (reject && key !== 'w:numberingChange') return 'restore';
|
|
431
|
+
return 'remove';
|
|
254
432
|
}
|
|
255
433
|
if (REVISION_ELEMENTS.RANGE_MARKERS.includes(key)) {
|
|
256
|
-
return
|
|
434
|
+
return 'remove'; // Always remove range markers
|
|
257
435
|
}
|
|
258
|
-
return
|
|
436
|
+
return 'none';
|
|
259
437
|
}
|
|
260
438
|
|
|
261
439
|
/**
|
|
@@ -414,6 +592,172 @@ export class RevisionWalker {
|
|
|
414
592
|
delete parent[key];
|
|
415
593
|
}
|
|
416
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Restore the previous formatting recorded by a property-change element
|
|
597
|
+
* (reject direction). A change such as `w:rPrChange` lives inside the
|
|
598
|
+
* properties wrapper it tracks (`w:rPr`) and embeds the complete previous
|
|
599
|
+
* wrapper as a child:
|
|
600
|
+
*
|
|
601
|
+
* <w:rPr><w:b/><w:rPrChange><w:rPr><w:i/></w:rPr></w:rPrChange></w:rPr>
|
|
602
|
+
*
|
|
603
|
+
* Rejecting it replaces the current direct formatting (`w:b`) with the
|
|
604
|
+
* embedded previous formatting (`w:i`) and drops the change marker. Children
|
|
605
|
+
* that are tracked independently of the snapshot — e.g. a paragraph's mark
|
|
606
|
+
* run properties / section properties, or a section's header/footer
|
|
607
|
+
* references (see {@link PRESERVED_ON_RESTORE}) — are carried over from the
|
|
608
|
+
* current wrapper in their correct schema position rather than discarded.
|
|
609
|
+
*
|
|
610
|
+
* @param parent - the properties wrapper containing the change element
|
|
611
|
+
* @param changeKey - the change element key, e.g. 'w:rPrChange'
|
|
612
|
+
*/
|
|
613
|
+
private static restorePropertyChange(parent: any, changeKey: string): void {
|
|
614
|
+
const changeRaw = parent[changeKey];
|
|
615
|
+
if (!changeRaw) return;
|
|
616
|
+
const changeEl = Array.isArray(changeRaw) ? changeRaw[0] : changeRaw;
|
|
617
|
+
|
|
618
|
+
// 'w:rPrChange' -> 'w:rPr', 'w:tblGridChange' -> 'w:tblGrid', etc.
|
|
619
|
+
const previousKey = changeKey.replace(/Change$/, '');
|
|
620
|
+
const previousRaw =
|
|
621
|
+
changeEl && typeof changeEl === 'object' ? changeEl[previousKey] : undefined;
|
|
622
|
+
|
|
623
|
+
// Malformed change with no embedded previous-properties snapshot: there is
|
|
624
|
+
// nothing to restore, so drop just the marker and leave the current
|
|
625
|
+
// formatting intact rather than emptying the wrapper.
|
|
626
|
+
if (previousRaw === undefined) {
|
|
627
|
+
RevisionWalker.removeAllElements(parent, changeKey);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const previousEl = Array.isArray(previousRaw) ? previousRaw[0] : previousRaw;
|
|
631
|
+
|
|
632
|
+
const preserve = PRESERVED_ON_RESTORE[previousKey];
|
|
633
|
+
const preserveKeys = preserve?.keys ?? [];
|
|
634
|
+
|
|
635
|
+
// Snapshot children become the restored formatting (minus any preserved
|
|
636
|
+
// keys it might redundantly carry).
|
|
637
|
+
const snapshotChildren =
|
|
638
|
+
previousEl && typeof previousEl === 'object'
|
|
639
|
+
? RevisionWalker.getOrderedChildList(previousEl).filter(
|
|
640
|
+
(c) => !preserveKeys.includes(c.type)
|
|
641
|
+
)
|
|
642
|
+
: [];
|
|
643
|
+
|
|
644
|
+
// Preserved children are read from the CURRENT wrapper in their existing
|
|
645
|
+
// document order (captured before the wrapper is cleared below).
|
|
646
|
+
const preservedChildren = preserveKeys.length
|
|
647
|
+
? RevisionWalker.getOrderedChildList(parent).filter(
|
|
648
|
+
(c) => preserveKeys.includes(c.type) && c.type !== changeKey
|
|
649
|
+
)
|
|
650
|
+
: [];
|
|
651
|
+
|
|
652
|
+
const newChildren =
|
|
653
|
+
preserve?.position === 'before'
|
|
654
|
+
? [...preservedChildren, ...snapshotChildren]
|
|
655
|
+
: [...snapshotChildren, ...preservedChildren];
|
|
656
|
+
|
|
657
|
+
RevisionWalker.rebuildChildren(parent, newChildren);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Replace all non-metadata children of `parent` with the supplied ordered
|
|
662
|
+
* `{type, element}` list, regrouping per-type arrays and rebuilding
|
|
663
|
+
* `_orderedChildren` so the serializer emits them in the given order.
|
|
664
|
+
* Attributes (`@_*`) and `#text` are left untouched.
|
|
665
|
+
*/
|
|
666
|
+
private static rebuildChildren(parent: any, ordered: { type: string; element: any }[]): void {
|
|
667
|
+
for (const k of Object.keys(parent)) {
|
|
668
|
+
if (!k.startsWith('@_') && k !== '#text' && k !== '_orderedChildren') {
|
|
669
|
+
delete parent[k];
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const grouped = new Map<string, any[]>();
|
|
674
|
+
for (const { type, element } of ordered) {
|
|
675
|
+
if (!grouped.has(type)) grouped.set(type, []);
|
|
676
|
+
grouped.get(type)!.push(element);
|
|
677
|
+
}
|
|
678
|
+
for (const [type, elements] of grouped) {
|
|
679
|
+
parent[type] = elements.length === 1 ? elements[0] : elements;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const orderedChildren: OrderedChildInfo[] = [];
|
|
683
|
+
const counters = new Map<string, number>();
|
|
684
|
+
for (const { type } of ordered) {
|
|
685
|
+
const idx = counters.get(type) || 0;
|
|
686
|
+
counters.set(type, idx + 1);
|
|
687
|
+
orderedChildren.push({ type, index: idx });
|
|
688
|
+
}
|
|
689
|
+
if (orderedChildren.length > 0) {
|
|
690
|
+
parent._orderedChildren = orderedChildren;
|
|
691
|
+
} else {
|
|
692
|
+
delete parent._orderedChildren;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Flatten an element's children into an ordered `{type, element}` list,
|
|
698
|
+
* honouring `_orderedChildren` when present and falling back to key order.
|
|
699
|
+
*/
|
|
700
|
+
private static getOrderedChildList(el: any): { type: string; element: any }[] {
|
|
701
|
+
const result: { type: string; element: any }[] = [];
|
|
702
|
+
const ordered = el._orderedChildren as OrderedChildInfo[] | undefined;
|
|
703
|
+
|
|
704
|
+
if (Array.isArray(ordered) && ordered.length > 0) {
|
|
705
|
+
const counters = new Map<string, number>();
|
|
706
|
+
for (const entry of ordered) {
|
|
707
|
+
const { type } = entry;
|
|
708
|
+
const idx = counters.get(type) || 0;
|
|
709
|
+
counters.set(type, idx + 1);
|
|
710
|
+
const value = el[type];
|
|
711
|
+
if (value === undefined) continue;
|
|
712
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
713
|
+
if (idx < arr.length) result.push({ type, element: arr[idx] });
|
|
714
|
+
}
|
|
715
|
+
return result;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
for (const key of Object.keys(el)) {
|
|
719
|
+
if (key.startsWith('@_') || key === '#text' || key === '_orderedChildren') continue;
|
|
720
|
+
const value = el[key];
|
|
721
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
722
|
+
for (const element of arr) result.push({ type: key, element });
|
|
723
|
+
}
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Convert restored deleted text back to live text throughout the tree
|
|
729
|
+
* (reject direction). After w:del unwrapping, the runs that were deleted
|
|
730
|
+
* still hold their text in w:delText / w:delInstrText; rename those to the
|
|
731
|
+
* normal w:t / w:instrText so the content is real again, fixing any
|
|
732
|
+
* `_orderedChildren` type references as we go.
|
|
733
|
+
*/
|
|
734
|
+
private static convertDeletedTextToNormal(obj: any): void {
|
|
735
|
+
if (obj === null || typeof obj !== 'object') return;
|
|
736
|
+
|
|
737
|
+
if (Array.isArray(obj)) {
|
|
738
|
+
for (const item of obj) RevisionWalker.convertDeletedTextToNormal(item);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// When this node carries any deleted-text element, rebuild its children
|
|
743
|
+
// from the document-ordered list with the deleted-text keys renamed. Going
|
|
744
|
+
// through the ordered list keeps text in its original position even if a
|
|
745
|
+
// run holds both a w:delText and a live w:t (CT_R permits interleaving).
|
|
746
|
+
const hasDeletedText = Object.keys(obj).some((k) => k in DELETED_TEXT_RENAMES);
|
|
747
|
+
if (hasDeletedText) {
|
|
748
|
+
const ordered = RevisionWalker.getOrderedChildList(obj).map((child) => ({
|
|
749
|
+
type: DELETED_TEXT_RENAMES[child.type] ?? child.type,
|
|
750
|
+
element: child.element,
|
|
751
|
+
}));
|
|
752
|
+
RevisionWalker.rebuildChildren(obj, ordered);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
for (const key of Object.keys(obj)) {
|
|
756
|
+
if (key.startsWith('@_') || key === '#text' || key === '_orderedChildren') continue;
|
|
757
|
+
RevisionWalker.convertDeletedTextToNormal(obj[key]);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
417
761
|
/**
|
|
418
762
|
* Merge a child value into the parent, handling arrays properly
|
|
419
763
|
*/
|