@docx4j/core-ts 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +32 -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 +509 -141
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +195 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  31. package/dist/model/content/InlinePicture.d.mts +11 -0
  32. package/dist/model/content/InlinePicture.mjs +21 -1
  33. package/dist/model/content/List.mjs +3 -0
  34. package/dist/model/content/Paragraph.d.mts +189 -13
  35. package/dist/model/content/Paragraph.mjs +870 -74
  36. package/dist/model/content/Range.d.mts +227 -12
  37. package/dist/model/content/Range.mjs +785 -59
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +172 -13
  43. package/dist/model/content/TrackedChange.mjs +728 -167
  44. package/dist/model/content/comments.d.mts +20 -6
  45. package/dist/model/content/comments.mjs +67 -57
  46. package/dist/model/content/generalException.d.mts +18 -0
  47. package/dist/model/content/generalException.mjs +24 -0
  48. package/dist/model/content/index.d.mts +8 -3
  49. package/dist/model/content/index.mjs +7 -2
  50. package/dist/model/content/mathText.d.mts +21 -0
  51. package/dist/model/content/mathText.mjs +267 -0
  52. package/dist/model/content/officeText.d.mts +27 -0
  53. package/dist/model/content/officeText.mjs +74 -0
  54. package/dist/model/content/outline.d.mts +5 -0
  55. package/dist/model/content/outline.mjs +13 -0
  56. package/dist/model/content/placeholders.d.mts +110 -0
  57. package/dist/model/content/placeholders.mjs +253 -0
  58. package/dist/model/content/search.d.mts +72 -0
  59. package/dist/model/content/search.mjs +204 -2
  60. package/dist/model/content/spanEdits.d.mts +159 -0
  61. package/dist/model/content/spanEdits.mjs +728 -0
  62. package/dist/model/content/story.d.mts +44 -0
  63. package/dist/model/content/story.mjs +92 -0
  64. package/dist/model/content/tableColumns.d.mts +1 -1
  65. package/dist/model/content/tableColumns.mjs +108 -80
  66. package/dist/model/content/tableRevisions.d.mts +11 -3
  67. package/dist/model/content/tableRevisions.mjs +72 -42
  68. package/dist/model/content/tableRules.d.mts +30 -0
  69. package/dist/model/content/tableRules.mjs +100 -0
  70. package/dist/model/content/textBoxes.d.mts +18 -0
  71. package/dist/model/content/textBoxes.mjs +105 -0
  72. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  73. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  74. package/dist/model/content/tracking.d.mts +28 -0
  75. package/dist/model/content/tracking.mjs +149 -10
  76. package/dist/model/content/tree.d.mts +171 -22
  77. package/dist/model/content/tree.mjs +329 -53
  78. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  79. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  80. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  81. package/dist/model/customxml/XmlMapping.mjs +11 -0
  82. package/dist/model/customxml/bindings.d.mts +21 -2
  83. package/dist/model/customxml/bindings.mjs +76 -32
  84. package/dist/model/customxml/kinds.d.mts +8 -3
  85. package/dist/model/customxml/kinds.mjs +26 -5
  86. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  87. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  88. package/dist/model/customxml/pictures.d.mts +4 -3
  89. package/dist/model/customxml/pictures.mjs +18 -6
  90. package/dist/model/customxml/xhtml.mjs +8 -2
  91. package/dist/model/index.d.mts +2 -0
  92. package/dist/model/index.mjs +4 -0
  93. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  94. package/dist/model/listnumbering/Emulator.mjs +63 -9
  95. package/dist/model/properties/PropertyResolver.d.mts +60 -1
  96. package/dist/model/properties/PropertyResolver.mjs +115 -2
  97. package/dist/model/properties/catalogue.d.mts +19 -5
  98. package/dist/model/properties/catalogue.mjs +41 -15
  99. package/dist/model/properties/log.d.mts +1 -1
  100. package/dist/model/properties/log.mjs +5 -5
  101. package/dist/model/properties/table.mjs +4 -2
  102. package/dist/model/text/Extractor.d.mts +289 -0
  103. package/dist/model/text/Extractor.mjs +2162 -0
  104. package/dist/model/text/LinkTargets.d.mts +45 -0
  105. package/dist/model/text/LinkTargets.mjs +170 -0
  106. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  107. package/dist/model/text/MarkdownExporter.mjs +255 -0
  108. package/dist/model/text/PlainText.d.mts +29 -0
  109. package/dist/model/text/PlainText.mjs +539 -0
  110. package/dist/model/text/TextContext.d.mts +161 -0
  111. package/dist/model/text/TextContext.mjs +425 -0
  112. package/dist/model/text/TextOptions.d.mts +150 -0
  113. package/dist/model/text/TextOptions.mjs +278 -0
  114. package/dist/model/text/TextResult.d.mts +504 -0
  115. package/dist/model/text/TextResult.mjs +526 -0
  116. package/dist/model/text/Texts.d.mts +99 -0
  117. package/dist/model/text/Texts.mjs +187 -0
  118. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  119. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  120. package/dist/model/text/commonmark.d.mts +121 -0
  121. package/dist/model/text/commonmark.mjs +655 -0
  122. package/dist/model/text/index.d.mts +7 -0
  123. package/dist/model/text/index.mjs +8 -0
  124. package/dist/model/text/normalize.d.mts +8 -0
  125. package/dist/model/text/normalize.mjs +13 -0
  126. package/dist/model/text/outline.d.mts +4 -0
  127. package/dist/model/text/outline.mjs +71 -0
  128. package/dist/office-js/fragment.d.mts +7 -5
  129. package/dist/office-js/fragment.mjs +63 -14
  130. package/dist/office-js/proxy.d.mts +8 -0
  131. package/dist/office-js/proxy.mjs +29 -0
  132. package/dist/office-js/run.mjs +2 -0
  133. package/dist/office-js/supported.generated.mjs +16 -0
  134. package/dist/office-js/toApiScript.mjs +21 -5
  135. package/dist/opc/Save.d.mts +17 -0
  136. package/dist/opc/Save.mjs +12 -2
  137. package/dist/opc/index.d.mts +3 -3
  138. package/dist/opc/index.mjs +1 -1
  139. package/dist/opc/mce/preprocessor.d.mts +28 -3
  140. package/dist/opc/mce/preprocessor.mjs +4 -4
  141. package/dist/opc/mce/understood.d.mts +30 -6
  142. package/dist/opc/mce/understood.mjs +37 -7
  143. package/dist/packages/OpcPackage.d.mts +13 -0
  144. package/dist/packages/OpcPackage.mjs +1 -0
  145. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  146. package/dist/packages/WordprocessingMLPackage.mjs +155 -20
  147. package/dist/parts/DefaultXmlPart.mjs +2 -2
  148. package/dist/parts/Part.d.mts +10 -2
  149. package/dist/parts/XmlPart.d.mts +12 -30
  150. package/dist/parts/XmlPart.mjs +44 -93
  151. package/dist/parts/wml/comments.mjs +30 -0
  152. package/dist/parts/wml/index.mjs +12 -0
  153. package/dist/xml/dom.d.mts +7 -0
  154. package/dist/xml/dom.mjs +18 -0
  155. package/package.json +2 -2
@@ -1,26 +1,31 @@
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';
5
6
  import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
6
7
  function withRPr(run, rPr) {
7
8
  if (rPr)
8
9
  run.value.rPr = rPr;
9
10
  return run;
10
11
  }
11
- import { Font } from './Font.mjs';
12
+ import { Font, markHolderOf } from './Font.mjs';
12
13
  import { runFontSelectorOf } from '../fonts/lookup.mjs';
13
14
  import { Range } from './Range.mjs';
14
- import { searchPattern, matchesOf, expandReplacement } from './search.mjs';
15
+ import { officeMatches, expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
15
16
  import { builtInOf, idOfBuiltIn, styleNameOf, styleIdOf } from './styles.mjs';
16
17
  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';
18
+ import { ContentControl, collectRunControls, controlViewOf, plainTextLinesOf, textForRunControl, assertControlHolds, controlsOfTypes, holdsOneParagraph, showsPlaceholderWhenMade, BLOCK_CONTROL } from './ContentControl.mjs';
19
+ import { breakParagraphAt, BREAKABLE } from './spanEdits.mjs';
20
+ import { endsCell, endsWithParagraph, wrappingCellOf } from './tableRules.mjs';
21
+ import { dropExcluded, forWriter, linesOf, officeItems, oneParagraphRun, holdsElementCharacters } from './officeText.mjs';
22
+ import { sdt as sdtOf, nextSdtId, sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
19
23
  import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
20
24
  import { InlinePicture, picturesIn, addImage, writableWidthEmu } from './InlinePicture.mjs';
25
+ import { showPicturePlaceholder } from '../customxml/picturePlaceholder.mjs';
21
26
  import { contentOf } from './ooxml.mjs';
22
27
  import { commentApi } from './comments.mjs';
23
- import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties } from './tracking.mjs';
28
+ import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties, markEquationDeleted, equationRevisionIn } from './tracking.mjs';
24
29
  import { trackedChangesOfParagraph } from './TrackedChange.mjs';
25
30
  import { isListItem, listOf, listOrNullObjectOf, listItemOf, listItemOrNullObjectOf, startNewList, attachToList, detachFromList, } from './List.mjs';
26
31
  const TWIPS_PER_POINT = 20;
@@ -62,23 +67,57 @@ export class Paragraph {
62
67
  get p() {
63
68
  return this.element.value;
64
69
  }
70
+ /** @internal A view of another paragraph of this one's body, for a `Range` across paragraphs (CR-014). */
71
+ viewOf(element, container) {
72
+ return new Paragraph(element, container, this.parentBody);
73
+ }
74
+ /**
75
+ * @internal A range from this paragraph's start to the end of `last`, a later paragraph of the same story, with
76
+ * `last`'s mark where `mark` is set, or `endMark` in its place (CR-014 phase 1).
77
+ */
78
+ rangeTo(last, mark, endMark) {
79
+ return new Range(this, 0, last.text.length, true, mark, last.element === this.element ? undefined : last, endMark);
80
+ }
81
+ /** How many tables deep the paragraph is: 0 outside a table, 1 in a cell, 2 in a nested table's (Office JS `tableNestingLevel`). */
82
+ get tableNestingLevel() {
83
+ let level = 0;
84
+ for (let cell = this.parentTableCell; cell; cell = cell.parentTable.parentTableCell)
85
+ level++;
86
+ return level;
87
+ }
65
88
  get index() {
66
89
  return this.container.indexOf(this.element);
67
90
  }
68
- /** The paragraph's text: runs joined, tabs as \t, breaks as \n; deleted text excluded. */
91
+ /**
92
+ * The paragraph's text as Office JS gives it in Word's default view (CR-012 phase 4, section 6.2):
93
+ * the accepted text, hidden text left out, in Word's characters (check 48: a note reference U+0002,
94
+ * a line break U+000B, a page break U+000C, a column break U+000E, a `w:cr` U+000D, the hyphens
95
+ * U+001E and U+001F, a symbol `(`, an equation as Word draws it). A kept `mc:AlternateContent`
96
+ * reads as its selected branch (check 49). Every offset a `Range` or `search` takes counts in it.
97
+ */
69
98
  get text() {
70
- return textOf(this.p);
99
+ return this.segments().map((s) => s.text).join('');
71
100
  }
72
101
  /** Replaces the content with one run of the text, keeping the first run's formatting. */
73
102
  set text(value) {
74
103
  var _a;
104
+ this.assertWritable();
105
+ // one paragraph, its line breaks w:br and w:cr (CR-002 section 60 E2), or as a plain-text control takes them (F3)
106
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
107
+ const mode = lines === 'multi' ? 'multi-line' : lines === 'single' ? 'single-line' : 'one-paragraph';
108
+ // in a block control showing its placeholder, the text in the placeholder's place (CR-002 section 63)
109
+ const placeholder = this.placeholderControl;
110
+ if (placeholder) {
111
+ placeholder.writePlaceholder(value, { lineBreaks: mode });
112
+ return;
113
+ }
75
114
  if (this.changeTracker) {
76
- this.splice(0, this.text.length, value, 'before', WHOLE);
115
+ this.splice(0, this.text.length, value, 'before', WHOLE, mode);
77
116
  return;
78
117
  }
79
118
  const rPr = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value.rPr;
80
119
  this.releaseRelationships(this.p.content);
81
- this.p.content = [withRPr(textRun(value), rPr ? deepCopy(rPr) : undefined)];
120
+ this.p.content = [oneParagraphRun(value, rPr ? deepCopy(rPr) : undefined, mode)];
82
121
  linkParents(this.p.content, this.p);
83
122
  }
84
123
  /**
@@ -86,7 +125,7 @@ export class Paragraph {
86
125
  * reads the document as it was before the tracked changes: `w:del` included, `w:ins` excluded.
87
126
  */
88
127
  getText(options) {
89
- return (options === null || options === void 0 ? void 0 : options.view) === 'original' ? textOfView(this.p, options) : this.text;
128
+ return (options === null || options === void 0 ? void 0 : options.view) === 'original' ? this.segments({ view: 'original' }).map((s) => s.text).join('') : this.text;
90
129
  }
91
130
  /** The style id (w:pStyle; docx4j's name for it); 'Normal' when none is set. Extension: Office JS has `style` and `styleBuiltIn` only. */
92
131
  get styleId() {
@@ -227,24 +266,36 @@ export class Paragraph {
227
266
  else
228
267
  pPr.outlineLvl = { val: level - 1 }; this.settleFormatting(); }
229
268
  /**
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).
269
+ * The runs' formatting: reads the *effective* properties of the runs holding the paragraph's text, `null` (or `''`,
270
+ * `Mixed`) where they differ, and the mark's where it has no text, as Office JS reads them (CR-002 section 59); writes
271
+ * direct formatting to every run (extension: `Font` is over runs, not the paragraph mark).
232
272
  */
233
273
  get font() {
234
274
  return this.getFont();
235
275
  }
236
- /** `font`, with `{ direct: true }` for the direct formatting of the first run (extension). */
276
+ /** `font`, with `{ direct: true }` for the runs' direct formatting (extension). */
237
277
  getFont(options) {
238
- const holders = () => runsOf(this.p).map((r) => r.value);
278
+ const holders = (write) => {
279
+ if (write) {
280
+ this.assertWritable();
281
+ return runsOf(this.p).map((r) => r.value);
282
+ }
283
+ // a read takes the runs of Paragraph.text, Word's default view, as a range over it does; with none, a placeholder's
284
+ // runs, which Word's font reads though its text doesn't (check 52 case 13, after Accept All: Word's bold filler)
285
+ const runs = this.getRange('Content').runs.map((r) => r.value);
286
+ return runs.length > 0 ? runs : runsOf(this.p).map((r) => r.value).filter((r) => inShowingPlaceholder(r));
287
+ };
288
+ const mark = () => markHolderOf(this.p);
239
289
  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); });
290
+ return new Font(holders, () => this.fontTracking(), undefined, undefined, mark);
291
+ 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
292
  }
243
293
  /** w14:paraId, the stable address Word gives paragraphs. */
244
294
  get paraId() {
245
295
  return this.p.paraId;
246
296
  }
247
297
  set paraId(id) {
298
+ this.assertWritable();
248
299
  if (id === undefined)
249
300
  delete this.p.paraId;
250
301
  else
@@ -264,6 +315,14 @@ export class Paragraph {
264
315
  collectRunControls(this.p, this.parentBody, out);
265
316
  return out;
266
317
  }
318
+ /**
319
+ * Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
320
+ * where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
321
+ * departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
322
+ */
323
+ getContentControls(options) {
324
+ return controlsOfTypes(this.contentControls, options);
325
+ }
267
326
  /**
268
327
  * The inline pictures in this paragraph, in order (Office JS Paragraph.inlinePictures): each
269
328
  * `w:drawing` whose `wp:inline` holds a `pic:pic`, in a run or in the selected branch of a kept
@@ -283,27 +342,55 @@ export class Paragraph {
283
342
  }
284
343
  /** Text inserted at the start, the end, or replacing the whole paragraph's text. */
285
344
  insertText(text, location) {
345
+ // in a block control showing its placeholder the text takes the placeholder's place wherever it is put (CR-002
346
+ // section 63; check 63's run 2, cases 7 to 9 and 33 to 35)
347
+ const placeholder = this.placeholderControl;
348
+ if (placeholder)
349
+ return placeholder.writePlaceholder(text);
286
350
  const length = this.text.length;
287
351
  if (location === 'Start')
288
352
  return this.splice(0, 0, text);
289
353
  if (location === 'End')
290
- return this.splice(length, length, text);
354
+ return this.writeAtEnd(text);
355
+ // tracked, the insertion goes first, before the deletion, as a range's Replace writes it and Word wrote this one
356
+ // (check 63 cases 45 and 52; CR-002 section 63 D3)
357
+ if (this.changeTracker && length > 0)
358
+ return this.getRange('Content').insertText(text, 'Replace');
291
359
  return this.splice(0, length, text, 'before', WHOLE);
292
360
  }
293
- /** A new paragraph of the text before or after this one, with this paragraph's properties (as Word). */
361
+ /**
362
+ * A new paragraph of the text before or after this one, with this paragraph's properties (as Word). Beside the only
363
+ * paragraph of a block control showing its placeholder it goes outside the control, as Word's went, and the control
364
+ * stays at block level (CR-002 section 63, D6 (b); check 63 cases 10, 11, 36 and 37).
365
+ */
294
366
  insertParagraph(text, location) {
367
+ var _a, _b;
368
+ this.assertWritable();
295
369
  const pPr = this.p.pPr ? deepCopy(this.p.pPr) : undefined;
296
370
  if (pPr) {
297
371
  delete pPr.rPr;
298
372
  delete pPr.sectPr;
299
373
  delete pPr.pPrChange;
300
374
  }
301
- const el = paragraphOf(text === '' ? [] : [textRun(text)], pPr);
375
+ const run = oneParagraphRun(text); // E2
376
+ const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : [], pPr);
377
+ const control = this.blockControl;
378
+ if ((control === null || control === void 0 ? void 0 : control.isShowingPlaceholder) && holdsOneParagraph(control)) {
379
+ // the body holding the control: this paragraph's is the control's content
380
+ const owner = (_b = control.element.value.PARENT) !== null && _b !== void 0 ? _b : this.parentBody.container;
381
+ return this.parentBody.sub(owner).insertElement(el, location, { element: control.element, container: control.container });
382
+ }
302
383
  return this.parentBody.insertElement(el, location, this);
303
384
  }
385
+ /** The block-level control holding this paragraph directly while it shows its placeholder (CR-002 section 63). */
386
+ get placeholderControl() {
387
+ const control = this.blockControl;
388
+ return (control === null || control === void 0 ? void 0 : control.showsPlaceholderContent) ? control : undefined;
389
+ }
304
390
  insertBreak(type, location) {
305
391
  var _a;
306
392
  var _b;
393
+ this.assertWritable();
307
394
  const brk = breakItem(type === 'Page' ? 'page' : undefined);
308
395
  if (location === 'Before' || location === 'After') {
309
396
  const p = this.insertParagraph('', location);
@@ -331,11 +418,15 @@ export class Paragraph {
331
418
  insertInlinePictureFromBase64(base64, location, options) {
332
419
  var _a;
333
420
  var _b;
421
+ this.assertWritable();
334
422
  const part = this.parentBody.part;
335
423
  if (!part)
336
424
  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
425
  const tracker = this.changeTracker;
426
+ // refused before the image part is added, not after (CR-012 section 12.4)
427
+ if (location === 'Replace' && tracker)
428
+ this.assertPlaceable(0, this.text.length, 'replace');
429
+ const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
339
430
  // under tracking the picture is an insertion, and what it replaces is deleted as insertText's is
340
431
  if (location === 'Replace' && tracker)
341
432
  this.splice(0, this.text.length, '', 'before', WHOLE);
@@ -360,6 +451,7 @@ export class Paragraph {
360
451
  */
361
452
  async insertOoxml(ooxml, location) {
362
453
  var _a, _b;
454
+ this.assertWritable();
363
455
  const preprocess = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
364
456
  const elements = await contentOf(ooxml, { preprocess: preprocess ? (doc) => preprocess(doc) : undefined, target: this.parentBody.part });
365
457
  if (elements.length === 0)
@@ -370,12 +462,12 @@ export class Paragraph {
370
462
  return [this];
371
463
  }
372
464
  if (location === 'Replace') {
373
- const inserted = this.parentBody.insertElement(elements, 'Before', this);
465
+ const inserted = this.parentBody.insertElement(elements, 'Before', this, 'GeneralException', this.element);
374
466
  void inserted;
375
467
  this.delete();
376
468
  }
377
469
  else {
378
- this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this);
470
+ this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this, 'GeneralException');
379
471
  }
380
472
  return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
381
473
  ? new Paragraph(el, this.container, this.parentBody)
@@ -383,6 +475,10 @@ export class Paragraph {
383
475
  }
384
476
  /** Matches within this paragraph. */
385
477
  search(text, options) {
478
+ // Word's paragraph mark, ^p, is searched in the paragraph's whole range, which holds its mark (CR-014 phase 3), and so
479
+ // is a wildcard query, whose ? or class matches the mark too (check 59; CR-002 section 32, K7)
480
+ if ((options === null || options === void 0 ? void 0 : options.matchWildcards) || (!(options === null || options === void 0 ? void 0 : options.matchRegExp) && paragraphMarkPieces(text)))
481
+ return this.getRange('Whole').search(text, options);
386
482
  return this.searchMatches(text, options).map((m) => m.range);
387
483
  }
388
484
  /**
@@ -391,8 +487,8 @@ export class Paragraph {
391
487
  * and `Body.replaceText` the other.
392
488
  */
393
489
  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 }));
490
+ return officeMatches(this.segments({ objects: true }), text, options)
491
+ .map(({ start, end, match }) => ({ range: new Range(this, start, end), match }));
396
492
  }
397
493
  /**
398
494
  * Replaces every match of `find` with `replace`, last match first so that the offsets of the
@@ -400,6 +496,9 @@ export class Paragraph {
400
496
  * the package's `changeTrackingMode` is on.
401
497
  */
402
498
  replaceText(find, replace, options) {
499
+ refuseMarkReplacement('Paragraph', find, options);
500
+ if (options === null || options === void 0 ? void 0 : options.matchWildcards)
501
+ refuseWildcardMarkReplacement('Paragraph', this.search(find, options));
403
502
  const matches = this.searchMatches(find, options);
404
503
  for (let i = matches.length - 1; i >= 0; i--) {
405
504
  const { range, match } = matches[i];
@@ -414,24 +513,51 @@ export class Paragraph {
414
513
  /**
415
514
  * The paragraph's range: 'Whole' and 'Content' hold its whole content, the pictures at its start and
416
515
  * end among them, so deleting it deletes them (CR-002 section 48); 'Start' and 'End' are collapsed.
516
+ * 'Whole' holds the paragraph's mark too, so its `text` ends with `markText`, as Office JS's does
517
+ * (check 48); 'Content' ends with the paragraph's text.
417
518
  */
418
519
  getRange(location = 'Whole') {
419
520
  const length = this.text.length;
420
521
  if (location === 'Start')
421
522
  return new Range(this, 0, 0);
422
523
  if (location === 'End')
423
- return new Range(this, length, length);
424
- return new Range(this, 0, length, true);
524
+ return new Range(this, length, length).atEndOf('End');
525
+ const range = new Range(this, 0, length, true, location === 'Whole');
526
+ return location === 'Content' ? range.atEndOf('Content') : range;
527
+ }
528
+ /**
529
+ * The character Office JS gives for this paragraph's mark at the end of its 'Whole' range (check 48):
530
+ * `\t` for the last paragraph of a table cell, the cell's end; nothing for a mark tracked as deleted, which
531
+ * Word's default view doesn't show (CR-012 section 19, check 58); and `\r` for any other.
532
+ */
533
+ get markText() {
534
+ var _a, _b;
535
+ if (lastInCell(this.p))
536
+ return '\t';
537
+ return ((_b = (_a = this.p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.del) !== undefined ? '' : '\r';
425
538
  }
426
539
  /**
427
540
  * Removes the paragraph from its container. While the package tracks changes, the content
428
541
  * becomes a `w:del` and the mark is marked deleted (`w:pPr/w:rPr/w:del`) instead, unless the
429
542
  * whole paragraph was this author's own insertion, which Word simply takes back.
543
+ * The paragraph a table cell ends with, as Word reads it, stays where removing it would leave the
544
+ * cell without one, emptied, its properties kept; tracked, its mark is never marked deleted, as Word
545
+ * keeps a cell's last paragraph (check 52, cases 88 to 91; CR-002 section 55.6).
430
546
  */
431
547
  delete() {
548
+ this.remove(true);
549
+ }
550
+ /**
551
+ * `delete()`; with `keepCellEnd` false a cell's last paragraph goes as any other, for `Body.clearContent`'s
552
+ * callers, which put their own paragraph in next.
553
+ * @internal
554
+ */
555
+ remove(keepCellEnd) {
556
+ this.assertWritable();
557
+ const cellEnd = keepCellEnd && endsCell(this.p);
432
558
  const tracker = this.changeTracker;
433
559
  if (tracker) {
434
- this.trackedDelete(tracker);
560
+ this.trackedDelete(tracker, cellEnd);
435
561
  return;
436
562
  }
437
563
  const i = this.index;
@@ -439,8 +565,17 @@ export class Paragraph {
439
565
  return;
440
566
  this.container.splice(i, 1);
441
567
  this.releaseRelationships(this.element);
568
+ if (cellEnd && !this.cellStillEnds()) {
569
+ this.p.content = [];
570
+ this.container.splice(i, 0, this.element);
571
+ }
572
+ }
573
+ /** Whether the cell this paragraph was in ends with a paragraph without it, as Word reads it (CR-002 section 55). */
574
+ cellStillEnds() {
575
+ var _a;
576
+ return endsWithParagraph((_a = wrappingCellOf(this.p.PARENT)) === null || _a === void 0 ? void 0 : _a.content);
442
577
  }
443
- trackedDelete(tracker) {
578
+ trackedDelete(tracker, cellEnd) {
444
579
  var _a, _b;
445
580
  if (markDeleted(this.p))
446
581
  throw new Docx4JException('This paragraph is already marked deleted');
@@ -451,10 +586,17 @@ export class Paragraph {
451
586
  const ownMark = (rPr === null || rPr === void 0 ? void 0 : rPr.ins) !== undefined && rPr.ins.author === tracker.author;
452
587
  if (ownMark && ((_b = this.p.content) !== null && _b !== void 0 ? _b : []).length === 0) {
453
588
  const i = this.index;
454
- if (i >= 0)
589
+ if (i >= 0) {
455
590
  this.container.splice(i, 1);
591
+ if (!cellEnd || this.cellStillEnds())
592
+ return;
593
+ this.container.splice(i, 0, this.element);
594
+ }
456
595
  return;
457
596
  }
597
+ // a cell's last mark: Word deletes the paragraph's text and never marks the mark (check 52, cases 90 and 91)
598
+ if (cellEnd)
599
+ return;
458
600
  tracker.markParagraphDeleted(this.p);
459
601
  // Word, deleting a paragraph mark ALONE under tracking, gives the next paragraph this one's
460
602
  // properties as a w:pPrChange (CR-002 section 38 item 2, the editor's measure-word15.docx), so
@@ -469,13 +611,14 @@ export class Paragraph {
469
611
  * what the cells of a deleted table row need (a `w:tc` must keep a block-level child, and Word
470
612
  * writes `w:del` around every run and `w:pPr/w:rPr/w:del` on every mark inside a deleted row).
471
613
  * The same primitives `delete()` uses, without its "take back my own insertion" step.
472
- * Does nothing when the mark is already deleted.
614
+ * A mark already deleted is left as it is, and the text is deleted still: a paragraph another author's
615
+ * deletion ended keeps text a range's deletion across it must take (CR-014 phase 2).
473
616
  */
474
617
  markDeletedInPlace(tracker) {
475
- if (markDeleted(this.p))
476
- return;
618
+ this.assertWritable();
477
619
  this.deleteText(tracker, 0, this.text.length, WHOLE);
478
- tracker.markParagraphDeleted(this.p);
620
+ if (!markDeleted(this.p))
621
+ tracker.markParagraphDeleted(this.p);
479
622
  }
480
623
  /**
481
624
  * Queues the relationships that content removed outright referenced (a picture's image, a link's
@@ -512,6 +655,7 @@ export class Paragraph {
512
655
  pPr() {
513
656
  var _a, _b;
514
657
  var _c;
658
+ this.assertWritable();
515
659
  const pPr = ((_a = (_c = this.p).pPr) !== null && _a !== void 0 ? _a : (_c.pPr = { TYPE_NAME: 'org_docx4j_wml.PPr' }));
516
660
  linkParents(pPr, this.p);
517
661
  (_b = this.changeTracker) === null || _b === void 0 ? void 0 : _b.recordPPrChange(pPr);
@@ -590,9 +734,49 @@ export class Paragraph {
590
734
  ownInsertions.add(seg.run);
591
735
  return { tracker, ownInsertions };
592
736
  }
593
- /** The text segments (w:t, w:tab, ...) with their offsets; `{ objects: true }` adds the pictures. */
737
+ /**
738
+ * The text segments (w:t, w:tab, ...) with their offsets in `text`; `{ objects: true }` adds the
739
+ * pictures and the hidden runs' items.
740
+ */
594
741
  segments(options) {
595
- return segmentsOf(this.p, options);
742
+ return segmentsOf(this.p, { hidden: this.hiddenRuns(), ...options });
743
+ }
744
+ /**
745
+ * True for a paragraph in a text box (`w:txbxContent`), at a `textbox:` address (CR-012 D4). Such a view
746
+ * reads as any paragraph does and is read-only (D5): every write throws `NotSupportedError`.
747
+ */
748
+ get inTextBox() {
749
+ let o = this.p.PARENT;
750
+ for (let guard = 0; o && guard < 256; guard++, o = o.PARENT) {
751
+ if (o.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent')
752
+ return true;
753
+ }
754
+ return false;
755
+ }
756
+ /**
757
+ * Refuses a write to a paragraph in a text box (CR-012 D5): a box is in each branch of its
758
+ * `mc:AlternateContent`, `wps:txbx` in the Choice and `v:textbox` in the Fallback, and nothing maps the
759
+ * one's paragraphs to the other's, so an edit could not reach every branch as CR-010's D2 has mutators do.
760
+ * And a write to a paragraph in a branch of a kept `mc:AlternateContent` at block, row or cell level
761
+ * (CR-013 D4), for the same reason. Internal: the content API's mutators call it first.
762
+ */
763
+ assertWritable() {
764
+ if (this.inTextBox) {
765
+ throw new NotSupportedError('Paragraph', 'edit', 'this paragraph is in a text box, whose paragraphs are read-only (CR-012 D5): '
766
+ + "writes into a text box would have to reach its other branch's box too, which nothing maps yet");
767
+ }
768
+ assertOutsideBranch(this.p, 'Paragraph');
769
+ }
770
+ /**
771
+ * True for a paragraph in the selected branch of a kept `mc:AlternateContent` at block, row or cell
772
+ * level, which readers see as Word does (CR-013 phase 2). Such a view is read-only (D4).
773
+ */
774
+ get inBranch() {
775
+ return inBranch(this.p);
776
+ }
777
+ /** Which of this paragraph's runs are hidden: a run's own `w:vanish`, else its styles' (CR-012 phase 4). */
778
+ hiddenRuns() {
779
+ return this.parentBody.hiddenRuns(this.p);
596
780
  }
597
781
  /**
598
782
  * Replaces the text in [start, end) with `text` and returns the range of the inserted text.
@@ -604,13 +788,313 @@ export class Paragraph {
604
788
  * says the span holds (`WHOLE` for a paragraph's whole content), so a span of no text can still
605
789
  * delete a picture (CR-002 section 48).
606
790
  */
607
- splice(start, end, text, prefer = 'before', edges) {
608
- var _a, _b, _c, _d, _e, _f;
609
- var _g;
791
+ splice(start, end, text, prefer = 'before', edges, lineBreaks) {
792
+ this.assertWritable();
793
+ // text into a block control showing its placeholder takes the placeholder's place (CR-002 section 63)
794
+ const placeholder = text === '' ? undefined : this.placeholderControl;
795
+ if (placeholder)
796
+ return placeholder.writePlaceholder(text, lineBreaks ? { lineBreaks } : {});
797
+ // every text write into a bound control writes its node (CR-002 section 63, D7), as ContentControl.insertText did
798
+ const bound = this.boundControlsAt(start, end);
799
+ const range = this.spliceWrite(start, end, text, prefer, edges, lineBreaks);
800
+ for (const control of bound)
801
+ control.syncBoundNode();
802
+ return range;
803
+ }
804
+ /**
805
+ * The controls bound by `w:dataBinding` that [start, end) is in: those holding the paragraph, and the run-level ones
806
+ * holding text there.
807
+ */
808
+ boundControlsAt(start, end, ancestorsOnly = false) {
809
+ const out = [];
810
+ const seen = new Set();
811
+ const add = (o) => {
812
+ const tn = o.TYPE_NAME;
813
+ if (tn === undefined || !SDT_VALUE_TYPES.has(tn) || seen.has(o))
814
+ return;
815
+ seen.add(o);
816
+ if (sdtProperty(o.sdtPr, 'dataBinding') === undefined)
817
+ return;
818
+ try {
819
+ out.push(controlViewOf(o, this.parentBody));
820
+ }
821
+ catch {
822
+ // not where its PARENT says: nothing to write
823
+ }
824
+ };
825
+ for (let o = this.p.PARENT; o; o = o.PARENT)
826
+ add(o);
827
+ if (ancestorsOnly || !holdsBoundRunControl(this.p))
828
+ return out;
829
+ for (const seg of this.segments({ objects: true })) {
830
+ if (seg.end < start || seg.start > end)
831
+ continue;
832
+ for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT)
833
+ add(o);
834
+ }
835
+ return out;
836
+ }
837
+ /**
838
+ * @internal Text written at the paragraph's End by Office JS's routes to it: `insertText('End')`, its End range at any
839
+ * location, its Content range's End and After, and so a body's or a cell's `insertText('End')` (CR-002 sections 63.24
840
+ * to 63.27; checks 69 and 70). As Word writes it (D12): after everything the paragraph holds, into none of it - a
841
+ * 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
842
+ * own, or the last run extended. The text takes the last item's run properties where that item is a run holding text,
843
+ * so after a hidden run it is hidden and its range reads nothing (D13); otherwise it has none, and with `plain` (the End
844
+ * range's After, D14) none either. Word's proofing marks, `w:proofErr`, are no item (a choice: Word redoes them).
845
+ * Tracked, the run is a `w:ins`, or joins this author's own `w:ins` where one ends the paragraph. A line break makes
846
+ * paragraphs, as `splice` does (CR-002 section 60 E1); text so broken after a hidden run is written visible (a choice,
847
+ * unmeasured).
848
+ */
849
+ writeAtEnd(text, plain = false) {
850
+ var _a, _b;
851
+ var _c;
852
+ this.assertWritable();
853
+ const placeholder = this.placeholderControl;
854
+ if (placeholder)
855
+ return placeholder.writePlaceholder(text);
856
+ const length = this.text.length;
857
+ text = dropExcluded(text);
858
+ if (text === '')
859
+ return new Range(this, length, length);
860
+ let mode = 'paragraphs';
861
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
862
+ if (lines)
863
+ mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
864
+ text = forWriter(text, mode);
865
+ const broken = mode === 'paragraphs' && /[\r\n]/.test(text) ? linesOf(text) : undefined;
866
+ const joined = broken ? broken.join('') : text;
867
+ if (joined === '' && !broken)
868
+ return new Range(this, length, length);
869
+ const bound = this.boundControlsAt(length, length, true);
870
+ const content = ((_a = (_c = this.p).content) !== null && _a !== void 0 ? _a : (_c.content = []));
871
+ let at = content.length;
872
+ while (at > 0 && typeNameOf(content[at - 1]) === 'org_docx4j_wml.ProofErr')
873
+ at--;
874
+ const last = at > 0 ? content[at - 1] : undefined;
875
+ const textRun = (item) => {
876
+ var _a;
877
+ if (!item || typeNameOf(item) !== 'org_docx4j_wml.R')
878
+ return undefined;
879
+ const run = item.value;
880
+ 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;
881
+ };
882
+ const hidden = this.hiddenRuns();
883
+ // the run whose properties the text takes, and which it may extend: a run holding text, unless `plain`
884
+ let source = plain ? undefined : textRun(last);
885
+ if (source && broken && hidden(source))
886
+ source = undefined;
887
+ const items = officeItems(joined);
888
+ const tracker = this.changeTracker;
889
+ let written;
890
+ const own = tracker && last && typeNameOf(last) === 'org_docx4j_wml.RunIns' && tracker.ownInsertion({ kind: 'ins', value: last.value });
891
+ if (items.length === 0) {
892
+ // nothing but line breaks: only the paragraphs they make
893
+ }
894
+ else if (own) {
895
+ // this author's own insertion ends the paragraph: the text joins it, after its last run, in that run's look
896
+ const value = last.value;
897
+ const holder = (_b = runItemsOf(value)) !== null && _b !== void 0 ? _b : (value.customXmlOrSmartTagOrSdt = []);
898
+ const inner = plain ? undefined : textRun(holder[holder.length - 1]);
899
+ written = this.appendTo(holder, inner, items, value);
900
+ }
901
+ else if (tracker) {
902
+ const ins = tracker.ins([runOf(items, copyRPr(source === null || source === void 0 ? void 0 : source.rPr))]);
903
+ content.splice(at, 0, ins);
904
+ linkParents(ins, this.p);
905
+ written = runsOf(ins.value)[0].value;
906
+ }
907
+ else {
908
+ written = this.appendTo(content, source, items, this.p, at);
909
+ }
910
+ for (const control of bound)
911
+ control.syncBoundNode();
912
+ if (broken) {
913
+ const offsets = [];
914
+ let end = length;
915
+ for (const line of broken.slice(0, -1))
916
+ offsets.push(end += line.length);
917
+ const { first, last: tail } = breakParagraphAt(this, offsets);
918
+ return new Range(first, length, broken[broken.length - 1].length, false, false, tail);
919
+ }
920
+ return written && hidden(written) ? new Range(this, length, length) : new Range(this, length, length + joined.length);
921
+ }
922
+ /**
923
+ * `items` put at `at` in `list` (its end by default): onto the end of `run`, which is in `list` right before `at`,
924
+ * merging into its last `w:t`, or as a new run in its properties where `run` is undefined. Returns the run written.
925
+ */
926
+ appendTo(list, run, items, parent, at = list.length) {
927
+ var _a, _b, _c, _d, _e;
928
+ if (run) {
929
+ const own = ((_a = run.content) !== null && _a !== void 0 ? _a : (run.content = []));
930
+ const tail = own[own.length - 1];
931
+ const head = items[0];
932
+ 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') {
933
+ setText(tail.value, ((_d = tail.value.value) !== null && _d !== void 0 ? _d : '') + ((_e = head.value.value) !== null && _e !== void 0 ? _e : ''));
934
+ items = items.slice(1);
935
+ }
936
+ own.push(...items);
937
+ linkParents(items, run);
938
+ return run;
939
+ }
940
+ const added = runOf(items);
941
+ list.splice(at, 0, added);
942
+ linkParents(added, parent);
943
+ return added.value;
944
+ }
945
+ /** `splice`'s write. */
946
+ spliceWrite(start, end, text, prefer, edges, lineBreaks) {
947
+ // Office JS's characters written back (CR-002 section 60): the characters XML 1.0 excludes go (E3), a line feed or
948
+ // carriage return is a paragraph break (E1) unless the writer or the control takes it otherwise, and every
949
+ // character that is an element is written as it (D1)
950
+ text = dropExcluded(text);
951
+ if (text === '')
952
+ return this.spliceText(start, end, text, prefer, edges);
953
+ const from = Math.max(0, Math.min(start, this.text.length));
954
+ const landing = this.landingSegment(from, prefer, end > from);
955
+ const runControl = this.runControlOf(landing);
956
+ let mode = lineBreaks !== null && lineBreaks !== void 0 ? lineBreaks : 'paragraphs';
957
+ if (runControl) {
958
+ text = textForRunControl(runControl, text); // F4 and F3
959
+ }
960
+ else if (lineBreaks === undefined) {
961
+ const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
962
+ if (lines)
963
+ mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
964
+ }
965
+ text = forWriter(text, mode);
966
+ if (mode === 'paragraphs' && /[\r\n]/.test(text))
967
+ return this.spliceLines(start, end, linesOf(text), prefer, edges, landing);
968
+ const range = this.spliceText(start, end, text, prefer, edges);
969
+ this.writeElements(range.start, range.start + text.length);
970
+ return range;
971
+ }
972
+ /**
973
+ * Text of several lines put in place of [start, end) as Word's insertText puts it (CR-002 section 60 E1): the lines'
974
+ * text, then a paragraph break after each line but the last, the paragraphs keeping this one's properties; tracked,
975
+ * each new mark inserted. Refused before anything changes where a paragraph can't be split there.
976
+ */
977
+ spliceLines(start, end, lines, prefer, edges, landing) {
978
+ this.assertBreakable(landing);
979
+ const joined = lines.join('');
980
+ const range = this.spliceText(start, end, joined, prefer, edges);
981
+ const from = range.start;
982
+ this.writeElements(from, from + joined.length);
983
+ const offsets = [];
984
+ let at = from;
985
+ for (const line of lines.slice(0, -1))
986
+ offsets.push(at += line.length);
987
+ const { first, last } = breakParagraphAt(this, offsets);
988
+ return new Range(first, from, lines[lines.length - 1].length, false, false, last);
989
+ }
990
+ /** @internal Refuses, before anything changes, a paragraph break at `at` where none can be made (E1; `Range`'s Replace across paragraphs). */
991
+ assertBreakableAt(at, prefer) {
992
+ this.assertBreakable(this.landingSegment(at, prefer));
993
+ }
994
+ /**
995
+ * The segment text put at `at` goes into, as `spliceText` chooses it: a replacement's, the one holding the first
996
+ * replaced character; else the one containing `at`, else the nearer neighbour.
997
+ */
998
+ landingSegment(at, prefer, replacing = false) {
999
+ const segs = this.segments();
1000
+ if (replacing) {
1001
+ const first = segs.find((s) => s.start <= at && at < s.end);
1002
+ if (first)
1003
+ return first;
1004
+ }
1005
+ const containing = segs.find((s) => s.editable && s.start < at && at < s.end);
1006
+ const ending = segs.find((s) => s.editable && s.end === at);
1007
+ const starting = segs.find((s) => s.editable && s.start === at);
1008
+ return containing !== null && containing !== void 0 ? containing : (prefer === 'after' ? starting !== null && starting !== void 0 ? starting : ending : ending !== null && ending !== void 0 ? ending : starting);
1009
+ }
1010
+ /** The run-level content control a segment's run is in, the innermost, as a view; undefined for none. */
1011
+ runControlOf(seg) {
1012
+ var _a;
1013
+ 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) {
1014
+ if (o.TYPE_NAME === 'org_docx4j_wml.SdtRun')
1015
+ return controlViewOf(o, this.parentBody);
1016
+ }
1017
+ return undefined;
1018
+ }
1019
+ /** The block-level content control holding this paragraph directly, as a view; undefined for none. */
1020
+ get blockControl() {
1021
+ const content = this.p.PARENT;
1022
+ const sdt = (content === null || content === void 0 ? void 0 : content.TYPE_NAME) === 'org_docx4j_wml.SdtContentBlock' ? content.PARENT : undefined;
1023
+ return (sdt === null || sdt === void 0 ? void 0 : sdt.TYPE_NAME) === 'org_docx4j_wml.SdtBlock' ? controlViewOf(sdt, this.parentBody) : undefined;
1024
+ }
1025
+ /**
1026
+ * Refuses, before anything changes, a paragraph break where the text lands inside something a paragraph can't be
1027
+ * split in: a content control, an equation, a kept mc:AlternateContent (CR-002 section 60 E1, as `splitParagraph`
1028
+ * refuses one). A field's result is split: a complex field's runs across the paragraphs, a `w:fldSimple` made a
1029
+ * complex field first (section 62.2 D1).
1030
+ */
1031
+ assertBreakable(seg) {
1032
+ var _a;
1033
+ if (!seg)
1034
+ return;
1035
+ const refuse = (what) => {
1036
+ throw new NotSupportedError('Paragraph', 'insertText', `a line feed or carriage return is a paragraph break (CR-002 section 60, E1), `
1037
+ + `and this text goes inside ${what}, which can't be cut in two`);
1038
+ };
1039
+ if (seg.alternate || seg.math)
1040
+ refuse('an equation or an mc:AlternateContent');
1041
+ for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT) {
1042
+ const tn = (_a = o.TYPE_NAME) !== null && _a !== void 0 ? _a : '';
1043
+ if (!BREAKABLE.has(tn))
1044
+ refuse(`a ${tn.replace('org_docx4j_wml.', '')}`);
1045
+ }
1046
+ }
1047
+ /**
1048
+ * The characters of [from, to) that are elements written as the elements they read as (CR-002 section 60 D1), each
1049
+ * in its run where it stood: a w:t holding one is cut around it. Characters outside the span are left as they are.
1050
+ */
1051
+ writeElements(from, to) {
1052
+ var _a;
1053
+ if (to <= from)
1054
+ return;
1055
+ for (const seg of this.segments().reverse()) {
1056
+ if (!seg.editable || seg.end <= from || seg.start >= to)
1057
+ continue;
1058
+ const value = seg.text;
1059
+ const a = Math.max(0, from - seg.start);
1060
+ const b = Math.min(value.length, to - seg.start);
1061
+ const inside = value.substring(a, b);
1062
+ if (!holdsElementCharacters(inside))
1063
+ continue;
1064
+ const items = [
1065
+ ...(a > 0 ? [textItem(value.substring(0, a))] : []),
1066
+ ...officeItems(inside),
1067
+ ...(b < value.length ? [textItem(value.substring(b))] : []),
1068
+ ];
1069
+ const parent = (_a = seg.item.value.PARENT) !== null && _a !== void 0 ? _a : seg.run;
1070
+ seg.owner.splice(seg.index, 1, ...items);
1071
+ linkParents(items, parent);
1072
+ }
1073
+ }
1074
+ /**
1075
+ * The first run's properties, for text with no run beside it to take its look from; none where that run is hidden,
1076
+ * so that text put into a paragraph whose every run is hidden can be seen (CR-002 section 60: the text was hidden
1077
+ * with it, so read as nothing, and its characters escaped the map).
1078
+ */
1079
+ visibleFirstRunRPr() {
1080
+ var _a;
1081
+ const first = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value;
1082
+ return first && !this.hiddenRuns()(first) ? first.rPr : undefined;
1083
+ }
1084
+ /** `splice` before section 60: the text put in as it is, as one stretch of a w:t's text. */
1085
+ spliceText(start, end, text, prefer = 'before', edges) {
1086
+ var _a, _b, _c, _d, _e;
1087
+ var _f;
610
1088
  const length = this.text.length;
611
1089
  start = Math.max(0, Math.min(start, length));
612
1090
  end = Math.max(start, Math.min(end, length));
613
1091
  const tracker = this.changeTracker;
1092
+ this.assertPlaceable(start, end, tracker === undefined ? false : text.length > 0 ? 'replace' : 'delete');
1093
+ // a run-level control showing its placeholder that the span holds whole goes with it, as Word takes one; tracked,
1094
+ // `deleteText` marks it deleted (CR-002 section 63, D3; check 63 cases 19, 20 and 26)
1095
+ if (!tracker && (end > start || edges))
1096
+ for (const control of this.placeholderControlsIn(start, end, edges))
1097
+ control.takeWhole();
614
1098
  if (tracker) {
615
1099
  // a replacement is the w:del first and the w:ins after it, as Word writes one
616
1100
  const deletions = end > start || edges ? this.deleteText(tracker, start, end, edges) : [];
@@ -621,14 +1105,16 @@ export class Paragraph {
621
1105
  let segs = this.segments();
622
1106
  let inserted = false;
623
1107
  const objects = this.segments({ objects: true }).filter((s) => s.object && objectInSpan(s.start, start, end, edges));
1108
+ // a comment's reference in the span goes with the text, and its comment with it (CR-014 section 12.6)
1109
+ const references = end > start || edges ? this.commentReferencesIn(start, end, edges) : [];
624
1110
  // A replacement that begins inside a revision is a deletion and then the new text typed there, so
625
1111
  // the new text is plain and splits the revision, as typed text is (test/README.md check 21), rather
626
1112
  // than joining the revision it replaced part of.
627
1113
  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);
1114
+ this.spliceText(start, end, '', prefer, edges);
1115
+ return this.spliceText(start, start, text);
630
1116
  }
631
- if (end > start || objects.length > 0) {
1117
+ if (end > start || objects.length > 0 || references.length > 0) {
632
1118
  // Replace: the new text takes the place (and formatting) of the first replaced character, as Word does.
633
1119
  const first = segs.find((s) => s.start <= start && start < s.end);
634
1120
  const toRemove = [];
@@ -656,6 +1142,13 @@ export class Paragraph {
656
1142
  if (i >= 0)
657
1143
  seg.owner.splice(i, 1);
658
1144
  }
1145
+ removeEmptiedAlternates([...toRemove, ...objects]);
1146
+ for (const { run, owner, id } of references) {
1147
+ const i = owner.indexOf(run);
1148
+ if (i >= 0)
1149
+ owner.splice(i, 1);
1150
+ this.queueCommentRemoval(id);
1151
+ }
659
1152
  this.releaseRelationships(objects.map((seg) => seg.item));
660
1153
  if (inserted && text.length > 0) {
661
1154
  this.removeEmptyRuns();
@@ -696,7 +1189,7 @@ export class Paragraph {
696
1189
  const before = [...segs].reverse().find((s) => s.end <= start);
697
1190
  const after = segs.find((s) => s.start >= start);
698
1191
  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;
1192
+ const firstRun = this.visibleFirstRunRPr();
700
1193
  const rPr = (neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) ? deepCopy(neighbour.run.rPr) : firstRun ? deepCopy(firstRun) : undefined;
701
1194
  const run = runOf([textItem(text)], rPr);
702
1195
  if (neighbour === before && before)
@@ -704,7 +1197,7 @@ export class Paragraph {
704
1197
  else if (after)
705
1198
  after.runOwner.splice(after.runIndex, 0, run);
706
1199
  else {
707
- const content = ((_f = (_g = this.p).content) !== null && _f !== void 0 ? _f : (_g.content = []));
1200
+ const content = ((_e = (_f = this.p).content) !== null && _e !== void 0 ? _e : (_f.content = []));
708
1201
  if (start === 0)
709
1202
  content.unshift(run);
710
1203
  else
@@ -722,8 +1215,10 @@ export class Paragraph {
722
1215
  insertItemsAt(offset, items) {
723
1216
  var _a, _b, _c, _d;
724
1217
  var _e;
1218
+ this.assertWritable();
725
1219
  if (items.length === 0)
726
1220
  return;
1221
+ this.assertPlaceable(offset, offset, false);
727
1222
  const tracker = this.changeTracker;
728
1223
  if (tracker) {
729
1224
  // With tracking on, what goes in is an insertion: its runs in w:ins, in a link, a control or a
@@ -753,18 +1248,46 @@ export class Paragraph {
753
1248
  /** Splits the run at a text offset so that [offset, ...) begins a run; returns nothing when the offset is already a boundary. */
754
1249
  splitAt(offset) {
755
1250
  var _a;
1251
+ this.assertWritable();
1252
+ this.assertPlaceable(offset, offset, false);
756
1253
  const seg = this.segments().find((s) => s.editable && s.start < offset && offset < s.end);
757
1254
  if (!seg)
758
1255
  return;
759
1256
  const at = offset - seg.start;
760
1257
  const t = seg.item.value;
761
1258
  const head = seg.text.substring(0, at);
762
- const tail = seg.text.substring(at);
1259
+ const tail = textItem(seg.text.substring(at));
763
1260
  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);
1261
+ seg.owner.splice(seg.index + 1, 0, tail);
1262
+ linkParents(tail, (_a = t.PARENT) !== null && _a !== void 0 ? _a : seg.run);
1263
+ this.splitRunAtItem(seg, seg.owner, seg.index + 1);
1264
+ }
1265
+ /**
1266
+ * Splits `seg`'s run so that the items of `owner` from `index` on begin a run of their own, behind it.
1267
+ * `owner` is the run's content, or the selected branch of a run-level `mc:AlternateContent` in it
1268
+ * (CR-012 section 12.2): a run holds the element whole, so the branch splits with it, as the editor's
1269
+ * ED-009 section 3.4 has it. The first piece keeps the element, its other branches as they were; the
1270
+ * second gets an element of its own, the same branches in the same order with the same `Requires`, the
1271
+ * selected one holding the rest of its items and the others empty, so that a reader taking any branch
1272
+ * takes the same one in both. The run's items after the element go with the second piece.
1273
+ */
1274
+ splitRunAtItem(seg, owner, index) {
1275
+ var _a, _b;
1276
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && owner === seg.alternate.branch ? seg.alternate : undefined;
1277
+ let moved;
1278
+ if (alternate) {
1279
+ const piece = alternatePiece(alternate, owner.splice(index));
1280
+ const at = alternate.owner.indexOf(alternate.element);
1281
+ moved = [piece, ...alternate.owner.splice(at + 1)];
1282
+ }
1283
+ else {
1284
+ moved = owner.splice(index);
1285
+ }
1286
+ if (moved.length === 0)
1287
+ return;
1288
+ const second = runOf(moved, seg.run.rPr ? deepCopy(seg.run.rPr) : undefined);
766
1289
  seg.runOwner.splice(seg.runIndex + 1, 0, second);
767
- linkParents(second, (_a = seg.run.PARENT) !== null && _a !== void 0 ? _a : this.p);
1290
+ linkParents(second, (_b = seg.run.PARENT) !== null && _b !== void 0 ? _b : this.p);
768
1291
  }
769
1292
  // --- lists (CR-002 phase H) ---
770
1293
  /**
@@ -801,14 +1324,17 @@ export class Paragraph {
801
1324
  * unmarshalled (CR-002 section 17).
802
1325
  */
803
1326
  startNewList(options) {
1327
+ this.assertWritable();
804
1328
  return startNewList(this, options);
805
1329
  }
806
1330
  /** Office JS `paragraph.attachToList(listId, level)`: this paragraph joins that list. */
807
1331
  attachToList(listId, level = 0) {
1332
+ this.assertWritable();
808
1333
  return attachToList(this, listId, level);
809
1334
  }
810
1335
  /** Office JS `paragraph.detachFromList()`: this paragraph leaves its list. */
811
1336
  detachFromList() {
1337
+ this.assertWritable();
812
1338
  detachFromList(this);
813
1339
  }
814
1340
  /**
@@ -816,6 +1342,7 @@ export class Paragraph {
816
1342
  * `w:abstractNum` with a `w:startOverride` (extension; `List.restart()` is the verb).
817
1343
  */
818
1344
  restartList() {
1345
+ this.assertWritable();
819
1346
  const list = this.list;
820
1347
  const level = this.listItem.level;
821
1348
  const restarted = list.restart();
@@ -829,6 +1356,7 @@ export class Paragraph {
829
1356
  }
830
1357
  /** Comments the whole paragraph (sugar over `getRange().insertComment`). */
831
1358
  async insertComment(content, options) {
1359
+ this.assertWritable();
832
1360
  return commentApi().insertComment(this.getRange(), content, options);
833
1361
  }
834
1362
  // --- content controls (CR-002 phase E) ---
@@ -839,18 +1367,32 @@ export class Paragraph {
839
1367
  */
840
1368
  insertContentControl(kind) {
841
1369
  var _a;
1370
+ this.assertWritable();
842
1371
  const at = this.index;
843
1372
  if (at < 0)
844
1373
  throw new Docx4JException('This paragraph is not in its container any more');
1374
+ // the paragraph's parent read before the builder makes the control its parent: read after, it was the
1375
+ // control's own content, and the control's PARENT pointed into itself (found in CR-012 phase 4)
1376
+ const parent = (_a = this.p.PARENT) !== null && _a !== void 0 ? _a : this.parentBody.container;
1377
+ // a bound block control holds one paragraph (CR-002 section 61): checked before sdtOf, which takes the paragraph
1378
+ assertControlHolds(this.parentBody, parent, this.container.filter((e) => e !== this.element), [BLOCK_CONTROL], undefined, ['Paragraph', 'insertContentControl']);
1379
+ const empty = this.segments({ objects: true }).length === 0;
845
1380
  const sdt = sdtOf([this.element], { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this.parentBody)), form: 'block' });
846
1381
  this.container.splice(at, 1, sdt);
847
- linkParents(sdt, (_a = this.p.PARENT) !== null && _a !== void 0 ? _a : this.parentBody.container);
1382
+ linkParents(sdt, parent);
848
1383
  declareSdtExtensions(this.parentBody, sdt);
849
1384
  // under tracking the control itself is the insertion, the paragraph what it was (check 30)
850
1385
  const tracker = this.changeTracker;
851
1386
  if (tracker)
852
1387
  markHolderInserted(tracker, sdt, this.container, () => { });
853
- return new ContentControl(sdt, this.container, this.parentBody);
1388
+ const control = new ContentControl(sdt, this.container, this.parentBody);
1389
+ // around an empty paragraph, a control showing Word's default placeholder, as Word's (CR-002 section 63, D8; check 64);
1390
+ // a picture control Word's form, the flag and the placeholder picture (CR-005 section 10, D2; docx4j's check 71 case 8)
1391
+ if (empty && showsPlaceholderWhenMade(kind))
1392
+ control.showPlaceholder();
1393
+ else if (empty && kind === 'Picture')
1394
+ showPicturePlaceholder(control);
1395
+ return control;
854
1396
  }
855
1397
  removeEmptyRuns() {
856
1398
  var _a;
@@ -862,6 +1404,16 @@ export class Paragraph {
862
1404
  if (!v.content || v.content.length === 0)
863
1405
  items.splice(i, 1);
864
1406
  }
1407
+ else if (isAlternateContent(el)) {
1408
+ // among the runs: a selected branch an edit has emptied takes the element, every branch with
1409
+ // it, as a deletion of all of a run-level element's text does (CR-012 section 12.2 item 4)
1410
+ const branch = selectedBranchOf(el.value);
1411
+ if (!branch || branch.length === 0)
1412
+ continue;
1413
+ prune(branch);
1414
+ if (branch.length === 0)
1415
+ items.splice(i, 1);
1416
+ }
865
1417
  else if (v.TYPE_NAME !== 'org_docx4j_wml.P') {
866
1418
  const nested = runItemsOf(v);
867
1419
  if (nested) {
@@ -881,16 +1433,28 @@ export class Paragraph {
881
1433
  * turned into `w:delText`, and each run of consecutive ones moved into one `w:del`. Text this
882
1434
  * author had inserted is simply removed, as Word does, rather than nested in a `w:del`.
883
1435
  * 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).
1436
+ * where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48). An equation is deleted where
1437
+ * it stands, in Word's form, a `w:del` in each math run and each structure's `m:ctrlPr` (`markEquationDeleted`;
1438
+ * 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
1439
+ * `w:del` whole, as every equation did before.
885
1440
  * Returns the `w:del` elements it made, in document order.
886
1441
  */
887
1442
  deleteText(tracker, start, end, edges) {
888
- var _a, _b, _c, _d, _e;
1443
+ var _a, _b, _c, _d, _e, _f, _g;
1444
+ // a run-level control showing its placeholder that the span holds whole is marked deleted whole, in Word's markers,
1445
+ // its placeholder gone unmarked (CR-002 section 63, D3; check 63's run 2, cases 45, 46 and 52)
1446
+ for (const control of this.placeholderControlsIn(start, end, edges))
1447
+ control.takeWhole();
889
1448
  const targets = this.isolate(start, end, edges);
890
1449
  for (const seg of targets)
891
1450
  tracker.assertEditable(seg.revision);
892
1451
  const list = [];
1452
+ const equations = [];
893
1453
  for (const seg of targets) {
1454
+ if (seg.math && !tracker.ownInsertion(seg.revision) && equationRevisionIn(seg.item.value) === undefined) {
1455
+ equations.push(seg.item.value);
1456
+ continue;
1457
+ }
894
1458
  const run = seg.runOwner[seg.runIndex];
895
1459
  if (list.length > 0 && list[list.length - 1].run === run)
896
1460
  continue;
@@ -899,6 +1463,22 @@ export class Paragraph {
899
1463
  target.revision = seg.revision;
900
1464
  list.push(target);
901
1465
  }
1466
+ // a comment's reference in the span goes into the w:del with the text, where it stands among the runs, as Word
1467
+ // puts it (check 54 case 28; CR-014 section 12.6); accepting the deletion then takes the comment
1468
+ const references = this.commentReferencesIn(start, end, edges);
1469
+ if (references.length > 0) {
1470
+ const order = runOrderOf(this.p);
1471
+ const revisions = new Map(targets.filter((seg) => seg.revision).map((seg) => [seg.runOwner, seg.revision]));
1472
+ for (const { run, owner } of references) {
1473
+ const revision = (_a = revisions.get(owner)) !== null && _a !== void 0 ? _a : revisionHolding(run);
1474
+ const target = { run, owner, own: tracker.ownInsertion(revision) };
1475
+ if (revision)
1476
+ target.revision = revision;
1477
+ const key = (_b = order.get(run)) !== null && _b !== void 0 ? _b : Number.NaN;
1478
+ const at = list.findIndex((t) => { var _a; return ((_a = order.get(t.run)) !== null && _a !== void 0 ? _a : Number.NaN) > key; });
1479
+ list.splice(at < 0 ? list.length : at, 0, target);
1480
+ }
1481
+ }
902
1482
  // groups of consecutive runs in the same array, so that one w:del holds them all
903
1483
  const groups = [];
904
1484
  for (const t of list) {
@@ -910,6 +1490,8 @@ export class Paragraph {
910
1490
  else
911
1491
  groups.push([t]);
912
1492
  }
1493
+ for (const equation of equations)
1494
+ markEquationDeleted(tracker, equation);
913
1495
  const made = [];
914
1496
  for (let g = groups.length - 1; g >= 0; g--) {
915
1497
  const group = groups[g];
@@ -918,8 +1500,11 @@ export class Paragraph {
918
1500
  const runs = owner.splice(at, group.length);
919
1501
  if (group[0].own) {
920
1502
  // 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)
1503
+ // image waits for the save, as `InlinePicture.delete` leaves it (section 47); a comment whose
1504
+ // reference goes with them goes too
922
1505
  this.releaseRelationships(runs);
1506
+ for (const id of commentReferenceIds(runs))
1507
+ this.queueCommentRemoval(id);
923
1508
  const revision = group[0].revision;
924
1509
  if (revision && revision.items.length === 0) {
925
1510
  const i = revision.owner.indexOf(revision.element);
@@ -932,15 +1517,60 @@ export class Paragraph {
932
1517
  toDeletedText(run.value);
933
1518
  const del = tracker.del(runs);
934
1519
  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));
1520
+ 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
1521
  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) };
1522
+ 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
1523
  if (revision && revision.items === owner)
939
1524
  anchor.revision = revision;
940
1525
  made.unshift(anchor);
941
1526
  }
942
1527
  return made;
943
1528
  }
1529
+ /**
1530
+ * The run-level controls showing their placeholder that [start, end) holds whole: where the walk passes one strictly
1531
+ * inside the span, or at an edge `edges` says it holds. A placeholder is no text, so such a control has no width
1532
+ * (CR-002 section 63, D3).
1533
+ */
1534
+ placeholderControlsIn(start, end, edges) {
1535
+ const found = [];
1536
+ this.segments({ visit: (item, at) => {
1537
+ var _a;
1538
+ if (typeNameOf(item) !== 'org_docx4j_wml.SdtRun' || !objectInSpan(at, start, end, edges))
1539
+ return;
1540
+ const sdtPr = item.value.sdtPr;
1541
+ const flag = sdtProperty(sdtPr, 'showingPlcHdr');
1542
+ if (flag !== undefined && ((_a = flag.value) === null || _a === void 0 ? void 0 : _a.val) !== false)
1543
+ found.push(item.value);
1544
+ } });
1545
+ return found.map((sdt) => controlViewOf(sdt, this.parentBody));
1546
+ }
1547
+ /**
1548
+ * The runs of comments' references in [start, end): each a run holding nothing but a reference, where the walk
1549
+ * passes it, strictly inside the span or at an edge `edges` says it holds. A reference has no text, so a
1550
+ * deletion's segments never held it, and a comment whose reference a deletion took stayed. Word's deletion takes
1551
+ * it with the text (check 54 cases 27 and 28; CR-014 section 12.6).
1552
+ */
1553
+ commentReferencesIn(start, end, edges) {
1554
+ const out = [];
1555
+ this.segments({ visit: (item, at, _owner, run) => {
1556
+ var _a, _b;
1557
+ if (((_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart) !== 'commentReference' || !run || !objectInSpan(at, start, end, edges))
1558
+ return;
1559
+ if (out.some((r) => r.run === run.element))
1560
+ return;
1561
+ 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'; }))
1562
+ return;
1563
+ out.push({ run: run.element, owner: run.owner, id: Number(item.value.id) });
1564
+ } });
1565
+ return out;
1566
+ }
1567
+ /** A comment queued to go when the package is saved, its markers, replies and entries with it, as a resolution queues one. */
1568
+ queueCommentRemoval(id) {
1569
+ var _a, _b;
1570
+ if (!Number.isInteger(id))
1571
+ return;
1572
+ (_b = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.pendingCommentRemovals) === null || _b === void 0 ? void 0 : _b.add(id);
1573
+ }
944
1574
  /**
945
1575
  * Splits the revision that holds the text at `at` into two - the second half a copy of its
946
1576
  * attributes under a new `w:id` - and puts `item` between them. Word splits a revision so when
@@ -1030,7 +1660,7 @@ export class Paragraph {
1030
1660
  * of a replacement, which the insertion must follow.
1031
1661
  */
1032
1662
  insertTracked(tracker, at, text, anchor, prefer = 'before') {
1033
- var _a, _b, _c;
1663
+ var _a, _b;
1034
1664
  if (anchor) {
1035
1665
  const rPr = copyRPr((_a = runsOf(anchor.element.value, { view: 'original' })[0]) === null || _a === void 0 ? void 0 : _a.value.rPr);
1036
1666
  const ins = tracker.ins([runOf([textItem(text)], rPr)]);
@@ -1066,7 +1696,7 @@ export class Paragraph {
1066
1696
  }
1067
1697
  const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
1068
1698
  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);
1699
+ const rPr = copyRPr((_b = neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) !== null && _b !== void 0 ? _b : this.visibleFirstRunRPr());
1070
1700
  const ins = tracker.ins([runOf([textItem(text)], rPr)]);
1071
1701
  this.placeInsertion(at, [ins], before, after, () => tracker.nextId(), prefer);
1072
1702
  }
@@ -1106,6 +1736,13 @@ export class Paragraph {
1106
1736
  linkParents(items, this.p);
1107
1737
  }
1108
1738
  }
1739
+ /**
1740
+ * Splits runs so that every run holding text in [start, end) holds nothing else (internal: what a
1741
+ * `Range.font` write formats). A kept `mc:AlternateContent`'s branch splits with its run (section 12.2).
1742
+ */
1743
+ splitRunsAround(start, end) {
1744
+ this.isolate(start, end);
1745
+ }
1109
1746
  /**
1110
1747
  * Splits runs so that every run holding text in [start, end) holds nothing else, and returns
1111
1748
  * the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
@@ -1125,16 +1762,16 @@ export class Paragraph {
1125
1762
  if (seen.has(seg.run))
1126
1763
  continue;
1127
1764
  seen.add(seg.run);
1765
+ if (seg.math)
1766
+ continue; // in no run: taken whole or refused
1128
1767
  const ofRun = inside.filter((s) => s.run === seg.run);
1129
1768
  const first = ofRun[0];
1130
1769
  const last = ofRun[ofRun.length - 1];
1131
- if (first.index > 0) {
1132
- this.splitRunBefore(first);
1770
+ if (this.splitRunBefore(first)) {
1133
1771
  split = true;
1134
1772
  break;
1135
1773
  }
1136
- if (last.index < last.owner.length - 1) {
1137
- this.splitRunAfter(last);
1774
+ if (this.splitRunAfter(last)) {
1138
1775
  split = true;
1139
1776
  break;
1140
1777
  }
@@ -1144,23 +1781,58 @@ export class Paragraph {
1144
1781
  }
1145
1782
  return this.segments({ objects: true }).filter(inSpan);
1146
1783
  }
1147
- /** Moves the items before `seg` into a run of their own, in front of `seg`'s run. */
1784
+ /**
1785
+ * Refuses an edit that would cut an equation (CR-012 phase 4). Its text is one segment, Word's drawing of
1786
+ * its math runs (`mathTextOf`), so an edit may take it whole but not part of it, nor insert into it. Under change
1787
+ * tracking a deletion takes it whole in Word's form (`deleteText`, CR-002 section 62.2 D2, check 57), and a
1788
+ * replacement doesn't take it at all: where Word puts the new text beside a deleted equation isn't measured. Since
1789
+ * phase 4 every other offset is a place in the runs: `text` is the segments' text, where before it was the objects
1790
+ * package's `textOf` and section 12.4 refused an offset past a kept `mc:AlternateContent` among the runs.
1791
+ */
1792
+ assertPlaceable(start, end, tracked) {
1793
+ for (const seg of this.segments()) {
1794
+ if (!seg.math)
1795
+ continue;
1796
+ const cuts = (seg.start < start && start < seg.end) || (seg.start < end && end < seg.end);
1797
+ const takes = start < seg.end && seg.start < end;
1798
+ if (!cuts && !(tracked === 'replace' && takes))
1799
+ continue;
1800
+ throw new NotSupportedError('Paragraph', 'edit', `[${start}, ${end}) ${cuts ? 'cuts' : 'takes'} the equation at [${seg.start}, ${seg.end})`
1801
+ + (cuts ? ': an edit takes an equation whole or not at all (CR-012 phase 4)'
1802
+ : ' under change tracking, with text in its place: a tracked replacement of an equation is not measured (CR-002 section 62.2)'));
1803
+ }
1804
+ }
1805
+ /** Moves the items before `seg` into a run of their own, in front of the rest; false where there are none. */
1148
1806
  splitRunBefore(seg) {
1149
1807
  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);
1808
+ if (seg.index > 0) {
1809
+ this.splitRunAtItem(seg, seg.owner, seg.index);
1810
+ return true;
1811
+ }
1812
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
1813
+ if (!alternate)
1814
+ return false;
1815
+ const at = alternate.owner.indexOf(alternate.element);
1816
+ if (at <= 0)
1817
+ return false;
1818
+ this.splitRunAtItem(seg, alternate.owner, at);
1819
+ return true;
1154
1820
  }
1155
- /** Moves the items after `seg` into a run of their own, behind `seg`'s run. */
1821
+ /** Moves the items after `seg` into a run of their own, behind it; false where there are none. */
1156
1822
  splitRunAfter(seg) {
1157
1823
  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);
1824
+ if (seg.index < seg.owner.length - 1) {
1825
+ this.splitRunAtItem(seg, seg.owner, seg.index + 1);
1826
+ return true;
1827
+ }
1828
+ const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
1829
+ if (!alternate)
1830
+ return false;
1831
+ const at = alternate.owner.indexOf(alternate.element);
1832
+ if (at < 0 || at >= alternate.owner.length - 1)
1833
+ return false;
1834
+ this.splitRunAtItem(seg, alternate.owner, at + 1);
1835
+ return true;
1164
1836
  }
1165
1837
  }
1166
1838
  export { W_NS };
@@ -1212,3 +1884,127 @@ function setText(t, value) {
1212
1884
  else
1213
1885
  delete t.space;
1214
1886
  }
1887
+ /**
1888
+ * The element a run-level `mc:AlternateContent`'s later piece is written as (CR-012 section 12.2; the
1889
+ * editor's ED-009 section 3.4, P2): its branches in order with their `Requires`, the selected one holding
1890
+ * `items` and every other one empty.
1891
+ */
1892
+ function alternatePiece(alternate, items) {
1893
+ const ac = alternate.element.value;
1894
+ const emptied = (branch) => {
1895
+ if (branch === undefined)
1896
+ return undefined;
1897
+ const copy = {};
1898
+ for (const [key, value] of Object.entries(branch))
1899
+ if (key !== 'any' && key !== 'PARENT')
1900
+ copy[key] = value;
1901
+ copy.any = branch.any === alternate.branch ? items : [];
1902
+ return copy;
1903
+ };
1904
+ const value = {};
1905
+ for (const [key, v] of Object.entries(ac)) {
1906
+ if (key === 'PARENT')
1907
+ continue;
1908
+ if (key === 'choice')
1909
+ value.choice = v.map(emptied);
1910
+ else if (key === 'fallback')
1911
+ value.fallback = emptied(v);
1912
+ else
1913
+ value[key] = v;
1914
+ }
1915
+ return { name: alternate.element.name, value };
1916
+ }
1917
+ /**
1918
+ * After an untracked deletion: a run-level `mc:AlternateContent` whose selected branch the deletion
1919
+ * emptied goes, every branch with it (CR-012 section 12.2 item 4; ED-009 section 3.5). A branch that
1920
+ * was empty before is left as it is.
1921
+ */
1922
+ function removeEmptiedAlternates(removed) {
1923
+ const seen = new Set();
1924
+ for (const seg of removed) {
1925
+ const alternate = seg.alternate;
1926
+ if ((alternate === null || alternate === void 0 ? void 0 : alternate.level) !== 'run' || seen.has(alternate.element))
1927
+ continue;
1928
+ seen.add(alternate.element);
1929
+ if (alternate.branch.length > 0)
1930
+ continue;
1931
+ const i = alternate.owner.indexOf(alternate.element);
1932
+ if (i >= 0)
1933
+ alternate.owner.splice(i, 1);
1934
+ }
1935
+ }
1936
+ /** Block holders a cell's paragraph may stand in: a block content control, its content, custom XML. */
1937
+ const BLOCK_HOLDERS = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtContentBlock', 'org_docx4j_wml.CTSdtContentBlock', 'org_docx4j_wml.CTCustomXmlBlock']);
1938
+ /** True for the last paragraph of a table cell, through the block holders between them. */
1939
+ function lastInCell(p) {
1940
+ var _a;
1941
+ let holder = p.PARENT;
1942
+ for (let guard = 0; holder && guard < 16 && BLOCK_HOLDERS.has((_a = holder.TYPE_NAME) !== null && _a !== void 0 ? _a : ''); guard++) {
1943
+ holder = holder.PARENT;
1944
+ }
1945
+ if ((holder === null || holder === void 0 ? void 0 : holder.TYPE_NAME) !== 'org_docx4j_wml.Tc')
1946
+ return false;
1947
+ let last;
1948
+ const visit = (items) => {
1949
+ for (const el of items !== null && items !== void 0 ? items : []) {
1950
+ const tn = typeNameOf(el);
1951
+ if (tn === 'org_docx4j_wml.P')
1952
+ last = el.value;
1953
+ else if (tn !== 'org_docx4j_wml.Tbl' && typeof el.value === 'object' && el.value !== null) {
1954
+ const v = el.value;
1955
+ visit(v.sdtContent ? v.sdtContent.content : v.content);
1956
+ }
1957
+ }
1958
+ };
1959
+ visit(holder.content);
1960
+ return last === p;
1961
+ }
1962
+ /** Every element among a paragraph's runs, in document order: where a deletion places a comment's reference among its runs. */
1963
+ function runOrderOf(p) {
1964
+ const order = new Map();
1965
+ const visit = (items) => {
1966
+ for (const el of items !== null && items !== void 0 ? items : []) {
1967
+ order.set(el, order.size);
1968
+ const v = el.value;
1969
+ if (typeof v === 'object' && v !== null && typeNameOf(el) !== 'org_docx4j_wml.R')
1970
+ visit(runItemsOf(v));
1971
+ }
1972
+ };
1973
+ visit(runItemsOf(p));
1974
+ return order;
1975
+ }
1976
+ /** The `w:ins` (or other revision) a run stands directly in, read from its `PARENT`, if any. */
1977
+ function revisionHolding(run) {
1978
+ const holder = run.value.PARENT;
1979
+ const items = holder === null || holder === void 0 ? void 0 : holder.customXmlOrSmartTagOrSdt;
1980
+ const outer = (holder === null || holder === void 0 ? void 0 : holder.PARENT) ? runItemsOf(holder.PARENT) : undefined;
1981
+ const element = outer === null || outer === void 0 ? void 0 : outer.find((e) => e.value === holder);
1982
+ const kind = element ? revisionKindOf(element) : undefined;
1983
+ if (!holder || !items || !outer || !element || !kind)
1984
+ return undefined;
1985
+ return { kind, element, value: holder, items, owner: outer };
1986
+ }
1987
+ /** The ids of the comment references among runs. */
1988
+ function commentReferenceIds(runs) {
1989
+ return runs.flatMap((run) => {
1990
+ var _a;
1991
+ return ((_a = run.value.content) !== null && _a !== void 0 ? _a : [])
1992
+ .filter((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference'; }).map((c) => Number(c.value.id));
1993
+ });
1994
+ }
1995
+ /** The `w:sdt` value types, block, run, row and cell. */
1996
+ const SDT_VALUE_TYPES = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtRun', 'org_docx4j_wml.CTSdtRow', 'org_docx4j_wml.CTSdtCell']);
1997
+ /** Whether a paragraph holds a run-level control bound by `w:dataBinding`, at any depth among its runs. */
1998
+ function holdsBoundRunControl(holder) {
1999
+ var _a;
2000
+ for (const item of (_a = runItemsOf(holder)) !== null && _a !== void 0 ? _a : []) {
2001
+ const value = item.value;
2002
+ if (!value || typeof value !== 'object')
2003
+ continue;
2004
+ if (value.TYPE_NAME === 'org_docx4j_wml.SdtRun' && sdtProperty(value.sdtPr, 'dataBinding') !== undefined)
2005
+ return true;
2006
+ if (value.TYPE_NAME !== 'org_docx4j_wml.R' && holdsBoundRunControl(value))
2007
+ return true;
2008
+ }
2009
+ return false;
2010
+ }