@docx4j/core-ts 0.5.0 → 0.7.0

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