@docx4j/core-ts 0.2.2 → 0.3.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.
- package/dist/model/content/Body.d.mts +15 -3
- package/dist/model/content/Body.mjs +62 -20
- package/dist/model/content/Comment.d.mts +21 -2
- package/dist/model/content/Comment.mjs +104 -19
- package/dist/model/content/Font.mjs +2 -0
- package/dist/model/content/List.mjs +9 -6
- package/dist/model/content/Paragraph.d.mts +36 -1
- package/dist/model/content/Paragraph.mjs +172 -34
- package/dist/model/content/Range.d.mts +6 -4
- package/dist/model/content/Range.mjs +68 -10
- package/dist/model/content/TrackedChange.d.mts +94 -3
- package/dist/model/content/TrackedChange.mjs +585 -25
- package/dist/model/content/comments.d.mts +20 -3
- package/dist/model/content/comments.mjs +1 -9
- package/dist/model/content/index.d.mts +3 -3
- package/dist/model/content/index.mjs +2 -2
- package/dist/model/content/tableRevisions.d.mts +30 -0
- package/dist/model/content/tableRevisions.mjs +207 -0
- package/dist/model/content/tracking.d.mts +58 -8
- package/dist/model/content/tracking.mjs +116 -10
- package/dist/parts/wml/comments.mjs +47 -0
- package/package.json +1 -1
|
@@ -195,15 +195,27 @@ export declare class Body {
|
|
|
195
195
|
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead.
|
|
196
196
|
*/
|
|
197
197
|
clear(): void;
|
|
198
|
-
/**
|
|
198
|
+
/**
|
|
199
|
+
* Every tracked change in this body, in document order. Touching insertions or deletions by one
|
|
200
|
+
* author - a deleted paragraph's text and mark, a deletion across a mark - are one change, as
|
|
201
|
+
* Office JS lists them (CR-002 section 29, checks 22 and 23).
|
|
202
|
+
*/
|
|
199
203
|
getTrackedChanges(): TrackedChange[];
|
|
200
204
|
/**
|
|
201
205
|
* Accepts every tracked change, last first so that a paragraph join never disturbs one still
|
|
202
|
-
* to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were
|
|
206
|
+
* to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were listed.
|
|
203
207
|
*/
|
|
204
208
|
acceptAll(): number;
|
|
205
|
-
/** Rejects every tracked change, last first. Returns how many were
|
|
209
|
+
/** Rejects every tracked change, last first. Returns how many were listed. */
|
|
206
210
|
rejectAll(): number;
|
|
211
|
+
/**
|
|
212
|
+
* Resolving one change can leave another to be listed afresh: accepting a move carries a deletion
|
|
213
|
+
* nested in its moved text out of it, and resolving a broken move dissolves the rest of it into a
|
|
214
|
+
* new `w:ins` or `w:del` (CR-002 section 29), so the list read first goes stale. The list is read
|
|
215
|
+
* again after each pass until nothing is left, as "all" promises (found by the editor's C5); a
|
|
216
|
+
* pass that resolves nothing ends it.
|
|
217
|
+
*/
|
|
218
|
+
private resolveAll;
|
|
207
219
|
/** Every `w:tr` in this body's tables, in document order (rows of nested tables included). */
|
|
208
220
|
private rowElements;
|
|
209
221
|
/** The part's XML (extension; Office JS getOoxml wraps it in a package). */
|
|
@@ -12,7 +12,7 @@ import { contentOf } from './ooxml.mjs';
|
|
|
12
12
|
import { expandReplacement } from './search.mjs';
|
|
13
13
|
import { commentApi } from './comments.mjs';
|
|
14
14
|
import { trackerOf, trackInsertedParagraph, trackInsertedTable } from './tracking.mjs';
|
|
15
|
-
import { trackedChangesOfRow } from './TrackedChange.mjs';
|
|
15
|
+
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted } from './TrackedChange.mjs';
|
|
16
16
|
import { listsOf, listLabelsOf } from './List.mjs';
|
|
17
17
|
/**
|
|
18
18
|
* A subset of Office JS `Word.Body` over any container with block-level content: the main
|
|
@@ -215,7 +215,8 @@ export class Body {
|
|
|
215
215
|
}
|
|
216
216
|
/** The package's change tracker while `changeTrackingMode` is on; undefined when it is off (CR-002 phase F). */
|
|
217
217
|
get changeTracker() {
|
|
218
|
-
|
|
218
|
+
var _a;
|
|
219
|
+
return (_a = trackerOf(this.package_)) === null || _a === void 0 ? void 0 : _a.forPart(this.part);
|
|
219
220
|
}
|
|
220
221
|
insertParagraph(text, location) {
|
|
221
222
|
const el = paragraphOf(text === '' ? [] : [textRun(text)]);
|
|
@@ -378,7 +379,11 @@ export class Body {
|
|
|
378
379
|
p.delete();
|
|
379
380
|
}
|
|
380
381
|
// --- change tracking (CR-002 phase F) ---------------------------------------------------
|
|
381
|
-
/**
|
|
382
|
+
/**
|
|
383
|
+
* Every tracked change in this body, in document order. Touching insertions or deletions by one
|
|
384
|
+
* author - a deleted paragraph's text and mark, a deletion across a mark - are one change, as
|
|
385
|
+
* Office JS lists them (CR-002 section 29, checks 22 and 23).
|
|
386
|
+
*/
|
|
382
387
|
getTrackedChanges() {
|
|
383
388
|
const out = [];
|
|
384
389
|
const visit = (items, into) => {
|
|
@@ -386,49 +391,86 @@ export class Body {
|
|
|
386
391
|
for (const el of items) {
|
|
387
392
|
const tn = typeNameOf(el);
|
|
388
393
|
if (tn === 'org_docx4j_wml.P') {
|
|
389
|
-
into.push(...new Paragraph(el, items, this)
|
|
394
|
+
into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this)));
|
|
390
395
|
continue;
|
|
391
396
|
}
|
|
392
397
|
const v = el.value;
|
|
393
398
|
if (typeof v !== 'object' || v === null)
|
|
394
399
|
continue;
|
|
395
400
|
if (tn === 'org_docx4j_wml.Tbl') {
|
|
401
|
+
into.push(BREAK);
|
|
402
|
+
// the table's property records, one change where the table stands (CR-002 section 35)
|
|
403
|
+
const tableChange = tablePropertiesChangeOf(el);
|
|
404
|
+
if (tableChange)
|
|
405
|
+
into.push(tableChange, BREAK);
|
|
396
406
|
for (const row of rowsOf(v)) {
|
|
397
407
|
// A row that is itself a revision reports as one change, as Office JS does: the
|
|
398
408
|
// markup its cells carry (Word marks every run and mark of a deleted or inserted row)
|
|
399
409
|
// belongs to that change, which accepts or rejects it along with the row.
|
|
400
|
-
const
|
|
401
|
-
for (const cell of cellsOf(row.element.value))
|
|
402
|
-
|
|
410
|
+
const tokens = [];
|
|
411
|
+
for (const cell of cellsOf(row.element.value)) {
|
|
412
|
+
const cellChange = cellChangeOf(cell.element, cell.container, row.element.value);
|
|
413
|
+
if (cellChange)
|
|
414
|
+
tokens.push(cellChange, BREAK);
|
|
415
|
+
visit((_a = childrenOf(cell.element.value)) !== null && _a !== void 0 ? _a : [], tokens);
|
|
416
|
+
tokens.push(BREAK);
|
|
417
|
+
}
|
|
418
|
+
const inner = groupTouching(tokens);
|
|
403
419
|
const rowChanges = trackedChangesOfRow(row.element, row.container, inner);
|
|
404
|
-
|
|
420
|
+
for (const change of rowChanges.length > 0 ? rowChanges : inner)
|
|
421
|
+
into.push(change, BREAK);
|
|
405
422
|
}
|
|
406
423
|
continue;
|
|
407
424
|
}
|
|
425
|
+
// a block-level content control or custom XML element keeps what is inside it apart; a
|
|
426
|
+
// marker between paragraphs (a bookmark or range end) keeps nothing apart
|
|
408
427
|
const children = childrenOf(v);
|
|
409
|
-
if (children)
|
|
428
|
+
if (children) {
|
|
429
|
+
into.push(BREAK);
|
|
410
430
|
visit(children, into);
|
|
431
|
+
into.push(BREAK);
|
|
432
|
+
}
|
|
411
433
|
}
|
|
412
434
|
};
|
|
413
435
|
visit(this.content, out);
|
|
414
|
-
|
|
436
|
+
// the body's last section, after everything in it
|
|
437
|
+
const section = bodySectionChangeOf(this.container);
|
|
438
|
+
if (section)
|
|
439
|
+
out.push(BREAK, section);
|
|
440
|
+
return groupTouching(out);
|
|
415
441
|
}
|
|
416
442
|
/**
|
|
417
443
|
* Accepts every tracked change, last first so that a paragraph join never disturbs one still
|
|
418
|
-
* to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were
|
|
444
|
+
* to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were listed.
|
|
419
445
|
*/
|
|
420
446
|
acceptAll() {
|
|
421
|
-
|
|
422
|
-
for (let i = changes.length - 1; i >= 0; i--)
|
|
423
|
-
changes[i].accept();
|
|
424
|
-
return changes.length;
|
|
447
|
+
return this.resolveAll('accept');
|
|
425
448
|
}
|
|
426
|
-
/** Rejects every tracked change, last first. Returns how many were
|
|
449
|
+
/** Rejects every tracked change, last first. Returns how many were listed. */
|
|
427
450
|
rejectAll() {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
451
|
+
return this.resolveAll('reject');
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Resolving one change can leave another to be listed afresh: accepting a move carries a deletion
|
|
455
|
+
* nested in its moved text out of it, and resolving a broken move dissolves the rest of it into a
|
|
456
|
+
* new `w:ins` or `w:del` (CR-002 section 29), so the list read first goes stale. The list is read
|
|
457
|
+
* again after each pass until nothing is left, as "all" promises (found by the editor's C5); a
|
|
458
|
+
* pass that resolves nothing ends it.
|
|
459
|
+
*/
|
|
460
|
+
resolveAll(what) {
|
|
461
|
+
let changes = this.getTrackedChanges();
|
|
462
|
+
const count = changes.length;
|
|
463
|
+
while (changes.length > 0) {
|
|
464
|
+
for (let i = changes.length - 1; i >= 0; i--)
|
|
465
|
+
changes[i][what]();
|
|
466
|
+
const left = this.getTrackedChanges();
|
|
467
|
+
if (left.length >= changes.length)
|
|
468
|
+
break;
|
|
469
|
+
changes = left;
|
|
470
|
+
}
|
|
471
|
+
// and what is never listed: a cell merge, a numbering change, records that change nothing
|
|
472
|
+
resolveUnlisted(this.content, what);
|
|
473
|
+
return count;
|
|
432
474
|
}
|
|
433
475
|
/** Every `w:tr` in this body's tables, in document order (rows of nested tables included). */
|
|
434
476
|
rowElements() {
|
|
@@ -2,7 +2,7 @@ import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
|
|
|
2
2
|
import { Body } from './Body.mjs';
|
|
3
3
|
import { Paragraph } from './Paragraph.mjs';
|
|
4
4
|
import { Range } from './Range.mjs';
|
|
5
|
-
import { type CommentContent, type CommentOptions, type CommentParts } from './comments.mjs';
|
|
5
|
+
import { type CommentContent, type CommentOptions, type CommentEntry, type CommentParts } from './comments.mjs';
|
|
6
6
|
/**
|
|
7
7
|
* A subset of Office JS `Word.Comment` over a `w:comment` and the markers that anchor it. A view:
|
|
8
8
|
* the tree is the state, and a view may be created and discarded freely. Replies are `Comment`s
|
|
@@ -33,7 +33,12 @@ export declare class Comment {
|
|
|
33
33
|
get initials(): string;
|
|
34
34
|
/** The author's email from `w:people`, '' when the document records none. */
|
|
35
35
|
get authorEmail(): string;
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* When the comment was made, as Word means it (CR-002 section 29, the revision dates' rule): its
|
|
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.
|
|
41
|
+
*/
|
|
37
42
|
get creationDate(): Date | undefined;
|
|
38
43
|
/** The comment's paragraphs as a `Body` (extension: the same content API as the document's). */
|
|
39
44
|
get commentBody(): Body;
|
|
@@ -80,6 +85,20 @@ export declare class Comment {
|
|
|
80
85
|
* throws.
|
|
81
86
|
*/
|
|
82
87
|
export declare function insertComment(range: Range, content: CommentContent, options?: CommentOptions): Promise<Comment>;
|
|
88
|
+
/**
|
|
89
|
+
* A comment's entry for markers the caller placed (CR-002 section 36, asked by the editor for its
|
|
90
|
+
* E4.e, which places a new comment's markers itself: a selection may span paragraphs, where
|
|
91
|
+
* `Range.insertComment` anchors in one). Everything `insertComment` writes but the markers: the
|
|
92
|
+
* `w:comment` with the given id, author, initials, date and paragraphs (the first with a
|
|
93
|
+
* `w14:paraId`); its `w15:commentEx` (`w15:done="0"`, and `w15:paraIdParent` for a reply); its
|
|
94
|
+
* `w16cid:commentId` where that part exists, and its `w16cex:commentExtensible` where that one does;
|
|
95
|
+
* and the author's `w15:person`. The comment parts are created as `insertComment` creates them, and
|
|
96
|
+
* the date follows section 29's rule (`w:date` on the local wall clock, `w16cex:dateUtc` the date
|
|
97
|
+
* given). The id must be unused in the comments part - markers naming it are expected, being the
|
|
98
|
+
* caller's - and is not checked against them; a comment with no markers is still a comment, which
|
|
99
|
+
* Word shows unanchored.
|
|
100
|
+
*/
|
|
101
|
+
export declare function addCommentEntry(body: Body, entry: CommentEntry): Promise<Comment>;
|
|
83
102
|
/**
|
|
84
103
|
* `getComments()` on a body, a paragraph or a range: the comments anchored there in document
|
|
85
104
|
* order, replies nested under their parent (a reply is in the array only when its parent is not).
|
|
@@ -8,7 +8,8 @@ import { Body } from './Body.mjs';
|
|
|
8
8
|
import { Paragraph } from './Paragraph.mjs';
|
|
9
9
|
import { Range } from './Range.mjs';
|
|
10
10
|
import { runOf, paragraphOf, typeNameOf } from './tree.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import { revisionDateOf, wordCalendarOf, copyRPr } from './tracking.mjs';
|
|
12
|
+
import { COMMENT_REFERENCE_STYLE, COMMENT_TEXT_STYLE, commentExOf, commentIdsOf, commentPartsAccess, commentParaId, initialsOf, isDone, markersOf, markersOfParagraph, nextCommentId, paraIdTaken, randomHexId, removeMarkers, setCommentApi, } from './comments.mjs';
|
|
12
13
|
/**
|
|
13
14
|
* A subset of Office JS `Word.Comment` over a `w:comment` and the markers that anchor it. A view:
|
|
14
15
|
* the tree is the state, and a view may be created and discarded freely. Replies are `Comment`s
|
|
@@ -49,9 +50,21 @@ export class Comment {
|
|
|
49
50
|
const ad = /^S::([^:]+)::/.exec(userId);
|
|
50
51
|
return ad ? ad[1] : userId;
|
|
51
52
|
}
|
|
52
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* When the comment was made, as Word means it (CR-002 section 29, the revision dates' rule): its
|
|
55
|
+
* `w16cex:dateUtc` when the document has one for it, which is UTC; else `w:date`, which Word writes
|
|
56
|
+
* on the author's local wall clock whatever its `Z` says, read as local time. Undefined when the
|
|
57
|
+
* comment carries neither.
|
|
58
|
+
*/
|
|
53
59
|
get creationDate() {
|
|
54
|
-
|
|
60
|
+
var _a;
|
|
61
|
+
for (const el of [...((_a = this.element.content) !== null && _a !== void 0 ? _a : [])].reverse()) {
|
|
62
|
+
const paraId = typeNameOf(el) === 'org_docx4j_wml.P' ? el.value.paraId : undefined;
|
|
63
|
+
const utc = paraId ? this.parts.datesUtc.get(paraId.toUpperCase()) : undefined;
|
|
64
|
+
if (utc)
|
|
65
|
+
return utc;
|
|
66
|
+
}
|
|
67
|
+
return revisionDateOf({ date: this.element.date });
|
|
55
68
|
}
|
|
56
69
|
/** The comment's paragraphs as a `Body` (extension: the same content API as the document's). */
|
|
57
70
|
get commentBody() {
|
|
@@ -195,6 +208,7 @@ export class Comment {
|
|
|
195
208
|
ids.splice(i, 1);
|
|
196
209
|
if (durableId)
|
|
197
210
|
(_c = this.parts.extensible) === null || _c === void 0 ? void 0 : _c.remove(durableId);
|
|
211
|
+
this.parts.datesUtc.delete(paraId.toUpperCase());
|
|
198
212
|
}
|
|
199
213
|
}
|
|
200
214
|
}
|
|
@@ -226,9 +240,48 @@ export class Comment {
|
|
|
226
240
|
function annotationRefRun() {
|
|
227
241
|
return runOf([el.annotationRef(wmlFactory.createRAnnotationRef())], applyRunOptions({}, { style: COMMENT_REFERENCE_STYLE }));
|
|
228
242
|
}
|
|
229
|
-
/**
|
|
230
|
-
|
|
231
|
-
|
|
243
|
+
/**
|
|
244
|
+
* The run that anchors a comment in the body: `w:commentReference` in the `CommentReference` style,
|
|
245
|
+
* over the run properties of the text it stands by when given (Word's reference run for a comment at
|
|
246
|
+
* a caret carried the text's `w:sz`, CR-002 section 36).
|
|
247
|
+
*/
|
|
248
|
+
function referenceRun(id, textRPr) {
|
|
249
|
+
var _a;
|
|
250
|
+
const rPr = applyRunOptions((_a = copyRPr(textRPr)) !== null && _a !== void 0 ? _a : {}, { style: COMMENT_REFERENCE_STYLE });
|
|
251
|
+
return runOf([el.commentReference(wmlFactory.createRCommentReference({ id }))], rPr);
|
|
252
|
+
}
|
|
253
|
+
/** A comment marker that takes no room: a range start or end, or a run holding only a reference. */
|
|
254
|
+
function isCommentMarker(item) {
|
|
255
|
+
var _a, _b;
|
|
256
|
+
if (!item)
|
|
257
|
+
return false;
|
|
258
|
+
const name = (_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart;
|
|
259
|
+
if (name === 'commentRangeStart' || name === 'commentRangeEnd')
|
|
260
|
+
return true;
|
|
261
|
+
if (typeNameOf(item) !== 'org_docx4j_wml.R')
|
|
262
|
+
return false;
|
|
263
|
+
const content = ((_b = item.value.content) !== null && _b !== void 0 ? _b : []);
|
|
264
|
+
return content.length > 0 && content.every((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference'; });
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Where an empty range's markers go when other comments' empty ranges already stand at the caret:
|
|
268
|
+
* inside them, right after their starts, as Word nests a second comment made at the same place
|
|
269
|
+
* (start 0, start 1, end 1, reference 1, end 0, reference 0; CR-002 section 36). A start counts only
|
|
270
|
+
* when its comment's end is at the caret too, so a comment over text that begins there is not entered.
|
|
271
|
+
*/
|
|
272
|
+
function nestedIndex(owner, index) {
|
|
273
|
+
var _a;
|
|
274
|
+
let from = index;
|
|
275
|
+
while (isCommentMarker(owner[from - 1]))
|
|
276
|
+
from--;
|
|
277
|
+
let to = index;
|
|
278
|
+
while (isCommentMarker(owner[to]))
|
|
279
|
+
to++;
|
|
280
|
+
const endsHere = new Set(owner.slice(from, to).filter((e) => { var _a; return ((_a = e.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeEnd'; }).map((e) => String(e.value.id)));
|
|
281
|
+
let at = from;
|
|
282
|
+
while (at < to && ((_a = owner[at].name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentRangeStart' && endsHere.has(String(owner[at].value.id)))
|
|
283
|
+
at++;
|
|
284
|
+
return at > from ? at : index;
|
|
232
285
|
}
|
|
233
286
|
/** A paragraph of a comment: the `CommentText` style, opened by the annotation reference run. */
|
|
234
287
|
function commentParagraph(text, first) {
|
|
@@ -271,19 +324,21 @@ function placeAround(range, id) {
|
|
|
271
324
|
const covered = range.end > range.start ? paragraph.segments().filter((s) => s.end > range.start && s.start < range.end) : [];
|
|
272
325
|
const start = el.commentRangeStart(wmlFactory.createCommentRangeStart({ id }));
|
|
273
326
|
const end = el.commentRangeEnd(wmlFactory.createCommentRangeEnd({ id }));
|
|
274
|
-
const reference = referenceRun(id);
|
|
275
327
|
if (covered.length === 0) {
|
|
276
|
-
//
|
|
328
|
+
// An empty range or an empty paragraph: both range markers with nothing between them, then the
|
|
329
|
+
// reference run in the text's run properties, and a later comment at the same place nested inside
|
|
330
|
+
// the earlier - what Word writes (measured by Jason 2026-09-29, CR-002 section 36).
|
|
277
331
|
const segments = paragraph.segments();
|
|
278
332
|
const after = segments.find((s) => s.start >= range.start);
|
|
279
333
|
const before = [...segments].reverse().find((s) => s.end <= range.start);
|
|
334
|
+
const text = after !== null && after !== void 0 ? after : before;
|
|
280
335
|
const site = after ? markerSite(after, paragraph.p, false) : before ? markerSite(before, paragraph.p, true) : undefined;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
insertAt(((_a = (_b = paragraph.p).content) !== null && _a !== void 0 ? _a : (_b.content = [])), paragraph.p.content.length, [reference], paragraph.p);
|
|
336
|
+
const owner = site ? site.owner : ((_a = (_b = paragraph.p).content) !== null && _a !== void 0 ? _a : (_b.content = []));
|
|
337
|
+
const index = nestedIndex(owner, site ? site.index : owner.length);
|
|
338
|
+
insertAt(owner, index, [start, end, referenceRun(id, text === null || text === void 0 ? void 0 : text.run.rPr)], site ? site.parent : paragraph.p);
|
|
285
339
|
return;
|
|
286
340
|
}
|
|
341
|
+
const reference = referenceRun(id);
|
|
287
342
|
const after = markerSite(covered[covered.length - 1], paragraph.p, true);
|
|
288
343
|
insertAt(after.owner, after.index, [end, reference], after.parent);
|
|
289
344
|
const before = markerSite(covered[0], paragraph.p, false);
|
|
@@ -315,17 +370,19 @@ function placeAfter(body, parentId, id) {
|
|
|
315
370
|
* paragraphs come checked, by `paragraphsOfContent` before the markers were placed.
|
|
316
371
|
*/
|
|
317
372
|
function addComment(parts, body, id, paragraphs, parent, options) {
|
|
318
|
-
var _a, _b, _c, _d, _e, _f
|
|
319
|
-
var
|
|
373
|
+
var _a, _b, _c, _d, _e, _f;
|
|
374
|
+
var _g, _h;
|
|
320
375
|
const author = (_a = options === null || options === void 0 ? void 0 : options.author) !== null && _a !== void 0 ? _a : parts.author;
|
|
376
|
+
// one instant for w:date (the local wall clock, as Word writes it) and w16cex:dateUtc (UTC)
|
|
377
|
+
const made = (_b = options === null || options === void 0 ? void 0 : options.date) !== null && _b !== void 0 ? _b : new Date();
|
|
321
378
|
const paraId = randomHexId((candidate) => paraIdTaken(parts, body.container, candidate));
|
|
322
379
|
paragraphs[0].value.paraId = paraId;
|
|
323
380
|
const comment = wmlFactory.createCommentsComment({
|
|
324
|
-
id, author: author.name, initials: (
|
|
325
|
-
date:
|
|
381
|
+
id, author: author.name, initials: (_c = options === null || options === void 0 ? void 0 : options.initials) !== null && _c !== void 0 ? _c : initialsOf(author),
|
|
382
|
+
date: wordCalendarOf(made),
|
|
326
383
|
content: paragraphs,
|
|
327
384
|
});
|
|
328
|
-
const comments = ((_d = (
|
|
385
|
+
const comments = ((_d = (_g = parts.comments).comment) !== null && _d !== void 0 ? _d : (_g.comment = []));
|
|
329
386
|
comments.push(comment);
|
|
330
387
|
linkParents(comment, parts.comments);
|
|
331
388
|
const extended = parts.ensureCommentsEx();
|
|
@@ -337,9 +394,12 @@ function addComment(parts, body, id, paragraphs, parent, options) {
|
|
|
337
394
|
if (parts.commentsIds) {
|
|
338
395
|
const durableId = randomHexId((candidate) => { var _a; return ((_a = parts.commentsIds.commentId) !== null && _a !== void 0 ? _a : []).some((e) => e.durableId === candidate); });
|
|
339
396
|
const commentId = w16cidFactory.createCTCommentId({ paraId, durableId });
|
|
340
|
-
((_f = (
|
|
397
|
+
((_f = (_h = parts.commentsIds).commentId) !== null && _f !== void 0 ? _f : (_h.commentId = [])).push(commentId);
|
|
341
398
|
linkParents(commentId, parts.commentsIds);
|
|
342
|
-
(
|
|
399
|
+
if (parts.extensible) {
|
|
400
|
+
parts.extensible.set(durableId, made);
|
|
401
|
+
parts.datesUtc.set(paraId.toUpperCase(), made);
|
|
402
|
+
}
|
|
343
403
|
}
|
|
344
404
|
addPerson(parts, author.name, author.email);
|
|
345
405
|
return new Comment(comment, parts, body);
|
|
@@ -381,6 +441,31 @@ export async function insertComment(range, content, options) {
|
|
|
381
441
|
placeAround(range, id);
|
|
382
442
|
return addComment(parts, body, id, paragraphs, undefined, options);
|
|
383
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* A comment's entry for markers the caller placed (CR-002 section 36, asked by the editor for its
|
|
446
|
+
* E4.e, which places a new comment's markers itself: a selection may span paragraphs, where
|
|
447
|
+
* `Range.insertComment` anchors in one). Everything `insertComment` writes but the markers: the
|
|
448
|
+
* `w:comment` with the given id, author, initials, date and paragraphs (the first with a
|
|
449
|
+
* `w14:paraId`); its `w15:commentEx` (`w15:done="0"`, and `w15:paraIdParent` for a reply); its
|
|
450
|
+
* `w16cid:commentId` where that part exists, and its `w16cex:commentExtensible` where that one does;
|
|
451
|
+
* and the author's `w15:person`. The comment parts are created as `insertComment` creates them, and
|
|
452
|
+
* the date follows section 29's rule (`w:date` on the local wall clock, `w16cex:dateUtc` the date
|
|
453
|
+
* given). The id must be unused in the comments part - markers naming it are expected, being the
|
|
454
|
+
* caller's - and is not checked against them; a comment with no markers is still a comment, which
|
|
455
|
+
* Word shows unanchored.
|
|
456
|
+
*/
|
|
457
|
+
export async function addCommentEntry(body, entry) {
|
|
458
|
+
var _a;
|
|
459
|
+
const paragraphs = paragraphsOfContent(entry.content);
|
|
460
|
+
const parts = await commentPartsAccess().create(body);
|
|
461
|
+
const id = entry.id;
|
|
462
|
+
if (!Number.isInteger(id) || id < 0)
|
|
463
|
+
throw new Docx4JException(`A comment id must be a non-negative integer, not ${id}`);
|
|
464
|
+
if (((_a = parts.comments.comment) !== null && _a !== void 0 ? _a : []).some((c) => c.id === id)) {
|
|
465
|
+
throw new Docx4JException(`The comment id ${id} is already in use in this document's comments part`);
|
|
466
|
+
}
|
|
467
|
+
return addComment(parts, body, id, paragraphs, entry.parent, entry);
|
|
468
|
+
}
|
|
384
469
|
/**
|
|
385
470
|
* The `w:id` for a new comment: the caller's when given, checked unused across the comments part and
|
|
386
471
|
* the markers in the story, else the next free one. A clash throws rather than renumbering, as a
|
|
@@ -511,6 +511,7 @@ export class ListItem {
|
|
|
511
511
|
}
|
|
512
512
|
numPr.ilvl = f.createPPrBaseNumPrIlvl({ val: value });
|
|
513
513
|
linkParents(numPr.ilvl, numPr);
|
|
514
|
+
this.paragraph.settleFormatting();
|
|
514
515
|
}
|
|
515
516
|
/**
|
|
516
517
|
* Office JS `listItem.listString`: the label Word paints in front of the paragraph.
|
|
@@ -628,6 +629,7 @@ export function attachToList(paragraph, listId, level = 0) {
|
|
|
628
629
|
numPr.numId = f.createPPrBaseNumPrNumId({ val: Number(listId) });
|
|
629
630
|
numPr.ilvl = f.createPPrBaseNumPrIlvl({ val: level });
|
|
630
631
|
linkParents(numPr, paragraph.p.pPr);
|
|
632
|
+
paragraph.settleFormatting();
|
|
631
633
|
return new List(String(listId), paragraph.parentBody);
|
|
632
634
|
}
|
|
633
635
|
/**
|
|
@@ -640,12 +642,13 @@ export function detachFromList(paragraph) {
|
|
|
640
642
|
if (((_a = paragraph.p.pPr) === null || _a === void 0 ? void 0 : _a.numPr) !== undefined) {
|
|
641
643
|
delete paragraph.pPr().numPr;
|
|
642
644
|
}
|
|
643
|
-
if (
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
645
|
+
if (isListItem(paragraph)) {
|
|
646
|
+
const numPr = paragraph.numPr();
|
|
647
|
+
numPr.numId = f.createPPrBaseNumPrNumId({ val: 0 });
|
|
648
|
+
linkParents(numPr.numId, numPr);
|
|
649
|
+
delete numPr.ilvl;
|
|
650
|
+
}
|
|
651
|
+
paragraph.settleFormatting();
|
|
649
652
|
}
|
|
650
653
|
/**
|
|
651
654
|
* Office JS `paragraph.startNewList()`: a `w:abstractNum` copied from docx4j's default
|
|
@@ -9,7 +9,7 @@ import { ContentControl, type ContentControlType } from './ContentControl.mjs';
|
|
|
9
9
|
import { InlinePicture, type InlinePictureOptions } from './InlinePicture.mjs';
|
|
10
10
|
import { type CommentContent, type CommentOptions } from './comments.mjs';
|
|
11
11
|
import type { Comment } from './Comment.mjs';
|
|
12
|
-
import {
|
|
12
|
+
import { ChangeTracker } from './tracking.mjs';
|
|
13
13
|
import { TrackedChange } from './TrackedChange.mjs';
|
|
14
14
|
import { type List, type ListItem, type StartListOptions } from './List.mjs';
|
|
15
15
|
/**
|
|
@@ -206,6 +206,20 @@ export declare class Paragraph {
|
|
|
206
206
|
* @internal public so that the list views (CR-002 phase H) write `w:numPr` the same way.
|
|
207
207
|
*/
|
|
208
208
|
pPr(): wml.PPr;
|
|
209
|
+
/**
|
|
210
|
+
* After a tracked write of the paragraph's properties: when the paragraph is as it was, its
|
|
211
|
+
* recorded properties go back as they were and the `w:pPrChange` goes, since Word keeps no
|
|
212
|
+
* formatting change that changes nothing, whoever changed it back (`test/README.md` check 26:
|
|
213
|
+
* centred then left again, by the same author and by another; Increase Indent then Decrease
|
|
214
|
+
* Indent; each left no `w:pPr`; CR-002 section 29). "As it was" is Office JS's paragraph
|
|
215
|
+
* formatting (alignment, indents, spacing, outline level) read resolved from both, the rest of the
|
|
216
|
+
* properties - the style, the list - written the same: Word's Ctrl+L and Decrease Indent removed
|
|
217
|
+
* the direct values where this package's setters write `w:jc="left"` and `w:ind w:left="0"`.
|
|
218
|
+
* Without the package's `PropertyResolver` only properties written the same count.
|
|
219
|
+
*
|
|
220
|
+
* @internal public so that the list views (CR-002 phase H) settle their `w:numPr` writes.
|
|
221
|
+
*/
|
|
222
|
+
settleFormatting(): void;
|
|
209
223
|
/**
|
|
210
224
|
* The paragraph's `w:numPr`, created when absent, through {@link pPr} so that a tracked
|
|
211
225
|
* write records `w:pPrChange` first.
|
|
@@ -286,6 +300,27 @@ export declare class Paragraph {
|
|
|
286
300
|
* Returns the `w:del` elements it made, in document order.
|
|
287
301
|
*/
|
|
288
302
|
private deleteText;
|
|
303
|
+
/**
|
|
304
|
+
* Splits the revision that holds the text at `at` into two - the second half a copy of its
|
|
305
|
+
* attributes under a new `w:id` - and puts `item` between them. Word splits a revision so when
|
|
306
|
+
* text is typed inside it: around the typist's `w:ins` with tracking on, around plain text with it
|
|
307
|
+
* off (`test/README.md` checks 20 and 21). False, with nothing changed but a run split at `at`,
|
|
308
|
+
* where the text there is not directly in the revision (in a hyperlink inside it, say): the caller
|
|
309
|
+
* then does what it did before.
|
|
310
|
+
*/
|
|
311
|
+
private insertBetweenHalves;
|
|
312
|
+
/**
|
|
313
|
+
* Puts `item` beside a revision rather than in it: after it, or before it - and then before the
|
|
314
|
+
* range start of a move whose range opens just there, so that text typed at the start of moved
|
|
315
|
+
* text is not inside the move (`test/README.md` check 20 case 03).
|
|
316
|
+
*/
|
|
317
|
+
private insertBesideRevision;
|
|
318
|
+
/**
|
|
319
|
+
* A fresh annotation id for markup this paragraph writes itself: the package's counter when there
|
|
320
|
+
* is a package that keeps one, since a document has one id space; else one above every id in the
|
|
321
|
+
* story.
|
|
322
|
+
*/
|
|
323
|
+
private nextAnnotationId;
|
|
289
324
|
/**
|
|
290
325
|
* Text inserted at `at` as a `w:ins`. A run of this author's own that is already inside a
|
|
291
326
|
* `w:ins` is extended rather than nested in another one, as Word does. `anchor` is the `w:del`
|