@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,20 +1,32 @@
|
|
|
1
1
|
import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
|
|
2
2
|
import * as w15el from '@docx4j/generated-objects-ts/el/org_docx4j_w15';
|
|
3
3
|
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
4
|
+
import { NotSupportedError } from '../../office-js/errors.mjs';
|
|
5
|
+
import { GeneralException, InvalidArgument } from './generalException.mjs';
|
|
4
6
|
import { marshalString, deepCopy } from '@docx4j/generated-objects-ts';
|
|
5
7
|
// CR-002 phase E: the mapping and the typed kinds live in src/model/customxml/; they import
|
|
6
8
|
// nothing from here at run time (types only), so this direction is the only one.
|
|
7
9
|
import { XmlMapping } from '../customxml/XmlMapping.mjs';
|
|
8
10
|
import { CheckboxContentControl, DatePickerContentControl, ListContentControl, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKBOX_FONT, } from '../customxml/kinds.mjs';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { updateFromControl, holdsFlatOpc, placeholderRun, PLACEHOLDER_STYLE } from '../customxml/bindings.mjs';
|
|
12
|
+
import { dropExcluded, forWriter, officeItems } from './officeText.mjs';
|
|
13
|
+
import { declareIgnorable, markHolderDeleted } from './tracking.mjs';
|
|
14
|
+
import { placeholderEntryOf, namePlaceholderEntry, newEntryName, entryContentOf, defaultEntryFor, DEFAULT_ENTRIES } from './placeholders.mjs';
|
|
15
|
+
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
16
|
+
import { commentReferencesIn } from './TrackedChange.mjs';
|
|
17
|
+
import { wrappingCellOf, paragraphEndOf, endsWithParagraph, endWithParagraph } from './tableRules.mjs';
|
|
18
|
+
import { sdtProperty, sdtKindOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
19
|
+
import { typeNameOf, childrenOf, runItemsOf, runsOf, linkParents, runOf, SDT_TYPES, ALTERNATE_CONTENT, BLOCK_LEVEL_TYPES, branchListsOf, inBranch, assertOutsideBranch } from './tree.mjs';
|
|
13
20
|
import { Range } from './Range.mjs';
|
|
14
21
|
import { Table } from './Table.mjs';
|
|
22
|
+
import { assignParaIds } from './paraIds.mjs';
|
|
15
23
|
/** The namespaces of the Word 2010 and 2012 content-control properties. */
|
|
16
24
|
export const W14_NS = 'http://schemas.microsoft.com/office/word/2010/wordml';
|
|
17
25
|
export const W15_NS = 'http://schemas.microsoft.com/office/word/2012/wordml';
|
|
26
|
+
/** The markers of a control inserted or deleted under tracking (`markHolderInserted`, `markHolderDeleted`). */
|
|
27
|
+
const TRACKED_HOLDER_MARKER = /^customXml(Ins|Del)Range(Start|End)$/;
|
|
28
|
+
/** What a block, row or cell control's content ends with, for its range's end: a paragraph, a table or a row. */
|
|
29
|
+
const BLOCK_ITEMS = new Set(['org_docx4j_wml.P', 'org_docx4j_wml.Tbl', 'org_docx4j_wml.Tr', 'org_docx4j_wml.Tc']);
|
|
18
30
|
/** A subset of Office JS `Word.ContentControl` over a `w:sdt`. A view, as Paragraph is. */
|
|
19
31
|
export class ContentControl {
|
|
20
32
|
constructor(
|
|
@@ -54,7 +66,10 @@ export class ContentControl {
|
|
|
54
66
|
}
|
|
55
67
|
/** The type Word reports (w:sdtPr's kind element); RichText when none is given, as Word does. */
|
|
56
68
|
get type() {
|
|
57
|
-
|
|
69
|
+
const kind = sdtKindOf(this.sdt.sdtPr);
|
|
70
|
+
// Word reads a block control bound by w:dataBinding, untyped or w:richText, as single-line plain text, and saves it
|
|
71
|
+
// so (checks 35 and 56; CR-002 section 61 H4); the file is left as it is
|
|
72
|
+
return kind === 'RichText' && isBoundBlockControl(this) ? 'PlainText' : kind;
|
|
58
73
|
}
|
|
59
74
|
/** w:tag. */
|
|
60
75
|
get tag() {
|
|
@@ -77,10 +92,20 @@ export class ContentControl {
|
|
|
77
92
|
const val = (_c = item === null || item === void 0 ? void 0 : item.value) === null || _c === void 0 ? void 0 : _c.val;
|
|
78
93
|
return typeof val === 'number' ? val : 0;
|
|
79
94
|
}
|
|
80
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* The control's text as Office JS gives it (CR-012 phase 4): a run-level control's span of
|
|
97
|
+
* `Paragraph.text`; a block-level one's content as `Body.text` reads it, its paragraphs a mark `\r` apart
|
|
98
|
+
* (check 51); a row control's cells a `\t` apart and a cell control's paragraphs `\r` apart (check 53).
|
|
99
|
+
*/
|
|
81
100
|
get text() {
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
if (this.form === 'Run') {
|
|
102
|
+
const span = this.runSpan();
|
|
103
|
+
return span ? span.paragraph.text.substring(span.start, span.end) : '';
|
|
104
|
+
}
|
|
105
|
+
// a row control's cells a \t apart (check 53: ROW15A\tROW15B), a cell control's paragraphs a \r apart; a
|
|
106
|
+
// control's content ending with a row (a row control, or a table last) leaves the row's end out (check 53)
|
|
107
|
+
const text = this.body().text;
|
|
108
|
+
return text.endsWith('\r\n') ? text.slice(0, -2) : text;
|
|
84
109
|
}
|
|
85
110
|
/** Every paragraph in the control; for a run-level control, the paragraph it is in. */
|
|
86
111
|
get paragraphs() {
|
|
@@ -106,6 +131,14 @@ export class ContentControl {
|
|
|
106
131
|
collectControls(this.content, this.parentBody, out);
|
|
107
132
|
return out;
|
|
108
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Office JS `getContentControls` (WordApi 1.5): the content controls `contentControls` lists, those of `options.types`
|
|
136
|
+
* where given. Word's `contentControls` lists rich-text controls only; this package's lists every kind, a recorded
|
|
137
|
+
* departure (CR-002 section 60 G1, check 55b), so this is how add-in code that asks for plain-text controls finds them.
|
|
138
|
+
*/
|
|
139
|
+
getContentControls(options) {
|
|
140
|
+
return controlsOfTypes(this.contentControls, options);
|
|
141
|
+
}
|
|
109
142
|
/**
|
|
110
143
|
* Text at the start, the end, or replacing the control's text. On a **bound** control the value
|
|
111
144
|
* is written through to the custom XML node as well when the mapping resolves (CR-002 phase E):
|
|
@@ -114,10 +147,298 @@ export class ContentControl {
|
|
|
114
147
|
* write is left to `pkg.customXmlParts.updateFromContentControls()`.
|
|
115
148
|
*/
|
|
116
149
|
insertText(text, location) {
|
|
150
|
+
this.assertWritable();
|
|
151
|
+
// a control showing its placeholder takes the text in the placeholder's place, wherever it is put (check 58 cases 20
|
|
152
|
+
// to 22, CR-012 section 18; CR-002 section 63)
|
|
153
|
+
if (this.showsPlaceholderContent)
|
|
154
|
+
return this.writePlaceholder(text);
|
|
117
155
|
const range = this.insertTextInto(text, location);
|
|
118
156
|
this.syncBoundNode();
|
|
119
157
|
return range;
|
|
120
158
|
}
|
|
159
|
+
// --- CR-002 section 63: writing into a control showing its placeholder ---
|
|
160
|
+
/**
|
|
161
|
+
* The glossary entry the control's `w:placeholder` names, which makes it Word's form; undefined where it names none,
|
|
162
|
+
* the bare form, which has no entry to show once emptied, nor for `placeholderText` to read (CR-002 section 63; an
|
|
163
|
+
* extension). Writes are the same in both forms (check 63's run 2).
|
|
164
|
+
*/
|
|
165
|
+
get placeholderEntry() {
|
|
166
|
+
return placeholderEntryOf(this.sdt.sdtPr);
|
|
167
|
+
}
|
|
168
|
+
/** @internal True for a block or run-level control showing its placeholder, which a text write replaces (section 63). */
|
|
169
|
+
get showsPlaceholderContent() {
|
|
170
|
+
return this.isShowingPlaceholder && (this.form === 'Block' || this.form === 'Run');
|
|
171
|
+
}
|
|
172
|
+
/** The package's glossary entries, where the control is in a package. */
|
|
173
|
+
get glossary() {
|
|
174
|
+
var _a;
|
|
175
|
+
return (_a = this.parentBody.package_) === null || _a === void 0 ? void 0 : _a.glossaryEntries;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @internal Text written into the content of this control while it shows its placeholder, by any route: its own
|
|
179
|
+
* `insertText`, its 'Content' range's, a block control's paragraph's (CR-002 section 63, D1, D2 (b), D5, D11). The
|
|
180
|
+
* placeholder is no content, in either form (checks 64 and 67, check 63's run 2): its runs go, untracked, the text takes
|
|
181
|
+
* their place in one paragraph, and the flag is cleared; under tracking only the insertion is recorded. The text takes
|
|
182
|
+
* the control's own run properties (`w:sdtPr/w:rPr`) and nothing of the placeholder's (check 68, cases 1 to 20).
|
|
183
|
+
*/
|
|
184
|
+
writePlaceholder(text, options = {}) {
|
|
185
|
+
this.assertWritable();
|
|
186
|
+
const form = this.form;
|
|
187
|
+
// refused before anything changes: a line break into a run-level control (CR-002 section 60 F4)
|
|
188
|
+
if (form === 'Run')
|
|
189
|
+
textForRunControl(this, dropExcluded(text));
|
|
190
|
+
this.takePlaceholderRuns(form);
|
|
191
|
+
this.isShowingPlaceholder = false;
|
|
192
|
+
let range;
|
|
193
|
+
if (form === 'Run') {
|
|
194
|
+
const paragraph = this.parentParagraph();
|
|
195
|
+
if (!paragraph)
|
|
196
|
+
throw new Docx4JException('This content control is not in a paragraph');
|
|
197
|
+
range = this.insertIntoEmpty(paragraph, text);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
range = this.firstParagraph().splice(0, 0, text, 'before', undefined, options.lineBreaks);
|
|
201
|
+
}
|
|
202
|
+
// every run now in the control is the write's: each takes a copy of the control's run properties (D11)
|
|
203
|
+
const rPr = this.runProperties;
|
|
204
|
+
if (rPr) {
|
|
205
|
+
const holder = form === 'Run' ? this.sdt.sdtContent : undefined;
|
|
206
|
+
const runs = holder ? runsOf(holder) : this.body().paragraphs.flatMap((p) => runsOf(p.p));
|
|
207
|
+
for (const run of runs) {
|
|
208
|
+
run.value.rPr = deepCopy(rPr);
|
|
209
|
+
linkParents(run.value.rPr, run.value);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.syncBoundNode();
|
|
213
|
+
return range;
|
|
214
|
+
}
|
|
215
|
+
/** The control's first paragraph, made where its content has none. */
|
|
216
|
+
firstParagraph() {
|
|
217
|
+
var _a;
|
|
218
|
+
var _b;
|
|
219
|
+
const body = this.body();
|
|
220
|
+
const first = body.paragraphs[0];
|
|
221
|
+
if (first)
|
|
222
|
+
return first;
|
|
223
|
+
const p = el.p({ content: [] });
|
|
224
|
+
const content = ((_a = (_b = this.sdt.sdtContent).content) !== null && _a !== void 0 ? _a : (_b.content = []));
|
|
225
|
+
content.push(p);
|
|
226
|
+
linkParents(p, this.sdt.sdtContent);
|
|
227
|
+
assignParaIds(this.sdt.sdtContent, [p]);
|
|
228
|
+
return body.paragraphs[0];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Takes the placeholder's runs out of the content: a run-level control's, or a block control's paragraphs', the first
|
|
232
|
+
* paragraph kept, emptied, and any other going. What held them.
|
|
233
|
+
*/
|
|
234
|
+
takePlaceholderRuns(form) {
|
|
235
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
236
|
+
var _h, _j, _k;
|
|
237
|
+
const runs = [];
|
|
238
|
+
const take = (items) => {
|
|
239
|
+
for (let i = 0; i < items.length; i++) {
|
|
240
|
+
if (typeNameOf(items[i]) !== 'org_docx4j_wml.R')
|
|
241
|
+
continue;
|
|
242
|
+
runs.push(items.splice(i, 1)[0]);
|
|
243
|
+
i--;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
if (form === 'Run') {
|
|
247
|
+
const owner = ((_a = (_h = this.sdt).sdtContent) !== null && _a !== void 0 ? _a : (_h.sdtContent = {}));
|
|
248
|
+
const holder = ((_b = owner.content) !== null && _b !== void 0 ? _b : (owner.content = []));
|
|
249
|
+
take(holder);
|
|
250
|
+
return { runs, holder, owner };
|
|
251
|
+
}
|
|
252
|
+
const content = (_d = (_c = this.sdt.sdtContent) === null || _c === void 0 ? void 0 : _c.content) !== null && _d !== void 0 ? _d : [];
|
|
253
|
+
const paragraphs = content.filter((e) => typeNameOf(e) === 'org_docx4j_wml.P');
|
|
254
|
+
for (const p of paragraphs)
|
|
255
|
+
take(((_e = (_j = p.value).content) !== null && _e !== void 0 ? _e : (_j.content = [])));
|
|
256
|
+
for (const p of paragraphs.slice(1))
|
|
257
|
+
content.splice(content.indexOf(p), 1);
|
|
258
|
+
const first = (_f = paragraphs[0]) !== null && _f !== void 0 ? _f : this.firstParagraph().element;
|
|
259
|
+
return { runs, holder: ((_g = (_k = first.value).content) !== null && _g !== void 0 ? _g : (_k.content = [])), owner: first.value };
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Shows Word's filler for a control emptied in the bare form, where no glossary entry gives it a placeholder: the flag
|
|
263
|
+
* on over a run of five spaces, in the control's own run properties (CR-002 section 63, D10; check 63's run 2, cases 6,
|
|
264
|
+
* 17, 32 and 43, and check 52's case 13). Nothing is marked under tracking. The spaces read as no text while the flag is
|
|
265
|
+
* on (CR-012 section 18).
|
|
266
|
+
*/
|
|
267
|
+
showFiller() {
|
|
268
|
+
const { holder, owner } = this.takePlaceholderRuns(this.form);
|
|
269
|
+
const rPr = this.runProperties;
|
|
270
|
+
const run = el.r({ ...(rPr ? { rPr: deepCopy(rPr) } : {}), content: [el.t({ TYPE_NAME: 'org_docx4j_wml.Text', value: ' ', space: 'preserve' })] });
|
|
271
|
+
pushBeforeMarker(holder, run);
|
|
272
|
+
linkParents(run, owner);
|
|
273
|
+
this.isShowingPlaceholder = true;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* @internal A write through the control's 'Content' or 'Whole' range that the control takes (D3, D4): text into a
|
|
277
|
+
* control showing its placeholder, or into an empty run-level control, goes inside it; a 'Whole' range's Replace over
|
|
278
|
+
* a run-level control showing its placeholder takes the control, the text in its place. Undefined where the range's
|
|
279
|
+
* own write applies.
|
|
280
|
+
*/
|
|
281
|
+
writeThrough(part, text, location) {
|
|
282
|
+
var _a;
|
|
283
|
+
if (part === 'Content') {
|
|
284
|
+
if (this.showsPlaceholderContent)
|
|
285
|
+
return this.writePlaceholder(text);
|
|
286
|
+
if (this.form !== 'Run' || !((_a = this.runSpan()) === null || _a === void 0 ? void 0 : _a.empty))
|
|
287
|
+
return undefined;
|
|
288
|
+
this.assertWritable();
|
|
289
|
+
const range = this.insertTextInto(text, 'Replace');
|
|
290
|
+
this.syncBoundNode();
|
|
291
|
+
return range;
|
|
292
|
+
}
|
|
293
|
+
if (location !== 'Replace' || this.form !== 'Run' || !this.isShowingPlaceholder)
|
|
294
|
+
return undefined;
|
|
295
|
+
return this.takeWhole(text);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* @internal A delete through the control's 'Content' or 'Whole' range that the control takes; false where the range's
|
|
299
|
+
* own delete applies. A 'Content' delete of a control showing its placeholder keeps the control and the flag: in the
|
|
300
|
+
* bare form over Word's filler, five spaces (D10; check 63's run 2, cases 6, 17, 32 and 43, nothing marked under
|
|
301
|
+
* tracking); in Word's form nothing changes, the placeholder being no content. A 'Whole' delete takes a run-level
|
|
302
|
+
* control showing its placeholder (D3).
|
|
303
|
+
*/
|
|
304
|
+
deleteThrough(part) {
|
|
305
|
+
if (part === 'Whole') {
|
|
306
|
+
if (this.form !== 'Run' || !this.isShowingPlaceholder)
|
|
307
|
+
return false;
|
|
308
|
+
this.takeWhole('');
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
if (!this.showsPlaceholderContent)
|
|
312
|
+
return false;
|
|
313
|
+
this.assertWritable();
|
|
314
|
+
if (this.placeholderEntry === undefined)
|
|
315
|
+
this.showFiller();
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* @internal After a 'Content' delete, untracked, that left the control holding no text: in Word's form it shows its
|
|
320
|
+
* entry again (check 66 case 3); in the bare form Word's filler (D10; check 52's case 13), as for a control of a kind
|
|
321
|
+
* Word gives no default entry, naming an entry the glossary lacks.
|
|
322
|
+
*/
|
|
323
|
+
showEmptied() {
|
|
324
|
+
var _a;
|
|
325
|
+
if (this.isShowingPlaceholder || this.text !== '' || (this.form !== 'Block' && this.form !== 'Run'))
|
|
326
|
+
return;
|
|
327
|
+
const name = this.placeholderEntry;
|
|
328
|
+
if (name !== undefined && (this.defaultPlaceholderEntry !== undefined || ((_a = this.glossary) === null || _a === void 0 ? void 0 : _a.has(name)) === true))
|
|
329
|
+
this.showPlaceholder();
|
|
330
|
+
else
|
|
331
|
+
this.showFiller();
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* @internal Word's default placeholder entry for this control's kind (CR-002 section 63.22; docx4j CR-063's D8): the
|
|
335
|
+
* text kinds', the date picker's, the lists', a building block gallery's, a repeating section item's. Undefined for a
|
|
336
|
+
* kind Word gives none.
|
|
337
|
+
*/
|
|
338
|
+
get defaultPlaceholderEntry() {
|
|
339
|
+
return defaultEntryFor(this.sdt.sdtPr);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* @internal Whether an empty control shows a placeholder in Word's form (D8; docx4j CR-063's D3 and D8): a kind Word
|
|
343
|
+
* gives a default entry, or one naming an entry the glossary holds.
|
|
344
|
+
*/
|
|
345
|
+
get showsWordsFormPlaceholder() {
|
|
346
|
+
var _a;
|
|
347
|
+
if (this.defaultPlaceholderEntry !== undefined)
|
|
348
|
+
return true;
|
|
349
|
+
const name = this.placeholderEntry;
|
|
350
|
+
return name !== undefined && name !== '' && ((_a = this.glossary) === null || _a === void 0 ? void 0 : _a.has(name)) === true;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* @internal Takes the control whole (D3): a Replace or delete over a control showing its placeholder, as Word takes one
|
|
354
|
+
* (check 63 cases 12, 18 to 20, 26, 38, 44 to 46 and 52). Untracked it goes, a block control leaving its paragraph,
|
|
355
|
+
* emptied, in its place; tracked it is marked deleted in Word's form (`markHolderDeleted`: `w:customXmlDelRange`
|
|
356
|
+
* markers around it and inside its content), its placeholder gone unmarked and the flag cleared (check 63's run 2,
|
|
357
|
+
* cases 38, 44 to 46 and 52). `text` goes where a run-level control stood, before it under tracking, as Word puts a
|
|
358
|
+
* Replace's insertion first.
|
|
359
|
+
*/
|
|
360
|
+
takeWhole(text = '') {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
this.assertWritable();
|
|
363
|
+
const form = this.form;
|
|
364
|
+
const tracker = this.parentBody.changeTracker;
|
|
365
|
+
if (form === 'Run') {
|
|
366
|
+
const span = this.runSpan();
|
|
367
|
+
if (!span)
|
|
368
|
+
throw new Docx4JException('This content control is not in a paragraph');
|
|
369
|
+
const { paragraph, start } = span;
|
|
370
|
+
if (!tracker) {
|
|
371
|
+
const at = this.container.indexOf(this.element);
|
|
372
|
+
if (at >= 0)
|
|
373
|
+
this.container.splice(at, 1);
|
|
374
|
+
return text === '' ? new Range(paragraph, start, start) : paragraph.splice(start, start, text);
|
|
375
|
+
}
|
|
376
|
+
const inserted = text === '' ? new Range(paragraph, start, start) : paragraph.splice(start, start, text);
|
|
377
|
+
this.markWholeDeleted(tracker, form);
|
|
378
|
+
return inserted;
|
|
379
|
+
}
|
|
380
|
+
const first = this.firstParagraph();
|
|
381
|
+
if (!tracker) {
|
|
382
|
+
const at = this.container.indexOf(this.element);
|
|
383
|
+
this.takePlaceholderRuns(form);
|
|
384
|
+
const paragraph = first.element;
|
|
385
|
+
const owner = (_a = this.element.value.PARENT) !== null && _a !== void 0 ? _a : ownerOf(this.container, this.parentBody);
|
|
386
|
+
this.container.splice(at, 1, paragraph);
|
|
387
|
+
linkParents(paragraph, owner);
|
|
388
|
+
return new Range((_b = this.parentBody.paragraphFor(paragraph.value)) !== null && _b !== void 0 ? _b : first, 0, 0);
|
|
389
|
+
}
|
|
390
|
+
this.markWholeDeleted(tracker, form);
|
|
391
|
+
return new Range(first, 0, 0);
|
|
392
|
+
}
|
|
393
|
+
/** The control marked deleted with Word's markers, its placeholder gone, unmarked, and the flag cleared (`takeWhole`). */
|
|
394
|
+
markWholeDeleted(tracker, form) {
|
|
395
|
+
markHolderDeleted(tracker, this.element, this.container, () => { this.takePlaceholderRuns(form); });
|
|
396
|
+
this.isShowingPlaceholder = false;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Shows the control's placeholder (CR-002 section 63, D8; section 63.18): a copy of the content of the entry its
|
|
400
|
+
* `w:placeholder` names, as the glossary holds it, the control's own run properties not over it, as Word shows it
|
|
401
|
+
* (check 68, section 63.21), with `w:showingPlcHdr`. A control naming no entry, or one the glossary lacks, is made to
|
|
402
|
+
* name Word's default entry for its kind (section 63.22: a date picker's is "Click or tap to enter a date.", a list's
|
|
403
|
+
* "Choose an item."), which is written into the glossary on save where it lacks it (D9), as docx4j's CR-063 has it:
|
|
404
|
+
* Word's save drops a reference no entry holds (check 65). A kind Word gives no default entry (a picture, a check box)
|
|
405
|
+
* keeps the name it has, and naming none shows docx4j's run, with no reference; any control keeps its name until the
|
|
406
|
+
* glossary is read (`getBody`, `Word.run`), showing its kind's default text. An extension.
|
|
407
|
+
*/
|
|
408
|
+
showPlaceholder() {
|
|
409
|
+
var _a, _b, _c, _d;
|
|
410
|
+
var _e, _f;
|
|
411
|
+
this.assertWritable();
|
|
412
|
+
const sdtPr = ((_a = (_e = this.sdt).sdtPr) !== null && _a !== void 0 ? _a : (_e.sdtPr = {}));
|
|
413
|
+
const glossary = this.glossary;
|
|
414
|
+
const fallback = this.defaultPlaceholderEntry;
|
|
415
|
+
let name = (_b = this.placeholderEntry) !== null && _b !== void 0 ? _b : '';
|
|
416
|
+
const missing = name === '' || ((glossary === null || glossary === void 0 ? void 0 : glossary.loaded) === true && !glossary.has(name));
|
|
417
|
+
if (missing && fallback) {
|
|
418
|
+
name = fallback.name;
|
|
419
|
+
namePlaceholderEntry(sdtPr, name);
|
|
420
|
+
}
|
|
421
|
+
if (fallback && name === fallback.name)
|
|
422
|
+
glossary === null || glossary === void 0 ? void 0 : glossary.require(name, fallback.text);
|
|
423
|
+
const body = name === '' ? null : glossary === null || glossary === void 0 ? void 0 : glossary.bodyOf(name);
|
|
424
|
+
// no entry to copy: the kind's default text as its entry holds it, else docx4j's run (CR-063's D8 (a))
|
|
425
|
+
const paragraphs = body ? entryContentOf(body) : [[fallback ? placeholderRun(undefined, fallback.text) : placeholderRun(this.runProperties)]];
|
|
426
|
+
this.setBoundContent((_c = paragraphs[0]) !== null && _c !== void 0 ? _c : [], true);
|
|
427
|
+
// a block control shows an entry's further paragraphs too, where it may hold more than one (CR-002 section 61)
|
|
428
|
+
if (this.form !== 'Block' || paragraphs.length < 2 || isBoundBlockControl(this))
|
|
429
|
+
return;
|
|
430
|
+
const content = (_d = (_f = this.sdt.sdtContent).content) !== null && _d !== void 0 ? _d : (_f.content = []);
|
|
431
|
+
const first = content.find((e) => typeNameOf(e) === 'org_docx4j_wml.P');
|
|
432
|
+
let at = first ? content.indexOf(first) + 1 : content.length;
|
|
433
|
+
// the further paragraphs take a copy of the control's own paragraph's properties, as docx4j's binding gives them
|
|
434
|
+
const pPr = first === null || first === void 0 ? void 0 : first.value.pPr;
|
|
435
|
+
for (const runs of paragraphs.slice(1)) {
|
|
436
|
+
const p = el.p({ ...(pPr ? { pPr: deepCopy(pPr) } : {}), content: runs });
|
|
437
|
+
content.splice(at++, 0, p);
|
|
438
|
+
linkParents(p, this.sdt.sdtContent);
|
|
439
|
+
assignParaIds(this.sdt.sdtContent, [p]);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
121
442
|
insertTextInto(text, location) {
|
|
122
443
|
const form = this.form;
|
|
123
444
|
if ((form === 'Row' || form === 'Cell') && location === 'Replace') {
|
|
@@ -125,6 +446,7 @@ export class ContentControl {
|
|
|
125
446
|
}
|
|
126
447
|
if (form !== 'Run')
|
|
127
448
|
return this.body().insertText(text, location);
|
|
449
|
+
text = textForRunControl(this, dropExcluded(text)); // CR-002 section 60: F4, F3
|
|
128
450
|
const span = this.runSpan();
|
|
129
451
|
if (!span)
|
|
130
452
|
throw new Docx4JException('This content control is not in a paragraph');
|
|
@@ -139,7 +461,31 @@ export class ContentControl {
|
|
|
139
461
|
}
|
|
140
462
|
/** A paragraph at the start or end of a block control; before or after the paragraph of a run control. */
|
|
141
463
|
insertParagraph(text, location) {
|
|
464
|
+
this.assertWritable();
|
|
142
465
|
const form = this.form;
|
|
466
|
+
if ((location === 'Start' || location === 'End') && form === 'Block' && this.isShowingPlaceholder) {
|
|
467
|
+
// CR-002 section 63, D1: the placeholder goes, its paragraph left empty, the flag cleared, and the new paragraph
|
|
468
|
+
// follows, in either form (check 64 cases 6 and 27, check 63's run 2 cases 3 and 29)
|
|
469
|
+
this.takePlaceholderRuns(form);
|
|
470
|
+
this.isShowingPlaceholder = false;
|
|
471
|
+
}
|
|
472
|
+
if ((location === 'Start' || location === 'End') && isBoundBlockControl(this)) {
|
|
473
|
+
// Word holds a bound block control to one paragraph (CR-002 section 61): into a single-line one the text joins
|
|
474
|
+
// its paragraph, as Word's insertParagraph writes it; into a multi-line one Word's own second paragraph makes a
|
|
475
|
+
// file Word can't open, so it is refused (check 56, H2)
|
|
476
|
+
if (this.isMultiLine) {
|
|
477
|
+
throw new NotSupportedError('ContentControl', 'insertParagraph', 'this control is bound by w:dataBinding, and Word holds a bound '
|
|
478
|
+
+ "block control to one paragraph: its own second paragraph in a multi-line one makes a file Word can't open (CR-002 section 61, check 56); "
|
|
479
|
+
+ 'insertText with a line feed writes a line break');
|
|
480
|
+
}
|
|
481
|
+
const paragraphs = this.paragraphs;
|
|
482
|
+
const p = location === 'Start' ? paragraphs[0] : paragraphs[paragraphs.length - 1];
|
|
483
|
+
if (p) {
|
|
484
|
+
p.insertText(text, location);
|
|
485
|
+
this.syncBoundNode();
|
|
486
|
+
return p;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
143
489
|
if ((form === 'Row' || form === 'Cell') && (location === 'Start' || location === 'End')) {
|
|
144
490
|
throw new Docx4JException(`A ${form.toLowerCase()}-level content control holds ${form === 'Row' ? 'rows' : 'cells'}, not paragraphs; insert into a cell's body`);
|
|
145
491
|
}
|
|
@@ -159,10 +505,19 @@ export class ContentControl {
|
|
|
159
505
|
return range ? range.search(text, options) : [];
|
|
160
506
|
}
|
|
161
507
|
/**
|
|
162
|
-
* The control's range. For a run-level control, exactly the text it holds; for the other forms,
|
|
163
|
-
* the first
|
|
508
|
+
* The control's range. For a run-level control, exactly the text it holds; for the other forms, its
|
|
509
|
+
* paragraphs: one `Range` from the first one's start to the last one's end (CR-014 phase 1), its 'Whole'
|
|
510
|
+
* ending with the last one's mark, as Office JS's does (check 51: `CCP1\rCCP2\rCCP3\r`, its 'Content'
|
|
511
|
+
* without the last `\r`).
|
|
164
512
|
*/
|
|
165
513
|
getRange(location = 'Whole') {
|
|
514
|
+
const range = this.rangeOf(location);
|
|
515
|
+
// its own 'Content' and 'Whole' ranges know the control, for a write into one with no width (CR-002 section 63, D4)
|
|
516
|
+
if ((location === 'Content' || location === 'Whole') && (this.form === 'Run' || this.form === 'Block'))
|
|
517
|
+
range.ofControl = { control: this, part: location };
|
|
518
|
+
return range;
|
|
519
|
+
}
|
|
520
|
+
rangeOf(location) {
|
|
166
521
|
if (this.form === 'Run') {
|
|
167
522
|
const span = this.runSpan();
|
|
168
523
|
if (!span)
|
|
@@ -174,22 +529,51 @@ export class ContentControl {
|
|
|
174
529
|
return new Range(paragraph, end, end);
|
|
175
530
|
return new Range(paragraph, start, end);
|
|
176
531
|
}
|
|
177
|
-
const
|
|
532
|
+
const paragraphs = this.paragraphs;
|
|
533
|
+
const first = paragraphs[0];
|
|
178
534
|
if (!first)
|
|
179
535
|
throw new Docx4JException('This content control holds no paragraph');
|
|
180
|
-
|
|
536
|
+
const last = paragraphs[paragraphs.length - 1];
|
|
537
|
+
if (location === 'End')
|
|
538
|
+
return last.getRange('End');
|
|
539
|
+
if (location === 'Start')
|
|
540
|
+
return first.getRange('Start');
|
|
541
|
+
// a row-level control, or one whose content ends with a table: 'Content' ends with the last cell's text, as
|
|
542
|
+
// the control's text does, and 'Whole' with \t\t, as Word 365 gives them (check 53)
|
|
543
|
+
const lastItem = [...this.content].reverse().find((item) => { var _a; return BLOCK_ITEMS.has((_a = typeNameOf(item)) !== null && _a !== void 0 ? _a : ''); });
|
|
544
|
+
if (this.form === 'Row' || (lastItem !== undefined && typeNameOf(lastItem) === 'org_docx4j_wml.Tbl')) {
|
|
545
|
+
return location === 'Whole' ? first.rangeTo(last, true, '\t\t') : first.rangeTo(last, false);
|
|
546
|
+
}
|
|
547
|
+
if (paragraphs.length === 1)
|
|
548
|
+
return first.getRange(location);
|
|
549
|
+
return new Range(first, 0, last.text.length, true, location === 'Whole', last);
|
|
181
550
|
}
|
|
182
|
-
/**
|
|
551
|
+
/**
|
|
552
|
+
* Removes the control; `keepContent` puts what it held in its place, as Office JS. Where the control held the
|
|
553
|
+
* paragraph a table cell ends with, that paragraph stays in its place, emptied, its properties kept, outside
|
|
554
|
+
* any control, as Word keeps a cell's last paragraph (check 52, case 95; CR-002 section 55.6).
|
|
555
|
+
*/
|
|
183
556
|
delete(keepContent = false) {
|
|
557
|
+
this.assertWritable();
|
|
184
558
|
const at = this.container.indexOf(this.element);
|
|
185
559
|
if (at < 0)
|
|
186
560
|
return;
|
|
187
561
|
const content = keepContent ? [...this.content] : [];
|
|
562
|
+
const owner = ownerOf(this.container, this.parentBody);
|
|
563
|
+
const cell = this.form === 'Block' ? wrappingCellOf(owner) : undefined;
|
|
564
|
+
const end = cell ? paragraphEndOf(cell.content) : undefined;
|
|
188
565
|
this.container.splice(at, 1, ...content);
|
|
189
|
-
if (keepContent)
|
|
190
|
-
const owner = ownerOf(this.container, this.parentBody);
|
|
566
|
+
if (keepContent)
|
|
191
567
|
for (const item of content)
|
|
192
568
|
linkParents(item, owner);
|
|
569
|
+
if (cell && !endsWithParagraph(cell.content)) {
|
|
570
|
+
if (end) {
|
|
571
|
+
end.element.value.content = [];
|
|
572
|
+
this.container.splice(at, 0, end.element);
|
|
573
|
+
linkParents(end.element, owner);
|
|
574
|
+
}
|
|
575
|
+
else
|
|
576
|
+
endWithParagraph(cell);
|
|
193
577
|
}
|
|
194
578
|
}
|
|
195
579
|
/** The XML of the control (extension). */
|
|
@@ -213,6 +597,7 @@ export class ContentControl {
|
|
|
213
597
|
setProperty(localPart, value, make) {
|
|
214
598
|
var _a, _b;
|
|
215
599
|
var _c;
|
|
600
|
+
this.assertWritable();
|
|
216
601
|
const sdt = this.sdt;
|
|
217
602
|
(_a = sdt.sdtPr) !== null && _a !== void 0 ? _a : (sdt.sdtPr = {});
|
|
218
603
|
const items = ((_b = (_c = sdt.sdtPr).rPrOrAliasOrLock) !== null && _b !== void 0 ? _b : (_c.rPrOrAliasOrLock = []));
|
|
@@ -254,7 +639,11 @@ export class ContentControl {
|
|
|
254
639
|
};
|
|
255
640
|
let start;
|
|
256
641
|
let end = 0;
|
|
257
|
-
|
|
642
|
+
// where the walk passes the control: a control holding no text stands there, after the text before it
|
|
643
|
+
// in the paragraph (before, it was reported at 0, and text put into it went to the paragraph's start)
|
|
644
|
+
let at = 0;
|
|
645
|
+
const segs = paragraph.segments({ visit: (el, offset) => { if (el === this.element)
|
|
646
|
+
at = offset; } });
|
|
258
647
|
for (const seg of segs) {
|
|
259
648
|
if (!inside(seg.run))
|
|
260
649
|
continue;
|
|
@@ -262,34 +651,8 @@ export class ContentControl {
|
|
|
262
651
|
start = seg.start;
|
|
263
652
|
end = seg.end;
|
|
264
653
|
}
|
|
265
|
-
if (start === undefined)
|
|
266
|
-
|
|
267
|
-
// paragraph (before, it was reported at 0, and text put into it went to the paragraph's start)
|
|
268
|
-
let pos = 0;
|
|
269
|
-
let found = false;
|
|
270
|
-
const visit = (items) => {
|
|
271
|
-
for (const el of items !== null && items !== void 0 ? items : []) {
|
|
272
|
-
if (found)
|
|
273
|
-
return;
|
|
274
|
-
if (el === this.element) {
|
|
275
|
-
found = true;
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const v = el.value;
|
|
279
|
-
if (typeof v !== 'object' || v === null)
|
|
280
|
-
continue;
|
|
281
|
-
if (typeNameOf(el) === 'org_docx4j_wml.R') {
|
|
282
|
-
const last = segs.filter((seg) => seg.run === v).pop();
|
|
283
|
-
if (last)
|
|
284
|
-
pos = last.end;
|
|
285
|
-
}
|
|
286
|
-
else
|
|
287
|
-
visit(runItemsOf(v));
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
visit(paragraph.p.content);
|
|
291
|
-
return { paragraph, start: pos, end: pos, empty: true };
|
|
292
|
-
}
|
|
654
|
+
if (start === undefined)
|
|
655
|
+
return { paragraph, start: at, end: at, empty: true };
|
|
293
656
|
return { paragraph, start, end };
|
|
294
657
|
}
|
|
295
658
|
/**
|
|
@@ -300,9 +663,10 @@ export class ContentControl {
|
|
|
300
663
|
insertIntoEmpty(paragraph, text) {
|
|
301
664
|
var _a, _b;
|
|
302
665
|
var _c;
|
|
666
|
+
text = textForRunControl(this, dropExcluded(text));
|
|
303
667
|
const sdt = this.sdt;
|
|
304
668
|
const items = ((_b = (_c = ((_a = sdt.sdtContent) !== null && _a !== void 0 ? _a : (sdt.sdtContent = {}))).content) !== null && _b !== void 0 ? _b : (_c.content = []));
|
|
305
|
-
const run = runOf(
|
|
669
|
+
const run = runOf(officeItems(text));
|
|
306
670
|
const tracker = paragraph.changeTracker;
|
|
307
671
|
const item = tracker ? tracker.ins([run]) : run;
|
|
308
672
|
const at = items.findIndex((el) => { var _a; return ((_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'customXmlInsRangeStart'; });
|
|
@@ -318,26 +682,52 @@ export class ContentControl {
|
|
|
318
682
|
return new XmlMapping(this, pkg === null || pkg === void 0 ? void 0 : pkg.customXmlParts);
|
|
319
683
|
}
|
|
320
684
|
/**
|
|
321
|
-
* The placeholder Word shows for an empty control: the
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
685
|
+
* The placeholder Word shows for an empty control: the text of the glossary entry the control's `w:placeholder` names,
|
|
686
|
+
* as Office JS reads it (CR-002 section 63, D8; check 65), `null` where the control names none (the bare form) or the
|
|
687
|
+
* glossary lacks the entry. The glossary is read when the body is (`getBody`, `Word.run`). Setting it names Word's
|
|
688
|
+
* default entry for Word's default text, else a new entry holding the text, named as Word names one (32 hex digits),
|
|
689
|
+
* which is written into the glossary on save, the entry named before left there; a control showing its placeholder,
|
|
690
|
+
* or holding nothing, shows the new text. A control's content is left alone. Until section 63 the text was written as
|
|
691
|
+
* the control's content in the bare form, and a control holding content refused it.
|
|
326
692
|
*/
|
|
327
693
|
get placeholderText() {
|
|
328
|
-
|
|
694
|
+
var _a, _b;
|
|
695
|
+
const name = this.placeholderEntry;
|
|
696
|
+
if (name === undefined)
|
|
697
|
+
return null;
|
|
698
|
+
return (_b = (_a = this.glossary) === null || _a === void 0 ? void 0 : _a.textOf(name)) !== null && _b !== void 0 ? _b : null;
|
|
329
699
|
}
|
|
330
700
|
set placeholderText(text) {
|
|
331
|
-
var _a;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
701
|
+
var _a, _b, _c;
|
|
702
|
+
var _d;
|
|
703
|
+
this.assertWritable();
|
|
704
|
+
// the kind's default text, or '', names its default entry (section 63.22); any other text a new entry of its own
|
|
705
|
+
const fallback = (_a = this.defaultPlaceholderEntry) !== null && _a !== void 0 ? _a : DEFAULT_ENTRIES.text;
|
|
706
|
+
const own = text !== '' && text !== fallback.text;
|
|
707
|
+
const name = own ? newEntryName() : fallback.name;
|
|
708
|
+
const shown = own ? text : fallback.text;
|
|
709
|
+
namePlaceholderEntry(((_b = (_d = this.sdt).sdtPr) !== null && _b !== void 0 ? _b : (_d.sdtPr = {})), name);
|
|
710
|
+
(_c = this.glossary) === null || _c === void 0 ? void 0 : _c.require(name, shown);
|
|
711
|
+
// the entry's run, as the glossary will hold it: the control's own run properties aren't over it (check 68)
|
|
712
|
+
if (this.isShowingPlaceholder || this.text === '')
|
|
713
|
+
this.setBoundContent([placeholderRun(undefined, shown)], true);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Blocks taken out of the control's content: the relationships only they used (a picture's image) and the comments
|
|
717
|
+
* whose references they held go when the package is saved, as a deletion's do.
|
|
718
|
+
*/
|
|
719
|
+
removeBlocks(content, blocks) {
|
|
720
|
+
const pkg = this.parentBody.package_;
|
|
721
|
+
const ids = new Set();
|
|
722
|
+
relationshipIdsIn(blocks, ids);
|
|
723
|
+
if (ids.size > 0)
|
|
724
|
+
queueRelationshipRemovals(pkg === null || pkg === void 0 ? void 0 : pkg.pendingRelationshipRemovals, this.parentBody.part, ids);
|
|
725
|
+
if (pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals)
|
|
726
|
+
commentReferencesIn(blocks, pkg.pendingCommentRemovals);
|
|
727
|
+
for (const block of blocks) {
|
|
728
|
+
const at = content.indexOf(block);
|
|
729
|
+
if (at >= 0)
|
|
730
|
+
content.splice(at, 1);
|
|
341
731
|
}
|
|
342
732
|
}
|
|
343
733
|
/** Office JS `appearance`: how Word draws the control (`w15:appearance`). */
|
|
@@ -402,19 +792,19 @@ export class ContentControl {
|
|
|
402
792
|
get datePickerContentControl() {
|
|
403
793
|
var _a;
|
|
404
794
|
const value = (_a = sdtProperty(this.sdt.sdtPr, 'date')) === null || _a === void 0 ? void 0 : _a.value;
|
|
405
|
-
return value ? new DatePickerContentControl(value) : undefined;
|
|
795
|
+
return value ? new DatePickerContentControl(value, () => this.assertWritable()) : undefined;
|
|
406
796
|
}
|
|
407
797
|
/** The list view, for a `w:dropDownList` control. */
|
|
408
798
|
get dropDownListContentControl() {
|
|
409
799
|
var _a;
|
|
410
800
|
const value = (_a = sdtProperty(this.sdt.sdtPr, 'dropDownList')) === null || _a === void 0 ? void 0 : _a.value;
|
|
411
|
-
return value ? new ListContentControl(value) : undefined;
|
|
801
|
+
return value ? new ListContentControl(value, () => this.assertWritable()) : undefined;
|
|
412
802
|
}
|
|
413
803
|
/** The list view, for a `w:comboBox` control. */
|
|
414
804
|
get comboBoxContentControl() {
|
|
415
805
|
var _a;
|
|
416
806
|
const value = (_a = sdtProperty(this.sdt.sdtPr, 'comboBox')) === null || _a === void 0 ? void 0 : _a.value;
|
|
417
|
-
return value ? new ListContentControl(value) : undefined;
|
|
807
|
+
return value ? new ListContentControl(value, () => this.assertWritable()) : undefined;
|
|
418
808
|
}
|
|
419
809
|
/** The picture view, for a `w:picture` control. */
|
|
420
810
|
get pictureContentControl() {
|
|
@@ -438,6 +828,7 @@ export class ContentControl {
|
|
|
438
828
|
putProperty(element) {
|
|
439
829
|
var _a, _b;
|
|
440
830
|
var _c;
|
|
831
|
+
this.assertWritable();
|
|
441
832
|
const sdt = this.sdt;
|
|
442
833
|
(_a = sdt.sdtPr) !== null && _a !== void 0 ? _a : (sdt.sdtPr = {});
|
|
443
834
|
const items = ((_b = (_c = sdt.sdtPr).rPrOrAliasOrLock) !== null && _b !== void 0 ? _b : (_c.rPrOrAliasOrLock = []));
|
|
@@ -452,6 +843,7 @@ export class ContentControl {
|
|
|
452
843
|
/** Removes a `w:sdtPr` child (extension). */
|
|
453
844
|
removeProperty(localPart, namespaceURI) {
|
|
454
845
|
var _a;
|
|
846
|
+
this.assertWritable();
|
|
455
847
|
const items = (_a = this.sdt.sdtPr) === null || _a === void 0 ? void 0 : _a.rPrOrAliasOrLock;
|
|
456
848
|
if (!items)
|
|
457
849
|
return;
|
|
@@ -469,6 +861,10 @@ export class ContentControl {
|
|
|
469
861
|
var _a, _b;
|
|
470
862
|
return ((_b = (_a = sdtProperty(this.sdt.sdtPr, 'text')) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.multiLine) === true;
|
|
471
863
|
}
|
|
864
|
+
/** Office JS's name for `isShowingPlaceholder` (`showingPlaceholderText`, WordApiDesktop 1.3, read-only there). */
|
|
865
|
+
get showingPlaceholderText() {
|
|
866
|
+
return this.isShowingPlaceholder;
|
|
867
|
+
}
|
|
472
868
|
/** Whether the control is showing its placeholder rather than a value (`w:showingPlcHdr`). */
|
|
473
869
|
get isShowingPlaceholder() {
|
|
474
870
|
var _a;
|
|
@@ -480,7 +876,7 @@ export class ContentControl {
|
|
|
480
876
|
this.removeProperty('showingPlcHdr');
|
|
481
877
|
return;
|
|
482
878
|
}
|
|
483
|
-
this.putProperty(el.showingPlcHdr({
|
|
879
|
+
this.putProperty(el.showingPlcHdr({})); // <w:showingPlcHdr/>, as Word writes it
|
|
484
880
|
}
|
|
485
881
|
/**
|
|
486
882
|
* Replaces what the control shows with these runs (docx4j BindingHandler's applyBoundContent):
|
|
@@ -490,16 +886,22 @@ export class ContentControl {
|
|
|
490
886
|
setBoundContent(runs, showingPlaceholder = false) {
|
|
491
887
|
var _a, _b, _c, _d;
|
|
492
888
|
var _e;
|
|
889
|
+
this.assertWritable();
|
|
493
890
|
const sdt = this.sdt;
|
|
494
891
|
(_a = sdt.sdtContent) !== null && _a !== void 0 ? _a : (sdt.sdtContent = {});
|
|
495
892
|
const content = ((_b = (_e = sdt.sdtContent).content) !== null && _b !== void 0 ? _b : (_e.content = []));
|
|
496
893
|
const first = content.find((e) => typeNameOf(e) !== undefined);
|
|
497
894
|
if (this.form === 'Run' || (first && typeNameOf(first) === 'org_docx4j_wml.R')) {
|
|
895
|
+
// a tracked control's own markers stay where they are, its inner end first and its inner start last (check 30)
|
|
896
|
+
const ends = content.filter((e) => { var _a, _b; return TRACKED_HOLDER_MARKER.test((_b = (_a = e.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '') && e.name.localPart.endsWith('End'); });
|
|
897
|
+
const starts = content.filter((e) => { var _a, _b; return TRACKED_HOLDER_MARKER.test((_b = (_a = e.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '') && e.name.localPart.endsWith('Start'); });
|
|
498
898
|
content.length = 0;
|
|
499
|
-
content.push(...runs);
|
|
899
|
+
content.push(...ends, ...runs, ...starts);
|
|
500
900
|
linkParents(content, sdt.sdtContent);
|
|
501
901
|
}
|
|
502
|
-
else {
|
|
902
|
+
else if (!isBoundBlockControl(this)) {
|
|
903
|
+
// a cell or row control, or a block control section 61's rule doesn't cover (a w15:dataBinding, Word's rich-text
|
|
904
|
+
// binding, or none): the value goes into its first paragraph, the rest as it is
|
|
503
905
|
const paragraph = this.paragraphs[0];
|
|
504
906
|
if (paragraph) {
|
|
505
907
|
paragraph.p.content = runs;
|
|
@@ -509,8 +911,30 @@ export class ContentControl {
|
|
|
509
911
|
const p = paragraphElementOf(runs);
|
|
510
912
|
content.push(p);
|
|
511
913
|
linkParents(p, sdt.sdtContent);
|
|
914
|
+
assignParaIds(sdt.sdtContent, [p]);
|
|
512
915
|
}
|
|
513
916
|
}
|
|
917
|
+
else {
|
|
918
|
+
// A bound block control holds one paragraph (CR-002 section 61): the value goes into its first paragraph and any
|
|
919
|
+
// other block it holds goes, as docx4j's bind.xslt writes one paragraph (section 61.6, docx4j-python's review).
|
|
920
|
+
// Before, the first paragraph took the value and the rest stayed, so a control holding two kept them, and Word
|
|
921
|
+
// still couldn't open the file.
|
|
922
|
+
const blocks = content.filter((e) => { var _a; return BLOCK_LEVEL_TYPES.has((_a = typeNameOf(e)) !== null && _a !== void 0 ? _a : ''); });
|
|
923
|
+
const paragraph = blocks.find((e) => typeNameOf(e) === 'org_docx4j_wml.P');
|
|
924
|
+
if (paragraph) {
|
|
925
|
+
paragraph.value.content = runs;
|
|
926
|
+
linkParents(paragraph.value.content, paragraph.value);
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
const p = paragraphElementOf(runs);
|
|
930
|
+
content.push(p);
|
|
931
|
+
linkParents(p, sdt.sdtContent);
|
|
932
|
+
assignParaIds(sdt.sdtContent, [p]);
|
|
933
|
+
}
|
|
934
|
+
const others = blocks.filter((e) => e !== paragraph);
|
|
935
|
+
if (others.length > 0)
|
|
936
|
+
this.removeBlocks(content, others);
|
|
937
|
+
}
|
|
514
938
|
this.isShowingPlaceholder = showingPlaceholder;
|
|
515
939
|
// The placeholder run names the PlaceholderText style, which a document made here lacks: the
|
|
516
940
|
// package splices the definition in when it is saved, as it does a new hyperlink's style. Word
|
|
@@ -527,6 +951,7 @@ export class ContentControl {
|
|
|
527
951
|
}
|
|
528
952
|
/** A copy of this control right after it, as Word's repeating-section + button does (extension). */
|
|
529
953
|
insertCopyAfter() {
|
|
954
|
+
this.assertWritable();
|
|
530
955
|
const at = this.container.indexOf(this.element);
|
|
531
956
|
if (at < 0)
|
|
532
957
|
throw new Docx4JException('This content control is not in its container any more');
|
|
@@ -536,27 +961,18 @@ export class ContentControl {
|
|
|
536
961
|
return new ContentControl(copy, this.container, this.parentBody);
|
|
537
962
|
}
|
|
538
963
|
/**
|
|
539
|
-
* Writes the control's text into the node it is bound to, when there is one and the XPath engine
|
|
540
|
-
* is warm. Silent when it is not: reading a custom XML part is `pkg.customXmlParts.load()`'s job,
|
|
964
|
+
* @internal Writes the control's text into the node it is bound to, when there is one and the XPath engine
|
|
965
|
+
* is warm (CR-002 section 63, D7: every text write into a bound control). Silent when it is not: reading a custom XML part is `pkg.customXmlParts.load()`'s job,
|
|
541
966
|
* and `updateFromContentControls()` is the explicit form of this write.
|
|
542
967
|
*/
|
|
543
968
|
syncBoundNode() {
|
|
544
969
|
try {
|
|
545
|
-
|
|
546
|
-
updateFromControl(this);
|
|
970
|
+
updateFromControl(this);
|
|
547
971
|
}
|
|
548
972
|
catch {
|
|
549
973
|
// the part is not parsed, or the engine is not ready: leave it to updateFromContentControls()
|
|
550
974
|
}
|
|
551
975
|
}
|
|
552
|
-
firstRunOfContent() {
|
|
553
|
-
var _a, _b, _c, _d;
|
|
554
|
-
const runs = this.form === 'Run' ? (_b = runItemsOf((_a = this.sdt.sdtContent) !== null && _a !== void 0 ? _a : {})) !== null && _b !== void 0 ? _b : [] : (_d = (_c = this.paragraphs[0]) === null || _c === void 0 ? void 0 : _c.runs) !== null && _d !== void 0 ? _d : [];
|
|
555
|
-
for (const item of runs)
|
|
556
|
-
if (typeNameOf(item) === 'org_docx4j_wml.R')
|
|
557
|
-
return item.value;
|
|
558
|
-
return undefined;
|
|
559
|
-
}
|
|
560
976
|
lockValue() {
|
|
561
977
|
var _a, _b;
|
|
562
978
|
return (_b = (_a = sdtProperty(this.sdt.sdtPr, 'lock')) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.val;
|
|
@@ -572,11 +988,22 @@ export class ContentControl {
|
|
|
572
988
|
}
|
|
573
989
|
this.putProperty(el.lock({ val }));
|
|
574
990
|
}
|
|
575
|
-
/**
|
|
991
|
+
/**
|
|
992
|
+
* True for a control in the selected branch of a kept `mc:AlternateContent`, at any level, which the
|
|
993
|
+
* collectors list as Word reads it (CR-013 phase 2). Such a view is read-only (D4).
|
|
994
|
+
*/
|
|
995
|
+
get inBranch() {
|
|
996
|
+
return inBranch(this.element.value);
|
|
997
|
+
}
|
|
998
|
+
/** Refuses a write to a control in a branch (CR-013 D4). Internal: the control's mutators, and its kinds', call it first. */
|
|
999
|
+
assertWritable() {
|
|
1000
|
+
assertOutsideBranch(this.element.value, 'ContentControl');
|
|
1001
|
+
}
|
|
1002
|
+
/** The table a row-level or cell-level control belongs to, through the row-level controls around it, nested to any depth. */
|
|
576
1003
|
ancestorTable() {
|
|
577
1004
|
var _a;
|
|
578
1005
|
let current = this.element.value.PARENT;
|
|
579
|
-
for (let guard = 0; current && guard <
|
|
1006
|
+
for (let guard = 0; current && guard < 256; guard++) {
|
|
580
1007
|
if (typeNameOf(current) === 'org_docx4j_wml.Tbl') {
|
|
581
1008
|
const tbl = current;
|
|
582
1009
|
const container = childrenOf(((_a = tbl.PARENT) !== null && _a !== void 0 ? _a : {}));
|
|
@@ -588,8 +1015,13 @@ export class ContentControl {
|
|
|
588
1015
|
return undefined;
|
|
589
1016
|
}
|
|
590
1017
|
}
|
|
591
|
-
/**
|
|
592
|
-
|
|
1018
|
+
/**
|
|
1019
|
+
* Every content control under a list of block-level items, in document order, nested ones included. In a
|
|
1020
|
+
* kept `mc:AlternateContent` a reader's (`'read'`, the default) takes the selected branch's controls (CR-013
|
|
1021
|
+
* phase 2), the binding passes (`'every'`) every branch's, each other branch's where the element stands
|
|
1022
|
+
* (phase 3).
|
|
1023
|
+
*/
|
|
1024
|
+
export function collectControls(items, body, out, mode = 'read') {
|
|
593
1025
|
var _a;
|
|
594
1026
|
for (const element of items) {
|
|
595
1027
|
const tn = typeNameOf(element);
|
|
@@ -598,24 +1030,29 @@ export function collectControls(items, body, out) {
|
|
|
598
1030
|
if (SDT_TYPES.has(tn)) {
|
|
599
1031
|
const control = new ContentControl(element, items, body);
|
|
600
1032
|
out.push(control);
|
|
601
|
-
collectControls(control.content, body, out);
|
|
1033
|
+
collectControls(control.content, body, out, mode);
|
|
602
1034
|
continue;
|
|
603
1035
|
}
|
|
604
1036
|
if (tn === 'org_docx4j_wml.P') {
|
|
605
|
-
collectRunControls(element.value, body, out);
|
|
1037
|
+
collectRunControls(element.value, body, out, mode);
|
|
606
1038
|
continue;
|
|
607
1039
|
}
|
|
608
1040
|
if (tn === 'org_docx4j_wml.Tbl') {
|
|
609
|
-
collectInRows((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out);
|
|
1041
|
+
collectInRows((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out, mode);
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
if (tn === ALTERNATE_CONTENT) {
|
|
1045
|
+
for (const branch of branchListsOf(element.value, mode))
|
|
1046
|
+
collectControls(branch, body, out, mode);
|
|
610
1047
|
continue;
|
|
611
1048
|
}
|
|
612
1049
|
const children = childrenOf(element.value);
|
|
613
1050
|
if (children)
|
|
614
|
-
collectControls(children, body, out);
|
|
1051
|
+
collectControls(children, body, out, mode);
|
|
615
1052
|
}
|
|
616
1053
|
}
|
|
617
1054
|
/** Row-level controls and the controls inside the rows of a table. */
|
|
618
|
-
function collectInRows(items, body, out) {
|
|
1055
|
+
function collectInRows(items, body, out, mode) {
|
|
619
1056
|
var _a;
|
|
620
1057
|
for (const element of items) {
|
|
621
1058
|
const tn = typeNameOf(element);
|
|
@@ -624,15 +1061,18 @@ function collectInRows(items, body, out) {
|
|
|
624
1061
|
if (SDT_TYPES.has(tn)) {
|
|
625
1062
|
const control = new ContentControl(element, items, body);
|
|
626
1063
|
out.push(control);
|
|
627
|
-
collectInRows(control.content, body, out);
|
|
1064
|
+
collectInRows(control.content, body, out, mode);
|
|
628
1065
|
continue;
|
|
629
1066
|
}
|
|
630
1067
|
if (tn === 'org_docx4j_wml.Tr')
|
|
631
|
-
collectInCells((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out);
|
|
1068
|
+
collectInCells((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out, mode);
|
|
1069
|
+
else if (tn === ALTERNATE_CONTENT)
|
|
1070
|
+
for (const branch of branchListsOf(element.value, mode))
|
|
1071
|
+
collectInRows(branch, body, out, mode);
|
|
632
1072
|
}
|
|
633
1073
|
}
|
|
634
1074
|
/** Cell-level controls and the controls inside the cells of a row. */
|
|
635
|
-
function collectInCells(items, body, out) {
|
|
1075
|
+
function collectInCells(items, body, out, mode) {
|
|
636
1076
|
var _a;
|
|
637
1077
|
for (const element of items) {
|
|
638
1078
|
const tn = typeNameOf(element);
|
|
@@ -641,18 +1081,29 @@ function collectInCells(items, body, out) {
|
|
|
641
1081
|
if (SDT_TYPES.has(tn)) {
|
|
642
1082
|
const control = new ContentControl(element, items, body);
|
|
643
1083
|
out.push(control);
|
|
644
|
-
collectInCells(control.content, body, out);
|
|
1084
|
+
collectInCells(control.content, body, out, mode);
|
|
645
1085
|
continue;
|
|
646
1086
|
}
|
|
647
1087
|
if (tn === 'org_docx4j_wml.Tc')
|
|
648
|
-
collectControls((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out);
|
|
1088
|
+
collectControls((_a = childrenOf(element.value)) !== null && _a !== void 0 ? _a : [], body, out, mode);
|
|
1089
|
+
else if (tn === ALTERNATE_CONTENT)
|
|
1090
|
+
for (const branch of branchListsOf(element.value, mode))
|
|
1091
|
+
collectInCells(branch, body, out, mode);
|
|
649
1092
|
}
|
|
650
1093
|
}
|
|
651
1094
|
/** The run-level controls in a paragraph (or any run holder), in order, nested ones included. */
|
|
652
|
-
export function collectRunControls(holder, body, out) {
|
|
653
|
-
|
|
654
|
-
if (
|
|
1095
|
+
export function collectRunControls(holder, body, out, mode = 'read') {
|
|
1096
|
+
// a kept mc:AlternateContent among runs: its selected branch's (CR-013 phase 2), or every branch's (phase 3)
|
|
1097
|
+
if (holder.TYPE_NAME === ALTERNATE_CONTENT) {
|
|
1098
|
+
for (const branch of branchListsOf(holder, mode))
|
|
1099
|
+
collectRunItems(branch, body, out, mode);
|
|
655
1100
|
return;
|
|
1101
|
+
}
|
|
1102
|
+
const items = runItemsOf(holder);
|
|
1103
|
+
if (items)
|
|
1104
|
+
collectRunItems(items, body, out, mode);
|
|
1105
|
+
}
|
|
1106
|
+
function collectRunItems(items, body, out, mode) {
|
|
656
1107
|
for (const element of items) {
|
|
657
1108
|
const tn = typeNameOf(element);
|
|
658
1109
|
if (tn === undefined)
|
|
@@ -660,15 +1111,84 @@ export function collectRunControls(holder, body, out) {
|
|
|
660
1111
|
if (SDT_TYPES.has(tn)) {
|
|
661
1112
|
const control = new ContentControl(element, items, body);
|
|
662
1113
|
out.push(control);
|
|
663
|
-
collectRunControls(element.value, body, out);
|
|
1114
|
+
collectRunControls(element.value, body, out, mode);
|
|
664
1115
|
continue;
|
|
665
1116
|
}
|
|
666
1117
|
if (tn !== 'org_docx4j_wml.R')
|
|
667
|
-
collectRunControls(element.value, body, out);
|
|
1118
|
+
collectRunControls(element.value, body, out, mode);
|
|
668
1119
|
}
|
|
669
1120
|
}
|
|
670
1121
|
function paragraphElement(text) {
|
|
671
|
-
|
|
1122
|
+
const items = officeItems(forWriter(dropExcluded(text), 'one-paragraph'));
|
|
1123
|
+
return el.p({ content: items.length === 0 ? [] : [el.r({ content: items })] });
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* A view of the content control whose `w:sdt` value this is, found in its owner's list (a paragraph's runs, a body's
|
|
1127
|
+
* blocks, a holder's items).
|
|
1128
|
+
*/
|
|
1129
|
+
export function controlViewOf(sdt, body) {
|
|
1130
|
+
const owner = sdt.PARENT;
|
|
1131
|
+
for (const value of Object.values(owner !== null && owner !== void 0 ? owner : {})) {
|
|
1132
|
+
if (!Array.isArray(value))
|
|
1133
|
+
continue;
|
|
1134
|
+
const element = value.find((e) => (e === null || e === void 0 ? void 0 : e.value) === sdt);
|
|
1135
|
+
if (element)
|
|
1136
|
+
return new ContentControl(element, value, body);
|
|
1137
|
+
}
|
|
1138
|
+
throw new Docx4JException('This content control is not in the document any more');
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* A block-level control bound by `w:dataBinding`, untyped, `w:text` or `w:richText`, whose node doesn't hold flat OPC
|
|
1142
|
+
* (CR-002 section 61 H1): Word holds its content to one paragraph, and can't open a file where it holds more (check 56,
|
|
1143
|
+
* docx4j-python's file 3). A node not resolved (the custom XML parts not loaded) counts as not flat OPC. `w15:dataBinding`
|
|
1144
|
+
* is Word's rich-text binding, and isn't this.
|
|
1145
|
+
*/
|
|
1146
|
+
export function isBoundBlockControl(control) {
|
|
1147
|
+
if (control.form !== 'Block')
|
|
1148
|
+
return false;
|
|
1149
|
+
const pr = control.sdt.sdtPr;
|
|
1150
|
+
if (sdtProperty(pr, 'dataBinding') === undefined)
|
|
1151
|
+
return false;
|
|
1152
|
+
const kind = sdtKindOf(pr);
|
|
1153
|
+
if (kind !== 'RichText' && kind !== 'PlainText')
|
|
1154
|
+
return false;
|
|
1155
|
+
let node;
|
|
1156
|
+
try {
|
|
1157
|
+
node = control.xmlMapping.customXmlNode;
|
|
1158
|
+
}
|
|
1159
|
+
catch {
|
|
1160
|
+
node = undefined; // the parts aren't parsed: the rule applies
|
|
1161
|
+
}
|
|
1162
|
+
return !(node && holdsFlatOpc(node.text));
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* How a control holds lines, as Word reads it: `multi` or `single` for a plain-text control (`w:text` and its
|
|
1166
|
+
* `w:multiLine`), `single` for a bound block control Word reads as plain text (section 61 H4); undefined for any other.
|
|
1167
|
+
*/
|
|
1168
|
+
export function plainTextLinesOf(control) {
|
|
1169
|
+
if (sdtProperty(control.sdt.sdtPr, 'text') !== undefined)
|
|
1170
|
+
return control.isMultiLine ? 'multi' : 'single';
|
|
1171
|
+
return isBoundBlockControl(control) ? 'single' : undefined;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Text for a run-level control: a line feed or carriage return there is refused before anything changes (CR-002
|
|
1175
|
+
* section 60 F4: Word, given one, moved a bound plain-text control to block level), and a plain-text control's breaks
|
|
1176
|
+
* go as its kind takes them (F3: dropped in a single-line one).
|
|
1177
|
+
*/
|
|
1178
|
+
export function textForRunControl(control, text) {
|
|
1179
|
+
if (/[\r\n]/.test(text)) {
|
|
1180
|
+
throw new NotSupportedError('ContentControl', 'insertText', 'a line feed or carriage return in a run-level content control would make a '
|
|
1181
|
+
+ 'paragraph break inside it, which Word answers by moving the control to block level (CR-002 section 60, F4); write a line break (U+000B) instead');
|
|
1182
|
+
}
|
|
1183
|
+
const lines = plainTextLinesOf(control);
|
|
1184
|
+
return lines === 'single' ? forWriter(text, 'single-line') : text;
|
|
1185
|
+
}
|
|
1186
|
+
/** An item put last in a control's content, before the inner start marker a control tracked as inserted ends with (check 30). */
|
|
1187
|
+
function pushBeforeMarker(items, item) {
|
|
1188
|
+
var _a, _b;
|
|
1189
|
+
const last = items[items.length - 1];
|
|
1190
|
+
const marker = last !== undefined && TRACKED_HOLDER_MARKER.test((_b = (_a = last.name) === null || _a === void 0 ? void 0 : _a.localPart) !== null && _b !== void 0 ? _b : '') && last.name.localPart.endsWith('Start');
|
|
1191
|
+
items.splice(marker ? items.length - 1 : items.length, 0, item);
|
|
672
1192
|
}
|
|
673
1193
|
/** A `w:p` holding these run-level items (CR-002 phase E: the bound content of an empty control). */
|
|
674
1194
|
function paragraphElementOf(runs) {
|
|
@@ -695,3 +1215,56 @@ function ownerOf(container, body) {
|
|
|
695
1215
|
};
|
|
696
1216
|
return (_a = seek(body.container)) !== null && _a !== void 0 ? _a : body.container;
|
|
697
1217
|
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Refuses, before anything changes, putting blocks into a block-level control bound by `w:dataBinding` (untyped,
|
|
1220
|
+
* `w:text` or `w:richText`, its node not flat OPC) that would leave it holding anything but one paragraph: Word can't
|
|
1221
|
+
* open such a file, and its repair loses the extra blocks (CR-002 section 61, check 56). Word's own error where
|
|
1222
|
+
* measured (`insertTable` `InvalidArgument`, `insertOoxml` `GeneralException`), else `NotSupportedError` (H3).
|
|
1223
|
+
*/
|
|
1224
|
+
export function assertControlHolds(body, holder, container, elements, refusal, member = ['Body', 'insertElement']) {
|
|
1225
|
+
var _a;
|
|
1226
|
+
const owner = holder;
|
|
1227
|
+
if ((owner === null || owner === void 0 ? void 0 : owner.TYPE_NAME) !== 'org_docx4j_wml.SdtContentBlock' || ((_a = owner.PARENT) === null || _a === void 0 ? void 0 : _a.TYPE_NAME) !== 'org_docx4j_wml.SdtBlock')
|
|
1228
|
+
return;
|
|
1229
|
+
const control = controlViewOf(owner.PARENT, body);
|
|
1230
|
+
if (!isBoundBlockControl(control))
|
|
1231
|
+
return;
|
|
1232
|
+
const blocks = [...container, ...elements].filter((e) => { var _a; return BLOCK_LEVEL_TYPES.has((_a = typeNameOf(e)) !== null && _a !== void 0 ? _a : ''); });
|
|
1233
|
+
if (blocks.length === 1 && typeNameOf(blocks[0]) === 'org_docx4j_wml.P')
|
|
1234
|
+
return;
|
|
1235
|
+
const message = `this content control is bound by w:dataBinding, and Word holds a bound block control to one paragraph: it can't open `
|
|
1236
|
+
+ `a file where one holds more, and its repair loses them (CR-002 section 61, check 56)`;
|
|
1237
|
+
if (refusal === 'InvalidArgument')
|
|
1238
|
+
throw new InvalidArgument(`Table: ${message}`);
|
|
1239
|
+
if (refusal === 'GeneralException')
|
|
1240
|
+
throw new GeneralException(`insertOoxml: ${message}`);
|
|
1241
|
+
throw new NotSupportedError(member[0], member[1], message);
|
|
1242
|
+
}
|
|
1243
|
+
/** The controls of `options.types`, or all of them. */
|
|
1244
|
+
export function controlsOfTypes(controls, options) {
|
|
1245
|
+
const types = options === null || options === void 0 ? void 0 : options.types;
|
|
1246
|
+
return types && types.length > 0 ? controls.filter((c) => types.includes(c.type)) : controls;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Whether a control of this kind made on an empty range shows its default placeholder, as Word's does (CR-002 section
|
|
1250
|
+
* 63, D8; check 64; Jason's `placeholders.docx`, section 63.22): a text control, a date picker, a drop-down list or a
|
|
1251
|
+
* combo box. The building block and repeating section kinds are made as before.
|
|
1252
|
+
*/
|
|
1253
|
+
export function showsPlaceholderWhenMade(kind) {
|
|
1254
|
+
return kind === undefined || kind === 'RichText' || kind === 'PlainText' || kind === 'DatePicker' || kind === 'DropDownList' || kind === 'ComboBox';
|
|
1255
|
+
}
|
|
1256
|
+
/** A stand-in for a block-level control about to be made, for `assertControlHolds` before `sdtOf` takes its content. */
|
|
1257
|
+
export const BLOCK_CONTROL = Object.freeze({
|
|
1258
|
+
name: { namespaceURI: 'http://schemas.openxmlformats.org/wordprocessingml/2006/main', localPart: 'sdt' },
|
|
1259
|
+
value: { TYPE_NAME: 'org_docx4j_wml.SdtBlock' },
|
|
1260
|
+
});
|
|
1261
|
+
/**
|
|
1262
|
+
* Whether a block-level control holds one paragraph and nothing else at block level, or nothing yet: what a bound
|
|
1263
|
+
* block control may hold (CR-002 section 61).
|
|
1264
|
+
*/
|
|
1265
|
+
export function holdsOneParagraph(control) {
|
|
1266
|
+
var _a, _b;
|
|
1267
|
+
const content = (_b = (_a = control.sdt.sdtContent) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : [];
|
|
1268
|
+
const blocks = content.filter((e) => { var _a; return BLOCK_LEVEL_TYPES.has((_a = typeNameOf(e)) !== null && _a !== void 0 ? _a : ''); });
|
|
1269
|
+
return blocks.length === 0 || (blocks.length === 1 && typeNameOf(blocks[0]) === 'org_docx4j_wml.P');
|
|
1270
|
+
}
|