@docx4j/core-ts 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -0
- package/README.md +92 -4
- package/dist/html/convert.mjs +2 -1
- package/dist/html/elements.mjs +2 -1
- package/dist/model/color/ColorMath.d.mts +29 -0
- package/dist/model/color/ColorMath.mjs +146 -0
- package/dist/model/color/ColorTables.d.mts +6 -0
- package/dist/model/color/ColorTables.mjs +83 -0
- package/dist/model/color/DrawingMLColors.d.mts +52 -0
- package/dist/model/color/DrawingMLColors.mjs +374 -0
- package/dist/model/color/ResolvedColor.d.mts +36 -0
- package/dist/model/color/ResolvedColor.mjs +65 -0
- package/dist/model/color/SpreadsheetColors.d.mts +36 -0
- package/dist/model/color/SpreadsheetColors.mjs +84 -0
- package/dist/model/color/StyleFill.d.mts +28 -0
- package/dist/model/color/StyleFill.mjs +21 -0
- package/dist/model/color/ThemeColors.d.mts +86 -0
- package/dist/model/color/ThemeColors.mjs +304 -0
- package/dist/model/color/WordColors.d.mts +71 -0
- package/dist/model/color/WordColors.mjs +121 -0
- package/dist/model/color/index.d.mts +6 -0
- package/dist/model/color/index.mjs +30 -0
- package/dist/model/content/Body.d.mts +145 -23
- package/dist/model/content/Body.mjs +507 -150
- package/dist/model/content/Comment.d.mts +23 -5
- package/dist/model/content/Comment.mjs +207 -26
- package/dist/model/content/ContentControl.d.mts +198 -19
- package/dist/model/content/ContentControl.mjs +672 -99
- package/dist/model/content/Font.d.mts +70 -17
- package/dist/model/content/Font.mjs +119 -36
- package/dist/model/content/InlinePicture.d.mts +11 -0
- package/dist/model/content/InlinePicture.mjs +21 -1
- package/dist/model/content/List.d.mts +6 -3
- package/dist/model/content/List.mjs +18 -8
- package/dist/model/content/Paragraph.d.mts +200 -14
- package/dist/model/content/Paragraph.mjs +898 -82
- package/dist/model/content/Range.d.mts +227 -12
- package/dist/model/content/Range.mjs +785 -59
- package/dist/model/content/ResolvedView.d.mts +104 -0
- package/dist/model/content/ResolvedView.mjs +596 -0
- package/dist/model/content/Table.d.mts +29 -3
- package/dist/model/content/Table.mjs +135 -49
- package/dist/model/content/TrackedChange.d.mts +174 -15
- package/dist/model/content/TrackedChange.mjs +734 -170
- 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 +10 -3
- package/dist/model/content/index.mjs +10 -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/ooxml.mjs +12 -1
- package/dist/model/content/outline.d.mts +5 -0
- package/dist/model/content/outline.mjs +13 -0
- package/dist/model/content/paraIds.d.mts +5 -0
- package/dist/model/content/paraIds.mjs +54 -0
- package/dist/model/content/placeholders.d.mts +110 -0
- package/dist/model/content/placeholders.mjs +253 -0
- package/dist/model/content/search.d.mts +74 -0
- package/dist/model/content/search.mjs +255 -9
- package/dist/model/content/spanEdits.d.mts +159 -0
- package/dist/model/content/spanEdits.mjs +728 -0
- package/dist/model/content/story.d.mts +47 -0
- package/dist/model/content/story.mjs +95 -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 +102 -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 +63 -6
- package/dist/model/content/tracking.mjs +242 -22
- package/dist/model/content/tree.d.mts +172 -24
- package/dist/model/content/tree.mjs +328 -54
- package/dist/model/content/wordRange.d.mts +48 -0
- package/dist/model/content/wordRange.mjs +97 -0
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +30 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +81 -12
- package/dist/model/customxml/XmlMapping.d.mts +32 -8
- package/dist/model/customxml/XmlMapping.mjs +64 -10
- package/dist/model/customxml/bindings.d.mts +33 -4
- package/dist/model/customxml/bindings.mjs +171 -45
- package/dist/model/customxml/index.d.mts +1 -0
- package/dist/model/customxml/index.mjs +1 -0
- package/dist/model/customxml/kinds.d.mts +8 -3
- package/dist/model/customxml/kinds.mjs +26 -5
- package/dist/model/customxml/opendope.d.mts +4 -1
- package/dist/model/customxml/opendope.mjs +9 -6
- package/dist/model/customxml/picturePlaceholder.d.mts +17 -0
- package/dist/model/customxml/picturePlaceholder.mjs +32 -0
- package/dist/model/customxml/pictures.d.mts +4 -3
- package/dist/model/customxml/pictures.mjs +22 -8
- package/dist/model/customxml/storeItems.d.mts +57 -0
- package/dist/model/customxml/storeItems.mjs +122 -0
- package/dist/model/customxml/xhtml.mjs +23 -22
- package/dist/model/dateZone.d.mts +25 -0
- package/dist/model/dateZone.mjs +57 -0
- package/dist/model/index.d.mts +3 -0
- package/dist/model/index.mjs +5 -0
- package/dist/model/listnumbering/Emulator.d.mts +37 -5
- package/dist/model/listnumbering/Emulator.mjs +63 -9
- package/dist/model/properties/PropertyResolver.d.mts +74 -2
- package/dist/model/properties/PropertyResolver.mjs +133 -5
- package/dist/model/properties/catalogue.d.mts +19 -5
- package/dist/model/properties/catalogue.mjs +41 -15
- package/dist/model/properties/index.d.mts +1 -1
- package/dist/model/properties/log.d.mts +1 -1
- package/dist/model/properties/log.mjs +5 -5
- package/dist/model/properties/styleUtil.d.mts +3 -1
- package/dist/model/properties/styleUtil.mjs +20 -24
- package/dist/model/properties/table.mjs +4 -2
- package/dist/model/text/CaseMapping.d.mts +38 -0
- package/dist/model/text/CaseMapping.mjs +86 -0
- package/dist/model/text/Extractor.d.mts +349 -0
- package/dist/model/text/Extractor.mjs +2406 -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 +542 -0
- package/dist/model/text/TextContext.d.mts +256 -0
- package/dist/model/text/TextContext.mjs +600 -0
- package/dist/model/text/TextOptions.d.mts +233 -0
- package/dist/model/text/TextOptions.mjs +419 -0
- package/dist/model/text/TextResult.d.mts +621 -0
- package/dist/model/text/TextResult.mjs +629 -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 +936 -0
- package/dist/model/text/caseMapping.generated.d.mts +7 -0
- package/dist/model/text/caseMapping.generated.mjs +214 -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 +8 -0
- package/dist/model/text/index.mjs +9 -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/model/wordIntegers.d.mts +29 -0
- package/dist/model/wordIntegers.mjs +78 -0
- package/dist/office-js/document.mjs +15 -1
- 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 +29 -12
- package/dist/opc/DirectoryPartStore.d.mts +6 -2
- package/dist/opc/DirectoryPartStore.mjs +11 -4
- package/dist/opc/FlatOpcPartStore.d.mts +4 -2
- package/dist/opc/FlatOpcPartStore.mjs +11 -8
- package/dist/opc/Load.d.mts +32 -0
- package/dist/opc/Load.mjs +115 -5
- package/dist/opc/PartStore.d.mts +34 -1
- package/dist/opc/PartStore.mjs +71 -8
- package/dist/opc/Save.d.mts +32 -0
- package/dist/opc/Save.mjs +110 -4
- package/dist/opc/ZipPartStore.d.mts +4 -2
- package/dist/opc/ZipPartStore.mjs +12 -7
- package/dist/opc/exceptions.d.mts +140 -0
- package/dist/opc/exceptions.mjs +108 -0
- package/dist/opc/index.d.mts +8 -5
- package/dist/opc/index.mjs +5 -3
- package/dist/opc/mce/preprocessor.d.mts +141 -5
- package/dist/opc/mce/preprocessor.mjs +119 -18
- package/dist/opc/mce/understood.d.mts +3 -11
- package/dist/opc/mce/understood.mjs +27 -23
- package/dist/opc/repair/breaks.d.mts +23 -0
- package/dist/opc/repair/breaks.mjs +288 -0
- package/dist/opc/repair/hosts.d.mts +12 -0
- package/dist/opc/repair/hosts.mjs +33 -0
- package/dist/opc/repair/mainRoot.d.mts +19 -0
- package/dist/opc/repair/mainRoot.mjs +168 -0
- package/dist/opc/repair/unknownElements.d.mts +8 -0
- package/dist/opc/repair/unknownElements.mjs +180 -0
- package/dist/opc/repair/wmlNames.generated.d.mts +2 -0
- package/dist/opc/repair/wmlNames.generated.mjs +80 -0
- package/dist/opc/skipped.d.mts +50 -0
- package/dist/opc/skipped.mjs +140 -0
- package/dist/packages/OpcPackage.d.mts +110 -4
- package/dist/packages/OpcPackage.mjs +123 -7
- package/dist/packages/WordprocessingMLPackage.d.mts +45 -8
- package/dist/packages/WordprocessingMLPackage.mjs +158 -21
- package/dist/packages/index.d.mts +1 -1
- package/dist/parts/DefaultXmlPart.mjs +2 -2
- package/dist/parts/Part.d.mts +14 -2
- package/dist/parts/RelationshipsPart.d.mts +15 -0
- package/dist/parts/RelationshipsPart.mjs +66 -1
- package/dist/parts/XmlPart.d.mts +63 -32
- package/dist/parts/XmlPart.mjs +240 -96
- package/dist/parts/wml/comments.mjs +30 -0
- package/dist/parts/wml/index.d.mts +12 -1
- package/dist/parts/wml/index.mjs +37 -1
- package/dist/xml/dom.d.mts +7 -0
- package/dist/xml/dom.mjs +18 -0
- package/package.json +2 -2
|
@@ -1,26 +1,33 @@
|
|
|
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 { shownInDefaultView } from './story.mjs';
|
|
7
|
+
import { withinWord, WORD_STORAGE } from './wordRange.mjs';
|
|
5
8
|
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
6
9
|
function withRPr(run, rPr) {
|
|
7
10
|
if (rPr)
|
|
8
11
|
run.value.rPr = rPr;
|
|
9
12
|
return run;
|
|
10
13
|
}
|
|
11
|
-
import { Font } from './Font.mjs';
|
|
14
|
+
import { Font, markHolderOf } from './Font.mjs';
|
|
12
15
|
import { runFontSelectorOf } from '../fonts/lookup.mjs';
|
|
13
16
|
import { Range } from './Range.mjs';
|
|
14
|
-
import {
|
|
17
|
+
import { officeMatches, expandReplacement, paragraphMarkPieces, refuseMarkReplacement, refuseWildcardMarkReplacement } from './search.mjs';
|
|
15
18
|
import { builtInOf, idOfBuiltIn, styleNameOf, styleIdOf } from './styles.mjs';
|
|
16
19
|
import { cellOf } from './Table.mjs';
|
|
17
|
-
import { ContentControl, collectRunControls } from './ContentControl.mjs';
|
|
18
|
-
import {
|
|
20
|
+
import { ContentControl, collectRunControls, controlViewOf, plainTextLinesOf, textForRunControl, assertControlHolds, controlsOfTypes, holdsOneParagraph, showsPlaceholderWhenMade, BLOCK_CONTROL } from './ContentControl.mjs';
|
|
21
|
+
import { breakParagraphAt, BREAKABLE } from './spanEdits.mjs';
|
|
22
|
+
import { endsCell, endsWithParagraph, wrappingCellOf } from './tableRules.mjs';
|
|
23
|
+
import { dropExcluded, forWriter, linesOf, officeItems, oneParagraphRun, holdsElementCharacters } from './officeText.mjs';
|
|
24
|
+
import { sdt as sdtOf, nextSdtId, sdtProperty } from '@docx4j/generated-objects-ts/builders/wml';
|
|
19
25
|
import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
|
|
20
26
|
import { InlinePicture, picturesIn, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
27
|
+
import { showPicturePlaceholder } from '../customxml/picturePlaceholder.mjs';
|
|
21
28
|
import { contentOf } from './ooxml.mjs';
|
|
22
29
|
import { commentApi } from './comments.mjs';
|
|
23
|
-
import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties } from './tracking.mjs';
|
|
30
|
+
import { ChangeTracker, copyRPr, markDeleted, toDeletedText, highestAnnotationId, canonical, restorePPr, wrapInsertedRuns, markHolderInserted, pruneParagraphProperties, markEquationDeleted, equationRevisionIn } from './tracking.mjs';
|
|
24
31
|
import { trackedChangesOfParagraph } from './TrackedChange.mjs';
|
|
25
32
|
import { isListItem, listOf, listOrNullObjectOf, listItemOf, listItemOrNullObjectOf, startNewList, attachToList, detachFromList, } from './List.mjs';
|
|
26
33
|
const TWIPS_PER_POINT = 20;
|
|
@@ -62,23 +69,57 @@ export class Paragraph {
|
|
|
62
69
|
get p() {
|
|
63
70
|
return this.element.value;
|
|
64
71
|
}
|
|
72
|
+
/** @internal A view of another paragraph of this one's body, for a `Range` across paragraphs (CR-014). */
|
|
73
|
+
viewOf(element, container) {
|
|
74
|
+
return new Paragraph(element, container, this.parentBody);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @internal A range from this paragraph's start to the end of `last`, a later paragraph of the same story, with
|
|
78
|
+
* `last`'s mark where `mark` is set, or `endMark` in its place (CR-014 phase 1).
|
|
79
|
+
*/
|
|
80
|
+
rangeTo(last, mark, endMark) {
|
|
81
|
+
return new Range(this, 0, last.text.length, true, mark, last.element === this.element ? undefined : last, endMark);
|
|
82
|
+
}
|
|
83
|
+
/** How many tables deep the paragraph is: 0 outside a table, 1 in a cell, 2 in a nested table's (Office JS `tableNestingLevel`). */
|
|
84
|
+
get tableNestingLevel() {
|
|
85
|
+
let level = 0;
|
|
86
|
+
for (let cell = this.parentTableCell; cell; cell = cell.parentTable.parentTableCell)
|
|
87
|
+
level++;
|
|
88
|
+
return level;
|
|
89
|
+
}
|
|
65
90
|
get index() {
|
|
66
91
|
return this.container.indexOf(this.element);
|
|
67
92
|
}
|
|
68
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* The paragraph's text as Office JS gives it in Word's default view (CR-012 phase 4, section 6.2):
|
|
95
|
+
* the accepted text, hidden text left out, in Word's characters (check 48: a note reference U+0002,
|
|
96
|
+
* a line break U+000B, a page break U+000C, a column break U+000E, a `w:cr` U+000D, the hyphens
|
|
97
|
+
* U+001E and U+001F, a symbol `(`, an equation as Word draws it). A kept `mc:AlternateContent`
|
|
98
|
+
* reads as its selected branch (check 49). Every offset a `Range` or `search` takes counts in it.
|
|
99
|
+
*/
|
|
69
100
|
get text() {
|
|
70
|
-
return
|
|
101
|
+
return this.segments().map((s) => s.text).join('');
|
|
71
102
|
}
|
|
72
103
|
/** Replaces the content with one run of the text, keeping the first run's formatting. */
|
|
73
104
|
set text(value) {
|
|
74
105
|
var _a;
|
|
106
|
+
this.assertWritable();
|
|
107
|
+
// one paragraph, its line breaks w:br and w:cr (CR-002 section 60 E2), or as a plain-text control takes them (F3)
|
|
108
|
+
const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
|
|
109
|
+
const mode = lines === 'multi' ? 'multi-line' : lines === 'single' ? 'single-line' : 'one-paragraph';
|
|
110
|
+
// in a block control showing its placeholder, the text in the placeholder's place (CR-002 section 63)
|
|
111
|
+
const placeholder = this.placeholderControl;
|
|
112
|
+
if (placeholder) {
|
|
113
|
+
placeholder.writePlaceholder(value, { lineBreaks: mode });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
75
116
|
if (this.changeTracker) {
|
|
76
|
-
this.splice(0, this.text.length, value, 'before', WHOLE);
|
|
117
|
+
this.splice(0, this.text.length, value, 'before', WHOLE, mode);
|
|
77
118
|
return;
|
|
78
119
|
}
|
|
79
120
|
const rPr = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value.rPr;
|
|
80
121
|
this.releaseRelationships(this.p.content);
|
|
81
|
-
this.p.content = [
|
|
122
|
+
this.p.content = [oneParagraphRun(value, rPr ? deepCopy(rPr) : undefined, mode)];
|
|
82
123
|
linkParents(this.p.content, this.p);
|
|
83
124
|
}
|
|
84
125
|
/**
|
|
@@ -86,7 +127,7 @@ export class Paragraph {
|
|
|
86
127
|
* reads the document as it was before the tracked changes: `w:del` included, `w:ins` excluded.
|
|
87
128
|
*/
|
|
88
129
|
getText(options) {
|
|
89
|
-
return (options === null || options === void 0 ? void 0 : options.view) === 'original' ?
|
|
130
|
+
return (options === null || options === void 0 ? void 0 : options.view) === 'original' ? this.segments({ view: 'original' }).map((s) => s.text).join('') : this.text;
|
|
90
131
|
}
|
|
91
132
|
/** The style id (w:pStyle; docx4j's name for it); 'Normal' when none is set. Extension: Office JS has `style` and `styleBuiltIn` only. */
|
|
92
133
|
get styleId() {
|
|
@@ -190,36 +231,54 @@ export class Paragraph {
|
|
|
190
231
|
// in a table cell what is inherited includes the table style's (CR-007)
|
|
191
232
|
return alignmentOf(resolver.getEffectivePPr(rest, resolver.cellContextOf(this.p)), true);
|
|
192
233
|
}
|
|
193
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* Indents and spacing in points, as Office JS; the effective values (CR-001 Phase B step 2). A value written is held to
|
|
236
|
+
* the range Word 365 stores its attribute in (`withinWord`, CR-002 section 65): outside it, `InvalidArgument`, before
|
|
237
|
+
* anything changes.
|
|
238
|
+
*/
|
|
194
239
|
get leftIndent() { var _a, _b; return ((_b = (_a = this.effectivePPr.ind) === null || _a === void 0 ? void 0 : _a.left) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
|
|
195
|
-
set leftIndent(pt) { this.ind().left =
|
|
240
|
+
set leftIndent(pt) { const v = withinWord('Paragraph.leftIndent', pt, WORD_STORAGE.indLeft); this.ind().left = v; this.settleFormatting(); }
|
|
196
241
|
get rightIndent() { var _a, _b; return ((_b = (_a = this.effectivePPr.ind) === null || _a === void 0 ? void 0 : _a.right) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
|
|
197
|
-
set rightIndent(pt) { this.ind().right =
|
|
242
|
+
set rightIndent(pt) { const v = withinWord('Paragraph.rightIndent', pt, WORD_STORAGE.indRight); this.ind().right = v; this.settleFormatting(); }
|
|
198
243
|
/** First-line indent; negative for a hanging indent, as Office JS. */
|
|
199
244
|
get firstLineIndent() {
|
|
200
245
|
return firstLineIndentOf(this.effectivePPr);
|
|
201
246
|
}
|
|
202
247
|
set firstLineIndent(pt) {
|
|
248
|
+
const v = pt < 0
|
|
249
|
+
? withinWord('Paragraph.firstLineIndent (a hanging indent)', -pt, WORD_STORAGE.indHanging)
|
|
250
|
+
: withinWord('Paragraph.firstLineIndent', pt, WORD_STORAGE.indFirstLine);
|
|
203
251
|
const ind = this.ind();
|
|
204
252
|
if (pt < 0) {
|
|
205
|
-
ind.hanging =
|
|
253
|
+
ind.hanging = v;
|
|
206
254
|
delete ind.firstLine;
|
|
207
255
|
}
|
|
208
256
|
else {
|
|
209
|
-
ind.firstLine =
|
|
257
|
+
ind.firstLine = v;
|
|
210
258
|
delete ind.hanging;
|
|
211
259
|
}
|
|
212
260
|
this.settleFormatting();
|
|
213
261
|
}
|
|
214
262
|
get spaceBefore() { var _a, _b; return ((_b = (_a = this.effectivePPr.spacing) === null || _a === void 0 ? void 0 : _a.before) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
|
|
215
|
-
set spaceBefore(pt) { this.spacing().before =
|
|
263
|
+
set spaceBefore(pt) { const v = withinWord('Paragraph.spaceBefore', pt, WORD_STORAGE.spacingBefore); this.spacing().before = v; this.settleFormatting(); }
|
|
216
264
|
get spaceAfter() { var _a, _b; return ((_b = (_a = this.effectivePPr.spacing) === null || _a === void 0 ? void 0 : _a.after) !== null && _b !== void 0 ? _b : 0) / TWIPS_PER_POINT; }
|
|
217
|
-
set spaceAfter(pt) { this.spacing().after =
|
|
265
|
+
set spaceAfter(pt) { const v = withinWord('Paragraph.spaceAfter', pt, WORD_STORAGE.spacingAfter); this.spacing().after = v; this.settleFormatting(); }
|
|
218
266
|
/** Line spacing in points: w:line/240 lines of 12pt when the rule is auto, else w:line twips; 0 when not set. */
|
|
219
267
|
get lineSpacing() {
|
|
220
268
|
return lineSpacingOf(this.effectivePPr);
|
|
221
269
|
}
|
|
222
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Writes `w:line` as 20 × points under `w:lineRule="auto"`, as Word 365's own setter does (check 71b: 0.75 pt as
|
|
272
|
+
* `w:line="15" w:lineRule="auto"`; Office JS: "in the Word UI, this value is divided by 12", so 12 pt is single spacing),
|
|
273
|
+
* within Word's own limits, 0.75 to 1584 pt (CR-002 section 65.9). Until check 71b it wrote an exact rule.
|
|
274
|
+
*/
|
|
275
|
+
set lineSpacing(pt) {
|
|
276
|
+
const v = withinWord('Paragraph.lineSpacing', pt, WORD_STORAGE.spacingLineAuto);
|
|
277
|
+
const s = this.spacing();
|
|
278
|
+
s.line = v;
|
|
279
|
+
s.lineRule = 'auto';
|
|
280
|
+
this.settleFormatting();
|
|
281
|
+
}
|
|
223
282
|
/** w:outlineLvl + 1 (1 to 9); 10 for body text, as Office JS. A heading style supplies it. */
|
|
224
283
|
get outlineLevel() { return outlineLevelOf(this.effectivePPr); }
|
|
225
284
|
set outlineLevel(level) { const pPr = this.pPr(); if (level >= 10 || level < 1)
|
|
@@ -227,24 +286,36 @@ export class Paragraph {
|
|
|
227
286
|
else
|
|
228
287
|
pPr.outlineLvl = { val: level - 1 }; this.settleFormatting(); }
|
|
229
288
|
/**
|
|
230
|
-
* The runs' formatting: reads the
|
|
231
|
-
*
|
|
289
|
+
* The runs' formatting: reads the *effective* properties of the runs holding the paragraph's text, `null` (or `''`,
|
|
290
|
+
* `Mixed`) where they differ, and the mark's where it has no text, as Office JS reads them (CR-002 section 59); writes
|
|
291
|
+
* direct formatting to every run (extension: `Font` is over runs, not the paragraph mark).
|
|
232
292
|
*/
|
|
233
293
|
get font() {
|
|
234
294
|
return this.getFont();
|
|
235
295
|
}
|
|
236
|
-
/** `font`, with `{ direct: true }` for the direct formatting
|
|
296
|
+
/** `font`, with `{ direct: true }` for the runs' direct formatting (extension). */
|
|
237
297
|
getFont(options) {
|
|
238
|
-
const holders = () =>
|
|
298
|
+
const holders = (write) => {
|
|
299
|
+
if (write) {
|
|
300
|
+
this.assertWritable();
|
|
301
|
+
return runsOf(this.p).map((r) => r.value);
|
|
302
|
+
}
|
|
303
|
+
// a read takes the runs of Paragraph.text, Word's default view, as a range over it does; with none, a placeholder's
|
|
304
|
+
// runs, which Word's font reads though its text doesn't (check 52 case 13, after Accept All: Word's bold filler)
|
|
305
|
+
const runs = this.getRange('Content').runs.map((r) => r.value);
|
|
306
|
+
return runs.length > 0 ? runs : runsOf(this.p).map((r) => r.value).filter((r) => inShowingPlaceholder(r));
|
|
307
|
+
};
|
|
308
|
+
const mark = () => markHolderOf(this.p);
|
|
239
309
|
if ((options === null || options === void 0 ? void 0 : options.direct) === true)
|
|
240
|
-
return new Font(holders, () => this.fontTracking());
|
|
241
|
-
return new Font(holders, () => this.fontTracking(), (rPr) => this.parentBody.propertyResolver.getEffectiveRPr(rPr, this.p.pPr, this.cellContext), (rPr) => { var _a; return (_a = runFontSelectorOf(this.parentBody.package_)) === null || _a === void 0 ? void 0 : _a.asciiFontName(rPr); });
|
|
310
|
+
return new Font(holders, () => this.fontTracking(), undefined, undefined, mark);
|
|
311
|
+
return new Font(holders, () => this.fontTracking(), (rPr) => this.parentBody.propertyResolver.getEffectiveRPr(rPr, this.p.pPr, this.cellContext), (rPr) => { var _a; return (_a = runFontSelectorOf(this.parentBody.package_)) === null || _a === void 0 ? void 0 : _a.asciiFontName(rPr); }, mark, () => this.parentBody.propertyResolver.getThemeColors());
|
|
242
312
|
}
|
|
243
313
|
/** w14:paraId, the stable address Word gives paragraphs. */
|
|
244
314
|
get paraId() {
|
|
245
315
|
return this.p.paraId;
|
|
246
316
|
}
|
|
247
317
|
set paraId(id) {
|
|
318
|
+
this.assertWritable();
|
|
248
319
|
if (id === undefined)
|
|
249
320
|
delete this.p.paraId;
|
|
250
321
|
else
|
|
@@ -264,6 +335,14 @@ export class Paragraph {
|
|
|
264
335
|
collectRunControls(this.p, this.parentBody, out);
|
|
265
336
|
return out;
|
|
266
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
|
|
340
|
+
* where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
|
|
341
|
+
* departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
|
|
342
|
+
*/
|
|
343
|
+
getContentControls(options) {
|
|
344
|
+
return controlsOfTypes(this.contentControls, options);
|
|
345
|
+
}
|
|
267
346
|
/**
|
|
268
347
|
* The inline pictures in this paragraph, in order (Office JS Paragraph.inlinePictures): each
|
|
269
348
|
* `w:drawing` whose `wp:inline` holds a `pic:pic`, in a run or in the selected branch of a kept
|
|
@@ -283,27 +362,55 @@ export class Paragraph {
|
|
|
283
362
|
}
|
|
284
363
|
/** Text inserted at the start, the end, or replacing the whole paragraph's text. */
|
|
285
364
|
insertText(text, location) {
|
|
365
|
+
// in a block control showing its placeholder the text takes the placeholder's place wherever it is put (CR-002
|
|
366
|
+
// section 63; check 63's run 2, cases 7 to 9 and 33 to 35)
|
|
367
|
+
const placeholder = this.placeholderControl;
|
|
368
|
+
if (placeholder)
|
|
369
|
+
return placeholder.writePlaceholder(text);
|
|
286
370
|
const length = this.text.length;
|
|
287
371
|
if (location === 'Start')
|
|
288
372
|
return this.splice(0, 0, text);
|
|
289
373
|
if (location === 'End')
|
|
290
|
-
return this.
|
|
374
|
+
return this.writeAtEnd(text);
|
|
375
|
+
// tracked, the insertion goes first, before the deletion, as a range's Replace writes it and Word wrote this one
|
|
376
|
+
// (check 63 cases 45 and 52; CR-002 section 63 D3)
|
|
377
|
+
if (this.changeTracker && length > 0)
|
|
378
|
+
return this.getRange('Content').insertText(text, 'Replace');
|
|
291
379
|
return this.splice(0, length, text, 'before', WHOLE);
|
|
292
380
|
}
|
|
293
|
-
/**
|
|
381
|
+
/**
|
|
382
|
+
* A new paragraph of the text before or after this one, with this paragraph's properties (as Word). Beside the only
|
|
383
|
+
* paragraph of a block control showing its placeholder it goes outside the control, as Word's went, and the control
|
|
384
|
+
* stays at block level (CR-002 section 63, D6 (b); check 63 cases 10, 11, 36 and 37).
|
|
385
|
+
*/
|
|
294
386
|
insertParagraph(text, location) {
|
|
387
|
+
var _a, _b;
|
|
388
|
+
this.assertWritable();
|
|
295
389
|
const pPr = this.p.pPr ? deepCopy(this.p.pPr) : undefined;
|
|
296
390
|
if (pPr) {
|
|
297
391
|
delete pPr.rPr;
|
|
298
392
|
delete pPr.sectPr;
|
|
299
393
|
delete pPr.pPrChange;
|
|
300
394
|
}
|
|
301
|
-
const
|
|
395
|
+
const run = oneParagraphRun(text); // E2
|
|
396
|
+
const el = paragraphOf(((_a = run.value.content) === null || _a === void 0 ? void 0 : _a.length) ? [run] : [], pPr);
|
|
397
|
+
const control = this.blockControl;
|
|
398
|
+
if ((control === null || control === void 0 ? void 0 : control.isShowingPlaceholder) && holdsOneParagraph(control)) {
|
|
399
|
+
// the body holding the control: this paragraph's is the control's content
|
|
400
|
+
const owner = (_b = control.element.value.PARENT) !== null && _b !== void 0 ? _b : this.parentBody.container;
|
|
401
|
+
return this.parentBody.sub(owner).insertElement(el, location, { element: control.element, container: control.container });
|
|
402
|
+
}
|
|
302
403
|
return this.parentBody.insertElement(el, location, this);
|
|
303
404
|
}
|
|
405
|
+
/** The block-level control holding this paragraph directly while it shows its placeholder (CR-002 section 63). */
|
|
406
|
+
get placeholderControl() {
|
|
407
|
+
const control = this.blockControl;
|
|
408
|
+
return (control === null || control === void 0 ? void 0 : control.showsPlaceholderContent) ? control : undefined;
|
|
409
|
+
}
|
|
304
410
|
insertBreak(type, location) {
|
|
305
411
|
var _a;
|
|
306
412
|
var _b;
|
|
413
|
+
this.assertWritable();
|
|
307
414
|
const brk = breakItem(type === 'Page' ? 'page' : undefined);
|
|
308
415
|
if (location === 'Before' || location === 'After') {
|
|
309
416
|
const p = this.insertParagraph('', location);
|
|
@@ -331,11 +438,15 @@ export class Paragraph {
|
|
|
331
438
|
insertInlinePictureFromBase64(base64, location, options) {
|
|
332
439
|
var _a;
|
|
333
440
|
var _b;
|
|
441
|
+
this.assertWritable();
|
|
334
442
|
const part = this.parentBody.part;
|
|
335
443
|
if (!part)
|
|
336
444
|
throw new Docx4JException('This body has no part, so an image cannot be related to it');
|
|
337
|
-
const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
|
|
338
445
|
const tracker = this.changeTracker;
|
|
446
|
+
// refused before the image part is added, not after (CR-012 section 12.4)
|
|
447
|
+
if (location === 'Replace' && tracker)
|
|
448
|
+
this.assertPlaceable(0, this.text.length, 'replace');
|
|
449
|
+
const { run, drawing } = addImage(part, base64, this.parentBody.container, writableWidthEmu(this.parentBody.container), options);
|
|
339
450
|
// under tracking the picture is an insertion, and what it replaces is deleted as insertText's is
|
|
340
451
|
if (location === 'Replace' && tracker)
|
|
341
452
|
this.splice(0, this.text.length, '', 'before', WHOLE);
|
|
@@ -360,6 +471,7 @@ export class Paragraph {
|
|
|
360
471
|
*/
|
|
361
472
|
async insertOoxml(ooxml, location) {
|
|
362
473
|
var _a, _b;
|
|
474
|
+
this.assertWritable();
|
|
363
475
|
const preprocess = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.loadOptions.preprocessor;
|
|
364
476
|
const elements = await contentOf(ooxml, { preprocess: preprocess ? (doc) => preprocess(doc) : undefined, target: this.parentBody.part });
|
|
365
477
|
if (elements.length === 0)
|
|
@@ -370,12 +482,12 @@ export class Paragraph {
|
|
|
370
482
|
return [this];
|
|
371
483
|
}
|
|
372
484
|
if (location === 'Replace') {
|
|
373
|
-
const inserted = this.parentBody.insertElement(elements, 'Before', this);
|
|
485
|
+
const inserted = this.parentBody.insertElement(elements, 'Before', this, 'GeneralException', this.element);
|
|
374
486
|
void inserted;
|
|
375
487
|
this.delete();
|
|
376
488
|
}
|
|
377
489
|
else {
|
|
378
|
-
this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this);
|
|
490
|
+
this.parentBody.insertElement(elements, location === 'Start' ? 'Before' : location === 'End' ? 'After' : location, this, 'GeneralException');
|
|
379
491
|
}
|
|
380
492
|
return elements.map((el) => typeNameOf(el) === 'org_docx4j_wml.P'
|
|
381
493
|
? new Paragraph(el, this.container, this.parentBody)
|
|
@@ -383,6 +495,10 @@ export class Paragraph {
|
|
|
383
495
|
}
|
|
384
496
|
/** Matches within this paragraph. */
|
|
385
497
|
search(text, options) {
|
|
498
|
+
// Word's paragraph mark, ^p, is searched in the paragraph's whole range, which holds its mark (CR-014 phase 3), and so
|
|
499
|
+
// is a wildcard query, whose ? or class matches the mark too (check 59; CR-002 section 32, K7)
|
|
500
|
+
if ((options === null || options === void 0 ? void 0 : options.matchWildcards) || (!(options === null || options === void 0 ? void 0 : options.matchRegExp) && paragraphMarkPieces(text)))
|
|
501
|
+
return this.getRange('Whole').search(text, options);
|
|
386
502
|
return this.searchMatches(text, options).map((m) => m.range);
|
|
387
503
|
}
|
|
388
504
|
/**
|
|
@@ -391,8 +507,8 @@ export class Paragraph {
|
|
|
391
507
|
* and `Body.replaceText` the other.
|
|
392
508
|
*/
|
|
393
509
|
searchMatches(text, options) {
|
|
394
|
-
return
|
|
395
|
-
.map((match) => ({ range: new Range(this,
|
|
510
|
+
return officeMatches(this.segments({ objects: true }), text, options)
|
|
511
|
+
.map(({ start, end, match }) => ({ range: new Range(this, start, end), match }));
|
|
396
512
|
}
|
|
397
513
|
/**
|
|
398
514
|
* Replaces every match of `find` with `replace`, last match first so that the offsets of the
|
|
@@ -400,6 +516,9 @@ export class Paragraph {
|
|
|
400
516
|
* the package's `changeTrackingMode` is on.
|
|
401
517
|
*/
|
|
402
518
|
replaceText(find, replace, options) {
|
|
519
|
+
refuseMarkReplacement('Paragraph', find, options);
|
|
520
|
+
if (options === null || options === void 0 ? void 0 : options.matchWildcards)
|
|
521
|
+
refuseWildcardMarkReplacement('Paragraph', this.search(find, options));
|
|
403
522
|
const matches = this.searchMatches(find, options);
|
|
404
523
|
for (let i = matches.length - 1; i >= 0; i--) {
|
|
405
524
|
const { range, match } = matches[i];
|
|
@@ -414,24 +533,51 @@ export class Paragraph {
|
|
|
414
533
|
/**
|
|
415
534
|
* The paragraph's range: 'Whole' and 'Content' hold its whole content, the pictures at its start and
|
|
416
535
|
* end among them, so deleting it deletes them (CR-002 section 48); 'Start' and 'End' are collapsed.
|
|
536
|
+
* 'Whole' holds the paragraph's mark too, so its `text` ends with `markText`, as Office JS's does
|
|
537
|
+
* (check 48); 'Content' ends with the paragraph's text.
|
|
417
538
|
*/
|
|
418
539
|
getRange(location = 'Whole') {
|
|
419
540
|
const length = this.text.length;
|
|
420
541
|
if (location === 'Start')
|
|
421
542
|
return new Range(this, 0, 0);
|
|
422
543
|
if (location === 'End')
|
|
423
|
-
return new Range(this, length, length);
|
|
424
|
-
|
|
544
|
+
return new Range(this, length, length).atEndOf('End');
|
|
545
|
+
const range = new Range(this, 0, length, true, location === 'Whole');
|
|
546
|
+
return location === 'Content' ? range.atEndOf('Content') : range;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* The character Office JS gives for this paragraph's mark at the end of its 'Whole' range (check 48):
|
|
550
|
+
* `\t` for the last paragraph of a table cell, the cell's end; nothing for a mark tracked as deleted, which
|
|
551
|
+
* Word's default view doesn't show (CR-012 section 19, check 58), or as moved from, as the paragraph's runs read
|
|
552
|
+
* (CR-002 section 64); and `\r` for any other.
|
|
553
|
+
*/
|
|
554
|
+
get markText() {
|
|
555
|
+
if (lastInCell(this.p))
|
|
556
|
+
return '\t';
|
|
557
|
+
return shownInDefaultView(this.p) ? '\r' : '';
|
|
425
558
|
}
|
|
426
559
|
/**
|
|
427
560
|
* Removes the paragraph from its container. While the package tracks changes, the content
|
|
428
561
|
* becomes a `w:del` and the mark is marked deleted (`w:pPr/w:rPr/w:del`) instead, unless the
|
|
429
562
|
* whole paragraph was this author's own insertion, which Word simply takes back.
|
|
563
|
+
* The paragraph a table cell ends with, as Word reads it, stays where removing it would leave the
|
|
564
|
+
* cell without one, emptied, its properties kept; tracked, its mark is never marked deleted, as Word
|
|
565
|
+
* keeps a cell's last paragraph (check 52, cases 88 to 91; CR-002 section 55.6).
|
|
430
566
|
*/
|
|
431
567
|
delete() {
|
|
568
|
+
this.remove(true);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* `delete()`; with `keepCellEnd` false a cell's last paragraph goes as any other, for `Body.clearContent`'s
|
|
572
|
+
* callers, which put their own paragraph in next.
|
|
573
|
+
* @internal
|
|
574
|
+
*/
|
|
575
|
+
remove(keepCellEnd) {
|
|
576
|
+
this.assertWritable();
|
|
577
|
+
const cellEnd = keepCellEnd && endsCell(this.p);
|
|
432
578
|
const tracker = this.changeTracker;
|
|
433
579
|
if (tracker) {
|
|
434
|
-
this.trackedDelete(tracker);
|
|
580
|
+
this.trackedDelete(tracker, cellEnd);
|
|
435
581
|
return;
|
|
436
582
|
}
|
|
437
583
|
const i = this.index;
|
|
@@ -439,8 +585,17 @@ export class Paragraph {
|
|
|
439
585
|
return;
|
|
440
586
|
this.container.splice(i, 1);
|
|
441
587
|
this.releaseRelationships(this.element);
|
|
588
|
+
if (cellEnd && !this.cellStillEnds()) {
|
|
589
|
+
this.p.content = [];
|
|
590
|
+
this.container.splice(i, 0, this.element);
|
|
591
|
+
}
|
|
442
592
|
}
|
|
443
|
-
|
|
593
|
+
/** Whether the cell this paragraph was in ends with a paragraph without it, as Word reads it (CR-002 section 55). */
|
|
594
|
+
cellStillEnds() {
|
|
595
|
+
var _a;
|
|
596
|
+
return endsWithParagraph((_a = wrappingCellOf(this.p.PARENT)) === null || _a === void 0 ? void 0 : _a.content);
|
|
597
|
+
}
|
|
598
|
+
trackedDelete(tracker, cellEnd) {
|
|
444
599
|
var _a, _b;
|
|
445
600
|
if (markDeleted(this.p))
|
|
446
601
|
throw new Docx4JException('This paragraph is already marked deleted');
|
|
@@ -451,10 +606,17 @@ export class Paragraph {
|
|
|
451
606
|
const ownMark = (rPr === null || rPr === void 0 ? void 0 : rPr.ins) !== undefined && rPr.ins.author === tracker.author;
|
|
452
607
|
if (ownMark && ((_b = this.p.content) !== null && _b !== void 0 ? _b : []).length === 0) {
|
|
453
608
|
const i = this.index;
|
|
454
|
-
if (i >= 0)
|
|
609
|
+
if (i >= 0) {
|
|
455
610
|
this.container.splice(i, 1);
|
|
611
|
+
if (!cellEnd || this.cellStillEnds())
|
|
612
|
+
return;
|
|
613
|
+
this.container.splice(i, 0, this.element);
|
|
614
|
+
}
|
|
456
615
|
return;
|
|
457
616
|
}
|
|
617
|
+
// a cell's last mark: Word deletes the paragraph's text and never marks the mark (check 52, cases 90 and 91)
|
|
618
|
+
if (cellEnd)
|
|
619
|
+
return;
|
|
458
620
|
tracker.markParagraphDeleted(this.p);
|
|
459
621
|
// Word, deleting a paragraph mark ALONE under tracking, gives the next paragraph this one's
|
|
460
622
|
// properties as a w:pPrChange (CR-002 section 38 item 2, the editor's measure-word15.docx), so
|
|
@@ -469,13 +631,14 @@ export class Paragraph {
|
|
|
469
631
|
* what the cells of a deleted table row need (a `w:tc` must keep a block-level child, and Word
|
|
470
632
|
* writes `w:del` around every run and `w:pPr/w:rPr/w:del` on every mark inside a deleted row).
|
|
471
633
|
* The same primitives `delete()` uses, without its "take back my own insertion" step.
|
|
472
|
-
*
|
|
634
|
+
* A mark already deleted is left as it is, and the text is deleted still: a paragraph another author's
|
|
635
|
+
* deletion ended keeps text a range's deletion across it must take (CR-014 phase 2).
|
|
473
636
|
*/
|
|
474
637
|
markDeletedInPlace(tracker) {
|
|
475
|
-
|
|
476
|
-
return;
|
|
638
|
+
this.assertWritable();
|
|
477
639
|
this.deleteText(tracker, 0, this.text.length, WHOLE);
|
|
478
|
-
|
|
640
|
+
if (!markDeleted(this.p))
|
|
641
|
+
tracker.markParagraphDeleted(this.p);
|
|
479
642
|
}
|
|
480
643
|
/**
|
|
481
644
|
* Queues the relationships that content removed outright referenced (a picture's image, a link's
|
|
@@ -512,6 +675,7 @@ export class Paragraph {
|
|
|
512
675
|
pPr() {
|
|
513
676
|
var _a, _b;
|
|
514
677
|
var _c;
|
|
678
|
+
this.assertWritable();
|
|
515
679
|
const pPr = ((_a = (_c = this.p).pPr) !== null && _a !== void 0 ? _a : (_c.pPr = { TYPE_NAME: 'org_docx4j_wml.PPr' }));
|
|
516
680
|
linkParents(pPr, this.p);
|
|
517
681
|
(_b = this.changeTracker) === null || _b === void 0 ? void 0 : _b.recordPPrChange(pPr);
|
|
@@ -590,9 +754,49 @@ export class Paragraph {
|
|
|
590
754
|
ownInsertions.add(seg.run);
|
|
591
755
|
return { tracker, ownInsertions };
|
|
592
756
|
}
|
|
593
|
-
/**
|
|
757
|
+
/**
|
|
758
|
+
* The text segments (w:t, w:tab, ...) with their offsets in `text`; `{ objects: true }` adds the
|
|
759
|
+
* pictures and the hidden runs' items.
|
|
760
|
+
*/
|
|
594
761
|
segments(options) {
|
|
595
|
-
return segmentsOf(this.p, options);
|
|
762
|
+
return segmentsOf(this.p, { hidden: this.hiddenRuns(), ...options });
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* True for a paragraph in a text box (`w:txbxContent`), at a `textbox:` address (CR-012 D4). Such a view
|
|
766
|
+
* reads as any paragraph does and is read-only (D5): every write throws `NotSupportedError`.
|
|
767
|
+
*/
|
|
768
|
+
get inTextBox() {
|
|
769
|
+
let o = this.p.PARENT;
|
|
770
|
+
for (let guard = 0; o && guard < 256; guard++, o = o.PARENT) {
|
|
771
|
+
if (o.TYPE_NAME === 'org_docx4j_wml.CTTxbxContent')
|
|
772
|
+
return true;
|
|
773
|
+
}
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Refuses a write to a paragraph in a text box (CR-012 D5): a box is in each branch of its
|
|
778
|
+
* `mc:AlternateContent`, `wps:txbx` in the Choice and `v:textbox` in the Fallback, and nothing maps the
|
|
779
|
+
* one's paragraphs to the other's, so an edit could not reach every branch as CR-010's D2 has mutators do.
|
|
780
|
+
* And a write to a paragraph in a branch of a kept `mc:AlternateContent` at block, row or cell level
|
|
781
|
+
* (CR-013 D4), for the same reason. Internal: the content API's mutators call it first.
|
|
782
|
+
*/
|
|
783
|
+
assertWritable() {
|
|
784
|
+
if (this.inTextBox) {
|
|
785
|
+
throw new NotSupportedError('Paragraph', 'edit', 'this paragraph is in a text box, whose paragraphs are read-only (CR-012 D5): '
|
|
786
|
+
+ "writes into a text box would have to reach its other branch's box too, which nothing maps yet");
|
|
787
|
+
}
|
|
788
|
+
assertOutsideBranch(this.p, 'Paragraph');
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* True for a paragraph in the selected branch of a kept `mc:AlternateContent` at block, row or cell
|
|
792
|
+
* level, which readers see as Word does (CR-013 phase 2). Such a view is read-only (D4).
|
|
793
|
+
*/
|
|
794
|
+
get inBranch() {
|
|
795
|
+
return inBranch(this.p);
|
|
796
|
+
}
|
|
797
|
+
/** Which of this paragraph's runs are hidden: a run's own `w:vanish`, else its styles' (CR-012 phase 4). */
|
|
798
|
+
hiddenRuns() {
|
|
799
|
+
return this.parentBody.hiddenRuns(this.p);
|
|
596
800
|
}
|
|
597
801
|
/**
|
|
598
802
|
* Replaces the text in [start, end) with `text` and returns the range of the inserted text.
|
|
@@ -604,13 +808,313 @@ export class Paragraph {
|
|
|
604
808
|
* says the span holds (`WHOLE` for a paragraph's whole content), so a span of no text can still
|
|
605
809
|
* delete a picture (CR-002 section 48).
|
|
606
810
|
*/
|
|
607
|
-
splice(start, end, text, prefer = 'before', edges) {
|
|
608
|
-
|
|
609
|
-
|
|
811
|
+
splice(start, end, text, prefer = 'before', edges, lineBreaks) {
|
|
812
|
+
this.assertWritable();
|
|
813
|
+
// text into a block control showing its placeholder takes the placeholder's place (CR-002 section 63)
|
|
814
|
+
const placeholder = text === '' ? undefined : this.placeholderControl;
|
|
815
|
+
if (placeholder)
|
|
816
|
+
return placeholder.writePlaceholder(text, lineBreaks ? { lineBreaks } : {});
|
|
817
|
+
// every text write into a bound control writes its node (CR-002 section 63, D7), as ContentControl.insertText did
|
|
818
|
+
const bound = this.boundControlsAt(start, end);
|
|
819
|
+
const range = this.spliceWrite(start, end, text, prefer, edges, lineBreaks);
|
|
820
|
+
for (const control of bound)
|
|
821
|
+
control.syncBoundNode();
|
|
822
|
+
return range;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* The controls bound by `w:dataBinding` that [start, end) is in: those holding the paragraph, and the run-level ones
|
|
826
|
+
* holding text there.
|
|
827
|
+
*/
|
|
828
|
+
boundControlsAt(start, end, ancestorsOnly = false) {
|
|
829
|
+
const out = [];
|
|
830
|
+
const seen = new Set();
|
|
831
|
+
const add = (o) => {
|
|
832
|
+
const tn = o.TYPE_NAME;
|
|
833
|
+
if (tn === undefined || !SDT_VALUE_TYPES.has(tn) || seen.has(o))
|
|
834
|
+
return;
|
|
835
|
+
seen.add(o);
|
|
836
|
+
if (sdtProperty(o.sdtPr, 'dataBinding') === undefined)
|
|
837
|
+
return;
|
|
838
|
+
try {
|
|
839
|
+
out.push(controlViewOf(o, this.parentBody));
|
|
840
|
+
}
|
|
841
|
+
catch {
|
|
842
|
+
// not where its PARENT says: nothing to write
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
for (let o = this.p.PARENT; o; o = o.PARENT)
|
|
846
|
+
add(o);
|
|
847
|
+
if (ancestorsOnly || !holdsBoundRunControl(this.p))
|
|
848
|
+
return out;
|
|
849
|
+
for (const seg of this.segments({ objects: true })) {
|
|
850
|
+
if (seg.end < start || seg.start > end)
|
|
851
|
+
continue;
|
|
852
|
+
for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT)
|
|
853
|
+
add(o);
|
|
854
|
+
}
|
|
855
|
+
return out;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* @internal Text written at the paragraph's End by Office JS's routes to it: `insertText('End')`, its End range at any
|
|
859
|
+
* location, its Content range's End and After, and so a body's or a cell's `insertText('End')` (CR-002 sections 63.24
|
|
860
|
+
* to 63.27; checks 69 and 70). As Word writes it (D12): after everything the paragraph holds, into none of it - a
|
|
861
|
+
* 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
|
|
862
|
+
* own, or the last run extended. The text takes the last item's run properties where that item is a run holding text,
|
|
863
|
+
* so after a hidden run it is hidden and its range reads nothing (D13); otherwise it has none, and with `plain` (the End
|
|
864
|
+
* range's After, D14) none either. Word's proofing marks, `w:proofErr`, are no item (a choice: Word redoes them).
|
|
865
|
+
* Tracked, the run is a `w:ins`, or joins this author's own `w:ins` where one ends the paragraph. A line break makes
|
|
866
|
+
* paragraphs, as `splice` does (CR-002 section 60 E1); text so broken after a hidden run is written visible (a choice,
|
|
867
|
+
* unmeasured).
|
|
868
|
+
*/
|
|
869
|
+
writeAtEnd(text, plain = false) {
|
|
870
|
+
var _a, _b;
|
|
871
|
+
var _c;
|
|
872
|
+
this.assertWritable();
|
|
873
|
+
const placeholder = this.placeholderControl;
|
|
874
|
+
if (placeholder)
|
|
875
|
+
return placeholder.writePlaceholder(text);
|
|
876
|
+
const length = this.text.length;
|
|
877
|
+
text = dropExcluded(text);
|
|
878
|
+
if (text === '')
|
|
879
|
+
return new Range(this, length, length);
|
|
880
|
+
let mode = 'paragraphs';
|
|
881
|
+
const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
|
|
882
|
+
if (lines)
|
|
883
|
+
mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
|
|
884
|
+
text = forWriter(text, mode);
|
|
885
|
+
const broken = mode === 'paragraphs' && /[\r\n]/.test(text) ? linesOf(text) : undefined;
|
|
886
|
+
const joined = broken ? broken.join('') : text;
|
|
887
|
+
if (joined === '' && !broken)
|
|
888
|
+
return new Range(this, length, length);
|
|
889
|
+
const bound = this.boundControlsAt(length, length, true);
|
|
890
|
+
const content = ((_a = (_c = this.p).content) !== null && _a !== void 0 ? _a : (_c.content = []));
|
|
891
|
+
let at = content.length;
|
|
892
|
+
while (at > 0 && typeNameOf(content[at - 1]) === 'org_docx4j_wml.ProofErr')
|
|
893
|
+
at--;
|
|
894
|
+
const last = at > 0 ? content[at - 1] : undefined;
|
|
895
|
+
const textRun = (item) => {
|
|
896
|
+
var _a;
|
|
897
|
+
if (!item || typeNameOf(item) !== 'org_docx4j_wml.R')
|
|
898
|
+
return undefined;
|
|
899
|
+
const run = item.value;
|
|
900
|
+
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;
|
|
901
|
+
};
|
|
902
|
+
const hidden = this.hiddenRuns();
|
|
903
|
+
// the run whose properties the text takes, and which it may extend: a run holding text, unless `plain`
|
|
904
|
+
let source = plain ? undefined : textRun(last);
|
|
905
|
+
if (source && broken && hidden(source))
|
|
906
|
+
source = undefined;
|
|
907
|
+
const items = officeItems(joined);
|
|
908
|
+
const tracker = this.changeTracker;
|
|
909
|
+
let written;
|
|
910
|
+
const own = tracker && last && typeNameOf(last) === 'org_docx4j_wml.RunIns' && tracker.ownInsertion({ kind: 'ins', value: last.value });
|
|
911
|
+
if (items.length === 0) {
|
|
912
|
+
// nothing but line breaks: only the paragraphs they make
|
|
913
|
+
}
|
|
914
|
+
else if (own) {
|
|
915
|
+
// this author's own insertion ends the paragraph: the text joins it, after its last run, in that run's look
|
|
916
|
+
const value = last.value;
|
|
917
|
+
const holder = (_b = runItemsOf(value)) !== null && _b !== void 0 ? _b : (value.customXmlOrSmartTagOrSdt = []);
|
|
918
|
+
const inner = plain ? undefined : textRun(holder[holder.length - 1]);
|
|
919
|
+
written = this.appendTo(holder, inner, items, value);
|
|
920
|
+
}
|
|
921
|
+
else if (tracker) {
|
|
922
|
+
const ins = tracker.ins([runOf(items, copyRPr(source === null || source === void 0 ? void 0 : source.rPr))]);
|
|
923
|
+
content.splice(at, 0, ins);
|
|
924
|
+
linkParents(ins, this.p);
|
|
925
|
+
written = runsOf(ins.value)[0].value;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
written = this.appendTo(content, source, items, this.p, at);
|
|
929
|
+
}
|
|
930
|
+
for (const control of bound)
|
|
931
|
+
control.syncBoundNode();
|
|
932
|
+
if (broken) {
|
|
933
|
+
const offsets = [];
|
|
934
|
+
let end = length;
|
|
935
|
+
for (const line of broken.slice(0, -1))
|
|
936
|
+
offsets.push(end += line.length);
|
|
937
|
+
const { first, last: tail } = breakParagraphAt(this, offsets);
|
|
938
|
+
return new Range(first, length, broken[broken.length - 1].length, false, false, tail);
|
|
939
|
+
}
|
|
940
|
+
return written && hidden(written) ? new Range(this, length, length) : new Range(this, length, length + joined.length);
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* `items` put at `at` in `list` (its end by default): onto the end of `run`, which is in `list` right before `at`,
|
|
944
|
+
* merging into its last `w:t`, or as a new run in its properties where `run` is undefined. Returns the run written.
|
|
945
|
+
*/
|
|
946
|
+
appendTo(list, run, items, parent, at = list.length) {
|
|
947
|
+
var _a, _b, _c, _d, _e;
|
|
948
|
+
if (run) {
|
|
949
|
+
const own = ((_a = run.content) !== null && _a !== void 0 ? _a : (run.content = []));
|
|
950
|
+
const tail = own[own.length - 1];
|
|
951
|
+
const head = items[0];
|
|
952
|
+
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') {
|
|
953
|
+
setText(tail.value, ((_d = tail.value.value) !== null && _d !== void 0 ? _d : '') + ((_e = head.value.value) !== null && _e !== void 0 ? _e : ''));
|
|
954
|
+
items = items.slice(1);
|
|
955
|
+
}
|
|
956
|
+
own.push(...items);
|
|
957
|
+
linkParents(items, run);
|
|
958
|
+
return run;
|
|
959
|
+
}
|
|
960
|
+
const added = runOf(items);
|
|
961
|
+
list.splice(at, 0, added);
|
|
962
|
+
linkParents(added, parent);
|
|
963
|
+
return added.value;
|
|
964
|
+
}
|
|
965
|
+
/** `splice`'s write. */
|
|
966
|
+
spliceWrite(start, end, text, prefer, edges, lineBreaks) {
|
|
967
|
+
// Office JS's characters written back (CR-002 section 60): the characters XML 1.0 excludes go (E3), a line feed or
|
|
968
|
+
// carriage return is a paragraph break (E1) unless the writer or the control takes it otherwise, and every
|
|
969
|
+
// character that is an element is written as it (D1)
|
|
970
|
+
text = dropExcluded(text);
|
|
971
|
+
if (text === '')
|
|
972
|
+
return this.spliceText(start, end, text, prefer, edges);
|
|
973
|
+
const from = Math.max(0, Math.min(start, this.text.length));
|
|
974
|
+
const landing = this.landingSegment(from, prefer, end > from);
|
|
975
|
+
const runControl = this.runControlOf(landing);
|
|
976
|
+
let mode = lineBreaks !== null && lineBreaks !== void 0 ? lineBreaks : 'paragraphs';
|
|
977
|
+
if (runControl) {
|
|
978
|
+
text = textForRunControl(runControl, text); // F4 and F3
|
|
979
|
+
}
|
|
980
|
+
else if (lineBreaks === undefined) {
|
|
981
|
+
const lines = this.blockControl ? plainTextLinesOf(this.blockControl) : undefined;
|
|
982
|
+
if (lines)
|
|
983
|
+
mode = lines === 'multi' ? 'multi-line' : 'single-line'; // section 61 item 3, F3
|
|
984
|
+
}
|
|
985
|
+
text = forWriter(text, mode);
|
|
986
|
+
if (mode === 'paragraphs' && /[\r\n]/.test(text))
|
|
987
|
+
return this.spliceLines(start, end, linesOf(text), prefer, edges, landing);
|
|
988
|
+
const range = this.spliceText(start, end, text, prefer, edges);
|
|
989
|
+
this.writeElements(range.start, range.start + text.length);
|
|
990
|
+
return range;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Text of several lines put in place of [start, end) as Word's insertText puts it (CR-002 section 60 E1): the lines'
|
|
994
|
+
* text, then a paragraph break after each line but the last, the paragraphs keeping this one's properties; tracked,
|
|
995
|
+
* each new mark inserted. Refused before anything changes where a paragraph can't be split there.
|
|
996
|
+
*/
|
|
997
|
+
spliceLines(start, end, lines, prefer, edges, landing) {
|
|
998
|
+
this.assertBreakable(landing);
|
|
999
|
+
const joined = lines.join('');
|
|
1000
|
+
const range = this.spliceText(start, end, joined, prefer, edges);
|
|
1001
|
+
const from = range.start;
|
|
1002
|
+
this.writeElements(from, from + joined.length);
|
|
1003
|
+
const offsets = [];
|
|
1004
|
+
let at = from;
|
|
1005
|
+
for (const line of lines.slice(0, -1))
|
|
1006
|
+
offsets.push(at += line.length);
|
|
1007
|
+
const { first, last } = breakParagraphAt(this, offsets);
|
|
1008
|
+
return new Range(first, from, lines[lines.length - 1].length, false, false, last);
|
|
1009
|
+
}
|
|
1010
|
+
/** @internal Refuses, before anything changes, a paragraph break at `at` where none can be made (E1; `Range`'s Replace across paragraphs). */
|
|
1011
|
+
assertBreakableAt(at, prefer) {
|
|
1012
|
+
this.assertBreakable(this.landingSegment(at, prefer));
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* The segment text put at `at` goes into, as `spliceText` chooses it: a replacement's, the one holding the first
|
|
1016
|
+
* replaced character; else the one containing `at`, else the nearer neighbour.
|
|
1017
|
+
*/
|
|
1018
|
+
landingSegment(at, prefer, replacing = false) {
|
|
1019
|
+
const segs = this.segments();
|
|
1020
|
+
if (replacing) {
|
|
1021
|
+
const first = segs.find((s) => s.start <= at && at < s.end);
|
|
1022
|
+
if (first)
|
|
1023
|
+
return first;
|
|
1024
|
+
}
|
|
1025
|
+
const containing = segs.find((s) => s.editable && s.start < at && at < s.end);
|
|
1026
|
+
const ending = segs.find((s) => s.editable && s.end === at);
|
|
1027
|
+
const starting = segs.find((s) => s.editable && s.start === at);
|
|
1028
|
+
return containing !== null && containing !== void 0 ? containing : (prefer === 'after' ? starting !== null && starting !== void 0 ? starting : ending : ending !== null && ending !== void 0 ? ending : starting);
|
|
1029
|
+
}
|
|
1030
|
+
/** The run-level content control a segment's run is in, the innermost, as a view; undefined for none. */
|
|
1031
|
+
runControlOf(seg) {
|
|
1032
|
+
var _a;
|
|
1033
|
+
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) {
|
|
1034
|
+
if (o.TYPE_NAME === 'org_docx4j_wml.SdtRun')
|
|
1035
|
+
return controlViewOf(o, this.parentBody);
|
|
1036
|
+
}
|
|
1037
|
+
return undefined;
|
|
1038
|
+
}
|
|
1039
|
+
/** The block-level content control holding this paragraph directly, as a view; undefined for none. */
|
|
1040
|
+
get blockControl() {
|
|
1041
|
+
const content = this.p.PARENT;
|
|
1042
|
+
const sdt = (content === null || content === void 0 ? void 0 : content.TYPE_NAME) === 'org_docx4j_wml.SdtContentBlock' ? content.PARENT : undefined;
|
|
1043
|
+
return (sdt === null || sdt === void 0 ? void 0 : sdt.TYPE_NAME) === 'org_docx4j_wml.SdtBlock' ? controlViewOf(sdt, this.parentBody) : undefined;
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Refuses, before anything changes, a paragraph break where the text lands inside something a paragraph can't be
|
|
1047
|
+
* split in: a content control, an equation, a kept mc:AlternateContent (CR-002 section 60 E1, as `splitParagraph`
|
|
1048
|
+
* refuses one). A field's result is split: a complex field's runs across the paragraphs, a `w:fldSimple` made a
|
|
1049
|
+
* complex field first (section 62.2 D1).
|
|
1050
|
+
*/
|
|
1051
|
+
assertBreakable(seg) {
|
|
1052
|
+
var _a;
|
|
1053
|
+
if (!seg)
|
|
1054
|
+
return;
|
|
1055
|
+
const refuse = (what) => {
|
|
1056
|
+
throw new NotSupportedError('Paragraph', 'insertText', `a line feed or carriage return is a paragraph break (CR-002 section 60, E1), `
|
|
1057
|
+
+ `and this text goes inside ${what}, which can't be cut in two`);
|
|
1058
|
+
};
|
|
1059
|
+
if (seg.alternate || seg.math)
|
|
1060
|
+
refuse('an equation or an mc:AlternateContent');
|
|
1061
|
+
for (let o = seg.run.PARENT; o && o !== this.p; o = o.PARENT) {
|
|
1062
|
+
const tn = (_a = o.TYPE_NAME) !== null && _a !== void 0 ? _a : '';
|
|
1063
|
+
if (!BREAKABLE.has(tn))
|
|
1064
|
+
refuse(`a ${tn.replace('org_docx4j_wml.', '')}`);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* The characters of [from, to) that are elements written as the elements they read as (CR-002 section 60 D1), each
|
|
1069
|
+
* in its run where it stood: a w:t holding one is cut around it. Characters outside the span are left as they are.
|
|
1070
|
+
*/
|
|
1071
|
+
writeElements(from, to) {
|
|
1072
|
+
var _a;
|
|
1073
|
+
if (to <= from)
|
|
1074
|
+
return;
|
|
1075
|
+
for (const seg of this.segments().reverse()) {
|
|
1076
|
+
if (!seg.editable || seg.end <= from || seg.start >= to)
|
|
1077
|
+
continue;
|
|
1078
|
+
const value = seg.text;
|
|
1079
|
+
const a = Math.max(0, from - seg.start);
|
|
1080
|
+
const b = Math.min(value.length, to - seg.start);
|
|
1081
|
+
const inside = value.substring(a, b);
|
|
1082
|
+
if (!holdsElementCharacters(inside))
|
|
1083
|
+
continue;
|
|
1084
|
+
const items = [
|
|
1085
|
+
...(a > 0 ? [textItem(value.substring(0, a))] : []),
|
|
1086
|
+
...officeItems(inside),
|
|
1087
|
+
...(b < value.length ? [textItem(value.substring(b))] : []),
|
|
1088
|
+
];
|
|
1089
|
+
const parent = (_a = seg.item.value.PARENT) !== null && _a !== void 0 ? _a : seg.run;
|
|
1090
|
+
seg.owner.splice(seg.index, 1, ...items);
|
|
1091
|
+
linkParents(items, parent);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* The first run's properties, for text with no run beside it to take its look from; none where that run is hidden,
|
|
1096
|
+
* so that text put into a paragraph whose every run is hidden can be seen (CR-002 section 60: the text was hidden
|
|
1097
|
+
* with it, so read as nothing, and its characters escaped the map).
|
|
1098
|
+
*/
|
|
1099
|
+
visibleFirstRunRPr() {
|
|
1100
|
+
var _a;
|
|
1101
|
+
const first = (_a = runsOf(this.p)[0]) === null || _a === void 0 ? void 0 : _a.value;
|
|
1102
|
+
return first && !this.hiddenRuns()(first) ? first.rPr : undefined;
|
|
1103
|
+
}
|
|
1104
|
+
/** `splice` before section 60: the text put in as it is, as one stretch of a w:t's text. */
|
|
1105
|
+
spliceText(start, end, text, prefer = 'before', edges) {
|
|
1106
|
+
var _a, _b, _c, _d, _e;
|
|
1107
|
+
var _f;
|
|
610
1108
|
const length = this.text.length;
|
|
611
1109
|
start = Math.max(0, Math.min(start, length));
|
|
612
1110
|
end = Math.max(start, Math.min(end, length));
|
|
613
1111
|
const tracker = this.changeTracker;
|
|
1112
|
+
this.assertPlaceable(start, end, tracker === undefined ? false : text.length > 0 ? 'replace' : 'delete');
|
|
1113
|
+
// a run-level control showing its placeholder that the span holds whole goes with it, as Word takes one; tracked,
|
|
1114
|
+
// `deleteText` marks it deleted (CR-002 section 63, D3; check 63 cases 19, 20 and 26)
|
|
1115
|
+
if (!tracker && (end > start || edges))
|
|
1116
|
+
for (const control of this.placeholderControlsIn(start, end, edges))
|
|
1117
|
+
control.takeWhole();
|
|
614
1118
|
if (tracker) {
|
|
615
1119
|
// a replacement is the w:del first and the w:ins after it, as Word writes one
|
|
616
1120
|
const deletions = end > start || edges ? this.deleteText(tracker, start, end, edges) : [];
|
|
@@ -621,14 +1125,16 @@ export class Paragraph {
|
|
|
621
1125
|
let segs = this.segments();
|
|
622
1126
|
let inserted = false;
|
|
623
1127
|
const objects = this.segments({ objects: true }).filter((s) => s.object && objectInSpan(s.start, start, end, edges));
|
|
1128
|
+
// a comment's reference in the span goes with the text, and its comment with it (CR-014 section 12.6)
|
|
1129
|
+
const references = end > start || edges ? this.commentReferencesIn(start, end, edges) : [];
|
|
624
1130
|
// A replacement that begins inside a revision is a deletion and then the new text typed there, so
|
|
625
1131
|
// the new text is plain and splits the revision, as typed text is (test/README.md check 21), rather
|
|
626
1132
|
// than joining the revision it replaced part of.
|
|
627
1133
|
if (end > start && text.length > 0 && ((_a = segs.find((s) => s.start <= start && start < s.end)) === null || _a === void 0 ? void 0 : _a.revision)) {
|
|
628
|
-
this.
|
|
629
|
-
return this.
|
|
1134
|
+
this.spliceText(start, end, '', prefer, edges);
|
|
1135
|
+
return this.spliceText(start, start, text);
|
|
630
1136
|
}
|
|
631
|
-
if (end > start || objects.length > 0) {
|
|
1137
|
+
if (end > start || objects.length > 0 || references.length > 0) {
|
|
632
1138
|
// Replace: the new text takes the place (and formatting) of the first replaced character, as Word does.
|
|
633
1139
|
const first = segs.find((s) => s.start <= start && start < s.end);
|
|
634
1140
|
const toRemove = [];
|
|
@@ -656,6 +1162,13 @@ export class Paragraph {
|
|
|
656
1162
|
if (i >= 0)
|
|
657
1163
|
seg.owner.splice(i, 1);
|
|
658
1164
|
}
|
|
1165
|
+
removeEmptiedAlternates([...toRemove, ...objects]);
|
|
1166
|
+
for (const { run, owner, id } of references) {
|
|
1167
|
+
const i = owner.indexOf(run);
|
|
1168
|
+
if (i >= 0)
|
|
1169
|
+
owner.splice(i, 1);
|
|
1170
|
+
this.queueCommentRemoval(id);
|
|
1171
|
+
}
|
|
659
1172
|
this.releaseRelationships(objects.map((seg) => seg.item));
|
|
660
1173
|
if (inserted && text.length > 0) {
|
|
661
1174
|
this.removeEmptyRuns();
|
|
@@ -696,7 +1209,7 @@ export class Paragraph {
|
|
|
696
1209
|
const before = [...segs].reverse().find((s) => s.end <= start);
|
|
697
1210
|
const after = segs.find((s) => s.start >= start);
|
|
698
1211
|
const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
|
|
699
|
-
const firstRun =
|
|
1212
|
+
const firstRun = this.visibleFirstRunRPr();
|
|
700
1213
|
const rPr = (neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) ? deepCopy(neighbour.run.rPr) : firstRun ? deepCopy(firstRun) : undefined;
|
|
701
1214
|
const run = runOf([textItem(text)], rPr);
|
|
702
1215
|
if (neighbour === before && before)
|
|
@@ -704,7 +1217,7 @@ export class Paragraph {
|
|
|
704
1217
|
else if (after)
|
|
705
1218
|
after.runOwner.splice(after.runIndex, 0, run);
|
|
706
1219
|
else {
|
|
707
|
-
const content = ((
|
|
1220
|
+
const content = ((_e = (_f = this.p).content) !== null && _e !== void 0 ? _e : (_f.content = []));
|
|
708
1221
|
if (start === 0)
|
|
709
1222
|
content.unshift(run);
|
|
710
1223
|
else
|
|
@@ -722,8 +1235,10 @@ export class Paragraph {
|
|
|
722
1235
|
insertItemsAt(offset, items) {
|
|
723
1236
|
var _a, _b, _c, _d;
|
|
724
1237
|
var _e;
|
|
1238
|
+
this.assertWritable();
|
|
725
1239
|
if (items.length === 0)
|
|
726
1240
|
return;
|
|
1241
|
+
this.assertPlaceable(offset, offset, false);
|
|
727
1242
|
const tracker = this.changeTracker;
|
|
728
1243
|
if (tracker) {
|
|
729
1244
|
// With tracking on, what goes in is an insertion: its runs in w:ins, in a link, a control or a
|
|
@@ -753,18 +1268,46 @@ export class Paragraph {
|
|
|
753
1268
|
/** Splits the run at a text offset so that [offset, ...) begins a run; returns nothing when the offset is already a boundary. */
|
|
754
1269
|
splitAt(offset) {
|
|
755
1270
|
var _a;
|
|
1271
|
+
this.assertWritable();
|
|
1272
|
+
this.assertPlaceable(offset, offset, false);
|
|
756
1273
|
const seg = this.segments().find((s) => s.editable && s.start < offset && offset < s.end);
|
|
757
1274
|
if (!seg)
|
|
758
1275
|
return;
|
|
759
1276
|
const at = offset - seg.start;
|
|
760
1277
|
const t = seg.item.value;
|
|
761
1278
|
const head = seg.text.substring(0, at);
|
|
762
|
-
const tail = seg.text.substring(at);
|
|
1279
|
+
const tail = textItem(seg.text.substring(at));
|
|
763
1280
|
setText(t, head);
|
|
764
|
-
|
|
765
|
-
|
|
1281
|
+
seg.owner.splice(seg.index + 1, 0, tail);
|
|
1282
|
+
linkParents(tail, (_a = t.PARENT) !== null && _a !== void 0 ? _a : seg.run);
|
|
1283
|
+
this.splitRunAtItem(seg, seg.owner, seg.index + 1);
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Splits `seg`'s run so that the items of `owner` from `index` on begin a run of their own, behind it.
|
|
1287
|
+
* `owner` is the run's content, or the selected branch of a run-level `mc:AlternateContent` in it
|
|
1288
|
+
* (CR-012 section 12.2): a run holds the element whole, so the branch splits with it, as the editor's
|
|
1289
|
+
* ED-009 section 3.4 has it. The first piece keeps the element, its other branches as they were; the
|
|
1290
|
+
* second gets an element of its own, the same branches in the same order with the same `Requires`, the
|
|
1291
|
+
* selected one holding the rest of its items and the others empty, so that a reader taking any branch
|
|
1292
|
+
* takes the same one in both. The run's items after the element go with the second piece.
|
|
1293
|
+
*/
|
|
1294
|
+
splitRunAtItem(seg, owner, index) {
|
|
1295
|
+
var _a, _b;
|
|
1296
|
+
const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && owner === seg.alternate.branch ? seg.alternate : undefined;
|
|
1297
|
+
let moved;
|
|
1298
|
+
if (alternate) {
|
|
1299
|
+
const piece = alternatePiece(alternate, owner.splice(index));
|
|
1300
|
+
const at = alternate.owner.indexOf(alternate.element);
|
|
1301
|
+
moved = [piece, ...alternate.owner.splice(at + 1)];
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
moved = owner.splice(index);
|
|
1305
|
+
}
|
|
1306
|
+
if (moved.length === 0)
|
|
1307
|
+
return;
|
|
1308
|
+
const second = runOf(moved, seg.run.rPr ? deepCopy(seg.run.rPr) : undefined);
|
|
766
1309
|
seg.runOwner.splice(seg.runIndex + 1, 0, second);
|
|
767
|
-
linkParents(second, (
|
|
1310
|
+
linkParents(second, (_b = seg.run.PARENT) !== null && _b !== void 0 ? _b : this.p);
|
|
768
1311
|
}
|
|
769
1312
|
// --- lists (CR-002 phase H) ---
|
|
770
1313
|
/**
|
|
@@ -801,14 +1344,17 @@ export class Paragraph {
|
|
|
801
1344
|
* unmarshalled (CR-002 section 17).
|
|
802
1345
|
*/
|
|
803
1346
|
startNewList(options) {
|
|
1347
|
+
this.assertWritable();
|
|
804
1348
|
return startNewList(this, options);
|
|
805
1349
|
}
|
|
806
1350
|
/** Office JS `paragraph.attachToList(listId, level)`: this paragraph joins that list. */
|
|
807
1351
|
attachToList(listId, level = 0) {
|
|
1352
|
+
this.assertWritable();
|
|
808
1353
|
return attachToList(this, listId, level);
|
|
809
1354
|
}
|
|
810
1355
|
/** Office JS `paragraph.detachFromList()`: this paragraph leaves its list. */
|
|
811
1356
|
detachFromList() {
|
|
1357
|
+
this.assertWritable();
|
|
812
1358
|
detachFromList(this);
|
|
813
1359
|
}
|
|
814
1360
|
/**
|
|
@@ -816,6 +1362,7 @@ export class Paragraph {
|
|
|
816
1362
|
* `w:abstractNum` with a `w:startOverride` (extension; `List.restart()` is the verb).
|
|
817
1363
|
*/
|
|
818
1364
|
restartList() {
|
|
1365
|
+
this.assertWritable();
|
|
819
1366
|
const list = this.list;
|
|
820
1367
|
const level = this.listItem.level;
|
|
821
1368
|
const restarted = list.restart();
|
|
@@ -829,6 +1376,7 @@ export class Paragraph {
|
|
|
829
1376
|
}
|
|
830
1377
|
/** Comments the whole paragraph (sugar over `getRange().insertComment`). */
|
|
831
1378
|
async insertComment(content, options) {
|
|
1379
|
+
this.assertWritable();
|
|
832
1380
|
return commentApi().insertComment(this.getRange(), content, options);
|
|
833
1381
|
}
|
|
834
1382
|
// --- content controls (CR-002 phase E) ---
|
|
@@ -839,18 +1387,32 @@ export class Paragraph {
|
|
|
839
1387
|
*/
|
|
840
1388
|
insertContentControl(kind) {
|
|
841
1389
|
var _a;
|
|
1390
|
+
this.assertWritable();
|
|
842
1391
|
const at = this.index;
|
|
843
1392
|
if (at < 0)
|
|
844
1393
|
throw new Docx4JException('This paragraph is not in its container any more');
|
|
1394
|
+
// the paragraph's parent read before the builder makes the control its parent: read after, it was the
|
|
1395
|
+
// control's own content, and the control's PARENT pointed into itself (found in CR-012 phase 4)
|
|
1396
|
+
const parent = (_a = this.p.PARENT) !== null && _a !== void 0 ? _a : this.parentBody.container;
|
|
1397
|
+
// a bound block control holds one paragraph (CR-002 section 61): checked before sdtOf, which takes the paragraph
|
|
1398
|
+
assertControlHolds(this.parentBody, parent, this.container.filter((e) => e !== this.element), [BLOCK_CONTROL], undefined, ['Paragraph', 'insertContentControl']);
|
|
1399
|
+
const empty = this.segments({ objects: true }).length === 0;
|
|
845
1400
|
const sdt = sdtOf([this.element], { kind: sdtKindFor(kind), id: nextSdtId(controlIdScope(this.parentBody)), form: 'block' });
|
|
846
1401
|
this.container.splice(at, 1, sdt);
|
|
847
|
-
linkParents(sdt,
|
|
1402
|
+
linkParents(sdt, parent);
|
|
848
1403
|
declareSdtExtensions(this.parentBody, sdt);
|
|
849
1404
|
// under tracking the control itself is the insertion, the paragraph what it was (check 30)
|
|
850
1405
|
const tracker = this.changeTracker;
|
|
851
1406
|
if (tracker)
|
|
852
1407
|
markHolderInserted(tracker, sdt, this.container, () => { });
|
|
853
|
-
|
|
1408
|
+
const control = new ContentControl(sdt, this.container, this.parentBody);
|
|
1409
|
+
// around an empty paragraph, a control showing Word's default placeholder, as Word's (CR-002 section 63, D8; check 64);
|
|
1410
|
+
// a picture control Word's form, the flag and the placeholder picture (CR-005 section 10, D2; docx4j's check 71 case 8)
|
|
1411
|
+
if (empty && showsPlaceholderWhenMade(kind))
|
|
1412
|
+
control.showPlaceholder();
|
|
1413
|
+
else if (empty && kind === 'Picture')
|
|
1414
|
+
showPicturePlaceholder(control);
|
|
1415
|
+
return control;
|
|
854
1416
|
}
|
|
855
1417
|
removeEmptyRuns() {
|
|
856
1418
|
var _a;
|
|
@@ -862,6 +1424,16 @@ export class Paragraph {
|
|
|
862
1424
|
if (!v.content || v.content.length === 0)
|
|
863
1425
|
items.splice(i, 1);
|
|
864
1426
|
}
|
|
1427
|
+
else if (isAlternateContent(el)) {
|
|
1428
|
+
// among the runs: a selected branch an edit has emptied takes the element, every branch with
|
|
1429
|
+
// it, as a deletion of all of a run-level element's text does (CR-012 section 12.2 item 4)
|
|
1430
|
+
const branch = selectedBranchOf(el.value);
|
|
1431
|
+
if (!branch || branch.length === 0)
|
|
1432
|
+
continue;
|
|
1433
|
+
prune(branch);
|
|
1434
|
+
if (branch.length === 0)
|
|
1435
|
+
items.splice(i, 1);
|
|
1436
|
+
}
|
|
865
1437
|
else if (v.TYPE_NAME !== 'org_docx4j_wml.P') {
|
|
866
1438
|
const nested = runItemsOf(v);
|
|
867
1439
|
if (nested) {
|
|
@@ -881,16 +1453,28 @@ export class Paragraph {
|
|
|
881
1453
|
* turned into `w:delText`, and each run of consecutive ones moved into one `w:del`. Text this
|
|
882
1454
|
* author had inserted is simply removed, as Word does, rather than nested in a `w:del`.
|
|
883
1455
|
* The pictures in the span (`splice`'s `edges`) are deleted with the text, each in a run of its own
|
|
884
|
-
* where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48).
|
|
1456
|
+
* where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48). An equation is deleted where
|
|
1457
|
+
* it stands, in Word's form, a `w:del` in each math run and each structure's `m:ctrlPr` (`markEquationDeleted`;
|
|
1458
|
+
* 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
|
|
1459
|
+
* `w:del` whole, as every equation did before.
|
|
885
1460
|
* Returns the `w:del` elements it made, in document order.
|
|
886
1461
|
*/
|
|
887
1462
|
deleteText(tracker, start, end, edges) {
|
|
888
|
-
var _a, _b, _c, _d, _e;
|
|
1463
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1464
|
+
// a run-level control showing its placeholder that the span holds whole is marked deleted whole, in Word's markers,
|
|
1465
|
+
// its placeholder gone unmarked (CR-002 section 63, D3; check 63's run 2, cases 45, 46 and 52)
|
|
1466
|
+
for (const control of this.placeholderControlsIn(start, end, edges))
|
|
1467
|
+
control.takeWhole();
|
|
889
1468
|
const targets = this.isolate(start, end, edges);
|
|
890
1469
|
for (const seg of targets)
|
|
891
1470
|
tracker.assertEditable(seg.revision);
|
|
892
1471
|
const list = [];
|
|
1472
|
+
const equations = [];
|
|
893
1473
|
for (const seg of targets) {
|
|
1474
|
+
if (seg.math && !tracker.ownInsertion(seg.revision) && equationRevisionIn(seg.item.value) === undefined) {
|
|
1475
|
+
equations.push(seg.item.value);
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
894
1478
|
const run = seg.runOwner[seg.runIndex];
|
|
895
1479
|
if (list.length > 0 && list[list.length - 1].run === run)
|
|
896
1480
|
continue;
|
|
@@ -899,6 +1483,22 @@ export class Paragraph {
|
|
|
899
1483
|
target.revision = seg.revision;
|
|
900
1484
|
list.push(target);
|
|
901
1485
|
}
|
|
1486
|
+
// a comment's reference in the span goes into the w:del with the text, where it stands among the runs, as Word
|
|
1487
|
+
// puts it (check 54 case 28; CR-014 section 12.6); accepting the deletion then takes the comment
|
|
1488
|
+
const references = this.commentReferencesIn(start, end, edges);
|
|
1489
|
+
if (references.length > 0) {
|
|
1490
|
+
const order = runOrderOf(this.p);
|
|
1491
|
+
const revisions = new Map(targets.filter((seg) => seg.revision).map((seg) => [seg.runOwner, seg.revision]));
|
|
1492
|
+
for (const { run, owner } of references) {
|
|
1493
|
+
const revision = (_a = revisions.get(owner)) !== null && _a !== void 0 ? _a : revisionHolding(run);
|
|
1494
|
+
const target = { run, owner, own: tracker.ownInsertion(revision) };
|
|
1495
|
+
if (revision)
|
|
1496
|
+
target.revision = revision;
|
|
1497
|
+
const key = (_b = order.get(run)) !== null && _b !== void 0 ? _b : Number.NaN;
|
|
1498
|
+
const at = list.findIndex((t) => { var _a; return ((_a = order.get(t.run)) !== null && _a !== void 0 ? _a : Number.NaN) > key; });
|
|
1499
|
+
list.splice(at < 0 ? list.length : at, 0, target);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
902
1502
|
// groups of consecutive runs in the same array, so that one w:del holds them all
|
|
903
1503
|
const groups = [];
|
|
904
1504
|
for (const t of list) {
|
|
@@ -910,6 +1510,8 @@ export class Paragraph {
|
|
|
910
1510
|
else
|
|
911
1511
|
groups.push([t]);
|
|
912
1512
|
}
|
|
1513
|
+
for (const equation of equations)
|
|
1514
|
+
markEquationDeleted(tracker, equation);
|
|
913
1515
|
const made = [];
|
|
914
1516
|
for (let g = groups.length - 1; g >= 0; g--) {
|
|
915
1517
|
const group = groups[g];
|
|
@@ -918,8 +1520,11 @@ export class Paragraph {
|
|
|
918
1520
|
const runs = owner.splice(at, group.length);
|
|
919
1521
|
if (group[0].own) {
|
|
920
1522
|
// taking back our own insertion: the runs go, and an emptied w:ins with them; a picture's
|
|
921
|
-
// image waits for the save, as `InlinePicture.delete` leaves it (section 47)
|
|
1523
|
+
// image waits for the save, as `InlinePicture.delete` leaves it (section 47); a comment whose
|
|
1524
|
+
// reference goes with them goes too
|
|
922
1525
|
this.releaseRelationships(runs);
|
|
1526
|
+
for (const id of commentReferenceIds(runs))
|
|
1527
|
+
this.queueCommentRemoval(id);
|
|
923
1528
|
const revision = group[0].revision;
|
|
924
1529
|
if (revision && revision.items.length === 0) {
|
|
925
1530
|
const i = revision.owner.indexOf(revision.element);
|
|
@@ -932,15 +1537,60 @@ export class Paragraph {
|
|
|
932
1537
|
toDeletedText(run.value);
|
|
933
1538
|
const del = tracker.del(runs);
|
|
934
1539
|
owner.splice(at, 0, del);
|
|
935
|
-
linkParents(del, ((
|
|
1540
|
+
linkParents(del, ((_e = (_d = (_c = group[0].revision) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : group[0].run.value.PARENT) !== null && _e !== void 0 ? _e : this.p));
|
|
936
1541
|
const revision = group[0].revision;
|
|
937
|
-
const anchor = { owner, element: del, parent: ((
|
|
1542
|
+
const anchor = { owner, element: del, parent: ((_g = (_f = revision === null || revision === void 0 ? void 0 : revision.value) !== null && _f !== void 0 ? _f : del.value.PARENT) !== null && _g !== void 0 ? _g : this.p) };
|
|
938
1543
|
if (revision && revision.items === owner)
|
|
939
1544
|
anchor.revision = revision;
|
|
940
1545
|
made.unshift(anchor);
|
|
941
1546
|
}
|
|
942
1547
|
return made;
|
|
943
1548
|
}
|
|
1549
|
+
/**
|
|
1550
|
+
* The run-level controls showing their placeholder that [start, end) holds whole: where the walk passes one strictly
|
|
1551
|
+
* inside the span, or at an edge `edges` says it holds. A placeholder is no text, so such a control has no width
|
|
1552
|
+
* (CR-002 section 63, D3).
|
|
1553
|
+
*/
|
|
1554
|
+
placeholderControlsIn(start, end, edges) {
|
|
1555
|
+
const found = [];
|
|
1556
|
+
this.segments({ visit: (item, at) => {
|
|
1557
|
+
var _a;
|
|
1558
|
+
if (typeNameOf(item) !== 'org_docx4j_wml.SdtRun' || !objectInSpan(at, start, end, edges))
|
|
1559
|
+
return;
|
|
1560
|
+
const sdtPr = item.value.sdtPr;
|
|
1561
|
+
const flag = sdtProperty(sdtPr, 'showingPlcHdr');
|
|
1562
|
+
if (flag !== undefined && ((_a = flag.value) === null || _a === void 0 ? void 0 : _a.val) !== false)
|
|
1563
|
+
found.push(item.value);
|
|
1564
|
+
} });
|
|
1565
|
+
return found.map((sdt) => controlViewOf(sdt, this.parentBody));
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* The runs of comments' references in [start, end): each a run holding nothing but a reference, where the walk
|
|
1569
|
+
* passes it, strictly inside the span or at an edge `edges` says it holds. A reference has no text, so a
|
|
1570
|
+
* deletion's segments never held it, and a comment whose reference a deletion took stayed. Word's deletion takes
|
|
1571
|
+
* it with the text (check 54 cases 27 and 28; CR-014 section 12.6).
|
|
1572
|
+
*/
|
|
1573
|
+
commentReferencesIn(start, end, edges) {
|
|
1574
|
+
const out = [];
|
|
1575
|
+
this.segments({ visit: (item, at, _owner, run) => {
|
|
1576
|
+
var _a, _b;
|
|
1577
|
+
if (((_a = item.name) === null || _a === void 0 ? void 0 : _a.localPart) !== 'commentReference' || !run || !objectInSpan(at, start, end, edges))
|
|
1578
|
+
return;
|
|
1579
|
+
if (out.some((r) => r.run === run.element))
|
|
1580
|
+
return;
|
|
1581
|
+
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'; }))
|
|
1582
|
+
return;
|
|
1583
|
+
out.push({ run: run.element, owner: run.owner, id: Number(item.value.id) });
|
|
1584
|
+
} });
|
|
1585
|
+
return out;
|
|
1586
|
+
}
|
|
1587
|
+
/** A comment queued to go when the package is saved, its markers, replies and entries with it, as a resolution queues one. */
|
|
1588
|
+
queueCommentRemoval(id) {
|
|
1589
|
+
var _a, _b;
|
|
1590
|
+
if (!Number.isInteger(id))
|
|
1591
|
+
return;
|
|
1592
|
+
(_b = (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.pendingCommentRemovals) === null || _b === void 0 ? void 0 : _b.add(id);
|
|
1593
|
+
}
|
|
944
1594
|
/**
|
|
945
1595
|
* Splits the revision that holds the text at `at` into two - the second half a copy of its
|
|
946
1596
|
* attributes under a new `w:id` - and puts `item` between them. Word splits a revision so when
|
|
@@ -1030,7 +1680,7 @@ export class Paragraph {
|
|
|
1030
1680
|
* of a replacement, which the insertion must follow.
|
|
1031
1681
|
*/
|
|
1032
1682
|
insertTracked(tracker, at, text, anchor, prefer = 'before') {
|
|
1033
|
-
var _a, _b
|
|
1683
|
+
var _a, _b;
|
|
1034
1684
|
if (anchor) {
|
|
1035
1685
|
const rPr = copyRPr((_a = runsOf(anchor.element.value, { view: 'original' })[0]) === null || _a === void 0 ? void 0 : _a.value.rPr);
|
|
1036
1686
|
const ins = tracker.ins([runOf([textItem(text)], rPr)]);
|
|
@@ -1066,7 +1716,7 @@ export class Paragraph {
|
|
|
1066
1716
|
}
|
|
1067
1717
|
const neighbour = prefer === 'after' ? after !== null && after !== void 0 ? after : before : before !== null && before !== void 0 ? before : after;
|
|
1068
1718
|
tracker.assertEditable(neighbour === null || neighbour === void 0 ? void 0 : neighbour.revision);
|
|
1069
|
-
const rPr = copyRPr((_b = neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) !== null && _b !== void 0 ? _b :
|
|
1719
|
+
const rPr = copyRPr((_b = neighbour === null || neighbour === void 0 ? void 0 : neighbour.run.rPr) !== null && _b !== void 0 ? _b : this.visibleFirstRunRPr());
|
|
1070
1720
|
const ins = tracker.ins([runOf([textItem(text)], rPr)]);
|
|
1071
1721
|
this.placeInsertion(at, [ins], before, after, () => tracker.nextId(), prefer);
|
|
1072
1722
|
}
|
|
@@ -1106,6 +1756,13 @@ export class Paragraph {
|
|
|
1106
1756
|
linkParents(items, this.p);
|
|
1107
1757
|
}
|
|
1108
1758
|
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Splits runs so that every run holding text in [start, end) holds nothing else (internal: what a
|
|
1761
|
+
* `Range.font` write formats). A kept `mc:AlternateContent`'s branch splits with its run (section 12.2).
|
|
1762
|
+
*/
|
|
1763
|
+
splitRunsAround(start, end) {
|
|
1764
|
+
this.isolate(start, end);
|
|
1765
|
+
}
|
|
1109
1766
|
/**
|
|
1110
1767
|
* Splits runs so that every run holding text in [start, end) holds nothing else, and returns
|
|
1111
1768
|
* the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
|
|
@@ -1125,16 +1782,16 @@ export class Paragraph {
|
|
|
1125
1782
|
if (seen.has(seg.run))
|
|
1126
1783
|
continue;
|
|
1127
1784
|
seen.add(seg.run);
|
|
1785
|
+
if (seg.math)
|
|
1786
|
+
continue; // in no run: taken whole or refused
|
|
1128
1787
|
const ofRun = inside.filter((s) => s.run === seg.run);
|
|
1129
1788
|
const first = ofRun[0];
|
|
1130
1789
|
const last = ofRun[ofRun.length - 1];
|
|
1131
|
-
if (first
|
|
1132
|
-
this.splitRunBefore(first);
|
|
1790
|
+
if (this.splitRunBefore(first)) {
|
|
1133
1791
|
split = true;
|
|
1134
1792
|
break;
|
|
1135
1793
|
}
|
|
1136
|
-
if (
|
|
1137
|
-
this.splitRunAfter(last);
|
|
1794
|
+
if (this.splitRunAfter(last)) {
|
|
1138
1795
|
split = true;
|
|
1139
1796
|
break;
|
|
1140
1797
|
}
|
|
@@ -1144,23 +1801,58 @@ export class Paragraph {
|
|
|
1144
1801
|
}
|
|
1145
1802
|
return this.segments({ objects: true }).filter(inSpan);
|
|
1146
1803
|
}
|
|
1147
|
-
/**
|
|
1804
|
+
/**
|
|
1805
|
+
* Refuses an edit that would cut an equation (CR-012 phase 4). Its text is one segment, Word's drawing of
|
|
1806
|
+
* its math runs (`mathTextOf`), so an edit may take it whole but not part of it, nor insert into it. Under change
|
|
1807
|
+
* tracking a deletion takes it whole in Word's form (`deleteText`, CR-002 section 62.2 D2, check 57), and a
|
|
1808
|
+
* replacement doesn't take it at all: where Word puts the new text beside a deleted equation isn't measured. Since
|
|
1809
|
+
* phase 4 every other offset is a place in the runs: `text` is the segments' text, where before it was the objects
|
|
1810
|
+
* package's `textOf` and section 12.4 refused an offset past a kept `mc:AlternateContent` among the runs.
|
|
1811
|
+
*/
|
|
1812
|
+
assertPlaceable(start, end, tracked) {
|
|
1813
|
+
for (const seg of this.segments()) {
|
|
1814
|
+
if (!seg.math)
|
|
1815
|
+
continue;
|
|
1816
|
+
const cuts = (seg.start < start && start < seg.end) || (seg.start < end && end < seg.end);
|
|
1817
|
+
const takes = start < seg.end && seg.start < end;
|
|
1818
|
+
if (!cuts && !(tracked === 'replace' && takes))
|
|
1819
|
+
continue;
|
|
1820
|
+
throw new NotSupportedError('Paragraph', 'edit', `[${start}, ${end}) ${cuts ? 'cuts' : 'takes'} the equation at [${seg.start}, ${seg.end})`
|
|
1821
|
+
+ (cuts ? ': an edit takes an equation whole or not at all (CR-012 phase 4)'
|
|
1822
|
+
: ' under change tracking, with text in its place: a tracked replacement of an equation is not measured (CR-002 section 62.2)'));
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
/** Moves the items before `seg` into a run of their own, in front of the rest; false where there are none. */
|
|
1148
1826
|
splitRunBefore(seg) {
|
|
1149
1827
|
var _a;
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1828
|
+
if (seg.index > 0) {
|
|
1829
|
+
this.splitRunAtItem(seg, seg.owner, seg.index);
|
|
1830
|
+
return true;
|
|
1831
|
+
}
|
|
1832
|
+
const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
|
|
1833
|
+
if (!alternate)
|
|
1834
|
+
return false;
|
|
1835
|
+
const at = alternate.owner.indexOf(alternate.element);
|
|
1836
|
+
if (at <= 0)
|
|
1837
|
+
return false;
|
|
1838
|
+
this.splitRunAtItem(seg, alternate.owner, at);
|
|
1839
|
+
return true;
|
|
1154
1840
|
}
|
|
1155
|
-
/** Moves the items after `seg` into a run of their own, behind
|
|
1841
|
+
/** Moves the items after `seg` into a run of their own, behind it; false where there are none. */
|
|
1156
1842
|
splitRunAfter(seg) {
|
|
1157
1843
|
var _a;
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
return;
|
|
1161
|
-
|
|
1162
|
-
seg.
|
|
1163
|
-
|
|
1844
|
+
if (seg.index < seg.owner.length - 1) {
|
|
1845
|
+
this.splitRunAtItem(seg, seg.owner, seg.index + 1);
|
|
1846
|
+
return true;
|
|
1847
|
+
}
|
|
1848
|
+
const alternate = ((_a = seg.alternate) === null || _a === void 0 ? void 0 : _a.level) === 'run' && seg.owner === seg.alternate.branch ? seg.alternate : undefined;
|
|
1849
|
+
if (!alternate)
|
|
1850
|
+
return false;
|
|
1851
|
+
const at = alternate.owner.indexOf(alternate.element);
|
|
1852
|
+
if (at < 0 || at >= alternate.owner.length - 1)
|
|
1853
|
+
return false;
|
|
1854
|
+
this.splitRunAtItem(seg, alternate.owner, at + 1);
|
|
1855
|
+
return true;
|
|
1164
1856
|
}
|
|
1165
1857
|
}
|
|
1166
1858
|
export { W_NS };
|
|
@@ -1212,3 +1904,127 @@ function setText(t, value) {
|
|
|
1212
1904
|
else
|
|
1213
1905
|
delete t.space;
|
|
1214
1906
|
}
|
|
1907
|
+
/**
|
|
1908
|
+
* The element a run-level `mc:AlternateContent`'s later piece is written as (CR-012 section 12.2; the
|
|
1909
|
+
* editor's ED-009 section 3.4, P2): its branches in order with their `Requires`, the selected one holding
|
|
1910
|
+
* `items` and every other one empty.
|
|
1911
|
+
*/
|
|
1912
|
+
function alternatePiece(alternate, items) {
|
|
1913
|
+
const ac = alternate.element.value;
|
|
1914
|
+
const emptied = (branch) => {
|
|
1915
|
+
if (branch === undefined)
|
|
1916
|
+
return undefined;
|
|
1917
|
+
const copy = {};
|
|
1918
|
+
for (const [key, value] of Object.entries(branch))
|
|
1919
|
+
if (key !== 'any' && key !== 'PARENT')
|
|
1920
|
+
copy[key] = value;
|
|
1921
|
+
copy.any = branch.any === alternate.branch ? items : [];
|
|
1922
|
+
return copy;
|
|
1923
|
+
};
|
|
1924
|
+
const value = {};
|
|
1925
|
+
for (const [key, v] of Object.entries(ac)) {
|
|
1926
|
+
if (key === 'PARENT')
|
|
1927
|
+
continue;
|
|
1928
|
+
if (key === 'choice')
|
|
1929
|
+
value.choice = v.map(emptied);
|
|
1930
|
+
else if (key === 'fallback')
|
|
1931
|
+
value.fallback = emptied(v);
|
|
1932
|
+
else
|
|
1933
|
+
value[key] = v;
|
|
1934
|
+
}
|
|
1935
|
+
return { name: alternate.element.name, value };
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* After an untracked deletion: a run-level `mc:AlternateContent` whose selected branch the deletion
|
|
1939
|
+
* emptied goes, every branch with it (CR-012 section 12.2 item 4; ED-009 section 3.5). A branch that
|
|
1940
|
+
* was empty before is left as it is.
|
|
1941
|
+
*/
|
|
1942
|
+
function removeEmptiedAlternates(removed) {
|
|
1943
|
+
const seen = new Set();
|
|
1944
|
+
for (const seg of removed) {
|
|
1945
|
+
const alternate = seg.alternate;
|
|
1946
|
+
if ((alternate === null || alternate === void 0 ? void 0 : alternate.level) !== 'run' || seen.has(alternate.element))
|
|
1947
|
+
continue;
|
|
1948
|
+
seen.add(alternate.element);
|
|
1949
|
+
if (alternate.branch.length > 0)
|
|
1950
|
+
continue;
|
|
1951
|
+
const i = alternate.owner.indexOf(alternate.element);
|
|
1952
|
+
if (i >= 0)
|
|
1953
|
+
alternate.owner.splice(i, 1);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
/** Block holders a cell's paragraph may stand in: a block content control, its content, custom XML. */
|
|
1957
|
+
const BLOCK_HOLDERS = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtContentBlock', 'org_docx4j_wml.CTSdtContentBlock', 'org_docx4j_wml.CTCustomXmlBlock']);
|
|
1958
|
+
/** True for the last paragraph of a table cell, through the block holders between them. */
|
|
1959
|
+
function lastInCell(p) {
|
|
1960
|
+
var _a;
|
|
1961
|
+
let holder = p.PARENT;
|
|
1962
|
+
for (let guard = 0; holder && guard < 16 && BLOCK_HOLDERS.has((_a = holder.TYPE_NAME) !== null && _a !== void 0 ? _a : ''); guard++) {
|
|
1963
|
+
holder = holder.PARENT;
|
|
1964
|
+
}
|
|
1965
|
+
if ((holder === null || holder === void 0 ? void 0 : holder.TYPE_NAME) !== 'org_docx4j_wml.Tc')
|
|
1966
|
+
return false;
|
|
1967
|
+
let last;
|
|
1968
|
+
const visit = (items) => {
|
|
1969
|
+
for (const el of items !== null && items !== void 0 ? items : []) {
|
|
1970
|
+
const tn = typeNameOf(el);
|
|
1971
|
+
if (tn === 'org_docx4j_wml.P')
|
|
1972
|
+
last = el.value;
|
|
1973
|
+
else if (tn !== 'org_docx4j_wml.Tbl' && typeof el.value === 'object' && el.value !== null) {
|
|
1974
|
+
const v = el.value;
|
|
1975
|
+
visit(v.sdtContent ? v.sdtContent.content : v.content);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
visit(holder.content);
|
|
1980
|
+
return last === p;
|
|
1981
|
+
}
|
|
1982
|
+
/** Every element among a paragraph's runs, in document order: where a deletion places a comment's reference among its runs. */
|
|
1983
|
+
function runOrderOf(p) {
|
|
1984
|
+
const order = new Map();
|
|
1985
|
+
const visit = (items) => {
|
|
1986
|
+
for (const el of items !== null && items !== void 0 ? items : []) {
|
|
1987
|
+
order.set(el, order.size);
|
|
1988
|
+
const v = el.value;
|
|
1989
|
+
if (typeof v === 'object' && v !== null && typeNameOf(el) !== 'org_docx4j_wml.R')
|
|
1990
|
+
visit(runItemsOf(v));
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
visit(runItemsOf(p));
|
|
1994
|
+
return order;
|
|
1995
|
+
}
|
|
1996
|
+
/** The `w:ins` (or other revision) a run stands directly in, read from its `PARENT`, if any. */
|
|
1997
|
+
function revisionHolding(run) {
|
|
1998
|
+
const holder = run.value.PARENT;
|
|
1999
|
+
const items = holder === null || holder === void 0 ? void 0 : holder.customXmlOrSmartTagOrSdt;
|
|
2000
|
+
const outer = (holder === null || holder === void 0 ? void 0 : holder.PARENT) ? runItemsOf(holder.PARENT) : undefined;
|
|
2001
|
+
const element = outer === null || outer === void 0 ? void 0 : outer.find((e) => e.value === holder);
|
|
2002
|
+
const kind = element ? revisionKindOf(element) : undefined;
|
|
2003
|
+
if (!holder || !items || !outer || !element || !kind)
|
|
2004
|
+
return undefined;
|
|
2005
|
+
return { kind, element, value: holder, items, owner: outer };
|
|
2006
|
+
}
|
|
2007
|
+
/** The ids of the comment references among runs. */
|
|
2008
|
+
function commentReferenceIds(runs) {
|
|
2009
|
+
return runs.flatMap((run) => {
|
|
2010
|
+
var _a;
|
|
2011
|
+
return ((_a = run.value.content) !== null && _a !== void 0 ? _a : [])
|
|
2012
|
+
.filter((c) => { var _a; return ((_a = c.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'commentReference'; }).map((c) => Number(c.value.id));
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
/** The `w:sdt` value types, block, run, row and cell. */
|
|
2016
|
+
const SDT_VALUE_TYPES = new Set(['org_docx4j_wml.SdtBlock', 'org_docx4j_wml.SdtRun', 'org_docx4j_wml.CTSdtRow', 'org_docx4j_wml.CTSdtCell']);
|
|
2017
|
+
/** Whether a paragraph holds a run-level control bound by `w:dataBinding`, at any depth among its runs. */
|
|
2018
|
+
function holdsBoundRunControl(holder) {
|
|
2019
|
+
var _a;
|
|
2020
|
+
for (const item of (_a = runItemsOf(holder)) !== null && _a !== void 0 ? _a : []) {
|
|
2021
|
+
const value = item.value;
|
|
2022
|
+
if (!value || typeof value !== 'object')
|
|
2023
|
+
continue;
|
|
2024
|
+
if (value.TYPE_NAME === 'org_docx4j_wml.SdtRun' && sdtProperty(value.sdtPr, 'dataBinding') !== undefined)
|
|
2025
|
+
return true;
|
|
2026
|
+
if (value.TYPE_NAME !== 'org_docx4j_wml.R' && holdsBoundRunControl(value))
|
|
2027
|
+
return true;
|
|
2028
|
+
}
|
|
2029
|
+
return false;
|
|
2030
|
+
}
|