@docx4j/core-ts 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +92 -4
  3. package/dist/html/convert.mjs +2 -1
  4. package/dist/html/elements.mjs +2 -1
  5. package/dist/model/color/ColorMath.d.mts +29 -0
  6. package/dist/model/color/ColorMath.mjs +146 -0
  7. package/dist/model/color/ColorTables.d.mts +6 -0
  8. package/dist/model/color/ColorTables.mjs +83 -0
  9. package/dist/model/color/DrawingMLColors.d.mts +52 -0
  10. package/dist/model/color/DrawingMLColors.mjs +374 -0
  11. package/dist/model/color/ResolvedColor.d.mts +36 -0
  12. package/dist/model/color/ResolvedColor.mjs +65 -0
  13. package/dist/model/color/SpreadsheetColors.d.mts +36 -0
  14. package/dist/model/color/SpreadsheetColors.mjs +84 -0
  15. package/dist/model/color/StyleFill.d.mts +28 -0
  16. package/dist/model/color/StyleFill.mjs +21 -0
  17. package/dist/model/color/ThemeColors.d.mts +86 -0
  18. package/dist/model/color/ThemeColors.mjs +304 -0
  19. package/dist/model/color/WordColors.d.mts +71 -0
  20. package/dist/model/color/WordColors.mjs +121 -0
  21. package/dist/model/color/index.d.mts +6 -0
  22. package/dist/model/color/index.mjs +30 -0
  23. package/dist/model/content/Body.d.mts +145 -23
  24. package/dist/model/content/Body.mjs +507 -150
  25. package/dist/model/content/Comment.d.mts +23 -5
  26. package/dist/model/content/Comment.mjs +207 -26
  27. package/dist/model/content/ContentControl.d.mts +198 -19
  28. package/dist/model/content/ContentControl.mjs +672 -99
  29. package/dist/model/content/Font.d.mts +70 -17
  30. package/dist/model/content/Font.mjs +119 -36
  31. package/dist/model/content/InlinePicture.d.mts +11 -0
  32. package/dist/model/content/InlinePicture.mjs +21 -1
  33. package/dist/model/content/List.d.mts +6 -3
  34. package/dist/model/content/List.mjs +18 -8
  35. package/dist/model/content/Paragraph.d.mts +200 -14
  36. package/dist/model/content/Paragraph.mjs +898 -82
  37. package/dist/model/content/Range.d.mts +227 -12
  38. package/dist/model/content/Range.mjs +785 -59
  39. package/dist/model/content/ResolvedView.d.mts +104 -0
  40. package/dist/model/content/ResolvedView.mjs +596 -0
  41. package/dist/model/content/Table.d.mts +29 -3
  42. package/dist/model/content/Table.mjs +135 -49
  43. package/dist/model/content/TrackedChange.d.mts +174 -15
  44. package/dist/model/content/TrackedChange.mjs +734 -170
  45. package/dist/model/content/comments.d.mts +20 -6
  46. package/dist/model/content/comments.mjs +67 -57
  47. package/dist/model/content/generalException.d.mts +18 -0
  48. package/dist/model/content/generalException.mjs +24 -0
  49. package/dist/model/content/index.d.mts +10 -3
  50. package/dist/model/content/index.mjs +10 -2
  51. package/dist/model/content/mathText.d.mts +21 -0
  52. package/dist/model/content/mathText.mjs +267 -0
  53. package/dist/model/content/officeText.d.mts +27 -0
  54. package/dist/model/content/officeText.mjs +74 -0
  55. package/dist/model/content/ooxml.mjs +12 -1
  56. package/dist/model/content/outline.d.mts +5 -0
  57. package/dist/model/content/outline.mjs +13 -0
  58. package/dist/model/content/paraIds.d.mts +5 -0
  59. package/dist/model/content/paraIds.mjs +54 -0
  60. package/dist/model/content/placeholders.d.mts +110 -0
  61. package/dist/model/content/placeholders.mjs +253 -0
  62. package/dist/model/content/search.d.mts +74 -0
  63. package/dist/model/content/search.mjs +255 -9
  64. package/dist/model/content/spanEdits.d.mts +159 -0
  65. package/dist/model/content/spanEdits.mjs +728 -0
  66. package/dist/model/content/story.d.mts +47 -0
  67. package/dist/model/content/story.mjs +95 -0
  68. package/dist/model/content/tableColumns.d.mts +1 -1
  69. package/dist/model/content/tableColumns.mjs +108 -80
  70. package/dist/model/content/tableRevisions.d.mts +11 -3
  71. package/dist/model/content/tableRevisions.mjs +72 -42
  72. package/dist/model/content/tableRules.d.mts +30 -0
  73. package/dist/model/content/tableRules.mjs +102 -0
  74. package/dist/model/content/textBoxes.d.mts +18 -0
  75. package/dist/model/content/textBoxes.mjs +105 -0
  76. package/dist/model/content/trackedChangeFilter.d.mts +39 -0
  77. package/dist/model/content/trackedChangeFilter.mjs +81 -0
  78. package/dist/model/content/tracking.d.mts +63 -6
  79. package/dist/model/content/tracking.mjs +242 -22
  80. package/dist/model/content/tree.d.mts +172 -24
  81. package/dist/model/content/tree.mjs +328 -54
  82. package/dist/model/content/wordRange.d.mts +48 -0
  83. package/dist/model/content/wordRange.mjs +97 -0
  84. package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
  85. package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
  86. package/dist/model/customxml/XmlMapping.d.mts +32 -8
  87. package/dist/model/customxml/XmlMapping.mjs +64 -10
  88. package/dist/model/customxml/bindings.d.mts +33 -4
  89. package/dist/model/customxml/bindings.mjs +171 -45
  90. package/dist/model/customxml/index.d.mts +1 -0
  91. package/dist/model/customxml/index.mjs +1 -0
  92. package/dist/model/customxml/kinds.d.mts +8 -3
  93. package/dist/model/customxml/kinds.mjs +26 -5
  94. package/dist/model/customxml/opendope.d.mts +4 -1
  95. package/dist/model/customxml/opendope.mjs +9 -6
  96. package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
  97. package/dist/model/customxml/picturePlaceholder.mjs +32 -0
  98. package/dist/model/customxml/pictures.d.mts +4 -3
  99. package/dist/model/customxml/pictures.mjs +22 -8
  100. package/dist/model/customxml/storeItems.d.mts +57 -0
  101. package/dist/model/customxml/storeItems.mjs +122 -0
  102. package/dist/model/customxml/xhtml.mjs +23 -22
  103. package/dist/model/dateZone.d.mts +25 -0
  104. package/dist/model/dateZone.mjs +57 -0
  105. package/dist/model/index.d.mts +3 -0
  106. package/dist/model/index.mjs +5 -0
  107. package/dist/model/listnumbering/Emulator.d.mts +37 -5
  108. package/dist/model/listnumbering/Emulator.mjs +63 -9
  109. package/dist/model/properties/PropertyResolver.d.mts +74 -2
  110. package/dist/model/properties/PropertyResolver.mjs +133 -5
  111. package/dist/model/properties/catalogue.d.mts +19 -5
  112. package/dist/model/properties/catalogue.mjs +41 -15
  113. package/dist/model/properties/index.d.mts +1 -1
  114. package/dist/model/properties/log.d.mts +1 -1
  115. package/dist/model/properties/log.mjs +5 -5
  116. package/dist/model/properties/styleUtil.d.mts +3 -1
  117. package/dist/model/properties/styleUtil.mjs +20 -24
  118. package/dist/model/properties/table.mjs +4 -2
  119. package/dist/model/text/CaseMapping.d.mts +38 -0
  120. package/dist/model/text/CaseMapping.mjs +86 -0
  121. package/dist/model/text/Extractor.d.mts +349 -0
  122. package/dist/model/text/Extractor.mjs +2406 -0
  123. package/dist/model/text/LinkTargets.d.mts +45 -0
  124. package/dist/model/text/LinkTargets.mjs +170 -0
  125. package/dist/model/text/MarkdownExporter.d.mts +140 -0
  126. package/dist/model/text/MarkdownExporter.mjs +255 -0
  127. package/dist/model/text/PlainText.d.mts +29 -0
  128. package/dist/model/text/PlainText.mjs +542 -0
  129. package/dist/model/text/TextContext.d.mts +256 -0
  130. package/dist/model/text/TextContext.mjs +600 -0
  131. package/dist/model/text/TextOptions.d.mts +233 -0
  132. package/dist/model/text/TextOptions.mjs +419 -0
  133. package/dist/model/text/TextResult.d.mts +621 -0
  134. package/dist/model/text/TextResult.mjs +629 -0
  135. package/dist/model/text/Texts.d.mts +99 -0
  136. package/dist/model/text/Texts.mjs +187 -0
  137. package/dist/model/text/WmlToMarkdown.d.mts +120 -0
  138. package/dist/model/text/WmlToMarkdown.mjs +936 -0
  139. package/dist/model/text/caseMapping.generated.d.mts +7 -0
  140. package/dist/model/text/caseMapping.generated.mjs +214 -0
  141. package/dist/model/text/commonmark.d.mts +121 -0
  142. package/dist/model/text/commonmark.mjs +655 -0
  143. package/dist/model/text/index.d.mts +8 -0
  144. package/dist/model/text/index.mjs +9 -0
  145. package/dist/model/text/normalize.d.mts +8 -0
  146. package/dist/model/text/normalize.mjs +13 -0
  147. package/dist/model/text/outline.d.mts +4 -0
  148. package/dist/model/text/outline.mjs +71 -0
  149. package/dist/model/wordIntegers.d.mts +29 -0
  150. package/dist/model/wordIntegers.mjs +78 -0
  151. package/dist/office-js/document.mjs +15 -1
  152. package/dist/office-js/fragment.d.mts +7 -5
  153. package/dist/office-js/fragment.mjs +63 -14
  154. package/dist/office-js/proxy.d.mts +8 -0
  155. package/dist/office-js/proxy.mjs +29 -0
  156. package/dist/office-js/run.mjs +2 -0
  157. package/dist/office-js/supported.generated.mjs +16 -0
  158. package/dist/office-js/toApiScript.mjs +29 -12
  159. package/dist/opc/DirectoryPartStore.d.mts +6 -2
  160. package/dist/opc/DirectoryPartStore.mjs +11 -4
  161. package/dist/opc/FlatOpcPartStore.d.mts +4 -2
  162. package/dist/opc/FlatOpcPartStore.mjs +11 -8
  163. package/dist/opc/Load.d.mts +32 -0
  164. package/dist/opc/Load.mjs +115 -5
  165. package/dist/opc/PartStore.d.mts +34 -1
  166. package/dist/opc/PartStore.mjs +71 -8
  167. package/dist/opc/Save.d.mts +32 -0
  168. package/dist/opc/Save.mjs +110 -4
  169. package/dist/opc/ZipPartStore.d.mts +4 -2
  170. package/dist/opc/ZipPartStore.mjs +12 -7
  171. package/dist/opc/exceptions.d.mts +140 -0
  172. package/dist/opc/exceptions.mjs +108 -0
  173. package/dist/opc/index.d.mts +8 -5
  174. package/dist/opc/index.mjs +5 -3
  175. package/dist/opc/mce/preprocessor.d.mts +141 -5
  176. package/dist/opc/mce/preprocessor.mjs +119 -18
  177. package/dist/opc/mce/understood.d.mts +3 -11
  178. package/dist/opc/mce/understood.mjs +27 -23
  179. package/dist/opc/repair/breaks.d.mts +23 -0
  180. package/dist/opc/repair/breaks.mjs +288 -0
  181. package/dist/opc/repair/hosts.d.mts +12 -0
  182. package/dist/opc/repair/hosts.mjs +33 -0
  183. package/dist/opc/repair/mainRoot.d.mts +19 -0
  184. package/dist/opc/repair/mainRoot.mjs +168 -0
  185. package/dist/opc/repair/unknownElements.d.mts +8 -0
  186. package/dist/opc/repair/unknownElements.mjs +180 -0
  187. package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
  188. package/dist/opc/repair/wmlNames.generated.mjs +80 -0
  189. package/dist/opc/skipped.d.mts +50 -0
  190. package/dist/opc/skipped.mjs +140 -0
  191. package/dist/packages/OpcPackage.d.mts +110 -4
  192. package/dist/packages/OpcPackage.mjs +123 -7
  193. package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
  194. package/dist/packages/WordprocessingMLPackage.mjs +158 -21
  195. package/dist/packages/index.d.mts +1 -1
  196. package/dist/parts/DefaultXmlPart.mjs +2 -2
  197. package/dist/parts/Part.d.mts +14 -2
  198. package/dist/parts/RelationshipsPart.d.mts +15 -0
  199. package/dist/parts/RelationshipsPart.mjs +66 -1
  200. package/dist/parts/XmlPart.d.mts +63 -32
  201. package/dist/parts/XmlPart.mjs +240 -96
  202. package/dist/parts/wml/comments.mjs +30 -0
  203. package/dist/parts/wml/index.d.mts +12 -1
  204. package/dist/parts/wml/index.mjs +37 -1
  205. package/dist/xml/dom.d.mts +7 -0
  206. package/dist/xml/dom.mjs +18 -0
  207. package/package.json +2 -2
@@ -36,8 +36,8 @@ export declare class Comment {
36
36
  /**
37
37
  * When the comment was made, as Word means it (CR-002 section 29, the revision dates' rule): its
38
38
  * `w16cex:dateUtc` when the document has one for it, which is UTC; else `w:date`, which Word writes
39
- * on the author's local wall clock whatever its `Z` says, read as local time. Undefined when the
40
- * comment carries neither.
39
+ * on the author's local wall clock whatever its `Z` says, read as local time in the package's
40
+ * `dateTimeZone` (CR-018), else the runtime's. Undefined when the comment carries neither.
41
41
  */
42
42
  get creationDate(): Date | undefined;
43
43
  /** The comment's paragraphs as a `Body` (extension: the same content API as the document's). */
@@ -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,15 @@ 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 { revisionDateOf, wordCalendarOf, copyRPr } from './tracking.mjs';
11
+ import { runOf, paragraphOf, typeNameOf, selectedBranchOf, BRANCH_TYPES } from './tree.mjs';
12
+ import { storyLayout } from './story.mjs';
13
+ import { revisionDateOf, wordCalendarOf, nowToTheMinute, copyRPr } from './tracking.mjs';
12
14
  import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
13
15
  import { COMMENT_REFERENCE_STYLE, COMMENT_TEXT_STYLE, commentExOf, commentIdsOf, commentPartsAccess, commentParaId, initialsOf, isDone, markersOf, markersOfParagraph, nextCommentId, paraIdTaken, randomHexId, removeMarkers, setCommentApi, } from './comments.mjs';
14
16
  /**
@@ -54,8 +56,8 @@ export class Comment {
54
56
  /**
55
57
  * When the comment was made, as Word means it (CR-002 section 29, the revision dates' rule): its
56
58
  * `w16cex:dateUtc` when the document has one for it, which is UTC; else `w:date`, which Word writes
57
- * on the author's local wall clock whatever its `Z` says, read as local time. Undefined when the
58
- * comment carries neither.
59
+ * on the author's local wall clock whatever its `Z` says, read as local time in the package's
60
+ * `dateTimeZone` (CR-018), else the runtime's. Undefined when the comment carries neither.
59
61
  */
60
62
  get creationDate() {
61
63
  var _a;
@@ -65,7 +67,7 @@ export class Comment {
65
67
  if (utc)
66
68
  return utc;
67
69
  }
68
- return revisionDateOf({ date: this.element.date });
70
+ return revisionDateOf({ date: this.element.date }, zoneOf(this.body.package_));
69
71
  }
70
72
  /** The comment's paragraphs as a `Body` (extension: the same content API as the document's). */
71
73
  get commentBody() {
@@ -78,11 +80,13 @@ export class Comment {
78
80
  }
79
81
  /** The comment's text, a paragraph per line; setting it replaces the paragraphs. */
80
82
  get content() {
83
+ // Office JS's comment.content: its body's text, the paragraphs a mark \r apart (check 51)
81
84
  return this.commentBody.text;
82
85
  }
83
86
  set content(text) {
84
87
  const paraId = commentParaId(this.element);
85
- 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));
86
90
  if (paraId)
87
91
  paragraphs[0].value.paraId = paraId;
88
92
  this.element.content = paragraphs;
@@ -136,6 +140,14 @@ export class Comment {
136
140
  /** Removes the comment and its replies: the markers, the reference runs and every side-part entry. */
137
141
  async delete() {
138
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() {
139
151
  for (const comment of this.thread())
140
152
  comment.removeSelf();
141
153
  if (this.parentComment) {
@@ -146,12 +158,57 @@ export class Comment {
146
158
  }
147
159
  }
148
160
  /**
149
- * The commented range: a `Range` per paragraph the comment touches (extension; Office JS
150
- * 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.
151
167
  */
152
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() {
153
210
  var _a, _b, _c, _d;
154
- 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);
155
212
  if (markers.length === 0)
156
213
  return [];
157
214
  const paragraphs = this.body.paragraphs;
@@ -292,11 +349,14 @@ function nestedIndex(owner, index) {
292
349
  }
293
350
  /** A paragraph of a comment: the `CommentText` style, opened by the annotation reference run. */
294
351
  function commentParagraph(text, first) {
352
+ var _a;
295
353
  const runs = [];
296
354
  if (first)
297
355
  runs.push(annotationRefRun());
298
- if (text !== '')
299
- 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);
300
360
  return paragraphOf(runs, { TYPE_NAME: 'org_docx4j_wml.PPr', pStyle: { val: COMMENT_TEXT_STYLE } });
301
361
  }
302
362
  function insertAt(owner, index, items, parent) {
@@ -307,6 +367,18 @@ function insertAt(owner, index, items, parent) {
307
367
  function parentOf(element) {
308
368
  return element.value.PARENT;
309
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
+ }
310
382
  /**
311
383
  * Where a marker for this segment's run goes when it is not put inside the revision the run sits
312
384
  * in (`placeAround`): beside the run, but outside the `w:ins` or `w:del`, so that a comment made
@@ -323,10 +395,17 @@ function markerSite(seg, fallback, after) {
323
395
  function placeAround(range, id) {
324
396
  var _a;
325
397
  var _b;
398
+ if (range.spansParagraphs) {
399
+ placeAcross(range, id);
400
+ return;
401
+ }
326
402
  const paragraph = range.paragraph;
327
- 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)
328
405
  if (range.end > range.start)
329
- paragraph.splitAt(range.end);
406
+ paragraph.splitRunsAround(range.start, range.end);
407
+ else
408
+ paragraph.splitAt(range.start);
330
409
  const covered = range.end > range.start ? paragraph.segments().filter((s) => s.end > range.start && s.start < range.end) : [];
331
410
  const start = el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }));
332
411
  const end = el.commentRangeEnd(wmlFactory.createCommentRangeEnd({ id }));
@@ -369,16 +448,90 @@ function placeAround(range, id) {
369
448
  insertAt(before.owner, before.index, [start], before.parent);
370
449
  }
371
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
+ }
372
505
  /** Writes the markers of a reply next to its parent's, as Word nests them. */
373
506
  function placeAfter(body, parentId, id) {
374
- const markers = markersOf(body.container).filter((m) => m.id === parentId);
375
- const start = markers.find((m) => m.kind === 'start');
376
- const end = [...markers].reverse().find((m) => m.kind === 'end');
377
- const reference = [...markers].reverse().find((m) => m.kind === 'reference');
378
- 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')) {
379
522
  // refused before anything is written, so that no marker is left naming a comment never made
380
523
  throw new Docx4JException(`Comment ${parentId} has no markers in this body; cannot anchor a reply`);
381
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;
382
535
  if (start)
383
536
  insertAt(start.owner, start.owner.indexOf(start.item) + 1, [el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }))], parentOf(start.item));
384
537
  if (end)
@@ -398,13 +551,14 @@ function addComment(parts, body, id, paragraphs, parent, options) {
398
551
  var _a, _b, _c, _d, _e, _f;
399
552
  var _g, _h;
400
553
  const author = (_a = options === null || options === void 0 ? void 0 : options.author) !== null && _a !== void 0 ? _a : parts.author;
401
- // one instant for w:date (the local wall clock, as Word writes it) and w16cex:dateUtc (UTC)
402
- const made = (_b = options === null || options === void 0 ? void 0 : options.date) !== null && _b !== void 0 ? _b : new Date();
554
+ // one instant for w:date (the local wall clock in the package's zone, as Word writes it) and w16cex:dateUtc (UTC);
555
+ // a new comment is stamped to the minute, as a revision is and as Word stamps (CR-018, the date rule's point 5)
556
+ const made = (_b = options === null || options === void 0 ? void 0 : options.date) !== null && _b !== void 0 ? _b : nowToTheMinute();
403
557
  const paraId = randomHexId((candidate) => paraIdTaken(parts, body.container, candidate));
404
558
  paragraphs[0].value.paraId = paraId;
405
559
  const comment = wmlFactory.createCommentsComment({
406
560
  id, author: author.name, initials: (_c = options === null || options === void 0 ? void 0 : options.initials) !== null && _c !== void 0 ? _c : initialsOf(author),
407
- date: wordCalendarOf(made),
561
+ date: wordCalendarOf(made, zoneOf(body.package_)),
408
562
  content: paragraphs,
409
563
  });
410
564
  const comments = ((_d = (_g = parts.comments).comment) !== null && _d !== void 0 ? _d : (_g.comment = []));
@@ -513,8 +667,9 @@ function commentId(parts, body, options) {
513
667
  }
514
668
  /** The paragraphs of a comment's content: a string splits on newlines, elements are taken as they are. */
515
669
  function paragraphsOfContent(content) {
670
+ // a paragraph per line, at \r, \n or \r\n, as Word's comments take them (CR-002 section 60 D5, check 55)
516
671
  if (typeof content === 'string')
517
- return content.split('\n').map((line, i) => commentParagraph(line, i === 0));
672
+ return content.split(/\r\n|\r|\n/).map((line, i) => commentParagraph(line, i === 0));
518
673
  const paragraphs = [...content];
519
674
  if (paragraphs.length === 0)
520
675
  throw new Docx4JException('A comment needs at least one paragraph');
@@ -549,6 +704,7 @@ export async function commentsOf(scope) {
549
704
  * Comments whose anchored text went with a rejected insertion or an accepted deletion (check 32;
550
705
  * `TrackedChange`'s `removeRevision`) had their markers removed then; their entries go here, with
551
706
  * their replies, when the comments are next read or the package is saved (`WordprocessingMLPackage.saveTo`).
707
+ * With the last comment, the comment parts go too (`flushPendingRemovals`).
552
708
  */
553
709
  export async function flushPendingCommentRemovals(body) {
554
710
  var _a;
@@ -562,18 +718,39 @@ export async function flushPendingCommentRemovals(body) {
562
718
  }
563
719
  await flushPendingRemovals(body, parts);
564
720
  }
721
+ /**
722
+ * The entries of the comments a resolution took away, replies included. The side parts the document
723
+ * has are loaded for it and none is created: before section 54 a removal created the commentsExtended,
724
+ * commentsIds and people parts a document lacked, as `delete()` does.
725
+ *
726
+ * With the last comment a resolution takes away, the comment parts go, as Word 365 removed them
727
+ * (docx4j CR-043 section 15; docx4j-python's artefact 8 rejected, docx4j's U22, core-ts check 32): the
728
+ * comments part and its commentsExtended, commentsIds and commentsExtensible parts, with their
729
+ * relationships; and the people part where no tracked change is left to name an author (U22: Word kept
730
+ * it while a deletion was still tracked). docx4j decides when its resolution ends; this, when the
731
+ * entries go, so tracked changes made or resolved in between count.
732
+ */
565
733
  async function flushPendingRemovals(body, parts) {
734
+ var _a;
566
735
  const pkg = body.package_;
567
736
  const pending = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals;
568
737
  if (!(pending === null || pending === void 0 ? void 0 : pending.size))
569
738
  return;
739
+ await parts.loadExisting();
570
740
  const views = viewsOf(parts, body);
741
+ let removed = false;
571
742
  for (const id of [...pending]) {
572
743
  const view = views.get(id);
573
- if (view)
574
- await view.delete();
744
+ if (view) {
745
+ view.removeThread();
746
+ removed = true;
747
+ }
575
748
  pending.delete(id);
576
749
  }
750
+ if (!removed || ((_a = parts.comments.comment) !== null && _a !== void 0 ? _a : []).length > 0)
751
+ return;
752
+ const people = parts.people !== undefined && (pkg === null || pkg === void 0 ? void 0 : pkg.getAllTrackedChanges) !== undefined && (await pkg.getAllTrackedChanges()).length === 0;
753
+ parts.drop(people);
577
754
  }
578
755
  /** A `Comment` view per entry in the parts, replies linked to their parents. */
579
756
  function viewsOf(parts, body) {
@@ -602,7 +779,7 @@ function idsInRange(range) {
602
779
  var _a;
603
780
  const spans = new Map();
604
781
  const order = [];
605
- for (const marker of markersOfParagraph(range.paragraph.element, range.paragraph.container)) {
782
+ for (const marker of markersOfParagraph(range.paragraph.element, range.paragraph.container, range.paragraph.hiddenRuns())) {
606
783
  const at = (_a = marker.offset) !== null && _a !== void 0 ? _a : 0;
607
784
  const span = spans.get(marker.id);
608
785
  if (span) {
@@ -621,3 +798,7 @@ function unique(ids) {
621
798
  return ids.filter((id) => (seen.has(id) ? false : (seen.add(id), true)));
622
799
  }
623
800
  setCommentApi({ commentsOf, insertComment });
801
+ /** The package's `dateTimeZone` (CR-018), read duck-typed since the package module cannot be imported here. */
802
+ function zoneOf(pkg) {
803
+ return pkg === null || pkg === void 0 ? void 0 : pkg.dateTimeZone;
804
+ }