@docx4j/core-ts 0.6.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/README.md +60 -0
- package/dist/model/content/Body.mjs +6 -17
- package/dist/model/content/Comment.d.mts +2 -2
- package/dist/model/content/Comment.mjs +12 -7
- package/dist/model/content/ContentControl.d.mts +1 -1
- package/dist/model/content/ContentControl.mjs +8 -4
- package/dist/model/content/Font.d.mts +4 -1
- package/dist/model/content/Font.mjs +6 -2
- package/dist/model/content/List.d.mts +6 -3
- package/dist/model/content/List.mjs +15 -8
- package/dist/model/content/Paragraph.d.mts +12 -2
- package/dist/model/content/Paragraph.mjs +31 -11
- package/dist/model/content/Table.mjs +26 -16
- package/dist/model/content/TrackedChange.d.mts +2 -2
- package/dist/model/content/TrackedChange.mjs +6 -3
- package/dist/model/content/index.d.mts +3 -1
- package/dist/model/content/index.mjs +4 -1
- package/dist/model/content/ooxml.mjs +12 -1
- package/dist/model/content/paraIds.d.mts +5 -0
- package/dist/model/content/paraIds.mjs +54 -0
- package/dist/model/content/search.d.mts +2 -0
- package/dist/model/content/search.mjs +51 -7
- package/dist/model/content/story.d.mts +5 -2
- package/dist/model/content/story.mjs +6 -3
- package/dist/model/content/tableRules.mjs +2 -0
- package/dist/model/content/tracking.d.mts +35 -6
- package/dist/model/content/tracking.mjs +93 -12
- package/dist/model/content/tree.d.mts +7 -8
- package/dist/model/content/tree.mjs +7 -9
- package/dist/model/content/wordRange.d.mts +48 -0
- package/dist/model/content/wordRange.mjs +97 -0
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +22 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +48 -2
- package/dist/model/customxml/XmlMapping.d.mts +31 -7
- package/dist/model/customxml/XmlMapping.mjs +53 -10
- package/dist/model/customxml/bindings.d.mts +12 -2
- package/dist/model/customxml/bindings.mjs +98 -16
- package/dist/model/customxml/index.d.mts +1 -0
- package/dist/model/customxml/index.mjs +1 -0
- package/dist/model/customxml/opendope.d.mts +4 -1
- package/dist/model/customxml/opendope.mjs +9 -6
- package/dist/model/customxml/pictures.mjs +5 -3
- package/dist/model/customxml/storeItems.d.mts +57 -0
- package/dist/model/customxml/storeItems.mjs +122 -0
- package/dist/model/customxml/xhtml.mjs +15 -20
- package/dist/model/dateZone.d.mts +25 -0
- package/dist/model/dateZone.mjs +57 -0
- package/dist/model/index.d.mts +1 -0
- package/dist/model/index.mjs +1 -0
- package/dist/model/properties/PropertyResolver.d.mts +14 -1
- package/dist/model/properties/PropertyResolver.mjs +18 -3
- package/dist/model/properties/index.d.mts +1 -1
- package/dist/model/properties/log.mjs +2 -2
- package/dist/model/properties/styleUtil.d.mts +3 -1
- package/dist/model/properties/styleUtil.mjs +20 -24
- package/dist/model/text/CaseMapping.d.mts +38 -0
- package/dist/model/text/CaseMapping.mjs +86 -0
- package/dist/model/text/Extractor.d.mts +66 -6
- package/dist/model/text/Extractor.mjs +300 -56
- package/dist/model/text/PlainText.mjs +7 -4
- package/dist/model/text/TextContext.d.mts +103 -8
- package/dist/model/text/TextContext.mjs +228 -53
- package/dist/model/text/TextOptions.d.mts +88 -5
- package/dist/model/text/TextOptions.mjs +158 -17
- package/dist/model/text/TextResult.d.mts +126 -9
- package/dist/model/text/TextResult.mjs +115 -12
- package/dist/model/text/WmlToMarkdown.mjs +32 -7
- package/dist/model/text/caseMapping.generated.d.mts +7 -0
- package/dist/model/text/caseMapping.generated.mjs +214 -0
- package/dist/model/text/index.d.mts +3 -2
- package/dist/model/text/index.mjs +1 -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/toApiScript.mjs +8 -7
- 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 +17 -2
- package/dist/opc/Save.mjs +100 -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 +7 -4
- package/dist/opc/index.mjs +5 -3
- package/dist/opc/mce/preprocessor.d.mts +121 -10
- package/dist/opc/mce/preprocessor.mjs +122 -21
- package/dist/opc/mce/understood.d.mts +3 -35
- package/dist/opc/mce/understood.mjs +27 -53
- 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 +98 -5
- package/dist/packages/OpcPackage.mjs +122 -7
- package/dist/packages/WordprocessingMLPackage.mjs +3 -1
- package/dist/packages/index.d.mts +1 -1
- package/dist/parts/Part.d.mts +5 -1
- package/dist/parts/RelationshipsPart.d.mts +15 -0
- package/dist/parts/RelationshipsPart.mjs +66 -1
- package/dist/parts/XmlPart.d.mts +54 -5
- package/dist/parts/XmlPart.mjs +203 -10
- package/dist/parts/wml/index.d.mts +12 -1
- package/dist/parts/wml/index.mjs +25 -1
- package/package.json +2 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { wallClockIn } from '../dateZone.mjs';
|
|
1
2
|
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
2
3
|
import * as el from '@docx4j/generated-objects-ts/el/org_docx4j_wml';
|
|
3
4
|
import { sdtProperty, textOf } from '@docx4j/generated-objects-ts/builders/wml';
|
|
4
5
|
import { bindPicture } from './pictures.mjs';
|
|
6
|
+
import { isPropertiesStoreItem } from './storeItems.mjs';
|
|
5
7
|
/**
|
|
6
8
|
* The style Word gives placeholder text, and the text docx4j's placeholder.xml carries: Word 365's since docx4j's CR-063
|
|
7
9
|
* D5 (CR-002 section 63.22), where it was Word 2010's "Click here to enter text.".
|
|
@@ -78,7 +80,9 @@ export function placeholderRun(rPr, text = PLACEHOLDER_TEXT) {
|
|
|
78
80
|
* A date in Word's `w:dateFormat` pattern (the .NET forms Word writes: `d MMMM yyyy`,
|
|
79
81
|
* `dddd, d MMMM yyyy`, `d/MM/yyyy`), in the control's `w:lid` locale. Month and day names come
|
|
80
82
|
* from `Intl`; text inside single or double quotes is literal. docx4j uses Java's
|
|
81
|
-
* `SimpleDateFormat` with `dddd` mapped to `EEEE`; this is the same vocabulary.
|
|
83
|
+
* `SimpleDateFormat` with `dddd` mapped to `EEEE`; this is the same vocabulary. `am/pm` and
|
|
84
|
+
* `AM/PM` (any mix of cases, as docx4j's `isAMPM`) are the locale's day-period names upper-cased,
|
|
85
|
+
* as Word shows both (CR-020 D5, docx4j CR-058's `wordAmPm`): `h:mm am/pm` at 15:05 is `3:05 PM`.
|
|
82
86
|
*
|
|
83
87
|
* The date's **UTC fields** are formatted: a bound date is its nominal date, the fields as written,
|
|
84
88
|
* which `w:fullDate` keeps with a `Z` and `DatePickerContentControl.fullDate` returns as a `Date` in
|
|
@@ -96,9 +100,32 @@ export function formatDate(pattern, date, locale = 'en-US') {
|
|
|
96
100
|
};
|
|
97
101
|
const hours = date.getUTCHours();
|
|
98
102
|
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
99
|
-
|
|
103
|
+
const dayPeriod = () => {
|
|
104
|
+
const of = (tag) => {
|
|
105
|
+
var _a;
|
|
106
|
+
return (_a = new Intl.DateTimeFormat(tag, { hour: 'numeric', hour12: true, timeZone: 'UTC' })
|
|
107
|
+
.formatToParts(date).find((part) => part.type === 'dayPeriod')) === null || _a === void 0 ? void 0 : _a.value;
|
|
108
|
+
};
|
|
109
|
+
let value;
|
|
110
|
+
try {
|
|
111
|
+
value = of(locale);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
value = of('en-US');
|
|
115
|
+
}
|
|
116
|
+
value !== null && value !== void 0 ? value : (value = hours < 12 ? 'AM' : 'PM');
|
|
117
|
+
try {
|
|
118
|
+
return value.toLocaleUpperCase(locale);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return value.toUpperCase();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
return pattern.replace(/[aA][mM]\/[pP][mM]|d{1,4}|M{1,4}|y{2,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|tt|'[^']*'|"[^"]*"/g, (token) => {
|
|
100
125
|
if (token.startsWith("'") || token.startsWith('"'))
|
|
101
126
|
return token.slice(1, -1);
|
|
127
|
+
if (token.length === 5 && token[2] === '/')
|
|
128
|
+
return dayPeriod();
|
|
102
129
|
switch (token) {
|
|
103
130
|
case 'd': return String(date.getUTCDate());
|
|
104
131
|
case 'dd': return pad(date.getUTCDate(), 2);
|
|
@@ -137,31 +164,84 @@ function isoOf(date) {
|
|
|
137
164
|
* rule, as Word shows them; CR-002 section 56). A date that doesn't exist (`2026-02-30`) is not one.
|
|
138
165
|
*/
|
|
139
166
|
function dateOf(value) {
|
|
140
|
-
|
|
167
|
+
var _a;
|
|
168
|
+
return (_a = parsedDateOf(value)) === null || _a === void 0 ? void 0 : _a.nominal;
|
|
169
|
+
}
|
|
170
|
+
const BOUND_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?)?(Z|[+-]\d{2}:?\d{2})?$/;
|
|
171
|
+
/** A bound value's date as its nominal fields (UTC fields of `nominal`), and its zone marker's offset in minutes, where it has one. */
|
|
172
|
+
function parsedDateOf(value) {
|
|
173
|
+
const m = BOUND_DATE.exec(value);
|
|
141
174
|
if (!m)
|
|
142
175
|
return undefined;
|
|
143
|
-
const [year, month, day, hour, minute, second] = m.slice(1).map((f) => Number(f !== null && f !== void 0 ? f : 0));
|
|
176
|
+
const [year, month, day, hour, minute, second] = m.slice(1, 7).map((f) => Number(f !== null && f !== void 0 ? f : 0));
|
|
144
177
|
const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
|
|
145
178
|
// Date.UTC rolls an impossible field over (30 February is 2 March): such a value is not a date
|
|
146
179
|
const exists = date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day
|
|
147
180
|
&& date.getUTCHours() === hour && date.getUTCMinutes() === minute && date.getUTCSeconds() === second;
|
|
148
|
-
|
|
181
|
+
if (!exists)
|
|
182
|
+
return undefined;
|
|
183
|
+
const marker = m[7];
|
|
184
|
+
const offset = marker === undefined ? undefined : marker === 'Z' ? 0
|
|
185
|
+
: (marker.startsWith('-') ? -1 : 1) * (Number(marker.slice(1, 3)) * 60 + Number(marker.slice(-2)));
|
|
186
|
+
return { nominal: date, offset };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A bound value's date as Word shows it to a reader in `zone` (`pkg.boundDatesConvertedAsWord`, CR-018): a value carrying
|
|
190
|
+
* a `Z` or an offset is the instant it names, shown on `zone`'s wall clock (Word reads the picker's `Z` as UTC and shows
|
|
191
|
+
* the machine's local time: on a machine at UTC+11, `2026-10-22T15:05:00Z` shows 2:05 AM the next day; docx4j CR-058 D6's
|
|
192
|
+
* probe); a value with no marker, or a date alone, is nominal, as `dateOf` reads it (unmeasured in Word, the date
|
|
193
|
+
* rule's point 4). The result is a nominal date in UTC fields, as every bound date here is, so `formatDate` and
|
|
194
|
+
* `w:fullDate` take it unchanged.
|
|
195
|
+
*/
|
|
196
|
+
function dateAsWordShows(value, zone) {
|
|
197
|
+
const parsed = parsedDateOf(value);
|
|
198
|
+
if (!parsed || parsed.offset === undefined)
|
|
199
|
+
return parsed === null || parsed === void 0 ? void 0 : parsed.nominal;
|
|
200
|
+
const instant = new Date(parsed.nominal.getTime() - parsed.offset * 60000);
|
|
201
|
+
const clock = wallClockIn(instant, zone);
|
|
202
|
+
return new Date(Date.UTC(clock.year, clock.month - 1, clock.day, clock.hour, clock.minute, clock.second));
|
|
149
203
|
}
|
|
150
204
|
/**
|
|
151
205
|
* Pushes the custom XML value into one control (docx4j BindingHandler's per-sdt work). Returns
|
|
152
206
|
* false when nothing was written: no binding, no part, an XPath that selects nothing, a kind
|
|
153
207
|
* this phase does not handle (explicit rich text), a node holding a Flat OPC package, or a
|
|
154
208
|
* picture control whose node holds no image (`notes` says which; CR-005 section 9).
|
|
209
|
+
*
|
|
210
|
+
* The part is the one the binding resolves to (CR-020): where its `w:storeItemID` names no part,
|
|
211
|
+
* the one its XPath fits, as Word repairs it on open; a control so bound gets that part's ID in
|
|
212
|
+
* its binding, as Word's save writes it (D2), and a note, as does a stale ID no part fits (D4).
|
|
155
213
|
*/
|
|
156
214
|
export function applyBindingTo(control, notes) {
|
|
157
|
-
var _a;
|
|
158
215
|
const mapping = control.xmlMapping;
|
|
159
|
-
|
|
216
|
+
const binding = mapping.dataBinding;
|
|
217
|
+
if (!binding)
|
|
160
218
|
return false;
|
|
161
|
-
const
|
|
219
|
+
const resolved = mapping.resolution;
|
|
220
|
+
const label = control.title || control.tag || `content control ${control.id}`;
|
|
221
|
+
if (!resolved) {
|
|
222
|
+
if (binding.storeItemID && !isPropertiesStoreItem(binding.storeItemID)) {
|
|
223
|
+
notes === null || notes === void 0 ? void 0 : notes.push(`${label}: store item ${binding.storeItemID} names no custom XML part, and none fits ${binding.xpath}; left as it is`);
|
|
224
|
+
}
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const node = resolved.part.selectSingleNode(binding.xpath, binding.prefixMappings);
|
|
162
228
|
if (!node)
|
|
163
229
|
return false;
|
|
164
|
-
|
|
230
|
+
if (!bindValue(control, node.text.trim(), notes))
|
|
231
|
+
return false;
|
|
232
|
+
if (resolved.repaired) {
|
|
233
|
+
const stale = binding.storeItemID;
|
|
234
|
+
const id = resolved.part.id.toUpperCase();
|
|
235
|
+
mapping.repairStoreItemID(stale, id);
|
|
236
|
+
notes === null || notes === void 0 ? void 0 : notes.push(`${label}: store item ${stale} names no custom XML part; ${resolved.part.part.partName.name} ${id}`
|
|
237
|
+
+ (resolved.fitting > 1 ? `, the first of the ${resolved.fitting} that fit ${binding.xpath} to select a node,` : ` fits ${binding.xpath} and`)
|
|
238
|
+
+ ' is bound, its ID written into the control, as Word repairs it');
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
/** `applyBindingTo`'s write of a node's value into the control, by the control's kind. */
|
|
243
|
+
function bindValue(control, value, notes) {
|
|
244
|
+
var _a, _b, _c;
|
|
165
245
|
// A picture control's node holds base64 image data: the picture's a:blip is pointed at a new
|
|
166
246
|
// image part and the drawing kept, as docx4j's bind.xslt mode picture3 does (CR-005 section 9).
|
|
167
247
|
if (control.type === 'Picture') {
|
|
@@ -198,12 +278,15 @@ export function applyBindingTo(control, notes) {
|
|
|
198
278
|
}
|
|
199
279
|
else if (control.type === 'DatePicker') {
|
|
200
280
|
const date = control.datePickerContentControl;
|
|
201
|
-
const
|
|
281
|
+
const pkg = control.parentBody.package_;
|
|
282
|
+
const parsed = (pkg === null || pkg === void 0 ? void 0 : pkg.boundDatesConvertedAsWord) && pkg.effectiveDateTimeZone ? dateAsWordShows(value, pkg.effectiveDateTimeZone()) : dateOf(value);
|
|
202
283
|
if (date && parsed) {
|
|
203
284
|
date.fullDate = parsed;
|
|
204
|
-
// Word shows the date in w:dateFormat, not the stored form (docx4j formats it the same way)
|
|
285
|
+
// Word shows the date in w:dateFormat, not the stored form (docx4j formats it the same way), in w:lid's language,
|
|
286
|
+
// else the document's default language (CR-020 D5, docx4j CR-058 D2 revised), else en-US
|
|
287
|
+
const language = date.dateDisplayLocale || ((_c = (_b = pkg === null || pkg === void 0 ? void 0 : pkg.propertyResolverOrUndefined) === null || _b === void 0 ? void 0 : _b.getDocumentDefaultRPr().lang) === null || _c === void 0 ? void 0 : _c.val) || undefined;
|
|
205
288
|
if (date.dateFormat !== '')
|
|
206
|
-
display = formatDate(date.dateFormat, parsed,
|
|
289
|
+
display = formatDate(date.dateFormat, parsed, language);
|
|
207
290
|
}
|
|
208
291
|
}
|
|
209
292
|
// an empty value shows the control's placeholder in Word's form: the entry its w:placeholder names, else Word's default
|
|
@@ -221,7 +304,7 @@ export function applyBindingsTo(controls) {
|
|
|
221
304
|
const result = { bound: 0, updated: 0, skipped: 0 };
|
|
222
305
|
const notes = [];
|
|
223
306
|
for (const control of controls) {
|
|
224
|
-
if (!control.xmlMapping.
|
|
307
|
+
if (!control.xmlMapping.dataBinding)
|
|
225
308
|
continue;
|
|
226
309
|
result.bound++;
|
|
227
310
|
if (applyBindingTo(control, notes))
|
|
@@ -242,8 +325,7 @@ export function applyBindingsTo(controls) {
|
|
|
242
325
|
export function updateFromControl(control) {
|
|
243
326
|
var _a, _b;
|
|
244
327
|
const mapping = control.xmlMapping;
|
|
245
|
-
|
|
246
|
-
return false;
|
|
328
|
+
// the node in the part the binding resolves to, a stale ID's too, its ID left as it is (CR-020 D2, docx4j's UpdateXmlFromDocumentSurface)
|
|
247
329
|
const node = mapping.customXmlNode;
|
|
248
330
|
if (!node)
|
|
249
331
|
return false;
|
|
@@ -290,7 +372,7 @@ export function updateFromControl(control) {
|
|
|
290
372
|
export function updateFromControls(controls) {
|
|
291
373
|
const result = { bound: 0, updated: 0, skipped: 0 };
|
|
292
374
|
for (const control of controls) {
|
|
293
|
-
if (!control.xmlMapping.
|
|
375
|
+
if (!control.xmlMapping.dataBinding)
|
|
294
376
|
continue;
|
|
295
377
|
// counted by the picture pass, which reads the image part (CR-005 section 9)
|
|
296
378
|
if (control.type === 'Picture')
|
|
@@ -2,6 +2,7 @@ export { type XPathEngine, type XPathValue, type BooleanConversionMode, booleanV
|
|
|
2
2
|
export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection, type CustomXmlNodeType, type CustomXmlPrefixMapping, type CustomXmlPartOwner } from './CustomXmlPart.mjs';
|
|
3
3
|
export { CustomXmlPartCollection, type CustomXmlHost } from './CustomXmlPartCollection.mjs';
|
|
4
4
|
export { XmlMapping, type CustomXmlPartLookup } from './XmlMapping.mjs';
|
|
5
|
+
export { StoreItemResolver, firstStep, isPropertiesStoreItem, normaliseStoreItemId, CORE_PROPERTIES_STOREITEMID, EXTENDED_PROPERTIES_STOREITEMID, type StoreItemCandidate, type StoreItemResolution } from './storeItems.mjs';
|
|
5
6
|
export { CheckboxContentControl, DatePickerContentControl, ListContentControl, ContentControlListItem, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKED_SYMBOL, UNCHECKED_SYMBOL, CHECKBOX_FONT, } from './kinds.mjs';
|
|
6
7
|
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, holdsFlatOpc, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT, type BindingResult } from './bindings.mjs';
|
|
7
8
|
export { controlIdScope, sdtKindFor, isRunLevel } from './insert.mjs';
|
|
@@ -4,6 +4,7 @@ export { booleanValue, DefaultXPathEngine, parsePrefixMappings, formatPrefixMapp
|
|
|
4
4
|
export { CustomXmlPart, CustomXmlNode, CustomXmlPrefixMappingCollection } from './CustomXmlPart.mjs';
|
|
5
5
|
export { CustomXmlPartCollection } from './CustomXmlPartCollection.mjs';
|
|
6
6
|
export { XmlMapping } from './XmlMapping.mjs';
|
|
7
|
+
export { StoreItemResolver, firstStep, isPropertiesStoreItem, normaliseStoreItemId, CORE_PROPERTIES_STOREITEMID, EXTENDED_PROPERTIES_STOREITEMID } from './storeItems.mjs';
|
|
7
8
|
export { CheckboxContentControl, DatePickerContentControl, ListContentControl, ContentControlListItem, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKED_SYMBOL, UNCHECKED_SYMBOL, CHECKBOX_FONT, } from './kinds.mjs';
|
|
8
9
|
export { applyBindingsTo, applyBindingTo, updateFromControls, updateFromControl, runsForValue, formatDate, holdsFlatOpc, PLACEHOLDER_STYLE, PLACEHOLDER_TEXT } from './bindings.mjs';
|
|
9
10
|
export { controlIdScope, sdtKindFor, isRunLevel } from './insert.mjs';
|
|
@@ -22,11 +22,14 @@ export interface XPathsEntry {
|
|
|
22
22
|
export declare function xpathsEntriesOf(parts: CustomXmlPartLookup): Map<string, XPathsEntry>;
|
|
23
23
|
/**
|
|
24
24
|
* The node an `od:xpath` entry selects: the entry's XPath over the part its `storeItemID` names
|
|
25
|
-
* (the id compared without braces or case, as `getItem` does)
|
|
25
|
+
* (the id compared without braces or case, as `getItem` does), else, where it names none, the part
|
|
26
|
+
* its XPath fits, as a `w:dataBinding` is resolved (CR-020 D1, docx4j's `StoreItemResolver`; the
|
|
27
|
+
* entry is left as it is, and `repair` says which part was taken). What a tag-bound control's pass
|
|
26
28
|
* starts from; the note says why there is no node.
|
|
27
29
|
*/
|
|
28
30
|
export declare function selectEntryNode(entryId: string, parts: CustomXmlPartLookup): {
|
|
29
31
|
node: CustomXmlNode;
|
|
32
|
+
repair?: string;
|
|
30
33
|
} | {
|
|
31
34
|
note: string;
|
|
32
35
|
};
|
|
@@ -38,19 +38,22 @@ export function xpathsEntriesOf(parts) {
|
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* The node an `od:xpath` entry selects: the entry's XPath over the part its `storeItemID` names
|
|
41
|
-
* (the id compared without braces or case, as `getItem` does)
|
|
41
|
+
* (the id compared without braces or case, as `getItem` does), else, where it names none, the part
|
|
42
|
+
* its XPath fits, as a `w:dataBinding` is resolved (CR-020 D1, docx4j's `StoreItemResolver`; the
|
|
43
|
+
* entry is left as it is, and `repair` says which part was taken). What a tag-bound control's pass
|
|
42
44
|
* starts from; the note says why there is no node.
|
|
43
45
|
*/
|
|
44
46
|
export function selectEntryNode(entryId, parts) {
|
|
45
|
-
var _a;
|
|
46
47
|
const entry = xpathsEntriesOf(parts).get(entryId);
|
|
47
48
|
if (!entry)
|
|
48
49
|
return { note: `od:xpath=${entryId}: no such entry in the XPaths part` };
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
50
|
+
const resolved = entry.storeItemID === '' ? undefined : parts.resolveStoreItem(entry.storeItemID, entry.xpath, entry.prefixMappings);
|
|
51
|
+
if (!resolved)
|
|
51
52
|
return { note: `od:xpath=${entryId}: no custom XML part ${entry.storeItemID}` };
|
|
52
|
-
const node = part.selectSingleNode(entry.xpath, entry.prefixMappings);
|
|
53
|
+
const node = resolved.part.selectSingleNode(entry.xpath, entry.prefixMappings);
|
|
53
54
|
if (!node)
|
|
54
55
|
return { note: `od:xpath=${entryId}: ${entry.xpath} selects nothing` };
|
|
55
|
-
|
|
56
|
+
if (!resolved.repaired)
|
|
57
|
+
return { node };
|
|
58
|
+
return { node, repair: `od:xpath=${entryId}: store item ${entry.storeItemID} names no custom XML part; ${resolved.part.part.partName.name} ${resolved.part.id} fits ${entry.xpath} and is used` };
|
|
56
59
|
}
|
|
@@ -139,7 +139,9 @@ export function applyPictureHandlerTo(control, parts) {
|
|
|
139
139
|
const selected = selectEntryNode(entryId, parts);
|
|
140
140
|
if ('note' in selected)
|
|
141
141
|
return { done: false, note: selected.note };
|
|
142
|
-
|
|
142
|
+
const outcome = bindPicture(control, selected.node.text, pictureWidthOf(params), `od:xpath=${entryId}`);
|
|
143
|
+
// a stale storeItemID resolved as Word would (CR-020): said where the outcome has nothing else to say
|
|
144
|
+
return selected.repair && outcome.done && !outcome.note ? { ...outcome, note: selected.repair } : outcome;
|
|
143
145
|
}
|
|
144
146
|
/** The picture pass over the tag-bound controls, after the text bindings: counted into the result, what was left said in its notes. */
|
|
145
147
|
export function applyPictureHandlersTo(controls, parts, result) {
|
|
@@ -170,7 +172,7 @@ export async function updateFromPictureControl(control) {
|
|
|
170
172
|
var _a, _b;
|
|
171
173
|
const mapping = control.xmlMapping;
|
|
172
174
|
// a control showing its placeholder picture holds no picture of its node's (CR-005 section 10)
|
|
173
|
-
if (!mapping.
|
|
175
|
+
if (!mapping.dataBinding || control.type !== 'Picture' || control.isShowingPlaceholder)
|
|
174
176
|
return false;
|
|
175
177
|
const node = mapping.customXmlNode;
|
|
176
178
|
if (!node)
|
|
@@ -193,7 +195,7 @@ export async function updateFromPictureControl(control) {
|
|
|
193
195
|
/** `updateFromContentControls`' picture pass: the mapped picture controls, counted into the result. */
|
|
194
196
|
export async function updateFromPictureControls(controls, result) {
|
|
195
197
|
for (const control of controls) {
|
|
196
|
-
if (control.type !== 'Picture' || !control.xmlMapping.
|
|
198
|
+
if (control.type !== 'Picture' || !control.xmlMapping.dataBinding)
|
|
197
199
|
continue;
|
|
198
200
|
result.bound++;
|
|
199
201
|
if (await updateFromPictureControl(control))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** The `w:storeItemID`s of the core and application properties (docx4j `BindingHandler`): not custom XML parts, never repaired. */
|
|
2
|
+
export declare const CORE_PROPERTIES_STOREITEMID = "{6C3C8BC8-F283-45AE-878A-BAB7291924A1}";
|
|
3
|
+
export declare const EXTENDED_PROPERTIES_STOREITEMID = "{6668398D-A668-4E3E-A5EB-62B293D839F1}";
|
|
4
|
+
/** An itemID without braces, lower-cased: how `getItem` compares ids, and so how a binding's names its part. */
|
|
5
|
+
export declare function normaliseStoreItemId(id: string): string;
|
|
6
|
+
/** Whether an ID is the core or the application properties'. */
|
|
7
|
+
export declare function isPropertiesStoreItem(id: string): boolean;
|
|
8
|
+
/** One custom XML part as the resolver sees it. */
|
|
9
|
+
export interface StoreItemCandidate<P> {
|
|
10
|
+
readonly part: P;
|
|
11
|
+
/** Its itemID, as its properties part spells it. */
|
|
12
|
+
readonly id: string;
|
|
13
|
+
/** Its part name: the order among several that fit. */
|
|
14
|
+
readonly partName: string;
|
|
15
|
+
/** Its document element; none where the part can't be read, which then fits nothing. */
|
|
16
|
+
readonly root: Element | null | undefined;
|
|
17
|
+
/** Whether the XPath, with these prefix mappings, selects a node in it. */
|
|
18
|
+
selects(xpath: string, prefixMappings: string | undefined): boolean;
|
|
19
|
+
}
|
|
20
|
+
/** The part a binding is evaluated against. */
|
|
21
|
+
export interface StoreItemResolution<P> {
|
|
22
|
+
readonly part: P;
|
|
23
|
+
/** True where the binding's ID names no part and this one fits its XPath: Word's repair. */
|
|
24
|
+
readonly repaired: boolean;
|
|
25
|
+
/** Where repaired, how many parts fit the XPath's first step; this one is the first, in part-name order, that selects a node. */
|
|
26
|
+
readonly fitting: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* docx4j's `StoreItemResolver.resolve` rule over a package's custom XML parts:
|
|
30
|
+
* - the part the ID names, the comparison `getItem`'s (case and braces ignored); the core and application properties'
|
|
31
|
+
* IDs are left to the caller, and never repaired;
|
|
32
|
+
* - else the parts whose document element's namespace and local name match the XPath's first step, its prefix read with
|
|
33
|
+
* the binding's `w:prefixMappings`, else the objects package's `NAMESPACE_PREFIXES` (docx4j reads its own table); an
|
|
34
|
+
* XPath with no named first step (`//`, `*`, an axis, an attribute) fits every part. In part-name order: one fitting part
|
|
35
|
+
* is taken; of several, the first whose XPath selects a node, else the first (Word's choice in every fresh session,
|
|
36
|
+
* check 73b; CR-020 D7); none, and the binding is unresolved.
|
|
37
|
+
*
|
|
38
|
+
* What a stale ID resolves to is remembered per (ID, XPath, prefix mappings), since one stale ID fits different parts for
|
|
39
|
+
* different XPaths (the two-parts probe: B7 part 1, B11 part 2), and forgotten when the parts change (an add, a delete).
|
|
40
|
+
*/
|
|
41
|
+
export declare class StoreItemResolver<P> {
|
|
42
|
+
private readonly candidates;
|
|
43
|
+
private readonly memo;
|
|
44
|
+
private signature;
|
|
45
|
+
constructor(candidates: () => readonly StoreItemCandidate<P>[]);
|
|
46
|
+
/** The part for a binding: named by its ID, else the one its XPath fits; undefined where none is named and none fits. */
|
|
47
|
+
resolve(storeItemId: string | undefined, xpath: string | undefined, prefixMappings: string | undefined): StoreItemResolution<P> | undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The XPath's first step as its namespace and local name, with the binding's prefix mappings (`/p:root[1]/p:b7[1]` with
|
|
51
|
+
* `xmlns:p='urn:x'` is `urn:x` and `root`); undefined where the XPath does not start with a named step, when every part
|
|
52
|
+
* fits. docx4j's `StoreItemResolver.firstStep`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function firstStep(xpath: string | undefined, prefixMappings: string | undefined): {
|
|
55
|
+
namespaceURI: string;
|
|
56
|
+
localName: string;
|
|
57
|
+
} | undefined;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// CR-020: docx4j's StoreItemResolver (its CR-058 D4, `35cdeaee6`, released in 17.3.5): the custom XML part a binding is
|
|
2
|
+
// evaluated against. The part its `w:storeItemID` names, or, where that names no part, the one its XPath fits, as Word
|
|
3
|
+
// repairs such a binding on open (Word 365, 22 and 23 October 2026, and checks 73 and 73b: the control shows the part's
|
|
4
|
+
// value, Office JS's `customXmlPart` is that part, and the save writes its ID into `w:storeItemID`). Shared by every
|
|
5
|
+
// reader and the bind (CR-020 D1): the text model's bound values, `XmlMapping.customXmlPart` and `customXmlNode`,
|
|
6
|
+
// `applyBindings`, `updateFromContentControls` and the `od:xpath` passes, so they all name the part Word binds.
|
|
7
|
+
import { NAMESPACE_PREFIXES } from '@docx4j/generated-objects-ts';
|
|
8
|
+
import { parsePrefixMappings } from './xpath.mjs';
|
|
9
|
+
/** The `w:storeItemID`s of the core and application properties (docx4j `BindingHandler`): not custom XML parts, never repaired. */
|
|
10
|
+
export const CORE_PROPERTIES_STOREITEMID = '{6C3C8BC8-F283-45AE-878A-BAB7291924A1}';
|
|
11
|
+
export const EXTENDED_PROPERTIES_STOREITEMID = '{6668398D-A668-4E3E-A5EB-62B293D839F1}';
|
|
12
|
+
/** An itemID without braces, lower-cased: how `getItem` compares ids, and so how a binding's names its part. */
|
|
13
|
+
export function normaliseStoreItemId(id) {
|
|
14
|
+
return id.trim().replace(/[{}]/g, '').toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
/** Whether an ID is the core or the application properties'. */
|
|
17
|
+
export function isPropertiesStoreItem(id) {
|
|
18
|
+
const key = normaliseStoreItemId(id);
|
|
19
|
+
return key === normaliseStoreItemId(CORE_PROPERTIES_STOREITEMID) || key === normaliseStoreItemId(EXTENDED_PROPERTIES_STOREITEMID);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* docx4j's `StoreItemResolver.resolve` rule over a package's custom XML parts:
|
|
23
|
+
* - the part the ID names, the comparison `getItem`'s (case and braces ignored); the core and application properties'
|
|
24
|
+
* IDs are left to the caller, and never repaired;
|
|
25
|
+
* - else the parts whose document element's namespace and local name match the XPath's first step, its prefix read with
|
|
26
|
+
* the binding's `w:prefixMappings`, else the objects package's `NAMESPACE_PREFIXES` (docx4j reads its own table); an
|
|
27
|
+
* XPath with no named first step (`//`, `*`, an axis, an attribute) fits every part. In part-name order: one fitting part
|
|
28
|
+
* is taken; of several, the first whose XPath selects a node, else the first (Word's choice in every fresh session,
|
|
29
|
+
* check 73b; CR-020 D7); none, and the binding is unresolved.
|
|
30
|
+
*
|
|
31
|
+
* What a stale ID resolves to is remembered per (ID, XPath, prefix mappings), since one stale ID fits different parts for
|
|
32
|
+
* different XPaths (the two-parts probe: B7 part 1, B11 part 2), and forgotten when the parts change (an add, a delete).
|
|
33
|
+
*/
|
|
34
|
+
export class StoreItemResolver {
|
|
35
|
+
constructor(candidates) {
|
|
36
|
+
this.candidates = candidates;
|
|
37
|
+
this.memo = new Map();
|
|
38
|
+
this.signature = '';
|
|
39
|
+
}
|
|
40
|
+
/** The part for a binding: named by its ID, else the one its XPath fits; undefined where none is named and none fits. */
|
|
41
|
+
resolve(storeItemId, xpath, prefixMappings) {
|
|
42
|
+
var _a;
|
|
43
|
+
if (!storeItemId)
|
|
44
|
+
return undefined;
|
|
45
|
+
const all = this.candidates();
|
|
46
|
+
const key = normaliseStoreItemId(storeItemId);
|
|
47
|
+
const named = all.find((candidate) => normaliseStoreItemId(candidate.id) === key);
|
|
48
|
+
if (named)
|
|
49
|
+
return { part: named.part, repaired: false, fitting: 0 };
|
|
50
|
+
if (isPropertiesStoreItem(storeItemId))
|
|
51
|
+
return undefined;
|
|
52
|
+
const signature = all.map((candidate) => `${normaliseStoreItemId(candidate.id)} ${candidate.partName}`).join('|');
|
|
53
|
+
if (signature !== this.signature) {
|
|
54
|
+
this.memo.clear();
|
|
55
|
+
this.signature = signature;
|
|
56
|
+
}
|
|
57
|
+
const memoKey = `${key}|${xpath !== null && xpath !== void 0 ? xpath : ''}|${prefixMappings !== null && prefixMappings !== void 0 ? prefixMappings : ''}`;
|
|
58
|
+
let resolved = this.memo.get(memoKey);
|
|
59
|
+
if (resolved === undefined) {
|
|
60
|
+
resolved = (_a = repair(all, xpath !== null && xpath !== void 0 ? xpath : '', prefixMappings)) !== null && _a !== void 0 ? _a : null;
|
|
61
|
+
this.memo.set(memoKey, resolved);
|
|
62
|
+
}
|
|
63
|
+
return resolved !== null && resolved !== void 0 ? resolved : undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function repair(all, xpath, prefixMappings) {
|
|
67
|
+
const step = firstStep(xpath, prefixMappings);
|
|
68
|
+
const fitting = all.filter((candidate) => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
const root = candidate.root;
|
|
71
|
+
if (!root)
|
|
72
|
+
return false;
|
|
73
|
+
return step === undefined || (((_a = root.namespaceURI) !== null && _a !== void 0 ? _a : '') === step.namespaceURI && ((_b = root.localName) !== null && _b !== void 0 ? _b : root.nodeName) === step.localName);
|
|
74
|
+
}).sort((a, b) => (a.partName < b.partName ? -1 : a.partName > b.partName ? 1 : 0));
|
|
75
|
+
if (fitting.length === 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
if (fitting.length === 1)
|
|
78
|
+
return { part: fitting[0].part, repaired: true, fitting: 1 };
|
|
79
|
+
const selecting = fitting.find((candidate) => {
|
|
80
|
+
try {
|
|
81
|
+
return candidate.selects(xpath, prefixMappings);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return { part: (selecting !== null && selecting !== void 0 ? selecting : fitting[0]).part, repaired: true, fitting: fitting.length };
|
|
88
|
+
}
|
|
89
|
+
/** The objects package's prefixes, prefix to namespace: what a prefix the binding doesn't map reads as. */
|
|
90
|
+
const NAMESPACES_BY_PREFIX = (() => {
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const [uri, prefix] of Object.entries(NAMESPACE_PREFIXES))
|
|
93
|
+
if (prefix !== '' && !(prefix in out))
|
|
94
|
+
out[prefix] = uri;
|
|
95
|
+
return out;
|
|
96
|
+
})();
|
|
97
|
+
/**
|
|
98
|
+
* The XPath's first step as its namespace and local name, with the binding's prefix mappings (`/p:root[1]/p:b7[1]` with
|
|
99
|
+
* `xmlns:p='urn:x'` is `urn:x` and `root`); undefined where the XPath does not start with a named step, when every part
|
|
100
|
+
* fits. docx4j's `StoreItemResolver.firstStep`.
|
|
101
|
+
*/
|
|
102
|
+
export function firstStep(xpath, prefixMappings) {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
if (xpath === undefined)
|
|
105
|
+
return undefined;
|
|
106
|
+
let s = xpath.trim();
|
|
107
|
+
if (s.startsWith('//'))
|
|
108
|
+
return undefined;
|
|
109
|
+
if (s.startsWith('/'))
|
|
110
|
+
s = s.slice(1);
|
|
111
|
+
const end = s.search(/[/[(\s]/);
|
|
112
|
+
const step = end < 0 ? s : s.slice(0, end);
|
|
113
|
+
if (step === '' || step === '*' || step === '.' || step === '..' || step.includes('::') || step.startsWith('@'))
|
|
114
|
+
return undefined;
|
|
115
|
+
const colon = step.indexOf(':');
|
|
116
|
+
const prefix = colon < 0 ? '' : step.slice(0, colon);
|
|
117
|
+
const localName = colon < 0 ? step : step.slice(colon + 1);
|
|
118
|
+
if (localName === '' || localName === '*')
|
|
119
|
+
return undefined;
|
|
120
|
+
const namespaceURI = prefix === '' ? '' : ((_b = (_a = parsePrefixMappings(prefixMappings)[prefix]) !== null && _a !== void 0 ? _a : NAMESPACES_BY_PREFIX[prefix]) !== null && _b !== void 0 ? _b : '');
|
|
121
|
+
return { namespaceURI, localName };
|
|
122
|
+
}
|
|
@@ -5,7 +5,7 @@ import { contentOf } from '../content/ooxml.mjs';
|
|
|
5
5
|
import { wrappingCellOf, endWithParagraph } from '../content/tableRules.mjs';
|
|
6
6
|
import { convertHtml, styleLookupOf } from '../../html/convert.mjs';
|
|
7
7
|
import { blocksToXml, needsOf, HYPERLINK_STYLE } from '../../html/elements.mjs';
|
|
8
|
-
import { tagParamsOf,
|
|
8
|
+
import { tagParamsOf, selectEntryNode, XHTML_CONTENT_TYPE } from './opendope.mjs';
|
|
9
9
|
/** Whether a control is bound to XHTML through its tag. */
|
|
10
10
|
export function isXhtmlBound(control) {
|
|
11
11
|
const params = tagParamsOf(control.tag);
|
|
@@ -68,20 +68,15 @@ function inlineContentOf(paragraph) {
|
|
|
68
68
|
* `dropped` (a run-level control's blocks after the first, and what the converter dropped).
|
|
69
69
|
*/
|
|
70
70
|
export async function applyXhtmlBindingTo(control, parts, host, options = {}) {
|
|
71
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
72
|
-
var
|
|
71
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
72
|
+
var _h;
|
|
73
73
|
const params = tagParamsOf(control.tag);
|
|
74
74
|
const entryId = params.get('od:xpath');
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
return { done: false, note:
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
return { done: false, note: `od:xpath=${entryId}: no custom XML part ${entry.storeItemID}` };
|
|
81
|
-
const node = part.selectSingleNode(entry.xpath, entry.prefixMappings);
|
|
82
|
-
if (!node)
|
|
83
|
-
return { done: false, note: `od:xpath=${entryId}: ${entry.xpath} selects nothing` };
|
|
84
|
-
const parser = (_b = options.parser) !== null && _b !== void 0 ? _b : (typeof DOMParser === 'function' ? (h) => new DOMParser().parseFromString(h, 'text/html') : undefined);
|
|
75
|
+
const selected = selectEntryNode(entryId, parts);
|
|
76
|
+
if ('note' in selected)
|
|
77
|
+
return { done: false, note: selected.note };
|
|
78
|
+
const { node, repair } = selected;
|
|
79
|
+
const parser = (_a = options.parser) !== null && _a !== void 0 ? _a : (typeof DOMParser === 'function' ? (h) => new DOMParser().parseFromString(h, 'text/html') : undefined);
|
|
85
80
|
if (!parser)
|
|
86
81
|
return { done: false, note: `od:xpath=${entryId}: XHTML needs an HTML parser (applyBindings({ html: { parser } }))` };
|
|
87
82
|
const converted = convertHtml(node.text, await lookupFor(host), { parser });
|
|
@@ -93,7 +88,7 @@ export async function applyXhtmlBindingTo(control, parts, host, options = {}) {
|
|
|
93
88
|
await host.styles.ensure(converted.report.stylesWanted);
|
|
94
89
|
}
|
|
95
90
|
catch {
|
|
96
|
-
dropped['styles the defaults lack'] = ((
|
|
91
|
+
dropped['styles the defaults lack'] = ((_b = dropped['styles the defaults lack']) !== null && _b !== void 0 ? _b : 0) + converted.report.stylesWanted.length;
|
|
97
92
|
}
|
|
98
93
|
}
|
|
99
94
|
const needs = needsOf(blocks);
|
|
@@ -110,26 +105,26 @@ export async function applyXhtmlBindingTo(control, parts, host, options = {}) {
|
|
|
110
105
|
lists.set(key, host.numbering.restart(firstNumbered));
|
|
111
106
|
}
|
|
112
107
|
const links = new Map();
|
|
113
|
-
const rels = needs.links.length > 0 ? (
|
|
108
|
+
const rels = needs.links.length > 0 ? (_c = control.parentBody.part) === null || _c === void 0 ? void 0 : _c.getRelationshipsPart(true) : undefined;
|
|
114
109
|
if (rels) {
|
|
115
110
|
for (const address of needs.links)
|
|
116
111
|
links.set(address, rels.addExternalRelationship(Namespaces.HYPERLINK, address).id);
|
|
117
112
|
host.requireStyles([HYPERLINK_STYLE]);
|
|
118
113
|
}
|
|
119
114
|
else if (needs.links.length > 0)
|
|
120
|
-
dropped['links'] = ((
|
|
115
|
+
dropped['links'] = ((_d = dropped['links']) !== null && _d !== void 0 ? _d : 0) + needs.links.length;
|
|
121
116
|
const elements = await contentOf(blocksToXml(blocks, { numId: (key) => lists.get(key), relId: (address) => links.get(address) }));
|
|
122
117
|
applyRunProperties(elements, control.runProperties);
|
|
123
118
|
const sdt = control.sdt;
|
|
124
|
-
(
|
|
125
|
-
const content = ((
|
|
119
|
+
(_e = sdt.sdtContent) !== null && _e !== void 0 ? _e : (sdt.sdtContent = {});
|
|
120
|
+
const content = ((_f = (_h = sdt.sdtContent).content) !== null && _f !== void 0 ? _f : (_h.content = []));
|
|
126
121
|
if (control.form === 'Run') {
|
|
127
122
|
// A run-level control holds inline content: the first paragraph's, the rest reported.
|
|
128
123
|
const first = elements.find((e) => typeNameOf(e) === 'org_docx4j_wml.P');
|
|
129
124
|
const inlines = first ? inlineContentOf(first) : [];
|
|
130
125
|
const rest = elements.length - (first ? 1 : 0);
|
|
131
126
|
if (rest > 0)
|
|
132
|
-
dropped['blocks after the first, in a run-level control'] = ((
|
|
127
|
+
dropped['blocks after the first, in a run-level control'] = ((_g = dropped['blocks after the first, in a run-level control']) !== null && _g !== void 0 ? _g : 0) + rest;
|
|
133
128
|
content.length = 0;
|
|
134
129
|
content.push(...inlines);
|
|
135
130
|
linkParents(content, sdt.sdtContent);
|
|
@@ -145,7 +140,7 @@ export async function applyXhtmlBindingTo(control, parts, host, options = {}) {
|
|
|
145
140
|
}
|
|
146
141
|
// a control in a branch of a kept mc:AlternateContent too: the pass reaches every branch (CR-013 phase 3)
|
|
147
142
|
writingEveryBranch(() => { control.isShowingPlaceholder = false; });
|
|
148
|
-
return Object.keys(dropped).length > 0 ? {
|
|
143
|
+
return { done: true, ...(repair ? { note: repair } : {}), ...(Object.keys(dropped).length > 0 ? { dropped } : {}) };
|
|
149
144
|
}
|
|
150
145
|
/** The XHTML pass over the controls, after the text bindings: counted into the result, what was left said in its notes. */
|
|
151
146
|
export async function applyXhtmlBindingsTo(controls, parts, host, result, options = {}) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The fields a wall clock shows: `month` 1 to 12, `hour` 0 to 23. */
|
|
2
|
+
export interface WallClock {
|
|
3
|
+
readonly year: number;
|
|
4
|
+
readonly month: number;
|
|
5
|
+
readonly day: number;
|
|
6
|
+
readonly hour: number;
|
|
7
|
+
readonly minute: number;
|
|
8
|
+
readonly second: number;
|
|
9
|
+
}
|
|
10
|
+
/** The runtime's zone, an IANA id (`Australia/Sydney`; `UTC` where the runtime has no better answer). */
|
|
11
|
+
export declare function runtimeTimeZone(): string;
|
|
12
|
+
/**
|
|
13
|
+
* `id` when the runtime's `Intl` knows it as a zone, else a `RangeError` naming it: a package's
|
|
14
|
+
* `dateTimeZone` is refused at the set, never ignored, since a wrong zone shifts every stamp.
|
|
15
|
+
*/
|
|
16
|
+
export declare function assertTimeZone(id: string): string;
|
|
17
|
+
/** The wall clock `date` shows in `zone`; the runtime's zone where `zone` is undefined. */
|
|
18
|
+
export declare function wallClockIn(date: Date, zone: string | undefined): WallClock;
|
|
19
|
+
/**
|
|
20
|
+
* The instant at which a wall clock in `zone` shows `clock`; the runtime's zone where `zone` is
|
|
21
|
+
* undefined. Found by offset: the clock taken as UTC, corrected by what the zone shows for that
|
|
22
|
+
* guess, twice, which settles across a daylight-saving step; a clock time that does not exist on a
|
|
23
|
+
* spring-forward day gives the instant an hour on, as `Date` does for the runtime's zone.
|
|
24
|
+
*/
|
|
25
|
+
export declare function instantOf(clock: WallClock, zone: string | undefined): Date;
|