@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,26 +1,33 @@
1
1
  import { deepCopy, marshalString } from '@docx4j/generated-objects-ts';
2
2
  import { Docx4JException } from '../../opc/exceptions.mjs';
3
- import { r as textRun, t as textItem, br as breakItem } from '@docx4j/generated-objects-ts/builders/wml';
4
- import { segmentsOf, runsOf, runItemsOf, linkParents, textOf, textOfView, typeNameOf, W_NS, runOf, paragraphOf, revisionKindOf, objectInSpan, WHOLE } from './tree.mjs';
3
+ import { NotSupportedError } from '../../office-js/errors.mjs';
4
+ import { t as textItem, br as breakItem } from '@docx4j/generated-objects-ts/builders/wml';
5
+ import { segmentsOf, runsOf, runItemsOf, inShowingPlaceholder, linkParents, typeNameOf, W_NS, runOf, paragraphOf, revisionKindOf, objectInSpan, WHOLE, isAlternateContent, selectedBranchOf, inBranch, assertOutsideBranch } from './tree.mjs';
6
+ import { shownInDefaultView } from './story.mjs';
7
+ import { withinWord, WORD_STORAGE } from './wordRange.mjs';
5
8
  import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
6
9
  function withRPr(run, rPr) {
7
10
  if (rPr)
8
11
  run.value.rPr = rPr;
9
12
  return run;
10
13
  }
11
- import { Font } from './Font.mjs';
14
+ import { Font, markHolderOf } from './Font.mjs';
12
15
  import { runFontSelectorOf } from '../fonts/lookup.mjs';
13
16
  import { Range } from './Range.mjs';
14
- import { searchPattern, matchesOf, expandReplacement } from './search.mjs';
17
+ import { officeMatches, expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
15
18
  import { builtInOf, idOfBuiltIn, styleNameOf, styleIdOf } from './styles.mjs';
16
19
  import { cellOf } from './Table.mjs';
17
- import { ContentControl, collectRunControls } from './ContentControl.mjs';
18
- import { sdt as sdtOf, nextSdtId } from '@docx4j/generated-objects-ts/builders/wml';
20
+ import { ContentControl, collectRunControls, controlViewOf, plainTextLinesOf, textForRunControl, assertControlHolds, controlsOfTypes, holdsOneParagraph, showsPlaceholderWhenMade, BLOCK_CONTROL } from './ContentControl.mjs';
21
+ import { breakParagraphAt, BREAKABLE } from './spanEdits.mjs';
22
+ import { endsCell, endsWithParagraph, wrappingCellOf } from './tableRules.mjs';
23
+ import { dropExcluded, forWriter, linesOf, officeItems, oneParagraphRun, holdsElementCharacters } from './officeText.mjs';
24
+ import { sdt as sdtOf, nextSdtId, sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
19
25
  import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
20
26
  import { InlinePicture, picturesIn, addImage, writableWidthEmu } from './InlinePicture.mjs';
27
+ import { showPicturePlaceholder } from '../customxml/picturePlaceholder.mjs';
21
28
  import { contentOf } from './ooxml.mjs';
22
29
  import { commentApi } from './comments.mjs';
23
- import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties } from './tracking.mjs';
30
+ import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties, markEquationDeleted, equationRevisionIn } from './tracking.mjs';
24
31
  import { trackedChangesOfParagraph } from './TrackedChange.mjs';
25
32
  import { isListItem, listOf, listOrNullObjectOf, listItemOf, listItemOrNullObjectOf, startNewList, attachToList, detachFromList, } from './List.mjs';
26
33
  const TWIPS_PER_POINT = 20;
@@ -62,23 +69,57 @@ export class Paragraph {
62
69
  get p() {
63
70
  return this.element.value;
64
71
  }
72
+ /** @internal A view of another paragraph of this one's body, for a `Range` across paragraphs (CR-014). */
73
+ viewOf(element, container) {
74
+ return new Paragraph(element, container, this.parentBody);
75
+ }
76
+ /**
77
+ * @internal A range from this paragraph's start to the end of `last`, a later paragraph of the same story, with
78
+ * `last`'s mark where `mark` is set, or `endMark` in its place (CR-014 phase 1).
79
+ */
80
+ rangeTo(last, mark, endMark) {
81
+ return new Range(this, 0, last.text.length, true, mark, last.element === this.element ? undefined : last, endMark);
82
+ }
83
+ /** How many tables deep the paragraph is: 0 outside a table, 1 in a cell, 2 in a nested table's (Office JS `tableNestingLevel`). */
84
+ get tableNestingLevel() {
85
+ let level = 0;
86
+ for (let cell = this.parentTableCell; cell; cell = cell.parentTable.parentTableCell)
87
+ level++;
88
+ return level;
89
+ }
65
90
  get index() {
66
91
  return this.container.indexOf(this.element);
67
92
  }
68
- /** The paragraph's text: runs joined, tabs as \t, breaks as \n; deleted text excluded. */
93
+ /**
94
+ * The paragraph's text as Office JS gives it in Word's default view (CR-012 phase 4, section 6.2):
95
+ * the accepted text, hidden text left out, in Word's characters (check 48: a note reference U+0002,
96
+ * a line break U+000B, a page break U+000C, a column break U+000E, a `w:cr` U+000D, the hyphens
97
+ * U+001E and U+001F, a symbol `(`, an equation as Word draws it). A kept `mc:AlternateContent`
98
+ * reads as its selected branch (check 49). Every offset a `Range` or `search` takes counts in it.
99
+ */
69
100
  get text() {
70
- return textOf(this.p);
101
+ return this.segments().map((s) => s.text).join('');
71
102
  }
72
103
  /** Replaces the content with one run of the text, keeping the first run's formatting. */
73
104
  set text(value) {
74
105
  var _a;
106
+ this.assertWritable();
107
+ // one paragraph, its line breaks w:br and w:cr (CR-002 section 60 E2), or as a plain-text control takes them (F3)
108
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
109
+ const mode = lines === 'multi' ? 'multi-line' : lines === 'single' ? 'single-line' : 'one-paragraph';
110
+ // in a block control showing its placeholder, the text in the placeholder's place (CR-002 section 63)
111
+ const placeholder = this.placeholderControl;
112
+ if (placeholder) {
113
+ placeholder.writePlaceholder(value, { lineBreaks: mode });
114
+ return;
115
+ }
75
116
  if (this.changeTracker) {
76
- this.splice(0, this.text.length, value, 'before', WHOLE);
117
+ this.splice(0, this.text.length, value, 'before', WHOLE, mode);
77
118
  return;
78
119
  }
79
120
  const rPr = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value.rPr;
80
121
  this.releaseRelationships(this.p.content);
81
- this.p.content = [withRPr(textRun(value), rPr ? deepCopy(rPr) : undefined)];
122
+ this.p.content = [oneParagraphRun(value, rPr ? deepCopy(rPr) : undefined, mode)];
82
123
  linkParents(this.p.content, this.p);
83
124
  }
84
125
  /**
@@ -86,7 +127,7 @@ export class Paragraph {
86
127
  * reads the document as it was before the tracked changes: `w:del` included, `w:ins` excluded.
87
128
  */
88
129
  getText(options) {
89
- return (options === null || options === void 0 ? void 0 : options.view) === 'original' ? textOfView(this.p, options) : this.text;
130
+ return (options === null || options === void 0 ? void 0 : options.view) === 'original' ? this.segments({ view: 'original' }).map((s) => s.text).join('') : this.text;
90
131
  }
91
132
  /** The style id (w:pStyle; docx4j's name for it); 'Normal' when none is set. Extension: Office JS has `style` and `styleBuiltIn` only. */
92
133
  get styleId() {
@@ -190,36 +231,54 @@ export class Paragraph {
190
231
  // in a table cell what is inherited includes the table style's (CR-007)
191
232
  return alignmentOf(resolver.getEffectivePPr(rest, resolver.cellContextOf(this.p)), true);
192
233
  }
193
- /** Indents and spacing in points, as Office JS; the effective values (CR-001 Phase B step 2). */
234
+ /**
235
+ * Indents and spacing in points, as Office JS; the effective values (CR-001 Phase B step 2). A value written is held to
236
+ * the range Word 365 stores its attribute in (`withinWord`, CR-002 section 65): outside it, `InvalidArgument`, before
237
+ * anything changes.
238
+ */
194
239
  get leftIndent() { var _a, _b; return ((_b = (_a = this.effectivePPr.ind) === null || _a === void 0 ? void 0 : _a.left) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
195
- set leftIndent(pt) { this.ind().left = Math.round(pt * TWIPS_PER_POINT); this.settleFormatting(); }
240
+ set leftIndent(pt) { const v = withinWord('Paragraph.leftIndent', pt, WORD_STORAGE.indLeft); this.ind().left = v; this.settleFormatting(); }
196
241
  get rightIndent() { var _a, _b; return ((_b = (_a = this.effectivePPr.ind) === null || _a === void 0 ? void 0 : _a.right) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
197
- set rightIndent(pt) { this.ind().right = Math.round(pt * TWIPS_PER_POINT); this.settleFormatting(); }
242
+ set rightIndent(pt) { const v = withinWord('Paragraph.rightIndent', pt, WORD_STORAGE.indRight); this.ind().right = v; this.settleFormatting(); }
198
243
  /** First-line indent; negative for a hanging indent, as Office JS. */
199
244
  get firstLineIndent() {
200
245
  return firstLineIndentOf(this.effectivePPr);
201
246
  }
202
247
  set firstLineIndent(pt) {
248
+ const v = pt < 0
249
+ ? withinWord('Paragraph.firstLineIndent (a hanging indent)', -pt, WORD_STORAGE.indHanging)
250
+ : withinWord('Paragraph.firstLineIndent', pt, WORD_STORAGE.indFirstLine);
203
251
  const ind = this.ind();
204
252
  if (pt < 0) {
205
- ind.hanging = Math.round(-pt * TWIPS_PER_POINT);
253
+ ind.hanging = v;
206
254
  delete ind.firstLine;
207
255
  }
208
256
  else {
209
- ind.firstLine = Math.round(pt * TWIPS_PER_POINT);
257
+ ind.firstLine = v;
210
258
  delete ind.hanging;
211
259
  }
212
260
  this.settleFormatting();
213
261
  }
214
262
  get spaceBefore() { var _a, _b; return ((_b = (_a = this.effectivePPr.spacing) === null || _a === void 0 ? void 0 : _a.before) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
215
- set spaceBefore(pt) { this.spacing().before = Math.round(pt * TWIPS_PER_POINT); this.settleFormatting(); }
263
+ set spaceBefore(pt) { const v = withinWord('Paragraph.spaceBefore', pt, WORD_STORAGE.spacingBefore); this.spacing().before = v; this.settleFormatting(); }
216
264
  get spaceAfter() { var _a, _b; return ((_b = (_a = this.effectivePPr.spacing) === null || _a === void 0 ? void 0 : _a.after) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
217
- set spaceAfter(pt) { this.spacing().after = Math.round(pt * TWIPS_PER_POINT); this.settleFormatting(); }
265
+ set spaceAfter(pt) { const v = withinWord('Paragraph.spaceAfter', pt, WORD_STORAGE.spacingAfter); this.spacing().after = v; this.settleFormatting(); }
218
266
  /** Line spacing in points: w:line/240 lines of 12pt when the rule is auto, else w:line twips; 0 when not set. */
219
267
  get lineSpacing() {
220
268
  return lineSpacingOf(this.effectivePPr);
221
269
  }
222
- set lineSpacing(pt) { const s = this.spacing(); s.line = Math.round(pt * TWIPS_PER_POINT); s.lineRule = 'exact'; this.settleFormatting(); }
270
+ /**
271
+ * Writes `w:line` as 20 × points under `w:lineRule="auto"`, as Word 365's own setter does (check 71b: 0.75 pt as
272
+ * `w:line="15" w:lineRule="auto"`; Office JS: "in the Word UI, this value is divided by 12", so 12 pt is single spacing),
273
+ * within Word's own limits, 0.75 to 1584 pt (CR-002 section 65.9). Until check 71b it wrote an exact rule.
274
+ */
275
+ set lineSpacing(pt) {
276
+ const v = withinWord('Paragraph.lineSpacing', pt, WORD_STORAGE.spacingLineAuto);
277
+ const s = this.spacing();
278
+ s.line = v;
279
+ s.lineRule = 'auto';
280
+ this.settleFormatting();
281
+ }
223
282
  /** w:outlineLvl + 1 (1 to 9); 10 for body text, as Office JS. A heading style supplies it. */
224
283
  get outlineLevel() { return outlineLevelOf(this.effectivePPr); }
225
284
  set outlineLevel(level) { const pPr = this.pPr(); if (level >= 10 || level < 1)
@@ -227,24 +286,36 @@ export class Paragraph {
227
286
  else
228
287
  pPr.outlineLvl = { val: level - 1 }; this.settleFormatting(); }
229
288
  /**
230
- * The runs' formatting: reads the first run's *effective* properties, writes direct
231
- * formatting to all (extension: `Font` is over runs, not the paragraph mark).
289
+ * The runs' formatting: reads the *effective* properties of the runs holding the paragraph's text, `null` (or `''`,
290
+ * `Mixed`) where they differ, and the mark's where it has no text, as Office JS reads them (CR-002 section 59); writes
291
+ * direct formatting to every run (extension: `Font` is over runs, not the paragraph mark).
232
292
  */
233
293
  get font() {
234
294
  return this.getFont();
235
295
  }
236
- /** `font`, with `{ direct: true }` for the direct formatting of the first run (extension). */
296
+ /** `font`, with `{ direct: true }` for the runs' direct formatting (extension). */
237
297
  getFont(options) {
238
- const holders = () => runsOf(this.p).map((r) => r.value);
298
+ const holders = (write) => {
299
+ if (write) {
300
+ this.assertWritable();
301
+ return runsOf(this.p).map((r) => r.value);
302
+ }
303
+ // a read takes the runs of Paragraph.text, Word's default view, as a range over it does; with none, a placeholder's
304
+ // runs, which Word's font reads though its text doesn't (check 52 case 13, after Accept All: Word's bold filler)
305
+ const runs = this.getRange('Content').runs.map((r) => r.value);
306
+ return runs.length > 0 ? runs : runsOf(this.p).map((r) => r.value).filter((r) => inShowingPlaceholder(r));
307
+ };
308
+ const mark = () => markHolderOf(this.p);
239
309
  if ((options === null || options === void 0 ? void 0 : options.direct) === true)
240
- return new Font(holders, () => this.fontTracking());
241
- return new Font(holders, () => this.fontTracking(), (rPr) => this.parentBody.propertyResolver.getEffectiveRPr(rPr, this.p.pPr, this.cellContext), (rPr) => { var _a; return (_a = runFontSelectorOf(this.parentBody.package_)) === null || _a === void 0 ? void 0 : _a.asciiFontName(rPr); });
310
+ return new Font(holders, () => this.fontTracking(), undefined, undefined, mark);
311
+ return new Font(holders, () => this.fontTracking(), (rPr) => this.parentBody.propertyResolver.getEffectiveRPr(rPr, this.p.pPr, this.cellContext), (rPr) => { var _a; return (_a = runFontSelectorOf(this.parentBody.package_)) === null || _a === void 0 ? void 0 : _a.asciiFontName(rPr); }, mark, () => this.parentBody.propertyResolver.getThemeColors());
242
312
  }
243
313
  /** w14:paraId, the stable address Word gives paragraphs. */
244
314
  get paraId() {
245
315
  return this.p.paraId;
246
316
  }
247
317
  set paraId(id) {
318
+ this.assertWritable();
248
319
  if (id === undefined)
249
320
  delete this.p.paraId;
250
321
  else
@@ -264,6 +335,14 @@ export class Paragraph {
264
335
  collectRunControls(this.p, this.parentBody, out);
265
336
  return out;
266
337
  }
338
+ /**
339
+ * Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
340
+ * where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
341
+ * departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
342
+ */
343
+ getContentControls(options) {
344
+ return controlsOfTypes(this.contentControls, options);
345
+ }
267
346
  /**
268
347
  * The inline pictures in this paragraph, in order (Office JS Paragraph.inlinePictures): each
269
348
  * `w:drawing` whose `wp:inline` holds a `pic:pic`, in a run or in the selected branch of a kept
@@ -283,27 +362,55 @@ export class Paragraph {
283
362
  }
284
363
  /** Text inserted at the start, the end, or replacing the whole paragraph's text. */
285
364
  insertText(text, location) {
365
+ // in a block control showing its placeholder the text takes the placeholder's place wherever it is put (CR-002
366
+ // section 63; check 63's run 2, cases 7 to 9 and 33 to 35)
367
+ const placeholder = this.placeholderControl;
368
+ if (placeholder)
369
+ return placeholder.writePlaceholder(text);
286
370
  const length = this.text.length;
287
371
  if (location === 'Start')
288
372
  return this.splice(0, 0, text);
289
373
  if (location === 'End')
290
- return this.splice(length, length, text);
374
+ return this.writeAtEnd(text);
375
+ // tracked, the insertion goes first, before the deletion, as a range's Replace writes it and Word wrote this one
376
+ // (check 63 cases 45 and 52; CR-002 section 63 D3)
377
+ if (this.changeTracker && length > 0)
378
+ return this.getRange('Content').insertText(text, 'Replace');
291
379
  return this.splice(0, length, text, 'before', WHOLE);
292
380
  }
293
- /** A new paragraph of the text before or after this one, with this paragraph's properties (as Word). */
381
+ /**
382
+ * A new paragraph of the text before or after this one, with this paragraph's properties (as Word). Beside the only
383
+ * paragraph of a block control showing its placeholder it goes outside the control, as Word's went, and the control
384
+ * stays at block level (CR-002 section 63, D6 (b); check 63 cases 10, 11, 36 and 37).
385
+ */
294
386
  insertParagraph(text, location) {
387
+ var _a, _b;
388
+ this.assertWritable();
295
389
  const pPr = this.p.pPr ? deepCopy(this.p.pPr) : undefined;
296
390
  if (pPr) {
297
391
  delete pPr.rPr;
298
392
  delete pPr.sectPr;
299
393
  delete pPr.pPrChange;
300
394
  }
301
- const el = paragraphOf(text === '' ? [] : [textRun(text)], pPr);
395
+ const run = oneParagraphRun(text); // E2
396
+ const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : [], pPr);
397
+ const control = this.blockControl;
398
+ if ((control === null || control === void 0 ? void 0 : control.isShowingPlaceholder) && holdsOneParagraph(control)) {
399
+ // the body holding the control: this paragraph's is the control's content
400
+ const owner = (_b = control.element.value.PARENT) !== null && _b !== void 0 ? _b : this.parentBody.container;
401
+ return this.parentBody.sub(owner).insertElement(el, location, { element: control.element, container: control.container });
402
+ }
302
403
  return this.parentBody.insertElement(el, location, this);
303
404
  }
405
+ /** The block-level control holding this paragraph directly while it shows its placeholder (CR-002 section 63). */
406
+ get placeholderControl() {
407
+ const control = this.blockControl;
408
+ return (control === null || control === void 0 ? void 0 : control.showsPlaceholderContent) ? control : undefined;
409
+ }
304
410
  insertBreak(type, location) {
305
411
  var _a;
306
412
  var _b;
413
+ this.assertWritable();
307
414
  const brk = breakItem(type === 'Page' ? 'page' : undefined);
308
415
  if (location === 'Before' || location === 'After') {
309
416
  const p = this.insertParagraph('', location);
@@ -331,11 +438,15 @@ export class Paragraph {
331
438
  insertInlinePictureFromBase64(base64, location, options) {
332
439
  var _a;
333
440
  var _b;
441
+ this.assertWritable();
334
442
  const part = this.parentBody.part;
335
443
  if (!part)
336
444
  throw new Docx4JException('This body has no part, so an image cannot be related to it');
337
- const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
338
445
  const tracker = this.changeTracker;
446
+ // refused before the image part is added, not after (CR-012 section 12.4)
447
+ if (location === 'Replace' && tracker)
448
+ this.assertPlaceable(0, this.text.length, 'replace');
449
+ const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
339
450
  // under tracking the picture is an insertion, and what it replaces is deleted as insertText's is
340
451
  if (location === 'Replace' && tracker)
341
452
  this.splice(0, this.text.length, '', 'before', WHOLE);
@@ -360,6 +471,7 @@ export class Paragraph {
360
471
  */
361
472
  async insertOoxml(ooxml, location) {
362
473
  var _a, _b;
474
+ this.assertWritable();
363
475
  const preprocess = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
364
476
  const elements = await contentOf(ooxml, { preprocess: preprocess ? (doc) => preprocess(doc) : undefined, target: this.parentBody.part });
365
477
  if (elements.length === 0)
@@ -370,12 +482,12 @@ export class Paragraph {
370
482
  return [this];
371
483
  }
372
484
  if (location === 'Replace') {
373
- const inserted = this.parentBody.insertElement(elements, 'Before', this);
485
+ const inserted = this.parentBody.insertElement(elements, 'Before', this, 'GeneralException', this.element);
374
486
  void inserted;
375
487
  this.delete();
376
488
  }
377
489
  else {
378
- this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this);
490
+ this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this, 'GeneralException');
379
491
  }
380
492
  return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
381
493
  ? new Paragraph(el, this.container, this.parentBody)
@@ -383,6 +495,10 @@ export class Paragraph {
383
495
  }
384
496
  /** Matches within this paragraph. */
385
497
  search(text, options) {
498
+ // Word's paragraph mark, ^p, is searched in the paragraph's whole range, which holds its mark (CR-014 phase 3), and so
499
+ // is a wildcard query, whose ? or class matches the mark too (check 59; CR-002 section 32, K7)
500
+ if ((options === null || options === void 0 ? void 0 : options.matchWildcards) || (!(options === null || options === void 0 ? void 0 : options.matchRegExp) && paragraphMarkPieces(text)))
501
+ return this.getRange('Whole').search(text, options);
386
502
  return this.searchMatches(text, options).map((m) => m.range);
387
503
  }
388
504
  /**
@@ -391,8 +507,8 @@ export class Paragraph {
391
507
  * and `Body.replaceText` the other.
392
508
  */
393
509
  searchMatches(text, options) {
394
- return matchesOf(this.text, searchPattern(text, options))
395
- .map((match) => ({ range: new Range(this, match.index, match.index + match[0].length), match }));
510
+ return officeMatches(this.segments({ objects: true }), text, options)
511
+ .map(({ start, end, match }) => ({ range: new Range(this, start, end), match }));
396
512
  }
397
513
  /**
398
514
  * Replaces every match of `find` with `replace`, last match first so that the offsets of the
@@ -400,6 +516,9 @@ export class Paragraph {
400
516
  * the package's `changeTrackingMode` is on.
401
517
  */
402
518
  replaceText(find, replace, options) {
519
+ refuseMarkReplacement('Paragraph', find, options);
520
+ if (options === null || options === void 0 ? void 0 : options.matchWildcards)
521
+ refuseWildcardMarkReplacement('Paragraph', this.search(find, options));
403
522
  const matches = this.searchMatches(find, options);
404
523
  for (let i = matches.length - 1; i >= 0; i--) {
405
524
  const { range, match } = matches[i];
@@ -414,24 +533,51 @@ export class Paragraph {
414
533
  /**
415
534
  * The paragraph's range: 'Whole' and 'Content' hold its whole content, the pictures at its start and
416
535
  * end among them, so deleting it deletes them (CR-002 section 48); 'Start' and 'End' are collapsed.
536
+ * 'Whole' holds the paragraph's mark too, so its `text` ends with `markText`, as Office JS's does
537
+ * (check 48); 'Content' ends with the paragraph's text.
417
538
  */
418
539
  getRange(location = 'Whole') {
419
540
  const length = this.text.length;
420
541
  if (location === 'Start')
421
542
  return new Range(this, 0, 0);
422
543
  if (location === 'End')
423
- return new Range(this, length, length);
424
- return new Range(this, 0, length, true);
544
+ return new Range(this, length, length).atEndOf('End');
545
+ const range = new Range(this, 0, length, true, location === 'Whole');
546
+ return location === 'Content' ? range.atEndOf('Content') : range;
547
+ }
548
+ /**
549
+ * The character Office JS gives for this paragraph's mark at the end of its 'Whole' range (check 48):
550
+ * `\t` for the last paragraph of a table cell, the cell's end; nothing for a mark tracked as deleted, which
551
+ * Word's default view doesn't show (CR-012 section 19, check 58), or as moved from, as the paragraph's runs read
552
+ * (CR-002 section 64); and `\r` for any other.
553
+ */
554
+ get markText() {
555
+ if (lastInCell(this.p))
556
+ return '\t';
557
+ return shownInDefaultView(this.p) ? '\r' : '';
425
558
  }
426
559
  /**
427
560
  * Removes the paragraph from its container. While the package tracks changes, the content
428
561
  * becomes a `w:del` and the mark is marked deleted (`w:pPr/w:rPr/w:del`) instead, unless the
429
562
  * whole paragraph was this author's own insertion, which Word simply takes back.
563
+ * The paragraph a table cell ends with, as Word reads it, stays where removing it would leave the
564
+ * cell without one, emptied, its properties kept; tracked, its mark is never marked deleted, as Word
565
+ * keeps a cell's last paragraph (check 52, cases 88 to 91; CR-002 section 55.6).
430
566
  */
431
567
  delete() {
568
+ this.remove(true);
569
+ }
570
+ /**
571
+ * `delete()`; with `keepCellEnd` false a cell's last paragraph goes as any other, for `Body.clearContent`'s
572
+ * callers, which put their own paragraph in next.
573
+ * @internal
574
+ */
575
+ remove(keepCellEnd) {
576
+ this.assertWritable();
577
+ const cellEnd = keepCellEnd && endsCell(this.p);
432
578
  const tracker = this.changeTracker;
433
579
  if (tracker) {
434
- this.trackedDelete(tracker);
580
+ this.trackedDelete(tracker, cellEnd);
435
581
  return;
436
582
  }
437
583
  const i = this.index;
@@ -439,8 +585,17 @@ export class Paragraph {
439
585
  return;
440
586
  this.container.splice(i, 1);
441
587
  this.releaseRelationships(this.element);
588
+ if (cellEnd && !this.cellStillEnds()) {
589
+ this.p.content = [];
590
+ this.container.splice(i, 0, this.element);
591
+ }
442
592
  }
443
- trackedDelete(tracker) {
593
+ /** Whether the cell this paragraph was in ends with a paragraph without it, as Word reads it (CR-002 section 55). */
594
+ cellStillEnds() {
595
+ var _a;
596
+ return endsWithParagraph((_a = wrappingCellOf(this.p.PARENT)) === null || _a === void 0 ? void 0 : _a.content);
597
+ }
598
+ trackedDelete(tracker, cellEnd) {
444
599
  var _a, _b;
445
600
  if (markDeleted(this.p))
446
601
  throw new Docx4JException('This paragraph is already marked deleted');
@@ -451,10 +606,17 @@ export class Paragraph {
451
606
  const ownMark = (rPr === null || rPr === void 0 ? void 0 : rPr.ins) !== undefined && rPr.ins.author === tracker.author;
452
607
  if (ownMark && ((_b = this.p.content) !== null && _b !== void 0 ? _b : []).length === 0) {
453
608
  const i = this.index;
454
- if (i >= 0)
609
+ if (i >= 0) {
455
610
  this.container.splice(i, 1);
611
+ if (!cellEnd || this.cellStillEnds())
612
+ return;
613
+ this.container.splice(i, 0, this.element);
614
+ }
456
615
  return;
457
616
  }
617
+ // a cell's last mark: Word deletes the paragraph's text and never marks the mark (check 52, cases 90 and 91)
618
+ if (cellEnd)
619
+ return;
458
620
  tracker.markParagraphDeleted(this.p);
459
621
  // Word, deleting a paragraph mark ALONE under tracking, gives the next paragraph this one's
460
622
  // properties as a w:pPrChange (CR-002 section 38 item 2, the editor's measure-word15.docx), so
@@ -469,13 +631,14 @@ export class Paragraph {
469
631
  * what the cells of a deleted table row need (a `w:tc` must keep a block-level child, and Word
470
632
  * writes `w:del` around every run and `w:pPr/w:rPr/w:del` on every mark inside a deleted row).
471
633
  * The same primitives `delete()` uses, without its "take back my own insertion" step.
472
- * Does nothing when the mark is already deleted.
634
+ * A mark already deleted is left as it is, and the text is deleted still: a paragraph another author's
635
+ * deletion ended keeps text a range's deletion across it must take (CR-014 phase 2).
473
636
  */
474
637
  markDeletedInPlace(tracker) {
475
- if (markDeleted(this.p))
476
- return;
638
+ this.assertWritable();
477
639
  this.deleteText(tracker, 0, this.text.length, WHOLE);
478
- tracker.markParagraphDeleted(this.p);
640
+ if (!markDeleted(this.p))
641
+ tracker.markParagraphDeleted(this.p);
479
642
  }
480
643
  /**
481
644
  * Queues the relationships that content removed outright referenced (a picture's image, a link's
@@ -512,6 +675,7 @@ export class Paragraph {
512
675
  pPr() {
513
676
  var _a, _b;
514
677
  var _c;
678
+ this.assertWritable();
515
679
  const pPr = ((_a = (_c = this.p).pPr) !== null && _a !== void 0 ? _a : (_c.pPr = { TYPE_NAME: 'org_docx4j_wml.PPr' }));
516
680
  linkParents(pPr, this.p);
517
681
  (_b = this.changeTracker) === null || _b === void 0 ? void 0 : _b.recordPPrChange(pPr);
@@ -590,9 +754,49 @@ export class Paragraph {
590
754
  ownInsertions.add(seg.run);
591
755
  return { tracker, ownInsertions };
592
756
  }
593
- /** The text segments (w:t, w:tab, ...) with their offsets; `{ objects: true }` adds the pictures. */
757
+ /**
758
+ * The text segments (w:t, w:tab, ...) with their offsets in `text`; `{ objects: true }` adds the
759
+ * pictures and the hidden runs' items.
760
+ */
594
761
  segments(options) {
595
- return segmentsOf(this.p, options);
762
+ return segmentsOf(this.p, { hidden: this.hiddenRuns(), ...options });
763
+ }
764
+ /**
765
+ * True for a paragraph in a text box (`w:txbxContent`), at a `textbox:` address (CR-012 D4). Such a view
766
+ * reads as any paragraph does and is read-only (D5): every write throws `NotSupportedError`.
767
+ */
768
+ get inTextBox() {
769
+ let o = this.p.PARENT;
770
+ for (let guard = 0; o && guard < 256; guard++, o = o.PARENT) {
771
+ if (o.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent')
772
+ return true;
773
+ }
774
+ return false;
775
+ }
776
+ /**
777
+ * Refuses a write to a paragraph in a text box (CR-012 D5): a box is in each branch of its
778
+ * `mc:AlternateContent`, `wps:txbx` in the Choice and `v:textbox` in the Fallback, and nothing maps the
779
+ * one's paragraphs to the other's, so an edit could not reach every branch as CR-010's D2 has mutators do.
780
+ * And a write to a paragraph in a branch of a kept `mc:AlternateContent` at block, row or cell level
781
+ * (CR-013 D4), for the same reason. Internal: the content API's mutators call it first.
782
+ */
783
+ assertWritable() {
784
+ if (this.inTextBox) {
785
+ throw new NotSupportedError('Paragraph', 'edit', 'this paragraph is in a text box, whose paragraphs are read-only (CR-012 D5): '
786
+ + "writes into a text box would have to reach its other branch's box too, which nothing maps yet");
787
+ }
788
+ assertOutsideBranch(this.p, 'Paragraph');
789
+ }
790
+ /**
791
+ * True for a paragraph in the selected branch of a kept `mc:AlternateContent` at block, row or cell
792
+ * level, which readers see as Word does (CR-013 phase 2). Such a view is read-only (D4).
793
+ */
794
+ get inBranch() {
795
+ return inBranch(this.p);
796
+ }
797
+ /** Which of this paragraph's runs are hidden: a run's own `w:vanish`, else its styles' (CR-012 phase 4). */
798
+ hiddenRuns() {
799
+ return this.parentBody.hiddenRuns(this.p);
596
800
  }
597
801
  /**
598
802
  * Replaces the text in [start, end) with `text` and returns the range of the inserted text.
@@ -604,13 +808,313 @@ export class Paragraph {
604
808
  * says the span holds (`WHOLE` for a paragraph's whole content), so a span of no text can still
605
809
  * delete a picture (CR-002 section 48).
606
810
  */
607
- splice(start, end, text, prefer = 'before', edges) {
608
- var _a, _b, _c, _d, _e, _f;
609
- var _g;
811
+ splice(start, end, text, prefer = 'before', edges, lineBreaks) {
812
+ this.assertWritable();
813
+ // text into a block control showing its placeholder takes the placeholder's place (CR-002 section 63)
814
+ const placeholder = text === '' ? undefined : this.placeholderControl;
815
+ if (placeholder)
816
+ return placeholder.writePlaceholder(text, lineBreaks ? { lineBreaks } : {});
817
+ // every text write into a bound control writes its node (CR-002 section 63, D7), as ContentControl.insertText did
818
+ const bound = this.boundControlsAt(start, end);
819
+ const range = this.spliceWrite(start, end, text, prefer, edges, lineBreaks);
820
+ for (const control of bound)
821
+ control.syncBoundNode();
822
+ return range;
823
+ }
824
+ /**
825
+ * The controls bound by `w:dataBinding` that [start, end) is in: those holding the paragraph, and the run-level ones
826
+ * holding text there.
827
+ */
828
+ boundControlsAt(start, end, ancestorsOnly = false) {
829
+ const out = [];
830
+ const seen = new Set();
831
+ const add = (o) => {
832
+ const tn = o.TYPE_NAME;
833
+ if (tn === undefined || !SDT_VALUE_TYPES.has(tn) || seen.has(o))
834
+ return;
835
+ seen.add(o);
836
+ if (sdtProperty(o.sdtPr, 'dataBinding') === undefined)
837
+ return;
838
+ try {
839
+ out.push(controlViewOf(o, this.parentBody));
840
+ }
841
+ catch {
842
+ // not where its PARENT says: nothing to write
843
+ }
844
+ };
845
+ for (let o = this.p.PARENT; o; o = o.PARENT)
846
+ add(o);
847
+ if (ancestorsOnly || !holdsBoundRunControl(this.p))
848
+ return out;
849
+ for (const seg of this.segments({ objects: true })) {
850
+ if (seg.end < start || seg.start > end)
851
+ continue;
852
+ for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT)
853
+ add(o);
854
+ }
855
+ return out;
856
+ }
857
+ /**
858
+ * @internal Text written at the paragraph's End by Office JS's routes to it: `insertText('End')`, its End range at any
859
+ * location, its Content range's End and After, and so a body's or a cell's `insertText('End')` (CR-002 sections 63.24
860
+ * to 63.27; checks 69 and 70). As Word writes it (D12): after everything the paragraph holds, into none of it - a
861
+ * run-level control, a link, a revision, a field, a bookmark's or a comment's markers, a text box - in a run of its
862
+ * own, or the last run extended. The text takes the last item's run properties where that item is a run holding text,
863
+ * so after a hidden run it is hidden and its range reads nothing (D13); otherwise it has none, and with `plain` (the End
864
+ * range's After, D14) none either. Word's proofing marks, `w:proofErr`, are no item (a choice: Word redoes them).
865
+ * Tracked, the run is a `w:ins`, or joins this author's own `w:ins` where one ends the paragraph. A line break makes
866
+ * paragraphs, as `splice` does (CR-002 section 60 E1); text so broken after a hidden run is written visible (a choice,
867
+ * unmeasured).
868
+ */
869
+ writeAtEnd(text, plain = false) {
870
+ var _a, _b;
871
+ var _c;
872
+ this.assertWritable();
873
+ const placeholder = this.placeholderControl;
874
+ if (placeholder)
875
+ return placeholder.writePlaceholder(text);
876
+ const length = this.text.length;
877
+ text = dropExcluded(text);
878
+ if (text === '')
879
+ return new Range(this, length, length);
880
+ let mode = 'paragraphs';
881
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
882
+ if (lines)
883
+ mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
884
+ text = forWriter(text, mode);
885
+ const broken = mode === 'paragraphs' && /[\r\n]/.test(text) ? linesOf(text) : undefined;
886
+ const joined = broken ? broken.join('') : text;
887
+ if (joined === '' && !broken)
888
+ return new Range(this, length, length);
889
+ const bound = this.boundControlsAt(length, length, true);
890
+ const content = ((_a = (_c = this.p).content) !== null && _a !== void 0 ? _a : (_c.content = []));
891
+ let at = content.length;
892
+ while (at > 0 && typeNameOf(content[at - 1]) === 'org_docx4j_wml.ProofErr')
893
+ at--;
894
+ const last = at > 0 ? content[at - 1] : undefined;
895
+ const textRun = (item) => {
896
+ var _a;
897
+ if (!item || typeNameOf(item) !== 'org_docx4j_wml.R')
898
+ return undefined;
899
+ const run = item.value;
900
+ return ((_a = run.content) !== null && _a !== void 0 ? _a : []).some((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 't'; }) ? run : undefined;
901
+ };
902
+ const hidden = this.hiddenRuns();
903
+ // the run whose properties the text takes, and which it may extend: a run holding text, unless `plain`
904
+ let source = plain ? undefined : textRun(last);
905
+ if (source && broken && hidden(source))
906
+ source = undefined;
907
+ const items = officeItems(joined);
908
+ const tracker = this.changeTracker;
909
+ let written;
910
+ const own = tracker && last && typeNameOf(last) === 'org_docx4j_wml.RunIns' && tracker.ownInsertion({ kind: 'ins', value: last.value });
911
+ if (items.length === 0) {
912
+ // nothing but line breaks: only the paragraphs they make
913
+ }
914
+ else if (own) {
915
+ // this author's own insertion ends the paragraph: the text joins it, after its last run, in that run's look
916
+ const value = last.value;
917
+ const holder = (_b = runItemsOf(value)) !== null && _b !== void 0 ? _b : (value.customXmlOrSmartTagOrSdt = []);
918
+ const inner = plain ? undefined : textRun(holder[holder.length - 1]);
919
+ written = this.appendTo(holder, inner, items, value);
920
+ }
921
+ else if (tracker) {
922
+ const ins = tracker.ins([runOf(items, copyRPr(source === null || source === void 0 ? void 0 : source.rPr))]);
923
+ content.splice(at, 0, ins);
924
+ linkParents(ins, this.p);
925
+ written = runsOf(ins.value)[0].value;
926
+ }
927
+ else {
928
+ written = this.appendTo(content, source, items, this.p, at);
929
+ }
930
+ for (const control of bound)
931
+ control.syncBoundNode();
932
+ if (broken) {
933
+ const offsets = [];
934
+ let end = length;
935
+ for (const line of broken.slice(0, -1))
936
+ offsets.push(end += line.length);
937
+ const { first, last: tail } = breakParagraphAt(this, offsets);
938
+ return new Range(first, length, broken[broken.length - 1].length, false, false, tail);
939
+ }
940
+ return written && hidden(written) ? new Range(this, length, length) : new Range(this, length, length + joined.length);
941
+ }
942
+ /**
943
+ * `items` put at `at` in `list` (its end by default): onto the end of `run`, which is in `list` right before `at`,
944
+ * merging into its last `w:t`, or as a new run in its properties where `run` is undefined. Returns the run written.
945
+ */
946
+ appendTo(list, run, items, parent, at = list.length) {
947
+ var _a, _b, _c, _d, _e;
948
+ if (run) {
949
+ const own = ((_a = run.content) !== null && _a !== void 0 ? _a : (run.content = []));
950
+ const tail = own[own.length - 1];
951
+ const head = items[0];
952
+ if (((_b = tail === null || tail === void 0 ? void 0 : tail.name) === null || _b === void 0 ? void 0 : _b.localPart) === 't' && ((_c = head === null || head === void 0 ? void 0 : head.name) === null || _c === void 0 ? void 0 : _c.localPart) === 't') {
953
+ setText(tail.value, ((_d = tail.value.value) !== null && _d !== void 0 ? _d : '') + ((_e = head.value.value) !== null && _e !== void 0 ? _e : ''));
954
+ items = items.slice(1);
955
+ }
956
+ own.push(...items);
957
+ linkParents(items, run);
958
+ return run;
959
+ }
960
+ const added = runOf(items);
961
+ list.splice(at, 0, added);
962
+ linkParents(added, parent);
963
+ return added.value;
964
+ }
965
+ /** `splice`'s write. */
966
+ spliceWrite(start, end, text, prefer, edges, lineBreaks) {
967
+ // Office JS's characters written back (CR-002 section 60): the characters XML 1.0 excludes go (E3), a line feed or
968
+ // carriage return is a paragraph break (E1) unless the writer or the control takes it otherwise, and every
969
+ // character that is an element is written as it (D1)
970
+ text = dropExcluded(text);
971
+ if (text === '')
972
+ return this.spliceText(start, end, text, prefer, edges);
973
+ const from = Math.max(0, Math.min(start, this.text.length));
974
+ const landing = this.landingSegment(from, prefer, end > from);
975
+ const runControl = this.runControlOf(landing);
976
+ let mode = lineBreaks !== null && lineBreaks !== void 0 ? lineBreaks : 'paragraphs';
977
+ if (runControl) {
978
+ text = textForRunControl(runControl, text); // F4 and F3
979
+ }
980
+ else if (lineBreaks === undefined) {
981
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
982
+ if (lines)
983
+ mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
984
+ }
985
+ text = forWriter(text, mode);
986
+ if (mode === 'paragraphs' && /[\r\n]/.test(text))
987
+ return this.spliceLines(start, end, linesOf(text), prefer, edges, landing);
988
+ const range = this.spliceText(start, end, text, prefer, edges);
989
+ this.writeElements(range.start, range.start + text.length);
990
+ return range;
991
+ }
992
+ /**
993
+ * Text of several lines put in place of [start, end) as Word's insertText puts it (CR-002 section 60 E1): the lines'
994
+ * text, then a paragraph break after each line but the last, the paragraphs keeping this one's properties; tracked,
995
+ * each new mark inserted. Refused before anything changes where a paragraph can't be split there.
996
+ */
997
+ spliceLines(start, end, lines, prefer, edges, landing) {
998
+ this.assertBreakable(landing);
999
+ const joined = lines.join('');
1000
+ const range = this.spliceText(start, end, joined, prefer, edges);
1001
+ const from = range.start;
1002
+ this.writeElements(from, from + joined.length);
1003
+ const offsets = [];
1004
+ let at = from;
1005
+ for (const line of lines.slice(0, -1))
1006
+ offsets.push(at += line.length);
1007
+ const { first, last } = breakParagraphAt(this, offsets);
1008
+ return new Range(first, from, lines[lines.length - 1].length, false, false, last);
1009
+ }
1010
+ /** @internal Refuses, before anything changes, a paragraph break at `at` where none can be made (E1; `Range`'s Replace across paragraphs). */
1011
+ assertBreakableAt(at, prefer) {
1012
+ this.assertBreakable(this.landingSegment(at, prefer));
1013
+ }
1014
+ /**
1015
+ * The segment text put at `at` goes into, as `spliceText` chooses it: a replacement's, the one holding the first
1016
+ * replaced character; else the one containing `at`, else the nearer neighbour.
1017
+ */
1018
+ landingSegment(at, prefer, replacing = false) {
1019
+ const segs = this.segments();
1020
+ if (replacing) {
1021
+ const first = segs.find((s) => s.start <= at && at < s.end);
1022
+ if (first)
1023
+ return first;
1024
+ }
1025
+ const containing = segs.find((s) => s.editable && s.start < at && at < s.end);
1026
+ const ending = segs.find((s) => s.editable && s.end === at);
1027
+ const starting = segs.find((s) => s.editable && s.start === at);
1028
+ return containing !== null && containing !== void 0 ? containing : (prefer === 'after' ? starting !== null && starting !== void 0 ? starting : ending : ending !== null && ending !== void 0 ? ending : starting);
1029
+ }
1030
+ /** The run-level content control a segment's run is in, the innermost, as a view; undefined for none. */
1031
+ runControlOf(seg) {
1032
+ var _a;
1033
+ for (let o = (_a = seg === null || seg === void 0 ? void 0 : seg.run) === null || _a === void 0 ? void 0 : _a.PARENT; o && o !== this.p; o = o.PARENT) {
1034
+ if (o.TYPE_NAME === 'org_docx4j_wml.SdtRun')
1035
+ return controlViewOf(o, this.parentBody);
1036
+ }
1037
+ return undefined;
1038
+ }
1039
+ /** The block-level content control holding this paragraph directly, as a view; undefined for none. */
1040
+ get blockControl() {
1041
+ const content = this.p.PARENT;
1042
+ const sdt = (content === null || content === void 0 ? void 0 : content.TYPE_NAME) === 'org_docx4j_wml.SdtContentBlock' ? content.PARENT : undefined;
1043
+ return (sdt === null || sdt === void 0 ? void 0 : sdt.TYPE_NAME) === 'org_docx4j_wml.SdtBlock' ? controlViewOf(sdt, this.parentBody) : undefined;
1044
+ }
1045
+ /**
1046
+ * Refuses, before anything changes, a paragraph break where the text lands inside something a paragraph can't be
1047
+ * split in: a content control, an equation, a kept mc:AlternateContent (CR-002 section 60 E1, as `splitParagraph`
1048
+ * refuses one). A field's result is split: a complex field's runs across the paragraphs, a `w:fldSimple` made a
1049
+ * complex field first (section 62.2 D1).
1050
+ */
1051
+ assertBreakable(seg) {
1052
+ var _a;
1053
+ if (!seg)
1054
+ return;
1055
+ const refuse = (what) => {
1056
+ throw new NotSupportedError('Paragraph', 'insertText', `a line feed or carriage return is a paragraph break (CR-002 section 60, E1), `
1057
+ + `and this text goes inside ${what}, which can't be cut in two`);
1058
+ };
1059
+ if (seg.alternate || seg.math)
1060
+ refuse('an equation or an mc:AlternateContent');
1061
+ for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT) {
1062
+ const tn = (_a = o.TYPE_NAME) !== null && _a !== void 0 ? _a : '';
1063
+ if (!BREAKABLE.has(tn))
1064
+ refuse(`a ${tn.replace('org_docx4j_wml.', '')}`);
1065
+ }
1066
+ }
1067
+ /**
1068
+ * The characters of [from, to) that are elements written as the elements they read as (CR-002 section 60 D1), each
1069
+ * in its run where it stood: a w:t holding one is cut around it. Characters outside the span are left as they are.
1070
+ */
1071
+ writeElements(from, to) {
1072
+ var _a;
1073
+ if (to <= from)
1074
+ return;
1075
+ for (const seg of this.segments().reverse()) {
1076
+ if (!seg.editable || seg.end <= from || seg.start >= to)
1077
+ continue;
1078
+ const value = seg.text;
1079
+ const a = Math.max(0, from - seg.start);
1080
+ const b = Math.min(value.length, to - seg.start);
1081
+ const inside = value.substring(a, b);
1082
+ if (!holdsElementCharacters(inside))
1083
+ continue;
1084
+ const items = [
1085
+ ...(a > 0 ? [textItem(value.substring(0, a))] : []),
1086
+ ...officeItems(inside),
1087
+ ...(b < value.length ? [textItem(value.substring(b))] : []),
1088
+ ];
1089
+ const parent = (_a = seg.item.value.PARENT) !== null && _a !== void 0 ? _a : seg.run;
1090
+ seg.owner.splice(seg.index, 1, ...items);
1091
+ linkParents(items, parent);
1092
+ }
1093
+ }
1094
+ /**
1095
+ * The first run's properties, for text with no run beside it to take its look from; none where that run is hidden,
1096
+ * so that text put into a paragraph whose every run is hidden can be seen (CR-002 section 60: the text was hidden
1097
+ * with it, so read as nothing, and its characters escaped the map).
1098
+ */
1099
+ visibleFirstRunRPr() {
1100
+ var _a;
1101
+ const first = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value;
1102
+ return first && !this.hiddenRuns()(first) ? first.rPr : undefined;
1103
+ }
1104
+ /** `splice` before section 60: the text put in as it is, as one stretch of a w:t's text. */
1105
+ spliceText(start, end, text, prefer = 'before', edges) {
1106
+ var _a, _b, _c, _d, _e;
1107
+ var _f;
610
1108
  const length = this.text.length;
611
1109
  start = Math.max(0, Math.min(start, length));
612
1110
  end = Math.max(start, Math.min(end, length));
613
1111
  const tracker = this.changeTracker;
1112
+ this.assertPlaceable(start, end, tracker === undefined ? false : text.length > 0 ? 'replace' : 'delete');
1113
+ // a run-level control showing its placeholder that the span holds whole goes with it, as Word takes one; tracked,
1114
+ // `deleteText` marks it deleted (CR-002 section 63, D3; check 63 cases 19, 20 and 26)
1115
+ if (!tracker && (end > start || edges))
1116
+ for (const control of this.placeholderControlsIn(start, end, edges))
1117
+ control.takeWhole();
614
1118
  if (tracker) {
615
1119
  // a replacement is the w:del first and the w:ins after it, as Word writes one
616
1120
  const deletions = end > start || edges ? this.deleteText(tracker, start, end, edges) : [];
@@ -621,14 +1125,16 @@ export class Paragraph {
621
1125
  let segs = this.segments();
622
1126
  let inserted = false;
623
1127
  const objects = this.segments({ objects: true }).filter((s) => s.object && objectInSpan(s.start, start, end, edges));
1128
+ // a comment's reference in the span goes with the text, and its comment with it (CR-014 section 12.6)
1129
+ const references = end > start || edges ? this.commentReferencesIn(start, end, edges) : [];
624
1130
  // A replacement that begins inside a revision is a deletion and then the new text typed there, so
625
1131
  // the new text is plain and splits the revision, as typed text is (test/README.md check 21), rather
626
1132
  // than joining the revision it replaced part of.
627
1133
  if (end > start && text.length > 0 && ((_a = segs.find((s) => s.start <= start && start < s.end)) === null || _a === void 0 ? void 0 : _a.revision)) {
628
- this.splice(start, end, '', prefer, edges);
629
- return this.splice(start, start, text);
1134
+ this.spliceText(start, end, '', prefer, edges);
1135
+ return this.spliceText(start, start, text);
630
1136
  }
631
- if (end > start || objects.length > 0) {
1137
+ if (end > start || objects.length > 0 || references.length > 0) {
632
1138
  // Replace: the new text takes the place (and formatting) of the first replaced character, as Word does.
633
1139
  const first = segs.find((s) => s.start <= start && start < s.end);
634
1140
  const toRemove = [];
@@ -656,6 +1162,13 @@ export class Paragraph {
656
1162
  if (i >= 0)
657
1163
  seg.owner.splice(i, 1);
658
1164
  }
1165
+ removeEmptiedAlternates([...toRemove, ...objects]);
1166
+ for (const { run, owner, id } of references) {
1167
+ const i = owner.indexOf(run);
1168
+ if (i >= 0)
1169
+ owner.splice(i, 1);
1170
+ this.queueCommentRemoval(id);
1171
+ }
659
1172
  this.releaseRelationships(objects.map((seg) => seg.item));
660
1173
  if (inserted && text.length > 0) {
661
1174
  this.removeEmptyRuns();
@@ -696,7 +1209,7 @@ export class Paragraph {
696
1209
  const before = [...segs].reverse().find((s) => s.end <= start);
697
1210
  const after = segs.find((s) => s.start >= start);
698
1211
  const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
699
- const firstRun = (_e = runsOf(this.p)[0]) === null || _e === void 0 ? void 0 : _e.value.rPr;
1212
+ const firstRun = this.visibleFirstRunRPr();
700
1213
  const rPr = (neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) ? deepCopy(neighbour.run.rPr) : firstRun ? deepCopy(firstRun) : undefined;
701
1214
  const run = runOf([textItem(text)], rPr);
702
1215
  if (neighbour === before && before)
@@ -704,7 +1217,7 @@ export class Paragraph {
704
1217
  else if (after)
705
1218
  after.runOwner.splice(after.runIndex, 0, run);
706
1219
  else {
707
- const content = ((_f = (_g = this.p).content) !== null && _f !== void 0 ? _f : (_g.content = []));
1220
+ const content = ((_e = (_f = this.p).content) !== null && _e !== void 0 ? _e : (_f.content = []));
708
1221
  if (start === 0)
709
1222
  content.unshift(run);
710
1223
  else
@@ -722,8 +1235,10 @@ export class Paragraph {
722
1235
  insertItemsAt(offset, items) {
723
1236
  var _a, _b, _c, _d;
724
1237
  var _e;
1238
+ this.assertWritable();
725
1239
  if (items.length === 0)
726
1240
  return;
1241
+ this.assertPlaceable(offset, offset, false);
727
1242
  const tracker = this.changeTracker;
728
1243
  if (tracker) {
729
1244
  // With tracking on, what goes in is an insertion: its runs in w:ins, in a link, a control or a
@@ -753,18 +1268,46 @@ export class Paragraph {
753
1268
  /** Splits the run at a text offset so that [offset, ...) begins a run; returns nothing when the offset is already a boundary. */
754
1269
  splitAt(offset) {
755
1270
  var _a;
1271
+ this.assertWritable();
1272
+ this.assertPlaceable(offset, offset, false);
756
1273
  const seg = this.segments().find((s) => s.editable && s.start < offset && offset < s.end);
757
1274
  if (!seg)
758
1275
  return;
759
1276
  const at = offset - seg.start;
760
1277
  const t = seg.item.value;
761
1278
  const head = seg.text.substring(0, at);
762
- const tail = seg.text.substring(at);
1279
+ const tail = textItem(seg.text.substring(at));
763
1280
  setText(t, head);
764
- const rest = seg.owner.splice(seg.index + 1);
765
- const second = runOf([textItem(tail), ...rest], seg.run.rPr ? deepCopy(seg.run.rPr) : undefined);
1281
+ seg.owner.splice(seg.index + 1, 0, tail);
1282
+ linkParents(tail, (_a = t.PARENT) !== null && _a !== void 0 ? _a : seg.run);
1283
+ this.splitRunAtItem(seg, seg.owner, seg.index + 1);
1284
+ }
1285
+ /**
1286
+ * Splits `seg`'s run so that the items of `owner` from `index` on begin a run of their own, behind it.
1287
+ * `owner` is the run's content, or the selected branch of a run-level `mc:AlternateContent` in it
1288
+ * (CR-012 section 12.2): a run holds the element whole, so the branch splits with it, as the editor's
1289
+ * ED-009 section 3.4 has it. The first piece keeps the element, its other branches as they were; the
1290
+ * second gets an element of its own, the same branches in the same order with the same `Requires`, the
1291
+ * selected one holding the rest of its items and the others empty, so that a reader taking any branch
1292
+ * takes the same one in both. The run's items after the element go with the second piece.
1293
+ */
1294
+ splitRunAtItem(seg, owner, index) {
1295
+ var _a, _b;
1296
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && owner === seg.alternate.branch ? seg.alternate : undefined;
1297
+ let moved;
1298
+ if (alternate) {
1299
+ const piece = alternatePiece(alternate, owner.splice(index));
1300
+ const at = alternate.owner.indexOf(alternate.element);
1301
+ moved = [piece, ...alternate.owner.splice(at + 1)];
1302
+ }
1303
+ else {
1304
+ moved = owner.splice(index);
1305
+ }
1306
+ if (moved.length === 0)
1307
+ return;
1308
+ const second = runOf(moved, seg.run.rPr ? deepCopy(seg.run.rPr) : undefined);
766
1309
  seg.runOwner.splice(seg.runIndex + 1, 0, second);
767
- linkParents(second, (_a = seg.run.PARENT) !== null && _a !== void 0 ? _a : this.p);
1310
+ linkParents(second, (_b = seg.run.PARENT) !== null && _b !== void 0 ? _b : this.p);
768
1311
  }
769
1312
  // --- lists (CR-002 phase H) ---
770
1313
  /**
@@ -801,14 +1344,17 @@ export class Paragraph {
801
1344
  * unmarshalled (CR-002 section 17).
802
1345
  */
803
1346
  startNewList(options) {
1347
+ this.assertWritable();
804
1348
  return startNewList(this, options);
805
1349
  }
806
1350
  /** Office JS `paragraph.attachToList(listId, level)`: this paragraph joins that list. */
807
1351
  attachToList(listId, level = 0) {
1352
+ this.assertWritable();
808
1353
  return attachToList(this, listId, level);
809
1354
  }
810
1355
  /** Office JS `paragraph.detachFromList()`: this paragraph leaves its list. */
811
1356
  detachFromList() {
1357
+ this.assertWritable();
812
1358
  detachFromList(this);
813
1359
  }
814
1360
  /**
@@ -816,6 +1362,7 @@ export class Paragraph {
816
1362
  * `w:abstractNum` with a `w:startOverride` (extension; `List.restart()` is the verb).
817
1363
  */
818
1364
  restartList() {
1365
+ this.assertWritable();
819
1366
  const list = this.list;
820
1367
  const level = this.listItem.level;
821
1368
  const restarted = list.restart();
@@ -829,6 +1376,7 @@ export class Paragraph {
829
1376
  }
830
1377
  /** Comments the whole paragraph (sugar over `getRange().insertComment`). */
831
1378
  async insertComment(content, options) {
1379
+ this.assertWritable();
832
1380
  return commentApi().insertComment(this.getRange(), content, options);
833
1381
  }
834
1382
  // --- content controls (CR-002 phase E) ---
@@ -839,18 +1387,32 @@ export class Paragraph {
839
1387
  */
840
1388
  insertContentControl(kind) {
841
1389
  var _a;
1390
+ this.assertWritable();
842
1391
  const at = this.index;
843
1392
  if (at < 0)
844
1393
  throw new Docx4JException('This paragraph is not in its container any more');
1394
+ // the paragraph's parent read before the builder makes the control its parent: read after, it was the
1395
+ // control's own content, and the control's PARENT pointed into itself (found in CR-012 phase 4)
1396
+ const parent = (_a = this.p.PARENT) !== null && _a !== void 0 ? _a : this.parentBody.container;
1397
+ // a bound block control holds one paragraph (CR-002 section 61): checked before sdtOf, which takes the paragraph
1398
+ assertControlHolds(this.parentBody, parent, this.container.filter((e) => e !== this.element), [BLOCK_CONTROL], undefined, ['Paragraph', 'insertContentControl']);
1399
+ const empty = this.segments({ objects: true }).length === 0;
845
1400
  const sdt = sdtOf([this.element], { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this.parentBody)), form: 'block' });
846
1401
  this.container.splice(at, 1, sdt);
847
- linkParents(sdt, (_a = this.p.PARENT) !== null && _a !== void 0 ? _a : this.parentBody.container);
1402
+ linkParents(sdt, parent);
848
1403
  declareSdtExtensions(this.parentBody, sdt);
849
1404
  // under tracking the control itself is the insertion, the paragraph what it was (check 30)
850
1405
  const tracker = this.changeTracker;
851
1406
  if (tracker)
852
1407
  markHolderInserted(tracker, sdt, this.container, () => { });
853
- return new ContentControl(sdt, this.container, this.parentBody);
1408
+ const control = new ContentControl(sdt, this.container, this.parentBody);
1409
+ // around an empty paragraph, a control showing Word's default placeholder, as Word's (CR-002 section 63, D8; check 64);
1410
+ // a picture control Word's form, the flag and the placeholder picture (CR-005 section 10, D2; docx4j's check 71 case 8)
1411
+ if (empty && showsPlaceholderWhenMade(kind))
1412
+ control.showPlaceholder();
1413
+ else if (empty && kind === 'Picture')
1414
+ showPicturePlaceholder(control);
1415
+ return control;
854
1416
  }
855
1417
  removeEmptyRuns() {
856
1418
  var _a;
@@ -862,6 +1424,16 @@ export class Paragraph {
862
1424
  if (!v.content || v.content.length === 0)
863
1425
  items.splice(i, 1);
864
1426
  }
1427
+ else if (isAlternateContent(el)) {
1428
+ // among the runs: a selected branch an edit has emptied takes the element, every branch with
1429
+ // it, as a deletion of all of a run-level element's text does (CR-012 section 12.2 item 4)
1430
+ const branch = selectedBranchOf(el.value);
1431
+ if (!branch || branch.length === 0)
1432
+ continue;
1433
+ prune(branch);
1434
+ if (branch.length === 0)
1435
+ items.splice(i, 1);
1436
+ }
865
1437
  else if (v.TYPE_NAME !== 'org_docx4j_wml.P') {
866
1438
  const nested = runItemsOf(v);
867
1439
  if (nested) {
@@ -881,16 +1453,28 @@ export class Paragraph {
881
1453
  * turned into `w:delText`, and each run of consecutive ones moved into one `w:del`. Text this
882
1454
  * author had inserted is simply removed, as Word does, rather than nested in a `w:del`.
883
1455
  * The pictures in the span (`splice`'s `edges`) are deleted with the text, each in a run of its own
884
- * where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48).
1456
+ * where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48). An equation is deleted where
1457
+ * it stands, in Word's form, a `w:del` in each math run and each structure's `m:ctrlPr` (`markEquationDeleted`;
1458
+ * section 62.2 D2), the runs either side in `w:del`s of their own; one holding a revision of its own goes into the
1459
+ * `w:del` whole, as every equation did before.
885
1460
  * Returns the `w:del` elements it made, in document order.
886
1461
  */
887
1462
  deleteText(tracker, start, end, edges) {
888
- var _a, _b, _c, _d, _e;
1463
+ var _a, _b, _c, _d, _e, _f, _g;
1464
+ // a run-level control showing its placeholder that the span holds whole is marked deleted whole, in Word's markers,
1465
+ // its placeholder gone unmarked (CR-002 section 63, D3; check 63's run 2, cases 45, 46 and 52)
1466
+ for (const control of this.placeholderControlsIn(start, end, edges))
1467
+ control.takeWhole();
889
1468
  const targets = this.isolate(start, end, edges);
890
1469
  for (const seg of targets)
891
1470
  tracker.assertEditable(seg.revision);
892
1471
  const list = [];
1472
+ const equations = [];
893
1473
  for (const seg of targets) {
1474
+ if (seg.math && !tracker.ownInsertion(seg.revision) && equationRevisionIn(seg.item.value) === undefined) {
1475
+ equations.push(seg.item.value);
1476
+ continue;
1477
+ }
894
1478
  const run = seg.runOwner[seg.runIndex];
895
1479
  if (list.length > 0 && list[list.length - 1].run === run)
896
1480
  continue;
@@ -899,6 +1483,22 @@ export class Paragraph {
899
1483
  target.revision = seg.revision;
900
1484
  list.push(target);
901
1485
  }
1486
+ // a comment's reference in the span goes into the w:del with the text, where it stands among the runs, as Word
1487
+ // puts it (check 54 case 28; CR-014 section 12.6); accepting the deletion then takes the comment
1488
+ const references = this.commentReferencesIn(start, end, edges);
1489
+ if (references.length > 0) {
1490
+ const order = runOrderOf(this.p);
1491
+ const revisions = new Map(targets.filter((seg) => seg.revision).map((seg) => [seg.runOwner, seg.revision]));
1492
+ for (const { run, owner } of references) {
1493
+ const revision = (_a = revisions.get(owner)) !== null && _a !== void 0 ? _a : revisionHolding(run);
1494
+ const target = { run, owner, own: tracker.ownInsertion(revision) };
1495
+ if (revision)
1496
+ target.revision = revision;
1497
+ const key = (_b = order.get(run)) !== null && _b !== void 0 ? _b : Number.NaN;
1498
+ const at = list.findIndex((t) => { var _a; return ((_a = order.get(t.run)) !== null && _a !== void 0 ? _a : Number.NaN) > key; });
1499
+ list.splice(at < 0 ? list.length : at, 0, target);
1500
+ }
1501
+ }
902
1502
  // groups of consecutive runs in the same array, so that one w:del holds them all
903
1503
  const groups = [];
904
1504
  for (const t of list) {
@@ -910,6 +1510,8 @@ export class Paragraph {
910
1510
  else
911
1511
  groups.push([t]);
912
1512
  }
1513
+ for (const equation of equations)
1514
+ markEquationDeleted(tracker, equation);
913
1515
  const made = [];
914
1516
  for (let g = groups.length - 1; g >= 0; g--) {
915
1517
  const group = groups[g];
@@ -918,8 +1520,11 @@ export class Paragraph {
918
1520
  const runs = owner.splice(at, group.length);
919
1521
  if (group[0].own) {
920
1522
  // taking back our own insertion: the runs go, and an emptied w:ins with them; a picture's
921
- // image waits for the save, as `InlinePicture.delete` leaves it (section 47)
1523
+ // image waits for the save, as `InlinePicture.delete` leaves it (section 47); a comment whose
1524
+ // reference goes with them goes too
922
1525
  this.releaseRelationships(runs);
1526
+ for (const id of commentReferenceIds(runs))
1527
+ this.queueCommentRemoval(id);
923
1528
  const revision = group[0].revision;
924
1529
  if (revision && revision.items.length === 0) {
925
1530
  const i = revision.owner.indexOf(revision.element);
@@ -932,15 +1537,60 @@ export class Paragraph {
932
1537
  toDeletedText(run.value);
933
1538
  const del = tracker.del(runs);
934
1539
  owner.splice(at, 0, del);
935
- linkParents(del, ((_c = (_b = (_a = group[0].revision) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : group[0].run.value.PARENT) !== null && _c !== void 0 ? _c : this.p));
1540
+ linkParents(del, ((_e = (_d = (_c = group[0].revision) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : group[0].run.value.PARENT) !== null && _e !== void 0 ? _e : this.p));
936
1541
  const revision = group[0].revision;
937
- const anchor = { owner, element: del, parent: ((_e = (_d = revision === null || revision === void 0 ? void 0 : revision.value) !== null && _d !== void 0 ? _d : del.value.PARENT) !== null && _e !== void 0 ? _e : this.p) };
1542
+ const anchor = { owner, element: del, parent: ((_g = (_f = revision === null || revision === void 0 ? void 0 : revision.value) !== null && _f !== void 0 ? _f : del.value.PARENT) !== null && _g !== void 0 ? _g : this.p) };
938
1543
  if (revision && revision.items === owner)
939
1544
  anchor.revision = revision;
940
1545
  made.unshift(anchor);
941
1546
  }
942
1547
  return made;
943
1548
  }
1549
+ /**
1550
+ * The run-level controls showing their placeholder that [start, end) holds whole: where the walk passes one strictly
1551
+ * inside the span, or at an edge `edges` says it holds. A placeholder is no text, so such a control has no width
1552
+ * (CR-002 section 63, D3).
1553
+ */
1554
+ placeholderControlsIn(start, end, edges) {
1555
+ const found = [];
1556
+ this.segments({ visit: (item, at) => {
1557
+ var _a;
1558
+ if (typeNameOf(item) !== 'org_docx4j_wml.SdtRun' || !objectInSpan(at, start, end, edges))
1559
+ return;
1560
+ const sdtPr = item.value.sdtPr;
1561
+ const flag = sdtProperty(sdtPr, 'showingPlcHdr');
1562
+ if (flag !== undefined && ((_a = flag.value) === null || _a === void 0 ? void 0 : _a.val) !== false)
1563
+ found.push(item.value);
1564
+ } });
1565
+ return found.map((sdt) => controlViewOf(sdt, this.parentBody));
1566
+ }
1567
+ /**
1568
+ * The runs of comments' references in [start, end): each a run holding nothing but a reference, where the walk
1569
+ * passes it, strictly inside the span or at an edge `edges` says it holds. A reference has no text, so a
1570
+ * deletion's segments never held it, and a comment whose reference a deletion took stayed. Word's deletion takes
1571
+ * it with the text (check 54 cases 27 and 28; CR-014 section 12.6).
1572
+ */
1573
+ commentReferencesIn(start, end, edges) {
1574
+ const out = [];
1575
+ this.segments({ visit: (item, at, _owner, run) => {
1576
+ var _a, _b;
1577
+ if (((_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart) !== 'commentReference' || !run || !objectInSpan(at, start, end, edges))
1578
+ return;
1579
+ if (out.some((r) => r.run === run.element))
1580
+ return;
1581
+ if (!((_b = run.element.value.content) !== null && _b !== void 0 ? _b : []).every((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference'; }))
1582
+ return;
1583
+ out.push({ run: run.element, owner: run.owner, id: Number(item.value.id) });
1584
+ } });
1585
+ return out;
1586
+ }
1587
+ /** A comment queued to go when the package is saved, its markers, replies and entries with it, as a resolution queues one. */
1588
+ queueCommentRemoval(id) {
1589
+ var _a, _b;
1590
+ if (!Number.isInteger(id))
1591
+ return;
1592
+ (_b = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.pendingCommentRemovals) === null || _b === void 0 ? void 0 : _b.add(id);
1593
+ }
944
1594
  /**
945
1595
  * Splits the revision that holds the text at `at` into two - the second half a copy of its
946
1596
  * attributes under a new `w:id` - and puts `item` between them. Word splits a revision so when
@@ -1030,7 +1680,7 @@ export class Paragraph {
1030
1680
  * of a replacement, which the insertion must follow.
1031
1681
  */
1032
1682
  insertTracked(tracker, at, text, anchor, prefer = 'before') {
1033
- var _a, _b, _c;
1683
+ var _a, _b;
1034
1684
  if (anchor) {
1035
1685
  const rPr = copyRPr((_a = runsOf(anchor.element.value, { view: 'original' })[0]) === null || _a === void 0 ? void 0 : _a.value.rPr);
1036
1686
  const ins = tracker.ins([runOf([textItem(text)], rPr)]);
@@ -1066,7 +1716,7 @@ export class Paragraph {
1066
1716
  }
1067
1717
  const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
1068
1718
  tracker.assertEditable(neighbour === null || neighbour === void 0 ? void 0 : neighbour.revision);
1069
- const rPr = copyRPr((_b = neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) !== null && _b !== void 0 ? _b : (_c = runsOf(this.p)[0]) === null || _c === void 0 ? void 0 : _c.value.rPr);
1719
+ const rPr = copyRPr((_b = neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) !== null && _b !== void 0 ? _b : this.visibleFirstRunRPr());
1070
1720
  const ins = tracker.ins([runOf([textItem(text)], rPr)]);
1071
1721
  this.placeInsertion(at, [ins], before, after, () => tracker.nextId(), prefer);
1072
1722
  }
@@ -1106,6 +1756,13 @@ export class Paragraph {
1106
1756
  linkParents(items, this.p);
1107
1757
  }
1108
1758
  }
1759
+ /**
1760
+ * Splits runs so that every run holding text in [start, end) holds nothing else (internal: what a
1761
+ * `Range.font` write formats). A kept `mc:AlternateContent`'s branch splits with its run (section 12.2).
1762
+ */
1763
+ splitRunsAround(start, end) {
1764
+ this.isolate(start, end);
1765
+ }
1109
1766
  /**
1110
1767
  * Splits runs so that every run holding text in [start, end) holds nothing else, and returns
1111
1768
  * the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
@@ -1125,16 +1782,16 @@ export class Paragraph {
1125
1782
  if (seen.has(seg.run))
1126
1783
  continue;
1127
1784
  seen.add(seg.run);
1785
+ if (seg.math)
1786
+ continue; // in no run: taken whole or refused
1128
1787
  const ofRun = inside.filter((s) => s.run === seg.run);
1129
1788
  const first = ofRun[0];
1130
1789
  const last = ofRun[ofRun.length - 1];
1131
- if (first.index > 0) {
1132
- this.splitRunBefore(first);
1790
+ if (this.splitRunBefore(first)) {
1133
1791
  split = true;
1134
1792
  break;
1135
1793
  }
1136
- if (last.index < last.owner.length - 1) {
1137
- this.splitRunAfter(last);
1794
+ if (this.splitRunAfter(last)) {
1138
1795
  split = true;
1139
1796
  break;
1140
1797
  }
@@ -1144,23 +1801,58 @@ export class Paragraph {
1144
1801
  }
1145
1802
  return this.segments({ objects: true }).filter(inSpan);
1146
1803
  }
1147
- /** Moves the items before `seg` into a run of their own, in front of `seg`'s run. */
1804
+ /**
1805
+ * Refuses an edit that would cut an equation (CR-012 phase 4). Its text is one segment, Word's drawing of
1806
+ * its math runs (`mathTextOf`), so an edit may take it whole but not part of it, nor insert into it. Under change
1807
+ * tracking a deletion takes it whole in Word's form (`deleteText`, CR-002 section 62.2 D2, check 57), and a
1808
+ * replacement doesn't take it at all: where Word puts the new text beside a deleted equation isn't measured. Since
1809
+ * phase 4 every other offset is a place in the runs: `text` is the segments' text, where before it was the objects
1810
+ * package's `textOf` and section 12.4 refused an offset past a kept `mc:AlternateContent` among the runs.
1811
+ */
1812
+ assertPlaceable(start, end, tracked) {
1813
+ for (const seg of this.segments()) {
1814
+ if (!seg.math)
1815
+ continue;
1816
+ const cuts = (seg.start < start && start < seg.end) || (seg.start < end && end < seg.end);
1817
+ const takes = start < seg.end && seg.start < end;
1818
+ if (!cuts && !(tracked === 'replace' && takes))
1819
+ continue;
1820
+ throw new NotSupportedError('Paragraph', 'edit', `[${start}, ${end}) ${cuts ? 'cuts' : 'takes'} the equation at [${seg.start}, ${seg.end})`
1821
+ + (cuts ? ': an edit takes an equation whole or not at all (CR-012 phase 4)'
1822
+ : ' under change tracking, with text in its place: a tracked replacement of an equation is not measured (CR-002 section 62.2)'));
1823
+ }
1824
+ }
1825
+ /** Moves the items before `seg` into a run of their own, in front of the rest; false where there are none. */
1148
1826
  splitRunBefore(seg) {
1149
1827
  var _a;
1150
- const head = seg.owner.splice(0, seg.index);
1151
- const first = runOf(head, copyRPr(seg.run.rPr));
1152
- seg.runOwner.splice(seg.runIndex, 0, first);
1153
- linkParents(first, (_a = seg.run.PARENT) !== null && _a !== void 0 ? _a : this.p);
1828
+ if (seg.index > 0) {
1829
+ this.splitRunAtItem(seg, seg.owner, seg.index);
1830
+ return true;
1831
+ }
1832
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
1833
+ if (!alternate)
1834
+ return false;
1835
+ const at = alternate.owner.indexOf(alternate.element);
1836
+ if (at <= 0)
1837
+ return false;
1838
+ this.splitRunAtItem(seg, alternate.owner, at);
1839
+ return true;
1154
1840
  }
1155
- /** Moves the items after `seg` into a run of their own, behind `seg`'s run. */
1841
+ /** Moves the items after `seg` into a run of their own, behind it; false where there are none. */
1156
1842
  splitRunAfter(seg) {
1157
1843
  var _a;
1158
- const tail = seg.owner.splice(seg.index + 1);
1159
- if (tail.length === 0)
1160
- return;
1161
- const rest = runOf(tail, copyRPr(seg.run.rPr));
1162
- seg.runOwner.splice(seg.runIndex + 1, 0, rest);
1163
- linkParents(rest, (_a = seg.run.PARENT) !== null && _a !== void 0 ? _a : this.p);
1844
+ if (seg.index < seg.owner.length - 1) {
1845
+ this.splitRunAtItem(seg, seg.owner, seg.index + 1);
1846
+ return true;
1847
+ }
1848
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
1849
+ if (!alternate)
1850
+ return false;
1851
+ const at = alternate.owner.indexOf(alternate.element);
1852
+ if (at < 0 || at >= alternate.owner.length - 1)
1853
+ return false;
1854
+ this.splitRunAtItem(seg, alternate.owner, at + 1);
1855
+ return true;
1164
1856
  }
1165
1857
  }
1166
1858
  export { W_NS };
@@ -1212,3 +1904,127 @@ function setText(t, value) {
1212
1904
  else
1213
1905
  delete t.space;
1214
1906
  }
1907
+ /**
1908
+ * The element a run-level `mc:AlternateContent`'s later piece is written as (CR-012 section 12.2; the
1909
+ * editor's ED-009 section 3.4, P2): its branches in order with their `Requires`, the selected one holding
1910
+ * `items` and every other one empty.
1911
+ */
1912
+ function alternatePiece(alternate, items) {
1913
+ const ac = alternate.element.value;
1914
+ const emptied = (branch) => {
1915
+ if (branch === undefined)
1916
+ return undefined;
1917
+ const copy = {};
1918
+ for (const [key, value] of Object.entries(branch))
1919
+ if (key !== 'any' && key !== 'PARENT')
1920
+ copy[key] = value;
1921
+ copy.any = branch.any === alternate.branch ? items : [];
1922
+ return copy;
1923
+ };
1924
+ const value = {};
1925
+ for (const [key, v] of Object.entries(ac)) {
1926
+ if (key === 'PARENT')
1927
+ continue;
1928
+ if (key === 'choice')
1929
+ value.choice = v.map(emptied);
1930
+ else if (key === 'fallback')
1931
+ value.fallback = emptied(v);
1932
+ else
1933
+ value[key] = v;
1934
+ }
1935
+ return { name: alternate.element.name, value };
1936
+ }
1937
+ /**
1938
+ * After an untracked deletion: a run-level `mc:AlternateContent` whose selected branch the deletion
1939
+ * emptied goes, every branch with it (CR-012 section 12.2 item 4; ED-009 section 3.5). A branch that
1940
+ * was empty before is left as it is.
1941
+ */
1942
+ function removeEmptiedAlternates(removed) {
1943
+ const seen = new Set();
1944
+ for (const seg of removed) {
1945
+ const alternate = seg.alternate;
1946
+ if ((alternate === null || alternate === void 0 ? void 0 : alternate.level) !== 'run' || seen.has(alternate.element))
1947
+ continue;
1948
+ seen.add(alternate.element);
1949
+ if (alternate.branch.length > 0)
1950
+ continue;
1951
+ const i = alternate.owner.indexOf(alternate.element);
1952
+ if (i >= 0)
1953
+ alternate.owner.splice(i, 1);
1954
+ }
1955
+ }
1956
+ /** Block holders a cell's paragraph may stand in: a block content control, its content, custom XML. */
1957
+ const BLOCK_HOLDERS = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtContentBlock', 'org_docx4j_wml.CTSdtContentBlock', 'org_docx4j_wml.CTCustomXmlBlock']);
1958
+ /** True for the last paragraph of a table cell, through the block holders between them. */
1959
+ function lastInCell(p) {
1960
+ var _a;
1961
+ let holder = p.PARENT;
1962
+ for (let guard = 0; holder && guard < 16 && BLOCK_HOLDERS.has((_a = holder.TYPE_NAME) !== null && _a !== void 0 ? _a : ''); guard++) {
1963
+ holder = holder.PARENT;
1964
+ }
1965
+ if ((holder === null || holder === void 0 ? void 0 : holder.TYPE_NAME) !== 'org_docx4j_wml.Tc')
1966
+ return false;
1967
+ let last;
1968
+ const visit = (items) => {
1969
+ for (const el of items !== null && items !== void 0 ? items : []) {
1970
+ const tn = typeNameOf(el);
1971
+ if (tn === 'org_docx4j_wml.P')
1972
+ last = el.value;
1973
+ else if (tn !== 'org_docx4j_wml.Tbl' && typeof el.value === 'object' && el.value !== null) {
1974
+ const v = el.value;
1975
+ visit(v.sdtContent ? v.sdtContent.content : v.content);
1976
+ }
1977
+ }
1978
+ };
1979
+ visit(holder.content);
1980
+ return last === p;
1981
+ }
1982
+ /** Every element among a paragraph's runs, in document order: where a deletion places a comment's reference among its runs. */
1983
+ function runOrderOf(p) {
1984
+ const order = new Map();
1985
+ const visit = (items) => {
1986
+ for (const el of items !== null && items !== void 0 ? items : []) {
1987
+ order.set(el, order.size);
1988
+ const v = el.value;
1989
+ if (typeof v === 'object' && v !== null && typeNameOf(el) !== 'org_docx4j_wml.R')
1990
+ visit(runItemsOf(v));
1991
+ }
1992
+ };
1993
+ visit(runItemsOf(p));
1994
+ return order;
1995
+ }
1996
+ /** The `w:ins` (or other revision) a run stands directly in, read from its `PARENT`, if any. */
1997
+ function revisionHolding(run) {
1998
+ const holder = run.value.PARENT;
1999
+ const items = holder === null || holder === void 0 ? void 0 : holder.customXmlOrSmartTagOrSdt;
2000
+ const outer = (holder === null || holder === void 0 ? void 0 : holder.PARENT) ? runItemsOf(holder.PARENT) : undefined;
2001
+ const element = outer === null || outer === void 0 ? void 0 : outer.find((e) => e.value === holder);
2002
+ const kind = element ? revisionKindOf(element) : undefined;
2003
+ if (!holder || !items || !outer || !element || !kind)
2004
+ return undefined;
2005
+ return { kind, element, value: holder, items, owner: outer };
2006
+ }
2007
+ /** The ids of the comment references among runs. */
2008
+ function commentReferenceIds(runs) {
2009
+ return runs.flatMap((run) => {
2010
+ var _a;
2011
+ return ((_a = run.value.content) !== null && _a !== void 0 ? _a : [])
2012
+ .filter((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference'; }).map((c) => Number(c.value.id));
2013
+ });
2014
+ }
2015
+ /** The `w:sdt` value types, block, run, row and cell. */
2016
+ const SDT_VALUE_TYPES = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtRun', 'org_docx4j_wml.CTSdtRow', 'org_docx4j_wml.CTSdtCell']);
2017
+ /** Whether a paragraph holds a run-level control bound by `w:dataBinding`, at any depth among its runs. */
2018
+ function holdsBoundRunControl(holder) {
2019
+ var _a;
2020
+ for (const item of (_a = runItemsOf(holder)) !== null && _a !== void 0 ? _a : []) {
2021
+ const value = item.value;
2022
+ if (!value || typeof value !== 'object')
2023
+ continue;
2024
+ if (value.TYPE_NAME === 'org_docx4j_wml.SdtRun' && sdtProperty(value.sdtPr, 'dataBinding') !== undefined)
2025
+ return true;
2026
+ if (value.TYPE_NAME !== 'org_docx4j_wml.R' && holdsBoundRunControl(value))
2027
+ return true;
2028
+ }
2029
+ return false;
2030
+ }