@docx4j/core-ts 0.4.0 → 0.6.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 (166) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +34 -3
  3. package/dist/html/convert.mjs +40 -15
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +153 -19
  24. package/dist/model/content/Body.mjs +570 -131
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +202 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  31. package/dist/model/content/InlinePicture.d.mts +82 -7
  32. package/dist/model/content/InlinePicture.mjs +221 -28
  33. package/dist/model/content/List.mjs +13 -1
  34. package/dist/model/content/Paragraph.d.mts +213 -14
  35. package/dist/model/content/Paragraph.mjs +936 -100
  36. package/dist/model/content/Range.d.mts +242 -11
  37. package/dist/model/content/Range.mjs +975 -119
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +301 -16
  43. package/dist/model/content/TrackedChange.mjs +1284 -158
  44. package/dist/model/content/binder.d.mts +2 -1
  45. package/dist/model/content/binder.mjs +48 -1
  46. package/dist/model/content/comments.d.mts +20 -6
  47. package/dist/model/content/comments.mjs +67 -57
  48. package/dist/model/content/generalException.d.mts +18 -0
  49. package/dist/model/content/generalException.mjs +24 -0
  50. package/dist/model/content/index.d.mts +8 -3
  51. package/dist/model/content/index.mjs +7 -2
  52. package/dist/model/content/mathText.d.mts +21 -0
  53. package/dist/model/content/mathText.mjs +267 -0
  54. package/dist/model/content/officeText.d.mts +27 -0
  55. package/dist/model/content/officeText.mjs +74 -0
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/placeholders.d.mts +110 -0
  59. package/dist/model/content/placeholders.mjs +253 -0
  60. package/dist/model/content/relationships.d.mts +30 -0
  61. package/dist/model/content/relationships.mjs +133 -0
  62. package/dist/model/content/search.d.mts +72 -0
  63. package/dist/model/content/search.mjs +204 -2
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +44 -0
  67. package/dist/model/content/story.mjs +92 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +100 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +28 -0
  79. package/dist/model/content/tracking.mjs +149 -10
  80. package/dist/model/content/tree.d.mts +212 -13
  81. package/dist/model/content/tree.mjs +356 -35
  82. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  83. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  84. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  85. package/dist/model/customxml/XmlMapping.mjs +11 -0
  86. package/dist/model/customxml/bindings.d.mts +21 -2
  87. package/dist/model/customxml/bindings.mjs +76 -32
  88. package/dist/model/customxml/kinds.d.mts +8 -3
  89. package/dist/model/customxml/kinds.mjs +26 -5
  90. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  91. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  92. package/dist/model/customxml/pictures.d.mts +9 -5
  93. package/dist/model/customxml/pictures.mjs +31 -13
  94. package/dist/model/customxml/xhtml.mjs +8 -2
  95. package/dist/model/fonts/families.generated.mjs +2 -2
  96. package/dist/model/fonts/registry.mjs +7 -1
  97. package/dist/model/fonts/substitutions.generated.mjs +2 -2
  98. package/dist/model/fonts/themes.generated.mjs +1 -1
  99. package/dist/model/index.d.mts +2 -0
  100. package/dist/model/index.mjs +4 -0
  101. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  102. package/dist/model/listnumbering/Emulator.mjs +63 -9
  103. package/dist/model/listnumbering/definitions.mjs +18 -0
  104. package/dist/model/properties/PropertyResolver.d.mts +87 -7
  105. package/dist/model/properties/PropertyResolver.mjs +197 -17
  106. package/dist/model/properties/catalogue.d.mts +32 -4
  107. package/dist/model/properties/catalogue.mjs +115 -36
  108. package/dist/model/properties/log.d.mts +1 -1
  109. package/dist/model/properties/log.mjs +5 -5
  110. package/dist/model/properties/table.d.mts +16 -2
  111. package/dist/model/properties/table.mjs +17 -5
  112. package/dist/model/text/Extractor.d.mts +289 -0
  113. package/dist/model/text/Extractor.mjs +2162 -0
  114. package/dist/model/text/LinkTargets.d.mts +45 -0
  115. package/dist/model/text/LinkTargets.mjs +170 -0
  116. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  117. package/dist/model/text/MarkdownExporter.mjs +255 -0
  118. package/dist/model/text/PlainText.d.mts +29 -0
  119. package/dist/model/text/PlainText.mjs +539 -0
  120. package/dist/model/text/TextContext.d.mts +161 -0
  121. package/dist/model/text/TextContext.mjs +425 -0
  122. package/dist/model/text/TextOptions.d.mts +150 -0
  123. package/dist/model/text/TextOptions.mjs +278 -0
  124. package/dist/model/text/TextResult.d.mts +504 -0
  125. package/dist/model/text/TextResult.mjs +526 -0
  126. package/dist/model/text/Texts.d.mts +99 -0
  127. package/dist/model/text/Texts.mjs +187 -0
  128. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  129. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  130. package/dist/model/text/commonmark.d.mts +121 -0
  131. package/dist/model/text/commonmark.mjs +655 -0
  132. package/dist/model/text/index.d.mts +7 -0
  133. package/dist/model/text/index.mjs +8 -0
  134. package/dist/model/text/normalize.d.mts +8 -0
  135. package/dist/model/text/normalize.mjs +13 -0
  136. package/dist/model/text/outline.d.mts +4 -0
  137. package/dist/model/text/outline.mjs +71 -0
  138. package/dist/office-js/fragment.d.mts +7 -5
  139. package/dist/office-js/fragment.mjs +63 -14
  140. package/dist/office-js/proxy.d.mts +8 -0
  141. package/dist/office-js/proxy.mjs +29 -0
  142. package/dist/office-js/run.mjs +2 -0
  143. package/dist/office-js/supported.generated.mjs +16 -0
  144. package/dist/office-js/toApiScript.mjs +21 -5
  145. package/dist/opc/Load.d.mts +9 -2
  146. package/dist/opc/Save.d.mts +17 -0
  147. package/dist/opc/Save.mjs +12 -2
  148. package/dist/opc/index.d.mts +3 -3
  149. package/dist/opc/index.mjs +2 -2
  150. package/dist/opc/mce/preprocessor.d.mts +88 -7
  151. package/dist/opc/mce/preprocessor.mjs +144 -37
  152. package/dist/opc/mce/understood.d.mts +34 -0
  153. package/dist/opc/mce/understood.mjs +55 -3
  154. package/dist/packages/OpcPackage.d.mts +20 -0
  155. package/dist/packages/OpcPackage.mjs +16 -3
  156. package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
  157. package/dist/packages/WordprocessingMLPackage.mjs +242 -11
  158. package/dist/parts/DefaultXmlPart.mjs +2 -2
  159. package/dist/parts/Part.d.mts +10 -2
  160. package/dist/parts/XmlPart.d.mts +35 -32
  161. package/dist/parts/XmlPart.mjs +107 -107
  162. package/dist/parts/wml/comments.mjs +30 -0
  163. package/dist/parts/wml/index.mjs +12 -0
  164. package/dist/xml/dom.d.mts +7 -0
  165. package/dist/xml/dom.mjs +18 -0
  166. package/package.json +2 -2
@@ -1,18 +1,27 @@
1
1
  import { Docx4JException, PropertyResolverNotCreatedException } from '../../opc/exceptions.mjs';
2
- import { typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES, textOf, textOfView, rowsOf, cellsOf } from './tree.mjs';
3
- import { r as textRun, br as breakItem, tbl as tableOf } from '@docx4j/generated-objects-ts/builders/wml';
2
+ import { hiddenRunsOf, typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES, rowsOf, cellsOf, selectedBranchOf, branchesOf, branchListsOf, ALTERNATE_CONTENT, BRANCH_TYPES, inUnreadBranch, assertOutsideBranch, writingEveryBranch } from './tree.mjs';
3
+ import { br as breakItem, tbl as tableOf } from '@docx4j/generated-objects-ts/builders/wml';
4
+ import { oneParagraphRun } from './officeText.mjs';
4
5
  import { runOf, paragraphOf, runsOf } from './tree.mjs';
6
+ import { textBoxesOf } from './textBoxes.mjs';
7
+ import { outlineReader } from './outline.mjs';
5
8
  import { Paragraph } from './Paragraph.mjs';
9
+ import { Range } from './Range.mjs';
6
10
  import { Table } from './Table.mjs';
7
- import { ContentControl, collectControls } from './ContentControl.mjs';
11
+ import { ContentControl, collectControls, assertControlHolds, controlsOfTypes } from './ContentControl.mjs';
8
12
  import { sdt as sdtOf, nextSdtId } from '@docx4j/generated-objects-ts/builders/wml';
9
13
  import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
10
14
  import { InlinePicture, addImage, writableWidthEmu } from './InlinePicture.mjs';
11
15
  import { contentOf } from './ooxml.mjs';
12
- import { expandReplacement } from './search.mjs';
16
+ import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
17
+ import { wrappingCellOf, paragraphEndOf, endsWithParagraph, endWithParagraph, endCellsWithParagraphs } from './tableRules.mjs';
18
+ import { keepCellMarkLast } from './spanEdits.mjs';
19
+ import { expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
13
20
  import { commentApi } from './comments.mjs';
21
+ import { storyLayout } from './story.mjs';
14
22
  import { trackerOf, trackInsertedBlock, markHolderInserted } from './tracking.mjs';
15
- import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderInsertionAt } from './TrackedChange.mjs';
23
+ import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderChangeAt, resolving, resolvePieces, alternateTokens, listedListing, pairTwins } from './TrackedChange.mjs';
24
+ import { pieceMatcher } from './trackedChangeFilter.mjs';
16
25
  import { listsOf, listLabelsOf } from './List.mjs';
17
26
  /**
18
27
  * A subset of Office JS `Word.Body` over any container with block-level content: the main
@@ -46,14 +55,31 @@ export class Body {
46
55
  throw new PropertyResolverNotCreatedException();
47
56
  return resolver;
48
57
  }
58
+ /** Which of a paragraph's runs are hidden: a run's own `w:vanish`, else its styles' (`hiddenRunsOf`; CR-012 phase 4). */
59
+ hiddenRuns(p) {
60
+ const pkg = this.package_;
61
+ return hiddenRunsOf(pkg === null || pkg === void 0 ? void 0 : pkg.propertyResolverOrUndefined, p);
62
+ }
49
63
  /** The live content array (docx4j getContent()); block-level elements only, sectPr excluded. */
50
64
  get content() {
51
65
  var _a;
52
66
  var _b;
53
67
  return ((_a = (_b = this.container).content) !== null && _a !== void 0 ? _a : (_b.content = []));
54
68
  }
55
- /** Every paragraph in document order, descending into tables and content controls. */
69
+ /**
70
+ * Every paragraph in document order, descending into tables and content controls, and into the
71
+ * selected branch of a kept `mc:AlternateContent` at block, row and cell level, as Word reads it
72
+ * (CR-013 phase 2). A paragraph in a branch is read-only (D4).
73
+ */
56
74
  get paragraphs() {
75
+ return this.paragraphsIn('read');
76
+ }
77
+ /**
78
+ * The paragraphs a walk in this mode takes: a reader's, `paragraphs`; a bulk edit's (`'every'`), every
79
+ * branch's too, the selected branch's where a reader sees them and each other branch's after them,
80
+ * where the element stands (CR-013 phase 3).
81
+ */
82
+ paragraphsIn(mode) {
57
83
  const out = [];
58
84
  const visit = (items) => {
59
85
  var _a;
@@ -66,10 +92,14 @@ export class Body {
66
92
  if (typeof v !== 'object' || v === null)
67
93
  continue;
68
94
  if (tn === 'org_docx4j_wml.Tbl') {
69
- for (const row of rowsOf(v))
70
- for (const cell of cellsOf(row.element.value))
95
+ for (const row of rowsOf(v, mode))
96
+ for (const cell of cellsOf(row.element.value, mode))
71
97
  visit((_a = childrenOf(cell.element.value)) !== null && _a !== void 0 ? _a : []);
72
98
  }
99
+ else if (tn === ALTERNATE_CONTENT) {
100
+ for (const branch of branchListsOf(v, mode))
101
+ visit(branch);
102
+ }
73
103
  else {
74
104
  const children = childrenOf(v);
75
105
  if (children)
@@ -153,14 +183,45 @@ export class Body {
153
183
  }
154
184
  return out;
155
185
  }
156
- /** The tables directly in this body, as Office JS reports them (nested tables are a cell's). */
186
+ /**
187
+ * The tables directly in this body, as Office JS reports them (nested tables are a cell's), a table in
188
+ * the selected branch of a kept block-level `mc:AlternateContent` among them (CR-013 phase 2).
189
+ */
157
190
  get tables() {
158
- return this.content.filter((el) => typeNameOf(el) === 'org_docx4j_wml.Tbl').map((element) => new Table(element, this.content, this));
191
+ const out = [];
192
+ const visit = (items) => {
193
+ var _a;
194
+ for (const element of items) {
195
+ const tn = typeNameOf(element);
196
+ if (tn === 'org_docx4j_wml.Tbl')
197
+ out.push(new Table(element, items, this));
198
+ else if (tn === ALTERNATE_CONTENT)
199
+ visit((_a = selectedBranchOf(element.value)) !== null && _a !== void 0 ? _a : []);
200
+ }
201
+ };
202
+ visit(this.content);
203
+ return out;
159
204
  }
160
205
  /** Every content control in this body, in document order, nested ones included. */
161
206
  get contentControls() {
207
+ return this.contentControlsIn('read');
208
+ }
209
+ /**
210
+ * Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
211
+ * where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
212
+ * departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
213
+ */
214
+ getContentControls(options) {
215
+ return controlsOfTypes(this.contentControls, options);
216
+ }
217
+ /**
218
+ * The content controls a walk in this mode takes: a reader's, `contentControls`; the binding passes'
219
+ * (`'every'`), every branch's of a kept `mc:AlternateContent` too, each other branch's where the element
220
+ * stands (CR-013 phase 3). An extension.
221
+ */
222
+ contentControlsIn(mode) {
162
223
  const out = [];
163
- collectControls(this.content, this, out);
224
+ collectControls(this.content, this, out, mode);
164
225
  return out;
165
226
  }
166
227
  /** Every inline picture in this body, in document order (Office JS Body.inlinePictures). */
@@ -200,33 +261,72 @@ export class Body {
200
261
  const element = container === null || container === void 0 ? void 0 : container.find((e) => e.value === p);
201
262
  return element && container ? new Paragraph(element, container, this) : undefined;
202
263
  }
203
- /** The text, a paragraph per line. */
264
+ /**
265
+ * The text as Office JS's `body.text` gives it in Word's default view (CR-012 phase 4; checks 30, 31, 48
266
+ * and 51): each paragraph's text (`Paragraph.text`) followed by its mark, `\r`, the last paragraph's left
267
+ * out; in a table a row's cells a `\t` apart, each cell's paragraphs a mark apart (its last mark not
268
+ * written), and the row ended by `\r\n`. Text boxes are not the body's (check 42).
269
+ */
204
270
  get text() {
205
- return this.paragraphs.map((p) => p.text).join('\n');
271
+ return this.textIn('accepted');
206
272
  }
207
273
  /**
208
274
  * The text in one of the two views (extension; `text` is the accepted one).
209
275
  * `{ view: 'original' }` reads the document as it was before the tracked changes.
210
276
  */
211
277
  getText(options) {
212
- if ((options === null || options === void 0 ? void 0 : options.view) !== 'original')
213
- return this.text;
214
- return this.paragraphs.map((p) => textOfView(p.p, options)).join('\n');
278
+ var _a;
279
+ return this.textIn((_a = options === null || options === void 0 ? void 0 : options.view) !== null && _a !== void 0 ? _a : 'accepted');
280
+ }
281
+ /**
282
+ * The body's range (Office JS `Body.getRange`; CR-014 phase 1): 'Whole' from its first paragraph's start to
283
+ * its last one's end, with that paragraph's mark (check 51: a cell's `CELLP1\rCELLP2\rCELLP3\t`, a note's
284
+ * `… NOTEP2\r`), 'Content' the same without the mark, 'Start' and 'End' collapsed at its ends.
285
+ */
286
+ getRange(location = 'Whole') {
287
+ const paragraphs = this.paragraphs;
288
+ const first = paragraphs[0];
289
+ if (!first)
290
+ throw new Docx4JException('This body holds no paragraph');
291
+ const last = paragraphs[paragraphs.length - 1];
292
+ if (location === 'Start')
293
+ return first.getRange('Start');
294
+ if (location === 'End')
295
+ return last.getRange('End');
296
+ if (paragraphs.length === 1)
297
+ return first.getRange(location);
298
+ return new Range(first, 0, last.text.length, true, location === 'Whole', last);
299
+ }
300
+ textIn(view) {
301
+ // the original view's marks are the ones Reject All keeps: a deleted mark written, an inserted one not
302
+ const { text } = storyLayout(this.content, (el, items) => {
303
+ const p = new Paragraph(el, items, this);
304
+ return view === 'original' ? p.getText({ view }) : p.text;
305
+ }, view === 'original' ? (p) => { var _a, _b; return ((_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.ins) === undefined; } : undefined);
306
+ // the last paragraph's mark is not written
307
+ return text.endsWith('\r') ? text.slice(0, -1) : text;
215
308
  }
216
309
  /** The package's change tracker while `changeTrackingMode` is on; undefined when it is off (CR-002 phase F). */
217
310
  get changeTracker() {
218
311
  var _a;
219
312
  return (_a = trackerOf(this.package_)) === null || _a === void 0 ? void 0 : _a.forPart(this.part);
220
313
  }
314
+ /** Refuses a write to a body in a branch of a kept `mc:AlternateContent`: a cell or a control a reader found there (CR-013 D4). */
315
+ assertWritable() {
316
+ assertOutsideBranch(this.container, 'Body');
317
+ }
221
318
  insertParagraph(text, location) {
222
- const el = paragraphOf(text === '' ? [] : [textRun(text)]);
319
+ var _a;
320
+ const run = oneParagraphRun(text); // CR-002 section 60 E2: one paragraph, its line breaks w:br and w:cr
321
+ const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : []);
223
322
  return this.insertElement(el, location);
224
323
  }
225
324
  /** Text at the start of the first paragraph, the end of the last, or replacing everything with one paragraph. */
226
325
  insertText(text, location) {
326
+ this.assertWritable();
227
327
  const paragraphs = this.paragraphs;
228
328
  if (location === 'Replace' || paragraphs.length === 0) {
229
- this.clear();
329
+ this.clearContent(false); // its own paragraph ends a cell, so the cell's last one goes too
230
330
  const p = this.insertParagraph(text, 'End');
231
331
  return p.getRange('Content');
232
332
  }
@@ -251,7 +351,7 @@ export class Body {
251
351
  // cells the builder wrote, the table's width made automatic (CR-002 section 42.5 item 7).
252
352
  const tblPr = ((_a = (_b = element.value).tblPr) !== null && _a !== void 0 ? _a : (_b.tblPr = {}));
253
353
  tblPr.tblW = { w: 0, type: 'auto' };
254
- this.insertElement(element, location);
354
+ this.insertElement(element, location, undefined, 'InvalidArgument');
255
355
  return new Table(element, this.content, this);
256
356
  }
257
357
  /**
@@ -260,12 +360,16 @@ export class Body {
260
360
  * than the text area is scaled down, as docx4j's CxCy.scale does.
261
361
  */
262
362
  insertInlinePictureFromBase64(base64, location, options) {
363
+ this.assertWritable();
263
364
  if (!this.part)
264
365
  throw new Docx4JException('This body has no part, so an image cannot be related to it');
265
366
  const { run, drawing } = addImage(this.part, base64, this.container, writableWidthEmu(this.container), options);
266
367
  const paragraph = this.insertParagraph('', location);
267
- paragraph.p.content.push(run);
268
- linkParents(run, paragraph.p);
368
+ // under tracking the picture is an insertion, as its paragraph's mark is (insertBreak's form)
369
+ const tracker = paragraph.changeTracker;
370
+ const item = tracker ? tracker.ins([run]) : run;
371
+ paragraph.p.content.push(item);
372
+ linkParents(item, paragraph.p);
269
373
  return new InlinePicture(drawing, run, paragraph);
270
374
  }
271
375
  insertBreak(type, location) {
@@ -281,7 +385,8 @@ export class Body {
281
385
  * before or after a target paragraph, element or address. Links PARENT. Rejects anything the
282
386
  * body cannot hold, naming what was passed. Returns the view of the first inserted element.
283
387
  */
284
- insertElement(element, location, target) {
388
+ insertElement(element, location, target, refusal, replacing) {
389
+ this.assertWritable();
285
390
  const elements = Array.isArray(element) ? element : [element];
286
391
  if (elements.length === 0)
287
392
  throw new Docx4JException('Nothing to insert');
@@ -299,11 +404,18 @@ export class Body {
299
404
  index = container.length;
300
405
  else {
301
406
  const t = this.resolveTarget(target);
407
+ assertOutsideBranch(t.element.value, 'Body', 'the block to insert beside'); // beside it is in the branch too
302
408
  container = t.container;
303
409
  index = container.indexOf(t.element) + (location === 'After' ? 1 : 0);
304
410
  }
305
- container.splice(index, 0, ...elements);
411
+ // a bound block control holds one paragraph (CR-002 section 61): `replacing` is the block the write takes away after
412
+ assertControlHolds(this, this.ownerOf(container), replacing ? container.filter((e) => e !== replacing) : container, elements, refusal);
413
+ // a cell ends with a paragraph, as Word reads it: the cells of what goes in (D3), and the cell it goes in (CR-002 section 55)
414
+ endCellsWithParagraphs(elements);
306
415
  const owner = this.ownerOf(container);
416
+ const cell = wrappingCellOf(owner);
417
+ const end = cell ? paragraphEndOf(cell.content) : undefined;
418
+ container.splice(index, 0, ...elements);
307
419
  const tracker = this.changeTracker;
308
420
  for (const el of elements) {
309
421
  linkParents(el, owner);
@@ -315,12 +427,15 @@ export class Body {
315
427
  if (tracker)
316
428
  trackInsertedBlock(tracker, el, container);
317
429
  }
430
+ if (cell)
431
+ this.keepCellEnd(cell, end, container);
318
432
  const first = elements[0];
319
433
  return typeNameOf(first) === 'org_docx4j_wml.P' ? new Paragraph(first, container, this) : { element: first, container };
320
434
  }
321
435
  /** Inserts a WML fragment (one or more w:p / w:tbl / ... as inside document.xml); the standard prefixes are declared for it. */
322
436
  async insertXml(xml, location, target) {
323
- return this.insertContent(await contentOf(xml, { preprocess: this.preprocessor() }), location, target);
437
+ this.assertWritable();
438
+ return this.insertContent(await contentOf(xml, { preprocess: this.preprocessor() }), location, target, 'GeneralException');
324
439
  }
325
440
  /**
326
441
  * Word's `insertOoxml`: a flat OPC `pkg:package` string, whose body content is inserted with
@@ -330,12 +445,39 @@ export class Body {
330
445
  * what was inserted, as `insertXml` does, since a package may bring several blocks.
331
446
  */
332
447
  async insertOoxml(ooxml, location, target) {
448
+ this.assertWritable();
333
449
  const elements = await contentOf(ooxml, { preprocess: this.preprocessor(), target: this.part });
334
450
  if (location === 'Replace') {
335
- this.clear();
336
- return this.insertContent(elements, 'End');
451
+ assertControlHolds(this, this.container, [], elements, 'GeneralException'); // before the clear (CR-002 section 61)
452
+ // In a cell, what ends with a table goes in before the cell's last paragraph, which stays, emptied, as Word
453
+ // keeps it (check 52, case 94; CR-002 section 55.6); what ends with a paragraph replaces it, as before
454
+ const cell = endsWithParagraph(elements) ? undefined : wrappingCellOf(this.container);
455
+ this.clearContent(cell !== undefined);
456
+ const end = cell ? paragraphEndOf(cell.content) : undefined;
457
+ if (end && end.container === this.content)
458
+ return this.insertContent(elements, 'Before', { element: end.element, container: end.container }, 'GeneralException');
459
+ return this.insertContent(elements, 'End', undefined, 'GeneralException');
337
460
  }
338
- return this.insertContent(elements, location, target);
461
+ return this.insertContent(elements, location, target, 'GeneralException');
462
+ }
463
+ /**
464
+ * After an insertion into a cell: the cell still ends with a paragraph, as Word reads it (CR-002 section 55). Where
465
+ * a table went in after the paragraph the cell ended with, in the cell's own content, Word's form (check 52, cases 86
466
+ * and 87, `keepCellMarkLast`): that paragraph split at its end, the cell's mark staying last after the table, the
467
+ * first half's mark the inserted one under tracking. Anywhere else docx4j's repair, an empty paragraph after any wrapper.
468
+ */
469
+ keepCellEnd(cell, end, container) {
470
+ var _a;
471
+ const content = ((_a = cell.content) !== null && _a !== void 0 ? _a : (cell.content = []));
472
+ if (endsWithParagraph(content))
473
+ return;
474
+ if (end && end.container === content && container === content) {
475
+ keepCellMarkLast(new Paragraph(end.element, content, this), content);
476
+ return;
477
+ }
478
+ const appended = endWithParagraph(cell);
479
+ if (appended)
480
+ this.assignParaId(appended);
339
481
  }
340
482
  /** The package's DOM preprocessor (the MCE one), for fragments and incoming packages. */
341
483
  preprocessor() {
@@ -343,16 +485,22 @@ export class Body {
343
485
  const preprocess = (_a = this.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
344
486
  return preprocess ? (doc) => preprocess(doc) : undefined;
345
487
  }
346
- insertContent(elements, location, target) {
488
+ insertContent(elements, location, target, refusal) {
347
489
  if (elements.length === 0)
348
490
  return [];
349
- this.insertElement(elements, location, target);
491
+ this.insertElement(elements, location, target, refusal);
350
492
  return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
351
493
  ? new Paragraph(el, this.containerOf(el), this)
352
494
  : { element: el, container: this.containerOf(el) });
353
495
  }
354
496
  /** Matches over every paragraph, in document order. */
355
497
  search(text, options) {
498
+ // Word's paragraph mark, ^p, is searched across the body's whole range (CR-014 phase 3); a wildcard query across its
499
+ // content, which Word's wildcards read without the last paragraph's mark (check 59 case 5, a control's; K7)
500
+ if ((options === null || options === void 0 ? void 0 : options.matchWildcards) && this.paragraphs.length > 0)
501
+ return this.getRange('Content').search(text, options);
502
+ if (!(options === null || options === void 0 ? void 0 : options.matchWildcards) && !(options === null || options === void 0 ? void 0 : options.matchRegExp) && paragraphMarkPieces(text) && this.paragraphs.length > 0)
503
+ return this.getRange('Whole').search(text, options);
356
504
  return this.paragraphs.flatMap((p) => p.search(text, options));
357
505
  }
358
506
  /**
@@ -361,35 +509,101 @@ export class Body {
361
509
  * the package's `changeTrackingMode` is on.
362
510
  */
363
511
  replaceText(find, replace, options) {
364
- const matches = this.paragraphs.flatMap((p) => p.searchMatches(find, options));
365
- for (let i = matches.length - 1; i >= 0; i--) {
366
- const { range, match } = matches[i];
367
- range.insertText(expandReplacement(replace, match, options), 'Replace');
368
- }
369
- return matches.length;
512
+ refuseMarkReplacement('Body', find, options);
513
+ if (options === null || options === void 0 ? void 0 : options.matchWildcards)
514
+ refuseWildcardMarkReplacement('Body', this.search(find, options));
515
+ this.assertWritable();
516
+ // every branch of a kept mc:AlternateContent, each paragraph searched on its own; counted where a
517
+ // reader sees the match, as resolve-all counts the listed changes (CR-013 phase 3)
518
+ const matches = this.paragraphsIn('every').flatMap((p) => p.searchMatches(find, options).map((m) => ({ ...m, unread: inUnreadBranch(p.p) })));
519
+ writingEveryBranch(() => {
520
+ for (let i = matches.length - 1; i >= 0; i--) {
521
+ const { range, match } = matches[i];
522
+ range.insertText(expandReplacement(replace, match, options), 'Replace');
523
+ }
524
+ });
525
+ return matches.filter((m) => !m.unread).length;
370
526
  }
371
527
  /**
372
528
  * Removes everything (section properties stay). While the package tracks changes, every
373
- * paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead.
529
+ * paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead. Removed outright,
530
+ * the relationships the content held (its pictures' images) are settled on save (CR-002 section 47).
531
+ * In a table cell the paragraph the cell ends with stays, emptied, its properties kept, and tracked
532
+ * only its text is deleted, as Word keeps a cell's last paragraph (check 52, cases 92 and 93; CR-002 section 55.6).
374
533
  */
375
534
  clear() {
535
+ this.clearContent(true);
536
+ }
537
+ /**
538
+ * `clear()`; with `keepCellEnd` false a cell's last paragraph goes as well, its mark marked deleted under
539
+ * tracking, for a caller that puts its own paragraph in next (`insertText(…, 'Replace')`, `TableCell.value`).
540
+ * @internal
541
+ */
542
+ clearContent(keepCellEnd) {
543
+ var _a;
544
+ this.assertWritable();
376
545
  const tracker = this.changeTracker;
377
546
  if (!tracker) {
547
+ const cell = keepCellEnd ? wrappingCellOf(this.container) : undefined;
548
+ const end = cell ? paragraphEndOf(cell.content) : undefined;
549
+ const ids = new Set();
550
+ relationshipIdsIn(this.content, ids);
551
+ queueRelationshipRemovals((_a = this.package_) === null || _a === void 0 ? void 0 : _a.pendingRelationshipRemovals, this.part, ids);
378
552
  this.content.length = 0;
553
+ if (cell && !endsWithParagraph(cell.content)) {
554
+ if (end) {
555
+ end.element.value.content = [];
556
+ this.content.push(end.element);
557
+ linkParents(end.element, this.container);
558
+ }
559
+ else {
560
+ const appended = endWithParagraph(cell);
561
+ if (appended)
562
+ this.assignParaId(appended);
563
+ }
564
+ }
379
565
  return;
380
566
  }
381
- for (const row of this.rowElements())
382
- tracker.markRowDeleted(row.value);
383
- for (const p of this.paragraphs)
384
- p.delete();
567
+ // every branch of a kept mc:AlternateContent (CR-013 phase 3)
568
+ writingEveryBranch(() => {
569
+ for (const row of this.rowElements())
570
+ tracker.markRowDeleted(row.value);
571
+ for (const p of this.paragraphsIn('every'))
572
+ p.remove(keepCellEnd);
573
+ });
574
+ }
575
+ /**
576
+ * Marks everything in this body deleted in place, in every branch of a kept `mc:AlternateContent`: each
577
+ * paragraph's text and mark, each row of its tables (a tracked row or table deletion's cells; CR-013
578
+ * phase 3). Internal: what `Table.delete` and `deleteRows` write while changes are tracked.
579
+ */
580
+ markContentDeleted(tracker) {
581
+ // the paragraphs, then the rows last first: the order, and so the ids, a row's deletion has always written;
582
+ // a row already deleted is left as it is, as a paragraph already deleted is (a range's deletion, CR-014 phase 2)
583
+ writingEveryBranch(() => {
584
+ var _a;
585
+ for (const p of this.paragraphsIn('every'))
586
+ p.markDeletedInPlace(tracker);
587
+ for (const row of this.rowElements().reverse())
588
+ if (!((_a = row.value.trPr) === null || _a === void 0 ? void 0 : _a.del))
589
+ tracker.markRowDeleted(row.value);
590
+ });
385
591
  }
386
592
  // --- change tracking (CR-002 phase F) ---------------------------------------------------
387
593
  /**
388
594
  * Every tracked change in this body, in document order. Touching insertions or deletions by one
389
595
  * author - a deleted paragraph's text and mark, a deletion across a mark - are one change, as
390
596
  * Office JS lists them (CR-002 section 29, checks 22 and 23).
597
+ *
598
+ * In a kept `mc:AlternateContent` at block, paragraph, row or cell level the selected branch's changes
599
+ * are listed, as Word reads it (CR-013 phase 2); each other branch's are their twins, paired by kind and
600
+ * position, which resolving a listed change resolves too (phase 3, docx4j's twins).
391
601
  */
392
602
  getTrackedChanges() {
603
+ return this.trackedChanges(listedListing());
604
+ }
605
+ /** The listing in either mode (`Listing`): resolve-all's puts every other branch's changes after the story's. */
606
+ trackedChanges(listing) {
393
607
  const out = [];
394
608
  const visit = (items, into) => {
395
609
  var _a, _b;
@@ -397,12 +611,12 @@ export class Body {
397
611
  const el = items[i];
398
612
  const tn = typeNameOf(el);
399
613
  if (tn === 'org_docx4j_wml.P') {
400
- into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this)));
614
+ into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this), listing));
401
615
  continue;
402
616
  }
403
617
  // a block control or custom XML element that is itself an insertion (check 30): one piece,
404
618
  // grouping with the insertions around and inside it, as Word lists them
405
- const inserted = holderInsertionAt(items, i, undefined);
619
+ const inserted = holderChangeAt(items, i, undefined, this);
406
620
  if (inserted) {
407
621
  into.push(inserted);
408
622
  visit((_a = childrenOf(items[i + 1].value)) !== null && _a !== void 0 ? _a : [], into);
@@ -418,31 +632,16 @@ export class Body {
418
632
  // inserted paragraphs is one change; rows added at a table's end with a paragraph typed after
419
633
  // it, one; a paragraph whose mark is plain before rows added at the start, two). Its property
420
634
  // records are one change of their own where the table stands (CR-002 section 35), kept apart.
421
- const tableChange = tablePropertiesChangeOf(el);
635
+ const tableChange = tablePropertiesChangeOf(el, this);
422
636
  if (tableChange)
423
637
  into.push(BREAK, tableChange, BREAK);
424
- for (const row of rowsOf(v)) {
425
- // A row that is itself a revision reports as one change, as Office JS does: the
426
- // markup its cells carry (Word marks every run and mark of a deleted or inserted row)
427
- // belongs to that change, which accepts or rejects it along with the row.
428
- const tokens = [];
429
- for (const cell of cellsOf(row.element.value)) {
430
- const cellChange = cellChangeOf(cell.element, cell.container, row.element.value);
431
- if (cellChange)
432
- tokens.push(cellChange, BREAK);
433
- visit((_b = childrenOf(cell.element.value)) !== null && _b !== void 0 ? _b : [], tokens);
434
- tokens.push(BREAK);
435
- }
436
- const inner = groupTouching(tokens);
437
- const rowChanges = trackedChangesOfRow(row.element, row.container, inner, { element: el, container: items });
438
- if (rowChanges.length > 0)
439
- into.push(...rowChanges);
440
- else {
441
- into.push(BREAK);
442
- for (const change of inner)
443
- into.push(change, BREAK);
444
- }
445
- }
638
+ visitRows((_b = childrenOf(v)) !== null && _b !== void 0 ? _b : [], { element: el, container: items }, into);
639
+ continue;
640
+ }
641
+ // a kept mc:AlternateContent is its selected branch, as Word reads it, and keeps nothing apart
642
+ // (CR-013 phase 2): Word's save has no element there. Its other branches' are twins (phase 3)
643
+ if (tn === ALTERNATE_CONTENT) {
644
+ alternateTokens(v, visit, into, listing);
446
645
  continue;
447
646
  }
448
647
  // a block-level content control or custom XML element keeps what is inside it apart; a
@@ -455,47 +654,137 @@ export class Body {
455
654
  }
456
655
  }
457
656
  };
657
+ // a table's rows, through row-level controls and custom XML and a row-level mc:AlternateContent
658
+ const visitRows = (items, table, into) => {
659
+ var _a;
660
+ for (const el of items) {
661
+ const tn = typeNameOf(el);
662
+ const v = el.value;
663
+ if (typeof v !== 'object' || v === null)
664
+ continue;
665
+ if (tn === 'org_docx4j_wml.Tr') {
666
+ // A row that is itself a revision reports as one change, as Office JS does: the markup its
667
+ // cells carry (Word marks every run and mark of a deleted or inserted row) belongs to that
668
+ // change, which accepts or rejects it along with the row.
669
+ const tokens = [];
670
+ visitCells((_a = childrenOf(v)) !== null && _a !== void 0 ? _a : [], el, tokens);
671
+ const inner = groupTouching(tokens);
672
+ const rowChanges = trackedChangesOfRow(el, items, inner, table, this);
673
+ if (rowChanges.length > 0)
674
+ into.push(...rowChanges);
675
+ else {
676
+ into.push(BREAK);
677
+ for (const change of inner)
678
+ into.push(change, BREAK);
679
+ }
680
+ }
681
+ else if (tn === ALTERNATE_CONTENT) {
682
+ alternateTokens(v, (branch, tokens) => visitRows(branch, table, tokens), into, listing);
683
+ }
684
+ else if (tn !== 'org_docx4j_wml.Tc') {
685
+ const children = childrenOf(v);
686
+ if (children)
687
+ visitRows(children, table, into);
688
+ }
689
+ }
690
+ };
691
+ // a row's cells, through cell-level controls and custom XML and a cell-level mc:AlternateContent
692
+ const visitCells = (items, row, tokens) => {
693
+ var _a;
694
+ for (const el of items) {
695
+ const tn = typeNameOf(el);
696
+ const v = el.value;
697
+ if (typeof v !== 'object' || v === null)
698
+ continue;
699
+ if (tn === 'org_docx4j_wml.Tc') {
700
+ const cellChange = cellChangeOf(el, items, row.value, this);
701
+ if (cellChange)
702
+ tokens.push(cellChange, BREAK);
703
+ visit((_a = childrenOf(v)) !== null && _a !== void 0 ? _a : [], tokens);
704
+ tokens.push(BREAK);
705
+ }
706
+ else if (tn === ALTERNATE_CONTENT) {
707
+ alternateTokens(v, (branch, into) => visitCells(branch, row, into), tokens, listing);
708
+ }
709
+ else {
710
+ const children = childrenOf(v);
711
+ if (children)
712
+ visitCells(children, row, tokens);
713
+ }
714
+ }
715
+ };
458
716
  visit(this.content, out);
459
717
  // the body's last section, after everything in it
460
- const section = bodySectionChangeOf(this.container);
718
+ const section = bodySectionChangeOf(this.container, this);
461
719
  if (section)
462
720
  out.push(BREAK, section);
721
+ // resolve-all's: every other branch's changes, each kept apart, after the story's (resolved first, last first)
722
+ if (listing.extra.length > 0)
723
+ out.push(BREAK, ...listing.extra);
463
724
  return groupTouching(out);
464
725
  }
465
726
  /**
466
727
  * Accepts every tracked change, last first so that a paragraph join never disturbs one still
467
728
  * to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were listed.
729
+ *
730
+ * With a filter (an extension, CR-011), only the single changes it matches are accepted, so a group
731
+ * can be resolved in part, and what no change owns is left alone; the count is of the listed changes
732
+ * with a matching piece (docx4j's `TrackedChanges.acceptAll(pkg, filter)`).
468
733
  */
469
- acceptAll() {
470
- return this.resolveAll('accept');
734
+ acceptAll(filter) {
735
+ return this.resolveAll('accept', filter);
471
736
  }
472
- /** Rejects every tracked change, last first. Returns how many were listed. */
473
- rejectAll() {
474
- return this.resolveAll('reject');
737
+ /** Rejects every tracked change, last first, as `acceptAll` accepts them. Returns how many were listed. */
738
+ rejectAll(filter) {
739
+ return this.resolveAll('reject', filter);
475
740
  }
476
741
  /**
477
742
  * Resolving one change can leave another to be listed afresh: accepting a move carries a deletion
478
743
  * nested in its moved text out of it, and resolving a broken move dissolves the rest of it into a
479
744
  * new `w:ins` or `w:del` (CR-002 section 29), so the list read first goes stale. The list is read
480
- * again after each pass until nothing is left, as "all" promises (found by the editor's C5); a
481
- * pass that resolves nothing ends it.
745
+ * again after each pass until nothing is left, as "all" promises (found by the editor's C5); as
746
+ * docx4j's `TrackedChanges.resolveAll` does it (CR-011): a change whose pieces all match is resolved
747
+ * whole, one with some matching its matching pieces alone, for up to 20 passes, and two passes in a
748
+ * row that leave as many matching changes as the one before end it. Then, with no filter, what is
749
+ * never listed is swept.
482
750
  */
483
- resolveAll(what) {
484
- let changes = this.getTrackedChanges();
485
- const count = changes.length;
486
- while (changes.length > 0) {
487
- for (let i = changes.length - 1; i >= 0; i--)
488
- changes[i][what]();
489
- const left = this.getTrackedChanges();
490
- if (left.length >= changes.length)
491
- break;
492
- changes = left;
493
- }
494
- // and what is never listed: a cell merge, a numbering change, records that change nothing
495
- resolveUnlisted(this.content, what);
751
+ resolveAll(what, filter) {
752
+ const matches = filter === undefined ? undefined : pieceMatcher(filter);
753
+ const matching = (change) => (matches ? change.pieces.filter(matches) : change.pieces);
754
+ let count = 0;
755
+ // one resolution for the lot, so the bookmarks are read before the first and after the last
756
+ resolving(this.package_, this, () => {
757
+ // counted as listed; resolved over every branch of a kept mc:AlternateContent, each other branch's
758
+ // changes on their own, as docx4j's resolveAll lists them (CR-013 phase 3)
759
+ count = this.getTrackedChanges().filter((change) => matching(change).length > 0).length;
760
+ let left = Infinity;
761
+ let stalled = 0;
762
+ for (let pass = 0; pass < 20 && stalled < 2; pass++) {
763
+ const changes = this.trackedChanges({ mode: 'every', extra: [] });
764
+ let matched = 0;
765
+ for (let i = changes.length - 1; i >= 0; i--) {
766
+ const change = changes[i];
767
+ const pieces = matching(change);
768
+ if (pieces.length === 0)
769
+ continue;
770
+ matched++;
771
+ if (pieces.length === change.pieces.length)
772
+ change[what]();
773
+ else
774
+ resolvePieces(pieces, what);
775
+ }
776
+ if (matched === 0)
777
+ break;
778
+ stalled = matched >= left ? stalled + 1 : 0;
779
+ left = matched;
780
+ }
781
+ // and what is never listed: a cell merge, a numbering change, records that change nothing
782
+ if (!matches)
783
+ resolveUnlisted(this.content, what);
784
+ });
496
785
  return count;
497
786
  }
498
- /** Every `w:tr` in this body's tables, in document order (rows of nested tables included). */
787
+ /** Every `w:tr` in this body's tables, in document order (rows of nested tables included), in every branch (CR-013 phase 3). */
499
788
  rowElements() {
500
789
  const out = [];
501
790
  const visit = (items) => {
@@ -505,13 +794,18 @@ export class Body {
505
794
  if (typeof v !== 'object' || v === null)
506
795
  continue;
507
796
  if (typeNameOf(el) === 'org_docx4j_wml.Tbl') {
508
- for (const row of rowsOf(v)) {
797
+ for (const row of rowsOf(v, 'every')) {
509
798
  out.push(row.element);
510
- for (const cell of cellsOf(row.element.value))
799
+ for (const cell of cellsOf(row.element.value, 'every'))
511
800
  visit((_a = childrenOf(cell.element.value)) !== null && _a !== void 0 ? _a : []);
512
801
  }
513
802
  continue;
514
803
  }
804
+ if (typeNameOf(el) === ALTERNATE_CONTENT) {
805
+ for (const branch of branchesOf(v))
806
+ visit(branch);
807
+ continue;
808
+ }
515
809
  const children = childrenOf(v);
516
810
  if (children)
517
811
  visit(children);
@@ -527,7 +821,11 @@ export class Body {
527
821
  return this.part.getXml();
528
822
  }
529
823
  // --- addresses (CR-002 section 3.3) ---
530
- /** The element at an address: 'body/3' (fourth block), 'body/3/1/0/2' (table row 1, cell 0, block 2), a paraId, or a text match. */
824
+ /**
825
+ * The element at an address: 'body/3' (fourth block), 'body/3/1/0/2' (table row 1, cell 0, block 2),
826
+ * 'body/3/textbox:0/1' (the second block of the first text box body/3 anchors, CR-012 D4), a paraId, or a
827
+ * text match. Undefined, never an error, where the address names nothing.
828
+ */
531
829
  elementAt(address) {
532
830
  if (typeof address === 'string' && address.startsWith('w14:'))
533
831
  address = { paraId: address.substring(4) };
@@ -547,9 +845,22 @@ export class Body {
547
845
  let container;
548
846
  let element;
549
847
  for (const seg of tail === '' ? [] : tail.substring(1).split('/')) {
550
- container = childrenOf(current);
848
+ if (seg.startsWith('textbox:')) {
849
+ // the n-th text box the paragraph here anchors, as the text model numbers them (CR-012 D4)
850
+ if (typeNameOf(element !== null && element !== void 0 ? element : {}) !== 'org_docx4j_wml.P')
851
+ return undefined;
852
+ const n = Number(seg.substring('textbox:'.length));
853
+ const box = Number.isInteger(n) && n >= 0 ? textBoxesOf(current, this.hiddenRuns(current))[n] : undefined;
854
+ if (!(box === null || box === void 0 ? void 0 : box.content))
855
+ return undefined; // none, or legacy WordArt: no paragraphs
856
+ current = box.content;
857
+ element = undefined;
858
+ container = undefined;
859
+ continue;
860
+ }
861
+ container = childrenOf(current, 'read');
551
862
  const i = Number(seg);
552
- if (!container || !Number.isInteger(i) || i < 0 || i >= container.length)
863
+ if (!container || seg === '' || !Number.isInteger(i) || i < 0 || i >= container.length)
553
864
  return undefined;
554
865
  element = container[i];
555
866
  current = element.value;
@@ -567,38 +878,38 @@ export class Body {
567
878
  const path = this.pathOf(target.element.value);
568
879
  return path ? [this.prefix, ...path].join('/') : undefined;
569
880
  }
570
- /** What an agent asks for first: every paragraph and table with its address, style and text. */
571
- outline() {
572
- const paragraphs = [];
573
- const tables = [];
574
- const visit = (items, path) => {
575
- items.forEach((el, i) => {
576
- var _a, _b, _c, _d, _e;
577
- const tn = typeNameOf(el);
578
- const here = [...path, i];
579
- const address = [this.prefix, ...here].join('/');
580
- if (tn === 'org_docx4j_wml.P') {
581
- const p = el.value;
582
- const entry = { address, style: (_c = (_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.pStyle) === null || _b === void 0 ? void 0 : _b.val) !== null && _c !== void 0 ? _c : 'Normal', text: textOf(p) };
583
- if (p.paraId)
584
- entry.paraId = p.paraId;
585
- paragraphs.push(entry);
586
- }
587
- else if (tn === 'org_docx4j_wml.Tbl') {
588
- const rows = (_d = childrenOf(el.value)) !== null && _d !== void 0 ? _d : [];
589
- const cols = rows.length ? ((_e = childrenOf(rows[0].value)) !== null && _e !== void 0 ? _e : []).filter((c) => typeNameOf(c) === 'org_docx4j_wml.Tc').length : 0;
590
- tables.push({ address, rows: rows.filter((r) => typeNameOf(r) === 'org_docx4j_wml.Tr').length, cols });
591
- rows.forEach((row, ri) => { var _a; return ((_a = childrenOf(row.value)) !== null && _a !== void 0 ? _a : []).forEach((cell, ci) => { var _a; return visit((_a = childrenOf(cell.value)) !== null && _a !== void 0 ? _a : [], [...here, ri, ci]); }); });
592
- }
593
- else if (typeof el.value === 'object' && el.value !== null) {
594
- const children = childrenOf(el.value);
595
- if (children)
596
- visit(children, here);
597
- }
598
- });
599
- };
600
- visit(this.content, []);
601
- return { paragraphs, tables };
881
+ /**
882
+ * What an agent asks for first: every paragraph and table with its address, style and text, read by the
883
+ * text model (CR-012 D3; `Texts.of`'s options, `TextOptionsInit`): each paragraph's text is the model's
884
+ * (hidden text left out, a paragraph whose mark the view deletes joined to the next, in the view asked
885
+ * for), and each text box's paragraphs follow their anchoring paragraph, or the table it is in (REQ-518),
886
+ * at `textbox:` addresses (`body/3/textbox:0/1`), flagged `textbox`. A cell's paragraphs and tables follow
887
+ * their table's entry.
888
+ * Async since phase 4, as the model's reading is; calls under one `TextContext` (`Texts.contextOf`) read
889
+ * the package's stories once. The package is not changed (REQ-204).
890
+ *
891
+ * With `select`, only some of this body's top-level blocks are read (CR-012 section 17.13): their entries
892
+ * are those the whole outline gives them under the same context and options, in document order. A
893
+ * paragraph whose text a paragraph before it holds (`joined`) gives none; a block that starts inside a
894
+ * field open before it (a table of contents left out, D12) is read inside it. Under a context, the first
895
+ * such call over a body walks its blocks' field characters once; each block after that costs its own
896
+ * reading. A context is a snapshot: after an edit, make another.
897
+ */
898
+ async outline(options, context, select) {
899
+ return outlineReader()(this, options, context, select === null || select === void 0 ? void 0 : select.blocks.map((b) => this.blockIndexOf(b)));
900
+ }
901
+ /** The index in `content` of one of this body's top-level blocks. */
902
+ blockIndexOf(block) {
903
+ const content = this.content;
904
+ if (typeof block === 'number') {
905
+ if (!Number.isInteger(block) || block < 0 || block >= content.length)
906
+ throw new Docx4JException(`${this.prefix} has no block ${block}`);
907
+ return block;
908
+ }
909
+ const i = block.container === content ? content.indexOf(block.element) : -1;
910
+ if (i < 0)
911
+ throw new Docx4JException(`Not one of ${this.prefix}'s top-level blocks: ${describe(block.element)}`);
912
+ return i;
602
913
  }
603
914
  // --- docx4j names ---
604
915
  /** docx4j MainDocumentPart.addParagraphOfText. */
@@ -694,7 +1005,7 @@ export class Body {
694
1005
  return found;
695
1006
  }
696
1007
  pathOf(value) {
697
- var _a;
1008
+ var _a, _b;
698
1009
  const path = [];
699
1010
  let child = value;
700
1011
  for (let guard = 0; guard < 64; guard++) {
@@ -715,25 +1026,46 @@ export class Body {
715
1026
  path.unshift(i);
716
1027
  if (parent === this.container)
717
1028
  return path;
1029
+ if (BRANCH_TYPES.has((_b = parent.TYPE_NAME) !== null && _b !== void 0 ? _b : '')) {
1030
+ // a kept mc:AlternateContent's branch is no level of its own (D2): what is in the selected one is
1031
+ // addressed from the element, body/2/0; another branch's is not addressed, readers not seeing it
1032
+ const ac = parent.PARENT;
1033
+ if (!ac || selectedBranchOf(ac) !== children)
1034
+ return undefined;
1035
+ child = ac;
1036
+ continue;
1037
+ }
1038
+ if (parent.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent') {
1039
+ // a text box's block: addressed from the paragraph that anchors it, `textbox:n` its place among
1040
+ // that paragraph's boxes; none for a box the text model doesn't number (another branch's, a hidden run's)
1041
+ const anchor = anchorOf(parent);
1042
+ const n = anchor ? textBoxesOf(anchor, this.hiddenRuns(anchor)).findIndex((box) => box.content === parent) : -1;
1043
+ if (!anchor || n < 0)
1044
+ return undefined;
1045
+ path.unshift(`textbox:${n}`);
1046
+ child = anchor;
1047
+ continue;
1048
+ }
718
1049
  child = parent;
719
1050
  }
720
1051
  return undefined;
721
1052
  }
722
- /** Gives a new paragraph a w14:paraId when the document already uses them. */
1053
+ /**
1054
+ * Gives a new paragraph a w14:paraId when the document already uses them, free in every branch (CR-013 phase 3).
1055
+ * @internal a paragraph split (CR-014 phase 2) gives its new first half one.
1056
+ */
723
1057
  assignParaId(p) {
724
1058
  if (p.paraId)
725
1059
  return;
726
- if (!this.usesParaIds())
1060
+ const paragraphs = this.paragraphsIn('every');
1061
+ if (!paragraphs.some((q) => q.paraId !== undefined))
727
1062
  return;
728
1063
  let id;
729
1064
  do {
730
1065
  id = Math.floor(Math.random() * 0x7fffffff).toString(16).toUpperCase().padStart(8, '0');
731
- } while (this.paragraphs.some((q) => q.paraId === id));
1066
+ } while (paragraphs.some((q) => q.paraId === id));
732
1067
  p.paraId = id;
733
1068
  }
734
- usesParaIds() {
735
- return this.paragraphs.some((q) => q.paraId !== undefined);
736
- }
737
1069
  // --- comments (CR-002 phase G) ---
738
1070
  /**
739
1071
  * The comments anchored in this body, in document order, with replies nested under their
@@ -750,6 +1082,7 @@ export class Body {
750
1082
  * body. Returns the control.
751
1083
  */
752
1084
  insertContentControl(kind) {
1085
+ this.assertWritable();
753
1086
  const content = [...this.content];
754
1087
  const sdt = sdtOf(content, { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this)), form: 'block' });
755
1088
  this.content.length = 0;
@@ -777,3 +1110,109 @@ function describe(el) {
777
1110
  const tn = typeNameOf(el);
778
1111
  return `${(_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '?'} (${tn !== null && tn !== void 0 ? tn : 'no TYPE_NAME'})`;
779
1112
  }
1113
+ /**
1114
+ * A story's tracked changes resolved (`Body.acceptAll` or `rejectAll`, with the filter if one is given),
1115
+ * then those of each text box in it - a `w:txbxContent`, in a DrawingML shape or a VML one - which Word
1116
+ * counts as a story of its own (CR-002 section 49). A text box inside a text box is reached through its
1117
+ * own story. Returns how many changes were listed (with a filter, how many had a matching piece).
1118
+ *
1119
+ * In a kept `mc:AlternateContent` the text box of every branch is resolved, so that each says the same
1120
+ * afterwards (docx4j's `McMode.ALL`), and the changes are counted in the selected branch alone, so a
1121
+ * text box held in both counts once, as a resolved load's does (CR-010 phase 3).
1122
+ */
1123
+ export function resolveStory(body, what, filter) {
1124
+ let count = what === 'accept' ? body.acceptAll(filter) : body.rejectAll(filter);
1125
+ for (const { box, selected } of textBoxesIn(body.container)) {
1126
+ const resolved = resolveStory(body.sub(box), what, filter);
1127
+ if (selected)
1128
+ count += resolved;
1129
+ }
1130
+ return count;
1131
+ }
1132
+ /**
1133
+ * A story's tracked changes (`Body.getTrackedChanges`), then each text box's in it, as `resolveStory`
1134
+ * resolves them: docx4j's order (its CR-043 `Lister`, CR-011). In a kept `mc:AlternateContent` the
1135
+ * selected branch's text boxes alone are listed, so a text box held in both branches is listed once;
1136
+ * each other branch's box at the same place is its twin, whose changes are paired with the listed ones
1137
+ * by kind and position and resolved with them, as docx4j resolves a text box's `mc:Fallback` twin
1138
+ * (`pairTwins`; CR-013 phase 3).
1139
+ */
1140
+ export function trackedChangesOfStory(body) {
1141
+ const out = body.getTrackedChanges();
1142
+ for (const { box, selected, twins } of textBoxesIn(body.container)) {
1143
+ if (!selected)
1144
+ continue;
1145
+ const changes = trackedChangesOfStory(body.sub(box));
1146
+ for (const twin of twins !== null && twins !== void 0 ? twins : [])
1147
+ pairTwins(changes, trackedChangesOfStory(body.sub(twin)));
1148
+ out.push(...changes);
1149
+ }
1150
+ return out;
1151
+ }
1152
+ /**
1153
+ * The members `textBoxesIn` does not descend into: an element's name, and properties, which hold no text box.
1154
+ * Over every fixture the walk finds the same text boxes without them and visits 38% fewer objects (CR-012
1155
+ * section 17.13: it was most of a plan's cost).
1156
+ */
1157
+ const NO_TEXT_BOXES = new Set(['PARENT', 'name', 'pPr', 'rPr', 'tblPr', 'tblGrid', 'trPr', 'tcPr', 'sectPr', 'sdtPr', 'sdtEndPr']);
1158
+ /**
1159
+ * The `w:txbxContent` of every text box in a story's content, not descending into one found, each with
1160
+ * whether it is in a branch a reader takes: false inside a branch of a kept `mc:AlternateContent` other
1161
+ * than its selected one (`selectedBranchOf`). A selected box in a branch has as twins the boxes at its
1162
+ * place, by position, in the element's other branches (docx4j's `Lister.twins`).
1163
+ */
1164
+ function textBoxesIn(container) {
1165
+ const out = [];
1166
+ const seen = new Set();
1167
+ const visit = (o, selected, into) => {
1168
+ var _a;
1169
+ if (typeof o !== 'object' || o === null || seen.has(o))
1170
+ return;
1171
+ if (typeof o.nodeType === 'number')
1172
+ return; // DOM the model does not type
1173
+ seen.add(o);
1174
+ if (Array.isArray(o)) {
1175
+ for (const item of o)
1176
+ visit(item, selected, into);
1177
+ return;
1178
+ }
1179
+ const typeName = o.TYPE_NAME;
1180
+ if (typeName === 'org_docx4j_wml.CTTxbxContent') {
1181
+ into.push({ box: o, selected });
1182
+ return;
1183
+ }
1184
+ if (typeName === ALTERNATE_CONTENT) {
1185
+ const ac = o;
1186
+ const read = selectedBranchOf(ac);
1187
+ let primary = [];
1188
+ const others = [];
1189
+ for (const branch of [...((_a = ac.choice) !== null && _a !== void 0 ? _a : []), ...(ac.fallback ? [ac.fallback] : [])]) {
1190
+ const found = [];
1191
+ visit(branch.any, selected && branch.any === read, found);
1192
+ into.push(...found);
1193
+ if (branch.any === read)
1194
+ primary = found;
1195
+ else
1196
+ others.push(found);
1197
+ }
1198
+ for (const found of others)
1199
+ found.forEach(({ box }, i) => { var _a; const twin = primary[i]; if (twin)
1200
+ ((_a = twin.twins) !== null && _a !== void 0 ? _a : (twin.twins = [])).push(box); });
1201
+ return;
1202
+ }
1203
+ for (const key in o)
1204
+ if (!NO_TEXT_BOXES.has(key))
1205
+ visit(o[key], selected, into);
1206
+ };
1207
+ visit(container.content, true, out);
1208
+ return out;
1209
+ }
1210
+ /** The paragraph a text box's content is anchored in: its nearest `w:p` ancestor. */
1211
+ function anchorOf(box) {
1212
+ let o = box.PARENT;
1213
+ for (let guard = 0; o && guard < 64; guard++, o = o.PARENT) {
1214
+ if (o.TYPE_NAME === 'org_docx4j_wml.P')
1215
+ return o;
1216
+ }
1217
+ return undefined;
1218
+ }