@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.
Files changed (49) hide show
  1. package/README.md +5 -2
  2. package/dist/html/convert.mjs +38 -14
  3. package/dist/model/content/Body.d.mts +13 -1
  4. package/dist/model/content/Body.mjs +90 -19
  5. package/dist/model/content/Comment.mjs +7 -0
  6. package/dist/model/content/InlinePicture.d.mts +71 -7
  7. package/dist/model/content/InlinePicture.mjs +201 -28
  8. package/dist/model/content/List.mjs +10 -1
  9. package/dist/model/content/Paragraph.d.mts +28 -5
  10. package/dist/model/content/Paragraph.mjs +77 -37
  11. package/dist/model/content/Range.d.mts +18 -2
  12. package/dist/model/content/Range.mjs +194 -64
  13. package/dist/model/content/TrackedChange.d.mts +129 -3
  14. package/dist/model/content/TrackedChange.mjs +577 -12
  15. package/dist/model/content/binder.d.mts +2 -1
  16. package/dist/model/content/binder.mjs +48 -1
  17. package/dist/model/content/index.d.mts +2 -2
  18. package/dist/model/content/index.mjs +1 -1
  19. package/dist/model/content/relationships.d.mts +30 -0
  20. package/dist/model/content/relationships.mjs +133 -0
  21. package/dist/model/content/tree.d.mts +52 -2
  22. package/dist/model/content/tree.mjs +48 -3
  23. package/dist/model/customxml/pictures.d.mts +5 -2
  24. package/dist/model/customxml/pictures.mjs +13 -7
  25. package/dist/model/fonts/families.generated.mjs +2 -2
  26. package/dist/model/fonts/registry.mjs +7 -1
  27. package/dist/model/fonts/substitutions.generated.mjs +2 -2
  28. package/dist/model/fonts/themes.generated.mjs +1 -1
  29. package/dist/model/listnumbering/definitions.mjs +18 -0
  30. package/dist/model/properties/PropertyResolver.d.mts +27 -6
  31. package/dist/model/properties/PropertyResolver.mjs +83 -16
  32. package/dist/model/properties/catalogue.d.mts +15 -1
  33. package/dist/model/properties/catalogue.mjs +80 -27
  34. package/dist/model/properties/table.d.mts +16 -2
  35. package/dist/model/properties/table.mjs +13 -3
  36. package/dist/opc/Load.d.mts +9 -2
  37. package/dist/opc/index.d.mts +2 -2
  38. package/dist/opc/index.mjs +2 -2
  39. package/dist/opc/mce/preprocessor.d.mts +62 -6
  40. package/dist/opc/mce/preprocessor.mjs +142 -35
  41. package/dist/opc/mce/understood.d.mts +10 -0
  42. package/dist/opc/mce/understood.mjs +25 -3
  43. package/dist/packages/OpcPackage.d.mts +7 -0
  44. package/dist/packages/OpcPackage.mjs +15 -3
  45. package/dist/packages/WordprocessingMLPackage.d.mts +35 -2
  46. package/dist/packages/WordprocessingMLPackage.mjs +98 -2
  47. package/dist/parts/XmlPart.d.mts +26 -5
  48. package/dist/parts/XmlPart.mjs +67 -18
  49. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { Font } from './Font.mjs';
2
2
  import { runFontSelectorOf } from '../fonts/lookup.mjs';
3
- import { typeNameOf, linkParents, runItemsOf, runsOf } from './tree.mjs';
3
+ import { typeNameOf, linkParents, runItemsOf, runsOf, revisionKindOf, WHOLE } from './tree.mjs';
4
4
  import { Docx4JException } from '../../opc/exceptions.mjs';
5
5
  import { ContentControl } from './ContentControl.mjs';
6
6
  import { sdt as sdtOf, nextSdtId, applyRunOptions, t as textElement } from '@docx4j/generated-objects-ts/builders/wml';
@@ -15,12 +15,23 @@ import { commentApi } from './comments.mjs';
15
15
  * A subset of Office JS `Word.Range`: a span of text within one paragraph, [start, end) in the
16
16
  * paragraph's text. Positions are recomputed from the tree on every use, so a range stays
17
17
  * valid across edits made through it; edits made elsewhere may shift it.
18
+ *
19
+ * A picture has a place in the text and no text of its own, so a picture at the range's start or
20
+ * end offset may be inside it or outside it. `whole` says: a range from `Paragraph.getRange('Whole')`
21
+ * or `'Content'` holds the paragraph's whole content, the pictures at its edges too, and deleting or
22
+ * replacing it deletes them; any other range holds the pictures strictly inside it, so that a search
23
+ * hit replaced does not take the picture beside it (CR-002 section 48).
18
24
  */
19
25
  export class Range {
20
- constructor(paragraph, start, end) {
26
+ constructor(paragraph, start, end, whole = false) {
21
27
  this.paragraph = paragraph;
22
28
  this.start = start;
23
29
  this.end = end;
30
+ this.whole = whole;
31
+ }
32
+ /** The edges `Paragraph.splice` is given: both for a whole range. */
33
+ get edges() {
34
+ return this.whole ? WHOLE : undefined;
24
35
  }
25
36
  get paragraphs() {
26
37
  return [this.paragraph];
@@ -93,7 +104,7 @@ export class Range {
93
104
  insertText(text, location) {
94
105
  let r;
95
106
  if (location === 'Replace')
96
- r = this.paragraph.splice(this.start, this.end, text);
107
+ r = this.paragraph.splice(this.start, this.end, text, 'before', this.edges);
97
108
  // text put before the range joins the run the range starts in - inside the link the range is on,
98
109
  // as Office JS (check 32: insertText('Before') on a link's text went into the link, as 'After' did)
99
110
  else if (location === 'Before' || location === 'Start')
@@ -145,9 +156,9 @@ export class Range {
145
156
  : { element: el, container: this.paragraph.container };
146
157
  });
147
158
  }
148
- /** Removes the span's text. */
159
+ /** Removes the span's text, and the pictures in it (CR-002 section 48). */
149
160
  delete() {
150
- this.paragraph.splice(this.start, this.end, '');
161
+ this.paragraph.splice(this.start, this.end, '', 'before', this.edges);
151
162
  this.end = this.start;
152
163
  }
153
164
  search(text, options) {
@@ -183,7 +194,7 @@ export class Range {
183
194
  return new Range(this.paragraph, this.start, this.start);
184
195
  if (location === 'End')
185
196
  return new Range(this.paragraph, this.end, this.end);
186
- return new Range(this.paragraph, this.start, this.end);
197
+ return new Range(this.paragraph, this.start, this.end, this.whole);
187
198
  }
188
199
  /** The span's text as it stands (extension; Office JS getOoxml wraps it in a package). */
189
200
  toString() {
@@ -209,7 +220,7 @@ export class Range {
209
220
  * span gets an empty control at its position. Returns the control.
210
221
  */
211
222
  insertContentControl(kind) {
212
- var _a, _b;
223
+ var _a;
213
224
  const paragraph = this.paragraph;
214
225
  const body = paragraph.parentBody;
215
226
  // The control is built empty first: `sdt` refuses a kind that cannot be run-level (a repeating
@@ -220,62 +231,44 @@ export class Range {
220
231
  paragraph.insertItemsAt(this.start, [sdt]);
221
232
  return new ContentControl(sdt, containerOf(sdt, paragraph), body);
222
233
  }
223
- paragraph.splitAt(this.start);
224
- paragraph.splitAt(this.end);
225
- const segments = paragraph.segments().filter((s) => s.start >= this.start && s.end <= this.end);
226
- if (segments.length === 0)
234
+ // Every refusal before anything changes (CR-002 section 43): until then the runs were split first.
235
+ const covered = coveredSegments(paragraph, this.start, this.end);
236
+ if (covered.length === 0)
227
237
  throw new Docx4JException('This range covers no run');
228
- const owner = segments[0].runOwner;
229
- if (segments.some((s) => s.runOwner !== owner)) {
238
+ const coveredOwner = covered[0].runOwner;
239
+ if (covered.some((s) => s.runOwner !== coveredOwner)) {
230
240
  throw new Docx4JException('This range spans more than one run holder (a hyperlink or a tracked change); wrap a narrower span');
231
241
  }
232
- const items = [];
233
- for (const segment of segments) {
234
- const element = owner[segment.runIndex];
235
- if (!items.includes(element))
236
- items.push(element);
237
- }
238
- const at = owner.indexOf(items[0]);
242
+ refuseTrackedDeletion(coveredOwner, covered);
239
243
  const tracker = paragraph.changeTracker;
240
- const revision = segments[0].revision;
244
+ const coveredRevision = revisionHolding(covered[0], coveredOwner);
245
+ if (tracker && coveredRevision && itemsKeyOf(coveredRevision) === undefined) {
246
+ throw new Docx4JException('This range is in a revision that cannot be split around a content control');
247
+ }
248
+ paragraph.splitAt(this.start);
249
+ paragraph.splitAt(this.end);
250
+ const segments = paragraph.segments().filter((s) => s.start >= this.start && s.end <= this.end);
251
+ const owner = segments[0].runOwner;
252
+ const { at, items } = wrappedItems(owner, segments);
253
+ const revision = revisionHolding(segments[0], owner);
241
254
  if (tracker && revision) {
242
255
  // The runs are in a revision: the control goes between its halves, not in it, and the runs go
243
256
  // into the control in a revision of the same kind and attributes with a new id - Word's form for
244
257
  // inserted text wrapped in a new control (check 30, inserted-text-wrapped: the w:ins inside the
245
258
  // control, the control's own markers outside it).
246
- const index = revision.items.indexOf(items[0]);
247
- revision.items.splice(index, items.length);
248
- const value = revision.value;
249
- const itemsKey = Object.keys(value).find((key) => value[key] === revision.items);
250
- const inner = {};
251
- for (const [key, v] of Object.entries(value))
252
- if (key !== itemsKey && key !== 'PARENT')
253
- inner[key] = v;
254
- inner.id = tracker.nextId();
255
- inner[itemsKey] = items;
256
- const wrapped = { name: revision.element.name, value: inner };
257
- linkParents(items, inner);
259
+ revision.items.splice(at, items.length);
260
+ const wrapped = revisionAround(revision, items, tracker.nextId());
258
261
  sdt.value.sdtContent.content = [wrapped];
259
262
  linkParents(wrapped, sdt.value.sdtContent);
260
- const holderOwner = revision.owner;
261
- const parent = (_a = revision.value.PARENT) !== null && _a !== void 0 ? _a : paragraph.p;
262
- if (revision.items.length === 0) {
263
- holderOwner.splice(holderOwner.indexOf(revision.element), 1, sdt); // the whole revision wrapped
264
- linkParents(sdt, parent);
265
- }
266
- else if (index === 0) {
267
- holderOwner.splice(holderOwner.indexOf(revision.element), 0, sdt); // its start wrapped
268
- linkParents(sdt, parent);
269
- }
270
- else if (!paragraph.splitHolderAround(revision, index, sdt, () => tracker.nextId())) {
263
+ if (!placeInRevision(paragraph, revision, at, sdt, () => tracker.nextId())) {
271
264
  throw new Docx4JException('This range is in a revision that cannot be split around a content control');
272
265
  }
273
- markHolderInserted(tracker, sdt, holderOwner, () => { });
274
- return new ContentControl(sdt, holderOwner, body);
266
+ markHolderInserted(tracker, sdt, revision.owner, () => { });
267
+ return new ContentControl(sdt, revision.owner, body);
275
268
  }
276
269
  sdt.value.sdtContent.content = items;
277
270
  owner.splice(at, items.length, sdt);
278
- linkParents(sdt, (_b = segments[0].run.PARENT) !== null && _b !== void 0 ? _b : paragraph.p);
271
+ linkParents(sdt, (_a = segments[0].run.PARENT) !== null && _a !== void 0 ? _a : paragraph.p);
279
272
  // under tracking the control itself is the insertion, the runs in it what they were (check 30)
280
273
  if (tracker)
281
274
  markHolderInserted(tracker, sdt, owner, () => { });
@@ -340,12 +333,18 @@ export class Range {
340
333
  start = Math.min(start, span[0]);
341
334
  end = Math.max(end, span[1]);
342
335
  }
343
- this.removeHyperlinks();
344
- if (value === '')
345
- return;
346
336
  const hash = value.indexOf('#');
347
337
  const address = hash === -1 ? value : value.slice(0, hash);
348
338
  const location = hash === -1 ? '' : value.slice(hash + 1);
339
+ // Every refusal comes before anything changes, judged on what the range would hold once the links
340
+ // it touches were unwrapped (CR-002 section 43). Until then the setter unwrapped them, added the
341
+ // relationship and split the runs before it refused, so a refused call left the links gone, an
342
+ // orphan relationship and split runs (found by the docx4j-python session).
343
+ if (value !== '')
344
+ this.refuseUnlinkable(start, end, address);
345
+ this.removeHyperlinks();
346
+ if (value === '')
347
+ return;
349
348
  // w:history="1" is what Word writes on every hyperlink it makes, and what docx4j writes when
350
349
  // it builds one (MainDocumentPart.addHyperlink): "add the target to the viewed hyperlinks"
351
350
  // (ECMA-376 17.16.22). Readers do not depend on it, but a document holding one holder from
@@ -359,26 +358,79 @@ export class Range {
359
358
  paragraph.splitAt(start);
360
359
  paragraph.splitAt(end);
361
360
  const segments = paragraph.segments().filter((seg) => seg.start >= start && seg.end <= end);
362
- if (segments.length === 0)
363
- throw new Docx4JException('This range covers no run');
364
361
  const owner = segments[0].runOwner;
365
- if (segments.some((seg) => seg.runOwner !== owner)) {
366
- throw new Docx4JException('This range spans more than one run holder (a hyperlink or a tracked change); wrap a narrower span');
362
+ const { at, items } = wrappedItems(owner, segments);
363
+ const revision = revisionHolding(segments[0], owner);
364
+ let runsParent = holder.value;
365
+ if (revision) {
366
+ // A w:hyperlink cannot be in a w:ins or w:moveTo (CT_RunTrackChange holds EG_ContentRunContent
367
+ // only), so over runs in one, tracked or not, the link goes between the revision's halves and the
368
+ // runs into it in a revision of the same kind and attributes with a new id, as a content control
369
+ // over inserted text does (CR-002 section 43; the docx4j-python session's form). Until then the
370
+ // link went into the revision, which marshalled with an xsi:type. Word's own form for a link set
371
+ // on inserted text is a HYPERLINK field in the one w:ins (check 30, link-on-inserted-text), which
372
+ // this setter's getter would not read back.
373
+ revision.items.splice(at, items.length);
374
+ const inner = revisionAround(revision, items, paragraph.nextAnnotationId());
375
+ holder.value.content = [inner];
376
+ linkParents(inner, holder.value);
377
+ if (!placeInRevision(paragraph, revision, at, holder, () => paragraph.nextAnnotationId())) {
378
+ throw new Docx4JException('This range is in a revision that cannot be split around a hyperlink');
379
+ }
380
+ runsParent = inner.value;
367
381
  }
368
- const items = [];
369
- for (const segment of segments) {
370
- const element = owner[segment.runIndex];
371
- if (!items.includes(element))
372
- items.push(element);
382
+ else {
383
+ holder.value.content = items;
384
+ owner.splice(at, items.length, holder);
385
+ linkParents(holder, (_a = segments[0].run.PARENT) !== null && _a !== void 0 ? _a : paragraph.p);
373
386
  }
374
- const at = owner.indexOf(items[0]);
375
- holder.value.content = items;
376
- owner.splice(at, items.length, holder);
377
- linkParents(holder, (_a = segments[0].run.PARENT) !== null && _a !== void 0 ? _a : paragraph.p);
378
387
  this.restyle(holder.value, true);
379
- mergeTextRuns(holder.value.content, holder.value);
388
+ mergeTextRuns(items, runsParent);
380
389
  (_c = (_b = paragraph.parentBody.package_) === null || _b === void 0 ? void 0 : _b.requireStyles) === null || _c === void 0 ? void 0 : _c.call(_b, [HYPERLINK_STYLE]);
381
390
  }
391
+ /**
392
+ * Refuses, before anything changes, a link the setter could not make over [start, end): one with an
393
+ * address on a range with no part, over no run, over runs in more than one holder, or around a
394
+ * tracked deletion - each judged as the range would stand once the links it touches were unwrapped,
395
+ * their runs then in the list holding them.
396
+ */
397
+ refuseUnlinkable(start, end, address) {
398
+ const paragraph = this.paragraph;
399
+ if (address !== '' && !paragraph.parentBody.part) {
400
+ throw new Docx4JException('This range has no part, so a hyperlink cannot be related to it');
401
+ }
402
+ const touched = hyperlinkHolders(paragraph).filter((h) => this.touches(h));
403
+ const unwrapsInto = new Map();
404
+ for (const holder of touched) {
405
+ const inner = runItemsOf(holder.value);
406
+ if (inner)
407
+ unwrapsInto.set(inner, containerOf(holder, paragraph));
408
+ }
409
+ const ownerOnceUnwrapped = (owner) => {
410
+ let o = owner;
411
+ for (let guard = 0; guard < 100 && unwrapsInto.has(o); guard++)
412
+ o = unwrapsInto.get(o);
413
+ return o;
414
+ };
415
+ const covered = coveredSegments(paragraph, start, end);
416
+ if (covered.length === 0)
417
+ throw new Docx4JException('This range covers no run');
418
+ const owner = ownerOnceUnwrapped(covered[0].runOwner);
419
+ if (covered.some((seg) => ownerOnceUnwrapped(seg.runOwner) !== owner)) {
420
+ throw new Docx4JException('This range spans more than one run holder (a hyperlink or a tracked change); wrap a narrower span');
421
+ }
422
+ const unwrapped = new Set(touched);
423
+ const flatten = (items) => items.flatMap((item) => {
424
+ var _a;
425
+ return (unwrapped.has(item)
426
+ ? flatten(((_a = runItemsOf(item.value)) !== null && _a !== void 0 ? _a : [])) : [item]);
427
+ });
428
+ refuseTrackedDeletion(flatten(owner), covered);
429
+ const revision = revisionHolding(covered[0], owner);
430
+ if (revision && itemsKeyOf(revision) === undefined) {
431
+ throw new Docx4JException('This range is in a revision that cannot be split around a hyperlink');
432
+ }
433
+ }
382
434
  /** The first `w:hyperlink` this range touches, if any. */
383
435
  hyperlinkHolder() {
384
436
  return hyperlinkHolders(this.paragraph).find((holder) => this.touches(holder));
@@ -541,6 +593,84 @@ function hyperlinkHolders(paragraph) {
541
593
  return out;
542
594
  }
543
595
  /** The run-level array holding an element, after `insertItemsAt` put it somewhere in the paragraph. */
596
+ /**
597
+ * The segments a range covers, read before its runs are split at its edges: those inside it, and those
598
+ * a split would cut, whose inner part then is. What `segments()` gives inside the range once it is
599
+ * split, so that a wrapper's refusals can come before anything changes.
600
+ */
601
+ function coveredSegments(paragraph, start, end) {
602
+ return paragraph.segments().filter((s) => (s.start >= start && s.end <= end) || (s.start < end && s.end > start));
603
+ }
604
+ /**
605
+ * The items a range's runs are wrapped in place of: everything from the first run it covers to the
606
+ * last, in the one list holding them, so that what lies between - a bookmark, a comment's range, a
607
+ * proofing mark - goes into the wrapper with them. Until CR-002 section 43 as many items were taken as
608
+ * there were runs, from the first, which dropped what lay between and left a run in two places.
609
+ */
610
+ function wrappedItems(owner, segments) {
611
+ const indexes = segments.map((s) => owner.indexOf(s.runOwner[s.runIndex])).filter((i) => i >= 0);
612
+ const at = Math.min(...indexes);
613
+ return { at, items: owner.slice(at, Math.max(...indexes) + 1) };
614
+ }
615
+ /**
616
+ * Refuses a range with a tracked deletion (or text moved away) between its runs: having no text in
617
+ * the range's, it would be wrapped unseen, and a range spanning a tracked change is refused (CR-002
618
+ * section 43).
619
+ */
620
+ function refuseTrackedDeletion(items, segments) {
621
+ const indexes = segments.map((s) => items.indexOf(s.runOwner[s.runIndex])).filter((i) => i >= 0);
622
+ if (indexes.length === 0)
623
+ return;
624
+ for (const item of items.slice(Math.min(...indexes), Math.max(...indexes) + 1)) {
625
+ const kind = revisionKindOf(item);
626
+ if (kind === 'del' || kind === 'moveFrom') {
627
+ throw new Docx4JException('This range spans a tracked deletion; wrap the text on either side of it');
628
+ }
629
+ }
630
+ }
631
+ /** The `w:ins` or `w:moveTo` a segment's run is directly in, when `owner` is its own item list. */
632
+ function revisionHolding(segment, owner) {
633
+ return segment.revision !== undefined && segment.revision.items === owner ? segment.revision : undefined;
634
+ }
635
+ /** The key under which a revision's value holds its items, or undefined where none does. */
636
+ function itemsKeyOf(revision) {
637
+ const value = revision.value;
638
+ return Object.keys(value).find((key) => value[key] === revision.items);
639
+ }
640
+ /** A revision of the same kind and attributes as `revision`, with a new id, holding `items`. */
641
+ function revisionAround(revision, items, id) {
642
+ const value = revision.value;
643
+ const itemsKey = itemsKeyOf(revision);
644
+ const inner = {};
645
+ for (const [key, v] of Object.entries(value))
646
+ if (key !== itemsKey && key !== 'PARENT')
647
+ inner[key] = v;
648
+ inner.id = id;
649
+ inner[itemsKey] = items;
650
+ linkParents(items, inner);
651
+ return { name: revision.element.name, value: inner };
652
+ }
653
+ /**
654
+ * Puts `item` where items from `index` on were taken out of `revision`: in its place where it holds
655
+ * nothing now, before it where they were its first, else between its halves. False where the revision
656
+ * cannot be split.
657
+ */
658
+ function placeInRevision(paragraph, revision, index, item, nextId) {
659
+ var _a;
660
+ const owner = revision.owner;
661
+ const parent = (_a = revision.value.PARENT) !== null && _a !== void 0 ? _a : paragraph.p;
662
+ if (revision.items.length === 0) {
663
+ owner.splice(owner.indexOf(revision.element), 1, item); // the whole revision wrapped
664
+ linkParents(item, parent);
665
+ return true;
666
+ }
667
+ if (index === 0) {
668
+ owner.splice(owner.indexOf(revision.element), 0, item); // its start wrapped
669
+ linkParents(item, parent);
670
+ return true;
671
+ }
672
+ return paragraph.splitHolderAround(revision, index, item, nextId);
673
+ }
544
674
  function containerOf(element, paragraph) {
545
675
  var _a, _b;
546
676
  var _c;
@@ -1,7 +1,11 @@
1
1
  import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
+ import type * as math from '@docx4j/generated-objects-ts/modules/org_docx4j_math';
2
3
  import { type Element, type Located, type RevisionKind } from './tree.mjs';
3
4
  import { Range } from './Range.mjs';
4
5
  import type { Paragraph } from './Paragraph.mjs';
6
+ import type { Body } from './Body.mjs';
7
+ import type { Part } from '../../parts/Part.mjs';
8
+ import { type RelationshipRemovals } from './relationships.mjs';
5
9
  /** Office JS `Word.ChangeTrackingState` (`Unknown` is not produced here). */
6
10
  export type TrackedChangeType = 'Added' | 'Deleted' | 'Formatted' | 'None' | 'Unknown';
7
11
  /** Which piece of revision markup a `TrackedChange` is over (an extension: Office JS has no such member). */
@@ -112,6 +116,51 @@ export type TrackedChangeTarget =
112
116
  outer: [Element, Element];
113
117
  inner: [Element, Element];
114
118
  value: wml.CTTrackChange;
119
+ }
120
+ /**
121
+ * A field Word inserted under tracking, in its own form: a `w:ins` around the field's begin and
122
+ * instruction runs and another around its end, the separator and the result between them plain
123
+ * (check 30, `field-inserted`). Office JS lists it as one `Added` change whose text is the result's,
124
+ * and Word's Reject All took the whole field away, the result with it (CR-002 section 44). `span` is
125
+ * the items of `owner` from the first `w:ins` to the last.
126
+ */
127
+ | {
128
+ kind: 'field';
129
+ span: Element[];
130
+ owner: Element[];
131
+ value: wml.CTTrackChange;
132
+ }
133
+ /**
134
+ * A `w:ins` or `w:del` inside an `m:r`, which is where Word writes an equation's text inserted or
135
+ * deleted (`m:r` > `w:ins` > `m:rPr`, `w:rPr`, `m:t`; docx4j's `tracked-changes-equations.docx`).
136
+ * `item` is the `w:ins` or `w:del` in the run's content, `run` the `m:r` and `runOwner` its list.
137
+ * Kept, its content takes its place in the run; not kept, it goes, and a run left with no text with it
138
+ * (CR-002 section 51). `equation` is the `m:oMath` or `m:oMathPara` in the paragraph that holds it.
139
+ */
140
+ | {
141
+ kind: 'mathRun';
142
+ revision: 'ins' | 'del';
143
+ item: Element;
144
+ run: Element<math.CTR>;
145
+ runOwner: Element[];
146
+ value: wml.CTMathRunTrackChange;
147
+ equation?: Element;
148
+ }
149
+ /**
150
+ * A `w:ins` or `w:del` in an equation structure's `m:ctrlPr`, Word's mark of an `m:sSup`, an `m:acc`,
151
+ * ... inserted or deleted as a whole. `structure` is that element and `owner` its list, two up from
152
+ * the `m:ctrlPr`; undefined for an argument's own `m:ctrlPr` (an `m:e`'s), which marks no structure.
153
+ * Kept, the marker goes and the run properties it recorded become the control's; not kept, the
154
+ * structure goes whole, as Word's Accept All and Reject All did (CR-002 section 51).
155
+ */
156
+ | {
157
+ kind: 'mathStructure';
158
+ revision: 'ins' | 'del';
159
+ ctrlPr: math.CTCtrlPr;
160
+ structure?: Element;
161
+ owner?: Element[];
162
+ value: wml.CTRPrChange;
163
+ equation?: Element;
115
164
  };
116
165
  /**
117
166
  * A subset of Office JS `Word.TrackedChange`: `type`, `author`, `date`, `text`, `accept()`,
@@ -122,9 +171,13 @@ export declare class TrackedChange {
122
171
  readonly target: TrackedChangeTarget;
123
172
  /** The paragraph the change is in; undefined for a row revision. */
124
173
  readonly paragraph: Paragraph | undefined;
174
+ /** The body a change without a paragraph (a row, a cell) is in, for the notes it may take away. */
175
+ readonly body?: Body | undefined;
125
176
  constructor(target: TrackedChangeTarget,
126
177
  /** The paragraph the change is in; undefined for a row revision. */
127
- paragraph: Paragraph | undefined);
178
+ paragraph: Paragraph | undefined,
179
+ /** The body a change without a paragraph (a row, a cell) is in, for the notes it may take away. */
180
+ body?: Body | undefined);
128
181
  /** Office JS `Word.ChangeTrackingState`. */
129
182
  get type(): TrackedChangeType;
130
183
  get author(): string;
@@ -147,13 +200,76 @@ export declare class TrackedChange {
147
200
  * Either half of a move keeps the whole move, as Word's Review tab does (CR-002 section 29).
148
201
  */
149
202
  accept(): void;
203
+ private acceptHere;
150
204
  /**
151
205
  * Puts back what was there: a `w:ins` is removed, a `w:del` restored, a `w:rPrChange` re-applied.
152
206
  * Either half of a move rejects the whole move, as Word's Review tab does (CR-002 section 29).
153
207
  */
154
208
  reject(): void;
209
+ private rejectHere;
210
+ /** The body the change is in. */
211
+ private get body_();
212
+ /** The package the change is in, which keeps the notes whose references it took away. */
213
+ private get package_();
155
214
  private requireParagraph;
156
215
  }
216
+ /** The ids of the footnotes and endnotes whose references removed content took with it. */
217
+ export interface NoteRemovals {
218
+ footnote: Set<number>;
219
+ endnote: Set<number>;
220
+ }
221
+ /**
222
+ * Runs `resolve`, then takes away what went with the content it removed:
223
+ * - **Notes**: Word took a footnote from the notes part when its reference went with the content
224
+ * around it, accepting a deletion and rejecting an insertion alike (the editor's `review-for-word`
225
+ * saves, Word 15 and Word 2010); the entries go when the package is saved
226
+ * (`WordprocessingMLPackage.pendingNoteRemovals`).
227
+ * - **Bookmarks**: Word removed a bookmark whole when its end went with a deletion it accepted, and
228
+ * when the insertion it was put around went with a reject (docx4j CR-043's probe, C11 and C11b, Word
229
+ * 16.0.20326). docx4j's rule from the two (its CR-043 section 3.2) is followed here: a bookmark with a
230
+ * marker in removed content goes whole, its other marker too, even with content left beside it; so
231
+ * does one with content between its markers that is left with none, a deletion it was around accepted
232
+ * among them (U15); and one that was empty before, as `_GoBack` is, stays. Those three are not measured.
233
+ * - **Relationships and parts**: Word's saves kept no part the resolved content alone referenced. A
234
+ * picture deleted and accepted, or inserted and rejected, took its image part (docx4j CR-043's probe,
235
+ * C05 and C04), and a section break deleted and accepted took the headers and footers of the section
236
+ * it ended (C06: six of each became three). docx4j's rule (its CR-043 section 3.2, D7) is followed: a
237
+ * relationship of the change's part that the removed content referenced, through any `r:` attribute,
238
+ * goes when nothing left in the part references it; its part goes when no relationship in the package
239
+ * targets it any more, and what that part alone related to goes with it. The kinds beyond pictures,
240
+ * headers and footers (charts, OLE objects, `v:imagedata`) are docx4j's reading, not measured.
241
+ * A change resolved inside another's (a row's cells, a group's pieces, `acceptAll`'s changes) shares the
242
+ * outer resolution, so the bookmarks are read twice for the lot, and not at all when nothing is removed.
243
+ */
244
+ export declare function resolving(pkg: unknown, body: Body | undefined, resolve: () => void): void;
245
+ /** What `flushPendingNoteRemovals` needs of the main document part. */
246
+ interface NotesHost {
247
+ getContents(): Promise<unknown>;
248
+ footnotesPart?: Part & {
249
+ getContents(): Promise<wml.CTFootnotes>;
250
+ };
251
+ endnotesPart?: Part & {
252
+ getContents(): Promise<wml.CTEndnotes>;
253
+ };
254
+ }
255
+ /** Where a removed note's own content goes on to: the package's queues (`WordprocessingMLPackage`). */
256
+ interface NoteRemovalHost {
257
+ pendingCommentRemovals?: Set<number>;
258
+ pendingRelationshipRemovals?: RelationshipRemovals;
259
+ }
260
+ /**
261
+ * The notes `pending` holds taken out of the notes parts, each unless a reference to it still stands in
262
+ * the main document (an accepted move keeps the reference its source had, say); then `pending` is
263
+ * empty. A note's entry goes, Word's separators never: their ids are never referenced. The notes parts
264
+ * are read only when something is pending, so a save with none leaves them untouched. Word's own save
265
+ * also renumbers the notes left (the editor's `review-for-word` saves); their ids stay here.
266
+ *
267
+ * What a removed note held goes on with it, as docx4j's `Removals.flush` takes it (CR-002 section 50,
268
+ * docx4j's reading, not measured): the relationships its content referenced (a picture's image) are
269
+ * queued on `host` for the notes part, to go when nothing else uses them, and a comment whose reference
270
+ * was in it is queued for removal, its anchor being gone.
271
+ */
272
+ export declare function flushPendingNoteRemovals(main: NotesHost, pending: NoteRemovals, host?: NoteRemovalHost): Promise<void>;
157
273
  /**
158
274
  * The insertion of a content control or custom XML element, when `items[i]` is a
159
275
  * `w:customXmlInsRangeStart` and what follows is Word's form for one (check 30): the element, whose
@@ -161,6 +277,15 @@ export declare class TrackedChange {
161
277
  * one's end right after the element. Anything else is not one (a marker on its own is left alone).
162
278
  */
163
279
  export declare function holderInsertionAt(items: Element[], i: number, paragraph: Paragraph | undefined): TrackedChange | undefined;
280
+ /**
281
+ * Word's form for a field inserted under tracking, when `items[i]` starts it: a `w:ins` that opens a
282
+ * field (a `w:fldChar` begin it does not end), then plain runs - the separator and the result - and
283
+ * markers, then a `w:ins` by the same author in which that field ends (check 30, `field-inserted`:
284
+ * begin and instruction in one, the end run in the other). Another revision, a run whose formatting
285
+ * changed, a run holder, or a field that ends in a plain run is not this form, and is listed as before.
286
+ * A field wholly inside one `w:ins` needs nothing of this: that `w:ins` already takes it whole.
287
+ */
288
+ export declare function insertedFieldAt(items: Element[], i: number, paragraph: Paragraph | undefined): TrackedChange | undefined;
164
289
  /**
165
290
  * docx4j AcceptTrackedChanges: the paragraph takes the next one's content and properties, so the
166
291
  * mark that survives is the next one's. Called when a deleted mark is accepted and when an
@@ -200,11 +325,11 @@ export declare function groupTouching(tokens: TrackedChangeToken[]): TrackedChan
200
325
  * The `w:trPr/w:ins` and `w:trPr/w:del` revisions of a table row. `inner` is the revision markup
201
326
  * of the row's own cells, which the row's change owns rather than reporting separately.
202
327
  */
203
- export declare function trackedChangesOfRow(tr: Element<wml.Tr>, owner: Element[], inner?: TrackedChange[], table?: Located<wml.Tbl>): TrackedChange[];
328
+ export declare function trackedChangesOfRow(tr: Element<wml.Tr>, owner: Element[], inner?: TrackedChange[], table?: Located<wml.Tbl>, body?: Body): TrackedChange[];
204
329
  /** The change a table's property records make, one per table, or undefined when they make none. */
205
330
  export declare function tablePropertiesChangeOf(tbl: Element<wml.Tbl>): TrackedChange | undefined;
206
331
  /** A cell's own revision, `w:cellIns` or `w:cellDel`, or undefined. */
207
- export declare function cellChangeOf(tc: Element<wml.Tc>, owner: Element[], tr: wml.Tr): TrackedChange | undefined;
332
+ export declare function cellChangeOf(tc: Element<wml.Tc>, owner: Element[], tr: wml.Tr, body?: Body): TrackedChange | undefined;
208
333
  /** The change to the body's last section (`w:body/w:sectPr/w:sectPrChange`), its text the section's paragraphs'. */
209
334
  export declare function bodySectionChangeOf(container: {
210
335
  content?: Element[];
@@ -219,3 +344,4 @@ export declare function bodySectionChangeOf(container: {
219
344
  * the form `holderInsertionAt` lists marks nothing that can be found, and goes either way.
220
345
  */
221
346
  export declare function resolveUnlisted(items: Element[], what: 'accept' | 'reject'): void;
347
+ export {};