@docx4j/core-ts 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -0
- package/README.md +32 -4
- package/dist/html/convert.mjs +2 -1
- package/dist/html/elements.mjs +2 -1
- package/dist/model/color/ColorMath.d.mts +29 -0
- package/dist/model/color/ColorMath.mjs +146 -0
- package/dist/model/color/ColorTables.d.mts +6 -0
- package/dist/model/color/ColorTables.mjs +83 -0
- package/dist/model/color/DrawingMLColors.d.mts +52 -0
- package/dist/model/color/DrawingMLColors.mjs +374 -0
- package/dist/model/color/ResolvedColor.d.mts +36 -0
- package/dist/model/color/ResolvedColor.mjs +65 -0
- package/dist/model/color/SpreadsheetColors.d.mts +36 -0
- package/dist/model/color/SpreadsheetColors.mjs +84 -0
- package/dist/model/color/StyleFill.d.mts +28 -0
- package/dist/model/color/StyleFill.mjs +21 -0
- package/dist/model/color/ThemeColors.d.mts +86 -0
- package/dist/model/color/ThemeColors.mjs +304 -0
- package/dist/model/color/WordColors.d.mts +71 -0
- package/dist/model/color/WordColors.mjs +121 -0
- package/dist/model/color/index.d.mts +6 -0
- package/dist/model/color/index.mjs +30 -0
- package/dist/model/content/Body.d.mts +145 -23
- package/dist/model/content/Body.mjs +509 -141
- package/dist/model/content/Comment.d.mts +21 -3
- package/dist/model/content/Comment.mjs +195 -19
- package/dist/model/content/ContentControl.d.mts +197 -18
- package/dist/model/content/ContentControl.mjs +664 -95
- package/dist/model/content/Font.d.mts +67 -17
- package/dist/model/content/Font.mjs +114 -35
- package/dist/model/content/InlinePicture.d.mts +11 -0
- package/dist/model/content/InlinePicture.mjs +21 -1
- package/dist/model/content/List.mjs +3 -0
- package/dist/model/content/Paragraph.d.mts +189 -13
- package/dist/model/content/Paragraph.mjs +870 -74
- package/dist/model/content/Range.d.mts +227 -12
- package/dist/model/content/Range.mjs +785 -59
- package/dist/model/content/ResolvedView.d.mts +104 -0
- package/dist/model/content/ResolvedView.mjs +596 -0
- package/dist/model/content/Table.d.mts +29 -3
- package/dist/model/content/Table.mjs +109 -33
- package/dist/model/content/TrackedChange.d.mts +172 -13
- package/dist/model/content/TrackedChange.mjs +728 -167
- package/dist/model/content/comments.d.mts +20 -6
- package/dist/model/content/comments.mjs +67 -57
- package/dist/model/content/generalException.d.mts +18 -0
- package/dist/model/content/generalException.mjs +24 -0
- package/dist/model/content/index.d.mts +8 -3
- package/dist/model/content/index.mjs +7 -2
- package/dist/model/content/mathText.d.mts +21 -0
- package/dist/model/content/mathText.mjs +267 -0
- package/dist/model/content/officeText.d.mts +27 -0
- package/dist/model/content/officeText.mjs +74 -0
- package/dist/model/content/outline.d.mts +5 -0
- package/dist/model/content/outline.mjs +13 -0
- package/dist/model/content/placeholders.d.mts +110 -0
- package/dist/model/content/placeholders.mjs +253 -0
- package/dist/model/content/search.d.mts +72 -0
- package/dist/model/content/search.mjs +204 -2
- package/dist/model/content/spanEdits.d.mts +159 -0
- package/dist/model/content/spanEdits.mjs +728 -0
- package/dist/model/content/story.d.mts +44 -0
- package/dist/model/content/story.mjs +92 -0
- package/dist/model/content/tableColumns.d.mts +1 -1
- package/dist/model/content/tableColumns.mjs +108 -80
- package/dist/model/content/tableRevisions.d.mts +11 -3
- package/dist/model/content/tableRevisions.mjs +72 -42
- package/dist/model/content/tableRules.d.mts +30 -0
- package/dist/model/content/tableRules.mjs +100 -0
- package/dist/model/content/textBoxes.d.mts +18 -0
- package/dist/model/content/textBoxes.mjs +105 -0
- package/dist/model/content/trackedChangeFilter.d.mts +39 -0
- package/dist/model/content/trackedChangeFilter.mjs +81 -0
- package/dist/model/content/tracking.d.mts +28 -0
- package/dist/model/content/tracking.mjs +149 -10
- package/dist/model/content/tree.d.mts +171 -22
- package/dist/model/content/tree.mjs +329 -53
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +8 -0
- package/dist/model/customxml/CustomXmlPartCollection.mjs +34 -11
- package/dist/model/customxml/XmlMapping.d.mts +1 -1
- package/dist/model/customxml/XmlMapping.mjs +11 -0
- package/dist/model/customxml/bindings.d.mts +21 -2
- package/dist/model/customxml/bindings.mjs +76 -32
- package/dist/model/customxml/kinds.d.mts +8 -3
- package/dist/model/customxml/kinds.mjs +26 -5
- package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
- package/dist/model/customxml/picturePlaceholder.mjs +32 -0
- package/dist/model/customxml/pictures.d.mts +4 -3
- package/dist/model/customxml/pictures.mjs +18 -6
- package/dist/model/customxml/xhtml.mjs +8 -2
- package/dist/model/index.d.mts +2 -0
- package/dist/model/index.mjs +4 -0
- package/dist/model/listnumbering/Emulator.d.mts +37 -5
- package/dist/model/listnumbering/Emulator.mjs +63 -9
- package/dist/model/properties/PropertyResolver.d.mts +60 -1
- package/dist/model/properties/PropertyResolver.mjs +115 -2
- package/dist/model/properties/catalogue.d.mts +19 -5
- package/dist/model/properties/catalogue.mjs +41 -15
- package/dist/model/properties/log.d.mts +1 -1
- package/dist/model/properties/log.mjs +5 -5
- package/dist/model/properties/table.mjs +4 -2
- package/dist/model/text/Extractor.d.mts +289 -0
- package/dist/model/text/Extractor.mjs +2162 -0
- package/dist/model/text/LinkTargets.d.mts +45 -0
- package/dist/model/text/LinkTargets.mjs +170 -0
- package/dist/model/text/MarkdownExporter.d.mts +140 -0
- package/dist/model/text/MarkdownExporter.mjs +255 -0
- package/dist/model/text/PlainText.d.mts +29 -0
- package/dist/model/text/PlainText.mjs +539 -0
- package/dist/model/text/TextContext.d.mts +161 -0
- package/dist/model/text/TextContext.mjs +425 -0
- package/dist/model/text/TextOptions.d.mts +150 -0
- package/dist/model/text/TextOptions.mjs +278 -0
- package/dist/model/text/TextResult.d.mts +504 -0
- package/dist/model/text/TextResult.mjs +526 -0
- package/dist/model/text/Texts.d.mts +99 -0
- package/dist/model/text/Texts.mjs +187 -0
- package/dist/model/text/WmlToMarkdown.d.mts +120 -0
- package/dist/model/text/WmlToMarkdown.mjs +911 -0
- package/dist/model/text/commonmark.d.mts +121 -0
- package/dist/model/text/commonmark.mjs +655 -0
- package/dist/model/text/index.d.mts +7 -0
- package/dist/model/text/index.mjs +8 -0
- package/dist/model/text/normalize.d.mts +8 -0
- package/dist/model/text/normalize.mjs +13 -0
- package/dist/model/text/outline.d.mts +4 -0
- package/dist/model/text/outline.mjs +71 -0
- package/dist/office-js/fragment.d.mts +7 -5
- package/dist/office-js/fragment.mjs +63 -14
- package/dist/office-js/proxy.d.mts +8 -0
- package/dist/office-js/proxy.mjs +29 -0
- package/dist/office-js/run.mjs +2 -0
- package/dist/office-js/supported.generated.mjs +16 -0
- package/dist/office-js/toApiScript.mjs +21 -5
- package/dist/opc/Save.d.mts +17 -0
- package/dist/opc/Save.mjs +12 -2
- package/dist/opc/index.d.mts +3 -3
- package/dist/opc/index.mjs +1 -1
- package/dist/opc/mce/preprocessor.d.mts +28 -3
- package/dist/opc/mce/preprocessor.mjs +4 -4
- package/dist/opc/mce/understood.d.mts +30 -6
- package/dist/opc/mce/understood.mjs +37 -7
- package/dist/packages/OpcPackage.d.mts +13 -0
- package/dist/packages/OpcPackage.mjs +1 -0
- package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
- package/dist/packages/WordprocessingMLPackage.mjs +155 -20
- package/dist/parts/DefaultXmlPart.mjs +2 -2
- package/dist/parts/Part.d.mts +10 -2
- package/dist/parts/XmlPart.d.mts +12 -30
- package/dist/parts/XmlPart.mjs +44 -93
- package/dist/parts/wml/comments.mjs +30 -0
- package/dist/parts/wml/index.mjs +12 -0
- package/dist/xml/dom.d.mts +7 -0
- package/dist/xml/dom.mjs +18 -0
- package/package.json +2 -2
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { Docx4JException, PropertyResolverNotCreatedException } from '../../opc/exceptions.mjs';
|
|
2
|
-
import { typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES,
|
|
3
|
-
import {
|
|
2
|
+
import { hiddenRunsOf, typeNameOf, childrenOf, linkParents, BLOCK_LEVEL_TYPES, rowsOf, cellsOf, selectedBranchOf, branchesOf, branchListsOf, ALTERNATE_CONTENT, BRANCH_TYPES, inUnreadBranch, assertOutsideBranch, writingEveryBranch } from './tree.mjs';
|
|
3
|
+
import { br as breakItem, tbl as tableOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
|
+
import { oneParagraphRun } from './officeText.mjs';
|
|
4
5
|
import { runOf, paragraphOf, runsOf } from './tree.mjs';
|
|
6
|
+
import { textBoxesOf } from './textBoxes.mjs';
|
|
7
|
+
import { outlineReader } from './outline.mjs';
|
|
5
8
|
import { Paragraph } from './Paragraph.mjs';
|
|
9
|
+
import { Range } from './Range.mjs';
|
|
6
10
|
import { Table } from './Table.mjs';
|
|
7
|
-
import { ContentControl, collectControls } from './ContentControl.mjs';
|
|
11
|
+
import { ContentControl, collectControls, assertControlHolds, controlsOfTypes } from './ContentControl.mjs';
|
|
8
12
|
import { sdt as sdtOf, nextSdtId } from '@docx4j/generated-objects-ts/builders/wml';
|
|
9
13
|
import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
|
|
10
14
|
import { InlinePicture, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
11
15
|
import { contentOf } from './ooxml.mjs';
|
|
12
16
|
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
13
|
-
import {
|
|
17
|
+
import { wrappingCellOf, paragraphEndOf, endsWithParagraph, endWithParagraph, endCellsWithParagraphs } from './tableRules.mjs';
|
|
18
|
+
import { keepCellMarkLast } from './spanEdits.mjs';
|
|
19
|
+
import { expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
|
|
14
20
|
import { commentApi } from './comments.mjs';
|
|
21
|
+
import { storyLayout } from './story.mjs';
|
|
15
22
|
import { trackerOf, trackInsertedBlock, markHolderInserted } from './tracking.mjs';
|
|
16
|
-
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted,
|
|
23
|
+
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderChangeAt, resolving, resolvePieces, alternateTokens, listedListing, pairTwins } from './TrackedChange.mjs';
|
|
24
|
+
import { pieceMatcher } from './trackedChangeFilter.mjs';
|
|
17
25
|
import { listsOf, listLabelsOf } from './List.mjs';
|
|
18
26
|
/**
|
|
19
27
|
* A subset of Office JS `Word.Body` over any container with block-level content: the main
|
|
@@ -47,14 +55,31 @@ export class Body {
|
|
|
47
55
|
throw new PropertyResolverNotCreatedException();
|
|
48
56
|
return resolver;
|
|
49
57
|
}
|
|
58
|
+
/** Which of a paragraph's runs are hidden: a run's own `w:vanish`, else its styles' (`hiddenRunsOf`; CR-012 phase 4). */
|
|
59
|
+
hiddenRuns(p) {
|
|
60
|
+
const pkg = this.package_;
|
|
61
|
+
return hiddenRunsOf(pkg === null || pkg === void 0 ? void 0 : pkg.propertyResolverOrUndefined, p);
|
|
62
|
+
}
|
|
50
63
|
/** The live content array (docx4j getContent()); block-level elements only, sectPr excluded. */
|
|
51
64
|
get content() {
|
|
52
65
|
var _a;
|
|
53
66
|
var _b;
|
|
54
67
|
return ((_a = (_b = this.container).content) !== null && _a !== void 0 ? _a : (_b.content = []));
|
|
55
68
|
}
|
|
56
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Every paragraph in document order, descending into tables and content controls, and into the
|
|
71
|
+
* selected branch of a kept `mc:AlternateContent` at block, row and cell level, as Word reads it
|
|
72
|
+
* (CR-013 phase 2). A paragraph in a branch is read-only (D4).
|
|
73
|
+
*/
|
|
57
74
|
get paragraphs() {
|
|
75
|
+
return this.paragraphsIn('read');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The paragraphs a walk in this mode takes: a reader's, `paragraphs`; a bulk edit's (`'every'`), every
|
|
79
|
+
* branch's too, the selected branch's where a reader sees them and each other branch's after them,
|
|
80
|
+
* where the element stands (CR-013 phase 3).
|
|
81
|
+
*/
|
|
82
|
+
paragraphsIn(mode) {
|
|
58
83
|
const out = [];
|
|
59
84
|
const visit = (items) => {
|
|
60
85
|
var _a;
|
|
@@ -67,10 +92,14 @@ export class Body {
|
|
|
67
92
|
if (typeof v !== 'object' || v === null)
|
|
68
93
|
continue;
|
|
69
94
|
if (tn === 'org_docx4j_wml.Tbl') {
|
|
70
|
-
for (const row of rowsOf(v))
|
|
71
|
-
for (const cell of cellsOf(row.element.value))
|
|
95
|
+
for (const row of rowsOf(v, mode))
|
|
96
|
+
for (const cell of cellsOf(row.element.value, mode))
|
|
72
97
|
visit((_a = childrenOf(cell.element.value)) !== null && _a !== void 0 ? _a : []);
|
|
73
98
|
}
|
|
99
|
+
else if (tn === ALTERNATE_CONTENT) {
|
|
100
|
+
for (const branch of branchListsOf(v, mode))
|
|
101
|
+
visit(branch);
|
|
102
|
+
}
|
|
74
103
|
else {
|
|
75
104
|
const children = childrenOf(v);
|
|
76
105
|
if (children)
|
|
@@ -154,14 +183,45 @@ export class Body {
|
|
|
154
183
|
}
|
|
155
184
|
return out;
|
|
156
185
|
}
|
|
157
|
-
/**
|
|
186
|
+
/**
|
|
187
|
+
* The tables directly in this body, as Office JS reports them (nested tables are a cell's), a table in
|
|
188
|
+
* the selected branch of a kept block-level `mc:AlternateContent` among them (CR-013 phase 2).
|
|
189
|
+
*/
|
|
158
190
|
get tables() {
|
|
159
|
-
|
|
191
|
+
const out = [];
|
|
192
|
+
const visit = (items) => {
|
|
193
|
+
var _a;
|
|
194
|
+
for (const element of items) {
|
|
195
|
+
const tn = typeNameOf(element);
|
|
196
|
+
if (tn === 'org_docx4j_wml.Tbl')
|
|
197
|
+
out.push(new Table(element, items, this));
|
|
198
|
+
else if (tn === ALTERNATE_CONTENT)
|
|
199
|
+
visit((_a = selectedBranchOf(element.value)) !== null && _a !== void 0 ? _a : []);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
visit(this.content);
|
|
203
|
+
return out;
|
|
160
204
|
}
|
|
161
205
|
/** Every content control in this body, in document order, nested ones included. */
|
|
162
206
|
get contentControls() {
|
|
207
|
+
return this.contentControlsIn('read');
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
|
|
211
|
+
* where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
|
|
212
|
+
* departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
|
|
213
|
+
*/
|
|
214
|
+
getContentControls(options) {
|
|
215
|
+
return controlsOfTypes(this.contentControls, options);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The content controls a walk in this mode takes: a reader's, `contentControls`; the binding passes'
|
|
219
|
+
* (`'every'`), every branch's of a kept `mc:AlternateContent` too, each other branch's where the element
|
|
220
|
+
* stands (CR-013 phase 3). An extension.
|
|
221
|
+
*/
|
|
222
|
+
contentControlsIn(mode) {
|
|
163
223
|
const out = [];
|
|
164
|
-
collectControls(this.content, this, out);
|
|
224
|
+
collectControls(this.content, this, out, mode);
|
|
165
225
|
return out;
|
|
166
226
|
}
|
|
167
227
|
/** Every inline picture in this body, in document order (Office JS Body.inlinePictures). */
|
|
@@ -201,33 +261,72 @@ export class Body {
|
|
|
201
261
|
const element = container === null || container === void 0 ? void 0 : container.find((e) => e.value === p);
|
|
202
262
|
return element && container ? new Paragraph(element, container, this) : undefined;
|
|
203
263
|
}
|
|
204
|
-
/**
|
|
264
|
+
/**
|
|
265
|
+
* The text as Office JS's `body.text` gives it in Word's default view (CR-012 phase 4; checks 30, 31, 48
|
|
266
|
+
* and 51): each paragraph's text (`Paragraph.text`) followed by its mark, `\r`, the last paragraph's left
|
|
267
|
+
* out; in a table a row's cells a `\t` apart, each cell's paragraphs a mark apart (its last mark not
|
|
268
|
+
* written), and the row ended by `\r\n`. Text boxes are not the body's (check 42).
|
|
269
|
+
*/
|
|
205
270
|
get text() {
|
|
206
|
-
return this.
|
|
271
|
+
return this.textIn('accepted');
|
|
207
272
|
}
|
|
208
273
|
/**
|
|
209
274
|
* The text in one of the two views (extension; `text` is the accepted one).
|
|
210
275
|
* `{ view: 'original' }` reads the document as it was before the tracked changes.
|
|
211
276
|
*/
|
|
212
277
|
getText(options) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
278
|
+
var _a;
|
|
279
|
+
return this.textIn((_a = options === null || options === void 0 ? void 0 : options.view) !== null && _a !== void 0 ? _a : 'accepted');
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The body's range (Office JS `Body.getRange`; CR-014 phase 1): 'Whole' from its first paragraph's start to
|
|
283
|
+
* its last one's end, with that paragraph's mark (check 51: a cell's `CELLP1\rCELLP2\rCELLP3\t`, a note's
|
|
284
|
+
* `… NOTEP2\r`), 'Content' the same without the mark, 'Start' and 'End' collapsed at its ends.
|
|
285
|
+
*/
|
|
286
|
+
getRange(location = 'Whole') {
|
|
287
|
+
const paragraphs = this.paragraphs;
|
|
288
|
+
const first = paragraphs[0];
|
|
289
|
+
if (!first)
|
|
290
|
+
throw new Docx4JException('This body holds no paragraph');
|
|
291
|
+
const last = paragraphs[paragraphs.length - 1];
|
|
292
|
+
if (location === 'Start')
|
|
293
|
+
return first.getRange('Start');
|
|
294
|
+
if (location === 'End')
|
|
295
|
+
return last.getRange('End');
|
|
296
|
+
if (paragraphs.length === 1)
|
|
297
|
+
return first.getRange(location);
|
|
298
|
+
return new Range(first, 0, last.text.length, true, location === 'Whole', last);
|
|
299
|
+
}
|
|
300
|
+
textIn(view) {
|
|
301
|
+
// the original view's marks are the ones Reject All keeps: a deleted mark written, an inserted one not
|
|
302
|
+
const { text } = storyLayout(this.content, (el, items) => {
|
|
303
|
+
const p = new Paragraph(el, items, this);
|
|
304
|
+
return view === 'original' ? p.getText({ view }) : p.text;
|
|
305
|
+
}, view === 'original' ? (p) => { var _a, _b; return ((_b = (_a = p.pPr) === null || _a === void 0 ? void 0 : _a.rPr) === null || _b === void 0 ? void 0 : _b.ins) === undefined; } : undefined);
|
|
306
|
+
// the last paragraph's mark is not written
|
|
307
|
+
return text.endsWith('\r') ? text.slice(0, -1) : text;
|
|
216
308
|
}
|
|
217
309
|
/** The package's change tracker while `changeTrackingMode` is on; undefined when it is off (CR-002 phase F). */
|
|
218
310
|
get changeTracker() {
|
|
219
311
|
var _a;
|
|
220
312
|
return (_a = trackerOf(this.package_)) === null || _a === void 0 ? void 0 : _a.forPart(this.part);
|
|
221
313
|
}
|
|
314
|
+
/** Refuses a write to a body in a branch of a kept `mc:AlternateContent`: a cell or a control a reader found there (CR-013 D4). */
|
|
315
|
+
assertWritable() {
|
|
316
|
+
assertOutsideBranch(this.container, 'Body');
|
|
317
|
+
}
|
|
222
318
|
insertParagraph(text, location) {
|
|
223
|
-
|
|
319
|
+
var _a;
|
|
320
|
+
const run = oneParagraphRun(text); // CR-002 section 60 E2: one paragraph, its line breaks w:br and w:cr
|
|
321
|
+
const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : []);
|
|
224
322
|
return this.insertElement(el, location);
|
|
225
323
|
}
|
|
226
324
|
/** Text at the start of the first paragraph, the end of the last, or replacing everything with one paragraph. */
|
|
227
325
|
insertText(text, location) {
|
|
326
|
+
this.assertWritable();
|
|
228
327
|
const paragraphs = this.paragraphs;
|
|
229
328
|
if (location === 'Replace' || paragraphs.length === 0) {
|
|
230
|
-
this.
|
|
329
|
+
this.clearContent(false); // its own paragraph ends a cell, so the cell's last one goes too
|
|
231
330
|
const p = this.insertParagraph(text, 'End');
|
|
232
331
|
return p.getRange('Content');
|
|
233
332
|
}
|
|
@@ -252,7 +351,7 @@ export class Body {
|
|
|
252
351
|
// cells the builder wrote, the table's width made automatic (CR-002 section 42.5 item 7).
|
|
253
352
|
const tblPr = ((_a = (_b = element.value).tblPr) !== null && _a !== void 0 ? _a : (_b.tblPr = {}));
|
|
254
353
|
tblPr.tblW = { w: 0, type: 'auto' };
|
|
255
|
-
this.insertElement(element, location);
|
|
354
|
+
this.insertElement(element, location, undefined, 'InvalidArgument');
|
|
256
355
|
return new Table(element, this.content, this);
|
|
257
356
|
}
|
|
258
357
|
/**
|
|
@@ -261,6 +360,7 @@ export class Body {
|
|
|
261
360
|
* than the text area is scaled down, as docx4j's CxCy.scale does.
|
|
262
361
|
*/
|
|
263
362
|
insertInlinePictureFromBase64(base64, location, options) {
|
|
363
|
+
this.assertWritable();
|
|
264
364
|
if (!this.part)
|
|
265
365
|
throw new Docx4JException('This body has no part, so an image cannot be related to it');
|
|
266
366
|
const { run, drawing } = addImage(this.part, base64, this.container, writableWidthEmu(this.container), options);
|
|
@@ -285,7 +385,8 @@ export class Body {
|
|
|
285
385
|
* before or after a target paragraph, element or address. Links PARENT. Rejects anything the
|
|
286
386
|
* body cannot hold, naming what was passed. Returns the view of the first inserted element.
|
|
287
387
|
*/
|
|
288
|
-
insertElement(element, location, target) {
|
|
388
|
+
insertElement(element, location, target, refusal, replacing) {
|
|
389
|
+
this.assertWritable();
|
|
289
390
|
const elements = Array.isArray(element) ? element : [element];
|
|
290
391
|
if (elements.length === 0)
|
|
291
392
|
throw new Docx4JException('Nothing to insert');
|
|
@@ -303,11 +404,18 @@ export class Body {
|
|
|
303
404
|
index = container.length;
|
|
304
405
|
else {
|
|
305
406
|
const t = this.resolveTarget(target);
|
|
407
|
+
assertOutsideBranch(t.element.value, 'Body', 'the block to insert beside'); // beside it is in the branch too
|
|
306
408
|
container = t.container;
|
|
307
409
|
index = container.indexOf(t.element) + (location === 'After' ? 1 : 0);
|
|
308
410
|
}
|
|
309
|
-
|
|
411
|
+
// a bound block control holds one paragraph (CR-002 section 61): `replacing` is the block the write takes away after
|
|
412
|
+
assertControlHolds(this, this.ownerOf(container), replacing ? container.filter((e) => e !== replacing) : container, elements, refusal);
|
|
413
|
+
// a cell ends with a paragraph, as Word reads it: the cells of what goes in (D3), and the cell it goes in (CR-002 section 55)
|
|
414
|
+
endCellsWithParagraphs(elements);
|
|
310
415
|
const owner = this.ownerOf(container);
|
|
416
|
+
const cell = wrappingCellOf(owner);
|
|
417
|
+
const end = cell ? paragraphEndOf(cell.content) : undefined;
|
|
418
|
+
container.splice(index, 0, ...elements);
|
|
311
419
|
const tracker = this.changeTracker;
|
|
312
420
|
for (const el of elements) {
|
|
313
421
|
linkParents(el, owner);
|
|
@@ -319,12 +427,15 @@ export class Body {
|
|
|
319
427
|
if (tracker)
|
|
320
428
|
trackInsertedBlock(tracker, el, container);
|
|
321
429
|
}
|
|
430
|
+
if (cell)
|
|
431
|
+
this.keepCellEnd(cell, end, container);
|
|
322
432
|
const first = elements[0];
|
|
323
433
|
return typeNameOf(first) === 'org_docx4j_wml.P' ? new Paragraph(first, container, this) : { element: first, container };
|
|
324
434
|
}
|
|
325
435
|
/** Inserts a WML fragment (one or more w:p / w:tbl / ... as inside document.xml); the standard prefixes are declared for it. */
|
|
326
436
|
async insertXml(xml, location, target) {
|
|
327
|
-
|
|
437
|
+
this.assertWritable();
|
|
438
|
+
return this.insertContent(await contentOf(xml, { preprocess: this.preprocessor() }), location, target, 'GeneralException');
|
|
328
439
|
}
|
|
329
440
|
/**
|
|
330
441
|
* Word's `insertOoxml`: a flat OPC `pkg:package` string, whose body content is inserted with
|
|
@@ -334,12 +445,39 @@ export class Body {
|
|
|
334
445
|
* what was inserted, as `insertXml` does, since a package may bring several blocks.
|
|
335
446
|
*/
|
|
336
447
|
async insertOoxml(ooxml, location, target) {
|
|
448
|
+
this.assertWritable();
|
|
337
449
|
const elements = await contentOf(ooxml, { preprocess: this.preprocessor(), target: this.part });
|
|
338
450
|
if (location === 'Replace') {
|
|
339
|
-
this.clear()
|
|
340
|
-
|
|
451
|
+
assertControlHolds(this, this.container, [], elements, 'GeneralException'); // before the clear (CR-002 section 61)
|
|
452
|
+
// In a cell, what ends with a table goes in before the cell's last paragraph, which stays, emptied, as Word
|
|
453
|
+
// keeps it (check 52, case 94; CR-002 section 55.6); what ends with a paragraph replaces it, as before
|
|
454
|
+
const cell = endsWithParagraph(elements) ? undefined : wrappingCellOf(this.container);
|
|
455
|
+
this.clearContent(cell !== undefined);
|
|
456
|
+
const end = cell ? paragraphEndOf(cell.content) : undefined;
|
|
457
|
+
if (end && end.container === this.content)
|
|
458
|
+
return this.insertContent(elements, 'Before', { element: end.element, container: end.container }, 'GeneralException');
|
|
459
|
+
return this.insertContent(elements, 'End', undefined, 'GeneralException');
|
|
460
|
+
}
|
|
461
|
+
return this.insertContent(elements, location, target, 'GeneralException');
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* After an insertion into a cell: the cell still ends with a paragraph, as Word reads it (CR-002 section 55). Where
|
|
465
|
+
* a table went in after the paragraph the cell ended with, in the cell's own content, Word's form (check 52, cases 86
|
|
466
|
+
* and 87, `keepCellMarkLast`): that paragraph split at its end, the cell's mark staying last after the table, the
|
|
467
|
+
* first half's mark the inserted one under tracking. Anywhere else docx4j's repair, an empty paragraph after any wrapper.
|
|
468
|
+
*/
|
|
469
|
+
keepCellEnd(cell, end, container) {
|
|
470
|
+
var _a;
|
|
471
|
+
const content = ((_a = cell.content) !== null && _a !== void 0 ? _a : (cell.content = []));
|
|
472
|
+
if (endsWithParagraph(content))
|
|
473
|
+
return;
|
|
474
|
+
if (end && end.container === content && container === content) {
|
|
475
|
+
keepCellMarkLast(new Paragraph(end.element, content, this), content);
|
|
476
|
+
return;
|
|
341
477
|
}
|
|
342
|
-
|
|
478
|
+
const appended = endWithParagraph(cell);
|
|
479
|
+
if (appended)
|
|
480
|
+
this.assignParaId(appended);
|
|
343
481
|
}
|
|
344
482
|
/** The package's DOM preprocessor (the MCE one), for fragments and incoming packages. */
|
|
345
483
|
preprocessor() {
|
|
@@ -347,16 +485,22 @@ export class Body {
|
|
|
347
485
|
const preprocess = (_a = this.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
|
|
348
486
|
return preprocess ? (doc) => preprocess(doc) : undefined;
|
|
349
487
|
}
|
|
350
|
-
insertContent(elements, location, target) {
|
|
488
|
+
insertContent(elements, location, target, refusal) {
|
|
351
489
|
if (elements.length === 0)
|
|
352
490
|
return [];
|
|
353
|
-
this.insertElement(elements, location, target);
|
|
491
|
+
this.insertElement(elements, location, target, refusal);
|
|
354
492
|
return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
|
|
355
493
|
? new Paragraph(el, this.containerOf(el), this)
|
|
356
494
|
: { element: el, container: this.containerOf(el) });
|
|
357
495
|
}
|
|
358
496
|
/** Matches over every paragraph, in document order. */
|
|
359
497
|
search(text, options) {
|
|
498
|
+
// Word's paragraph mark, ^p, is searched across the body's whole range (CR-014 phase 3); a wildcard query across its
|
|
499
|
+
// content, which Word's wildcards read without the last paragraph's mark (check 59 case 5, a control's; K7)
|
|
500
|
+
if ((options === null || options === void 0 ? void 0 : options.matchWildcards) && this.paragraphs.length > 0)
|
|
501
|
+
return this.getRange('Content').search(text, options);
|
|
502
|
+
if (!(options === null || options === void 0 ? void 0 : options.matchWildcards) && !(options === null || options === void 0 ? void 0 : options.matchRegExp) && paragraphMarkPieces(text) && this.paragraphs.length > 0)
|
|
503
|
+
return this.getRange('Whole').search(text, options);
|
|
360
504
|
return this.paragraphs.flatMap((p) => p.search(text, options));
|
|
361
505
|
}
|
|
362
506
|
/**
|
|
@@ -365,40 +509,101 @@ export class Body {
|
|
|
365
509
|
* the package's `changeTrackingMode` is on.
|
|
366
510
|
*/
|
|
367
511
|
replaceText(find, replace, options) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
512
|
+
refuseMarkReplacement('Body', find, options);
|
|
513
|
+
if (options === null || options === void 0 ? void 0 : options.matchWildcards)
|
|
514
|
+
refuseWildcardMarkReplacement('Body', this.search(find, options));
|
|
515
|
+
this.assertWritable();
|
|
516
|
+
// every branch of a kept mc:AlternateContent, each paragraph searched on its own; counted where a
|
|
517
|
+
// reader sees the match, as resolve-all counts the listed changes (CR-013 phase 3)
|
|
518
|
+
const matches = this.paragraphsIn('every').flatMap((p) => p.searchMatches(find, options).map((m) => ({ ...m, unread: inUnreadBranch(p.p) })));
|
|
519
|
+
writingEveryBranch(() => {
|
|
520
|
+
for (let i = matches.length - 1; i >= 0; i--) {
|
|
521
|
+
const { range, match } = matches[i];
|
|
522
|
+
range.insertText(expandReplacement(replace, match, options), 'Replace');
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
return matches.filter((m) => !m.unread).length;
|
|
374
526
|
}
|
|
375
527
|
/**
|
|
376
528
|
* Removes everything (section properties stay). While the package tracks changes, every
|
|
377
529
|
* paragraph is marked deleted and every row's `w:trPr` takes a `w:del` instead. Removed outright,
|
|
378
530
|
* the relationships the content held (its pictures' images) are settled on save (CR-002 section 47).
|
|
531
|
+
* In a table cell the paragraph the cell ends with stays, emptied, its properties kept, and tracked
|
|
532
|
+
* only its text is deleted, as Word keeps a cell's last paragraph (check 52, cases 92 and 93; CR-002 section 55.6).
|
|
379
533
|
*/
|
|
380
534
|
clear() {
|
|
535
|
+
this.clearContent(true);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* `clear()`; with `keepCellEnd` false a cell's last paragraph goes as well, its mark marked deleted under
|
|
539
|
+
* tracking, for a caller that puts its own paragraph in next (`insertText(…, 'Replace')`, `TableCell.value`).
|
|
540
|
+
* @internal
|
|
541
|
+
*/
|
|
542
|
+
clearContent(keepCellEnd) {
|
|
381
543
|
var _a;
|
|
544
|
+
this.assertWritable();
|
|
382
545
|
const tracker = this.changeTracker;
|
|
383
546
|
if (!tracker) {
|
|
547
|
+
const cell = keepCellEnd ? wrappingCellOf(this.container) : undefined;
|
|
548
|
+
const end = cell ? paragraphEndOf(cell.content) : undefined;
|
|
384
549
|
const ids = new Set();
|
|
385
550
|
relationshipIdsIn(this.content, ids);
|
|
386
551
|
queueRelationshipRemovals((_a = this.package_) === null || _a === void 0 ? void 0 : _a.pendingRelationshipRemovals, this.part, ids);
|
|
387
552
|
this.content.length = 0;
|
|
553
|
+
if (cell && !endsWithParagraph(cell.content)) {
|
|
554
|
+
if (end) {
|
|
555
|
+
end.element.value.content = [];
|
|
556
|
+
this.content.push(end.element);
|
|
557
|
+
linkParents(end.element, this.container);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
const appended = endWithParagraph(cell);
|
|
561
|
+
if (appended)
|
|
562
|
+
this.assignParaId(appended);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
388
565
|
return;
|
|
389
566
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
567
|
+
// every branch of a kept mc:AlternateContent (CR-013 phase 3)
|
|
568
|
+
writingEveryBranch(() => {
|
|
569
|
+
for (const row of this.rowElements())
|
|
570
|
+
tracker.markRowDeleted(row.value);
|
|
571
|
+
for (const p of this.paragraphsIn('every'))
|
|
572
|
+
p.remove(keepCellEnd);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Marks everything in this body deleted in place, in every branch of a kept `mc:AlternateContent`: each
|
|
577
|
+
* paragraph's text and mark, each row of its tables (a tracked row or table deletion's cells; CR-013
|
|
578
|
+
* phase 3). Internal: what `Table.delete` and `deleteRows` write while changes are tracked.
|
|
579
|
+
*/
|
|
580
|
+
markContentDeleted(tracker) {
|
|
581
|
+
// the paragraphs, then the rows last first: the order, and so the ids, a row's deletion has always written;
|
|
582
|
+
// a row already deleted is left as it is, as a paragraph already deleted is (a range's deletion, CR-014 phase 2)
|
|
583
|
+
writingEveryBranch(() => {
|
|
584
|
+
var _a;
|
|
585
|
+
for (const p of this.paragraphsIn('every'))
|
|
586
|
+
p.markDeletedInPlace(tracker);
|
|
587
|
+
for (const row of this.rowElements().reverse())
|
|
588
|
+
if (!((_a = row.value.trPr) === null || _a === void 0 ? void 0 : _a.del))
|
|
589
|
+
tracker.markRowDeleted(row.value);
|
|
590
|
+
});
|
|
394
591
|
}
|
|
395
592
|
// --- change tracking (CR-002 phase F) ---------------------------------------------------
|
|
396
593
|
/**
|
|
397
594
|
* Every tracked change in this body, in document order. Touching insertions or deletions by one
|
|
398
595
|
* author - a deleted paragraph's text and mark, a deletion across a mark - are one change, as
|
|
399
596
|
* Office JS lists them (CR-002 section 29, checks 22 and 23).
|
|
597
|
+
*
|
|
598
|
+
* In a kept `mc:AlternateContent` at block, paragraph, row or cell level the selected branch's changes
|
|
599
|
+
* are listed, as Word reads it (CR-013 phase 2); each other branch's are their twins, paired by kind and
|
|
600
|
+
* position, which resolving a listed change resolves too (phase 3, docx4j's twins).
|
|
400
601
|
*/
|
|
401
602
|
getTrackedChanges() {
|
|
603
|
+
return this.trackedChanges(listedListing());
|
|
604
|
+
}
|
|
605
|
+
/** The listing in either mode (`Listing`): resolve-all's puts every other branch's changes after the story's. */
|
|
606
|
+
trackedChanges(listing) {
|
|
402
607
|
const out = [];
|
|
403
608
|
const visit = (items, into) => {
|
|
404
609
|
var _a, _b;
|
|
@@ -406,12 +611,12 @@ export class Body {
|
|
|
406
611
|
const el = items[i];
|
|
407
612
|
const tn = typeNameOf(el);
|
|
408
613
|
if (tn === 'org_docx4j_wml.P') {
|
|
409
|
-
into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this)));
|
|
614
|
+
into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this), listing));
|
|
410
615
|
continue;
|
|
411
616
|
}
|
|
412
617
|
// a block control or custom XML element that is itself an insertion (check 30): one piece,
|
|
413
618
|
// grouping with the insertions around and inside it, as Word lists them
|
|
414
|
-
const inserted =
|
|
619
|
+
const inserted = holderChangeAt(items, i, undefined, this);
|
|
415
620
|
if (inserted) {
|
|
416
621
|
into.push(inserted);
|
|
417
622
|
visit((_a = childrenOf(items[i + 1].value)) !== null && _a !== void 0 ? _a : [], into);
|
|
@@ -427,31 +632,16 @@ export class Body {
|
|
|
427
632
|
// inserted paragraphs is one change; rows added at a table's end with a paragraph typed after
|
|
428
633
|
// it, one; a paragraph whose mark is plain before rows added at the start, two). Its property
|
|
429
634
|
// records are one change of their own where the table stands (CR-002 section 35), kept apart.
|
|
430
|
-
const tableChange = tablePropertiesChangeOf(el);
|
|
635
|
+
const tableChange = tablePropertiesChangeOf(el, this);
|
|
431
636
|
if (tableChange)
|
|
432
637
|
into.push(BREAK, tableChange, BREAK);
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (cellChange)
|
|
441
|
-
tokens.push(cellChange, BREAK);
|
|
442
|
-
visit((_b = childrenOf(cell.element.value)) !== null && _b !== void 0 ? _b : [], tokens);
|
|
443
|
-
tokens.push(BREAK);
|
|
444
|
-
}
|
|
445
|
-
const inner = groupTouching(tokens);
|
|
446
|
-
const rowChanges = trackedChangesOfRow(row.element, row.container, inner, { element: el, container: items }, this);
|
|
447
|
-
if (rowChanges.length > 0)
|
|
448
|
-
into.push(...rowChanges);
|
|
449
|
-
else {
|
|
450
|
-
into.push(BREAK);
|
|
451
|
-
for (const change of inner)
|
|
452
|
-
into.push(change, BREAK);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
638
|
+
visitRows((_b = childrenOf(v)) !== null && _b !== void 0 ? _b : [], { element: el, container: items }, into);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
// a kept mc:AlternateContent is its selected branch, as Word reads it, and keeps nothing apart
|
|
642
|
+
// (CR-013 phase 2): Word's save has no element there. Its other branches' are twins (phase 3)
|
|
643
|
+
if (tn === ALTERNATE_CONTENT) {
|
|
644
|
+
alternateTokens(v, visit, into, listing);
|
|
455
645
|
continue;
|
|
456
646
|
}
|
|
457
647
|
// a block-level content control or custom XML element keeps what is inside it apart; a
|
|
@@ -464,51 +654,137 @@ export class Body {
|
|
|
464
654
|
}
|
|
465
655
|
}
|
|
466
656
|
};
|
|
657
|
+
// a table's rows, through row-level controls and custom XML and a row-level mc:AlternateContent
|
|
658
|
+
const visitRows = (items, table, into) => {
|
|
659
|
+
var _a;
|
|
660
|
+
for (const el of items) {
|
|
661
|
+
const tn = typeNameOf(el);
|
|
662
|
+
const v = el.value;
|
|
663
|
+
if (typeof v !== 'object' || v === null)
|
|
664
|
+
continue;
|
|
665
|
+
if (tn === 'org_docx4j_wml.Tr') {
|
|
666
|
+
// A row that is itself a revision reports as one change, as Office JS does: the markup its
|
|
667
|
+
// cells carry (Word marks every run and mark of a deleted or inserted row) belongs to that
|
|
668
|
+
// change, which accepts or rejects it along with the row.
|
|
669
|
+
const tokens = [];
|
|
670
|
+
visitCells((_a = childrenOf(v)) !== null && _a !== void 0 ? _a : [], el, tokens);
|
|
671
|
+
const inner = groupTouching(tokens);
|
|
672
|
+
const rowChanges = trackedChangesOfRow(el, items, inner, table, this);
|
|
673
|
+
if (rowChanges.length > 0)
|
|
674
|
+
into.push(...rowChanges);
|
|
675
|
+
else {
|
|
676
|
+
into.push(BREAK);
|
|
677
|
+
for (const change of inner)
|
|
678
|
+
into.push(change, BREAK);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
else if (tn === ALTERNATE_CONTENT) {
|
|
682
|
+
alternateTokens(v, (branch, tokens) => visitRows(branch, table, tokens), into, listing);
|
|
683
|
+
}
|
|
684
|
+
else if (tn !== 'org_docx4j_wml.Tc') {
|
|
685
|
+
const children = childrenOf(v);
|
|
686
|
+
if (children)
|
|
687
|
+
visitRows(children, table, into);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
// a row's cells, through cell-level controls and custom XML and a cell-level mc:AlternateContent
|
|
692
|
+
const visitCells = (items, row, tokens) => {
|
|
693
|
+
var _a;
|
|
694
|
+
for (const el of items) {
|
|
695
|
+
const tn = typeNameOf(el);
|
|
696
|
+
const v = el.value;
|
|
697
|
+
if (typeof v !== 'object' || v === null)
|
|
698
|
+
continue;
|
|
699
|
+
if (tn === 'org_docx4j_wml.Tc') {
|
|
700
|
+
const cellChange = cellChangeOf(el, items, row.value, this);
|
|
701
|
+
if (cellChange)
|
|
702
|
+
tokens.push(cellChange, BREAK);
|
|
703
|
+
visit((_a = childrenOf(v)) !== null && _a !== void 0 ? _a : [], tokens);
|
|
704
|
+
tokens.push(BREAK);
|
|
705
|
+
}
|
|
706
|
+
else if (tn === ALTERNATE_CONTENT) {
|
|
707
|
+
alternateTokens(v, (branch, into) => visitCells(branch, row, into), tokens, listing);
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
const children = childrenOf(v);
|
|
711
|
+
if (children)
|
|
712
|
+
visitCells(children, row, tokens);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
};
|
|
467
716
|
visit(this.content, out);
|
|
468
717
|
// the body's last section, after everything in it
|
|
469
|
-
const section = bodySectionChangeOf(this.container);
|
|
718
|
+
const section = bodySectionChangeOf(this.container, this);
|
|
470
719
|
if (section)
|
|
471
720
|
out.push(BREAK, section);
|
|
721
|
+
// resolve-all's: every other branch's changes, each kept apart, after the story's (resolved first, last first)
|
|
722
|
+
if (listing.extra.length > 0)
|
|
723
|
+
out.push(BREAK, ...listing.extra);
|
|
472
724
|
return groupTouching(out);
|
|
473
725
|
}
|
|
474
726
|
/**
|
|
475
727
|
* Accepts every tracked change, last first so that a paragraph join never disturbs one still
|
|
476
728
|
* to do (docx4j `AcceptTrackedChanges` is the reference). Returns how many were listed.
|
|
729
|
+
*
|
|
730
|
+
* With a filter (an extension, CR-011), only the single changes it matches are accepted, so a group
|
|
731
|
+
* can be resolved in part, and what no change owns is left alone; the count is of the listed changes
|
|
732
|
+
* with a matching piece (docx4j's `TrackedChanges.acceptAll(pkg, filter)`).
|
|
477
733
|
*/
|
|
478
|
-
acceptAll() {
|
|
479
|
-
return this.resolveAll('accept');
|
|
734
|
+
acceptAll(filter) {
|
|
735
|
+
return this.resolveAll('accept', filter);
|
|
480
736
|
}
|
|
481
|
-
/** Rejects every tracked change, last first. Returns how many were listed. */
|
|
482
|
-
rejectAll() {
|
|
483
|
-
return this.resolveAll('reject');
|
|
737
|
+
/** Rejects every tracked change, last first, as `acceptAll` accepts them. Returns how many were listed. */
|
|
738
|
+
rejectAll(filter) {
|
|
739
|
+
return this.resolveAll('reject', filter);
|
|
484
740
|
}
|
|
485
741
|
/**
|
|
486
742
|
* Resolving one change can leave another to be listed afresh: accepting a move carries a deletion
|
|
487
743
|
* nested in its moved text out of it, and resolving a broken move dissolves the rest of it into a
|
|
488
744
|
* new `w:ins` or `w:del` (CR-002 section 29), so the list read first goes stale. The list is read
|
|
489
|
-
* again after each pass until nothing is left, as "all" promises (found by the editor's C5);
|
|
490
|
-
*
|
|
745
|
+
* again after each pass until nothing is left, as "all" promises (found by the editor's C5); as
|
|
746
|
+
* docx4j's `TrackedChanges.resolveAll` does it (CR-011): a change whose pieces all match is resolved
|
|
747
|
+
* whole, one with some matching its matching pieces alone, for up to 20 passes, and two passes in a
|
|
748
|
+
* row that leave as many matching changes as the one before end it. Then, with no filter, what is
|
|
749
|
+
* never listed is swept.
|
|
491
750
|
*/
|
|
492
|
-
resolveAll(what) {
|
|
751
|
+
resolveAll(what, filter) {
|
|
752
|
+
const matches = filter === undefined ? undefined : pieceMatcher(filter);
|
|
753
|
+
const matching = (change) => (matches ? change.pieces.filter(matches) : change.pieces);
|
|
493
754
|
let count = 0;
|
|
494
755
|
// one resolution for the lot, so the bookmarks are read before the first and after the last
|
|
495
756
|
resolving(this.package_, this, () => {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
757
|
+
// counted as listed; resolved over every branch of a kept mc:AlternateContent, each other branch's
|
|
758
|
+
// changes on their own, as docx4j's resolveAll lists them (CR-013 phase 3)
|
|
759
|
+
count = this.getTrackedChanges().filter((change) => matching(change).length > 0).length;
|
|
760
|
+
let left = Infinity;
|
|
761
|
+
let stalled = 0;
|
|
762
|
+
for (let pass = 0; pass < 20 && stalled < 2; pass++) {
|
|
763
|
+
const changes = this.trackedChanges({ mode: 'every', extra: [] });
|
|
764
|
+
let matched = 0;
|
|
765
|
+
for (let i = changes.length - 1; i >= 0; i--) {
|
|
766
|
+
const change = changes[i];
|
|
767
|
+
const pieces = matching(change);
|
|
768
|
+
if (pieces.length === 0)
|
|
769
|
+
continue;
|
|
770
|
+
matched++;
|
|
771
|
+
if (pieces.length === change.pieces.length)
|
|
772
|
+
change[what]();
|
|
773
|
+
else
|
|
774
|
+
resolvePieces(pieces, what);
|
|
775
|
+
}
|
|
776
|
+
if (matched === 0)
|
|
503
777
|
break;
|
|
504
|
-
|
|
778
|
+
stalled = matched >= left ? stalled + 1 : 0;
|
|
779
|
+
left = matched;
|
|
505
780
|
}
|
|
506
781
|
// and what is never listed: a cell merge, a numbering change, records that change nothing
|
|
507
|
-
|
|
782
|
+
if (!matches)
|
|
783
|
+
resolveUnlisted(this.content, what);
|
|
508
784
|
});
|
|
509
785
|
return count;
|
|
510
786
|
}
|
|
511
|
-
/** Every `w:tr` in this body's tables, in document order (rows of nested tables included). */
|
|
787
|
+
/** Every `w:tr` in this body's tables, in document order (rows of nested tables included), in every branch (CR-013 phase 3). */
|
|
512
788
|
rowElements() {
|
|
513
789
|
const out = [];
|
|
514
790
|
const visit = (items) => {
|
|
@@ -518,13 +794,18 @@ export class Body {
|
|
|
518
794
|
if (typeof v !== 'object' || v === null)
|
|
519
795
|
continue;
|
|
520
796
|
if (typeNameOf(el) === 'org_docx4j_wml.Tbl') {
|
|
521
|
-
for (const row of rowsOf(v)) {
|
|
797
|
+
for (const row of rowsOf(v, 'every')) {
|
|
522
798
|
out.push(row.element);
|
|
523
|
-
for (const cell of cellsOf(row.element.value))
|
|
799
|
+
for (const cell of cellsOf(row.element.value, 'every'))
|
|
524
800
|
visit((_a = childrenOf(cell.element.value)) !== null && _a !== void 0 ? _a : []);
|
|
525
801
|
}
|
|
526
802
|
continue;
|
|
527
803
|
}
|
|
804
|
+
if (typeNameOf(el) === ALTERNATE_CONTENT) {
|
|
805
|
+
for (const branch of branchesOf(v))
|
|
806
|
+
visit(branch);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
528
809
|
const children = childrenOf(v);
|
|
529
810
|
if (children)
|
|
530
811
|
visit(children);
|
|
@@ -540,7 +821,11 @@ export class Body {
|
|
|
540
821
|
return this.part.getXml();
|
|
541
822
|
}
|
|
542
823
|
// --- addresses (CR-002 section 3.3) ---
|
|
543
|
-
/**
|
|
824
|
+
/**
|
|
825
|
+
* The element at an address: 'body/3' (fourth block), 'body/3/1/0/2' (table row 1, cell 0, block 2),
|
|
826
|
+
* 'body/3/textbox:0/1' (the second block of the first text box body/3 anchors, CR-012 D4), a paraId, or a
|
|
827
|
+
* text match. Undefined, never an error, where the address names nothing.
|
|
828
|
+
*/
|
|
544
829
|
elementAt(address) {
|
|
545
830
|
if (typeof address === 'string' && address.startsWith('w14:'))
|
|
546
831
|
address = { paraId: address.substring(4) };
|
|
@@ -560,9 +845,22 @@ export class Body {
|
|
|
560
845
|
let container;
|
|
561
846
|
let element;
|
|
562
847
|
for (const seg of tail === '' ? [] : tail.substring(1).split('/')) {
|
|
563
|
-
|
|
848
|
+
if (seg.startsWith('textbox:')) {
|
|
849
|
+
// the n-th text box the paragraph here anchors, as the text model numbers them (CR-012 D4)
|
|
850
|
+
if (typeNameOf(element !== null && element !== void 0 ? element : {}) !== 'org_docx4j_wml.P')
|
|
851
|
+
return undefined;
|
|
852
|
+
const n = Number(seg.substring('textbox:'.length));
|
|
853
|
+
const box = Number.isInteger(n) && n >= 0 ? textBoxesOf(current, this.hiddenRuns(current))[n] : undefined;
|
|
854
|
+
if (!(box === null || box === void 0 ? void 0 : box.content))
|
|
855
|
+
return undefined; // none, or legacy WordArt: no paragraphs
|
|
856
|
+
current = box.content;
|
|
857
|
+
element = undefined;
|
|
858
|
+
container = undefined;
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
container = childrenOf(current, 'read');
|
|
564
862
|
const i = Number(seg);
|
|
565
|
-
if (!container || !Number.isInteger(i) || i < 0 || i >= container.length)
|
|
863
|
+
if (!container || seg === '' || !Number.isInteger(i) || i < 0 || i >= container.length)
|
|
566
864
|
return undefined;
|
|
567
865
|
element = container[i];
|
|
568
866
|
current = element.value;
|
|
@@ -580,38 +878,38 @@ export class Body {
|
|
|
580
878
|
const path = this.pathOf(target.element.value);
|
|
581
879
|
return path ? [this.prefix, ...path].join('/') : undefined;
|
|
582
880
|
}
|
|
583
|
-
/**
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
return
|
|
881
|
+
/**
|
|
882
|
+
* What an agent asks for first: every paragraph and table with its address, style and text, read by the
|
|
883
|
+
* text model (CR-012 D3; `Texts.of`'s options, `TextOptionsInit`): each paragraph's text is the model's
|
|
884
|
+
* (hidden text left out, a paragraph whose mark the view deletes joined to the next, in the view asked
|
|
885
|
+
* for), and each text box's paragraphs follow their anchoring paragraph, or the table it is in (REQ-518),
|
|
886
|
+
* at `textbox:` addresses (`body/3/textbox:0/1`), flagged `textbox`. A cell's paragraphs and tables follow
|
|
887
|
+
* their table's entry.
|
|
888
|
+
* Async since phase 4, as the model's reading is; calls under one `TextContext` (`Texts.contextOf`) read
|
|
889
|
+
* the package's stories once. The package is not changed (REQ-204).
|
|
890
|
+
*
|
|
891
|
+
* With `select`, only some of this body's top-level blocks are read (CR-012 section 17.13): their entries
|
|
892
|
+
* are those the whole outline gives them under the same context and options, in document order. A
|
|
893
|
+
* paragraph whose text a paragraph before it holds (`joined`) gives none; a block that starts inside a
|
|
894
|
+
* field open before it (a table of contents left out, D12) is read inside it. Under a context, the first
|
|
895
|
+
* such call over a body walks its blocks' field characters once; each block after that costs its own
|
|
896
|
+
* reading. A context is a snapshot: after an edit, make another.
|
|
897
|
+
*/
|
|
898
|
+
async outline(options, context, select) {
|
|
899
|
+
return outlineReader()(this, options, context, select === null || select === void 0 ? void 0 : select.blocks.map((b) => this.blockIndexOf(b)));
|
|
900
|
+
}
|
|
901
|
+
/** The index in `content` of one of this body's top-level blocks. */
|
|
902
|
+
blockIndexOf(block) {
|
|
903
|
+
const content = this.content;
|
|
904
|
+
if (typeof block === 'number') {
|
|
905
|
+
if (!Number.isInteger(block) || block < 0 || block >= content.length)
|
|
906
|
+
throw new Docx4JException(`${this.prefix} has no block ${block}`);
|
|
907
|
+
return block;
|
|
908
|
+
}
|
|
909
|
+
const i = block.container === content ? content.indexOf(block.element) : -1;
|
|
910
|
+
if (i < 0)
|
|
911
|
+
throw new Docx4JException(`Not one of ${this.prefix}'s top-level blocks: ${describe(block.element)}`);
|
|
912
|
+
return i;
|
|
615
913
|
}
|
|
616
914
|
// --- docx4j names ---
|
|
617
915
|
/** docx4j MainDocumentPart.addParagraphOfText. */
|
|
@@ -707,7 +1005,7 @@ export class Body {
|
|
|
707
1005
|
return found;
|
|
708
1006
|
}
|
|
709
1007
|
pathOf(value) {
|
|
710
|
-
var _a;
|
|
1008
|
+
var _a, _b;
|
|
711
1009
|
const path = [];
|
|
712
1010
|
let child = value;
|
|
713
1011
|
for (let guard = 0; guard < 64; guard++) {
|
|
@@ -728,25 +1026,46 @@ export class Body {
|
|
|
728
1026
|
path.unshift(i);
|
|
729
1027
|
if (parent === this.container)
|
|
730
1028
|
return path;
|
|
1029
|
+
if (BRANCH_TYPES.has((_b = parent.TYPE_NAME) !== null && _b !== void 0 ? _b : '')) {
|
|
1030
|
+
// a kept mc:AlternateContent's branch is no level of its own (D2): what is in the selected one is
|
|
1031
|
+
// addressed from the element, body/2/0; another branch's is not addressed, readers not seeing it
|
|
1032
|
+
const ac = parent.PARENT;
|
|
1033
|
+
if (!ac || selectedBranchOf(ac) !== children)
|
|
1034
|
+
return undefined;
|
|
1035
|
+
child = ac;
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if (parent.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent') {
|
|
1039
|
+
// a text box's block: addressed from the paragraph that anchors it, `textbox:n` its place among
|
|
1040
|
+
// that paragraph's boxes; none for a box the text model doesn't number (another branch's, a hidden run's)
|
|
1041
|
+
const anchor = anchorOf(parent);
|
|
1042
|
+
const n = anchor ? textBoxesOf(anchor, this.hiddenRuns(anchor)).findIndex((box) => box.content === parent) : -1;
|
|
1043
|
+
if (!anchor || n < 0)
|
|
1044
|
+
return undefined;
|
|
1045
|
+
path.unshift(`textbox:${n}`);
|
|
1046
|
+
child = anchor;
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
731
1049
|
child = parent;
|
|
732
1050
|
}
|
|
733
1051
|
return undefined;
|
|
734
1052
|
}
|
|
735
|
-
/**
|
|
1053
|
+
/**
|
|
1054
|
+
* Gives a new paragraph a w14:paraId when the document already uses them, free in every branch (CR-013 phase 3).
|
|
1055
|
+
* @internal a paragraph split (CR-014 phase 2) gives its new first half one.
|
|
1056
|
+
*/
|
|
736
1057
|
assignParaId(p) {
|
|
737
1058
|
if (p.paraId)
|
|
738
1059
|
return;
|
|
739
|
-
|
|
1060
|
+
const paragraphs = this.paragraphsIn('every');
|
|
1061
|
+
if (!paragraphs.some((q) => q.paraId !== undefined))
|
|
740
1062
|
return;
|
|
741
1063
|
let id;
|
|
742
1064
|
do {
|
|
743
1065
|
id = Math.floor(Math.random() * 0x7fffffff).toString(16).toUpperCase().padStart(8, '0');
|
|
744
|
-
} while (
|
|
1066
|
+
} while (paragraphs.some((q) => q.paraId === id));
|
|
745
1067
|
p.paraId = id;
|
|
746
1068
|
}
|
|
747
|
-
usesParaIds() {
|
|
748
|
-
return this.paragraphs.some((q) => q.paraId !== undefined);
|
|
749
|
-
}
|
|
750
1069
|
// --- comments (CR-002 phase G) ---
|
|
751
1070
|
/**
|
|
752
1071
|
* The comments anchored in this body, in document order, with replies nested under their
|
|
@@ -763,6 +1082,7 @@ export class Body {
|
|
|
763
1082
|
* body. Returns the control.
|
|
764
1083
|
*/
|
|
765
1084
|
insertContentControl(kind) {
|
|
1085
|
+
this.assertWritable();
|
|
766
1086
|
const content = [...this.content];
|
|
767
1087
|
const sdt = sdtOf(content, { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this)), form: 'block' });
|
|
768
1088
|
this.content.length = 0;
|
|
@@ -791,33 +1111,60 @@ function describe(el) {
|
|
|
791
1111
|
return `${(_b = (_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '?'} (${tn !== null && tn !== void 0 ? tn : 'no TYPE_NAME'})`;
|
|
792
1112
|
}
|
|
793
1113
|
/**
|
|
794
|
-
* A story's tracked changes resolved (`Body.acceptAll` or `rejectAll
|
|
795
|
-
* it - a `w:txbxContent`, in a DrawingML shape or a VML one - which Word
|
|
796
|
-
* (CR-002 section 49). A text box inside a text box is reached through its
|
|
797
|
-
* changes were listed.
|
|
1114
|
+
* A story's tracked changes resolved (`Body.acceptAll` or `rejectAll`, with the filter if one is given),
|
|
1115
|
+
* then those of each text box in it - a `w:txbxContent`, in a DrawingML shape or a VML one - which Word
|
|
1116
|
+
* counts as a story of its own (CR-002 section 49). A text box inside a text box is reached through its
|
|
1117
|
+
* own story. Returns how many changes were listed (with a filter, how many had a matching piece).
|
|
798
1118
|
*
|
|
799
1119
|
* In a kept `mc:AlternateContent` the text box of every branch is resolved, so that each says the same
|
|
800
1120
|
* afterwards (docx4j's `McMode.ALL`), and the changes are counted in the selected branch alone, so a
|
|
801
1121
|
* text box held in both counts once, as a resolved load's does (CR-010 phase 3).
|
|
802
1122
|
*/
|
|
803
|
-
export function resolveStory(body, what) {
|
|
804
|
-
let count = what === 'accept' ? body.acceptAll() : body.rejectAll();
|
|
1123
|
+
export function resolveStory(body, what, filter) {
|
|
1124
|
+
let count = what === 'accept' ? body.acceptAll(filter) : body.rejectAll(filter);
|
|
805
1125
|
for (const { box, selected } of textBoxesIn(body.container)) {
|
|
806
|
-
const resolved = resolveStory(body.sub(box), what);
|
|
1126
|
+
const resolved = resolveStory(body.sub(box), what, filter);
|
|
807
1127
|
if (selected)
|
|
808
1128
|
count += resolved;
|
|
809
1129
|
}
|
|
810
1130
|
return count;
|
|
811
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* A story's tracked changes (`Body.getTrackedChanges`), then each text box's in it, as `resolveStory`
|
|
1134
|
+
* resolves them: docx4j's order (its CR-043 `Lister`, CR-011). In a kept `mc:AlternateContent` the
|
|
1135
|
+
* selected branch's text boxes alone are listed, so a text box held in both branches is listed once;
|
|
1136
|
+
* each other branch's box at the same place is its twin, whose changes are paired with the listed ones
|
|
1137
|
+
* by kind and position and resolved with them, as docx4j resolves a text box's `mc:Fallback` twin
|
|
1138
|
+
* (`pairTwins`; CR-013 phase 3).
|
|
1139
|
+
*/
|
|
1140
|
+
export function trackedChangesOfStory(body) {
|
|
1141
|
+
const out = body.getTrackedChanges();
|
|
1142
|
+
for (const { box, selected, twins } of textBoxesIn(body.container)) {
|
|
1143
|
+
if (!selected)
|
|
1144
|
+
continue;
|
|
1145
|
+
const changes = trackedChangesOfStory(body.sub(box));
|
|
1146
|
+
for (const twin of twins !== null && twins !== void 0 ? twins : [])
|
|
1147
|
+
pairTwins(changes, trackedChangesOfStory(body.sub(twin)));
|
|
1148
|
+
out.push(...changes);
|
|
1149
|
+
}
|
|
1150
|
+
return out;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* The members `textBoxesIn` does not descend into: an element's name, and properties, which hold no text box.
|
|
1154
|
+
* Over every fixture the walk finds the same text boxes without them and visits 38% fewer objects (CR-012
|
|
1155
|
+
* section 17.13: it was most of a plan's cost).
|
|
1156
|
+
*/
|
|
1157
|
+
const NO_TEXT_BOXES = new Set(['PARENT', 'name', 'pPr', 'rPr', 'tblPr', 'tblGrid', 'trPr', 'tcPr', 'sectPr', 'sdtPr', 'sdtEndPr']);
|
|
812
1158
|
/**
|
|
813
1159
|
* The `w:txbxContent` of every text box in a story's content, not descending into one found, each with
|
|
814
1160
|
* whether it is in a branch a reader takes: false inside a branch of a kept `mc:AlternateContent` other
|
|
815
|
-
* than its selected one (`selectedBranchOf`).
|
|
1161
|
+
* than its selected one (`selectedBranchOf`). A selected box in a branch has as twins the boxes at its
|
|
1162
|
+
* place, by position, in the element's other branches (docx4j's `Lister.twins`).
|
|
816
1163
|
*/
|
|
817
1164
|
function textBoxesIn(container) {
|
|
818
1165
|
const out = [];
|
|
819
1166
|
const seen = new Set();
|
|
820
|
-
const visit = (o, selected) => {
|
|
1167
|
+
const visit = (o, selected, into) => {
|
|
821
1168
|
var _a;
|
|
822
1169
|
if (typeof o !== 'object' || o === null || seen.has(o))
|
|
823
1170
|
return;
|
|
@@ -826,25 +1173,46 @@ function textBoxesIn(container) {
|
|
|
826
1173
|
seen.add(o);
|
|
827
1174
|
if (Array.isArray(o)) {
|
|
828
1175
|
for (const item of o)
|
|
829
|
-
visit(item, selected);
|
|
1176
|
+
visit(item, selected, into);
|
|
830
1177
|
return;
|
|
831
1178
|
}
|
|
832
1179
|
const typeName = o.TYPE_NAME;
|
|
833
1180
|
if (typeName === 'org_docx4j_wml.CTTxbxContent') {
|
|
834
|
-
|
|
1181
|
+
into.push({ box: o, selected });
|
|
835
1182
|
return;
|
|
836
1183
|
}
|
|
837
1184
|
if (typeName === ALTERNATE_CONTENT) {
|
|
838
1185
|
const ac = o;
|
|
839
1186
|
const read = selectedBranchOf(ac);
|
|
840
|
-
|
|
841
|
-
|
|
1187
|
+
let primary = [];
|
|
1188
|
+
const others = [];
|
|
1189
|
+
for (const branch of [...((_a = ac.choice) !== null && _a !== void 0 ? _a : []), ...(ac.fallback ? [ac.fallback] : [])]) {
|
|
1190
|
+
const found = [];
|
|
1191
|
+
visit(branch.any, selected && branch.any === read, found);
|
|
1192
|
+
into.push(...found);
|
|
1193
|
+
if (branch.any === read)
|
|
1194
|
+
primary = found;
|
|
1195
|
+
else
|
|
1196
|
+
others.push(found);
|
|
1197
|
+
}
|
|
1198
|
+
for (const found of others)
|
|
1199
|
+
found.forEach(({ box }, i) => { var _a; const twin = primary[i]; if (twin)
|
|
1200
|
+
((_a = twin.twins) !== null && _a !== void 0 ? _a : (twin.twins = [])).push(box); });
|
|
842
1201
|
return;
|
|
843
1202
|
}
|
|
844
1203
|
for (const key in o)
|
|
845
|
-
if (key
|
|
846
|
-
visit(o[key], selected);
|
|
1204
|
+
if (!NO_TEXT_BOXES.has(key))
|
|
1205
|
+
visit(o[key], selected, into);
|
|
847
1206
|
};
|
|
848
|
-
visit(container.content, true);
|
|
1207
|
+
visit(container.content, true, out);
|
|
849
1208
|
return out;
|
|
850
1209
|
}
|
|
1210
|
+
/** The paragraph a text box's content is anchored in: its nearest `w:p` ancestor. */
|
|
1211
|
+
function anchorOf(box) {
|
|
1212
|
+
let o = box.PARENT;
|
|
1213
|
+
for (let guard = 0; o && guard < 64; guard++, o = o.PARENT) {
|
|
1214
|
+
if (o.TYPE_NAME === 'org_docx4j_wml.P')
|
|
1215
|
+
return o;
|
|
1216
|
+
}
|
|
1217
|
+
return undefined;
|
|
1218
|
+
}
|