@docx4j/core-ts 0.5.0 → 0.7.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/NOTICE +4 -0
- package/README.md +92 -4
- package/dist/html/convert.mjs +2 -1
- package/dist/html/elements.mjs +2 -1
- package/dist/model/color/ColorMath.d.mts +29 -0
- package/dist/model/color/ColorMath.mjs +146 -0
- package/dist/model/color/ColorTables.d.mts +6 -0
- package/dist/model/color/ColorTables.mjs +83 -0
- package/dist/model/color/DrawingMLColors.d.mts +52 -0
- package/dist/model/color/DrawingMLColors.mjs +374 -0
- package/dist/model/color/ResolvedColor.d.mts +36 -0
- package/dist/model/color/ResolvedColor.mjs +65 -0
- package/dist/model/color/SpreadsheetColors.d.mts +36 -0
- package/dist/model/color/SpreadsheetColors.mjs +84 -0
- package/dist/model/color/StyleFill.d.mts +28 -0
- package/dist/model/color/StyleFill.mjs +21 -0
- package/dist/model/color/ThemeColors.d.mts +86 -0
- package/dist/model/color/ThemeColors.mjs +304 -0
- package/dist/model/color/WordColors.d.mts +71 -0
- package/dist/model/color/WordColors.mjs +121 -0
- package/dist/model/color/index.d.mts +6 -0
- package/dist/model/color/index.mjs +30 -0
- package/dist/model/content/Body.d.mts +145 -23
- package/dist/model/content/Body.mjs +507 -150
- package/dist/model/content/Comment.d.mts +23 -5
- package/dist/model/content/Comment.mjs +207 -26
- package/dist/model/content/ContentControl.d.mts +198 -19
- package/dist/model/content/ContentControl.mjs +672 -99
- package/dist/model/content/Font.d.mts +70 -17
- package/dist/model/content/Font.mjs +119 -36
- package/dist/model/content/InlinePicture.d.mts +11 -0
- package/dist/model/content/InlinePicture.mjs +21 -1
- package/dist/model/content/List.d.mts +6 -3
- package/dist/model/content/List.mjs +18 -8
- package/dist/model/content/Paragraph.d.mts +200 -14
- package/dist/model/content/Paragraph.mjs +898 -82
- package/dist/model/content/Range.d.mts +227 -12
- package/dist/model/content/Range.mjs +785 -59
- package/dist/model/content/ResolvedView.d.mts +104 -0
- package/dist/model/content/ResolvedView.mjs +596 -0
- package/dist/model/content/Table.d.mts +29 -3
- package/dist/model/content/Table.mjs +135 -49
- package/dist/model/content/TrackedChange.d.mts +174 -15
- package/dist/model/content/TrackedChange.mjs +734 -170
- package/dist/model/content/comments.d.mts +20 -6
- package/dist/model/content/comments.mjs +67 -57
- package/dist/model/content/generalException.d.mts +18 -0
- package/dist/model/content/generalException.mjs +24 -0
- package/dist/model/content/index.d.mts +10 -3
- package/dist/model/content/index.mjs +10 -2
- package/dist/model/content/mathText.d.mts +21 -0
- package/dist/model/content/mathText.mjs +267 -0
- package/dist/model/content/officeText.d.mts +27 -0
- package/dist/model/content/officeText.mjs +74 -0
- package/dist/model/content/ooxml.mjs +12 -1
- package/dist/model/content/outline.d.mts +5 -0
- package/dist/model/content/outline.mjs +13 -0
- package/dist/model/content/paraIds.d.mts +5 -0
- package/dist/model/content/paraIds.mjs +54 -0
- package/dist/model/content/placeholders.d.mts +110 -0
- package/dist/model/content/placeholders.mjs +253 -0
- package/dist/model/content/search.d.mts +74 -0
- package/dist/model/content/search.mjs +255 -9
- package/dist/model/content/spanEdits.d.mts +159 -0
- package/dist/model/content/spanEdits.mjs +728 -0
- package/dist/model/content/story.d.mts +47 -0
- package/dist/model/content/story.mjs +95 -0
- package/dist/model/content/tableColumns.d.mts +1 -1
- package/dist/model/content/tableColumns.mjs +108 -80
- package/dist/model/content/tableRevisions.d.mts +11 -3
- package/dist/model/content/tableRevisions.mjs +72 -42
- package/dist/model/content/tableRules.d.mts +30 -0
- package/dist/model/content/tableRules.mjs +102 -0
- package/dist/model/content/textBoxes.d.mts +18 -0
- package/dist/model/content/textBoxes.mjs +105 -0
- package/dist/model/content/trackedChangeFilter.d.mts +39 -0
- package/dist/model/content/trackedChangeFilter.mjs +81 -0
- package/dist/model/content/tracking.d.mts +63 -6
- package/dist/model/content/tracking.mjs +242 -22
- package/dist/model/content/tree.d.mts +172 -24
- package/dist/model/content/tree.mjs +328 -54
- package/dist/model/content/wordRange.d.mts +48 -0
- package/dist/model/content/wordRange.mjs +97 -0
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
- package/dist/model/customxml/XmlMapping.d.mts +32 -8
- package/dist/model/customxml/XmlMapping.mjs +64 -10
- package/dist/model/customxml/bindings.d.mts +33 -4
- package/dist/model/customxml/bindings.mjs +171 -45
- package/dist/model/customxml/index.d.mts +1 -0
- package/dist/model/customxml/index.mjs +1 -0
- package/dist/model/customxml/kinds.d.mts +8 -3
- package/dist/model/customxml/kinds.mjs +26 -5
- package/dist/model/customxml/opendope.d.mts +4 -1
- package/dist/model/customxml/opendope.mjs +9 -6
- package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
- package/dist/model/customxml/picturePlaceholder.mjs +32 -0
- package/dist/model/customxml/pictures.d.mts +4 -3
- package/dist/model/customxml/pictures.mjs +22 -8
- package/dist/model/customxml/storeItems.d.mts +57 -0
- package/dist/model/customxml/storeItems.mjs +122 -0
- package/dist/model/customxml/xhtml.mjs +23 -22
- package/dist/model/dateZone.d.mts +25 -0
- package/dist/model/dateZone.mjs +57 -0
- package/dist/model/index.d.mts +3 -0
- package/dist/model/index.mjs +5 -0
- package/dist/model/listnumbering/Emulator.d.mts +37 -5
- package/dist/model/listnumbering/Emulator.mjs +63 -9
- package/dist/model/properties/PropertyResolver.d.mts +74 -2
- package/dist/model/properties/PropertyResolver.mjs +133 -5
- package/dist/model/properties/catalogue.d.mts +19 -5
- package/dist/model/properties/catalogue.mjs +41 -15
- package/dist/model/properties/index.d.mts +1 -1
- package/dist/model/properties/log.d.mts +1 -1
- package/dist/model/properties/log.mjs +5 -5
- package/dist/model/properties/styleUtil.d.mts +3 -1
- package/dist/model/properties/styleUtil.mjs +20 -24
- package/dist/model/properties/table.mjs +4 -2
- package/dist/model/text/CaseMapping.d.mts +38 -0
- package/dist/model/text/CaseMapping.mjs +86 -0
- package/dist/model/text/Extractor.d.mts +349 -0
- package/dist/model/text/Extractor.mjs +2406 -0
- package/dist/model/text/LinkTargets.d.mts +45 -0
- package/dist/model/text/LinkTargets.mjs +170 -0
- package/dist/model/text/MarkdownExporter.d.mts +140 -0
- package/dist/model/text/MarkdownExporter.mjs +255 -0
- package/dist/model/text/PlainText.d.mts +29 -0
- package/dist/model/text/PlainText.mjs +542 -0
- package/dist/model/text/TextContext.d.mts +256 -0
- package/dist/model/text/TextContext.mjs +600 -0
- package/dist/model/text/TextOptions.d.mts +233 -0
- package/dist/model/text/TextOptions.mjs +419 -0
- package/dist/model/text/TextResult.d.mts +621 -0
- package/dist/model/text/TextResult.mjs +629 -0
- package/dist/model/text/Texts.d.mts +99 -0
- package/dist/model/text/Texts.mjs +187 -0
- package/dist/model/text/WmlToMarkdown.d.mts +120 -0
- package/dist/model/text/WmlToMarkdown.mjs +936 -0
- package/dist/model/text/caseMapping.generated.d.mts +7 -0
- package/dist/model/text/caseMapping.generated.mjs +214 -0
- package/dist/model/text/commonmark.d.mts +121 -0
- package/dist/model/text/commonmark.mjs +655 -0
- package/dist/model/text/index.d.mts +8 -0
- package/dist/model/text/index.mjs +9 -0
- package/dist/model/text/normalize.d.mts +8 -0
- package/dist/model/text/normalize.mjs +13 -0
- package/dist/model/text/outline.d.mts +4 -0
- package/dist/model/text/outline.mjs +71 -0
- package/dist/model/wordIntegers.d.mts +29 -0
- package/dist/model/wordIntegers.mjs +78 -0
- package/dist/office-js/document.mjs +15 -1
- package/dist/office-js/fragment.d.mts +7 -5
- package/dist/office-js/fragment.mjs +63 -14
- package/dist/office-js/proxy.d.mts +8 -0
- package/dist/office-js/proxy.mjs +29 -0
- package/dist/office-js/run.mjs +2 -0
- package/dist/office-js/supported.generated.mjs +16 -0
- package/dist/office-js/toApiScript.mjs +29 -12
- package/dist/opc/DirectoryPartStore.d.mts +6 -2
- package/dist/opc/DirectoryPartStore.mjs +11 -4
- package/dist/opc/FlatOpcPartStore.d.mts +4 -2
- package/dist/opc/FlatOpcPartStore.mjs +11 -8
- package/dist/opc/Load.d.mts +32 -0
- package/dist/opc/Load.mjs +115 -5
- package/dist/opc/PartStore.d.mts +34 -1
- package/dist/opc/PartStore.mjs +71 -8
- package/dist/opc/Save.d.mts +32 -0
- package/dist/opc/Save.mjs +110 -4
- package/dist/opc/ZipPartStore.d.mts +4 -2
- package/dist/opc/ZipPartStore.mjs +12 -7
- package/dist/opc/exceptions.d.mts +140 -0
- package/dist/opc/exceptions.mjs +108 -0
- package/dist/opc/index.d.mts +8 -5
- package/dist/opc/index.mjs +5 -3
- package/dist/opc/mce/preprocessor.d.mts +141 -5
- package/dist/opc/mce/preprocessor.mjs +119 -18
- package/dist/opc/mce/understood.d.mts +3 -11
- package/dist/opc/mce/understood.mjs +27 -23
- package/dist/opc/repair/breaks.d.mts +23 -0
- package/dist/opc/repair/breaks.mjs +288 -0
- package/dist/opc/repair/hosts.d.mts +12 -0
- package/dist/opc/repair/hosts.mjs +33 -0
- package/dist/opc/repair/mainRoot.d.mts +19 -0
- package/dist/opc/repair/mainRoot.mjs +168 -0
- package/dist/opc/repair/unknownElements.d.mts +8 -0
- package/dist/opc/repair/unknownElements.mjs +180 -0
- package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
- package/dist/opc/repair/wmlNames.generated.mjs +80 -0
- package/dist/opc/skipped.d.mts +50 -0
- package/dist/opc/skipped.mjs +140 -0
- package/dist/packages/OpcPackage.d.mts +110 -4
- package/dist/packages/OpcPackage.mjs +123 -7
- package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
- package/dist/packages/WordprocessingMLPackage.mjs +158 -21
- package/dist/packages/index.d.mts +1 -1
- package/dist/parts/DefaultXmlPart.mjs +2 -2
- package/dist/parts/Part.d.mts +14 -2
- package/dist/parts/RelationshipsPart.d.mts +15 -0
- package/dist/parts/RelationshipsPart.mjs +66 -1
- package/dist/parts/XmlPart.d.mts +63 -32
- package/dist/parts/XmlPart.mjs +240 -96
- package/dist/parts/wml/comments.mjs +30 -0
- package/dist/parts/wml/index.d.mts +12 -1
- package/dist/parts/wml/index.mjs +37 -1
- package/dist/xml/dom.d.mts +7 -0
- package/dist/xml/dom.mjs +18 -0
- package/package.json +2 -2
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { textOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
2
2
|
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
3
|
-
import { typeNameOf, runItemsOf, revisionKindOf, linkParents, segmentsOf, textOfView, cellsOf, rowsOf, childrenOf, W_NS, itemTextOf } from './tree.mjs';
|
|
3
|
+
import { typeNameOf, runItemsOf, revisionKindOf, linkParents, segmentsOf, textOfView, cellsOf, rowsOf, childrenOf, W_NS, itemTextOf, selectedBranchOf, branchesOf, BRANCH_TYPES, ALTERNATE_CONTENT } from './tree.mjs';
|
|
4
4
|
import { removeMarkers } from './comments.mjs';
|
|
5
5
|
import { Range } from './Range.mjs';
|
|
6
|
+
import { storyLayout, storyOf as storyOfParagraph } from './story.mjs';
|
|
6
7
|
import { rPrFromElements } from '@docx4j/generated-objects-ts/builders/wml';
|
|
7
8
|
import { revisionDateOf, toRestoredText, toDeletedText, restoreRPr, restorePPr, pruneParagraphProperties, HOLDER_TYPES, holderItemsOf } from './tracking.mjs';
|
|
8
9
|
import { tableRecordsOf, hasTableRecords, tableChangeText, rowText as tableRowText, acceptTable, rejectTable } from './tableRevisions.mjs';
|
|
@@ -10,8 +11,9 @@ import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
|
10
11
|
import { relationshipIdsIn, dropRelationshipsUnused, queueRelationshipRemovals } from './relationships.mjs';
|
|
11
12
|
/**
|
|
12
13
|
* A subset of Office JS `Word.TrackedChange`: `type`, `author`, `date`, `text`, `accept()`,
|
|
13
|
-
* `reject()` and `getRange()`, plus `element`, `id
|
|
14
|
-
* `Paragraph` and `Range`: it holds the markup it is
|
|
14
|
+
* `reject()` and `getRange()`, plus `element`, `id`, `target`, and docx4j's `kind`, `part` and
|
|
15
|
+
* `pieces` (CR-011) as extensions. A view, like `Paragraph` and `Range`: it holds the markup it is
|
|
16
|
+
* over, nothing is cached.
|
|
15
17
|
*/
|
|
16
18
|
export class TrackedChange {
|
|
17
19
|
constructor(target,
|
|
@@ -32,7 +34,7 @@ export class TrackedChange {
|
|
|
32
34
|
case 'row': return t.row === 'ins' ? 'Added' : 'Deleted';
|
|
33
35
|
case 'group': return t.revision === 'ins' ? 'Added' : t.revision === 'del' ? 'Deleted' : 'Formatted';
|
|
34
36
|
case 'cell': return t.cell === 'ins' ? 'Added' : 'Deleted';
|
|
35
|
-
case 'holder': return 'Added';
|
|
37
|
+
case 'holder': return t.revision === 'ins' ? 'Added' : 'Deleted';
|
|
36
38
|
case 'field': return 'Added';
|
|
37
39
|
case 'mathRun':
|
|
38
40
|
case 'mathStructure': return t.revision === 'ins' ? 'Added' : 'Deleted';
|
|
@@ -44,16 +46,63 @@ export class TrackedChange {
|
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
46
48
|
* When the change was made: `w16du:dateUtc` if the markup has it, else `w:date` read as Word writes
|
|
47
|
-
* it, local wall-clock time
|
|
48
|
-
* as Office JS reports it.
|
|
49
|
+
* it, local wall-clock time in the package's `dateTimeZone`, else the runtime's (`revisionDateOf`,
|
|
50
|
+
* CR-002 section 29, CR-018); a group's is its first piece's, as Office JS reports it.
|
|
49
51
|
*/
|
|
50
52
|
get date() {
|
|
51
|
-
|
|
53
|
+
var _a;
|
|
54
|
+
return revisionDateOf(this.target.value, (_a = this.package_) === null || _a === void 0 ? void 0 : _a.dateTimeZone);
|
|
52
55
|
}
|
|
53
56
|
/** The annotation id (`w:id`; an extension). */
|
|
54
57
|
get id() {
|
|
55
58
|
return this.target.value.id;
|
|
56
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* docx4j's kind of change (an extension; its `TrackedChange.getKind()`, CR-011): `target.kind` in
|
|
62
|
+
* docx4j's names, an equation's runs as runs. A group's is its pieces' when they are all of one kind,
|
|
63
|
+
* else `INSERTION`, `DELETION` or `FORMATTING`.
|
|
64
|
+
*/
|
|
65
|
+
get kind() {
|
|
66
|
+
const t = this.target;
|
|
67
|
+
switch (t.kind) {
|
|
68
|
+
case 'run': return t.revision === 'ins' ? 'RUN_INSERTION' : t.revision === 'del' ? 'RUN_DELETION' : 'MOVE';
|
|
69
|
+
case 'mark': return t.mark === 'ins' ? 'PARAGRAPH_MARK_INSERTION' : 'PARAGRAPH_MARK_DELETION';
|
|
70
|
+
case 'runProperties': return 'RUN_PROPERTIES';
|
|
71
|
+
case 'markProperties': return 'PARAGRAPH_MARK_PROPERTIES';
|
|
72
|
+
case 'paragraphProperties': return 'PARAGRAPH_PROPERTIES';
|
|
73
|
+
case 'row': return t.row === 'ins' ? 'ROW_INSERTION' : 'ROW_DELETION';
|
|
74
|
+
case 'field': return 'FIELD_INSERTION';
|
|
75
|
+
case 'holder': return t.revision === 'ins' ? 'CONTENT_CONTROL_INSERTION' : 'CONTENT_CONTROL_DELETION';
|
|
76
|
+
case 'mathRun': return t.revision === 'ins' ? 'RUN_INSERTION' : 'RUN_DELETION';
|
|
77
|
+
case 'mathStructure': return t.revision === 'ins' ? 'MATH_STRUCTURE_INSERTION' : 'MATH_STRUCTURE_DELETION';
|
|
78
|
+
case 'tableProperties': return 'TABLE_PROPERTIES';
|
|
79
|
+
case 'sectionProperties': return 'SECTION_PROPERTIES';
|
|
80
|
+
case 'cell': return t.cell === 'ins' ? 'CELL_INSERTION' : 'CELL_DELETION';
|
|
81
|
+
case 'group': {
|
|
82
|
+
const first = t.pieces[0].kind;
|
|
83
|
+
if (t.pieces.every((piece) => piece.kind === first))
|
|
84
|
+
return first;
|
|
85
|
+
return t.revision === 'ins' ? 'INSERTION' : t.revision === 'del' ? 'DELETION' : 'FORMATTING';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The part the change's story is in (an extension; docx4j's `getPart()`, CR-011): the main document
|
|
91
|
+
* part, a header or footer part, the footnotes, endnotes or comments part. A text box's change gives
|
|
92
|
+
* the part the text box is in.
|
|
93
|
+
*/
|
|
94
|
+
get part() {
|
|
95
|
+
var _a;
|
|
96
|
+
return (_a = this.body_) === null || _a === void 0 ? void 0 : _a.part;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The single changes this one groups, in document order (an extension; docx4j's `getPieces()`,
|
|
100
|
+
* CR-011); a change that is no group is its own one piece. A filter's conditions are met or not by
|
|
101
|
+
* each piece, and each can be accepted or rejected on its own.
|
|
102
|
+
*/
|
|
103
|
+
get pieces() {
|
|
104
|
+
return this.target.kind === 'group' ? this.target.pieces : [this];
|
|
105
|
+
}
|
|
57
106
|
/** The markup this change is over (an extension): the `w:ins` element, the `w:rPrChange`, ... */
|
|
58
107
|
get element() {
|
|
59
108
|
var _a;
|
|
@@ -83,10 +132,10 @@ export class TrackedChange {
|
|
|
83
132
|
case 'tableProperties': return tableChangeText(t.tbl.value);
|
|
84
133
|
case 'cell': return tableRowText(t.tr);
|
|
85
134
|
case 'sectionProperties': return t.text;
|
|
86
|
-
case 'run': return segmentsOf(t.element.value, { view: t.revision === 'ins' || t.revision === 'moveTo' ? 'accepted' : 'original' }).map((s) => s.text).join('');
|
|
135
|
+
case 'run': return segmentsOf(t.element.value, { view: t.revision === 'ins' || t.revision === 'moveTo' ? 'accepted' : 'original', characters: 'textOf' }).map((s) => s.text).join('');
|
|
87
136
|
case 'runProperties': return textOf(t.run);
|
|
88
137
|
// the field's result: its instruction is no text, as Office JS reports it (check 30: "1")
|
|
89
|
-
case 'field': return segmentsOf({ content: t.span }, { view: 'accepted' }).map((s) => s.text).join('');
|
|
138
|
+
case 'field': return segmentsOf({ content: t.span }, { view: 'accepted', characters: 'textOf' }).map((s) => s.text).join('');
|
|
90
139
|
case 'paragraphProperties': return (_b = (_a = this.paragraph) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : '';
|
|
91
140
|
// a deleted row's content is `w:delText` inside `w:del`, which the accepted view skips:
|
|
92
141
|
// its text is what the row said before, as for a run deletion
|
|
@@ -98,6 +147,7 @@ export class TrackedChange {
|
|
|
98
147
|
}
|
|
99
148
|
/** The range the change covers; a deletion is a collapsed range where it sits in the accepted text. */
|
|
100
149
|
getRange() {
|
|
150
|
+
var _a;
|
|
101
151
|
const p = this.paragraph;
|
|
102
152
|
if (!p)
|
|
103
153
|
return undefined;
|
|
@@ -107,15 +157,55 @@ export class TrackedChange {
|
|
|
107
157
|
return new Range(p, start, end);
|
|
108
158
|
}
|
|
109
159
|
if (t.kind === 'group') {
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
160
|
+
// the group from where its first piece starts to where its last ends, across paragraphs where its pieces are
|
|
161
|
+
// in several (CR-014 phase 1; check 52: an insertion through a paragraph's mark is one change whose range
|
|
162
|
+
// spans both paragraphs). A mark ends its own paragraph's range (check 53: an inserted paragraph's range is
|
|
163
|
+
// that paragraph, its mark included). A deletion is collapsed where it starts, its text no longer in the
|
|
164
|
+
// accepted view, as Word's default view reads it (check 53). Until then a group's range was its part in the
|
|
165
|
+
// paragraph it starts in.
|
|
166
|
+
const ranges = t.pieces.filter((piece) => piece.target.kind === 'run' || piece.target.kind === 'runProperties' || piece.target.kind === 'field')
|
|
167
|
+
.map((piece) => piece.getRange()).filter((r) => r !== undefined);
|
|
168
|
+
const marks = t.pieces.filter((piece) => piece.target.kind === 'mark' && piece.paragraph !== undefined).map((piece) => piece.paragraph);
|
|
169
|
+
if (ranges.length === 0 && marks.length === 0)
|
|
113
170
|
return t.pieces[0].getRange();
|
|
114
|
-
|
|
171
|
+
// within one paragraph, as most groups are: no story to lay out
|
|
172
|
+
if (marks.length === 0 && ranges.every((r) => r.paragraph.element === ranges[0].paragraph.element && !r.spansParagraphs)) {
|
|
173
|
+
return new Range(ranges[0].paragraph, Math.min(...ranges.map((r) => r.start)), Math.max(...ranges.map((r) => r.end)));
|
|
174
|
+
}
|
|
175
|
+
const layout = storyLayout(storyOfParagraph(p.p).items, (element, container) => p.viewOf(element, container).text);
|
|
176
|
+
const at = (paragraph, offset) => {
|
|
177
|
+
const i = layout.indexOf(paragraph.p);
|
|
178
|
+
return i < 0 ? Number.NaN : layout.paragraphs[i].start + offset;
|
|
179
|
+
};
|
|
180
|
+
// each piece's span in the story's text; a mark's is its paragraph's end, holding the mark
|
|
181
|
+
const spans = ranges.map((r) => ({ start: at(r.paragraph, r.start), end: at(r.endParagraph, r.end) }));
|
|
182
|
+
for (const paragraph of marks)
|
|
183
|
+
spans.push({ start: at(paragraph, paragraph.text.length), end: at(paragraph, paragraph.text.length), mark: paragraph });
|
|
184
|
+
if (spans.some((span) => Number.isNaN(span.start) || Number.isNaN(span.end))) {
|
|
185
|
+
const here = ranges.filter((r) => r.paragraph.element === p.element);
|
|
186
|
+
return here.length === 0 ? (_a = ranges[0]) !== null && _a !== void 0 ? _a : t.pieces[0].getRange()
|
|
187
|
+
: new Range(p, Math.min(...here.map((r) => r.start)), Math.max(...here.map((r) => r.end)));
|
|
188
|
+
}
|
|
189
|
+
const from = Math.min(...spans.map((span) => span.start));
|
|
190
|
+
const first = layout.paragraphs.find((sp) => sp.start <= from && from <= sp.end);
|
|
191
|
+
const view = (sp) => (sp.element === p.element ? p : p.viewOf(sp.element, sp.container));
|
|
192
|
+
if (this.type === 'Deleted')
|
|
193
|
+
return new Range(view(first), from - first.start, from - first.start);
|
|
194
|
+
// the last end, a mark's after text ending at the same place
|
|
195
|
+
const last = spans.reduce((a, b) => (b.end > a.end || (b.end === a.end && b.mark !== undefined) ? b : a));
|
|
196
|
+
const lastParagraph = layout.paragraphs.find((sp) => sp.start <= last.end && last.end <= sp.end && (last.mark === undefined || sp.element === last.mark.element));
|
|
197
|
+
return new Range(view(first), from - first.start, last.end - lastParagraph.start, false, last.mark !== undefined, lastParagraph === first ? undefined : view(lastParagraph));
|
|
198
|
+
}
|
|
199
|
+
if (t.kind === 'mark') {
|
|
200
|
+
// a paragraph's mark alone: inserted, the mark (check 53: \r); deleted, collapsed at the paragraph's end,
|
|
201
|
+
// as Word's default view reads it. Until CR-014 phase 1 it was the paragraph's whole range
|
|
202
|
+
const length = p.text.length;
|
|
203
|
+
return new Range(p, length, length, false, t.mark === 'ins');
|
|
115
204
|
}
|
|
116
205
|
if (t.kind === 'field') {
|
|
117
|
-
const start =
|
|
118
|
-
|
|
206
|
+
const [start] = spanOf(p, t.span[0]);
|
|
207
|
+
const length = segmentsOf({ content: t.span }, { hidden: p.hiddenRuns() }).map((s) => s.text).join('').length;
|
|
208
|
+
return new Range(p, start, start + length);
|
|
119
209
|
}
|
|
120
210
|
if (t.kind === 'runProperties') {
|
|
121
211
|
const segs = p.segments().filter((s) => s.run === t.run);
|
|
@@ -123,9 +213,13 @@ export class TrackedChange {
|
|
|
123
213
|
return p.getRange('Start');
|
|
124
214
|
return new Range(p, segs[0].start, segs[segs.length - 1].end);
|
|
125
215
|
}
|
|
126
|
-
//
|
|
216
|
+
// the equation's text, Word's drawing of it, since CR-012 phase 4; collapsed where it stands when the
|
|
217
|
+
// view leaves it none
|
|
127
218
|
if ((t.kind === 'mathRun' || t.kind === 'mathStructure') && t.equation) {
|
|
128
|
-
const
|
|
219
|
+
const equation = p.segments().find((seg) => seg.math && seg.item === t.equation);
|
|
220
|
+
if (equation)
|
|
221
|
+
return new Range(p, equation.start, equation.end);
|
|
222
|
+
const [at] = spanOf(p, t.equation);
|
|
129
223
|
return new Range(p, at, at);
|
|
130
224
|
}
|
|
131
225
|
return p.getRange('Whole');
|
|
@@ -133,9 +227,27 @@ export class TrackedChange {
|
|
|
133
227
|
/**
|
|
134
228
|
* Keeps the change: a `w:ins` is unwrapped, a `w:del` removed, a deleted mark joins the paragraphs.
|
|
135
229
|
* Either half of a move keeps the whole move, as Word's Review tab does (CR-002 section 29).
|
|
230
|
+
*
|
|
231
|
+
* Returns whether it acted: false, changing nothing, when the change has been resolved since it was
|
|
232
|
+
* listed (CR-002 section 53). An extension: Office JS's `accept()` returns nothing.
|
|
136
233
|
*/
|
|
137
234
|
accept() {
|
|
138
|
-
|
|
235
|
+
if (!this.isCurrent())
|
|
236
|
+
return false;
|
|
237
|
+
resolving(this.package_, this.body_, () => { this.acceptHere(); this.resolveTwins('accept'); });
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The change's twins resolved the same way: the changes in the other branches of the kept
|
|
242
|
+
* `mc:AlternateContent` this one was listed from, paired with it by kind and position (`pairTwins`,
|
|
243
|
+
* docx4j's; CR-013 phase 3), each with its own twins. A group's twins are its pieces', which its
|
|
244
|
+
* pieces resolve. A twin resolved since it was listed is passed over.
|
|
245
|
+
*/
|
|
246
|
+
resolveTwins(what) {
|
|
247
|
+
var _a;
|
|
248
|
+
for (const twin of (_a = twinsOf.get(this)) !== null && _a !== void 0 ? _a : [])
|
|
249
|
+
if (twin.isCurrent())
|
|
250
|
+
twin[what]();
|
|
139
251
|
}
|
|
140
252
|
acceptHere() {
|
|
141
253
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -186,7 +298,7 @@ export class TrackedChange {
|
|
|
186
298
|
if (t.cell === 'ins')
|
|
187
299
|
(_d = t.tc.value.tcPr) === null || _d === void 0 ? true : delete _d.cellIns;
|
|
188
300
|
else
|
|
189
|
-
removeCell(t.tc, t.owner);
|
|
301
|
+
removeCell(t.tc, t.owner, t.tr);
|
|
190
302
|
return;
|
|
191
303
|
case 'markProperties': {
|
|
192
304
|
const p = this.requireParagraph().p;
|
|
@@ -198,8 +310,12 @@ export class TrackedChange {
|
|
|
198
310
|
delete t.sectPr.sectPrChange;
|
|
199
311
|
return;
|
|
200
312
|
case 'holder':
|
|
201
|
-
// the element stays, the markers go (check 30: accept all leaves the control, no marker)
|
|
202
|
-
|
|
313
|
+
// an insertion: the element stays, the markers go (check 30: accept all leaves the control, no marker);
|
|
314
|
+
// a deletion: the element goes, its content with it (check 54 case 15: Word's Accept All took the control)
|
|
315
|
+
if (t.revision === 'ins')
|
|
316
|
+
removeHolderMarkers(t);
|
|
317
|
+
else
|
|
318
|
+
removeHolder(t);
|
|
203
319
|
return;
|
|
204
320
|
case 'field':
|
|
205
321
|
for (const el of t.span) {
|
|
@@ -218,9 +334,59 @@ export class TrackedChange {
|
|
|
218
334
|
/**
|
|
219
335
|
* Puts back what was there: a `w:ins` is removed, a `w:del` restored, a `w:rPrChange` re-applied.
|
|
220
336
|
* Either half of a move rejects the whole move, as Word's Review tab does (CR-002 section 29).
|
|
337
|
+
*
|
|
338
|
+
* Returns whether it acted, as `accept()` does.
|
|
221
339
|
*/
|
|
222
340
|
reject() {
|
|
223
|
-
|
|
341
|
+
if (!this.isCurrent())
|
|
342
|
+
return false;
|
|
343
|
+
resolving(this.package_, this.body_, () => { this.rejectHere(); this.resolveTwins('reject'); });
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Whether the record this view was listed for, `target.value`, is still where it was listed. A view
|
|
348
|
+
* whose change has been resolved since, by itself or by another verb, does nothing; otherwise it
|
|
349
|
+
* would act on what stands there now (CR-002 section 53). The record is compared by identity. docx4j's
|
|
350
|
+
* views throw instead, once any change on the package has been resolved since they were listed; that
|
|
351
|
+
* would stop a loop resolving one listing last first, as `Body.acceptAll` does.
|
|
352
|
+
*/
|
|
353
|
+
isCurrent() {
|
|
354
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
355
|
+
const t = this.target;
|
|
356
|
+
const inList = (owner, element) => currentOwner(owner, element).includes(element);
|
|
357
|
+
switch (t.kind) {
|
|
358
|
+
case 'run':
|
|
359
|
+
return inList(t.owner, t.element);
|
|
360
|
+
case 'mathRun':
|
|
361
|
+
return ((_a = t.run.value.content) !== null && _a !== void 0 ? _a : []).includes(t.item);
|
|
362
|
+
case 'tableProperties':
|
|
363
|
+
return hasTableRecords(t.tbl.value); // resolved from the table's records as they stand
|
|
364
|
+
case 'group':
|
|
365
|
+
return t.pieces.some((piece) => piece.isCurrent()); // each piece decides for itself
|
|
366
|
+
case 'mark': {
|
|
367
|
+
const paragraph = this.paragraph;
|
|
368
|
+
return !!paragraph && ((_c = (_b = paragraph.p.pPr) === null || _b === void 0 ? void 0 : _b.rPr) === null || _c === void 0 ? void 0 : _c[t.mark]) === t.value && inList(paragraph.container, paragraph.element);
|
|
369
|
+
}
|
|
370
|
+
case 'runProperties':
|
|
371
|
+
return ((_d = t.run.rPr) === null || _d === void 0 ? void 0 : _d.rPrChange) === t.value;
|
|
372
|
+
case 'paragraphProperties':
|
|
373
|
+
return ((_f = (_e = this.paragraph) === null || _e === void 0 ? void 0 : _e.p.pPr) === null || _f === void 0 ? void 0 : _f.pPrChange) === t.value;
|
|
374
|
+
case 'markProperties':
|
|
375
|
+
return ((_j = (_h = (_g = this.paragraph) === null || _g === void 0 ? void 0 : _g.p.pPr) === null || _h === void 0 ? void 0 : _h.rPr) === null || _j === void 0 ? void 0 : _j.rPrChange) === t.value;
|
|
376
|
+
case 'sectionProperties':
|
|
377
|
+
return t.sectPr.sectPrChange === t.value;
|
|
378
|
+
case 'row':
|
|
379
|
+
return ((_k = t.tr.value.trPr) === null || _k === void 0 ? void 0 : _k[t.row]) === t.value && inList(t.owner, t.tr);
|
|
380
|
+
case 'cell':
|
|
381
|
+
return ((_l = t.tc.value.tcPr) === null || _l === void 0 ? void 0 : _l[t.cell === 'ins' ? 'cellIns' : 'cellDel']) === t.value && inList(t.owner, t.tc);
|
|
382
|
+
case 'holder':
|
|
383
|
+
return inList(t.owner, t.outer[0]);
|
|
384
|
+
case 'field':
|
|
385
|
+
return t.span.length > 0 && inList(t.owner, t.span[0]);
|
|
386
|
+
case 'mathStructure':
|
|
387
|
+
// an equation's lists are not the ones childrenOf gives its PARENT, so the structure's own
|
|
388
|
+
return t.ctrlPr[t.revision] === t.value && (!t.structure || !t.owner || currentOwner(t.owner, t.structure).includes(t.structure));
|
|
389
|
+
}
|
|
224
390
|
}
|
|
225
391
|
rejectHere() {
|
|
226
392
|
var _a, _b;
|
|
@@ -271,7 +437,7 @@ export class TrackedChange {
|
|
|
271
437
|
if (t.cell === 'del')
|
|
272
438
|
(_b = t.tc.value.tcPr) === null || _b === void 0 ? true : delete _b.cellDel;
|
|
273
439
|
else
|
|
274
|
-
removeCell(t.tc, t.owner);
|
|
440
|
+
removeCell(t.tc, t.owner, t.tr);
|
|
275
441
|
return;
|
|
276
442
|
case 'markProperties':
|
|
277
443
|
restoreMarkProperties(this.requireParagraph().p, t.value);
|
|
@@ -280,10 +446,13 @@ export class TrackedChange {
|
|
|
280
446
|
restoreSection(t.sectPr, t.value);
|
|
281
447
|
return;
|
|
282
448
|
case 'holder':
|
|
283
|
-
// the element goes, its content stays where it stood: what is left of it after the
|
|
284
|
-
// inside were rejected (nothing, for content inserted with it; check 30: reject all removes
|
|
285
|
-
// control, and an existing control, which has no markers, stays)
|
|
286
|
-
|
|
449
|
+
// an insertion: the element goes, its content stays where it stood: what is left of it after the
|
|
450
|
+
// pieces inside were rejected (nothing, for content inserted with it; check 30: reject all removes
|
|
451
|
+
// the control, and an existing control, which has no markers, stays); a deletion: the markers go
|
|
452
|
+
if (t.revision === 'ins')
|
|
453
|
+
unwrapHolder(t);
|
|
454
|
+
else
|
|
455
|
+
removeHolderMarkers(t);
|
|
287
456
|
return;
|
|
288
457
|
case 'field':
|
|
289
458
|
removeField(t, this.paragraph);
|
|
@@ -329,19 +498,20 @@ function rowText(tr, view) {
|
|
|
329
498
|
for (const el of (_a = childrenOf(tc)) !== null && _a !== void 0 ? _a : []) {
|
|
330
499
|
const tn = typeNameOf(el);
|
|
331
500
|
if (tn === 'org_docx4j_wml.P') {
|
|
332
|
-
out += (afterParagraph ? '\r' : '') + textOfView(el.value, { view });
|
|
501
|
+
out += (afterParagraph ? '\r' : '') + textOfView(el.value, { view, characters: 'textOf' });
|
|
333
502
|
afterParagraph = true;
|
|
334
503
|
}
|
|
335
504
|
else if (tn === 'org_docx4j_wml.Tbl') {
|
|
336
|
-
for (const row of rowsOf(el.value))
|
|
505
|
+
for (const row of rowsOf(el.value, 'read'))
|
|
337
506
|
out += rowText(row.element.value, view);
|
|
338
507
|
afterParagraph = false;
|
|
339
508
|
}
|
|
340
509
|
else if (typeof el.value === 'object' && el.value !== null) {
|
|
341
|
-
// a block control or custom XML element
|
|
342
|
-
|
|
510
|
+
// a block control or custom XML element, or a kept mc:AlternateContent's selected branch (CR-013
|
|
511
|
+
// phase 2): what it holds, as if it were not there
|
|
512
|
+
for (const inner of (_b = childrenOf(el.value, 'read')) !== null && _b !== void 0 ? _b : []) {
|
|
343
513
|
if (typeNameOf(inner) === 'org_docx4j_wml.P') {
|
|
344
|
-
out += (afterParagraph ? '\r' : '') + textOfView(inner.value, { view });
|
|
514
|
+
out += (afterParagraph ? '\r' : '') + textOfView(inner.value, { view, characters: 'textOf' });
|
|
345
515
|
afterParagraph = true;
|
|
346
516
|
}
|
|
347
517
|
}
|
|
@@ -349,7 +519,7 @@ function rowText(tr, view) {
|
|
|
349
519
|
}
|
|
350
520
|
return out;
|
|
351
521
|
};
|
|
352
|
-
return cellsOf(tr).map((cell) => cellText(cell.element.value)).join('\t') + '\r\n';
|
|
522
|
+
return cellsOf(tr, 'read').map((cell) => cellText(cell.element.value)).join('\t') + '\r\n';
|
|
353
523
|
}
|
|
354
524
|
/** The changes inside a row, last first, so that a paragraph join never disturbs one still to do. */
|
|
355
525
|
function applyInner(inner, what) {
|
|
@@ -412,7 +582,7 @@ function indentWithin(ind, level) {
|
|
|
412
582
|
* never strands a piece still to do. Office JS and Word's Review tab resolve the group as one (checks
|
|
413
583
|
* 22 and 23), and so, piece by piece, does this.
|
|
414
584
|
*/
|
|
415
|
-
function resolvePieces(pieces, what) {
|
|
585
|
+
export function resolvePieces(pieces, what) {
|
|
416
586
|
const runs = pieces.filter((piece) => piece.target.kind !== 'mark');
|
|
417
587
|
const marks = pieces.filter((piece) => piece.target.kind === 'mark');
|
|
418
588
|
for (let i = runs.length - 1; i >= 0; i--)
|
|
@@ -421,14 +591,13 @@ function resolvePieces(pieces, what) {
|
|
|
421
591
|
marks[i][what]();
|
|
422
592
|
}
|
|
423
593
|
/**
|
|
424
|
-
* The list an element is in now: `owner` as the change was listed
|
|
425
|
-
*
|
|
426
|
-
*
|
|
594
|
+
* The list an element is in now: the list its `PARENT` keeps, else `owner` as the change was listed.
|
|
595
|
+
* A piece listed inside a holder that was unwrapped since (its content moved out into the owner, check
|
|
596
|
+
* 30) is still found, and so is a run whose paragraph was joined into the one before it: the joined
|
|
597
|
+
* paragraph's own list still holds it, but is no longer in the document (CR-002 section 53).
|
|
427
598
|
*/
|
|
428
599
|
function currentOwner(owner, element) {
|
|
429
600
|
var _a, _b;
|
|
430
|
-
if (owner.includes(element))
|
|
431
|
-
return owner;
|
|
432
601
|
const parent = (_a = element.value) === null || _a === void 0 ? void 0 : _a.PARENT;
|
|
433
602
|
const actual = parent ? (_b = runItemsOf(parent)) !== null && _b !== void 0 ? _b : childrenOf(parent) : undefined;
|
|
434
603
|
return (actual === null || actual === void 0 ? void 0 : actual.includes(element)) ? actual : owner;
|
|
@@ -462,17 +631,21 @@ let resolution;
|
|
|
462
631
|
* goes when nothing left in the part references it; its part goes when no relationship in the package
|
|
463
632
|
* targets it any more, and what that part alone related to goes with it. The kinds beyond pictures,
|
|
464
633
|
* headers and footers (charts, OLE objects, `v:imagedata`) are docx4j's reading, not measured.
|
|
634
|
+
* - **Comments**: a comment `removeRevision` left with its range's start and end in different paragraphs
|
|
635
|
+
* goes whole when the joins have brought them into one list with nothing with text between
|
|
636
|
+
* (`settleSpanningComments`; CR-002 section 54).
|
|
465
637
|
* A change resolved inside another's (a row's cells, a group's pieces, `acceptAll`'s changes) shares the
|
|
466
638
|
* outer resolution, so the bookmarks are read twice for the lot, and not at all when nothing is removed.
|
|
467
639
|
*/
|
|
468
640
|
export function resolving(pkg, body, resolve) {
|
|
469
|
-
var _a, _b, _c;
|
|
641
|
+
var _a, _b, _c, _d, _e, _f;
|
|
470
642
|
const notes = pkg === null || pkg === void 0 ? void 0 : pkg.pendingNoteRemovals;
|
|
471
643
|
const root = storyRootOf(body);
|
|
472
644
|
const outer = resolution;
|
|
473
645
|
if (outer) {
|
|
474
646
|
(_a = outer.root) !== null && _a !== void 0 ? _a : (outer.root = root);
|
|
475
647
|
(_b = outer.part) !== null && _b !== void 0 ? _b : (outer.part = body === null || body === void 0 ? void 0 : body.part);
|
|
648
|
+
(_c = outer.body) !== null && _c !== void 0 ? _c : (outer.body = body);
|
|
476
649
|
const previous = outer.notes;
|
|
477
650
|
outer.notes = notes !== null && notes !== void 0 ? notes : previous;
|
|
478
651
|
try {
|
|
@@ -484,12 +657,18 @@ export function resolving(pkg, body, resolve) {
|
|
|
484
657
|
return;
|
|
485
658
|
}
|
|
486
659
|
resolution = { notes, root, part: body === null || body === void 0 ? void 0 : body.part };
|
|
660
|
+
if (body)
|
|
661
|
+
resolution.body = body;
|
|
487
662
|
try {
|
|
488
663
|
resolve();
|
|
489
664
|
const done = resolution;
|
|
665
|
+
if ((_d = done.comments) === null || _d === void 0 ? void 0 : _d.size)
|
|
666
|
+
settleSpanningComments(done.comments);
|
|
667
|
+
if (((_e = done.commentCandidates) === null || _e === void 0 ? void 0 : _e.size) && done.root && done.body)
|
|
668
|
+
settleCommentAnchors(done.root, done.body, done.commentCandidates);
|
|
490
669
|
if (done.root && done.before)
|
|
491
670
|
dropBookmarksBroken(done.root, done.before);
|
|
492
|
-
if (done.part && ((
|
|
671
|
+
if (done.part && ((_f = done.relationships) === null || _f === void 0 ? void 0 : _f.size))
|
|
493
672
|
dropRelationshipsUnused(done.part, done.relationships);
|
|
494
673
|
}
|
|
495
674
|
finally {
|
|
@@ -509,15 +688,17 @@ function storyRootOf(body) {
|
|
|
509
688
|
* whose `w:sectPr` references its section's headers and footers.
|
|
510
689
|
*/
|
|
511
690
|
function discarding(element, properties) {
|
|
512
|
-
var _a, _b;
|
|
691
|
+
var _a, _b, _c;
|
|
513
692
|
if (!resolution)
|
|
514
693
|
return;
|
|
515
694
|
if (resolution.root)
|
|
516
695
|
(_a = resolution.before) !== null && _a !== void 0 ? _a : (resolution.before = bookmarksIn(resolution.root));
|
|
517
696
|
if (element && resolution.notes)
|
|
518
697
|
noteReferencesIn(element, resolution.notes);
|
|
698
|
+
if (element)
|
|
699
|
+
commentAnchorsIn(element, ((_b = resolution.commentCandidates) !== null && _b !== void 0 ? _b : (resolution.commentCandidates = new Set())));
|
|
519
700
|
if (resolution.part && (element || properties)) {
|
|
520
|
-
const into = ((
|
|
701
|
+
const into = ((_c = resolution.relationships) !== null && _c !== void 0 ? _c : (resolution.relationships = new Set()));
|
|
521
702
|
if (element)
|
|
522
703
|
relationshipIdsIn(element, into);
|
|
523
704
|
if (properties)
|
|
@@ -632,6 +813,17 @@ function dropBookmarksBroken(root, before) {
|
|
|
632
813
|
};
|
|
633
814
|
visit(root);
|
|
634
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* Queues the notes whose references `removed` holds to go when the package is saved, as a resolution's
|
|
818
|
+
* removals go: what a range deleted untracked takes with it (CR-014 phase 2; check 52 case 12, where Word took
|
|
819
|
+
* the footnote out of `footnotes.xml` with its reference). A note goes only where no reference to it stands
|
|
820
|
+
* by then (`flushPendingNoteRemovals`), so content queued and then kept costs nothing.
|
|
821
|
+
*/
|
|
822
|
+
export function queueNoteRemovals(pkg, removed) {
|
|
823
|
+
const notes = pkg === null || pkg === void 0 ? void 0 : pkg.pendingNoteRemovals;
|
|
824
|
+
if (notes)
|
|
825
|
+
noteReferencesIn(removed, notes);
|
|
826
|
+
}
|
|
635
827
|
/** Adds the ids of the `w:footnoteReference` and `w:endnoteReference` elements anywhere in a tree to `into`. */
|
|
636
828
|
function noteReferencesIn(tree, into) {
|
|
637
829
|
const visit = (o) => {
|
|
@@ -656,6 +848,20 @@ function noteReferencesIn(tree, into) {
|
|
|
656
848
|
};
|
|
657
849
|
visit(tree);
|
|
658
850
|
}
|
|
851
|
+
/**
|
|
852
|
+
* The notes `pending` holds that `flushPendingNoteRemovals` will take away: those whose reference no
|
|
853
|
+
* longer stands in the main document's tree. A listing of every story leaves them out, as docx4j's,
|
|
854
|
+
* which removes them when its resolution ends, does (CR-011).
|
|
855
|
+
*/
|
|
856
|
+
export function notesGoing(mainTree, pending) {
|
|
857
|
+
const standing = { footnote: new Set(), endnote: new Set() };
|
|
858
|
+
if (pending.footnote.size + pending.endnote.size > 0)
|
|
859
|
+
noteReferencesIn(mainTree, standing);
|
|
860
|
+
return {
|
|
861
|
+
footnote: new Set([...pending.footnote].filter((id) => !standing.footnote.has(id))),
|
|
862
|
+
endnote: new Set([...pending.endnote].filter((id) => !standing.endnote.has(id))),
|
|
863
|
+
};
|
|
864
|
+
}
|
|
659
865
|
/**
|
|
660
866
|
* The notes `pending` holds taken out of the notes parts, each unless a reference to it still stands in
|
|
661
867
|
* the main document (an accepted move keeps the reference its source had, say); then `pending` is
|
|
@@ -696,7 +902,7 @@ export async function flushPendingNoteRemovals(main, pending, host) {
|
|
|
696
902
|
pending.endnote.clear();
|
|
697
903
|
}
|
|
698
904
|
/** Adds the ids of the `w:commentReference` elements anywhere in a tree to `into`. */
|
|
699
|
-
function commentReferencesIn(tree, into) {
|
|
905
|
+
export function commentReferencesIn(tree, into) {
|
|
700
906
|
const visit = (o) => {
|
|
701
907
|
var _a, _b;
|
|
702
908
|
if (typeof o !== 'object' || o === null)
|
|
@@ -718,7 +924,12 @@ function commentReferencesIn(tree, into) {
|
|
|
718
924
|
};
|
|
719
925
|
visit(tree);
|
|
720
926
|
}
|
|
721
|
-
/** A holder's
|
|
927
|
+
/** A holder's deletion accepted: the markers go, and the element with what it holds. */
|
|
928
|
+
function removeHolder(t) {
|
|
929
|
+
removeHolderMarkers(t);
|
|
930
|
+
remove(t.owner, t.holder);
|
|
931
|
+
}
|
|
932
|
+
/** A holder's four insertion (or deletion) markers taken away; the element stays. */
|
|
722
933
|
function removeHolderMarkers(t) {
|
|
723
934
|
var _a;
|
|
724
935
|
remove(t.owner, t.outer[0]);
|
|
@@ -744,26 +955,29 @@ function unwrapHolder(t) {
|
|
|
744
955
|
* The insertion of a content control or custom XML element, when `items[i]` is a
|
|
745
956
|
* `w:customXmlInsRangeStart` and what follows is Word's form for one (check 30): the element, whose
|
|
746
957
|
* content starts with that marker's `w:customXmlInsRangeEnd` and ends with a second start, and that
|
|
747
|
-
* one's end right after the element
|
|
958
|
+
* one's end right after the element; or its deletion, the same form of `w:customXmlDelRangeStart` and
|
|
959
|
+
* `End` (check 54). Anything else is not one (a marker on its own is left alone).
|
|
748
960
|
*/
|
|
749
|
-
export function
|
|
750
|
-
var _a, _b, _c, _d, _e, _f;
|
|
961
|
+
export function holderChangeAt(items, i, paragraph, body) {
|
|
962
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
751
963
|
const start = items[i];
|
|
752
|
-
|
|
964
|
+
const revision = ((_a = start.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'customXmlInsRangeStart' ? 'ins' : ((_b = start.name) === null || _b === void 0 ? void 0 : _b.localPart) === 'customXmlDelRangeStart' ? 'del' : undefined;
|
|
965
|
+
if (!revision)
|
|
753
966
|
return undefined;
|
|
967
|
+
const [startName, endName] = revision === 'ins' ? ['customXmlInsRangeStart', 'customXmlInsRangeEnd'] : ['customXmlDelRangeStart', 'customXmlDelRangeEnd'];
|
|
754
968
|
const holder = items[i + 1];
|
|
755
969
|
const outerEnd = items[i + 2];
|
|
756
|
-
if (!holder || !outerEnd || !HOLDER_TYPES.has((
|
|
970
|
+
if (!holder || !outerEnd || !HOLDER_TYPES.has((_c = typeNameOf(holder)) !== null && _c !== void 0 ? _c : '') || ((_d = outerEnd.name) === null || _d === void 0 ? void 0 : _d.localPart) !== endName)
|
|
757
971
|
return undefined;
|
|
758
|
-
const inner = (
|
|
972
|
+
const inner = (_e = childrenOf(holder.value)) !== null && _e !== void 0 ? _e : [];
|
|
759
973
|
const innerEnd = inner[0];
|
|
760
974
|
const innerStart = inner[inner.length - 1];
|
|
761
|
-
if (inner.length < 2 || !innerEnd || !innerStart || ((
|
|
975
|
+
if (inner.length < 2 || !innerEnd || !innerStart || ((_f = innerEnd.name) === null || _f === void 0 ? void 0 : _f.localPart) !== endName || ((_g = innerStart.name) === null || _g === void 0 ? void 0 : _g.localPart) !== startName)
|
|
762
976
|
return undefined;
|
|
763
977
|
const idOf = (marker) => marker.value.id;
|
|
764
978
|
if (idOf(innerEnd) !== idOf(start) || idOf(outerEnd) !== idOf(innerStart))
|
|
765
979
|
return undefined;
|
|
766
|
-
return new TrackedChange({ kind: 'holder', holder, owner: items, outer: [start, outerEnd], inner: [innerEnd, innerStart], value: start.value }, paragraph);
|
|
980
|
+
return new TrackedChange({ kind: 'holder', revision, holder, owner: items, outer: [start, outerEnd], inner: [innerEnd, innerStart], value: start.value }, paragraph, body);
|
|
767
981
|
}
|
|
768
982
|
/**
|
|
769
983
|
* An inserted field rejected (Word's form, check 30 `field-inserted`): the `w:ins` pieces and the plain
|
|
@@ -853,45 +1067,56 @@ export function insertedFieldAt(items, i, paragraph) {
|
|
|
853
1067
|
*/
|
|
854
1068
|
function removeRow(t) {
|
|
855
1069
|
remove(t.owner, t.tr);
|
|
856
|
-
|
|
1070
|
+
// a row in any branch of a kept row-level mc:AlternateContent keeps the table (CR-013 phase 2, docx4j's
|
|
1071
|
+
// Lister.hasRows): counted over the selected branch alone, the table went with every branch's rows
|
|
1072
|
+
if (t.table && rowsOf(t.table.element.value, 'every').length === 0)
|
|
857
1073
|
remove(t.table.container, t.table.element);
|
|
858
1074
|
}
|
|
859
1075
|
/**
|
|
860
1076
|
* A revision taken away with its runs - a rejected insertion, an accepted deletion - and the comments
|
|
861
|
-
* on it,
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
1077
|
+
* on it, as Word 365 resolves them (CR-002 section 54; docx4j CR-043 section 15, `Resolver.removeRevision`):
|
|
1078
|
+
* a comment whose range start was inside the revision, or whose markers lie either side of it in its
|
|
1079
|
+
* list, goes whole - markers, reference run and entry - once nothing with text is left in its range.
|
|
1080
|
+
* - Word writes a comment's range start inside the `w:ins` of the text it is on; current Word removed
|
|
1081
|
+
* the comment with that text (check 32: rejecting the insertion, rejecting all, and accepting the
|
|
1082
|
+
* deletion in the same shape).
|
|
1083
|
+
* - Every marker outside the revision (this package's own form before 0.3.1, a tracker's that wraps
|
|
1084
|
+
* only the text): Word 365's Reject All of the editor's `review-for-word.docx` and of docx4j-python's
|
|
1085
|
+
* artefact 8, and its Accept All of a deletion (docx4j's U21), removed the comment whole too. Word
|
|
1086
|
+
* 2010 and Word 15 kept it, its range markers dropped, which was the rule here until section 54.
|
|
1087
|
+
* - A range whose start and end are in different paragraphs is judged when the resolution ends, once
|
|
1088
|
+
* the paragraph marks taken away have joined what is left (`resolving`): Word 365's Reject All of two
|
|
1089
|
+
* inserted paragraphs commented whole removed the comment (docx4j's U23). A range still crossing a
|
|
1090
|
+
* paragraph mark stays (docx4j's U24, not measured).
|
|
1091
|
+
* The markers and the reference go now, the entries in the comment parts when the comments are next
|
|
1092
|
+
* read or the package saved (`pendingCommentRemovals`). A comment with text left in its range keeps its
|
|
1093
|
+
* markers where the text was (not measured), and so does one whose range was empty before.
|
|
874
1094
|
*/
|
|
875
1095
|
function removeRevision(t, paragraph) {
|
|
876
|
-
var _a, _b, _c;
|
|
1096
|
+
var _a, _b, _c, _d;
|
|
877
1097
|
const owner = currentOwner(t.owner, t.element);
|
|
878
1098
|
const i = owner.indexOf(t.element);
|
|
879
1099
|
if (i < 0)
|
|
880
1100
|
return;
|
|
881
1101
|
const kept = ((_a = runItemsOf(t.element.value)) !== null && _a !== void 0 ? _a : []).filter((item) => { var _a, _b; return ((_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeStart' || ((_b = item.name) === null || _b === void 0 ? void 0 : _b.localPart) === 'commentRangeEnd'; });
|
|
882
|
-
// the comments whose range held the revision: a start inside it (Word's form)
|
|
883
|
-
// with its end after it (the
|
|
884
|
-
|
|
885
|
-
const inside = new Set(kept.filter((m) => { var _a; return ((_a = m.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeStart'; }).map(
|
|
1102
|
+
// the comments whose range held the revision: a start inside it (Word's form); a start before it
|
|
1103
|
+
// with its end after it (the markers beside the runs) or in another paragraph; an end after it with
|
|
1104
|
+
// its start in another paragraph
|
|
1105
|
+
const inside = new Set(kept.filter((m) => { var _a; return ((_a = m.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeStart'; }).map(commentIdOf));
|
|
886
1106
|
const outside = new Set();
|
|
887
1107
|
for (let s = 0; s < i; s++) {
|
|
888
1108
|
const el = owner[s];
|
|
889
1109
|
if (((_b = el.name) === null || _b === void 0 ? void 0 : _b.localPart) !== 'commentRangeStart')
|
|
890
1110
|
continue;
|
|
891
|
-
const id =
|
|
892
|
-
if (owner
|
|
1111
|
+
const id = commentIdOf(el);
|
|
1112
|
+
if (markerAt(owner, s + 1, owner.length, 'commentRangeEnd', id) < 0 || markerAt(owner, i + 1, owner.length, 'commentRangeEnd', id) >= 0)
|
|
893
1113
|
outside.add(id);
|
|
894
1114
|
}
|
|
1115
|
+
for (let e = i + 1; e < owner.length; e++) {
|
|
1116
|
+
const el = owner[e];
|
|
1117
|
+
if (((_c = el.name) === null || _c === void 0 ? void 0 : _c.localPart) === 'commentRangeEnd' && markerAt(owner, 0, e, 'commentRangeStart', commentIdOf(el)) < 0)
|
|
1118
|
+
outside.add(commentIdOf(el));
|
|
1119
|
+
}
|
|
895
1120
|
discarding(t.element);
|
|
896
1121
|
owner.splice(i, 1, ...kept);
|
|
897
1122
|
linkParents(kept, t.element.value.PARENT);
|
|
@@ -899,23 +1124,147 @@ function removeRevision(t, paragraph) {
|
|
|
899
1124
|
if (!body)
|
|
900
1125
|
return;
|
|
901
1126
|
for (const id of [...inside, ...outside]) {
|
|
902
|
-
const start = owner
|
|
903
|
-
if (
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
removeMarkers(body.container, id);
|
|
907
|
-
const pkg = body.package_;
|
|
908
|
-
(_c = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals) === null || _c === void 0 ? void 0 : _c.add(id);
|
|
1127
|
+
const start = markerAt(owner, 0, owner.length, 'commentRangeStart', id);
|
|
1128
|
+
if (start < 0 || markerAt(owner, start + 1, owner.length, 'commentRangeEnd', id) < 0) {
|
|
1129
|
+
if (resolution)
|
|
1130
|
+
((_d = resolution.comments) !== null && _d !== void 0 ? _d : (resolution.comments = new Map())).set(id, body); // start and end in different paragraphs
|
|
909
1131
|
}
|
|
910
|
-
else {
|
|
911
|
-
|
|
912
|
-
const end = owner.find((el) => { var _a; return ((_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeEnd' && idOf(el) === id; });
|
|
913
|
-
for (const marker of [end, start])
|
|
914
|
-
if (marker)
|
|
915
|
-
owner.splice(owner.indexOf(marker), 1);
|
|
1132
|
+
else if (rangeIsEmpty(owner, owner[start], id)) {
|
|
1133
|
+
removeCommentWhole(body, id);
|
|
916
1134
|
}
|
|
917
1135
|
}
|
|
918
1136
|
}
|
|
1137
|
+
const commentIdOf = (el) => { var _a; return (_a = el.value) === null || _a === void 0 ? void 0 : _a.id; };
|
|
1138
|
+
/** The index in `list`, from `from` up to `to`, of a comment's range start or end (`local`), or -1. */
|
|
1139
|
+
function markerAt(list, from, to, local, id) {
|
|
1140
|
+
var _a;
|
|
1141
|
+
for (let i = from; i < to; i++)
|
|
1142
|
+
if (((_a = list[i].name) === null || _a === void 0 ? void 0 : _a.localPart) === local && commentIdOf(list[i]) === id)
|
|
1143
|
+
return i;
|
|
1144
|
+
return -1;
|
|
1145
|
+
}
|
|
1146
|
+
/** A comment taken away: its markers and reference run now, its entries when the comments are next read or the package saved. */
|
|
1147
|
+
function removeCommentWhole(body, id) {
|
|
1148
|
+
var _a;
|
|
1149
|
+
removeMarkers(body.container, id);
|
|
1150
|
+
const pkg = body.package_;
|
|
1151
|
+
(_a = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals) === null || _a === void 0 ? void 0 : _a.add(id);
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* The comments `removeRevision` left with their range's start and end in different lists, judged once
|
|
1155
|
+
* the resolution's joins are made (docx4j's `Removals.removeCommentIfEmptied`): one goes whole when its
|
|
1156
|
+
* start and end now stand in one list with nothing with text between them.
|
|
1157
|
+
*/
|
|
1158
|
+
function settleSpanningComments(comments) {
|
|
1159
|
+
for (const [id, body] of comments) {
|
|
1160
|
+
const list = listHolding(storyRootOf(body), (el) => { var _a; return ((_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeStart' && commentIdOf(el) === id; });
|
|
1161
|
+
if (!list)
|
|
1162
|
+
continue;
|
|
1163
|
+
const start = markerAt(list, 0, list.length, 'commentRangeStart', id);
|
|
1164
|
+
if (rangeIsEmpty(list, list[start], id))
|
|
1165
|
+
removeCommentWhole(body, id);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* The comments whose reference or range marker the resolution removed, judged once it ends, as docx4j's
|
|
1170
|
+
* `Removals.flush` judges its candidates (its CR-043; core-ts check 32): one whose reference no longer stands
|
|
1171
|
+
* anywhere in the story goes whole, since Word keeps no comment without one; one with its reference and half its
|
|
1172
|
+
* range left loses that half. A tracked deletion holds a comment's reference where it took one (CR-014 section
|
|
1173
|
+
* 12.6, Word's case 28), so accepting it takes the comment, as the untracked deletion does.
|
|
1174
|
+
*/
|
|
1175
|
+
function settleCommentAnchors(root, body, candidates) {
|
|
1176
|
+
var _a;
|
|
1177
|
+
const pending = (_a = body.package_) === null || _a === void 0 ? void 0 : _a.pendingCommentRemovals;
|
|
1178
|
+
const references = new Set();
|
|
1179
|
+
const starts = new Set();
|
|
1180
|
+
const ends = new Set();
|
|
1181
|
+
forEachCommentAnchor(root, (name, id) => (name === 'commentReference' ? references : name === 'commentRangeStart' ? starts : ends).add(id));
|
|
1182
|
+
for (const id of candidates) {
|
|
1183
|
+
if (pending === null || pending === void 0 ? void 0 : pending.has(id))
|
|
1184
|
+
continue;
|
|
1185
|
+
if (!references.has(id))
|
|
1186
|
+
removeCommentWhole(body, id);
|
|
1187
|
+
else if (starts.has(id) !== ends.has(id))
|
|
1188
|
+
removeRangeMarkers(root, id);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
/** Adds the ids of the comment references and range markers anywhere in a tree to `into`. */
|
|
1192
|
+
function commentAnchorsIn(tree, into) {
|
|
1193
|
+
forEachCommentAnchor(tree, (_name, id) => into.add(id));
|
|
1194
|
+
}
|
|
1195
|
+
/** Calls `found` for each comment reference, range start and range end anywhere in a tree. */
|
|
1196
|
+
function forEachCommentAnchor(tree, found) {
|
|
1197
|
+
const visit = (o) => {
|
|
1198
|
+
var _a, _b;
|
|
1199
|
+
if (typeof o !== 'object' || o === null)
|
|
1200
|
+
return;
|
|
1201
|
+
if (Array.isArray(o)) {
|
|
1202
|
+
for (const item of o)
|
|
1203
|
+
visit(item);
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const name = (_a = o.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
1207
|
+
if (name === 'commentReference' || name === 'commentRangeStart' || name === 'commentRangeEnd') {
|
|
1208
|
+
const id = Number((_b = (o.value)) === null || _b === void 0 ? void 0 : _b.id);
|
|
1209
|
+
if (Number.isInteger(id))
|
|
1210
|
+
found(name, id);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
for (const [key, value] of Object.entries(o))
|
|
1214
|
+
if (key !== 'PARENT')
|
|
1215
|
+
visit(value);
|
|
1216
|
+
};
|
|
1217
|
+
visit(tree);
|
|
1218
|
+
}
|
|
1219
|
+
/** A comment's range markers taken out of a tree, its reference kept: what is left of half a range. */
|
|
1220
|
+
function removeRangeMarkers(tree, id) {
|
|
1221
|
+
const visit = (o) => {
|
|
1222
|
+
var _a;
|
|
1223
|
+
if (typeof o !== 'object' || o === null)
|
|
1224
|
+
return;
|
|
1225
|
+
if (Array.isArray(o)) {
|
|
1226
|
+
for (let i = o.length - 1; i >= 0; i--) {
|
|
1227
|
+
const el = o[i];
|
|
1228
|
+
const name = (_a = el === null || el === void 0 ? void 0 : el.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
1229
|
+
if ((name === 'commentRangeStart' || name === 'commentRangeEnd') && commentIdOf(el) === id)
|
|
1230
|
+
o.splice(i, 1);
|
|
1231
|
+
else
|
|
1232
|
+
visit(el);
|
|
1233
|
+
}
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
for (const [key, value] of Object.entries(o))
|
|
1237
|
+
if (key !== 'PARENT')
|
|
1238
|
+
visit(value);
|
|
1239
|
+
};
|
|
1240
|
+
visit(tree);
|
|
1241
|
+
}
|
|
1242
|
+
/** The list under `root` (block, run and run-content lists, math's) holding an item `match` matches, or undefined. */
|
|
1243
|
+
function listHolding(root, match) {
|
|
1244
|
+
let found;
|
|
1245
|
+
const seen = new Set();
|
|
1246
|
+
const visit = (o) => {
|
|
1247
|
+
if (found || typeof o !== 'object' || o === null || seen.has(o))
|
|
1248
|
+
return;
|
|
1249
|
+
seen.add(o);
|
|
1250
|
+
if (Array.isArray(o)) {
|
|
1251
|
+
if (o.some((item) => typeof item === 'object' && item !== null && match(item))) {
|
|
1252
|
+
found = o;
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
for (const item of o)
|
|
1256
|
+
visit(item);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
if (o.nodeType !== undefined)
|
|
1260
|
+
return; // a DOM node
|
|
1261
|
+
for (const key in o)
|
|
1262
|
+
if (key !== 'PARENT')
|
|
1263
|
+
visit(o[key]);
|
|
1264
|
+
};
|
|
1265
|
+
visit(root);
|
|
1266
|
+
return found;
|
|
1267
|
+
}
|
|
919
1268
|
/** True when nothing with text stands between a comment's range start and its end in the same list. */
|
|
920
1269
|
function rangeIsEmpty(owner, start, id) {
|
|
921
1270
|
var _a;
|
|
@@ -946,11 +1295,13 @@ function hasText(el) {
|
|
|
946
1295
|
/** A `w:ins` or `w:moveTo` accepted: its runs take its place. */
|
|
947
1296
|
function unwrap(t) {
|
|
948
1297
|
var _a;
|
|
949
|
-
const
|
|
1298
|
+
const owner = currentOwner(t.owner, t.element);
|
|
1299
|
+
const i = owner.indexOf(t.element);
|
|
950
1300
|
if (i < 0)
|
|
951
1301
|
return;
|
|
952
|
-
|
|
953
|
-
t.
|
|
1302
|
+
// the revision keeps nothing, so a view listed inside it no longer finds its element there (section 53)
|
|
1303
|
+
const items = ((_a = runItemsOf(t.element.value)) !== null && _a !== void 0 ? _a : []).splice(0);
|
|
1304
|
+
owner.splice(i, 1, ...items);
|
|
954
1305
|
const parent = t.value.PARENT;
|
|
955
1306
|
for (const item of items)
|
|
956
1307
|
linkParents(item, parent);
|
|
@@ -980,29 +1331,84 @@ function dropMark(paragraph, which) {
|
|
|
980
1331
|
*
|
|
981
1332
|
* `fallbackToPrevious` is for the second case at the end of a container, where this package's
|
|
982
1333
|
* inserted paragraph carries its own mark (section 13) and there is nothing after it to join
|
|
983
|
-
* with: the paragraph then goes, its content joining the one before.
|
|
984
|
-
*
|
|
1334
|
+
* with: the paragraph then goes, its content joining the one before. Otherwise, with no paragraph
|
|
1335
|
+
* next, a join never crosses a container's edge, and Word 365's Accept All (check 54 and the text-left
|
|
1336
|
+
* probe, docx4j CR-043 section 17) does one of four things: an emptied paragraph with a table or a
|
|
1337
|
+
* block control next goes; text left with a table next joins the table's first cell's first paragraph,
|
|
1338
|
+
* which keeps its properties; with a block control next it takes the control's first paragraph's
|
|
1339
|
+
* properties, nothing joined; a control's last paragraph keeps a bare mark. Else the mark is only dropped.
|
|
985
1340
|
*/
|
|
986
1341
|
export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
987
|
-
var _a, _b, _c, _d, _e;
|
|
1342
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
988
1343
|
discarding();
|
|
989
1344
|
const container = currentOwner(paragraph.container, paragraph.element);
|
|
990
1345
|
const i = container.indexOf(paragraph.element);
|
|
991
|
-
|
|
1346
|
+
// range markers between the two (a bookmark's end, a move's range end) go into the joined paragraph
|
|
1347
|
+
// where they stood, as docx4j's join (its CR-043, C10 and C10b) and Word's: found by CR-011's goldens
|
|
1348
|
+
let j = i + 1;
|
|
1349
|
+
while (i >= 0 && j < container.length && isRangeMarker(container[j]))
|
|
1350
|
+
j++;
|
|
1351
|
+
const next = i >= 0 ? container[j] : undefined;
|
|
992
1352
|
const p = paragraph.p;
|
|
993
1353
|
if (!next || typeNameOf(next) !== 'org_docx4j_wml.P') {
|
|
994
1354
|
const previous = fallbackToPrevious && i > 0 ? container[i - 1] : undefined;
|
|
995
1355
|
if (previous && typeNameOf(previous) === 'org_docx4j_wml.P') {
|
|
996
1356
|
const into = previous.value;
|
|
997
1357
|
const items = ((_a = into.content) !== null && _a !== void 0 ? _a : (into.content = []));
|
|
998
|
-
for (const item of (_b = p.content) !== null && _b !== void 0 ? _b : [])
|
|
999
|
-
items.push(item);
|
|
1358
|
+
for (const item of ((_b = p.content) !== null && _b !== void 0 ? _b : []).splice(0))
|
|
1359
|
+
items.push(item); // this paragraph keeps nothing
|
|
1000
1360
|
linkParents(items, into);
|
|
1001
1361
|
discarding(undefined, p.pPr);
|
|
1002
1362
|
container.splice(i, 1);
|
|
1003
1363
|
return;
|
|
1004
1364
|
}
|
|
1005
|
-
|
|
1365
|
+
// Word 365's Accept All where no paragraph follows (check 54 and the text-left probe; docx4j CR-043 section 17,
|
|
1366
|
+
// 29dbf5964 and e55a83b8e): a join never crosses a container's edge, and four shapes do something besides dropping
|
|
1367
|
+
// the mark, in docx4j's order
|
|
1368
|
+
const emptied = !((_c = p.content) !== null && _c !== void 0 ? _c : []).some((item) => !isRangeMarker(item));
|
|
1369
|
+
const table = next !== undefined && typeNameOf(next) === 'org_docx4j_wml.Tbl';
|
|
1370
|
+
const control = next !== undefined && HOLDER_TYPES.has((_d = typeNameOf(next)) !== null && _d !== void 0 ? _d : '');
|
|
1371
|
+
if (i >= 0 && emptied && (table || control)) {
|
|
1372
|
+
// check 54 case 9, the probe's case 3: an emptied paragraph with a table or a block control next goes, which
|
|
1373
|
+
// then follows what came before
|
|
1374
|
+
discarding(undefined, p.pPr);
|
|
1375
|
+
container.splice(i, 1);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
if (i >= 0 && table) {
|
|
1379
|
+
// the probe's case 1: text left with a table next joins the table's first cell's first paragraph, at its front,
|
|
1380
|
+
// the range markers after it with it; that paragraph keeps its own properties
|
|
1381
|
+
const first = firstCellParagraphOf(next.value);
|
|
1382
|
+
if (first) {
|
|
1383
|
+
const moved = [...((_e = p.content) !== null && _e !== void 0 ? _e : []).splice(0), ...container.slice(i + 1, j)];
|
|
1384
|
+
((_f = first.content) !== null && _f !== void 0 ? _f : (first.content = [])).splice(0, 0, ...moved);
|
|
1385
|
+
linkParents(moved, first);
|
|
1386
|
+
discarding(undefined, p.pPr);
|
|
1387
|
+
container.splice(i, j - i);
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if (control) {
|
|
1392
|
+
// check 54 case 19, the probe's case 5: text left with a block control next: nothing joins, but the paragraph
|
|
1393
|
+
// takes the properties a join would have given it, the control's first paragraph's
|
|
1394
|
+
const first = firstParagraphOf(childrenOf(next.value));
|
|
1395
|
+
if (first) {
|
|
1396
|
+
discarding(undefined, p.pPr);
|
|
1397
|
+
p.pPr = first.pPr ? deepCopy(first.pPr) : undefined;
|
|
1398
|
+
if (p.pPr)
|
|
1399
|
+
linkParents(p.pPr, p);
|
|
1400
|
+
else
|
|
1401
|
+
delete p.pPr;
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (i >= 0 && next === undefined && SDT_CONTENTS.has((_h = (_g = p.PARENT) === null || _g === void 0 ? void 0 : _g.TYPE_NAME) !== null && _h !== void 0 ? _h : '')) {
|
|
1406
|
+
// check 54 case 13, the probe's case 2: a control's last paragraph keeps a bare mark, its text with it, if any
|
|
1407
|
+
discarding(undefined, p.pPr);
|
|
1408
|
+
delete p.pPr;
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
const rPr = (_j = p.pPr) === null || _j === void 0 ? void 0 : _j.rPr;
|
|
1006
1412
|
if (rPr) {
|
|
1007
1413
|
delete rPr.ins;
|
|
1008
1414
|
delete rPr.del;
|
|
@@ -1011,9 +1417,11 @@ export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
|
1011
1417
|
return;
|
|
1012
1418
|
}
|
|
1013
1419
|
const nextP = next.value;
|
|
1014
|
-
const content = ((
|
|
1015
|
-
for (const item of (
|
|
1420
|
+
const content = ((_k = p.content) !== null && _k !== void 0 ? _k : (p.content = []));
|
|
1421
|
+
for (const item of container.slice(i + 1, j))
|
|
1016
1422
|
content.push(item);
|
|
1423
|
+
for (const item of ((_l = nextP.content) !== null && _l !== void 0 ? _l : []).splice(0))
|
|
1424
|
+
content.push(item); // the next keeps nothing
|
|
1017
1425
|
linkParents(content, p);
|
|
1018
1426
|
discarding(undefined, p.pPr); // a section break's sectPr goes with it
|
|
1019
1427
|
p.pPr = nextP.pPr;
|
|
@@ -1030,7 +1438,42 @@ export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
|
1030
1438
|
else
|
|
1031
1439
|
p[key] = value;
|
|
1032
1440
|
}
|
|
1033
|
-
container.splice(i + 1,
|
|
1441
|
+
container.splice(i + 1, j - i);
|
|
1442
|
+
}
|
|
1443
|
+
/** A block-level control's content, which holds paragraphs. */
|
|
1444
|
+
const SDT_CONTENTS = new Set(['org_docx4j_wml.SdtContentBlock', 'org_docx4j_wml.CTSdtContentBlock']);
|
|
1445
|
+
/** The first paragraph of a table's first cell, where a paragraph joining into the table lands; docx4j's `firstCellParagraph`. */
|
|
1446
|
+
export function firstCellParagraphOf(tbl) {
|
|
1447
|
+
var _a, _b;
|
|
1448
|
+
for (const row of ((_a = tbl.content) !== null && _a !== void 0 ? _a : [])) {
|
|
1449
|
+
if (typeNameOf(row) !== 'org_docx4j_wml.Tr')
|
|
1450
|
+
continue;
|
|
1451
|
+
for (const cell of ((_b = row.value.content) !== null && _b !== void 0 ? _b : [])) {
|
|
1452
|
+
if (typeNameOf(cell) !== 'org_docx4j_wml.Tc')
|
|
1453
|
+
continue;
|
|
1454
|
+
return firstParagraphOf(cell.value.content);
|
|
1455
|
+
}
|
|
1456
|
+
return undefined;
|
|
1457
|
+
}
|
|
1458
|
+
return undefined;
|
|
1459
|
+
}
|
|
1460
|
+
/** The first paragraph among a holder's blocks, before any table; docx4j's `firstParagraph`. */
|
|
1461
|
+
function firstParagraphOf(items) {
|
|
1462
|
+
for (const item of items !== null && items !== void 0 ? items : []) {
|
|
1463
|
+
const tn = typeNameOf(item);
|
|
1464
|
+
if (tn === 'org_docx4j_wml.P')
|
|
1465
|
+
return item.value;
|
|
1466
|
+
if (tn === 'org_docx4j_wml.Tbl')
|
|
1467
|
+
return undefined;
|
|
1468
|
+
}
|
|
1469
|
+
return undefined;
|
|
1470
|
+
}
|
|
1471
|
+
/** A marker that stands between two paragraphs without keeping them apart: a range's start or end, a bookmark's, a permission's, a proofing mark. */
|
|
1472
|
+
function isRangeMarker(el) {
|
|
1473
|
+
var _a, _b;
|
|
1474
|
+
const name = (_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '';
|
|
1475
|
+
return name.endsWith('RangeStart') || name.endsWith('RangeEnd') || name === 'bookmarkStart' || name === 'bookmarkEnd'
|
|
1476
|
+
|| name === 'permStart' || name === 'permEnd' || name === 'proofErr';
|
|
1034
1477
|
}
|
|
1035
1478
|
/** A `w:p`'s own attributes, which go with its mark. */
|
|
1036
1479
|
const PARAGRAPH_ATTRIBUTES = ['paraId', 'textId', 'rsidDel', 'rsidP', 'rsidR', 'rsidRDefault', 'rsidRPr', 'noSpellErr'];
|
|
@@ -1043,39 +1486,15 @@ function pruneRowProperties(tr) {
|
|
|
1043
1486
|
}
|
|
1044
1487
|
/** The span of a run-level revision in the paragraph's accepted text; collapsed for a deletion. */
|
|
1045
1488
|
function spanOf(paragraph, element) {
|
|
1046
|
-
|
|
1489
|
+
// where the walk passes the element: a deletion stands there in the accepted text, before what follows it
|
|
1490
|
+
let at;
|
|
1491
|
+
const segs = paragraph.segments({ visit: (el, offset) => { if (el === element && at === undefined)
|
|
1492
|
+
at = offset; } });
|
|
1047
1493
|
const inside = segs.filter((s) => { var _a; return ((_a = s.revision) === null || _a === void 0 ? void 0 : _a.element) === element; });
|
|
1048
1494
|
if (inside.length > 0)
|
|
1049
1495
|
return [inside[0].start, inside[inside.length - 1].end];
|
|
1050
|
-
const
|
|
1051
|
-
return [at, at];
|
|
1052
|
-
}
|
|
1053
|
-
/** How much accepted text comes before `target` in the paragraph. */
|
|
1054
|
-
function acceptedOffsetOf(p, target) {
|
|
1055
|
-
let pos = 0;
|
|
1056
|
-
let found = -1;
|
|
1057
|
-
const visit = (items) => {
|
|
1058
|
-
if (!items || found >= 0)
|
|
1059
|
-
return;
|
|
1060
|
-
for (const el of items) {
|
|
1061
|
-
if (found >= 0)
|
|
1062
|
-
return;
|
|
1063
|
-
if (el === target) {
|
|
1064
|
-
found = pos;
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
if (typeNameOf(el) === 'org_docx4j_wml.R') {
|
|
1068
|
-
pos += segmentsOf({ content: [el] }).map((s) => s.text).join('').length;
|
|
1069
|
-
continue;
|
|
1070
|
-
}
|
|
1071
|
-
const kind = revisionKindOf(el);
|
|
1072
|
-
if (kind === 'del' || kind === 'moveFrom')
|
|
1073
|
-
continue;
|
|
1074
|
-
visit(runItemsOf(el.value));
|
|
1075
|
-
}
|
|
1076
|
-
};
|
|
1077
|
-
visit(runItemsOf(p));
|
|
1078
|
-
return found >= 0 ? found : pos;
|
|
1496
|
+
const end = segs.length > 0 ? segs[segs.length - 1].end : 0;
|
|
1497
|
+
return [at !== null && at !== void 0 ? at : end, at !== null && at !== void 0 ? at : end];
|
|
1079
1498
|
}
|
|
1080
1499
|
// --- math (CR-002 section 51) ------------------------------------------------------------------
|
|
1081
1500
|
/** True for a value of the math model (`m:oMath`, `m:r`, `m:sSup`, ...). */
|
|
@@ -1102,7 +1521,7 @@ function resolveMathRun(t, keep) {
|
|
|
1102
1521
|
if (i < 0)
|
|
1103
1522
|
return;
|
|
1104
1523
|
if (keep) {
|
|
1105
|
-
const moved = ((_b = t.value.rest) !== null && _b !== void 0 ? _b : []);
|
|
1524
|
+
const moved = ((_b = t.value.rest) !== null && _b !== void 0 ? _b : []).splice(0);
|
|
1106
1525
|
content.splice(i, 1, ...moved);
|
|
1107
1526
|
linkParents(moved, t.run.value);
|
|
1108
1527
|
return;
|
|
@@ -1110,7 +1529,9 @@ function resolveMathRun(t, keep) {
|
|
|
1110
1529
|
discarding(t.item);
|
|
1111
1530
|
content.splice(i, 1);
|
|
1112
1531
|
if (!content.some((item) => { var _a; return mathTextOf(item) !== undefined || ((_a = item.value) === null || _a === void 0 ? void 0 : _a.TYPE_NAME) === 'org_docx4j_wml.CTMathRunTrackChange'; })) {
|
|
1532
|
+
const container = t.run.value.PARENT;
|
|
1113
1533
|
remove(t.runOwner, t.run);
|
|
1534
|
+
dropEmptiedEquation(container);
|
|
1114
1535
|
}
|
|
1115
1536
|
}
|
|
1116
1537
|
/**
|
|
@@ -1119,14 +1540,16 @@ function resolveMathRun(t, keep) {
|
|
|
1119
1540
|
* whole from its list. An argument's own marker has no structure, and is dropped either way.
|
|
1120
1541
|
*/
|
|
1121
1542
|
function resolveMathStructure(t, keep) {
|
|
1122
|
-
var _a;
|
|
1543
|
+
var _a, _b;
|
|
1123
1544
|
if (!keep && t.structure && t.owner) {
|
|
1545
|
+
const container = (_a = t.structure.value) === null || _a === void 0 ? void 0 : _a.PARENT;
|
|
1124
1546
|
remove(t.owner, t.structure);
|
|
1547
|
+
dropEmptiedEquation(container);
|
|
1125
1548
|
return;
|
|
1126
1549
|
}
|
|
1127
1550
|
const ctrlPr = t.ctrlPr;
|
|
1128
1551
|
if (!ctrlPr.rPr && t.value.rPr) {
|
|
1129
|
-
ctrlPr.rPr = rPrFromElements((
|
|
1552
|
+
ctrlPr.rPr = rPrFromElements((_b = t.value.rPr.egrPrBase) !== null && _b !== void 0 ? _b : []);
|
|
1130
1553
|
linkParents(ctrlPr.rPr, ctrlPr);
|
|
1131
1554
|
}
|
|
1132
1555
|
if (t.revision === 'ins')
|
|
@@ -1134,6 +1557,37 @@ function resolveMathStructure(t, keep) {
|
|
|
1134
1557
|
else
|
|
1135
1558
|
delete ctrlPr.del;
|
|
1136
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* An `m:oMath` that a resolution left with nothing in it goes, and an `m:oMathPara` left with none of its
|
|
1562
|
+
* `m:oMath` goes with it, as Word's Accept All of an equation deleted whole has it (check 57, CR-002 section
|
|
1563
|
+
* 62.2, F1; docx4j's `Resolver.mathRun` leaves them). A paragraph whose mark went with the equation is then
|
|
1564
|
+
* empty, or joined to the next one empty-handed. An argument left empty stays: its structure keeps its places.
|
|
1565
|
+
*/
|
|
1566
|
+
function dropEmptiedEquation(container) {
|
|
1567
|
+
var _a;
|
|
1568
|
+
const equation = container;
|
|
1569
|
+
if ((equation === null || equation === void 0 ? void 0 : equation.TYPE_NAME) !== 'org_docx4j_math.CTOMath' || ((_a = equation.egoMathElements) !== null && _a !== void 0 ? _a : []).length > 0)
|
|
1570
|
+
return;
|
|
1571
|
+
const holder = equation.PARENT;
|
|
1572
|
+
if ((holder === null || holder === void 0 ? void 0 : holder.TYPE_NAME) === 'org_docx4j_math.CTOMathPara') {
|
|
1573
|
+
const i = holder.oMath.indexOf(equation);
|
|
1574
|
+
if (i >= 0)
|
|
1575
|
+
holder.oMath.splice(i, 1);
|
|
1576
|
+
if (holder.oMath.length === 0)
|
|
1577
|
+
removeValue(holder);
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
removeValue(equation);
|
|
1581
|
+
}
|
|
1582
|
+
/** A value's element taken from the list its `PARENT` keeps. */
|
|
1583
|
+
function removeValue(value) {
|
|
1584
|
+
var _a;
|
|
1585
|
+
const parent = value.PARENT;
|
|
1586
|
+
const list = parent ? (_a = runItemsOf(parent)) !== null && _a !== void 0 ? _a : childrenOf(parent) : undefined;
|
|
1587
|
+
const element = list === null || list === void 0 ? void 0 : list.find((e) => e === value || (e === null || e === void 0 ? void 0 : e.value) === value);
|
|
1588
|
+
if (list && element)
|
|
1589
|
+
remove(list, element);
|
|
1590
|
+
}
|
|
1137
1591
|
/** The equation a math piece is in: the outermost level's element, the one in the paragraph. */
|
|
1138
1592
|
function equationOf(stack) {
|
|
1139
1593
|
var _a;
|
|
@@ -1221,7 +1675,7 @@ function resolveMove(change, action) {
|
|
|
1221
1675
|
const t = change.target;
|
|
1222
1676
|
if (t.kind !== 'run' || !change.paragraph)
|
|
1223
1677
|
return false;
|
|
1224
|
-
const story = storyOf(
|
|
1678
|
+
const story = storyOf(t.element.value);
|
|
1225
1679
|
const pieces = movePiecesOf(story);
|
|
1226
1680
|
const half = pieces.find((piece) => piece.kind === 'half' && piece.element === t.element);
|
|
1227
1681
|
if (!half || half.kind !== 'half' || half.names.length === 0)
|
|
@@ -1286,22 +1740,36 @@ function resolveMove(change, action) {
|
|
|
1286
1740
|
dissolveBrokenMoves(story);
|
|
1287
1741
|
return true;
|
|
1288
1742
|
}
|
|
1289
|
-
/**
|
|
1290
|
-
|
|
1743
|
+
/**
|
|
1744
|
+
* The story a paragraph is in, for its moves: the outermost array of block content above it (a body, a
|
|
1745
|
+
* header, a note). A kept `mc:AlternateContent`'s selected branch is part of the story it stands in, as
|
|
1746
|
+
* Word reads it; another branch is a story of its own, its list (docx4j's twin stories, its CR-051 phase
|
|
1747
|
+
* 2; CR-013 phase 3), so a move is paired within the branch. `value` may be the paragraph or anything in
|
|
1748
|
+
* it, a run-level branch's revision among them.
|
|
1749
|
+
*/
|
|
1750
|
+
export function storyOf(value) {
|
|
1291
1751
|
let story;
|
|
1292
|
-
for (let o =
|
|
1752
|
+
for (let o = value; o !== undefined; o = o.PARENT) {
|
|
1753
|
+
const tn = o.TYPE_NAME;
|
|
1754
|
+
if (tn !== undefined && BRANCH_TYPES.has(tn)) {
|
|
1755
|
+
const ac = o.PARENT;
|
|
1756
|
+
const items = o.any;
|
|
1757
|
+
if (ac && items && selectedBranchOf(ac) !== items)
|
|
1758
|
+
return items;
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1293
1761
|
const content = o.content;
|
|
1294
|
-
if (Array.isArray(content) &&
|
|
1762
|
+
if (Array.isArray(content) && tn !== 'org_docx4j_wml.P')
|
|
1295
1763
|
story = content;
|
|
1296
1764
|
}
|
|
1297
1765
|
return story !== null && story !== void 0 ? story : [];
|
|
1298
1766
|
}
|
|
1299
1767
|
/** Every piece of move markup in a story, in document order, each half and mark with the names of the ranges open over it. */
|
|
1300
|
-
function movePiecesOf(story) {
|
|
1768
|
+
export function movePiecesOf(story) {
|
|
1301
1769
|
const out = [];
|
|
1302
1770
|
const open = { from: new Map(), to: new Map() };
|
|
1303
1771
|
const visit = (items, runLevel = false) => {
|
|
1304
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1772
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1305
1773
|
for (const el of items) {
|
|
1306
1774
|
const marker = RANGE_MARKERS[(_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : ''];
|
|
1307
1775
|
const value = el.value;
|
|
@@ -1331,11 +1799,18 @@ function movePiecesOf(story) {
|
|
|
1331
1799
|
const v = el.value;
|
|
1332
1800
|
if (typeof v !== 'object' || v === null)
|
|
1333
1801
|
continue;
|
|
1334
|
-
|
|
1802
|
+
// a kept mc:AlternateContent at block, row or cell level: its selected branch, part of this story
|
|
1803
|
+
// (`storyOf`); among runs it is one object, as before (CR-010 phase 3)
|
|
1804
|
+
if (typeNameOf(el) === ALTERNATE_CONTENT) {
|
|
1805
|
+
if (!runLevel)
|
|
1806
|
+
visit((_e = selectedBranchOf(v)) !== null && _e !== void 0 ? _e : []);
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
const children = (_f = childrenOf(v)) !== null && _f !== void 0 ? _f : runItemsOf(v);
|
|
1335
1810
|
if (children)
|
|
1336
1811
|
visit(children, typeNameOf(el) === 'org_docx4j_wml.P');
|
|
1337
1812
|
if (typeNameOf(el) === 'org_docx4j_wml.P') {
|
|
1338
|
-
const rPr = (
|
|
1813
|
+
const rPr = (_g = v.pPr) === null || _g === void 0 ? void 0 : _g.rPr;
|
|
1339
1814
|
for (const side of ['from', 'to']) {
|
|
1340
1815
|
if (rPr === null || rPr === void 0 ? void 0 : rPr[side === 'from' ? 'moveFrom' : 'moveTo']) {
|
|
1341
1816
|
out.push({ kind: 'mark', side, paragraph: el, container: items, names: [...open[side].values()] });
|
|
@@ -1359,7 +1834,7 @@ function resolveRangelessHalf(change, action) {
|
|
|
1359
1834
|
const paragraph = change.paragraph;
|
|
1360
1835
|
if (t.kind !== 'run' || (t.revision !== 'moveTo' && t.revision !== 'moveFrom') || !paragraph)
|
|
1361
1836
|
return false;
|
|
1362
|
-
const story = storyOf(
|
|
1837
|
+
const story = storyOf(t.element.value);
|
|
1363
1838
|
const pieces = movePiecesOf(story);
|
|
1364
1839
|
const half = pieces.find((piece) => piece.kind === 'half' && piece.element === t.element);
|
|
1365
1840
|
if (!half || half.kind !== 'half' || half.names.length > 0)
|
|
@@ -1495,18 +1970,76 @@ function markBefore(paragraph) {
|
|
|
1495
1970
|
}
|
|
1496
1971
|
/** Between two tracked changes: something unrevised - text, a paragraph mark, a table - that keeps them apart. */
|
|
1497
1972
|
export const BREAK = 'break';
|
|
1973
|
+
/** The public listing's (`Listing`). */
|
|
1974
|
+
export function listedListing() {
|
|
1975
|
+
return { mode: 'listed', extra: [] };
|
|
1976
|
+
}
|
|
1977
|
+
/** The changes in the other branches each listed change resolves too (`TrackedChange.resolveTwins`). */
|
|
1978
|
+
const twinsOf = new WeakMap();
|
|
1979
|
+
/**
|
|
1980
|
+
* A kept `mc:AlternateContent`'s changes: its selected branch's into `into`, as Word reads them, keeping
|
|
1981
|
+
* nothing apart (CR-013 phase 2); each other branch's paired with them as twins or, resolve-all's,
|
|
1982
|
+
* listed on their own after the story's (`Listing`). `visit` lists one branch's items, at the element's
|
|
1983
|
+
* level (blocks, rows, cells or runs).
|
|
1984
|
+
*/
|
|
1985
|
+
export function alternateTokens(ac, visit, into, listing) {
|
|
1986
|
+
const read = selectedBranchOf(ac);
|
|
1987
|
+
const primary = [];
|
|
1988
|
+
visit(read !== null && read !== void 0 ? read : [], primary);
|
|
1989
|
+
into.push(...primary);
|
|
1990
|
+
for (const branch of branchesOf(ac)) {
|
|
1991
|
+
if (branch === read)
|
|
1992
|
+
continue;
|
|
1993
|
+
const tokens = [];
|
|
1994
|
+
visit(branch, tokens);
|
|
1995
|
+
if (listing.mode === 'every')
|
|
1996
|
+
listing.extra.push(BREAK, ...tokens, BREAK);
|
|
1997
|
+
else
|
|
1998
|
+
pairTwins(primary, tokens);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Another branch's changes attached to the listed branch's as twins, piece by piece: the n-th single
|
|
2003
|
+
* change of a kind with the n-th of that kind, never by id, as docx4j pairs a text box's `mc:Fallback`
|
|
2004
|
+
* changes and a branch's (its `Lister.pairTwins`; Word gives twins different ids). docx4j pairs the
|
|
2005
|
+
* listed changes, groups included; pieces are paired here, since this listing groups the selected
|
|
2006
|
+
* branch's with what touches the element, so a piece resolved on its own resolves its twin too.
|
|
2007
|
+
*/
|
|
2008
|
+
export function pairTwins(primary, twins) {
|
|
2009
|
+
var _a, _b;
|
|
2010
|
+
const piecesOf = (tokens) => tokens.flatMap((token) => (token === BREAK ? [] : token.pieces));
|
|
2011
|
+
const byKind = new Map();
|
|
2012
|
+
for (const twin of piecesOf(twins)) {
|
|
2013
|
+
let list = byKind.get(twin.kind);
|
|
2014
|
+
if (!list)
|
|
2015
|
+
byKind.set(twin.kind, list = []);
|
|
2016
|
+
list.push(twin);
|
|
2017
|
+
}
|
|
2018
|
+
const seen = new Map();
|
|
2019
|
+
for (const piece of piecesOf(primary)) {
|
|
2020
|
+
const n = (_a = seen.get(piece.kind)) !== null && _a !== void 0 ? _a : 0;
|
|
2021
|
+
seen.set(piece.kind, n + 1);
|
|
2022
|
+
const twin = (_b = byKind.get(piece.kind)) === null || _b === void 0 ? void 0 : _b[n];
|
|
2023
|
+
if (!twin)
|
|
2024
|
+
continue;
|
|
2025
|
+
let list = twinsOf.get(piece);
|
|
2026
|
+
if (!list)
|
|
2027
|
+
twinsOf.set(piece, list = []);
|
|
2028
|
+
list.push(twin);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
1498
2031
|
/**
|
|
1499
2032
|
* A paragraph's single changes in document order, its mark last, with a `BREAK` wherever unrevised
|
|
1500
2033
|
* content stands between two of them: `groupTouching` makes the groups from these.
|
|
1501
2034
|
*/
|
|
1502
|
-
export function trackedChangeTokensOfParagraph(paragraph) {
|
|
2035
|
+
export function trackedChangeTokensOfParagraph(paragraph, listing = listedListing()) {
|
|
1503
2036
|
var _a, _b, _c, _d;
|
|
1504
2037
|
const out = [];
|
|
1505
2038
|
const p = paragraph.p;
|
|
1506
2039
|
const pPrChange = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.pPrChange;
|
|
1507
2040
|
if (pPrChange)
|
|
1508
2041
|
out.push(new TrackedChange({ kind: 'paragraphProperties', value: pPrChange }, paragraph));
|
|
1509
|
-
tokensOfRunLevel(paragraph, ((_b = p.content) !== null && _b !== void 0 ? _b : []), out);
|
|
2042
|
+
tokensOfRunLevel(paragraph, ((_b = p.content) !== null && _b !== void 0 ? _b : []), out, listing);
|
|
1510
2043
|
const rPr = (_c = p.pPr) === null || _c === void 0 ? void 0 : _c.rPr;
|
|
1511
2044
|
// a mark whose formatting changed is a piece, grouping with its runs' (check 27: "Before.\r")
|
|
1512
2045
|
if (rPr === null || rPr === void 0 ? void 0 : rPr.rPrChange)
|
|
@@ -1533,17 +2066,19 @@ function sectionText(items, end) {
|
|
|
1533
2066
|
while (from > 0 && !(typeNameOf(items[from - 1]) === 'org_docx4j_wml.P' && ((_a = items[from - 1].value.pPr) === null || _a === void 0 ? void 0 : _a.sectPr)))
|
|
1534
2067
|
from--;
|
|
1535
2068
|
return items.slice(Math.max(from, 0), end).filter((el) => typeNameOf(el) === 'org_docx4j_wml.P')
|
|
1536
|
-
.map((el) => textOfView(el.value, { view: 'accepted' })).join('\r');
|
|
2069
|
+
.map((el) => textOfView(el.value, { view: 'accepted', characters: 'textOf' })).join('\r');
|
|
1537
2070
|
}
|
|
1538
|
-
function tokensOfRunLevel(paragraph, items, out) {
|
|
2071
|
+
function tokensOfRunLevel(paragraph, items, out, listing) {
|
|
1539
2072
|
var _a, _b, _c;
|
|
1540
2073
|
for (let i = 0; i < items.length; i++) {
|
|
1541
2074
|
const el = items[i];
|
|
2075
|
+
if (el.value === undefined)
|
|
2076
|
+
continue; // a DOM element a branch's wildcard keeps: no change in it to list (CR-026 section 4.8)
|
|
1542
2077
|
// an inline control or custom XML element that is itself an insertion: a piece, then what is in it
|
|
1543
|
-
const inserted =
|
|
2078
|
+
const inserted = holderChangeAt(items, i, paragraph);
|
|
1544
2079
|
if (inserted) {
|
|
1545
2080
|
out.push(inserted);
|
|
1546
|
-
tokensOfRunLevel(paragraph, (_a = childrenOf(items[i + 1].value)) !== null && _a !== void 0 ? _a : [], out);
|
|
2081
|
+
tokensOfRunLevel(paragraph, (_a = childrenOf(items[i + 1].value)) !== null && _a !== void 0 ? _a : [], out, listing);
|
|
1547
2082
|
i += 2;
|
|
1548
2083
|
continue;
|
|
1549
2084
|
}
|
|
@@ -1572,7 +2107,7 @@ function tokensOfRunLevel(paragraph, items, out) {
|
|
|
1572
2107
|
if (nested) {
|
|
1573
2108
|
// a revision inside a revision (a deletion of inserted text) was not measured: listed apart
|
|
1574
2109
|
const inner = [];
|
|
1575
|
-
tokensOfRunLevel(paragraph, nested, inner);
|
|
2110
|
+
tokensOfRunLevel(paragraph, nested, inner, listing);
|
|
1576
2111
|
if (inner.some((token) => token !== BREAK && token.target.kind === 'run'))
|
|
1577
2112
|
out.push(BREAK, ...inner, BREAK);
|
|
1578
2113
|
else
|
|
@@ -1591,6 +2126,12 @@ function tokensOfRunLevel(paragraph, items, out) {
|
|
|
1591
2126
|
out.push(BREAK);
|
|
1592
2127
|
continue;
|
|
1593
2128
|
}
|
|
2129
|
+
// a kept mc:AlternateContent among runs: its selected branch, as Word reads it, keeping nothing apart
|
|
2130
|
+
// (CR-013 phase 2; docx4j CR-051 section 10.5 item 3), the other branches' changes its twins (phase 3)
|
|
2131
|
+
if (typeNameOf(el) === ALTERNATE_CONTENT) {
|
|
2132
|
+
alternateTokens(el.value, (branch, into) => tokensOfRunLevel(paragraph, branch, into, listing), out, listing);
|
|
2133
|
+
continue;
|
|
2134
|
+
}
|
|
1594
2135
|
// a run holder that is not itself an insertion - a hyperlink, a field, a control or custom XML
|
|
1595
2136
|
// element that was there - keeps the insertions inside it apart from those outside: Office JS
|
|
1596
2137
|
// listed "before ", "link" and " after" as three changes over a w:ins in a w:hyperlink, and the same
|
|
@@ -1599,7 +2140,7 @@ function tokensOfRunLevel(paragraph, items, out) {
|
|
|
1599
2140
|
const nested = runItemsOf(el.value);
|
|
1600
2141
|
if (nested) {
|
|
1601
2142
|
out.push(BREAK);
|
|
1602
|
-
tokensOfRunLevel(paragraph, nested, out);
|
|
2143
|
+
tokensOfRunLevel(paragraph, nested, out, listing);
|
|
1603
2144
|
out.push(BREAK);
|
|
1604
2145
|
}
|
|
1605
2146
|
}
|
|
@@ -1617,7 +2158,9 @@ function groupable(change) {
|
|
|
1617
2158
|
return t.revision;
|
|
1618
2159
|
if (t.kind === 'runProperties' || t.kind === 'markProperties')
|
|
1619
2160
|
return 'format';
|
|
1620
|
-
if (t.kind === 'holder'
|
|
2161
|
+
if (t.kind === 'holder')
|
|
2162
|
+
return t.revision;
|
|
2163
|
+
if (t.kind === 'field')
|
|
1621
2164
|
return 'ins';
|
|
1622
2165
|
if (t.kind === 'mathRun' || t.kind === 'mathStructure')
|
|
1623
2166
|
return t.revision;
|
|
@@ -1663,7 +2206,7 @@ export function groupTouching(tokens) {
|
|
|
1663
2206
|
const first = item[0];
|
|
1664
2207
|
if (item.length === 1)
|
|
1665
2208
|
return first;
|
|
1666
|
-
return new TrackedChange({ kind: 'group', revision: groupable(first), pieces: item, value: first.target.value }, first.paragraph);
|
|
2209
|
+
return new TrackedChange({ kind: 'group', revision: groupable(first), pieces: item, value: first.target.value }, first.paragraph, first.body);
|
|
1667
2210
|
});
|
|
1668
2211
|
}
|
|
1669
2212
|
/**
|
|
@@ -1681,9 +2224,9 @@ export function trackedChangesOfRow(tr, owner, inner = [], table, body) {
|
|
|
1681
2224
|
}
|
|
1682
2225
|
// --- CR-002 section 35: tables, cells, marks and sections --------------------------------------------
|
|
1683
2226
|
/** The change a table's property records make, one per table, or undefined when they make none. */
|
|
1684
|
-
export function tablePropertiesChangeOf(tbl) {
|
|
2227
|
+
export function tablePropertiesChangeOf(tbl, body) {
|
|
1685
2228
|
const records = tableRecordsOf(tbl.value);
|
|
1686
|
-
return records ? new TrackedChange({ kind: 'tableProperties', tbl, value: records.value }, undefined) : undefined;
|
|
2229
|
+
return records ? new TrackedChange({ kind: 'tableProperties', tbl, value: records.value }, undefined, body) : undefined;
|
|
1687
2230
|
}
|
|
1688
2231
|
/** A cell's own revision, `w:cellIns` or `w:cellDel`, or undefined. */
|
|
1689
2232
|
export function cellChangeOf(tc, owner, tr, body) {
|
|
@@ -1695,30 +2238,37 @@ export function cellChangeOf(tc, owner, tr, body) {
|
|
|
1695
2238
|
return undefined;
|
|
1696
2239
|
}
|
|
1697
2240
|
/** The change to the body's last section (`w:body/w:sectPr/w:sectPrChange`), its text the section's paragraphs'. */
|
|
1698
|
-
export function bodySectionChangeOf(container) {
|
|
2241
|
+
export function bodySectionChangeOf(container, body) {
|
|
1699
2242
|
var _a;
|
|
1700
2243
|
const sectPr = container.sectPr;
|
|
1701
2244
|
if (!(sectPr === null || sectPr === void 0 ? void 0 : sectPr.sectPrChange))
|
|
1702
2245
|
return undefined;
|
|
1703
2246
|
const items = (_a = container.content) !== null && _a !== void 0 ? _a : [];
|
|
1704
|
-
return new TrackedChange({ kind: 'sectionProperties', sectPr, value: sectPr.sectPrChange, text: sectionText(items, items.length) }, undefined);
|
|
2247
|
+
return new TrackedChange({ kind: 'sectionProperties', sectPr, value: sectPr.sectPrChange, text: sectionText(items, items.length) }, undefined, body);
|
|
1705
2248
|
}
|
|
1706
2249
|
/**
|
|
1707
2250
|
* A cell taken out of its row, the cell before it (else after it) widened over its grid columns, as
|
|
1708
2251
|
* Word did rejecting an inserted cell and accepting a deleted one (check 27: `w:gridSpan` 2 and the
|
|
1709
|
-
* two widths added).
|
|
2252
|
+
* two widths added). The neighbour is the row's as a reader sees it, through cell-level controls and a
|
|
2253
|
+
* kept `mc:AlternateContent`'s selected branch; a cell in another branch (a twin) has its own branch's
|
|
2254
|
+
* cells for neighbours, and one alone there widens none, the row's neighbour having been widened once
|
|
2255
|
+
* when the selected branch's cell went, every branch spanning the same columns (CR-013 phase 3, docx4j's
|
|
2256
|
+
* `Tables.removeCell`, its 60bd4b9ad and 901a922a3).
|
|
1710
2257
|
*/
|
|
1711
|
-
function removeCell(tc, owner) {
|
|
2258
|
+
function removeCell(tc, owner, tr) {
|
|
1712
2259
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1713
2260
|
var _h;
|
|
1714
|
-
const
|
|
2261
|
+
const list = currentOwner(owner, tc);
|
|
2262
|
+
const i = list.indexOf(tc);
|
|
1715
2263
|
if (i < 0)
|
|
1716
2264
|
return;
|
|
1717
|
-
|
|
2265
|
+
let cells = cellsOf(tr, 'read').map((cell) => cell.element);
|
|
2266
|
+
if (!cells.includes(tc))
|
|
2267
|
+
cells = list.filter((el) => typeNameOf(el) === 'org_docx4j_wml.Tc');
|
|
1718
2268
|
const at = cells.indexOf(tc);
|
|
1719
2269
|
const neighbour = (_a = cells[at - 1]) !== null && _a !== void 0 ? _a : cells[at + 1];
|
|
1720
2270
|
discarding(tc);
|
|
1721
|
-
|
|
2271
|
+
list.splice(i, 1);
|
|
1722
2272
|
if (!neighbour)
|
|
1723
2273
|
return;
|
|
1724
2274
|
const removed = tc.value.tcPr;
|
|
@@ -1774,7 +2324,8 @@ function restoreSection(sectPr, change) {
|
|
|
1774
2324
|
* writes and drops when one is put in, and whose record is the old number's text, so that a reject
|
|
1775
2325
|
* too can only drop it; and a table's records that change nothing. A merge rejected gets the merge it
|
|
1776
2326
|
* recorded back (docx4j's reading; unmeasured). A `w:customXmlInsRangeStart` or `End` left without
|
|
1777
|
-
* the form `
|
|
2327
|
+
* the form `holderChangeAt` lists marks nothing that can be found, and goes either way. Every branch
|
|
2328
|
+
* of a kept `mc:AlternateContent` at block, row and cell level is swept (CR-013 phase 3).
|
|
1778
2329
|
*/
|
|
1779
2330
|
export function resolveUnlisted(items, what) {
|
|
1780
2331
|
const visit = (list) => {
|
|
@@ -1783,7 +2334,7 @@ export function resolveUnlisted(items, what) {
|
|
|
1783
2334
|
return;
|
|
1784
2335
|
for (let i = list.length - 1; i >= 0; i--) {
|
|
1785
2336
|
const name = (_a = list[i].name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
1786
|
-
if (name
|
|
2337
|
+
if (HOLDER_MARKERS.has(name !== null && name !== void 0 ? name : ''))
|
|
1787
2338
|
list.splice(i, 1);
|
|
1788
2339
|
}
|
|
1789
2340
|
for (const el of list) {
|
|
@@ -1799,8 +2350,8 @@ export function resolveUnlisted(items, what) {
|
|
|
1799
2350
|
const tbl = el.value;
|
|
1800
2351
|
if (hasTableRecords(tbl) && !tableRecordsOf(tbl))
|
|
1801
2352
|
acceptTable(tbl);
|
|
1802
|
-
for (const row of rowsOf(tbl)) {
|
|
1803
|
-
for (const cell of cellsOf(row.element.value)) {
|
|
2353
|
+
for (const row of rowsOf(tbl, 'every')) {
|
|
2354
|
+
for (const cell of cellsOf(row.element.value, 'every')) {
|
|
1804
2355
|
const tcPr = cell.element.value.tcPr;
|
|
1805
2356
|
if (tcPr === null || tcPr === void 0 ? void 0 : tcPr.cellMerge)
|
|
1806
2357
|
resolveCellMerge(tcPr, what);
|
|
@@ -1810,25 +2361,38 @@ export function resolveUnlisted(items, what) {
|
|
|
1810
2361
|
continue;
|
|
1811
2362
|
}
|
|
1812
2363
|
const v = el.value;
|
|
1813
|
-
if (typeof v
|
|
2364
|
+
if (typeof v !== 'object' || v === null)
|
|
2365
|
+
continue;
|
|
2366
|
+
// every branch of a kept mc:AlternateContent (CR-013 phase 3)
|
|
2367
|
+
if (tn === ALTERNATE_CONTENT)
|
|
2368
|
+
for (const branch of branchesOf(v))
|
|
2369
|
+
visit(branch);
|
|
2370
|
+
else
|
|
1814
2371
|
visit(childrenOf(v));
|
|
1815
2372
|
}
|
|
1816
2373
|
};
|
|
1817
2374
|
visit(items);
|
|
1818
2375
|
}
|
|
1819
|
-
/**
|
|
2376
|
+
/** A holder's insertion and deletion markers: one left without the form `holderChangeAt` lists goes either way. */
|
|
2377
|
+
const HOLDER_MARKERS = new Set(['customXmlInsRangeStart', 'customXmlInsRangeEnd', 'customXmlDelRangeStart', 'customXmlDelRangeEnd']);
|
|
2378
|
+
/** Stray `w:customXmlInsRangeStart` / `End` markers (or `Del`) taken out of run-level content, through its holders and revisions. */
|
|
1820
2379
|
function sweepMarkers(items) {
|
|
1821
2380
|
var _a;
|
|
1822
2381
|
if (!items)
|
|
1823
2382
|
return;
|
|
1824
2383
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
1825
2384
|
const name = (_a = items[i].name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
1826
|
-
if (name
|
|
2385
|
+
if (HOLDER_MARKERS.has(name !== null && name !== void 0 ? name : '')) {
|
|
1827
2386
|
items.splice(i, 1);
|
|
1828
2387
|
continue;
|
|
1829
2388
|
}
|
|
1830
2389
|
const v = items[i].value;
|
|
1831
|
-
if (typeof v
|
|
2390
|
+
if (typeof v !== 'object' || v === null)
|
|
2391
|
+
continue;
|
|
2392
|
+
if (typeNameOf(items[i]) === ALTERNATE_CONTENT)
|
|
2393
|
+
for (const branch of branchesOf(v))
|
|
2394
|
+
sweepMarkers(branch);
|
|
2395
|
+
else
|
|
1832
2396
|
sweepMarkers(runItemsOf(v));
|
|
1833
2397
|
}
|
|
1834
2398
|
}
|