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