@docx4j/core-ts 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/html/convert.mjs +38 -14
- package/dist/model/content/Body.d.mts +13 -1
- package/dist/model/content/Body.mjs +90 -19
- package/dist/model/content/Comment.mjs +7 -0
- package/dist/model/content/InlinePicture.d.mts +71 -7
- package/dist/model/content/InlinePicture.mjs +201 -28
- package/dist/model/content/List.mjs +10 -1
- package/dist/model/content/Paragraph.d.mts +28 -5
- package/dist/model/content/Paragraph.mjs +77 -37
- package/dist/model/content/Range.d.mts +18 -2
- package/dist/model/content/Range.mjs +194 -64
- package/dist/model/content/TrackedChange.d.mts +129 -3
- package/dist/model/content/TrackedChange.mjs +577 -12
- package/dist/model/content/binder.d.mts +2 -1
- package/dist/model/content/binder.mjs +48 -1
- package/dist/model/content/index.d.mts +2 -2
- package/dist/model/content/index.mjs +1 -1
- package/dist/model/content/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -0
- package/dist/model/content/tree.d.mts +52 -2
- package/dist/model/content/tree.mjs +48 -3
- package/dist/model/customxml/pictures.d.mts +5 -2
- package/dist/model/customxml/pictures.mjs +13 -7
- package/dist/model/fonts/families.generated.mjs +2 -2
- package/dist/model/fonts/registry.mjs +7 -1
- package/dist/model/fonts/substitutions.generated.mjs +2 -2
- package/dist/model/fonts/themes.generated.mjs +1 -1
- package/dist/model/listnumbering/definitions.mjs +18 -0
- package/dist/model/properties/PropertyResolver.d.mts +27 -6
- package/dist/model/properties/PropertyResolver.mjs +83 -16
- package/dist/model/properties/catalogue.d.mts +15 -1
- package/dist/model/properties/catalogue.mjs +80 -27
- package/dist/model/properties/table.d.mts +16 -2
- package/dist/model/properties/table.mjs +13 -3
- package/dist/opc/Load.d.mts +9 -2
- package/dist/opc/index.d.mts +2 -2
- package/dist/opc/index.mjs +2 -2
- package/dist/opc/mce/preprocessor.d.mts +62 -6
- package/dist/opc/mce/preprocessor.mjs +142 -35
- package/dist/opc/mce/understood.d.mts +10 -0
- package/dist/opc/mce/understood.mjs +25 -3
- package/dist/packages/OpcPackage.d.mts +7 -0
- package/dist/packages/OpcPackage.mjs +15 -3
- package/dist/packages/WordprocessingMLPackage.d.mts +35 -2
- package/dist/packages/WordprocessingMLPackage.mjs +98 -2
- package/dist/parts/XmlPart.d.mts +26 -5
- package/dist/parts/XmlPart.mjs +67 -18
- package/package.json +2 -2
|
@@ -7,6 +7,7 @@ import { rPrFromElements } from '@docx4j/generated-objects-ts/builders/wml';
|
|
|
7
7
|
import { revisionDateOf, toRestoredText, toDeletedText, restoreRPr, restorePPr, pruneParagraphProperties, HOLDER_TYPES, holderItemsOf } from './tracking.mjs';
|
|
8
8
|
import { tableRecordsOf, hasTableRecords, tableChangeText, rowText as tableRowText, acceptTable, rejectTable } from './tableRevisions.mjs';
|
|
9
9
|
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
10
|
+
import { relationshipIdsIn, dropRelationshipsUnused, queueRelationshipRemovals } from './relationships.mjs';
|
|
10
11
|
/**
|
|
11
12
|
* A subset of Office JS `Word.TrackedChange`: `type`, `author`, `date`, `text`, `accept()`,
|
|
12
13
|
* `reject()` and `getRange()`, plus `element`, `id` and `target` as extensions. A view, like
|
|
@@ -15,9 +16,12 @@ import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
|
15
16
|
export class TrackedChange {
|
|
16
17
|
constructor(target,
|
|
17
18
|
/** The paragraph the change is in; undefined for a row revision. */
|
|
18
|
-
paragraph
|
|
19
|
+
paragraph,
|
|
20
|
+
/** The body a change without a paragraph (a row, a cell) is in, for the notes it may take away. */
|
|
21
|
+
body) {
|
|
19
22
|
this.target = target;
|
|
20
23
|
this.paragraph = paragraph;
|
|
24
|
+
this.body = body;
|
|
21
25
|
}
|
|
22
26
|
/** Office JS `Word.ChangeTrackingState`. */
|
|
23
27
|
get type() {
|
|
@@ -29,6 +33,9 @@ export class TrackedChange {
|
|
|
29
33
|
case 'group': return t.revision === 'ins' ? 'Added' : t.revision === 'del' ? 'Deleted' : 'Formatted';
|
|
30
34
|
case 'cell': return t.cell === 'ins' ? 'Added' : 'Deleted';
|
|
31
35
|
case 'holder': return 'Added';
|
|
36
|
+
case 'field': return 'Added';
|
|
37
|
+
case 'mathRun':
|
|
38
|
+
case 'mathStructure': return t.revision === 'ins' ? 'Added' : 'Deleted';
|
|
32
39
|
default: return 'Formatted';
|
|
33
40
|
}
|
|
34
41
|
}
|
|
@@ -49,6 +56,7 @@ export class TrackedChange {
|
|
|
49
56
|
}
|
|
50
57
|
/** The markup this change is over (an extension): the `w:ins` element, the `w:rPrChange`, ... */
|
|
51
58
|
get element() {
|
|
59
|
+
var _a;
|
|
52
60
|
const t = this.target;
|
|
53
61
|
switch (t.kind) {
|
|
54
62
|
case 'run': return t.element;
|
|
@@ -58,12 +66,15 @@ export class TrackedChange {
|
|
|
58
66
|
case 'cell': return t.tc;
|
|
59
67
|
case 'sectionProperties': return t.sectPr;
|
|
60
68
|
case 'holder': return t.holder;
|
|
69
|
+
case 'field': return t.span[0];
|
|
70
|
+
case 'mathRun': return t.item;
|
|
71
|
+
case 'mathStructure': return (_a = t.structure) !== null && _a !== void 0 ? _a : t.ctrlPr;
|
|
61
72
|
default: return t.value;
|
|
62
73
|
}
|
|
63
74
|
}
|
|
64
75
|
/** The text the change covers; `\r` for a paragraph mark, as Office JS gives it (check 22). */
|
|
65
76
|
get text() {
|
|
66
|
-
var _a, _b;
|
|
77
|
+
var _a, _b, _c;
|
|
67
78
|
const t = this.target;
|
|
68
79
|
switch (t.kind) {
|
|
69
80
|
case 'mark': return '\r';
|
|
@@ -74,10 +85,14 @@ export class TrackedChange {
|
|
|
74
85
|
case 'sectionProperties': return t.text;
|
|
75
86
|
case 'run': return segmentsOf(t.element.value, { view: t.revision === 'ins' || t.revision === 'moveTo' ? 'accepted' : 'original' }).map((s) => s.text).join('');
|
|
76
87
|
case 'runProperties': return textOf(t.run);
|
|
88
|
+
// 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('');
|
|
77
90
|
case 'paragraphProperties': return (_b = (_a = this.paragraph) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : '';
|
|
78
91
|
// a deleted row's content is `w:delText` inside `w:del`, which the accepted view skips:
|
|
79
92
|
// its text is what the row said before, as for a run deletion
|
|
80
93
|
case 'row': return rowText(t.tr.value, t.row === 'del' ? 'original' : 'accepted');
|
|
94
|
+
// an equation's text: its m:t, as docx4j gives it; a structure's own marker covers no text
|
|
95
|
+
case 'mathRun': return ((_c = t.value.rest) !== null && _c !== void 0 ? _c : []).map((item) => { var _a; return (_a = mathTextOf(item)) !== null && _a !== void 0 ? _a : ''; }).join('');
|
|
81
96
|
default: return '';
|
|
82
97
|
}
|
|
83
98
|
}
|
|
@@ -93,17 +108,26 @@ export class TrackedChange {
|
|
|
93
108
|
}
|
|
94
109
|
if (t.kind === 'group') {
|
|
95
110
|
// a range is one paragraph's: the group's text in the paragraph it starts in
|
|
96
|
-
const here = t.pieces.filter((piece) => { var _a; return (piece.target.kind === 'run' || piece.target.kind === 'runProperties') && ((_a = piece.paragraph) === null || _a === void 0 ? void 0 : _a.element) === p.element; }).map((piece) => piece.getRange());
|
|
111
|
+
const here = t.pieces.filter((piece) => { var _a; return (piece.target.kind === 'run' || piece.target.kind === 'runProperties' || piece.target.kind === 'field') && ((_a = piece.paragraph) === null || _a === void 0 ? void 0 : _a.element) === p.element; }).map((piece) => piece.getRange());
|
|
97
112
|
if (here.length === 0)
|
|
98
113
|
return t.pieces[0].getRange();
|
|
99
114
|
return new Range(p, Math.min(...here.map((r) => r.start)), Math.max(...here.map((r) => r.end)));
|
|
100
115
|
}
|
|
116
|
+
if (t.kind === 'field') {
|
|
117
|
+
const start = acceptedOffsetOf(p.p, t.span[0]);
|
|
118
|
+
return new Range(p, start, start + this.text.length);
|
|
119
|
+
}
|
|
101
120
|
if (t.kind === 'runProperties') {
|
|
102
121
|
const segs = p.segments().filter((s) => s.run === t.run);
|
|
103
122
|
if (segs.length === 0)
|
|
104
123
|
return p.getRange('Start');
|
|
105
124
|
return new Range(p, segs[0].start, segs[segs.length - 1].end);
|
|
106
125
|
}
|
|
126
|
+
// an equation's text is not in the paragraph's: collapsed where the equation stands
|
|
127
|
+
if ((t.kind === 'mathRun' || t.kind === 'mathStructure') && t.equation) {
|
|
128
|
+
const at = acceptedOffsetOf(p.p, t.equation);
|
|
129
|
+
return new Range(p, at, at);
|
|
130
|
+
}
|
|
107
131
|
return p.getRange('Whole');
|
|
108
132
|
}
|
|
109
133
|
/**
|
|
@@ -111,6 +135,9 @@ export class TrackedChange {
|
|
|
111
135
|
* Either half of a move keeps the whole move, as Word's Review tab does (CR-002 section 29).
|
|
112
136
|
*/
|
|
113
137
|
accept() {
|
|
138
|
+
resolving(this.package_, this.body_, () => this.acceptHere());
|
|
139
|
+
}
|
|
140
|
+
acceptHere() {
|
|
114
141
|
var _a, _b, _c, _d, _e, _f;
|
|
115
142
|
const t = this.target;
|
|
116
143
|
switch (t.kind) {
|
|
@@ -174,6 +201,18 @@ export class TrackedChange {
|
|
|
174
201
|
// the element stays, the markers go (check 30: accept all leaves the control, no marker)
|
|
175
202
|
removeHolderMarkers(t);
|
|
176
203
|
return;
|
|
204
|
+
case 'field':
|
|
205
|
+
for (const el of t.span) {
|
|
206
|
+
if (revisionKindOf(el) === 'ins')
|
|
207
|
+
unwrap({ kind: 'run', revision: 'ins', element: el, owner: currentOwner(t.owner, el), value: el.value });
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
case 'mathRun':
|
|
211
|
+
resolveMathRun(t, t.revision === 'ins');
|
|
212
|
+
return;
|
|
213
|
+
case 'mathStructure':
|
|
214
|
+
resolveMathStructure(t, t.revision === 'ins');
|
|
215
|
+
return;
|
|
177
216
|
}
|
|
178
217
|
}
|
|
179
218
|
/**
|
|
@@ -181,6 +220,9 @@ export class TrackedChange {
|
|
|
181
220
|
* Either half of a move rejects the whole move, as Word's Review tab does (CR-002 section 29).
|
|
182
221
|
*/
|
|
183
222
|
reject() {
|
|
223
|
+
resolving(this.package_, this.body_, () => this.rejectHere());
|
|
224
|
+
}
|
|
225
|
+
rejectHere() {
|
|
184
226
|
var _a, _b;
|
|
185
227
|
const t = this.target;
|
|
186
228
|
switch (t.kind) {
|
|
@@ -243,8 +285,27 @@ export class TrackedChange {
|
|
|
243
285
|
// control, and an existing control, which has no markers, stays)
|
|
244
286
|
unwrapHolder(t);
|
|
245
287
|
return;
|
|
288
|
+
case 'field':
|
|
289
|
+
removeField(t, this.paragraph);
|
|
290
|
+
return;
|
|
291
|
+
case 'mathRun':
|
|
292
|
+
resolveMathRun(t, t.revision === 'del');
|
|
293
|
+
return;
|
|
294
|
+
case 'mathStructure':
|
|
295
|
+
resolveMathStructure(t, t.revision === 'del');
|
|
296
|
+
return;
|
|
246
297
|
}
|
|
247
298
|
}
|
|
299
|
+
/** The body the change is in. */
|
|
300
|
+
get body_() {
|
|
301
|
+
var _a, _b;
|
|
302
|
+
return (_b = (_a = this.paragraph) === null || _a === void 0 ? void 0 : _a.parentBody) !== null && _b !== void 0 ? _b : this.body;
|
|
303
|
+
}
|
|
304
|
+
/** The package the change is in, which keeps the notes whose references it took away. */
|
|
305
|
+
get package_() {
|
|
306
|
+
var _a;
|
|
307
|
+
return (_a = this.body_) === null || _a === void 0 ? void 0 : _a.package_;
|
|
308
|
+
}
|
|
248
309
|
requireParagraph() {
|
|
249
310
|
if (!this.paragraph)
|
|
250
311
|
throw new Docx4JException('This tracked change has no paragraph');
|
|
@@ -375,8 +436,287 @@ function currentOwner(owner, element) {
|
|
|
375
436
|
function remove(owner, element) {
|
|
376
437
|
const list = currentOwner(owner, element);
|
|
377
438
|
const i = list.indexOf(element);
|
|
378
|
-
if (i
|
|
379
|
-
|
|
439
|
+
if (i < 0)
|
|
440
|
+
return;
|
|
441
|
+
discarding(element);
|
|
442
|
+
list.splice(i, 1);
|
|
443
|
+
}
|
|
444
|
+
let resolution;
|
|
445
|
+
/**
|
|
446
|
+
* Runs `resolve`, then takes away what went with the content it removed:
|
|
447
|
+
* - **Notes**: Word took a footnote from the notes part when its reference went with the content
|
|
448
|
+
* around it, accepting a deletion and rejecting an insertion alike (the editor's `review-for-word`
|
|
449
|
+
* saves, Word 15 and Word 2010); the entries go when the package is saved
|
|
450
|
+
* (`WordprocessingMLPackage.pendingNoteRemovals`).
|
|
451
|
+
* - **Bookmarks**: Word removed a bookmark whole when its end went with a deletion it accepted, and
|
|
452
|
+
* when the insertion it was put around went with a reject (docx4j CR-043's probe, C11 and C11b, Word
|
|
453
|
+
* 16.0.20326). docx4j's rule from the two (its CR-043 section 3.2) is followed here: a bookmark with a
|
|
454
|
+
* marker in removed content goes whole, its other marker too, even with content left beside it; so
|
|
455
|
+
* does one with content between its markers that is left with none, a deletion it was around accepted
|
|
456
|
+
* among them (U15); and one that was empty before, as `_GoBack` is, stays. Those three are not measured.
|
|
457
|
+
* - **Relationships and parts**: Word's saves kept no part the resolved content alone referenced. A
|
|
458
|
+
* picture deleted and accepted, or inserted and rejected, took its image part (docx4j CR-043's probe,
|
|
459
|
+
* C05 and C04), and a section break deleted and accepted took the headers and footers of the section
|
|
460
|
+
* it ended (C06: six of each became three). docx4j's rule (its CR-043 section 3.2, D7) is followed: a
|
|
461
|
+
* relationship of the change's part that the removed content referenced, through any `r:` attribute,
|
|
462
|
+
* goes when nothing left in the part references it; its part goes when no relationship in the package
|
|
463
|
+
* targets it any more, and what that part alone related to goes with it. The kinds beyond pictures,
|
|
464
|
+
* headers and footers (charts, OLE objects, `v:imagedata`) are docx4j's reading, not measured.
|
|
465
|
+
* A change resolved inside another's (a row's cells, a group's pieces, `acceptAll`'s changes) shares the
|
|
466
|
+
* outer resolution, so the bookmarks are read twice for the lot, and not at all when nothing is removed.
|
|
467
|
+
*/
|
|
468
|
+
export function resolving(pkg, body, resolve) {
|
|
469
|
+
var _a, _b, _c;
|
|
470
|
+
const notes = pkg === null || pkg === void 0 ? void 0 : pkg.pendingNoteRemovals;
|
|
471
|
+
const root = storyRootOf(body);
|
|
472
|
+
const outer = resolution;
|
|
473
|
+
if (outer) {
|
|
474
|
+
(_a = outer.root) !== null && _a !== void 0 ? _a : (outer.root = root);
|
|
475
|
+
(_b = outer.part) !== null && _b !== void 0 ? _b : (outer.part = body === null || body === void 0 ? void 0 : body.part);
|
|
476
|
+
const previous = outer.notes;
|
|
477
|
+
outer.notes = notes !== null && notes !== void 0 ? notes : previous;
|
|
478
|
+
try {
|
|
479
|
+
resolve();
|
|
480
|
+
}
|
|
481
|
+
finally {
|
|
482
|
+
outer.notes = previous;
|
|
483
|
+
}
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
resolution = { notes, root, part: body === null || body === void 0 ? void 0 : body.part };
|
|
487
|
+
try {
|
|
488
|
+
resolve();
|
|
489
|
+
const done = resolution;
|
|
490
|
+
if (done.root && done.before)
|
|
491
|
+
dropBookmarksBroken(done.root, done.before);
|
|
492
|
+
if (done.part && ((_c = done.relationships) === null || _c === void 0 ? void 0 : _c.size))
|
|
493
|
+
dropRelationshipsUnused(done.part, done.relationships);
|
|
494
|
+
}
|
|
495
|
+
finally {
|
|
496
|
+
resolution = undefined;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/** The tree a body's bookmarks are read in: its part's, so a marker in another cell or paragraph is found. */
|
|
500
|
+
function storyRootOf(body) {
|
|
501
|
+
var _a;
|
|
502
|
+
if (!body)
|
|
503
|
+
return undefined;
|
|
504
|
+
return ((_a = body.part) === null || _a === void 0 ? void 0 : _a.isUnmarshalled) ? body.part.contents : body.container;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Called before content is removed (or a paragraph mark joins two): records its notes, the relationships
|
|
508
|
+
* it references, and the bookmarks as they stand. `properties` is a paragraph's `w:pPr` that a join drops,
|
|
509
|
+
* whose `w:sectPr` references its section's headers and footers.
|
|
510
|
+
*/
|
|
511
|
+
function discarding(element, properties) {
|
|
512
|
+
var _a, _b;
|
|
513
|
+
if (!resolution)
|
|
514
|
+
return;
|
|
515
|
+
if (resolution.root)
|
|
516
|
+
(_a = resolution.before) !== null && _a !== void 0 ? _a : (resolution.before = bookmarksIn(resolution.root));
|
|
517
|
+
if (element && resolution.notes)
|
|
518
|
+
noteReferencesIn(element, resolution.notes);
|
|
519
|
+
if (resolution.part && (element || properties)) {
|
|
520
|
+
const into = ((_b = resolution.relationships) !== null && _b !== void 0 ? _b : (resolution.relationships = new Set()));
|
|
521
|
+
if (element)
|
|
522
|
+
relationshipIdsIn(element, into);
|
|
523
|
+
if (properties)
|
|
524
|
+
relationshipIdsIn(properties, into);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
const MATH_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/math';
|
|
528
|
+
/** Run content that is something in the text: a character, a drawing, a field character, a note's reference. */
|
|
529
|
+
const FILLS = new Set(['tab', 'br', 'cr', 'noBreakHyphen', 'softHyphen', 'sym', 'ptab', 'drawing', 'pict', 'object',
|
|
530
|
+
'fldChar', 'footnoteReference', 'endnoteReference', 'commentReference', 'footnoteRef', 'endnoteRef', 'annotationRef',
|
|
531
|
+
'separator', 'continuationSeparator', 'pgNum', 'dayShort', 'monthShort', 'yearShort', 'dayLong', 'monthLong', 'yearLong',
|
|
532
|
+
'contentPart', 'ruby']);
|
|
533
|
+
/** Of those, the ones that can hold a story of their own (a text box), and so bookmarks: walked into. */
|
|
534
|
+
const HOLDS_CONTENT = new Set(['drawing', 'pict', 'object', 'ruby', 'contentPart']);
|
|
535
|
+
const TEXTS = new Set(['t', 'delText', 'instrText', 'delInstrText']);
|
|
536
|
+
/** Properties, which hold no bookmark and no content: not walked. */
|
|
537
|
+
const PROPERTIES = new Set(['PARENT', 'TYPE_NAME', 'name', 'otherAttributes', 'pPr', 'rPr', 'tblPr', 'tblPrEx', 'tblGrid', 'trPr', 'tcPr', 'sectPr', 'sdtPr', 'sdtEndPr']);
|
|
538
|
+
/**
|
|
539
|
+
* Every bookmark in a tree, by id, in document order. Deleted and inserted content both count as
|
|
540
|
+
* content, and so does a paragraph's mark, so a bookmark over a paragraph boundary is not empty.
|
|
541
|
+
*/
|
|
542
|
+
function bookmarksIn(root) {
|
|
543
|
+
const marks = new Map();
|
|
544
|
+
let open = [];
|
|
545
|
+
const fill = () => { for (const state of open)
|
|
546
|
+
state.filled = true; open = []; };
|
|
547
|
+
const visit = (o) => {
|
|
548
|
+
var _a, _b, _c, _d;
|
|
549
|
+
if (typeof o !== 'object' || o === null)
|
|
550
|
+
return;
|
|
551
|
+
if (Array.isArray(o)) {
|
|
552
|
+
for (const item of o)
|
|
553
|
+
visit(item);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const el = o;
|
|
557
|
+
const local = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
558
|
+
if (local === 'bookmarkStart' || local === 'bookmarkEnd') {
|
|
559
|
+
const id = String((_b = el.value) === null || _b === void 0 ? void 0 : _b.id);
|
|
560
|
+
let state = marks.get(id);
|
|
561
|
+
if (!state)
|
|
562
|
+
marks.set(id, state = { start: false, end: false, filled: false });
|
|
563
|
+
if (local === 'bookmarkStart') {
|
|
564
|
+
state.start = true;
|
|
565
|
+
open.push(state);
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
state.end = true;
|
|
569
|
+
open = open.filter((other) => other !== state);
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (local !== undefined && (el.name.namespaceURI === W_NS || el.name.namespaceURI === MATH_NS)) {
|
|
574
|
+
if (TEXTS.has(local)) {
|
|
575
|
+
if (String((_d = (_c = el.value) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '') !== '')
|
|
576
|
+
fill();
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (FILLS.has(local)) {
|
|
580
|
+
fill();
|
|
581
|
+
if (!HOLDS_CONTENT.has(local))
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
for (const key in o)
|
|
586
|
+
if (!PROPERTIES.has(key))
|
|
587
|
+
visit(o[key]);
|
|
588
|
+
if (o.TYPE_NAME === 'org_docx4j_wml.P')
|
|
589
|
+
fill();
|
|
590
|
+
};
|
|
591
|
+
visit(root);
|
|
592
|
+
return marks;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* The bookmarks whole before and broken now - a marker gone, or emptied - taken away, both markers.
|
|
596
|
+
* One broken already, or gone whole with the content it was in, is left as it is.
|
|
597
|
+
*/
|
|
598
|
+
function dropBookmarksBroken(root, before) {
|
|
599
|
+
if (before.size === 0)
|
|
600
|
+
return;
|
|
601
|
+
const after = bookmarksIn(root);
|
|
602
|
+
const drop = new Set();
|
|
603
|
+
for (const [id, was] of before) {
|
|
604
|
+
if (!was.start || !was.end)
|
|
605
|
+
continue;
|
|
606
|
+
const now = after.get(id);
|
|
607
|
+
if (!now)
|
|
608
|
+
continue;
|
|
609
|
+
if (!now.start || !now.end || (was.filled && !now.filled))
|
|
610
|
+
drop.add(id);
|
|
611
|
+
}
|
|
612
|
+
if (drop.size === 0)
|
|
613
|
+
return;
|
|
614
|
+
const visit = (o) => {
|
|
615
|
+
var _a, _b;
|
|
616
|
+
if (typeof o !== 'object' || o === null)
|
|
617
|
+
return;
|
|
618
|
+
if (Array.isArray(o)) {
|
|
619
|
+
for (let i = o.length - 1; i >= 0; i--) {
|
|
620
|
+
const el = o[i];
|
|
621
|
+
const local = (_a = el === null || el === void 0 ? void 0 : el.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
622
|
+
if ((local === 'bookmarkStart' || local === 'bookmarkEnd') && drop.has(String((_b = el.value) === null || _b === void 0 ? void 0 : _b.id)))
|
|
623
|
+
o.splice(i, 1);
|
|
624
|
+
else
|
|
625
|
+
visit(el);
|
|
626
|
+
}
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
for (const key in o)
|
|
630
|
+
if (!PROPERTIES.has(key))
|
|
631
|
+
visit(o[key]);
|
|
632
|
+
};
|
|
633
|
+
visit(root);
|
|
634
|
+
}
|
|
635
|
+
/** Adds the ids of the `w:footnoteReference` and `w:endnoteReference` elements anywhere in a tree to `into`. */
|
|
636
|
+
function noteReferencesIn(tree, into) {
|
|
637
|
+
const visit = (o) => {
|
|
638
|
+
var _a, _b;
|
|
639
|
+
if (typeof o !== 'object' || o === null)
|
|
640
|
+
return;
|
|
641
|
+
if (Array.isArray(o)) {
|
|
642
|
+
for (const item of o)
|
|
643
|
+
visit(item);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const local = (_a = o.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
647
|
+
if (local === 'footnoteReference' || local === 'endnoteReference') {
|
|
648
|
+
const id = Number((_b = (o.value)) === null || _b === void 0 ? void 0 : _b.id);
|
|
649
|
+
if (Number.isInteger(id))
|
|
650
|
+
into[local === 'footnoteReference' ? 'footnote' : 'endnote'].add(id);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
for (const [key, value] of Object.entries(o))
|
|
654
|
+
if (key !== 'PARENT')
|
|
655
|
+
visit(value);
|
|
656
|
+
};
|
|
657
|
+
visit(tree);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The notes `pending` holds taken out of the notes parts, each unless a reference to it still stands in
|
|
661
|
+
* the main document (an accepted move keeps the reference its source had, say); then `pending` is
|
|
662
|
+
* empty. A note's entry goes, Word's separators never: their ids are never referenced. The notes parts
|
|
663
|
+
* are read only when something is pending, so a save with none leaves them untouched. Word's own save
|
|
664
|
+
* also renumbers the notes left (the editor's `review-for-word` saves); their ids stay here.
|
|
665
|
+
*
|
|
666
|
+
* What a removed note held goes on with it, as docx4j's `Removals.flush` takes it (CR-002 section 50,
|
|
667
|
+
* docx4j's reading, not measured): the relationships its content referenced (a picture's image) are
|
|
668
|
+
* queued on `host` for the notes part, to go when nothing else uses them, and a comment whose reference
|
|
669
|
+
* was in it is queued for removal, its anchor being gone.
|
|
670
|
+
*/
|
|
671
|
+
export async function flushPendingNoteRemovals(main, pending, host) {
|
|
672
|
+
if (pending.footnote.size === 0 && pending.endnote.size === 0)
|
|
673
|
+
return;
|
|
674
|
+
const standing = { footnote: new Set(), endnote: new Set() };
|
|
675
|
+
noteReferencesIn(await main.getContents(), standing);
|
|
676
|
+
const prune = (part, notes, ids, kept) => {
|
|
677
|
+
if (!notes)
|
|
678
|
+
return;
|
|
679
|
+
const released = new Set();
|
|
680
|
+
for (let i = notes.length - 1; i >= 0; i--) {
|
|
681
|
+
const note = notes[i];
|
|
682
|
+
if ((note.type === undefined || note.type === 'normal') && ids.has(Number(note.id)) && !kept.has(Number(note.id))) {
|
|
683
|
+
relationshipIdsIn(note.content, released);
|
|
684
|
+
if (host === null || host === void 0 ? void 0 : host.pendingCommentRemovals)
|
|
685
|
+
commentReferencesIn(note.content, host.pendingCommentRemovals);
|
|
686
|
+
notes.splice(i, 1);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
queueRelationshipRemovals(host === null || host === void 0 ? void 0 : host.pendingRelationshipRemovals, part, released);
|
|
690
|
+
};
|
|
691
|
+
if (pending.footnote.size > 0 && main.footnotesPart)
|
|
692
|
+
prune(main.footnotesPart, (await main.footnotesPart.getContents()).footnote, pending.footnote, standing.footnote);
|
|
693
|
+
if (pending.endnote.size > 0 && main.endnotesPart)
|
|
694
|
+
prune(main.endnotesPart, (await main.endnotesPart.getContents()).endnote, pending.endnote, standing.endnote);
|
|
695
|
+
pending.footnote.clear();
|
|
696
|
+
pending.endnote.clear();
|
|
697
|
+
}
|
|
698
|
+
/** Adds the ids of the `w:commentReference` elements anywhere in a tree to `into`. */
|
|
699
|
+
function commentReferencesIn(tree, into) {
|
|
700
|
+
const visit = (o) => {
|
|
701
|
+
var _a, _b;
|
|
702
|
+
if (typeof o !== 'object' || o === null)
|
|
703
|
+
return;
|
|
704
|
+
if (Array.isArray(o)) {
|
|
705
|
+
for (const item of o)
|
|
706
|
+
visit(item);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (((_a = o.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference') {
|
|
710
|
+
const id = Number((_b = (o.value)) === null || _b === void 0 ? void 0 : _b.id);
|
|
711
|
+
if (Number.isInteger(id))
|
|
712
|
+
into.add(id);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
for (const [key, value] of Object.entries(o))
|
|
716
|
+
if (key !== 'PARENT')
|
|
717
|
+
visit(value);
|
|
718
|
+
};
|
|
719
|
+
visit(tree);
|
|
380
720
|
}
|
|
381
721
|
/** A holder's four insertion markers taken away; the element stays. */
|
|
382
722
|
function removeHolderMarkers(t) {
|
|
@@ -425,6 +765,87 @@ export function holderInsertionAt(items, i, paragraph) {
|
|
|
425
765
|
return undefined;
|
|
426
766
|
return new TrackedChange({ kind: 'holder', holder, owner: items, outer: [start, outerEnd], inner: [innerEnd, innerStart], value: start.value }, paragraph);
|
|
427
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* An inserted field rejected (Word's form, check 30 `field-inserted`): the `w:ins` pieces and the plain
|
|
770
|
+
* runs between them go, last first so that a comment's range is judged once the field's text is gone;
|
|
771
|
+
* a bookmark, a comment's range or a proofing mark between them stays where it stood.
|
|
772
|
+
*/
|
|
773
|
+
function removeField(t, paragraph) {
|
|
774
|
+
for (let i = t.span.length - 1; i >= 0; i--) {
|
|
775
|
+
const el = t.span[i];
|
|
776
|
+
if (revisionKindOf(el) === 'ins')
|
|
777
|
+
removeRevision({ kind: 'run', revision: 'ins', element: el, owner: currentOwner(t.owner, el), value: el.value }, paragraph);
|
|
778
|
+
else if (typeNameOf(el) === 'org_docx4j_wml.R')
|
|
779
|
+
remove(t.owner, el);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Word's form for a field inserted under tracking, when `items[i]` starts it: a `w:ins` that opens a
|
|
784
|
+
* field (a `w:fldChar` begin it does not end), then plain runs - the separator and the result - and
|
|
785
|
+
* markers, then a `w:ins` by the same author in which that field ends (check 30, `field-inserted`:
|
|
786
|
+
* begin and instruction in one, the end run in the other). Another revision, a run whose formatting
|
|
787
|
+
* changed, a run holder, or a field that ends in a plain run is not this form, and is listed as before.
|
|
788
|
+
* A field wholly inside one `w:ins` needs nothing of this: that `w:ins` already takes it whole.
|
|
789
|
+
*/
|
|
790
|
+
export function insertedFieldAt(items, i, paragraph) {
|
|
791
|
+
var _a;
|
|
792
|
+
const first = items[i];
|
|
793
|
+
if (revisionKindOf(first) !== 'ins')
|
|
794
|
+
return undefined;
|
|
795
|
+
const author = first.value.author;
|
|
796
|
+
let depth = 0;
|
|
797
|
+
// the fldChars of an element's runs in order: 'closed' when the field the span opened ends
|
|
798
|
+
const scan = (el) => {
|
|
799
|
+
var _a, _b, _c;
|
|
800
|
+
const runs = typeNameOf(el) === 'org_docx4j_wml.R' ? [el] : (_a = runItemsOf(el.value)) !== null && _a !== void 0 ? _a : [];
|
|
801
|
+
for (const run of runs) {
|
|
802
|
+
if (typeNameOf(run) !== 'org_docx4j_wml.R')
|
|
803
|
+
continue;
|
|
804
|
+
for (const item of (_b = run.value.content) !== null && _b !== void 0 ? _b : []) {
|
|
805
|
+
if (((_c = item.name) === null || _c === void 0 ? void 0 : _c.localPart) !== 'fldChar')
|
|
806
|
+
continue;
|
|
807
|
+
const type = item.value.fldCharType;
|
|
808
|
+
if (type === 'begin')
|
|
809
|
+
depth++;
|
|
810
|
+
else if (type === 'end') {
|
|
811
|
+
depth--;
|
|
812
|
+
if (depth < 0)
|
|
813
|
+
return 'other';
|
|
814
|
+
if (depth === 0)
|
|
815
|
+
return 'closed';
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return 'open';
|
|
820
|
+
};
|
|
821
|
+
if (scan(first) !== 'open' || depth === 0)
|
|
822
|
+
return undefined;
|
|
823
|
+
for (let j = i + 1; j < items.length; j++) {
|
|
824
|
+
const el = items[j];
|
|
825
|
+
const kind = revisionKindOf(el);
|
|
826
|
+
if (kind === 'ins') {
|
|
827
|
+
if (el.value.author !== author)
|
|
828
|
+
return undefined;
|
|
829
|
+
const state = scan(el);
|
|
830
|
+
if (state === 'other')
|
|
831
|
+
return undefined;
|
|
832
|
+
if (state === 'closed')
|
|
833
|
+
return new TrackedChange({ kind: 'field', span: items.slice(i, j + 1), owner: items, value: first.value }, paragraph);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (kind !== undefined)
|
|
837
|
+
return undefined;
|
|
838
|
+
const v = el.value;
|
|
839
|
+
if (typeNameOf(el) === 'org_docx4j_wml.R') {
|
|
840
|
+
if (((_a = v.rPr) === null || _a === void 0 ? void 0 : _a.rPrChange) || scan(el) !== 'open')
|
|
841
|
+
return undefined;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (typeof v === 'object' && v !== null && runItemsOf(v))
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
return undefined;
|
|
848
|
+
}
|
|
428
849
|
/**
|
|
429
850
|
* A row taken away, and its table with it when it was the last: a table has no revision of its own,
|
|
430
851
|
* so one whose rows were all inserted was inserted, and one whose rows were all deleted was deleted.
|
|
@@ -471,6 +892,7 @@ function removeRevision(t, paragraph) {
|
|
|
471
892
|
if (owner.some((other, e) => { var _a; return e > i && ((_a = other.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeEnd' && idOf(other) === id; }))
|
|
472
893
|
outside.add(id);
|
|
473
894
|
}
|
|
895
|
+
discarding(t.element);
|
|
474
896
|
owner.splice(i, 1, ...kept);
|
|
475
897
|
linkParents(kept, t.element.value.PARENT);
|
|
476
898
|
const body = paragraph === null || paragraph === void 0 ? void 0 : paragraph.parentBody;
|
|
@@ -563,6 +985,7 @@ function dropMark(paragraph, which) {
|
|
|
563
985
|
*/
|
|
564
986
|
export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
565
987
|
var _a, _b, _c, _d, _e;
|
|
988
|
+
discarding();
|
|
566
989
|
const container = currentOwner(paragraph.container, paragraph.element);
|
|
567
990
|
const i = container.indexOf(paragraph.element);
|
|
568
991
|
const next = i >= 0 ? container[i + 1] : undefined;
|
|
@@ -575,6 +998,7 @@ export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
|
575
998
|
for (const item of (_b = p.content) !== null && _b !== void 0 ? _b : [])
|
|
576
999
|
items.push(item);
|
|
577
1000
|
linkParents(items, into);
|
|
1001
|
+
discarding(undefined, p.pPr);
|
|
578
1002
|
container.splice(i, 1);
|
|
579
1003
|
return;
|
|
580
1004
|
}
|
|
@@ -591,6 +1015,7 @@ export function joinWithNext(paragraph, fallbackToPrevious = false) {
|
|
|
591
1015
|
for (const item of (_e = nextP.content) !== null && _e !== void 0 ? _e : [])
|
|
592
1016
|
content.push(item);
|
|
593
1017
|
linkParents(content, p);
|
|
1018
|
+
discarding(undefined, p.pPr); // a section break's sectPr goes with it
|
|
594
1019
|
p.pPr = nextP.pPr;
|
|
595
1020
|
if (p.pPr)
|
|
596
1021
|
linkParents(p.pPr, p);
|
|
@@ -652,6 +1077,131 @@ function acceptedOffsetOf(p, target) {
|
|
|
652
1077
|
visit(runItemsOf(p));
|
|
653
1078
|
return found >= 0 ? found : pos;
|
|
654
1079
|
}
|
|
1080
|
+
// --- math (CR-002 section 51) ------------------------------------------------------------------
|
|
1081
|
+
/** True for a value of the math model (`m:oMath`, `m:r`, `m:sSup`, ...). */
|
|
1082
|
+
function isMathValue(value) {
|
|
1083
|
+
const tn = value === null || value === void 0 ? void 0 : value.TYPE_NAME;
|
|
1084
|
+
return typeof tn === 'string' && tn.startsWith('org_docx4j_math.');
|
|
1085
|
+
}
|
|
1086
|
+
/** The text of an `m:t` (or a `w:t`) in an `m:r`; undefined for anything else. */
|
|
1087
|
+
function mathTextOf(item) {
|
|
1088
|
+
var _a, _b, _c;
|
|
1089
|
+
if (((_a = item === null || item === void 0 ? void 0 : item.name) === null || _a === void 0 ? void 0 : _a.localPart) !== 't')
|
|
1090
|
+
return undefined;
|
|
1091
|
+
return String((_c = (_b = item.value) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : '');
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* A math run's revision resolved: `keep` (an insertion accepted, a deletion rejected) puts its content in
|
|
1095
|
+
* its place in the `m:r`; otherwise it goes, and the `m:r` with it when no text is left in it (docx4j's
|
|
1096
|
+
* reading of Word's Accept All and Reject All of `tracked-changes-equations.docx`).
|
|
1097
|
+
*/
|
|
1098
|
+
function resolveMathRun(t, keep) {
|
|
1099
|
+
var _a, _b;
|
|
1100
|
+
const content = ((_a = t.run.value.content) !== null && _a !== void 0 ? _a : []);
|
|
1101
|
+
const i = content.indexOf(t.item);
|
|
1102
|
+
if (i < 0)
|
|
1103
|
+
return;
|
|
1104
|
+
if (keep) {
|
|
1105
|
+
const moved = ((_b = t.value.rest) !== null && _b !== void 0 ? _b : []);
|
|
1106
|
+
content.splice(i, 1, ...moved);
|
|
1107
|
+
linkParents(moved, t.run.value);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
discarding(t.item);
|
|
1111
|
+
content.splice(i, 1);
|
|
1112
|
+
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'; })) {
|
|
1113
|
+
remove(t.runOwner, t.run);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* A structure's insertion or deletion resolved: `keep` drops the marker, its recorded run properties
|
|
1118
|
+
* becoming the control's when it has none, as Word's saves have them; otherwise the structure goes
|
|
1119
|
+
* whole from its list. An argument's own marker has no structure, and is dropped either way.
|
|
1120
|
+
*/
|
|
1121
|
+
function resolveMathStructure(t, keep) {
|
|
1122
|
+
var _a;
|
|
1123
|
+
if (!keep && t.structure && t.owner) {
|
|
1124
|
+
remove(t.owner, t.structure);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
const ctrlPr = t.ctrlPr;
|
|
1128
|
+
if (!ctrlPr.rPr && t.value.rPr) {
|
|
1129
|
+
ctrlPr.rPr = rPrFromElements((_a = t.value.rPr.egrPrBase) !== null && _a !== void 0 ? _a : []);
|
|
1130
|
+
linkParents(ctrlPr.rPr, ctrlPr);
|
|
1131
|
+
}
|
|
1132
|
+
if (t.revision === 'ins')
|
|
1133
|
+
delete ctrlPr.ins;
|
|
1134
|
+
else
|
|
1135
|
+
delete ctrlPr.del;
|
|
1136
|
+
}
|
|
1137
|
+
/** The equation a math piece is in: the outermost level's element, the one in the paragraph. */
|
|
1138
|
+
function equationOf(stack) {
|
|
1139
|
+
var _a;
|
|
1140
|
+
const top = (_a = stack[0]) === null || _a === void 0 ? void 0 : _a.item;
|
|
1141
|
+
return top && typeof top === 'object' && 'name' in top ? { equation: top } : {};
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* An equation's single changes, in document order, docx4j's walk (its CR-043 `Lister.math`): a
|
|
1145
|
+
* revision inside an `m:r`; a structure's, in its `m:ctrlPr`, which comes before the structure's
|
|
1146
|
+
* arguments; and a run-level revision holding math. A math run with text and no revision keeps the
|
|
1147
|
+
* pieces either side apart, as plain text does.
|
|
1148
|
+
*/
|
|
1149
|
+
function mathTokens(paragraph, value, item, owner, stack, out) {
|
|
1150
|
+
var _a;
|
|
1151
|
+
const tn = value.TYPE_NAME;
|
|
1152
|
+
if (tn === 'org_docx4j_math.CTR') {
|
|
1153
|
+
for (const c of ((_a = value.content) !== null && _a !== void 0 ? _a : [])) {
|
|
1154
|
+
const cv = c === null || c === void 0 ? void 0 : c.value;
|
|
1155
|
+
if ((cv === null || cv === void 0 ? void 0 : cv.TYPE_NAME) === 'org_docx4j_wml.CTMathRunTrackChange' && owner) {
|
|
1156
|
+
out.push(new TrackedChange({ kind: 'mathRun', revision: c.name.localPart === 'del' ? 'del' : 'ins', item: c, run: item, runOwner: owner, value: cv, ...equationOf(stack) }, paragraph));
|
|
1157
|
+
}
|
|
1158
|
+
else if (mathTextOf(c) !== undefined)
|
|
1159
|
+
out.push(BREAK);
|
|
1160
|
+
}
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
if (tn === 'org_docx4j_math.CTCtrlPr') {
|
|
1164
|
+
const ctrlPr = value;
|
|
1165
|
+
if (!ctrlPr.ins && !ctrlPr.del)
|
|
1166
|
+
return;
|
|
1167
|
+
// in m:sSupPr in m:sSup, the structure is two up; an argument's (m:e's) own marks none
|
|
1168
|
+
const parent = stack[stack.length - 1];
|
|
1169
|
+
const grand = parent && parent.value.TYPE_NAME !== 'org_docx4j_math.CTOMathArg' ? stack[stack.length - 2] : undefined;
|
|
1170
|
+
const structure = { ...((grand === null || grand === void 0 ? void 0 : grand.owner) ? { structure: grand.item, owner: grand.owner } : {}), ...equationOf(stack) };
|
|
1171
|
+
if (ctrlPr.ins)
|
|
1172
|
+
out.push(new TrackedChange({ kind: 'mathStructure', revision: 'ins', ctrlPr, value: ctrlPr.ins, ...structure }, paragraph));
|
|
1173
|
+
if (ctrlPr.del)
|
|
1174
|
+
out.push(new TrackedChange({ kind: 'mathStructure', revision: 'del', ctrlPr, value: ctrlPr.del, ...structure }, paragraph));
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
stack.push({ value, item, owner });
|
|
1178
|
+
for (const key of Object.keys(value)) {
|
|
1179
|
+
if (key === 'PARENT' || key === 'TYPE_NAME')
|
|
1180
|
+
continue;
|
|
1181
|
+
const field = value[key];
|
|
1182
|
+
if (typeof field !== 'object' || field === null)
|
|
1183
|
+
continue;
|
|
1184
|
+
if (Array.isArray(field)) {
|
|
1185
|
+
const list = field;
|
|
1186
|
+
for (const c of list) {
|
|
1187
|
+
if (typeof c !== 'object' || c === null)
|
|
1188
|
+
continue;
|
|
1189
|
+
const kind = revisionKindOf(c);
|
|
1190
|
+
if (kind !== undefined) {
|
|
1191
|
+
// a w:ins or w:del holding m:r: as runs (schema-legal; docx4j's U7, in no fixture)
|
|
1192
|
+
out.push(new TrackedChange({ kind: 'run', revision: kind, element: c, owner: list, value: c.value }, paragraph));
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
const cv = c !== null && typeof c === 'object' && 'value' in c && 'name' in c ? c.value : c;
|
|
1196
|
+
if (isMathValue(cv))
|
|
1197
|
+
mathTokens(paragraph, cv, c, list, stack, out);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
else if (isMathValue(field))
|
|
1201
|
+
mathTokens(paragraph, field, field, undefined, stack, out);
|
|
1202
|
+
}
|
|
1203
|
+
stack.pop();
|
|
1204
|
+
}
|
|
655
1205
|
const RANGE_MARKERS = {
|
|
656
1206
|
moveFromRangeStart: { kind: 'start', side: 'from' }, moveFromRangeEnd: { kind: 'end', side: 'from' },
|
|
657
1207
|
moveToRangeStart: { kind: 'start', side: 'to' }, moveToRangeEnd: { kind: 'end', side: 'to' },
|
|
@@ -997,6 +1547,18 @@ function tokensOfRunLevel(paragraph, items, out) {
|
|
|
997
1547
|
i += 2;
|
|
998
1548
|
continue;
|
|
999
1549
|
}
|
|
1550
|
+
// a field Word inserted, its separator and result plain between two insertions: one piece
|
|
1551
|
+
const field = insertedFieldAt(items, i, paragraph);
|
|
1552
|
+
if (field && field.target.kind === 'field') {
|
|
1553
|
+
out.push(field);
|
|
1554
|
+
i += field.target.span.length - 1;
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
// an equation: the revisions in its runs and on its structures (section 51)
|
|
1558
|
+
if (isMathValue(el.value)) {
|
|
1559
|
+
mathTokens(paragraph, el.value, el, items, [], out);
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1000
1562
|
const kind = revisionKindOf(el);
|
|
1001
1563
|
if (kind !== undefined) {
|
|
1002
1564
|
// moved text is content Office JS cannot list (checks 16, 17), and is kept apart
|
|
@@ -1055,8 +1617,10 @@ function groupable(change) {
|
|
|
1055
1617
|
return t.revision;
|
|
1056
1618
|
if (t.kind === 'runProperties' || t.kind === 'markProperties')
|
|
1057
1619
|
return 'format';
|
|
1058
|
-
if (t.kind === 'holder')
|
|
1620
|
+
if (t.kind === 'holder' || t.kind === 'field')
|
|
1059
1621
|
return 'ins';
|
|
1622
|
+
if (t.kind === 'mathRun' || t.kind === 'mathStructure')
|
|
1623
|
+
return t.revision;
|
|
1060
1624
|
// an inserted or deleted row groups with rows and text touching it: Office JS lists an inserted
|
|
1061
1625
|
// table of three rows, with the inserted paragraphs before and after it, as one change, and rows
|
|
1062
1626
|
// added to a table that was there as one change per touching run of them (check 31)
|
|
@@ -1106,13 +1670,13 @@ export function groupTouching(tokens) {
|
|
|
1106
1670
|
* The `w:trPr/w:ins` and `w:trPr/w:del` revisions of a table row. `inner` is the revision markup
|
|
1107
1671
|
* of the row's own cells, which the row's change owns rather than reporting separately.
|
|
1108
1672
|
*/
|
|
1109
|
-
export function trackedChangesOfRow(tr, owner, inner = [], table) {
|
|
1673
|
+
export function trackedChangesOfRow(tr, owner, inner = [], table, body) {
|
|
1110
1674
|
const trPr = tr.value.trPr;
|
|
1111
1675
|
const out = [];
|
|
1112
1676
|
if (trPr === null || trPr === void 0 ? void 0 : trPr.ins)
|
|
1113
|
-
out.push(new TrackedChange({ kind: 'row', row: 'ins', tr, owner, value: trPr.ins, inner, table }, undefined));
|
|
1677
|
+
out.push(new TrackedChange({ kind: 'row', row: 'ins', tr, owner, value: trPr.ins, inner, table }, undefined, body));
|
|
1114
1678
|
if (trPr === null || trPr === void 0 ? void 0 : trPr.del)
|
|
1115
|
-
out.push(new TrackedChange({ kind: 'row', row: 'del', tr, owner, value: trPr.del, inner, table }, undefined));
|
|
1679
|
+
out.push(new TrackedChange({ kind: 'row', row: 'del', tr, owner, value: trPr.del, inner, table }, undefined, body));
|
|
1116
1680
|
return out;
|
|
1117
1681
|
}
|
|
1118
1682
|
// --- CR-002 section 35: tables, cells, marks and sections --------------------------------------------
|
|
@@ -1122,12 +1686,12 @@ export function tablePropertiesChangeOf(tbl) {
|
|
|
1122
1686
|
return records ? new TrackedChange({ kind: 'tableProperties', tbl, value: records.value }, undefined) : undefined;
|
|
1123
1687
|
}
|
|
1124
1688
|
/** A cell's own revision, `w:cellIns` or `w:cellDel`, or undefined. */
|
|
1125
|
-
export function cellChangeOf(tc, owner, tr) {
|
|
1689
|
+
export function cellChangeOf(tc, owner, tr, body) {
|
|
1126
1690
|
const tcPr = tc.value.tcPr;
|
|
1127
1691
|
if (tcPr === null || tcPr === void 0 ? void 0 : tcPr.cellIns)
|
|
1128
|
-
return new TrackedChange({ kind: 'cell', cell: 'ins', tc, owner, tr, value: tcPr.cellIns }, undefined);
|
|
1692
|
+
return new TrackedChange({ kind: 'cell', cell: 'ins', tc, owner, tr, value: tcPr.cellIns }, undefined, body);
|
|
1129
1693
|
if (tcPr === null || tcPr === void 0 ? void 0 : tcPr.cellDel)
|
|
1130
|
-
return new TrackedChange({ kind: 'cell', cell: 'del', tc, owner, tr, value: tcPr.cellDel }, undefined);
|
|
1694
|
+
return new TrackedChange({ kind: 'cell', cell: 'del', tc, owner, tr, value: tcPr.cellDel }, undefined, body);
|
|
1131
1695
|
return undefined;
|
|
1132
1696
|
}
|
|
1133
1697
|
/** The change to the body's last section (`w:body/w:sectPr/w:sectPrChange`), its text the section's paragraphs'. */
|
|
@@ -1153,6 +1717,7 @@ function removeCell(tc, owner) {
|
|
|
1153
1717
|
const cells = owner.filter((el) => typeNameOf(el) === 'org_docx4j_wml.Tc');
|
|
1154
1718
|
const at = cells.indexOf(tc);
|
|
1155
1719
|
const neighbour = (_a = cells[at - 1]) !== null && _a !== void 0 ? _a : cells[at + 1];
|
|
1720
|
+
discarding(tc);
|
|
1156
1721
|
owner.splice(i, 1);
|
|
1157
1722
|
if (!neighbour)
|
|
1158
1723
|
return;
|