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