@stll/folio-core 0.54.2 → 0.55.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/dist/ai-edits/apply.js +22 -1
- package/dist/ai-edits/headless.js +1 -0
- package/dist/compare/content-types.d.ts +3 -15
- package/dist/controller/canonicalComposition.d.ts +36 -0
- package/dist/controller/canonicalComposition.js +168 -0
- package/dist/controller/canonicalInput.d.ts +45 -0
- package/dist/controller/canonicalInput.js +338 -0
- package/dist/controller/canonicalOperations.d.ts +22 -0
- package/dist/controller/canonicalOperations.js +85 -0
- package/dist/controller/canonicalReview.d.ts +12 -0
- package/dist/controller/canonicalReview.js +19 -0
- package/dist/controller/canonicalSession.d.ts +149 -0
- package/dist/controller/canonicalSession.js +1038 -0
- package/dist/controller/canonicalStoryEditor.d.ts +34 -0
- package/dist/controller/canonicalStoryEditor.js +82 -0
- package/dist/controller/canonicalStructure.d.ts +16 -0
- package/dist/controller/canonicalStructure.js +269 -0
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/folioEditor.js +9 -0
- package/dist/controller/headerFooterEditorManager.d.ts +4 -0
- package/dist/controller/headerFooterEditorManager.js +71 -3
- package/dist/controller/hiddenEditorApi.d.ts +35 -0
- package/dist/controller/hiddenEditorApi.js +25 -8
- package/dist/controller/hiddenEditorManager.d.ts +11 -2
- package/dist/controller/hiddenEditorManager.js +456 -30
- package/dist/controller/layoutPipeline.d.ts +1 -1
- package/dist/controller/layoutPipeline.js +2 -0
- package/dist/controller/noteEditorManager.d.ts +4 -0
- package/dist/controller/noteEditorManager.js +113 -15
- package/dist/docx/attributeRemainder.js +1 -0
- package/dist/docx/canonicalSessionInput.d.ts +11 -0
- package/dist/docx/canonicalSessionInput.js +27 -0
- package/dist/docx/canonicalStoryRepack.d.ts +10 -0
- package/dist/docx/canonicalStoryRepack.js +41 -0
- package/dist/docx/contentTypeRegistry.d.ts +8 -0
- package/dist/docx/contentTypeRegistry.js +64 -0
- package/dist/docx/ensureParaIds.d.ts +7 -1
- package/dist/docx/ensureParaIds.js +163 -211
- package/dist/docx/foldedListNumberFields.d.ts +85 -0
- package/dist/docx/foldedListNumberFields.js +267 -0
- package/dist/docx/headerFooterVerbatim.d.ts +14 -1
- package/dist/docx/headerFooterVerbatim.js +95 -2
- package/dist/docx/noteIds.d.ts +5 -0
- package/dist/docx/noteIds.js +6 -2
- package/dist/docx/noteReferenceMark.d.ts +3 -1
- package/dist/docx/noteReferenceMark.js +15 -14
- package/dist/docx/numberingParser.d.ts +3 -1
- package/dist/docx/numberingParser.js +17 -3
- package/dist/docx/packageParts.d.ts +3 -1
- package/dist/docx/packageParts.js +65 -16
- package/dist/docx/paragraphMarkPropertyPresence.d.ts +9 -0
- package/dist/docx/paragraphMarkPropertyPresence.js +21 -0
- package/dist/docx/paragraphParser.js +101 -53
- package/dist/docx/paragraphPropertySource.d.ts +15 -1
- package/dist/docx/paragraphPropertySource.js +76 -22
- package/dist/docx/parser.js +2 -1
- package/dist/docx/reviewHistoryNamespace.d.ts +4 -0
- package/dist/docx/reviewHistoryNamespace.js +4 -0
- package/dist/docx/reviewResolutionProvenance.d.ts +25 -0
- package/dist/docx/reviewResolutionProvenance.js +153 -0
- package/dist/docx/rezip.d.ts +7 -3
- package/dist/docx/rezip.js +107 -45
- package/dist/docx/runConsolidator.d.ts +2 -58
- package/dist/docx/runConsolidator.js +3 -175
- package/dist/docx/runParser.d.ts +2 -1
- package/dist/docx/runParser.js +156 -116
- package/dist/docx/saveDiagnostics.d.ts +11 -0
- package/dist/docx/saveDiagnostics.js +0 -0
- package/dist/docx/selectiveSave.d.ts +2 -1
- package/dist/docx/selectiveSave.js +4 -1
- package/dist/docx/selectiveXmlPatch.d.ts +16 -8
- package/dist/docx/selectiveXmlPatch.js +74 -2
- package/dist/docx/serializer/headerFooterSerializer.d.ts +5 -1
- package/dist/docx/serializer/headerFooterSerializer.js +19 -11
- package/dist/docx/serializer/noteSerializer.d.ts +2 -2
- package/dist/docx/serializer/noteSerializer.js +24 -8
- package/dist/docx/serializer/paragraphSerializer.js +41 -4
- package/dist/docx/serializer/partNamespaces.js +2 -1
- package/dist/docx/serializer/runSerializer.js +3 -1
- package/dist/docx/settingsHeaderFooterUpdate.d.ts +5 -0
- package/dist/docx/settingsHeaderFooterUpdate.js +90 -0
- package/dist/docx/settingsParser.d.ts +1 -2
- package/dist/docx/settingsParser.js +5 -6
- package/dist/docx/storyBlockReplay.d.ts +11 -0
- package/dist/docx/storyBlockReplay.js +367 -0
- package/dist/docx/streamingXmlParser.d.ts +22 -1
- package/dist/docx/streamingXmlParser.js +49 -3
- package/dist/docx/structuralXmlPatch.js +8 -1
- package/dist/docx/verbatimCapture.d.ts +3 -1
- package/dist/docx/verbatimCapture.js +49 -6
- package/dist/i18n/messages/ar.gen.d.ts +5 -0
- package/dist/i18n/messages/ar.gen.js +656 -0
- package/dist/i18n/messages/cs.gen.d.ts +5 -0
- package/dist/i18n/messages/cs.gen.js +656 -0
- package/dist/i18n/messages/de.gen.d.ts +5 -0
- package/dist/i18n/messages/de.gen.js +656 -0
- package/dist/i18n/messages/en.gen.d.ts +5 -0
- package/dist/i18n/messages/en.gen.js +656 -0
- package/dist/i18n/messages/es.gen.d.ts +5 -0
- package/dist/i18n/messages/es.gen.js +656 -0
- package/dist/i18n/messages/et.gen.d.ts +5 -0
- package/dist/i18n/messages/et.gen.js +656 -0
- package/dist/i18n/messages/fr.gen.d.ts +5 -0
- package/dist/i18n/messages/fr.gen.js +656 -0
- package/dist/i18n/messages/he.gen.d.ts +5 -0
- package/dist/i18n/messages/he.gen.js +656 -0
- package/dist/i18n/messages/hi.gen.d.ts +5 -0
- package/dist/i18n/messages/hi.gen.js +656 -0
- package/dist/i18n/messages/hu.gen.d.ts +5 -0
- package/dist/i18n/messages/hu.gen.js +656 -0
- package/dist/i18n/messages/locales.d.ts +5 -0
- package/dist/i18n/messages/locales.js +22 -0
- package/dist/i18n/messages/lt.gen.d.ts +5 -0
- package/dist/i18n/messages/lt.gen.js +656 -0
- package/dist/i18n/messages/lv.gen.d.ts +5 -0
- package/dist/i18n/messages/lv.gen.js +656 -0
- package/dist/i18n/messages/pl.gen.d.ts +5 -0
- package/dist/i18n/messages/pl.gen.js +656 -0
- package/dist/i18n/messages/pt-BR.gen.d.ts +5 -0
- package/dist/i18n/messages/pt-BR.gen.js +656 -0
- package/dist/i18n/messages/sk.gen.d.ts +5 -0
- package/dist/i18n/messages/sk.gen.js +656 -0
- package/dist/i18n/messages/tr.gen.d.ts +5 -0
- package/dist/i18n/messages/tr.gen.js +656 -0
- package/dist/i18n/messages/zh-CN.gen.d.ts +5 -0
- package/dist/i18n/messages/zh-CN.gen.js +656 -0
- package/dist/i18n/messages.d.ts +2 -3
- package/dist/i18n/messages.js +1 -19
- package/dist/internal/noteMarkerAttrs.d.ts +8 -0
- package/dist/internal/noteMarkerAttrs.js +16 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +1 -0
- package/dist/internal/prosemirrorAttrBoundary.d.ts +21 -0
- package/dist/internal/prosemirrorAttrBoundary.js +50 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +3 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +1 -0
- package/dist/layout-bridge/convert/noteReferences.d.ts +1 -3
- package/dist/layout-bridge/convert/noteReferences.js +1 -6
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -4
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +2 -2
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -2
- package/dist/managers/DocumentLoaderManager.d.ts +1 -0
- package/dist/managers/DocumentLoaderManager.js +8 -1
- package/dist/markdown/renderBlock.js +15 -1
- package/dist/markdown/renderParagraph.js +9 -13
- package/dist/markdown/renderRuns.js +3 -2
- package/dist/markdown/renderTable.js +2 -0
- package/dist/markdown/types.d.ts +3 -9
- package/dist/prosemirror/attrs/index.d.ts +2 -12
- package/dist/prosemirror/attrs/index.js +22 -48
- package/dist/prosemirror/bookmarkBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/canonicalCommands.d.ts +97 -0
- package/dist/prosemirror/canonicalCommands.js +28 -0
- package/dist/prosemirror/commands/pageBreak.js +15 -1
- package/dist/prosemirror/commentReferenceAttrs.d.ts +2 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +91 -19
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +6 -0
- package/dist/prosemirror/conversion/listRenderingDefinition.js +97 -0
- package/dist/prosemirror/conversion/toProseDoc.d.ts +4 -3
- package/dist/prosemirror/conversion/toProseDoc.js +51 -11
- package/dist/prosemirror/executeEditorCommand.d.ts +11 -0
- package/dist/prosemirror/executeEditorCommand.js +26 -0
- package/dist/prosemirror/extensions/StarterKit.js +2 -0
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +50 -19
- package/dist/prosemirror/extensions/features/ListExtension.js +18 -5
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +5 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +13 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +3 -2
- package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +2 -0
- package/dist/prosemirror/extensions/marks/markUtils.js +70 -8
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/NoteMarkerExtension.js +36 -0
- package/dist/prosemirror/extensions/nodes/PreservedXmlExtension.js +4 -2
- package/dist/prosemirror/listMarker.d.ts +1 -0
- package/dist/prosemirror/listMarker.js +16 -7
- package/dist/prosemirror/listNumbering.js +26 -5
- package/dist/prosemirror/listRenderingAttrs.d.ts +1 -0
- package/dist/prosemirror/listRenderingAttrs.js +3 -0
- package/dist/prosemirror/markupViewNotes.js +3 -2
- package/dist/prosemirror/moveRangeBoundaryAttrs.d.ts +2 -1
- package/dist/prosemirror/noteReferenceReview.js +5 -3
- package/dist/prosemirror/plugins/suggestionMode.js +50 -2
- package/dist/prosemirror/rangeAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/runFormattingFromMarks.d.ts +4 -1
- package/dist/prosemirror/runFormattingFromMarks.js +15 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +2 -1
- package/dist/prosemirror/runFormattingInlineCarriers.js +4 -1
- package/dist/prosemirror/schema/marks.d.ts +2 -0
- package/dist/prosemirror/schema/nodes.d.ts +10 -2
- package/dist/prosemirror/textBoxAnchorAttrs.d.ts +2 -1
- package/dist/prosemirror/textInput.js +5 -1
- package/dist/prosemirror/trackedRevisionPath.js +2 -1
- package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -0
- package/dist/prosemirror/trackedRunInlineAtoms.js +1 -0
- package/dist/prosemirror/validation.js +4 -0
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/createDocument.js +2 -2
- package/package.json +14 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { deterministicHexId } from "../utils/hexId.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolvePackageRelationshipTarget } from "./packageParts.js";
|
|
3
|
+
import { spliceXml } from "./selectiveXmlPatch.js";
|
|
3
4
|
import { loadDocxArchive } from "./server/boundedArchive.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { scanStreamingXmlElements } from "./streamingXmlParser.js";
|
|
6
|
+
import { resolveNamespaceUri } from "./xmlNamespaceContext.js";
|
|
7
|
+
import { OFFICE_RELATIONSHIP_NAMESPACE_URIS, WORDPROCESSINGML_NAMESPACE_URIS, getAttribute, getChildElements, getLocalName, getNamespaceUri, parseXmlDocument, resolveAttributeNamespaceUri } from "./xmlParser.js";
|
|
6
8
|
import { TaggedError } from "better-result";
|
|
7
9
|
import JSZip from "jszip";
|
|
8
10
|
//#region src/docx/ensureParaIds.ts
|
|
@@ -27,7 +29,7 @@ import JSZip from "jszip";
|
|
|
27
29
|
* so documents carrying features folio's parser does not model come back
|
|
28
30
|
* with those features byte-identical. Contract:
|
|
29
31
|
*
|
|
30
|
-
* - Parts covered:
|
|
32
|
+
* - Parts covered: the officeDocument relationship target, `word/header*.xml`, `word/footer*.xml`,
|
|
31
33
|
* `word/footnotes.xml`, `word/endnotes.xml`. Paragraphs nested in table
|
|
32
34
|
* cells and in `mc:Choice` text boxes are plain `<w:p>` elements inside
|
|
33
35
|
* those parts and are covered by the same scan. The comments part mints its
|
|
@@ -50,12 +52,10 @@ import JSZip from "jszip";
|
|
|
50
52
|
* declarations and a `mc:Ignorable` listing `w14` when missing — non-Word
|
|
51
53
|
* producers declare neither, and absent `mc:Ignorable` handling is what
|
|
52
54
|
* makes pre-2010 consumers choke on the new attributes.
|
|
53
|
-
* - Prefixes are aliases
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* part whose bindings a string scan cannot follow (a nested element that
|
|
58
|
-
* rebinds one of them) is refused with {@link EnsureParaIdsError}.
|
|
55
|
+
* - Prefixes are aliases: elements and attributes resolve against their
|
|
56
|
+
* in-scope namespace URI, including Strict and Transitional profiles and
|
|
57
|
+
* nested rebinding. Minted attributes use a root prefix with no conflicting
|
|
58
|
+
* nested binding; a fresh prefix is declared when necessary.
|
|
59
59
|
* - Idempotent: a document that already has full coverage is returned as the
|
|
60
60
|
* original bytes, untouched (`alreadyComplete: true`). A body with
|
|
61
61
|
* paragraphs the scan did not see is refused, never reported complete.
|
|
@@ -63,6 +63,11 @@ import JSZip from "jszip";
|
|
|
63
63
|
* When normalization would rewrite the package, it fails unless the caller
|
|
64
64
|
* explicitly allows signature invalidation after warning the user.
|
|
65
65
|
*/
|
|
66
|
+
const ENSURE_PARA_IDS_REASONS = {
|
|
67
|
+
NORMALIZATION_FAILED: "normalization-failed",
|
|
68
|
+
NAMESPACE_INVALID: "namespace-invalid",
|
|
69
|
+
SIGNED_PACKAGE: "signed-package"
|
|
70
|
+
};
|
|
66
71
|
/** A malformed or unsupported package prevented paragraph-ID normalization. */
|
|
67
72
|
var EnsureParaIdsError = class extends TaggedError("EnsureParaIdsError") {};
|
|
68
73
|
const W14_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2010/wordml";
|
|
@@ -71,53 +76,15 @@ const MC_IGNORABLE_W14 = "w14";
|
|
|
71
76
|
const DIGITAL_SIGNATURE_PART_PREFIX = "_xmlsignatures/";
|
|
72
77
|
/** Word reads an all-zero `w14:paraId` as "no id assigned". */
|
|
73
78
|
const RESERVED_ZERO_ID_PATTERN = /^0*$/u;
|
|
74
|
-
const
|
|
79
|
+
const PACKAGE_RELATIONSHIP_URI = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
75
80
|
const TARGET_PART_PATTERNS = [
|
|
76
|
-
/^word\/document\.xml$/u,
|
|
77
81
|
/^word\/header\d*\.xml$/u,
|
|
78
82
|
/^word\/footer\d*\.xml$/u,
|
|
79
83
|
/^word\/footnotes\.xml$/u,
|
|
80
84
|
/^word\/endnotes\.xml$/u
|
|
81
85
|
];
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
open: "<!--",
|
|
85
|
-
close: "-->"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
open: "<![CDATA[",
|
|
89
|
-
close: "]]>"
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
open: "<?",
|
|
93
|
-
close: "?>"
|
|
94
|
-
}
|
|
95
|
-
];
|
|
96
|
-
/**
|
|
97
|
-
* Every prefixed `paraId` counts toward uniqueness: the parser accepts the
|
|
98
|
-
* WordprocessingML fallback, comment parts link replies via `w15:paraId`, and
|
|
99
|
-
* a producer may spell any of them under a prefix of its own. Over-collecting
|
|
100
|
-
* only steers a minted id away from a value; it never keeps one.
|
|
101
|
-
*/
|
|
102
|
-
const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId=(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
|
|
103
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
104
|
-
/** An attribute `localName` under any of `prefixes`, with `quote` and `value` groups. */
|
|
105
|
-
const prefixedAttributePattern = (prefixes, localName) => {
|
|
106
|
-
const names = prefixes.filter((prefix) => prefix !== "").map(escapeRegExp);
|
|
107
|
-
return names.length === 0 ? /(?!)/u : new RegExp(`\\s(?:${names.join("|")}):${localName}=(?<quote>["'])(?<value>[\\s\\S]*?)\\k<quote>`, "u");
|
|
108
|
-
};
|
|
109
|
-
const partSpelling = (xml, partPath) => {
|
|
110
|
-
const resolution = resolveWordprocessingPrefixes(xml);
|
|
111
|
-
if (resolution.type === "unsupported") throw createEnsureParaIdsError(`Cannot normalize paragraph ids in ${partPath}: ${resolution.reason}`);
|
|
112
|
-
const { prefixes } = resolution;
|
|
113
|
-
const attributePrefixes = [...prefixes.w14, ...prefixes.main];
|
|
114
|
-
return {
|
|
115
|
-
prefixes,
|
|
116
|
-
paraId: prefixedAttributePattern(attributePrefixes, "paraId"),
|
|
117
|
-
textId: prefixedAttributePattern(attributePrefixes, "textId"),
|
|
118
|
-
w14Prefix: (prefixes.w14Declared ? prefixes.w14[0] : void 0) ?? MC_IGNORABLE_W14
|
|
119
|
-
};
|
|
120
|
-
};
|
|
86
|
+
/** Reserve package ids even in parts whose content the pass leaves untouched. */
|
|
87
|
+
const ANY_PARA_ID_PATTERN = /\s[^\s=<>/"':]+:paraId\s*=\s*(?<quote>["'])(?<id>[\s\S]*?)\k<quote>/gu;
|
|
121
88
|
/**
|
|
122
89
|
* Stamp the edits into the part through the splice owner, which refuses a
|
|
123
90
|
* result that would leave a comment range with only one half. These edits
|
|
@@ -135,84 +102,98 @@ const applySplices = (xml, edits, partPath) => {
|
|
|
135
102
|
return patched;
|
|
136
103
|
};
|
|
137
104
|
const createEnsureParaIdsError = (message, cause) => new EnsureParaIdsError({
|
|
105
|
+
reason: ENSURE_PARA_IDS_REASONS.NORMALIZATION_FAILED,
|
|
138
106
|
message,
|
|
139
107
|
...cause === void 0 ? {} : { cause }
|
|
140
108
|
});
|
|
141
|
-
const skipOpaqueXmlRegion = (xml, start, partPath) => {
|
|
142
|
-
for (const { open, close } of OPAQUE_XML_REGIONS) {
|
|
143
|
-
if (!xml.startsWith(open, start)) continue;
|
|
144
|
-
const closeStart = xml.indexOf(close, start + open.length);
|
|
145
|
-
if (closeStart === -1) throw createEnsureParaIdsError(`Unterminated ${open} region in ${partPath}`);
|
|
146
|
-
return closeStart + close.length;
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
};
|
|
150
|
-
const attributeValueRange = (match, absoluteOffset, group) => {
|
|
151
|
-
const quote = match.groups["quote"];
|
|
152
|
-
const value = match.groups[group];
|
|
153
|
-
const start = absoluteOffset + match.index + match[0].indexOf(quote) + 1;
|
|
154
|
-
return {
|
|
155
|
-
start,
|
|
156
|
-
end: start + value.length
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
109
|
const collectExistingParaIds = (xml, into) => {
|
|
160
110
|
for (const match of xml.matchAll(ANY_PARA_ID_PATTERN)) {
|
|
161
111
|
const id = match.groups["id"];
|
|
162
112
|
if (id.length > 0) into.add(id.toUpperCase());
|
|
163
113
|
}
|
|
164
114
|
};
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
115
|
+
/** Resolve every element and attribute in its own scope; retain source offsets for splices. */
|
|
116
|
+
const partSpelling = (xml, partPath) => {
|
|
117
|
+
let root;
|
|
118
|
+
let rootNameEnd = 0;
|
|
119
|
+
let ignorable;
|
|
120
|
+
const paragraphs = [];
|
|
121
|
+
const fallbacks = /* @__PURE__ */ new WeakSet();
|
|
122
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
123
|
+
if (scanStreamingXmlElements(xml, ({ element, attributeValueSpans: spans, nameEnd, parent }) => {
|
|
124
|
+
const unboundElement = (element.name ?? "").includes(":") && getNamespaceUri(element) === void 0;
|
|
125
|
+
const unboundAttribute = Object.keys(element.attributes ?? {}).some((attribute) => attribute.includes(":") && !attribute.startsWith("xmlns:") && resolveAttributeNamespaceUri(element, attribute) === void 0);
|
|
126
|
+
if (unboundElement || unboundAttribute) throw new EnsureParaIdsError({
|
|
127
|
+
reason: ENSURE_PARA_IDS_REASONS.NAMESPACE_INVALID,
|
|
128
|
+
message: `Undeclared namespace prefix in ${partPath}`
|
|
129
|
+
});
|
|
130
|
+
if (parent === void 0) {
|
|
131
|
+
if (root !== void 0) throw createEnsureParaIdsError(`Multiple roots in ${partPath}`);
|
|
132
|
+
root = element;
|
|
133
|
+
rootNameEnd = nameEnd;
|
|
180
134
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
135
|
+
for (const [name, value] of Object.entries(element.attributes ?? {})) {
|
|
136
|
+
if (name !== "xmlns" && !name.startsWith("xmlns:")) continue;
|
|
137
|
+
const prefix = name === "xmlns" ? "" : name.slice(6);
|
|
138
|
+
let uris = bindings.get(prefix);
|
|
139
|
+
if (uris === void 0) {
|
|
140
|
+
uris = /* @__PURE__ */ new Set();
|
|
141
|
+
bindings.set(prefix, uris);
|
|
142
|
+
}
|
|
143
|
+
uris.add(String(value));
|
|
187
144
|
}
|
|
188
|
-
const
|
|
189
|
-
if (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
145
|
+
const inFallback = parent !== void 0 && fallbacks.has(parent) || getLocalName(element.name ?? "") === "Fallback" && getNamespaceUri(element) === MC_NAMESPACE_URI;
|
|
146
|
+
if (inFallback) fallbacks.add(element);
|
|
147
|
+
let paraId;
|
|
148
|
+
let textId;
|
|
149
|
+
for (const [name, span] of spans) {
|
|
150
|
+
const namespace = resolveAttributeNamespaceUri(element, name);
|
|
151
|
+
const localName = getLocalName(name);
|
|
152
|
+
const value = String(element.attributes?.[name] ?? "");
|
|
153
|
+
if (element === root && namespace === MC_NAMESPACE_URI && localName === "Ignorable") ignorable = {
|
|
154
|
+
...span,
|
|
155
|
+
value
|
|
156
|
+
};
|
|
157
|
+
if (namespace !== W14_NAMESPACE_URI && !WORDPROCESSINGML_NAMESPACE_URIS.has(namespace ?? "")) continue;
|
|
158
|
+
if (localName === "paraId") {
|
|
159
|
+
if (paraId === void 0 || namespace === W14_NAMESPACE_URI) paraId = {
|
|
160
|
+
...span,
|
|
161
|
+
value
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (localName === "textId") textId = {
|
|
165
|
+
...span,
|
|
166
|
+
value
|
|
167
|
+
};
|
|
193
168
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
169
|
+
if (getLocalName(element.name ?? "") === "p" && WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) paragraphs.push({
|
|
170
|
+
nameEnd,
|
|
171
|
+
inFallback,
|
|
172
|
+
paraId,
|
|
173
|
+
textId
|
|
174
|
+
});
|
|
175
|
+
}).status === "unsupported" || root === void 0) throw createEnsureParaIdsError(`Malformed XML in ${partPath}`);
|
|
176
|
+
const rootElement = root;
|
|
177
|
+
const prefixFor = (uri, preferred) => {
|
|
178
|
+
for (const [prefix, boundUri] of rootElement.namespaceScope?.bindings ?? []) {
|
|
179
|
+
if (prefix === "" || boundUri !== uri) continue;
|
|
180
|
+
if (uri !== W14_NAMESPACE_URI || bindings.get(prefix)?.size === 1) return prefix;
|
|
198
181
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (id) into.add(id.toUpperCase());
|
|
215
|
-
}
|
|
182
|
+
let candidate = preferred;
|
|
183
|
+
for (let suffix = 1; bindings.has(candidate); suffix += 1) candidate = `${preferred}_${suffix}`;
|
|
184
|
+
return candidate;
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
root,
|
|
188
|
+
rootNameEnd,
|
|
189
|
+
ignorable,
|
|
190
|
+
paragraphs,
|
|
191
|
+
w14Prefix: prefixFor(W14_NAMESPACE_URI, MC_IGNORABLE_W14),
|
|
192
|
+
mcPrefix: prefixFor(MC_NAMESPACE_URI, "mc")
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const collectFallbackParaIds = (spelling, into) => {
|
|
196
|
+
for (const { inFallback, paraId } of spelling.paragraphs) if (inFallback && paraId?.value) into.add(paraId.value.toUpperCase());
|
|
216
197
|
};
|
|
217
198
|
/**
|
|
218
199
|
* Deterministic fresh id for the paragraph at `ordinal` in `partPath`,
|
|
@@ -225,35 +206,27 @@ const mintParaId = (context, partPath, ordinal) => {
|
|
|
225
206
|
context.taken.add(id);
|
|
226
207
|
return id;
|
|
227
208
|
};
|
|
228
|
-
|
|
229
|
-
* One forward scan over a part: every paragraph open tag outside
|
|
230
|
-
* `mc:Fallback` either keeps its paraId (valid, first occurrence) or gets a
|
|
231
|
-
* splice edit minting / replacing one. `seen` spans parts so the
|
|
232
|
-
* first-occurrence rule is document-wide across the scan order.
|
|
233
|
-
*/
|
|
234
|
-
const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
209
|
+
const scanPart = ({ partPath, spelling, context, seen }) => {
|
|
235
210
|
const edits = [];
|
|
236
211
|
const minted = [];
|
|
237
212
|
const w14 = spelling.w14Prefix;
|
|
238
213
|
let assigned = 0;
|
|
239
214
|
let deduplicated = 0;
|
|
240
215
|
let ordinal = 0;
|
|
241
|
-
for (const {
|
|
216
|
+
for (const { nameEnd, inFallback, paraId, textId } of spelling.paragraphs) {
|
|
242
217
|
if (inFallback) continue;
|
|
243
218
|
ordinal += 1;
|
|
244
|
-
|
|
245
|
-
const paraIdMatch = spelling.paraId.exec(openTag);
|
|
246
|
-
if (!paraIdMatch) {
|
|
219
|
+
if (paraId === void 0) {
|
|
247
220
|
const id = mintParaId(context, partPath, ordinal);
|
|
248
|
-
|
|
249
|
-
if (textIdMatch) {
|
|
221
|
+
if (textId !== void 0) {
|
|
250
222
|
edits.push({
|
|
251
223
|
start: nameEnd,
|
|
252
224
|
end: nameEnd,
|
|
253
225
|
text: ` ${w14}:paraId="${id}"`
|
|
254
226
|
});
|
|
255
227
|
edits.push({
|
|
256
|
-
|
|
228
|
+
start: textId.start,
|
|
229
|
+
end: textId.end,
|
|
257
230
|
text: id
|
|
258
231
|
});
|
|
259
232
|
} else edits.push({
|
|
@@ -266,7 +239,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
266
239
|
seen.add(id);
|
|
267
240
|
continue;
|
|
268
241
|
}
|
|
269
|
-
const value =
|
|
242
|
+
const value = paraId.value.toUpperCase();
|
|
270
243
|
const unassigned = RESERVED_ZERO_ID_PATTERN.test(value);
|
|
271
244
|
if (!unassigned && !seen.has(value)) {
|
|
272
245
|
seen.add(value);
|
|
@@ -274,12 +247,13 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
274
247
|
}
|
|
275
248
|
const id = mintParaId(context, partPath, ordinal);
|
|
276
249
|
edits.push({
|
|
277
|
-
|
|
250
|
+
start: paraId.start,
|
|
251
|
+
end: paraId.end,
|
|
278
252
|
text: id
|
|
279
253
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
254
|
+
if (textId !== void 0) edits.push({
|
|
255
|
+
start: textId.start,
|
|
256
|
+
end: textId.end,
|
|
283
257
|
text: id
|
|
284
258
|
});
|
|
285
259
|
else edits.push({
|
|
@@ -296,8 +270,7 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
296
270
|
edits,
|
|
297
271
|
minted,
|
|
298
272
|
assigned,
|
|
299
|
-
deduplicated
|
|
300
|
-
paragraphs: ordinal
|
|
273
|
+
deduplicated
|
|
301
274
|
};
|
|
302
275
|
};
|
|
303
276
|
/**
|
|
@@ -305,71 +278,42 @@ const scanPart = (xml, partPath, spelling, context, seen) => {
|
|
|
305
278
|
* `xmlns:mc` and lists `w14` in `mc:Ignorable`, so consumers that predate the
|
|
306
279
|
* 2010 extensions skip the new attributes instead of rejecting the part.
|
|
307
280
|
*/
|
|
308
|
-
const ensureRootNamespaces = (
|
|
309
|
-
let pos = 0;
|
|
310
|
-
let rootStart = -1;
|
|
311
|
-
while (pos < xml.length) {
|
|
312
|
-
const lt = xml.indexOf("<", pos);
|
|
313
|
-
if (lt === -1) break;
|
|
314
|
-
const opaqueEnd = skipOpaqueXmlRegion(xml, lt, partPath);
|
|
315
|
-
if (opaqueEnd !== null) {
|
|
316
|
-
pos = opaqueEnd;
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
if (xml[lt + 1] === "!") {
|
|
320
|
-
const skipTo = xml.indexOf(">", lt);
|
|
321
|
-
if (skipTo === -1) throw createEnsureParaIdsError(`Unterminated prolog in ${partPath}`);
|
|
322
|
-
pos = skipTo + 1;
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
rootStart = lt;
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
if (rootStart === -1) throw createEnsureParaIdsError(`No root element in ${partPath}`);
|
|
329
|
-
const rootEnd = xml.indexOf(">", rootStart);
|
|
330
|
-
if (rootEnd === -1) throw createEnsureParaIdsError(`Unterminated root element in ${partPath}`);
|
|
331
|
-
const rootTag = xml.slice(rootStart, rootEnd + 1);
|
|
332
|
-
if (!prefixes.mcDeclared && prefixes.mc.length === 0) throw createEnsureParaIdsError(`xmlns:mc has an unexpected namespace URI in ${partPath}`);
|
|
333
|
-
if (!prefixes.w14Declared && prefixes.w14.length === 0) throw createEnsureParaIdsError(`xmlns:w14 has an unexpected namespace URI in ${partPath}`);
|
|
281
|
+
const ensureRootNamespaces = ({ root, rootNameEnd, ignorable, w14Prefix, mcPrefix }) => {
|
|
334
282
|
const edits = [];
|
|
335
283
|
const declarations = [];
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const text = tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`;
|
|
345
|
-
edits.push({
|
|
346
|
-
start: end,
|
|
347
|
-
end,
|
|
348
|
-
text
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
352
|
-
if (declarations.length > 0) {
|
|
353
|
-
let nameEnd = rootStart + 1;
|
|
354
|
-
while (nameEnd < xml.length && !isXmlNameBoundary(xml[nameEnd])) nameEnd += 1;
|
|
355
|
-
edits.push({
|
|
356
|
-
start: nameEnd,
|
|
357
|
-
end: nameEnd,
|
|
358
|
-
text: declarations.join("")
|
|
284
|
+
if (resolveNamespaceUri(root.namespaceScope, mcPrefix) !== MC_NAMESPACE_URI) declarations.push(` xmlns:${mcPrefix}="${MC_NAMESPACE_URI}"`);
|
|
285
|
+
if (resolveNamespaceUri(root.namespaceScope, w14Prefix) !== W14_NAMESPACE_URI) declarations.push(` xmlns:${w14Prefix}="${W14_NAMESPACE_URI}"`);
|
|
286
|
+
if (ignorable !== void 0) {
|
|
287
|
+
const tokens = ignorable.value.split(/\s+/u).filter((token) => token.length > 0);
|
|
288
|
+
if (!tokens.includes(w14Prefix)) edits.push({
|
|
289
|
+
start: ignorable.end,
|
|
290
|
+
end: ignorable.end,
|
|
291
|
+
text: tokens.length === 0 ? w14Prefix : ` ${w14Prefix}`
|
|
359
292
|
});
|
|
360
|
-
}
|
|
293
|
+
} else declarations.push(` ${mcPrefix}:Ignorable="${w14Prefix}"`);
|
|
294
|
+
if (declarations.length > 0) edits.push({
|
|
295
|
+
start: rootNameEnd,
|
|
296
|
+
end: rootNameEnd,
|
|
297
|
+
text: declarations.join("")
|
|
298
|
+
});
|
|
361
299
|
return edits;
|
|
362
300
|
};
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
301
|
+
const mainDocumentPart = (rels, entries) => {
|
|
302
|
+
const root = parseXmlDocument(rels);
|
|
303
|
+
if (root === null || getLocalName(root.name ?? "") !== "Relationships" || getNamespaceUri(root) !== PACKAGE_RELATIONSHIP_URI) throw createEnsureParaIdsError("Malformed package relationships");
|
|
304
|
+
const documents = getChildElements(root).filter((element) => {
|
|
305
|
+
if (getLocalName(element.name ?? "") !== "Relationship" || getNamespaceUri(element) !== PACKAGE_RELATIONSHIP_URI) return false;
|
|
306
|
+
const type = getAttribute(element, null, "Type");
|
|
307
|
+
return [...OFFICE_RELATIONSHIP_NAMESPACE_URIS].some((uri) => type === `${uri}/officeDocument`);
|
|
308
|
+
});
|
|
309
|
+
const document = documents.at(0);
|
|
310
|
+
if (documents.length !== 1 || document === void 0 || getAttribute(document, null, "TargetMode") === "External") throw createEnsureParaIdsError("Package must have one internal officeDocument relationship");
|
|
311
|
+
const target = getAttribute(document, null, "Target");
|
|
312
|
+
const partPath = target === null ? void 0 : resolvePackageRelationshipTarget(target, "_rels/.rels");
|
|
313
|
+
if (partPath === void 0) throw createEnsureParaIdsError("Invalid officeDocument target");
|
|
314
|
+
const name = entries.find((entry) => entry.toLowerCase() === partPath);
|
|
315
|
+
if (name === void 0) throw createEnsureParaIdsError("officeDocument part not found");
|
|
316
|
+
return name;
|
|
373
317
|
};
|
|
374
318
|
/** OPC part names are case-insensitive; compare lowercased. */
|
|
375
319
|
const isTargetPart = (path) => TARGET_PART_PATTERNS.some((pattern) => pattern.test(path.toLowerCase()));
|
|
@@ -382,12 +326,11 @@ const hasDigitalSignatureParts = (zip) => Object.values(zip.files).some(({ dir,
|
|
|
382
326
|
*/
|
|
383
327
|
const ensureParaIdsInternal = async (docx, options) => {
|
|
384
328
|
const archive = await loadDocxArchive(docx);
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
if (documentPartName === void 0) throw createEnsureParaIdsError("word/document.xml not found: not a WordprocessingML package");
|
|
329
|
+
const relsPath = archive.entries.find((name) => name.toLowerCase() === "_rels/.rels");
|
|
330
|
+
const rels = relsPath === void 0 ? null : await archive.readEntryString(relsPath);
|
|
331
|
+
if (rels === null) throw createEnsureParaIdsError("Package relationships not found");
|
|
332
|
+
const documentPartName = mainDocumentPart(rels, archive.entries);
|
|
333
|
+
const xmlPartNames = archive.entries.filter((name) => name.toLowerCase().endsWith(".xml") || name === documentPartName);
|
|
391
334
|
const partTexts = /* @__PURE__ */ new Map();
|
|
392
335
|
for (const name of xmlPartNames) {
|
|
393
336
|
const text = await archive.readEntryString(name);
|
|
@@ -395,17 +338,19 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
395
338
|
}
|
|
396
339
|
const taken = /* @__PURE__ */ new Set();
|
|
397
340
|
for (const text of partTexts.values()) collectExistingParaIds(text, taken);
|
|
341
|
+
const documentXml = partTexts.get(documentPartName);
|
|
342
|
+
if (documentXml === void 0) throw createEnsureParaIdsError("officeDocument part is unreadable");
|
|
398
343
|
const context = {
|
|
399
344
|
taken,
|
|
400
|
-
docKey: deterministicHexId(
|
|
345
|
+
docKey: deterministicHexId(documentXml)
|
|
401
346
|
};
|
|
402
347
|
const targetParts = [documentPartName, ...xmlPartNames.filter((name) => name !== documentPartName && isTargetPart(name)).sort((a, b) => a.localeCompare(b))];
|
|
403
348
|
const spellings = /* @__PURE__ */ new Map();
|
|
404
349
|
const seen = /* @__PURE__ */ new Set();
|
|
405
|
-
for (const [partPath, xml] of partTexts) if (isTargetPart(partPath)) {
|
|
350
|
+
for (const [partPath, xml] of partTexts) if (partPath === documentPartName || isTargetPart(partPath)) {
|
|
406
351
|
const spelling = partSpelling(xml, partPath);
|
|
407
352
|
spellings.set(partPath, spelling);
|
|
408
|
-
collectFallbackParaIds(
|
|
353
|
+
collectFallbackParaIds(spelling, seen);
|
|
409
354
|
} else collectExistingParaIds(xml, seen);
|
|
410
355
|
const updates = /* @__PURE__ */ new Map();
|
|
411
356
|
const mintedParaIds = [];
|
|
@@ -414,13 +359,17 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
414
359
|
for (const partPath of targetParts) {
|
|
415
360
|
const xml = partTexts.get(partPath);
|
|
416
361
|
const spelling = spellings.get(partPath);
|
|
417
|
-
const scan = scanPart(
|
|
418
|
-
|
|
362
|
+
const scan = scanPart({
|
|
363
|
+
partPath,
|
|
364
|
+
spelling,
|
|
365
|
+
context,
|
|
366
|
+
seen
|
|
367
|
+
});
|
|
419
368
|
if (scan.edits.length === 0) continue;
|
|
420
369
|
assigned += scan.assigned;
|
|
421
370
|
deduplicated += scan.deduplicated;
|
|
422
371
|
for (const id of scan.minted) mintedParaIds.push(id);
|
|
423
|
-
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(
|
|
372
|
+
updates.set(partPath, applySplices(xml, [...scan.edits, ...ensureRootNamespaces(spelling)], partPath));
|
|
424
373
|
}
|
|
425
374
|
if (updates.size === 0) return {
|
|
426
375
|
docx: toUint8Array(docx),
|
|
@@ -430,7 +379,10 @@ const ensureParaIdsInternal = async (docx, options) => {
|
|
|
430
379
|
mintedParaIds: []
|
|
431
380
|
};
|
|
432
381
|
const zip = await JSZip.loadAsync(docx);
|
|
433
|
-
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw
|
|
382
|
+
if (hasDigitalSignatureParts(zip) && options.allowSignedPackageMutation !== true) throw new EnsureParaIdsError({
|
|
383
|
+
reason: ENSURE_PARA_IDS_REASONS.SIGNED_PACKAGE,
|
|
384
|
+
message: "Refusing to normalize a digitally signed package because rewriting OOXML invalidates its signatures. Warn the user and pass allowSignedPackageMutation only if invalidation is acceptable."
|
|
385
|
+
});
|
|
434
386
|
for (const [partPath, content] of updates) {
|
|
435
387
|
const sourceEntry = zip.file(partPath);
|
|
436
388
|
if (sourceEntry === null) throw createEnsureParaIdsError(`Package part disappeared during normalization: ${partPath}`);
|
|
@@ -466,4 +418,4 @@ const ensureParaIds = async (docx, options = {}) => {
|
|
|
466
418
|
}
|
|
467
419
|
};
|
|
468
420
|
//#endregion
|
|
469
|
-
export { EnsureParaIdsError, ensureParaIds };
|
|
421
|
+
export { ENSURE_PARA_IDS_REASONS, EnsureParaIdsError, ensureParaIds };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/foldedListNumberFields.d.ts
|
|
3
|
+
/** Whether `content` is markup that may stand between a field and the tab that follows it. */
|
|
4
|
+
declare const isListNumberGapMarker: (content: document_d_exports.ParagraphContent) => boolean;
|
|
5
|
+
/** Whether `content` is a `LISTNUM` field, by its parsed type or its instruction. */
|
|
6
|
+
declare const isListNumberField: (content: document_d_exports.ParagraphContent) => content is document_d_exports.ComplexField;
|
|
7
|
+
/** A run that holds one tab and nothing else. */
|
|
8
|
+
declare const isTabOnlyRun: (content: document_d_exports.ParagraphContent) => content is document_d_exports.Run;
|
|
9
|
+
/** Whether `value` is what a capture stands for: a complex field, or a run. */
|
|
10
|
+
declare const isFoldedListNumber: (value: unknown) => value is document_d_exports.FoldedListNumber;
|
|
11
|
+
/** What `content` stands for when it is a capture the fold left, else nothing. */
|
|
12
|
+
declare const foldedListNumberOf: (content: document_d_exports.ParagraphContent) => document_d_exports.FoldedListNumber | undefined;
|
|
13
|
+
/** Whether `content` is a capture the fold left in place of a field or its tab. */
|
|
14
|
+
declare const isFoldedListNumberCapture: (content: document_d_exports.ParagraphContent) => boolean;
|
|
15
|
+
type ListNumberFieldFold = {
|
|
16
|
+
/** The content, each folded field and its tab replaced by a capture. */
|
|
17
|
+
content: document_d_exports.ParagraphContent[];
|
|
18
|
+
/** The cached display of each folded field that has one, in source order. */
|
|
19
|
+
cached: string[];
|
|
20
|
+
/** Every `LISTNUM` field the content holds, folded or not. */
|
|
21
|
+
fieldCount: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Replace the `LISTNUM` fields that open a paragraph, and the tab after each,
|
|
25
|
+
* by captures of the markup they were read from.
|
|
26
|
+
*
|
|
27
|
+
* Only what stands ahead of the first thing the line shows is folded: a field
|
|
28
|
+
* after text is on the line where the text is. `sourceMarkupOf` answers the
|
|
29
|
+
* markup; a field it has none for is left as the field it is, and ends the
|
|
30
|
+
* fold. Bookmark and comment markers between a field and its tab stay where
|
|
31
|
+
* they are, and the tab is still the field's when only they separate the two.
|
|
32
|
+
*/
|
|
33
|
+
declare const foldListNumberFields: (content: readonly document_d_exports.ParagraphContent[], sourceMarkupOf: (item: document_d_exports.ComplexField | document_d_exports.Run) => string | undefined) => ListNumberFieldFold;
|
|
34
|
+
/** One item of a paragraph, as far as the fold needs to know it. */
|
|
35
|
+
type ListNumberFoldItem =
|
|
36
|
+
/** A capture of a field, with the text its cached result shows. */
|
|
37
|
+
{
|
|
38
|
+
kind: "field";
|
|
39
|
+
cached: string;
|
|
40
|
+
} |
|
|
41
|
+
/** A capture of the tab after a field. */
|
|
42
|
+
{
|
|
43
|
+
kind: "tab";
|
|
44
|
+
} |
|
|
45
|
+
/** Markup that shows nothing. */
|
|
46
|
+
{
|
|
47
|
+
kind: "hidden";
|
|
48
|
+
} |
|
|
49
|
+
/** Anything the line shows. */
|
|
50
|
+
{
|
|
51
|
+
kind: "shown";
|
|
52
|
+
};
|
|
53
|
+
type ListNumberFoldPlan = {
|
|
54
|
+
/** The items' indices in the order they are to stand in. */
|
|
55
|
+
order: number[];
|
|
56
|
+
/** Indices of the captures that stay hidden; every other capture goes on the line. */
|
|
57
|
+
hidden: ReadonlySet<number>;
|
|
58
|
+
/** What the marker shows after its own text, or nothing. */
|
|
59
|
+
suffix: string | undefined;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Decide which captures of a paragraph stay hidden, and what its marker shows.
|
|
63
|
+
*
|
|
64
|
+
* `markerShowsFields` says whether the paragraph's marker shows folded fields
|
|
65
|
+
* at all. When it does not, no capture is hidden.
|
|
66
|
+
*
|
|
67
|
+
* When it does, the captures it shows are the ones that open the paragraph:
|
|
68
|
+
* those ahead of the first thing the line shows, each field followed at most
|
|
69
|
+
* by its own tab. Text put in front of them was put in front of the body, not
|
|
70
|
+
* of the marker, so if the first capture no longer opens the paragraph, it
|
|
71
|
+
* and the captures that stand with it go back to the start. Every capture
|
|
72
|
+
* further in is on the line.
|
|
73
|
+
*/
|
|
74
|
+
declare const planListNumberFold: (items: readonly ListNumberFoldItem[], markerShowsFields: boolean) => ListNumberFoldPlan;
|
|
75
|
+
/** The item a capture stands for, which shows on the line; any other item is itself. */
|
|
76
|
+
declare const unfoldedListNumberContent: (content: document_d_exports.ParagraphContent) => document_d_exports.ParagraphContent;
|
|
77
|
+
/**
|
|
78
|
+
* Bring `paragraph` to the one form the fold allows: the captures its marker
|
|
79
|
+
* shows stand at its start, every other capture is the field or the tab it
|
|
80
|
+
* stood for, and the marker shows exactly the fields still hidden behind it.
|
|
81
|
+
* A capture under a tracked change is never hidden.
|
|
82
|
+
*/
|
|
83
|
+
declare const normalizeFoldedListNumbers: (paragraph: document_d_exports.Paragraph) => void;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { ListNumberFieldFold, ListNumberFoldItem, ListNumberFoldPlan, foldListNumberFields, foldedListNumberOf, isFoldedListNumber, isFoldedListNumberCapture, isListNumberField, isListNumberGapMarker, isTabOnlyRun, normalizeFoldedListNumbers, planListNumberFold, unfoldedListNumberContent };
|