@docx4j/core-ts 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +34 -3
  3. package/dist/html/convert.mjs +40 -15
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +153 -19
  24. package/dist/model/content/Body.mjs +570 -131
  25. package/dist/model/content/Comment.d.mts +21 -3
  26. package/dist/model/content/Comment.mjs +202 -19
  27. package/dist/model/content/ContentControl.d.mts +197 -18
  28. package/dist/model/content/ContentControl.mjs +664 -95
  29. package/dist/model/content/Font.d.mts +67 -17
  30. package/dist/model/content/Font.mjs +114 -35
  31. package/dist/model/content/InlinePicture.d.mts +82 -7
  32. package/dist/model/content/InlinePicture.mjs +221 -28
  33. package/dist/model/content/List.mjs +13 -1
  34. package/dist/model/content/Paragraph.d.mts +213 -14
  35. package/dist/model/content/Paragraph.mjs +936 -100
  36. package/dist/model/content/Range.d.mts +242 -11
  37. package/dist/model/content/Range.mjs +975 -119
  38. package/dist/model/content/ResolvedView.d.mts +104 -0
  39. package/dist/model/content/ResolvedView.mjs +596 -0
  40. package/dist/model/content/Table.d.mts +29 -3
  41. package/dist/model/content/Table.mjs +109 -33
  42. package/dist/model/content/TrackedChange.d.mts +301 -16
  43. package/dist/model/content/TrackedChange.mjs +1284 -158
  44. package/dist/model/content/binder.d.mts +2 -1
  45. package/dist/model/content/binder.mjs +48 -1
  46. package/dist/model/content/comments.d.mts +20 -6
  47. package/dist/model/content/comments.mjs +67 -57
  48. package/dist/model/content/generalException.d.mts +18 -0
  49. package/dist/model/content/generalException.mjs +24 -0
  50. package/dist/model/content/index.d.mts +8 -3
  51. package/dist/model/content/index.mjs +7 -2
  52. package/dist/model/content/mathText.d.mts +21 -0
  53. package/dist/model/content/mathText.mjs +267 -0
  54. package/dist/model/content/officeText.d.mts +27 -0
  55. package/dist/model/content/officeText.mjs +74 -0
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/placeholders.d.mts +110 -0
  59. package/dist/model/content/placeholders.mjs +253 -0
  60. package/dist/model/content/relationships.d.mts +30 -0
  61. package/dist/model/content/relationships.mjs +133 -0
  62. package/dist/model/content/search.d.mts +72 -0
  63. package/dist/model/content/search.mjs +204 -2
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +44 -0
  67. package/dist/model/content/story.mjs +92 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +100 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +28 -0
  79. package/dist/model/content/tracking.mjs +149 -10
  80. package/dist/model/content/tree.d.mts +212 -13
  81. package/dist/model/content/tree.mjs +356 -35
  82. package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
  83. package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
  84. package/dist/model/customxml/XmlMapping.d.mts +1 -1
  85. package/dist/model/customxml/XmlMapping.mjs +11 -0
  86. package/dist/model/customxml/bindings.d.mts +21 -2
  87. package/dist/model/customxml/bindings.mjs +76 -32
  88. package/dist/model/customxml/kinds.d.mts +8 -3
  89. package/dist/model/customxml/kinds.mjs +26 -5
  90. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  91. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  92. package/dist/model/customxml/pictures.d.mts +9 -5
  93. package/dist/model/customxml/pictures.mjs +31 -13
  94. package/dist/model/customxml/xhtml.mjs +8 -2
  95. package/dist/model/fonts/families.generated.mjs +2 -2
  96. package/dist/model/fonts/registry.mjs +7 -1
  97. package/dist/model/fonts/substitutions.generated.mjs +2 -2
  98. package/dist/model/fonts/themes.generated.mjs +1 -1
  99. package/dist/model/index.d.mts +2 -0
  100. package/dist/model/index.mjs +4 -0
  101. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  102. package/dist/model/listnumbering/Emulator.mjs +63 -9
  103. package/dist/model/listnumbering/definitions.mjs +18 -0
  104. package/dist/model/properties/PropertyResolver.d.mts +87 -7
  105. package/dist/model/properties/PropertyResolver.mjs +197 -17
  106. package/dist/model/properties/catalogue.d.mts +32 -4
  107. package/dist/model/properties/catalogue.mjs +115 -36
  108. package/dist/model/properties/log.d.mts +1 -1
  109. package/dist/model/properties/log.mjs +5 -5
  110. package/dist/model/properties/table.d.mts +16 -2
  111. package/dist/model/properties/table.mjs +17 -5
  112. package/dist/model/text/Extractor.d.mts +289 -0
  113. package/dist/model/text/Extractor.mjs +2162 -0
  114. package/dist/model/text/LinkTargets.d.mts +45 -0
  115. package/dist/model/text/LinkTargets.mjs +170 -0
  116. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  117. package/dist/model/text/MarkdownExporter.mjs +255 -0
  118. package/dist/model/text/PlainText.d.mts +29 -0
  119. package/dist/model/text/PlainText.mjs +539 -0
  120. package/dist/model/text/TextContext.d.mts +161 -0
  121. package/dist/model/text/TextContext.mjs +425 -0
  122. package/dist/model/text/TextOptions.d.mts +150 -0
  123. package/dist/model/text/TextOptions.mjs +278 -0
  124. package/dist/model/text/TextResult.d.mts +504 -0
  125. package/dist/model/text/TextResult.mjs +526 -0
  126. package/dist/model/text/Texts.d.mts +99 -0
  127. package/dist/model/text/Texts.mjs +187 -0
  128. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  129. package/dist/model/text/WmlToMarkdown.mjs +911 -0
  130. package/dist/model/text/commonmark.d.mts +121 -0
  131. package/dist/model/text/commonmark.mjs +655 -0
  132. package/dist/model/text/index.d.mts +7 -0
  133. package/dist/model/text/index.mjs +8 -0
  134. package/dist/model/text/normalize.d.mts +8 -0
  135. package/dist/model/text/normalize.mjs +13 -0
  136. package/dist/model/text/outline.d.mts +4 -0
  137. package/dist/model/text/outline.mjs +71 -0
  138. package/dist/office-js/fragment.d.mts +7 -5
  139. package/dist/office-js/fragment.mjs +63 -14
  140. package/dist/office-js/proxy.d.mts +8 -0
  141. package/dist/office-js/proxy.mjs +29 -0
  142. package/dist/office-js/run.mjs +2 -0
  143. package/dist/office-js/supported.generated.mjs +16 -0
  144. package/dist/office-js/toApiScript.mjs +21 -5
  145. package/dist/opc/Load.d.mts +9 -2
  146. package/dist/opc/Save.d.mts +17 -0
  147. package/dist/opc/Save.mjs +12 -2
  148. package/dist/opc/index.d.mts +3 -3
  149. package/dist/opc/index.mjs +2 -2
  150. package/dist/opc/mce/preprocessor.d.mts +88 -7
  151. package/dist/opc/mce/preprocessor.mjs +144 -37
  152. package/dist/opc/mce/understood.d.mts +34 -0
  153. package/dist/opc/mce/understood.mjs +55 -3
  154. package/dist/packages/OpcPackage.d.mts +20 -0
  155. package/dist/packages/OpcPackage.mjs +16 -3
  156. package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
  157. package/dist/packages/WordprocessingMLPackage.mjs +242 -11
  158. package/dist/parts/DefaultXmlPart.mjs +2 -2
  159. package/dist/parts/Part.d.mts +10 -2
  160. package/dist/parts/XmlPart.d.mts +35 -32
  161. package/dist/parts/XmlPart.mjs +107 -107
  162. package/dist/parts/wml/comments.mjs +30 -0
  163. package/dist/parts/wml/index.mjs +12 -0
  164. package/dist/xml/dom.d.mts +7 -0
  165. package/dist/xml/dom.mjs +18 -0
  166. package/package.json +2 -2
@@ -64,10 +64,27 @@ export declare class Comment {
64
64
  /** Removes the comment and its replies: the markers, the reference runs and every side-part entry. */
65
65
  delete(): Promise<void>;
66
66
  /**
67
- * The commented range: a `Range` per paragraph the comment touches (extension; Office JS
68
- * returns one `Range`). Empty when the comment has no markers in this body.
67
+ * @internal `delete()` once the side parts it touches are loaded: public so that a resolution's
68
+ * removals (`flushPendingRemovals`), which load only the parts the document has, take a comment away
69
+ * without creating any (CR-002 section 54).
69
70
  */
70
- getRange(): Range[];
71
+ removeThread(): void;
72
+ /**
73
+ * The commented range: one `Range` from the comment's start to its end, across paragraphs where the comment
74
+ * spans them, as Office JS's `Comment.getRange()` (CR-014 phase 1, D2; check 52: a comment from one paragraph
75
+ * into the next reads `BODY1B\rLIST1A`). A start marker between blocks (Word writes one before a control's
76
+ * first paragraph) starts the range at the next paragraph, and an end marker between blocks ends it at the
77
+ * previous one's end. Undefined when the comment has no markers in this body. `getRanges()` gives the range a
78
+ * paragraph at a time, as this method did before.
79
+ */
80
+ getRange(): Range | undefined;
81
+ /** Where a range marker stands: its paragraph and offset, a block-level one's the next or the previous paragraph's edge. */
82
+ private anchor;
83
+ /**
84
+ * The commented range a paragraph at a time: a `Range` per paragraph the comment touches (extension; what
85
+ * `getRange()` returned until CR-014 phase 1). Empty when the comment has no markers in this body.
86
+ */
87
+ getRanges(): Range[];
71
88
  /** This comment and, depth first, its replies. */
72
89
  private thread;
73
90
  private removeSelf;
@@ -108,6 +125,7 @@ export declare function commentsOf(scope: Body | Paragraph | Range): Promise<Com
108
125
  * Comments whose anchored text went with a rejected insertion or an accepted deletion (check 32;
109
126
  * `TrackedChange`'s `removeRevision`) had their markers removed then; their entries go here, with
110
127
  * their replies, when the comments are next read or the package is saved (`WordprocessingMLPackage.saveTo`).
128
+ * With the last comment, the comment parts go too (`flushPendingRemovals`).
111
129
  */
112
130
  export declare function flushPendingCommentRemovals(body: Body): Promise<void>;
113
131
  export type { CommentParts, Author } from './comments.mjs';
@@ -2,13 +2,16 @@ import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
2
2
  import * as wmlFactory from '@docx4j/generated-objects-ts/factory/org_docx4j_wml';
3
3
  import * as w15Factory from '@docx4j/generated-objects-ts/factory/org_docx4j_w15';
4
4
  import * as w16cidFactory from '@docx4j/generated-objects-ts/factory/org_docx4j_w16cid';
5
- import { r as textRun, applyRunOptions, linkParents } from '@docx4j/generated-objects-ts/builders/wml';
5
+ import { applyRunOptions, linkParents } from '@docx4j/generated-objects-ts/builders/wml';
6
+ import { oneParagraphRun } from './officeText.mjs';
6
7
  import { Docx4JException } from '../../opc/exceptions.mjs';
7
8
  import { Body } from './Body.mjs';
8
9
  import { Paragraph } from './Paragraph.mjs';
9
10
  import { Range } from './Range.mjs';
10
- import { runOf, paragraphOf, typeNameOf } from './tree.mjs';
11
+ import { runOf, paragraphOf, typeNameOf, selectedBranchOf, BRANCH_TYPES } from './tree.mjs';
12
+ import { storyLayout } from './story.mjs';
11
13
  import { revisionDateOf, wordCalendarOf, copyRPr } from './tracking.mjs';
14
+ import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
12
15
  import { COMMENT_REFERENCE_STYLE, COMMENT_TEXT_STYLE, commentExOf, commentIdsOf, commentPartsAccess, commentParaId, initialsOf, isDone, markersOf, markersOfParagraph, nextCommentId, paraIdTaken, randomHexId, removeMarkers, setCommentApi, } from './comments.mjs';
13
16
  /**
14
17
  * A subset of Office JS `Word.Comment` over a `w:comment` and the markers that anchor it. A view:
@@ -77,11 +80,13 @@ export class Comment {
77
80
  }
78
81
  /** The comment's text, a paragraph per line; setting it replaces the paragraphs. */
79
82
  get content() {
83
+ // Office JS's comment.content: its body's text, the paragraphs a mark \r apart (check 51)
80
84
  return this.commentBody.text;
81
85
  }
82
86
  set content(text) {
83
87
  const paraId = commentParaId(this.element);
84
- const paragraphs = text.split('\n').map((line, i) => commentParagraph(line, i === 0));
88
+ // a paragraph per line, \r (as the getter gives it) or \n
89
+ const paragraphs = text.split(/\r\n|\r|\n/).map((line, i) => commentParagraph(line, i === 0));
85
90
  if (paraId)
86
91
  paragraphs[0].value.paraId = paraId;
87
92
  this.element.content = paragraphs;
@@ -135,6 +140,14 @@ export class Comment {
135
140
  /** Removes the comment and its replies: the markers, the reference runs and every side-part entry. */
136
141
  async delete() {
137
142
  await this.parts.ensureAll();
143
+ this.removeThread();
144
+ }
145
+ /**
146
+ * @internal `delete()` once the side parts it touches are loaded: public so that a resolution's
147
+ * removals (`flushPendingRemovals`), which load only the parts the document has, take a comment away
148
+ * without creating any (CR-002 section 54).
149
+ */
150
+ removeThread() {
138
151
  for (const comment of this.thread())
139
152
  comment.removeSelf();
140
153
  if (this.parentComment) {
@@ -145,12 +158,57 @@ export class Comment {
145
158
  }
146
159
  }
147
160
  /**
148
- * The commented range: a `Range` per paragraph the comment touches (extension; Office JS
149
- * returns one `Range`). Empty when the comment has no markers in this body.
161
+ * The commented range: one `Range` from the comment's start to its end, across paragraphs where the comment
162
+ * spans them, as Office JS's `Comment.getRange()` (CR-014 phase 1, D2; check 52: a comment from one paragraph
163
+ * into the next reads `BODY1B\rLIST1A`). A start marker between blocks (Word writes one before a control's
164
+ * first paragraph) starts the range at the next paragraph, and an end marker between blocks ends it at the
165
+ * previous one's end. Undefined when the comment has no markers in this body. `getRanges()` gives the range a
166
+ * paragraph at a time, as this method did before.
150
167
  */
151
168
  getRange() {
169
+ const markers = markersOf(this.body.container, (p) => this.body.hiddenRuns(p)).filter((m) => m.id === this.id);
170
+ const start = markers.find((m) => m.kind === 'start');
171
+ const end = [...markers].reverse().find((m) => m.kind === 'end');
172
+ const from = start && this.anchor(start, 'start');
173
+ const to = end && this.anchor(end, 'end');
174
+ if (!from || !to)
175
+ return this.getRanges()[0];
176
+ const paragraphs = this.body.paragraphs;
177
+ const a = paragraphs.findIndex((p) => p.element === from.paragraph.element);
178
+ const b = paragraphs.findIndex((p) => p.element === to.paragraph.element);
179
+ if (a < 0 || b < 0 || b < a || (a === b && to.offset < from.offset))
180
+ return this.getRanges()[0];
181
+ return a === b ? new Range(from.paragraph, from.offset, to.offset)
182
+ : new Range(from.paragraph, from.offset, to.offset, false, false, to.paragraph);
183
+ }
184
+ /** Where a range marker stands: its paragraph and offset, a block-level one's the next or the previous paragraph's edge. */
185
+ anchor(marker, side) {
186
+ var _a;
187
+ if (marker.paragraph && marker.paragraphContainer) {
188
+ const paragraph = new Paragraph(marker.paragraph, marker.paragraphContainer, this.body);
189
+ return { paragraph, offset: (_a = marker.offset) !== null && _a !== void 0 ? _a : (side === 'start' ? 0 : paragraph.text.length) };
190
+ }
191
+ const at = marker.owner.indexOf(marker.item);
192
+ if (at < 0)
193
+ return undefined;
194
+ const blocks = side === 'start' ? marker.owner.slice(at + 1) : marker.owner.slice(0, at).reverse();
195
+ for (const block of blocks) {
196
+ const found = storyLayout([block], () => '').paragraphs;
197
+ const sp = side === 'start' ? found[0] : found[found.length - 1];
198
+ if (!sp)
199
+ continue;
200
+ const paragraph = new Paragraph(sp.element, sp.container, this.body);
201
+ return { paragraph, offset: side === 'start' ? 0 : paragraph.text.length };
202
+ }
203
+ return undefined;
204
+ }
205
+ /**
206
+ * The commented range a paragraph at a time: a `Range` per paragraph the comment touches (extension; what
207
+ * `getRange()` returned until CR-014 phase 1). Empty when the comment has no markers in this body.
208
+ */
209
+ getRanges() {
152
210
  var _a, _b, _c, _d;
153
- const markers = markersOf(this.body.container).filter((m) => m.id === this.id && m.paragraph !== undefined);
211
+ const markers = markersOf(this.body.container, (p) => this.body.hiddenRuns(p)).filter((m) => m.id === this.id && m.paragraph !== undefined);
154
212
  if (markers.length === 0)
155
213
  return [];
156
214
  const paragraphs = this.body.paragraphs;
@@ -191,6 +249,12 @@ export class Comment {
191
249
  if (i >= 0)
192
250
  comments.splice(i, 1);
193
251
  }
252
+ // what the comment's own content related to - a picture's image - goes on save when nothing else
253
+ // uses it, as for a picture deleted outright (CR-002 section 50; docx4j's Removals)
254
+ const released = new Set();
255
+ relationshipIdsIn(this.element.content, released);
256
+ const pkg = this.body.package_;
257
+ queueRelationshipRemovals(pkg === null || pkg === void 0 ? void 0 : pkg.pendingRelationshipRemovals, this.parts.commentsPart, released);
194
258
  const paraId = this.paraId;
195
259
  if (paraId === undefined)
196
260
  return;
@@ -285,11 +349,14 @@ function nestedIndex(owner, index) {
285
349
  }
286
350
  /** A paragraph of a comment: the `CommentText` style, opened by the annotation reference run. */
287
351
  function commentParagraph(text, first) {
352
+ var _a;
288
353
  const runs = [];
289
354
  if (first)
290
355
  runs.push(annotationRefRun());
291
- if (text !== '')
292
- runs.push(textRun(text));
356
+ // its line's characters as they read (CR-002 section 60 D1, E3); a line break is a paragraph of its own (D5)
357
+ const run = oneParagraphRun(text);
358
+ if ((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length)
359
+ runs.push(run);
293
360
  return paragraphOf(runs, { TYPE_NAME: 'org_docx4j_wml.PPr', pStyle: { val: COMMENT_TEXT_STYLE } });
294
361
  }
295
362
  function insertAt(owner, index, items, parent) {
@@ -300,6 +367,18 @@ function insertAt(owner, index, items, parent) {
300
367
  function parentOf(element) {
301
368
  return element.value.PARENT;
302
369
  }
370
+ /** The innermost branch of a kept `mc:AlternateContent` no reader takes that holds a value, or undefined. */
371
+ function unreadBranchOf(value) {
372
+ for (let o = value.PARENT, guard = 0; o && guard < 256; o = o.PARENT, guard++) {
373
+ const tn = o.TYPE_NAME;
374
+ if (tn === undefined || !BRANCH_TYPES.has(tn))
375
+ continue;
376
+ const ac = o.PARENT;
377
+ if (ac && selectedBranchOf(ac) !== o.any)
378
+ return o;
379
+ }
380
+ return undefined;
381
+ }
303
382
  /**
304
383
  * Where a marker for this segment's run goes when it is not put inside the revision the run sits
305
384
  * in (`placeAround`): beside the run, but outside the `w:ins` or `w:del`, so that a comment made
@@ -316,10 +395,17 @@ function markerSite(seg, fallback, after) {
316
395
  function placeAround(range, id) {
317
396
  var _a;
318
397
  var _b;
398
+ if (range.spansParagraphs) {
399
+ placeAcross(range, id);
400
+ return;
401
+ }
319
402
  const paragraph = range.paragraph;
320
- paragraph.splitAt(range.start);
403
+ // a run holding the span holds nothing else, a tab or a break beside it moved out too, as `font` splits (until
404
+ // CR-014 phase 3 only the w:t were split, and a marker went beside the whole run, widening the anchor)
321
405
  if (range.end > range.start)
322
- paragraph.splitAt(range.end);
406
+ paragraph.splitRunsAround(range.start, range.end);
407
+ else
408
+ paragraph.splitAt(range.start);
323
409
  const covered = range.end > range.start ? paragraph.segments().filter((s) => s.end > range.start && s.start < range.end) : [];
324
410
  const start = el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }));
325
411
  const end = el.commentRangeEnd(wmlFactory.createCommentRangeEnd({ id }));
@@ -362,16 +448,90 @@ function placeAround(range, id) {
362
448
  insertAt(before.owner, before.index, [start], before.parent);
363
449
  }
364
450
  }
451
+ /**
452
+ * The markers of a comment over a range across paragraphs, as Word writes them (check 52, cases 79 to 85; CR-014
453
+ * phase 3): the start before the range's first character, in its first paragraph, and the end and the reference
454
+ * run after its last character, in its last paragraph, before a mark the range holds. A table between, or a cell
455
+ * either end is in, stays as it is: the markers stand in the paragraphs. On a block control's content range Word
456
+ * put the start before the control's first paragraph, in `w:sdtContent` (case 82); here it is inside that
457
+ * paragraph, which reads the same (CR-014 section 9.2). An insertion at either end is treated as `placeAround`
458
+ * treats one: the start inside it, before the first commented run; the end after the last, the rest of the
459
+ * insertion split off.
460
+ */
461
+ function placeAcross(range, id) {
462
+ var _a, _b, _c, _d, _e;
463
+ var _f, _g;
464
+ const first = range.paragraph;
465
+ const last = range.endParagraph;
466
+ const start = el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }));
467
+ const end = el.commentRangeEnd(wmlFactory.createCommentRangeEnd({ id }));
468
+ const reference = referenceRun(id);
469
+ // the end first; the two are in different paragraphs, so neither's offsets move with the other's markers. The
470
+ // runs are split as `font` splits them, so a marker never goes beside a run holding text outside the range
471
+ if (range.end > 0)
472
+ last.splitRunsAround(0, range.end);
473
+ const before = last.segments().filter((s) => s.start < range.end);
474
+ const lastSeg = before[before.length - 1];
475
+ if (((_a = lastSeg === null || lastSeg === void 0 ? void 0 : lastSeg.revision) === null || _a === void 0 ? void 0 : _a.kind) === 'ins') {
476
+ last.splitHolderAround(lastSeg.revision, lastSeg.runIndex + 1, [end, reference], () => last.nextAnnotationId());
477
+ }
478
+ else if (lastSeg) {
479
+ const site = markerSite(lastSeg, last.p, true);
480
+ insertAt(site.owner, site.index, [end, reference], site.parent);
481
+ }
482
+ else {
483
+ // nothing of the last paragraph in the range: the end at its start
484
+ const next = last.segments()[0];
485
+ const site = next ? markerSite(next, last.p, false) : { owner: ((_b = (_f = last.p).content) !== null && _b !== void 0 ? _b : (_f.content = [])), index: 0, parent: last.p };
486
+ insertAt(site.owner, site.index, [end, reference], site.parent);
487
+ }
488
+ if (range.start < first.text.length)
489
+ first.splitRunsAround(range.start, first.text.length);
490
+ const segments = first.segments();
491
+ const firstSeg = segments.find((s) => s.end > range.start);
492
+ if (((_c = firstSeg === null || firstSeg === void 0 ? void 0 : firstSeg.revision) === null || _c === void 0 ? void 0 : _c.kind) === 'ins')
493
+ insertAt(firstSeg.runOwner, firstSeg.runIndex, [start], firstSeg.revision.value);
494
+ else if (firstSeg) {
495
+ const site = markerSite(firstSeg, first.p, false);
496
+ insertAt(site.owner, site.index, [start], site.parent);
497
+ }
498
+ else {
499
+ // nothing of the first paragraph in the range: the start at its end
500
+ const previous = segments[segments.length - 1];
501
+ const site = previous ? markerSite(previous, first.p, true) : { owner: ((_d = (_g = first.p).content) !== null && _d !== void 0 ? _d : (_g.content = [])), index: ((_e = first.p.content) !== null && _e !== void 0 ? _e : []).length, parent: first.p };
502
+ insertAt(site.owner, site.index, [start], site.parent);
503
+ }
504
+ }
365
505
  /** Writes the markers of a reply next to its parent's, as Word nests them. */
366
506
  function placeAfter(body, parentId, id) {
367
- const markers = markersOf(body.container).filter((m) => m.id === parentId);
368
- const start = markers.find((m) => m.kind === 'start');
369
- const end = [...markers].reverse().find((m) => m.kind === 'end');
370
- const reference = [...markers].reverse().find((m) => m.kind === 'reference');
371
- if (!((reference === null || reference === void 0 ? void 0 : reference.run) && reference.runOwner) && !end) {
507
+ var _a;
508
+ // the parent's markers in every branch of a kept mc:AlternateContent, each branch no reader takes
509
+ // apart, so that the reply is anchored in each as its parent is (CR-013 phase 3)
510
+ const groups = new Map();
511
+ for (const marker of markersOf(body.container, undefined, 'every')) {
512
+ if (marker.id !== parentId)
513
+ continue;
514
+ const key = unreadBranchOf(marker.item.value);
515
+ let group = groups.get(key);
516
+ if (!group)
517
+ groups.set(key, group = []);
518
+ group.push(marker);
519
+ }
520
+ const read = (_a = groups.get(undefined)) !== null && _a !== void 0 ? _a : [];
521
+ if (!read.some((m) => (m.kind === 'reference' && m.run && m.runOwner) || m.kind === 'end')) {
372
522
  // refused before anything is written, so that no marker is left naming a comment never made
373
523
  throw new Docx4JException(`Comment ${parentId} has no markers in this body; cannot anchor a reply`);
374
524
  }
525
+ for (const markers of groups.values())
526
+ placeAfterIn(markers, id);
527
+ }
528
+ /** A reply's markers after its parent's, in one branch's markers (or those outside any no reader takes). */
529
+ function placeAfterIn(markers, id) {
530
+ const start = markers.find((m) => m.kind === 'start');
531
+ const end = [...markers].reverse().find((m) => m.kind === 'end');
532
+ const reference = [...markers].reverse().find((m) => m.kind === 'reference');
533
+ if (!((reference === null || reference === void 0 ? void 0 : reference.run) && reference.runOwner) && !end)
534
+ return;
375
535
  if (start)
376
536
  insertAt(start.owner, start.owner.indexOf(start.item) + 1, [el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }))], parentOf(start.item));
377
537
  if (end)
@@ -506,8 +666,9 @@ function commentId(parts, body, options) {
506
666
  }
507
667
  /** The paragraphs of a comment's content: a string splits on newlines, elements are taken as they are. */
508
668
  function paragraphsOfContent(content) {
669
+ // a paragraph per line, at \r, \n or \r\n, as Word's comments take them (CR-002 section 60 D5, check 55)
509
670
  if (typeof content === 'string')
510
- return content.split('\n').map((line, i) => commentParagraph(line, i === 0));
671
+ return content.split(/\r\n|\r|\n/).map((line, i) => commentParagraph(line, i === 0));
511
672
  const paragraphs = [...content];
512
673
  if (paragraphs.length === 0)
513
674
  throw new Docx4JException('A comment needs at least one paragraph');
@@ -542,6 +703,7 @@ export async function commentsOf(scope) {
542
703
  * Comments whose anchored text went with a rejected insertion or an accepted deletion (check 32;
543
704
  * `TrackedChange`'s `removeRevision`) had their markers removed then; their entries go here, with
544
705
  * their replies, when the comments are next read or the package is saved (`WordprocessingMLPackage.saveTo`).
706
+ * With the last comment, the comment parts go too (`flushPendingRemovals`).
545
707
  */
546
708
  export async function flushPendingCommentRemovals(body) {
547
709
  var _a;
@@ -555,18 +717,39 @@ export async function flushPendingCommentRemovals(body) {
555
717
  }
556
718
  await flushPendingRemovals(body, parts);
557
719
  }
720
+ /**
721
+ * The entries of the comments a resolution took away, replies included. The side parts the document
722
+ * has are loaded for it and none is created: before section 54 a removal created the commentsExtended,
723
+ * commentsIds and people parts a document lacked, as `delete()` does.
724
+ *
725
+ * With the last comment a resolution takes away, the comment parts go, as Word 365 removed them
726
+ * (docx4j CR-043 section 15; docx4j-python's artefact 8 rejected, docx4j's U22, core-ts check 32): the
727
+ * comments part and its commentsExtended, commentsIds and commentsExtensible parts, with their
728
+ * relationships; and the people part where no tracked change is left to name an author (U22: Word kept
729
+ * it while a deletion was still tracked). docx4j decides when its resolution ends; this, when the
730
+ * entries go, so tracked changes made or resolved in between count.
731
+ */
558
732
  async function flushPendingRemovals(body, parts) {
733
+ var _a;
559
734
  const pkg = body.package_;
560
735
  const pending = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals;
561
736
  if (!(pending === null || pending === void 0 ? void 0 : pending.size))
562
737
  return;
738
+ await parts.loadExisting();
563
739
  const views = viewsOf(parts, body);
740
+ let removed = false;
564
741
  for (const id of [...pending]) {
565
742
  const view = views.get(id);
566
- if (view)
567
- await view.delete();
743
+ if (view) {
744
+ view.removeThread();
745
+ removed = true;
746
+ }
568
747
  pending.delete(id);
569
748
  }
749
+ if (!removed || ((_a = parts.comments.comment) !== null && _a !== void 0 ? _a : []).length > 0)
750
+ return;
751
+ const people = parts.people !== undefined && (pkg === null || pkg === void 0 ? void 0 : pkg.getAllTrackedChanges) !== undefined && (await pkg.getAllTrackedChanges()).length === 0;
752
+ parts.drop(people);
570
753
  }
571
754
  /** A `Comment` view per entry in the parts, replies linked to their parents. */
572
755
  function viewsOf(parts, body) {
@@ -595,7 +778,7 @@ function idsInRange(range) {
595
778
  var _a;
596
779
  const spans = new Map();
597
780
  const order = [];
598
- for (const marker of markersOfParagraph(range.paragraph.element, range.paragraph.container)) {
781
+ for (const marker of markersOfParagraph(range.paragraph.element, range.paragraph.container, range.paragraph.hiddenRuns())) {
599
782
  const at = (_a = marker.offset) !== null && _a !== void 0 ? _a : 0;
600
783
  const span = spans.get(marker.id);
601
784
  if (span) {