@docx4j/core-ts 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -0
- package/README.md +34 -3
- package/dist/html/convert.mjs +40 -15
- 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 +153 -19
- package/dist/model/content/Body.mjs +570 -131
- package/dist/model/content/Comment.d.mts +21 -3
- package/dist/model/content/Comment.mjs +202 -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 +82 -7
- package/dist/model/content/InlinePicture.mjs +221 -28
- package/dist/model/content/List.mjs +13 -1
- package/dist/model/content/Paragraph.d.mts +213 -14
- package/dist/model/content/Paragraph.mjs +936 -100
- package/dist/model/content/Range.d.mts +242 -11
- package/dist/model/content/Range.mjs +975 -119
- 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 +301 -16
- package/dist/model/content/TrackedChange.mjs +1284 -158
- package/dist/model/content/binder.d.mts +2 -1
- package/dist/model/content/binder.mjs +48 -1
- 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/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -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 +212 -13
- package/dist/model/content/tree.mjs +356 -35
- 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 +9 -5
- package/dist/model/customxml/pictures.mjs +31 -13
- package/dist/model/customxml/xhtml.mjs +8 -2
- package/dist/model/fonts/families.generated.mjs +2 -2
- package/dist/model/fonts/registry.mjs +7 -1
- package/dist/model/fonts/substitutions.generated.mjs +2 -2
- package/dist/model/fonts/themes.generated.mjs +1 -1
- 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/listnumbering/definitions.mjs +18 -0
- package/dist/model/properties/PropertyResolver.d.mts +87 -7
- package/dist/model/properties/PropertyResolver.mjs +197 -17
- package/dist/model/properties/catalogue.d.mts +32 -4
- package/dist/model/properties/catalogue.mjs +115 -36
- package/dist/model/properties/log.d.mts +1 -1
- package/dist/model/properties/log.mjs +5 -5
- package/dist/model/properties/table.d.mts +16 -2
- package/dist/model/properties/table.mjs +17 -5
- 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/Load.d.mts +9 -2
- 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 +2 -2
- package/dist/opc/mce/preprocessor.d.mts +88 -7
- package/dist/opc/mce/preprocessor.mjs +144 -37
- package/dist/opc/mce/understood.d.mts +34 -0
- package/dist/opc/mce/understood.mjs +55 -3
- package/dist/packages/OpcPackage.d.mts +20 -0
- package/dist/packages/OpcPackage.mjs +16 -3
- package/dist/packages/WordprocessingMLPackage.d.mts +77 -7
- package/dist/packages/WordprocessingMLPackage.mjs +242 -11
- package/dist/parts/DefaultXmlPart.mjs +2 -2
- package/dist/parts/Part.d.mts +10 -2
- package/dist/parts/XmlPart.d.mts +35 -32
- package/dist/parts/XmlPart.mjs +107 -107
- 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,25 +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';
|
|
6
|
+
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
5
7
|
function withRPr(run, rPr) {
|
|
6
8
|
if (rPr)
|
|
7
9
|
run.value.rPr = rPr;
|
|
8
10
|
return run;
|
|
9
11
|
}
|
|
10
|
-
import { Font } from './Font.mjs';
|
|
12
|
+
import { Font, markHolderOf } from './Font.mjs';
|
|
11
13
|
import { runFontSelectorOf } from '../fonts/lookup.mjs';
|
|
12
14
|
import { Range } from './Range.mjs';
|
|
13
|
-
import {
|
|
15
|
+
import { officeMatches, expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
|
|
14
16
|
import { builtInOf, idOfBuiltIn, styleNameOf, styleIdOf } from './styles.mjs';
|
|
15
17
|
import { cellOf } from './Table.mjs';
|
|
16
|
-
import { ContentControl, collectRunControls } from './ContentControl.mjs';
|
|
17
|
-
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';
|
|
18
23
|
import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
|
|
19
|
-
import { InlinePicture, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
24
|
+
import { InlinePicture, picturesIn, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
25
|
+
import { showPicturePlaceholder } from '../customxml/picturePlaceholder.mjs';
|
|
20
26
|
import { contentOf } from './ooxml.mjs';
|
|
21
27
|
import { commentApi } from './comments.mjs';
|
|
22
|
-
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';
|
|
23
29
|
import { trackedChangesOfParagraph } from './TrackedChange.mjs';
|
|
24
30
|
import { isListItem, listOf, listOrNullObjectOf, listItemOf, listItemOrNullObjectOf, startNewList, attachToList, detachFromList, } from './List.mjs';
|
|
25
31
|
const TWIPS_PER_POINT = 20;
|
|
@@ -61,22 +67,57 @@ export class Paragraph {
|
|
|
61
67
|
get p() {
|
|
62
68
|
return this.element.value;
|
|
63
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
|
+
}
|
|
64
88
|
get index() {
|
|
65
89
|
return this.container.indexOf(this.element);
|
|
66
90
|
}
|
|
67
|
-
/**
|
|
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
|
+
*/
|
|
68
98
|
get text() {
|
|
69
|
-
return
|
|
99
|
+
return this.segments().map((s) => s.text).join('');
|
|
70
100
|
}
|
|
71
101
|
/** Replaces the content with one run of the text, keeping the first run's formatting. */
|
|
72
102
|
set text(value) {
|
|
73
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
|
+
}
|
|
74
114
|
if (this.changeTracker) {
|
|
75
|
-
this.splice(0, this.text.length, value);
|
|
115
|
+
this.splice(0, this.text.length, value, 'before', WHOLE, mode);
|
|
76
116
|
return;
|
|
77
117
|
}
|
|
78
118
|
const rPr = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value.rPr;
|
|
79
|
-
this.p.content
|
|
119
|
+
this.releaseRelationships(this.p.content);
|
|
120
|
+
this.p.content = [oneParagraphRun(value, rPr ? deepCopy(rPr) : undefined, mode)];
|
|
80
121
|
linkParents(this.p.content, this.p);
|
|
81
122
|
}
|
|
82
123
|
/**
|
|
@@ -84,7 +125,7 @@ export class Paragraph {
|
|
|
84
125
|
* reads the document as it was before the tracked changes: `w:del` included, `w:ins` excluded.
|
|
85
126
|
*/
|
|
86
127
|
getText(options) {
|
|
87
|
-
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;
|
|
88
129
|
}
|
|
89
130
|
/** The style id (w:pStyle; docx4j's name for it); 'Normal' when none is set. Extension: Office JS has `style` and `styleBuiltIn` only. */
|
|
90
131
|
get styleId() {
|
|
@@ -225,24 +266,36 @@ export class Paragraph {
|
|
|
225
266
|
else
|
|
226
267
|
pPr.outlineLvl = { val: level - 1 }; this.settleFormatting(); }
|
|
227
268
|
/**
|
|
228
|
-
* The runs' formatting: reads the
|
|
229
|
-
*
|
|
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).
|
|
230
272
|
*/
|
|
231
273
|
get font() {
|
|
232
274
|
return this.getFont();
|
|
233
275
|
}
|
|
234
|
-
/** `font`, with `{ direct: true }` for the direct formatting
|
|
276
|
+
/** `font`, with `{ direct: true }` for the runs' direct formatting (extension). */
|
|
235
277
|
getFont(options) {
|
|
236
|
-
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);
|
|
237
289
|
if ((options === null || options === void 0 ? void 0 : options.direct) === true)
|
|
238
|
-
return new Font(holders, () => this.fontTracking());
|
|
239
|
-
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());
|
|
240
292
|
}
|
|
241
293
|
/** w14:paraId, the stable address Word gives paragraphs. */
|
|
242
294
|
get paraId() {
|
|
243
295
|
return this.p.paraId;
|
|
244
296
|
}
|
|
245
297
|
set paraId(id) {
|
|
298
|
+
this.assertWritable();
|
|
246
299
|
if (id === undefined)
|
|
247
300
|
delete this.p.paraId;
|
|
248
301
|
else
|
|
@@ -262,45 +315,82 @@ export class Paragraph {
|
|
|
262
315
|
collectRunControls(this.p, this.parentBody, out);
|
|
263
316
|
return out;
|
|
264
317
|
}
|
|
265
|
-
/**
|
|
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
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* The inline pictures in this paragraph, in order (Office JS Paragraph.inlinePictures): each
|
|
328
|
+
* `w:drawing` whose `wp:inline` holds a `pic:pic`, in a run or in the selected branch of a kept
|
|
329
|
+
* `mc:AlternateContent` (`picturesIn`). An inline chart, text box or group is not one (CR-010
|
|
330
|
+
* section 12.6).
|
|
331
|
+
*/
|
|
266
332
|
get inlinePictures() {
|
|
267
|
-
var _a
|
|
333
|
+
var _a;
|
|
268
334
|
const out = [];
|
|
269
335
|
for (const run of runsOf(this.p)) {
|
|
270
336
|
for (const item of ((_a = run.value.content) !== null && _a !== void 0 ? _a : [])) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const inline = (_b = item.value.anchorOrInline) === null || _b === void 0 ? void 0 : _b[0];
|
|
274
|
-
if (inline && inline.TYPE_NAME === 'org_docx4j_dml_wordprocessingDrawing.Inline') {
|
|
275
|
-
out.push(new InlinePicture(item, run, this));
|
|
276
|
-
}
|
|
337
|
+
for (const drawing of picturesIn(item))
|
|
338
|
+
out.push(new InlinePicture(drawing, run, this, drawing === item ? undefined : item));
|
|
277
339
|
}
|
|
278
340
|
}
|
|
279
341
|
return out;
|
|
280
342
|
}
|
|
281
343
|
/** Text inserted at the start, the end, or replacing the whole paragraph's text. */
|
|
282
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);
|
|
283
350
|
const length = this.text.length;
|
|
284
351
|
if (location === 'Start')
|
|
285
352
|
return this.splice(0, 0, text);
|
|
286
353
|
if (location === 'End')
|
|
287
|
-
return this.
|
|
288
|
-
|
|
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');
|
|
359
|
+
return this.splice(0, length, text, 'before', WHOLE);
|
|
289
360
|
}
|
|
290
|
-
/**
|
|
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
|
+
*/
|
|
291
366
|
insertParagraph(text, location) {
|
|
367
|
+
var _a, _b;
|
|
368
|
+
this.assertWritable();
|
|
292
369
|
const pPr = this.p.pPr ? deepCopy(this.p.pPr) : undefined;
|
|
293
370
|
if (pPr) {
|
|
294
371
|
delete pPr.rPr;
|
|
295
372
|
delete pPr.sectPr;
|
|
296
373
|
delete pPr.pPrChange;
|
|
297
374
|
}
|
|
298
|
-
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
|
+
}
|
|
299
383
|
return this.parentBody.insertElement(el, location, this);
|
|
300
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
|
+
}
|
|
301
390
|
insertBreak(type, location) {
|
|
302
391
|
var _a;
|
|
303
392
|
var _b;
|
|
393
|
+
this.assertWritable();
|
|
304
394
|
const brk = breakItem(type === 'Page' ? 'page' : undefined);
|
|
305
395
|
if (location === 'Before' || location === 'After') {
|
|
306
396
|
const p = this.insertParagraph('', location);
|
|
@@ -328,18 +418,29 @@ export class Paragraph {
|
|
|
328
418
|
insertInlinePictureFromBase64(base64, location, options) {
|
|
329
419
|
var _a;
|
|
330
420
|
var _b;
|
|
421
|
+
this.assertWritable();
|
|
331
422
|
const part = this.parentBody.part;
|
|
332
423
|
if (!part)
|
|
333
424
|
throw new Docx4JException('This body has no part, so an image cannot be related to it');
|
|
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');
|
|
334
429
|
const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
|
|
430
|
+
// under tracking the picture is an insertion, and what it replaces is deleted as insertText's is
|
|
431
|
+
if (location === 'Replace' && tracker)
|
|
432
|
+
this.splice(0, this.text.length, '', 'before', WHOLE);
|
|
335
433
|
const content = ((_a = (_b = this.p).content) !== null && _a !== void 0 ? _a : (_b.content = []));
|
|
336
|
-
if (location === 'Replace')
|
|
434
|
+
if (location === 'Replace' && !tracker) {
|
|
435
|
+
this.releaseRelationships(content);
|
|
337
436
|
content.length = 0;
|
|
437
|
+
}
|
|
438
|
+
const item = tracker ? tracker.ins([run]) : run;
|
|
338
439
|
if (location === 'Start')
|
|
339
|
-
content.unshift(
|
|
440
|
+
content.unshift(item);
|
|
340
441
|
else
|
|
341
|
-
content.push(
|
|
342
|
-
linkParents(
|
|
442
|
+
content.push(item);
|
|
443
|
+
linkParents(item, this.p);
|
|
343
444
|
return new InlinePicture(drawing, run, this);
|
|
344
445
|
}
|
|
345
446
|
/**
|
|
@@ -350,6 +451,7 @@ export class Paragraph {
|
|
|
350
451
|
*/
|
|
351
452
|
async insertOoxml(ooxml, location) {
|
|
352
453
|
var _a, _b;
|
|
454
|
+
this.assertWritable();
|
|
353
455
|
const preprocess = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
|
|
354
456
|
const elements = await contentOf(ooxml, { preprocess: preprocess ? (doc) => preprocess(doc) : undefined, target: this.parentBody.part });
|
|
355
457
|
if (elements.length === 0)
|
|
@@ -360,12 +462,12 @@ export class Paragraph {
|
|
|
360
462
|
return [this];
|
|
361
463
|
}
|
|
362
464
|
if (location === 'Replace') {
|
|
363
|
-
const inserted = this.parentBody.insertElement(elements, 'Before', this);
|
|
465
|
+
const inserted = this.parentBody.insertElement(elements, 'Before', this, 'GeneralException', this.element);
|
|
364
466
|
void inserted;
|
|
365
467
|
this.delete();
|
|
366
468
|
}
|
|
367
469
|
else {
|
|
368
|
-
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');
|
|
369
471
|
}
|
|
370
472
|
return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
|
|
371
473
|
? new Paragraph(el, this.container, this.parentBody)
|
|
@@ -373,6 +475,10 @@ export class Paragraph {
|
|
|
373
475
|
}
|
|
374
476
|
/** Matches within this paragraph. */
|
|
375
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);
|
|
376
482
|
return this.searchMatches(text, options).map((m) => m.range);
|
|
377
483
|
}
|
|
378
484
|
/**
|
|
@@ -381,8 +487,8 @@ export class Paragraph {
|
|
|
381
487
|
* and `Body.replaceText` the other.
|
|
382
488
|
*/
|
|
383
489
|
searchMatches(text, options) {
|
|
384
|
-
return
|
|
385
|
-
.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 }));
|
|
386
492
|
}
|
|
387
493
|
/**
|
|
388
494
|
* Replaces every match of `find` with `replace`, last match first so that the offsets of the
|
|
@@ -390,6 +496,9 @@ export class Paragraph {
|
|
|
390
496
|
* the package's `changeTrackingMode` is on.
|
|
391
497
|
*/
|
|
392
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));
|
|
393
502
|
const matches = this.searchMatches(find, options);
|
|
394
503
|
for (let i = matches.length - 1; i >= 0; i--) {
|
|
395
504
|
const { range, match } = matches[i];
|
|
@@ -401,44 +510,93 @@ export class Paragraph {
|
|
|
401
510
|
getTrackedChanges() {
|
|
402
511
|
return trackedChangesOfParagraph(this);
|
|
403
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* The paragraph's range: 'Whole' and 'Content' hold its whole content, the pictures at its start and
|
|
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.
|
|
518
|
+
*/
|
|
404
519
|
getRange(location = 'Whole') {
|
|
405
520
|
const length = this.text.length;
|
|
406
521
|
if (location === 'Start')
|
|
407
522
|
return new Range(this, 0, 0);
|
|
408
523
|
if (location === 'End')
|
|
409
|
-
return new Range(this, length, length);
|
|
410
|
-
|
|
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';
|
|
411
538
|
}
|
|
412
539
|
/**
|
|
413
540
|
* Removes the paragraph from its container. While the package tracks changes, the content
|
|
414
541
|
* becomes a `w:del` and the mark is marked deleted (`w:pPr/w:rPr/w:del`) instead, unless the
|
|
415
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).
|
|
416
546
|
*/
|
|
417
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);
|
|
418
558
|
const tracker = this.changeTracker;
|
|
419
559
|
if (tracker) {
|
|
420
|
-
this.trackedDelete(tracker);
|
|
560
|
+
this.trackedDelete(tracker, cellEnd);
|
|
421
561
|
return;
|
|
422
562
|
}
|
|
423
563
|
const i = this.index;
|
|
424
|
-
if (i
|
|
425
|
-
|
|
564
|
+
if (i < 0)
|
|
565
|
+
return;
|
|
566
|
+
this.container.splice(i, 1);
|
|
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);
|
|
426
577
|
}
|
|
427
|
-
trackedDelete(tracker) {
|
|
578
|
+
trackedDelete(tracker, cellEnd) {
|
|
428
579
|
var _a, _b;
|
|
429
580
|
if (markDeleted(this.p))
|
|
430
581
|
throw new Docx4JException('This paragraph is already marked deleted');
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
582
|
+
// the whole content, its pictures too: before, a picture's run, having no text, stayed out of the
|
|
583
|
+
// w:del, and accepting joined it into the next paragraph (CR-002 section 48)
|
|
584
|
+
this.deleteText(tracker, 0, this.text.length, WHOLE);
|
|
434
585
|
const rPr = (_a = this.p.pPr) === null || _a === void 0 ? void 0 : _a.rPr;
|
|
435
586
|
const ownMark = (rPr === null || rPr === void 0 ? void 0 : rPr.ins) !== undefined && rPr.ins.author === tracker.author;
|
|
436
587
|
if (ownMark && ((_b = this.p.content) !== null && _b !== void 0 ? _b : []).length === 0) {
|
|
437
588
|
const i = this.index;
|
|
438
|
-
if (i >= 0)
|
|
589
|
+
if (i >= 0) {
|
|
439
590
|
this.container.splice(i, 1);
|
|
591
|
+
if (!cellEnd || this.cellStillEnds())
|
|
592
|
+
return;
|
|
593
|
+
this.container.splice(i, 0, this.element);
|
|
594
|
+
}
|
|
440
595
|
return;
|
|
441
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;
|
|
442
600
|
tracker.markParagraphDeleted(this.p);
|
|
443
601
|
// Word, deleting a paragraph mark ALONE under tracking, gives the next paragraph this one's
|
|
444
602
|
// properties as a w:pPrChange (CR-002 section 38 item 2, the editor's measure-word15.docx), so
|
|
@@ -453,15 +611,27 @@ export class Paragraph {
|
|
|
453
611
|
* what the cells of a deleted table row need (a `w:tc` must keep a block-level child, and Word
|
|
454
612
|
* writes `w:del` around every run and `w:pPr/w:rPr/w:del` on every mark inside a deleted row).
|
|
455
613
|
* The same primitives `delete()` uses, without its "take back my own insertion" step.
|
|
456
|
-
*
|
|
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).
|
|
457
616
|
*/
|
|
458
617
|
markDeletedInPlace(tracker) {
|
|
459
|
-
|
|
618
|
+
this.assertWritable();
|
|
619
|
+
this.deleteText(tracker, 0, this.text.length, WHOLE);
|
|
620
|
+
if (!markDeleted(this.p))
|
|
621
|
+
tracker.markParagraphDeleted(this.p);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Queues the relationships that content removed outright referenced (a picture's image, a link's
|
|
625
|
+
* target), to be settled when the package is saved: each goes then if nothing references it, and
|
|
626
|
+
* its part with it when nothing else targets that (CR-002 sections 47 and 48).
|
|
627
|
+
*/
|
|
628
|
+
releaseRelationships(removed) {
|
|
629
|
+
const ids = new Set();
|
|
630
|
+
relationshipIdsIn(removed, ids);
|
|
631
|
+
if (ids.size === 0)
|
|
460
632
|
return;
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
this.deleteText(tracker, 0, length);
|
|
464
|
-
tracker.markParagraphDeleted(this.p);
|
|
633
|
+
const pkg = this.parentBody.package_;
|
|
634
|
+
queueRelationshipRemovals(pkg === null || pkg === void 0 ? void 0 : pkg.pendingRelationshipRemovals, this.parentBody.part, ids);
|
|
465
635
|
}
|
|
466
636
|
/** The paragraph as XML (extension; Office JS getOoxml wraps it in a package). */
|
|
467
637
|
getXml() {
|
|
@@ -485,6 +655,7 @@ export class Paragraph {
|
|
|
485
655
|
pPr() {
|
|
486
656
|
var _a, _b;
|
|
487
657
|
var _c;
|
|
658
|
+
this.assertWritable();
|
|
488
659
|
const pPr = ((_a = (_c = this.p).pPr) !== null && _a !== void 0 ? _a : (_c.pPr = { TYPE_NAME: 'org_docx4j_wml.PPr' }));
|
|
489
660
|
linkParents(pPr, this.p);
|
|
490
661
|
(_b = this.changeTracker) === null || _b === void 0 ? void 0 : _b.recordPPrChange(pPr);
|
|
@@ -563,40 +734,387 @@ export class Paragraph {
|
|
|
563
734
|
ownInsertions.add(seg.run);
|
|
564
735
|
return { tracker, ownInsertions };
|
|
565
736
|
}
|
|
566
|
-
/**
|
|
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
|
+
*/
|
|
567
741
|
segments(options) {
|
|
568
|
-
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);
|
|
569
780
|
}
|
|
570
781
|
/**
|
|
571
782
|
* Replaces the text in [start, end) with `text` and returns the range of the inserted text.
|
|
572
783
|
* Runs are edited in place: a w:t loses or gains characters, a tab or break inside the range
|
|
573
784
|
* is removed, a run left empty is removed. Text is inserted into the w:t at `start`,
|
|
574
785
|
* else into a new run with the formatting of the nearest run.
|
|
786
|
+
*
|
|
787
|
+
* The pictures in the span go with its text: those strictly inside it, and those at an edge `edges`
|
|
788
|
+
* says the span holds (`WHOLE` for a paragraph's whole content), so a span of no text can still
|
|
789
|
+
* delete a picture (CR-002 section 48).
|
|
790
|
+
*/
|
|
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).
|
|
575
1078
|
*/
|
|
576
|
-
|
|
577
|
-
var _a
|
|
578
|
-
|
|
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;
|
|
579
1088
|
const length = this.text.length;
|
|
580
1089
|
start = Math.max(0, Math.min(start, length));
|
|
581
1090
|
end = Math.max(start, Math.min(end, length));
|
|
582
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();
|
|
583
1098
|
if (tracker) {
|
|
584
1099
|
// a replacement is the w:del first and the w:ins after it, as Word writes one
|
|
585
|
-
const deletions = end > start ? this.deleteText(tracker, start, end) : [];
|
|
1100
|
+
const deletions = end > start || edges ? this.deleteText(tracker, start, end, edges) : [];
|
|
586
1101
|
if (text.length > 0)
|
|
587
1102
|
this.insertTracked(tracker, start, text, deletions[deletions.length - 1], prefer);
|
|
588
1103
|
return new Range(this, start, start + text.length);
|
|
589
1104
|
}
|
|
590
1105
|
let segs = this.segments();
|
|
591
1106
|
let inserted = false;
|
|
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) : [];
|
|
592
1110
|
// A replacement that begins inside a revision is a deletion and then the new text typed there, so
|
|
593
1111
|
// the new text is plain and splits the revision, as typed text is (test/README.md check 21), rather
|
|
594
1112
|
// than joining the revision it replaced part of.
|
|
595
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)) {
|
|
596
|
-
this.
|
|
597
|
-
return this.
|
|
1114
|
+
this.spliceText(start, end, '', prefer, edges);
|
|
1115
|
+
return this.spliceText(start, start, text);
|
|
598
1116
|
}
|
|
599
|
-
if (end > start) {
|
|
1117
|
+
if (end > start || objects.length > 0 || references.length > 0) {
|
|
600
1118
|
// Replace: the new text takes the place (and formatting) of the first replaced character, as Word does.
|
|
601
1119
|
const first = segs.find((s) => s.start <= start && start < s.end);
|
|
602
1120
|
const toRemove = [];
|
|
@@ -619,11 +1137,19 @@ export class Paragraph {
|
|
|
619
1137
|
else
|
|
620
1138
|
toRemove.push(seg);
|
|
621
1139
|
}
|
|
622
|
-
for (const seg of toRemove.reverse()) {
|
|
1140
|
+
for (const seg of [...toRemove.reverse(), ...objects]) {
|
|
623
1141
|
const i = seg.owner.indexOf(seg.item);
|
|
624
1142
|
if (i >= 0)
|
|
625
1143
|
seg.owner.splice(i, 1);
|
|
626
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
|
+
}
|
|
1152
|
+
this.releaseRelationships(objects.map((seg) => seg.item));
|
|
627
1153
|
if (inserted && text.length > 0) {
|
|
628
1154
|
this.removeEmptyRuns();
|
|
629
1155
|
return new Range(this, start, start + text.length);
|
|
@@ -663,7 +1189,7 @@ export class Paragraph {
|
|
|
663
1189
|
const before = [...segs].reverse().find((s) => s.end <= start);
|
|
664
1190
|
const after = segs.find((s) => s.start >= start);
|
|
665
1191
|
const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
|
|
666
|
-
const firstRun =
|
|
1192
|
+
const firstRun = this.visibleFirstRunRPr();
|
|
667
1193
|
const rPr = (neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) ? deepCopy(neighbour.run.rPr) : firstRun ? deepCopy(firstRun) : undefined;
|
|
668
1194
|
const run = runOf([textItem(text)], rPr);
|
|
669
1195
|
if (neighbour === before && before)
|
|
@@ -671,7 +1197,7 @@ export class Paragraph {
|
|
|
671
1197
|
else if (after)
|
|
672
1198
|
after.runOwner.splice(after.runIndex, 0, run);
|
|
673
1199
|
else {
|
|
674
|
-
const content = ((
|
|
1200
|
+
const content = ((_e = (_f = this.p).content) !== null && _e !== void 0 ? _e : (_f.content = []));
|
|
675
1201
|
if (start === 0)
|
|
676
1202
|
content.unshift(run);
|
|
677
1203
|
else
|
|
@@ -689,8 +1215,10 @@ export class Paragraph {
|
|
|
689
1215
|
insertItemsAt(offset, items) {
|
|
690
1216
|
var _a, _b, _c, _d;
|
|
691
1217
|
var _e;
|
|
1218
|
+
this.assertWritable();
|
|
692
1219
|
if (items.length === 0)
|
|
693
1220
|
return;
|
|
1221
|
+
this.assertPlaceable(offset, offset, false);
|
|
694
1222
|
const tracker = this.changeTracker;
|
|
695
1223
|
if (tracker) {
|
|
696
1224
|
// With tracking on, what goes in is an insertion: its runs in w:ins, in a link, a control or a
|
|
@@ -720,18 +1248,46 @@ export class Paragraph {
|
|
|
720
1248
|
/** Splits the run at a text offset so that [offset, ...) begins a run; returns nothing when the offset is already a boundary. */
|
|
721
1249
|
splitAt(offset) {
|
|
722
1250
|
var _a;
|
|
1251
|
+
this.assertWritable();
|
|
1252
|
+
this.assertPlaceable(offset, offset, false);
|
|
723
1253
|
const seg = this.segments().find((s) => s.editable && s.start < offset && offset < s.end);
|
|
724
1254
|
if (!seg)
|
|
725
1255
|
return;
|
|
726
1256
|
const at = offset - seg.start;
|
|
727
1257
|
const t = seg.item.value;
|
|
728
1258
|
const head = seg.text.substring(0, at);
|
|
729
|
-
const tail = seg.text.substring(at);
|
|
1259
|
+
const tail = textItem(seg.text.substring(at));
|
|
730
1260
|
setText(t, head);
|
|
731
|
-
|
|
732
|
-
|
|
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);
|
|
733
1289
|
seg.runOwner.splice(seg.runIndex + 1, 0, second);
|
|
734
|
-
linkParents(second, (
|
|
1290
|
+
linkParents(second, (_b = seg.run.PARENT) !== null && _b !== void 0 ? _b : this.p);
|
|
735
1291
|
}
|
|
736
1292
|
// --- lists (CR-002 phase H) ---
|
|
737
1293
|
/**
|
|
@@ -768,14 +1324,17 @@ export class Paragraph {
|
|
|
768
1324
|
* unmarshalled (CR-002 section 17).
|
|
769
1325
|
*/
|
|
770
1326
|
startNewList(options) {
|
|
1327
|
+
this.assertWritable();
|
|
771
1328
|
return startNewList(this, options);
|
|
772
1329
|
}
|
|
773
1330
|
/** Office JS `paragraph.attachToList(listId, level)`: this paragraph joins that list. */
|
|
774
1331
|
attachToList(listId, level = 0) {
|
|
1332
|
+
this.assertWritable();
|
|
775
1333
|
return attachToList(this, listId, level);
|
|
776
1334
|
}
|
|
777
1335
|
/** Office JS `paragraph.detachFromList()`: this paragraph leaves its list. */
|
|
778
1336
|
detachFromList() {
|
|
1337
|
+
this.assertWritable();
|
|
779
1338
|
detachFromList(this);
|
|
780
1339
|
}
|
|
781
1340
|
/**
|
|
@@ -783,6 +1342,7 @@ export class Paragraph {
|
|
|
783
1342
|
* `w:abstractNum` with a `w:startOverride` (extension; `List.restart()` is the verb).
|
|
784
1343
|
*/
|
|
785
1344
|
restartList() {
|
|
1345
|
+
this.assertWritable();
|
|
786
1346
|
const list = this.list;
|
|
787
1347
|
const level = this.listItem.level;
|
|
788
1348
|
const restarted = list.restart();
|
|
@@ -796,6 +1356,7 @@ export class Paragraph {
|
|
|
796
1356
|
}
|
|
797
1357
|
/** Comments the whole paragraph (sugar over `getRange().insertComment`). */
|
|
798
1358
|
async insertComment(content, options) {
|
|
1359
|
+
this.assertWritable();
|
|
799
1360
|
return commentApi().insertComment(this.getRange(), content, options);
|
|
800
1361
|
}
|
|
801
1362
|
// --- content controls (CR-002 phase E) ---
|
|
@@ -806,18 +1367,32 @@ export class Paragraph {
|
|
|
806
1367
|
*/
|
|
807
1368
|
insertContentControl(kind) {
|
|
808
1369
|
var _a;
|
|
1370
|
+
this.assertWritable();
|
|
809
1371
|
const at = this.index;
|
|
810
1372
|
if (at < 0)
|
|
811
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;
|
|
812
1380
|
const sdt = sdtOf([this.element], { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this.parentBody)), form: 'block' });
|
|
813
1381
|
this.container.splice(at, 1, sdt);
|
|
814
|
-
linkParents(sdt,
|
|
1382
|
+
linkParents(sdt, parent);
|
|
815
1383
|
declareSdtExtensions(this.parentBody, sdt);
|
|
816
1384
|
// under tracking the control itself is the insertion, the paragraph what it was (check 30)
|
|
817
1385
|
const tracker = this.changeTracker;
|
|
818
1386
|
if (tracker)
|
|
819
1387
|
markHolderInserted(tracker, sdt, this.container, () => { });
|
|
820
|
-
|
|
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;
|
|
821
1396
|
}
|
|
822
1397
|
removeEmptyRuns() {
|
|
823
1398
|
var _a;
|
|
@@ -829,6 +1404,16 @@ export class Paragraph {
|
|
|
829
1404
|
if (!v.content || v.content.length === 0)
|
|
830
1405
|
items.splice(i, 1);
|
|
831
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
|
+
}
|
|
832
1417
|
else if (v.TYPE_NAME !== 'org_docx4j_wml.P') {
|
|
833
1418
|
const nested = runItemsOf(v);
|
|
834
1419
|
if (nested) {
|
|
@@ -847,15 +1432,29 @@ export class Paragraph {
|
|
|
847
1432
|
* The text in [start, end) becomes a deletion: the runs holding it are isolated, their `w:t`
|
|
848
1433
|
* turned into `w:delText`, and each run of consecutive ones moved into one `w:del`. Text this
|
|
849
1434
|
* author had inserted is simply removed, as Word does, rather than nested in a `w:del`.
|
|
1435
|
+
* The pictures in the span (`splice`'s `edges`) are deleted with the text, each in a run of its own
|
|
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.
|
|
850
1440
|
* Returns the `w:del` elements it made, in document order.
|
|
851
1441
|
*/
|
|
852
|
-
deleteText(tracker, start, end) {
|
|
853
|
-
var _a, _b, _c, _d, _e;
|
|
854
|
-
|
|
1442
|
+
deleteText(tracker, start, end, edges) {
|
|
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();
|
|
1448
|
+
const targets = this.isolate(start, end, edges);
|
|
855
1449
|
for (const seg of targets)
|
|
856
1450
|
tracker.assertEditable(seg.revision);
|
|
857
1451
|
const list = [];
|
|
1452
|
+
const equations = [];
|
|
858
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
|
+
}
|
|
859
1458
|
const run = seg.runOwner[seg.runIndex];
|
|
860
1459
|
if (list.length > 0 && list[list.length - 1].run === run)
|
|
861
1460
|
continue;
|
|
@@ -864,6 +1463,22 @@ export class Paragraph {
|
|
|
864
1463
|
target.revision = seg.revision;
|
|
865
1464
|
list.push(target);
|
|
866
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
|
+
}
|
|
867
1482
|
// groups of consecutive runs in the same array, so that one w:del holds them all
|
|
868
1483
|
const groups = [];
|
|
869
1484
|
for (const t of list) {
|
|
@@ -875,6 +1490,8 @@ export class Paragraph {
|
|
|
875
1490
|
else
|
|
876
1491
|
groups.push([t]);
|
|
877
1492
|
}
|
|
1493
|
+
for (const equation of equations)
|
|
1494
|
+
markEquationDeleted(tracker, equation);
|
|
878
1495
|
const made = [];
|
|
879
1496
|
for (let g = groups.length - 1; g >= 0; g--) {
|
|
880
1497
|
const group = groups[g];
|
|
@@ -882,7 +1499,12 @@ export class Paragraph {
|
|
|
882
1499
|
const at = owner.indexOf(group[0].run);
|
|
883
1500
|
const runs = owner.splice(at, group.length);
|
|
884
1501
|
if (group[0].own) {
|
|
885
|
-
// taking back our own insertion: the runs go, and an emptied w:ins with them
|
|
1502
|
+
// taking back our own insertion: the runs go, and an emptied w:ins with them; a picture's
|
|
1503
|
+
// image waits for the save, as `InlinePicture.delete` leaves it (section 47); a comment whose
|
|
1504
|
+
// reference goes with them goes too
|
|
1505
|
+
this.releaseRelationships(runs);
|
|
1506
|
+
for (const id of commentReferenceIds(runs))
|
|
1507
|
+
this.queueCommentRemoval(id);
|
|
886
1508
|
const revision = group[0].revision;
|
|
887
1509
|
if (revision && revision.items.length === 0) {
|
|
888
1510
|
const i = revision.owner.indexOf(revision.element);
|
|
@@ -895,15 +1517,60 @@ export class Paragraph {
|
|
|
895
1517
|
toDeletedText(run.value);
|
|
896
1518
|
const del = tracker.del(runs);
|
|
897
1519
|
owner.splice(at, 0, del);
|
|
898
|
-
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));
|
|
899
1521
|
const revision = group[0].revision;
|
|
900
|
-
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) };
|
|
901
1523
|
if (revision && revision.items === owner)
|
|
902
1524
|
anchor.revision = revision;
|
|
903
1525
|
made.unshift(anchor);
|
|
904
1526
|
}
|
|
905
1527
|
return made;
|
|
906
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
|
+
}
|
|
907
1574
|
/**
|
|
908
1575
|
* Splits the revision that holds the text at `at` into two - the second half a copy of its
|
|
909
1576
|
* attributes under a new `w:id` - and puts `item` between them. Word splits a revision so when
|
|
@@ -993,7 +1660,7 @@ export class Paragraph {
|
|
|
993
1660
|
* of a replacement, which the insertion must follow.
|
|
994
1661
|
*/
|
|
995
1662
|
insertTracked(tracker, at, text, anchor, prefer = 'before') {
|
|
996
|
-
var _a, _b
|
|
1663
|
+
var _a, _b;
|
|
997
1664
|
if (anchor) {
|
|
998
1665
|
const rPr = copyRPr((_a = runsOf(anchor.element.value, { view: 'original' })[0]) === null || _a === void 0 ? void 0 : _a.value.rPr);
|
|
999
1666
|
const ins = tracker.ins([runOf([textItem(text)], rPr)]);
|
|
@@ -1029,7 +1696,7 @@ export class Paragraph {
|
|
|
1029
1696
|
}
|
|
1030
1697
|
const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
|
|
1031
1698
|
tracker.assertEditable(neighbour === null || neighbour === void 0 ? void 0 : neighbour.revision);
|
|
1032
|
-
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());
|
|
1033
1700
|
const ins = tracker.ins([runOf([textItem(text)], rPr)]);
|
|
1034
1701
|
this.placeInsertion(at, [ins], before, after, () => tracker.nextId(), prefer);
|
|
1035
1702
|
}
|
|
@@ -1069,32 +1736,42 @@ export class Paragraph {
|
|
|
1069
1736
|
linkParents(items, this.p);
|
|
1070
1737
|
}
|
|
1071
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
|
+
}
|
|
1072
1746
|
/**
|
|
1073
1747
|
* Splits runs so that every run holding text in [start, end) holds nothing else, and returns
|
|
1074
1748
|
* the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
|
|
1075
1749
|
* of a run that straddles a boundary (a tab, a break, a drawing) out into runs of their own.
|
|
1750
|
+
* The pictures in the span (`splice`'s `edges`) are among the segments returned, and a run
|
|
1751
|
+
* holding one holds nothing outside the span either.
|
|
1076
1752
|
*/
|
|
1077
|
-
isolate(start, end) {
|
|
1753
|
+
isolate(start, end, edges) {
|
|
1078
1754
|
this.splitAt(start);
|
|
1079
1755
|
this.splitAt(end);
|
|
1756
|
+
const inSpan = (s) => s.object ? objectInSpan(s.start, start, end, edges) : s.start >= start && s.end <= end && s.text.length > 0;
|
|
1080
1757
|
for (let guard = 0; guard < 10000; guard++) {
|
|
1081
|
-
const inside = this.segments().filter(
|
|
1758
|
+
const inside = this.segments({ objects: true }).filter(inSpan);
|
|
1082
1759
|
let split = false;
|
|
1083
1760
|
const seen = new Set();
|
|
1084
1761
|
for (const seg of inside) {
|
|
1085
1762
|
if (seen.has(seg.run))
|
|
1086
1763
|
continue;
|
|
1087
1764
|
seen.add(seg.run);
|
|
1765
|
+
if (seg.math)
|
|
1766
|
+
continue; // in no run: taken whole or refused
|
|
1088
1767
|
const ofRun = inside.filter((s) => s.run === seg.run);
|
|
1089
1768
|
const first = ofRun[0];
|
|
1090
1769
|
const last = ofRun[ofRun.length - 1];
|
|
1091
|
-
if (first
|
|
1092
|
-
this.splitRunBefore(first);
|
|
1770
|
+
if (this.splitRunBefore(first)) {
|
|
1093
1771
|
split = true;
|
|
1094
1772
|
break;
|
|
1095
1773
|
}
|
|
1096
|
-
if (
|
|
1097
|
-
this.splitRunAfter(last);
|
|
1774
|
+
if (this.splitRunAfter(last)) {
|
|
1098
1775
|
split = true;
|
|
1099
1776
|
break;
|
|
1100
1777
|
}
|
|
@@ -1102,25 +1779,60 @@ export class Paragraph {
|
|
|
1102
1779
|
if (!split)
|
|
1103
1780
|
return inside;
|
|
1104
1781
|
}
|
|
1105
|
-
return this.segments().filter(
|
|
1782
|
+
return this.segments({ objects: true }).filter(inSpan);
|
|
1783
|
+
}
|
|
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
|
+
}
|
|
1106
1804
|
}
|
|
1107
|
-
/** Moves the items before `seg` into a run of their own, in front of
|
|
1805
|
+
/** Moves the items before `seg` into a run of their own, in front of the rest; false where there are none. */
|
|
1108
1806
|
splitRunBefore(seg) {
|
|
1109
1807
|
var _a;
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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;
|
|
1114
1820
|
}
|
|
1115
|
-
/** 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. */
|
|
1116
1822
|
splitRunAfter(seg) {
|
|
1117
1823
|
var _a;
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return;
|
|
1121
|
-
|
|
1122
|
-
seg.
|
|
1123
|
-
|
|
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;
|
|
1124
1836
|
}
|
|
1125
1837
|
}
|
|
1126
1838
|
export { W_NS };
|
|
@@ -1172,3 +1884,127 @@ function setText(t, value) {
|
|
|
1172
1884
|
else
|
|
1173
1885
|
delete t.space;
|
|
1174
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
|
+
}
|