@portone/docx-editor 0.3.0 → 0.5.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/CHANGELOG.md +205 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +45 -25
- package/dist/core.d.ts +1 -1
- package/dist/docx/cloning.js +4 -9
- package/dist/docx/commentOnlyChange.d.ts +5 -3
- package/dist/docx/comments/grammar.d.ts +27 -10
- package/dist/docx/comments/grammar.js +18 -67
- package/dist/docx/comments/model.d.ts +5 -6
- package/dist/docx/comments/model.js +0 -9
- package/dist/docx/comments/parts.d.ts +16 -10
- package/dist/docx/comments/parts.js +65 -13
- package/dist/docx/comments/people.d.ts +11 -3
- package/dist/docx/comments/people.js +20 -6
- package/dist/docx/comments/policy.js +7 -1
- package/dist/docx/comments/reading.d.ts +4 -2
- package/dist/docx/comments/reading.js +1 -14
- package/dist/docx/comments/writing.js +78 -35
- package/dist/docx/exportDocx.js +30 -18
- package/dist/docx/exportRefs.d.ts +8 -3
- package/dist/docx/exportRefs.js +2 -1
- package/dist/docx/fidelity.js +24 -10
- package/dist/docx/fields.d.ts +42 -0
- package/dist/docx/fields.js +76 -0
- package/dist/docx/formatting/attrs.d.ts +20 -2
- package/dist/docx/formatting/attrs.js +57 -2
- package/dist/docx/formatting/context.d.ts +10 -2
- package/dist/docx/formatting/context.js +23 -5
- package/dist/docx/formatting/resolve.d.ts +10 -4
- package/dist/docx/formatting/resolve.js +31 -11
- package/dist/docx/formatting/styles.d.ts +25 -2
- package/dist/docx/formatting/styles.js +84 -3
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/hyperlink.d.ts +14 -4
- package/dist/docx/hyperlink.js +4 -1
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +135 -74
- package/dist/docx/importParagraph.d.ts +16 -4
- package/dist/docx/importParagraph.js +87 -93
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +190 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.d.ts +2 -2
- package/dist/docx/importTable.js +107 -66
- package/dist/docx/invariants.js +42 -31
- package/dist/docx/newLists.d.ts +30 -0
- package/dist/docx/newLists.js +24 -1
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- package/dist/docx/numberingPlanner.d.ts +6 -3
- package/dist/docx/numberingPlanner.js +38 -7
- package/dist/docx/pageGeometry.d.ts +5 -14
- package/dist/docx/pageGeometry.js +0 -12
- package/dist/docx/protectionPolicy.d.ts +13 -5
- package/dist/docx/protectionPolicy.js +32 -22
- package/dist/docx/scan.d.ts +18 -8
- package/dist/docx/scan.js +17 -11
- package/dist/docx/sections.d.ts +126 -0
- package/dist/docx/sections.js +207 -0
- package/dist/docx/serializeBlock.d.ts +13 -4
- package/dist/docx/serializeBlock.js +16 -18
- package/dist/docx/serializeParagraph.d.ts +4 -0
- package/dist/docx/serializeParagraph.js +1 -0
- package/dist/docx/serializePreserved.d.ts +14 -0
- package/dist/docx/serializePreserved.js +24 -0
- package/dist/docx/serializeStory.d.ts +17 -0
- package/dist/docx/serializeStory.js +17 -0
- package/dist/docx/serializeTable.js +10 -13
- package/dist/docx/session.d.ts +24 -7
- package/dist/docx/session.js +37 -6
- package/dist/docx/story.d.ts +140 -0
- package/dist/docx/story.js +237 -0
- package/dist/docx/storyProjection.js +6 -1
- package/dist/docx/tableFormatting/conditions.d.ts +76 -0
- package/dist/docx/tableFormatting/conditions.js +196 -0
- package/dist/docx/tableFormatting/editing.d.ts +7 -3
- package/dist/docx/tableFormatting/editing.js +4 -5
- package/dist/docx/tableFormatting/reading.d.ts +49 -19
- package/dist/docx/tableFormatting/reading.js +109 -35
- package/dist/docx/tableFormatting.d.ts +1 -0
- package/dist/docx/tableFormatting.js +1 -0
- package/dist/docx/tableTemplate.js +12 -12
- package/dist/editor/clipboard/htmlReader.d.ts +30 -0
- package/dist/editor/clipboard/htmlReader.js +302 -0
- package/dist/editor/clipboard/internalChannel.d.ts +57 -0
- package/dist/editor/clipboard/internalChannel.js +58 -0
- package/dist/editor/clipboard/normalizers.d.ts +99 -0
- package/dist/editor/clipboard/normalizers.js +199 -0
- package/dist/editor/clipboard/parser.d.ts +34 -0
- package/dist/editor/clipboard/parser.js +58 -0
- package/dist/editor/clipboard/plugin.d.ts +21 -0
- package/dist/editor/clipboard/plugin.js +320 -0
- package/dist/editor/clipboard/readContext.d.ts +24 -0
- package/dist/editor/clipboard/readContext.js +19 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +175 -105
- package/dist/editor/commands/comments/model.d.ts +34 -17
- package/dist/editor/commands/comments/model.js +11 -0
- package/dist/editor/commands/comments/reading.d.ts +5 -3
- package/dist/editor/commands/comments/reading.js +8 -58
- package/dist/editor/commands/formatting/propertyCommands.js +6 -1
- package/dist/editor/commands/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/listCommands.d.ts +12 -3
- package/dist/editor/commands/listCommands.js +76 -30
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +6 -1
- package/dist/editor/createEditor.js +14 -4
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- package/dist/editor/editorDocument.d.ts +7 -6
- package/dist/editor/editorDocument.js +18 -6
- package/dist/editor/imageFiles.d.ts +2 -2
- package/dist/editor/imageFiles.js +2 -0
- package/dist/editor/insertTable.d.ts +1 -1
- package/dist/editor/insertTable.js +2 -2
- package/dist/editor/paragraphEdits.d.ts +16 -1
- package/dist/editor/paragraphEdits.js +13 -5
- package/dist/editor/paragraphPlacement.d.ts +11 -0
- package/dist/editor/paragraphPlacement.js +18 -0
- package/dist/editor/plainText.d.ts +12 -0
- package/dist/editor/plainText.js +2 -1
- package/dist/editor/plugins/columnResize.js +6 -3
- package/dist/editor/plugins/commentComposer.d.ts +28 -0
- package/dist/editor/plugins/commentComposer.js +46 -0
- package/dist/editor/plugins/commentDecorations.d.ts +29 -2
- package/dist/editor/plugins/commentDecorations.js +89 -15
- package/dist/editor/plugins/documentProjection.d.ts +31 -0
- package/dist/editor/plugins/documentProjection.js +22 -0
- package/dist/editor/plugins/imagePaste.js +20 -10
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/numberingDecorations.d.ts +10 -4
- package/dist/editor/plugins/numberingDecorations.js +43 -2
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/paragraphDisplay.js +9 -3
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/tableDisplay.js +19 -5
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- package/dist/model/format.d.ts +50 -0
- package/dist/model/format.js +65 -0
- package/dist/numbering/listRegistry.d.ts +37 -0
- package/dist/numbering/listRegistry.js +112 -0
- package/dist/numbering/listTemplate.d.ts +26 -17
- package/dist/numbering/listTemplate.js +66 -20
- package/dist/numbering/markers.d.ts +15 -6
- package/dist/numbering/markers.js +26 -59
- package/dist/numbering/parseNumbering.d.ts +70 -5
- package/dist/numbering/parseNumbering.js +100 -26
- package/dist/numbering/spellers.d.ts +15 -0
- package/dist/numbering/spellers.js +96 -0
- package/dist/numbering/writeNumbering.d.ts +5 -3
- package/dist/numbering/writeNumbering.js +15 -15
- package/dist/ooxml/conformance.d.ts +28 -0
- package/dist/ooxml/conformance.js +20 -0
- package/dist/ooxml/errors.d.ts +5 -5
- package/dist/ooxml/fragment.d.ts +11 -0
- package/dist/ooxml/fragment.js +14 -4
- package/dist/ooxml/image.d.ts +3 -4
- package/dist/ooxml/image.js +50 -5
- package/dist/ooxml/names.d.ts +15 -0
- package/dist/ooxml/names.js +10 -1
- package/dist/ooxml/partSplice.d.ts +1 -1
- package/dist/ooxml/partSplice.js +10 -3
- package/dist/ooxml/props.d.ts +8 -0
- package/dist/ooxml/props.js +14 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/ooxml/xml.d.ts +8 -9
- package/dist/ooxml/xml.js +6 -6
- package/dist/page/PageGuides.d.ts +8 -4
- package/dist/page/PageGuides.js +13 -37
- package/dist/page/pageLayout.d.ts +62 -13
- package/dist/page/pageLayout.js +91 -37
- package/dist/page/usePageLayout.d.ts +14 -11
- package/dist/page/usePageLayout.js +21 -29
- package/dist/schema/attrRoles.js +44 -19
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +220 -139
- package/dist/schema/editGuard.d.ts +1 -1
- package/dist/schema/guards.js +2 -2
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/preservedFragments.d.ts +12 -0
- package/dist/schema/preservedFragments.js +38 -0
- package/dist/schema/preservedGuards.d.ts +9 -7
- package/dist/schema/preservedGuards.js +112 -11
- package/dist/schema/protection.d.ts +34 -5
- package/dist/schema/protection.js +42 -14
- package/dist/schema/rendering.js +3 -1
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/styles/classNames.d.ts +16 -7
- package/dist/styles/classNames.js +16 -7
- package/dist/styles.css +90 -30
- package/dist/table/cellFormatting.js +1 -2
- package/dist/table/gridBorders.d.ts +11 -15
- package/dist/table/gridBorders.js +32 -18
- package/dist/table/resize.d.ts +5 -3
- package/dist/ui/CommentsPanel.d.ts +1 -3
- package/dist/ui/CommentsPanel.js +24 -15
- package/dist/ui/NotesPanel.js +2 -2
- package/dist/ui/TextMenu.d.ts +1 -3
- package/dist/ui/TextMenu.js +10 -6
- package/dist/ui/comments/CommentComposer.d.ts +6 -1
- package/dist/ui/comments/CommentComposer.js +26 -3
- package/package.json +1 -1
- package/dist/editor/externalClipboard.d.ts +0 -11
- package/dist/editor/externalClipboard.js +0 -456
package/dist/docx/invariants.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// src/docx/invariants.ts
|
|
2
|
-
import { spanCount } from "../model/format.js";
|
|
2
|
+
import { spanCount, toParagraphFormat } from "../model/format.js";
|
|
3
3
|
import {
|
|
4
4
|
attributeByLocalName,
|
|
5
5
|
parseXml,
|
|
6
6
|
W_NS,
|
|
7
7
|
withXmlParser
|
|
8
8
|
} from "../ooxml/xml.js";
|
|
9
|
+
import { visitPreservedFragments } from "../schema/preservedFragments.js";
|
|
10
|
+
import { unattributedCommentAuthors } from "../schema/protection.js";
|
|
9
11
|
import {
|
|
10
12
|
commentReferencesIn,
|
|
11
13
|
commentsChanged,
|
|
@@ -22,32 +24,23 @@ import { unrecordedAuthors } from "./comments/people.js";
|
|
|
22
24
|
import { currentCommentBodies } from "./comments/writing.js";
|
|
23
25
|
import { identityProblems } from "./identities.js";
|
|
24
26
|
import { insertedImageSrcs } from "./media.js";
|
|
25
|
-
import { newNumIds,
|
|
27
|
+
import { canDefineNewList, newNumIds, startedLists } from "./newLists.js";
|
|
26
28
|
import { CONTENT_TYPES_PATH } from "./packageParts.js";
|
|
27
|
-
import { lostOriginal } from "./
|
|
29
|
+
import { lostOriginal } from "./serializePreserved.js";
|
|
28
30
|
import {
|
|
29
31
|
originalBlock,
|
|
30
32
|
sessionOf
|
|
31
33
|
} from "./session.js";
|
|
32
|
-
function markerSourceOf(node, session) {
|
|
33
|
-
if (node.type.name === "rawInline" || node.type.name === "rawBlock") {
|
|
34
|
-
return typeof node.attrs.xml === "string" ? node.attrs.xml : null;
|
|
35
|
-
}
|
|
36
|
-
if (node.type.isInGroup("preserved")) {
|
|
37
|
-
return originalBlock(node, session)?.xml ?? null;
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
34
|
var bookmarkPairs = {
|
|
42
35
|
name: "bookmarkPairs",
|
|
43
36
|
check(doc, session) {
|
|
44
37
|
const problems = [];
|
|
45
38
|
const open = /* @__PURE__ */ new Map();
|
|
46
39
|
const used = /* @__PURE__ */ new Set();
|
|
47
|
-
doc
|
|
48
|
-
const source =
|
|
49
|
-
if (source === null) return
|
|
50
|
-
if (!source.includes("bookmark")) return
|
|
40
|
+
visitPreservedFragments(doc, (node, pos, xml) => {
|
|
41
|
+
const source = xml ?? originalBlock(node, session)?.xml ?? null;
|
|
42
|
+
if (source === null) return;
|
|
43
|
+
if (!source.includes("bookmark")) return;
|
|
51
44
|
let root;
|
|
52
45
|
try {
|
|
53
46
|
root = parseXml(
|
|
@@ -59,7 +52,7 @@ var bookmarkPairs = {
|
|
|
59
52
|
message: "preserved bookmark XML could not be parsed",
|
|
60
53
|
pos
|
|
61
54
|
});
|
|
62
|
-
return
|
|
55
|
+
return;
|
|
63
56
|
}
|
|
64
57
|
for (const marker of Array.from(root.getElementsByTagName("*"))) {
|
|
65
58
|
if (marker.namespaceURI !== W_NS || marker.localName !== "bookmarkStart" && marker.localName !== "bookmarkEnd")
|
|
@@ -91,7 +84,6 @@ var bookmarkPairs = {
|
|
|
91
84
|
});
|
|
92
85
|
}
|
|
93
86
|
}
|
|
94
|
-
return true;
|
|
95
87
|
});
|
|
96
88
|
for (const [id, pos] of open) {
|
|
97
89
|
problems.push({
|
|
@@ -132,11 +124,16 @@ var tableGrids = {
|
|
|
132
124
|
return problems;
|
|
133
125
|
}
|
|
134
126
|
};
|
|
127
|
+
var CARRIES_ITS_XML = /* @__PURE__ */ new Set([
|
|
128
|
+
"rawInline",
|
|
129
|
+
"rawRunContent"
|
|
130
|
+
]);
|
|
135
131
|
function lostOriginalOf(node, session) {
|
|
136
|
-
if (
|
|
132
|
+
if (CARRIES_ITS_XML.has(node.type.name)) {
|
|
137
133
|
return typeof node.attrs.xml === "string" ? null : "a preserved element has lost its original XML";
|
|
138
134
|
}
|
|
139
135
|
if (node.type.isInGroup("preserved")) {
|
|
136
|
+
if (typeof node.attrs.xml === "string") return null;
|
|
140
137
|
return originalBlock(node, session) ? null : lostOriginal(node, session);
|
|
141
138
|
}
|
|
142
139
|
return null;
|
|
@@ -160,17 +157,24 @@ var uniqueIdentities = {
|
|
|
160
157
|
return identityProblems(doc);
|
|
161
158
|
}
|
|
162
159
|
};
|
|
163
|
-
var
|
|
164
|
-
name: "
|
|
160
|
+
var listDefinitions = {
|
|
161
|
+
name: "listDefinitions",
|
|
165
162
|
check(doc, session) {
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
const undefinedIds = new Set(startedLists(doc, session).unregistered);
|
|
164
|
+
if (undefinedIds.size === 0) return [];
|
|
165
|
+
const problems = [];
|
|
166
|
+
doc.descendants((node, pos) => {
|
|
167
|
+
if (node.type.name !== "paragraph") return true;
|
|
168
|
+
const numId = toParagraphFormat(node.attrs.format)?.numbering?.numId;
|
|
169
|
+
if (numId === void 0 || !undefinedIds.delete(numId)) return false;
|
|
170
|
+
problems.push({
|
|
171
|
+
code: "unsupported-content",
|
|
172
|
+
message: `the list numbered ${numId} has no definition to be written`,
|
|
173
|
+
pos
|
|
174
|
+
});
|
|
175
|
+
return false;
|
|
176
|
+
});
|
|
177
|
+
return problems;
|
|
174
178
|
}
|
|
175
179
|
};
|
|
176
180
|
function addsCommentsPart(doc, session) {
|
|
@@ -184,7 +188,8 @@ function addsCommentsPart(doc, session) {
|
|
|
184
188
|
return true;
|
|
185
189
|
return bodyChanged && (peoplePart.pathIn(session) === null || session.comments.people.xml === null) && unrecordedAuthors(
|
|
186
190
|
currentCommentBodies(references).values(),
|
|
187
|
-
session.comments.people
|
|
191
|
+
session.comments.people,
|
|
192
|
+
unattributedCommentAuthors(session.comments.ordered)
|
|
188
193
|
).size > 0;
|
|
189
194
|
}
|
|
190
195
|
var mediaContentTypes = {
|
|
@@ -198,6 +203,12 @@ var mediaContentTypes = {
|
|
|
198
203
|
message: `cannot add an image to a package that has no ${CONTENT_TYPES_PATH}`
|
|
199
204
|
});
|
|
200
205
|
}
|
|
206
|
+
if (!canDefineNewList(session) && newNumIds(doc, session).length > 0) {
|
|
207
|
+
problems.push({
|
|
208
|
+
code: "missing-content-types",
|
|
209
|
+
message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
201
212
|
if (addsCommentsPart(doc, session)) {
|
|
202
213
|
problems.push({
|
|
203
214
|
code: "missing-content-types",
|
|
@@ -230,7 +241,7 @@ var EXPORT_INVARIANTS = [
|
|
|
230
241
|
tableGrids,
|
|
231
242
|
preservedOriginals,
|
|
232
243
|
uniqueIdentities,
|
|
233
|
-
|
|
244
|
+
listDefinitions,
|
|
234
245
|
mediaContentTypes,
|
|
235
246
|
commentPartRoots
|
|
236
247
|
];
|
package/dist/docx/newLists.d.ts
CHANGED
|
@@ -5,7 +5,16 @@
|
|
|
5
5
|
* writer rather than being read off it.
|
|
6
6
|
*/
|
|
7
7
|
import type { Node as PMNode } from "prosemirror-model";
|
|
8
|
+
import { type NewList } from "../numbering/parseNumbering";
|
|
8
9
|
import type { SessionStore } from "./session";
|
|
10
|
+
/**
|
|
11
|
+
* How the main part relates the numbering part, and what the package declares that part to hold.
|
|
12
|
+
*
|
|
13
|
+
* The reader finds the part by this relationship and the writer relates a new one under it, so
|
|
14
|
+
* neither can name it differently from the other.
|
|
15
|
+
*/
|
|
16
|
+
export declare const NUMBERING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering";
|
|
17
|
+
export declare const NUMBERING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml";
|
|
9
18
|
/**
|
|
10
19
|
* The numbering ids that appeared during editing and have no definition.
|
|
11
20
|
*
|
|
@@ -13,8 +22,29 @@ import type { SessionStore } from "./session";
|
|
|
13
22
|
* because exporting such a document without editing it must not disturb numbering.xml.
|
|
14
23
|
*/
|
|
15
24
|
export declare function newNumIds(doc: PMNode, session: SessionStore): number[];
|
|
25
|
+
/** The lists started during editing, split by whether a definition was registered for each */
|
|
26
|
+
export interface StartedLists {
|
|
27
|
+
/** The definition each list was started with, which is what goes into numbering.xml */
|
|
28
|
+
readonly defined: ReadonlyMap<number, NewList>;
|
|
29
|
+
/** The numbers a list was started under that no definition stands behind */
|
|
30
|
+
readonly unregistered: readonly number[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The lists started during editing, each against the definition it was started with.
|
|
34
|
+
*
|
|
35
|
+
* The definition is the one the document node carries (`numbering/listRegistry`), which the list
|
|
36
|
+
* commands and the clipboard record as they start a list. A number carrying none is reported here
|
|
37
|
+
* rather than written bare, which the export invariants turn into a refusal.
|
|
38
|
+
*/
|
|
39
|
+
export declare function startedLists(doc: PMNode, session: SessionStore): StartedLists;
|
|
16
40
|
/** The numbering part as it was opened, which a new definition is spliced into. null when the document has none */
|
|
17
41
|
export declare function numberingPartOf(session: SessionStore): {
|
|
18
42
|
path: string;
|
|
19
43
|
bytes: Uint8Array;
|
|
20
44
|
} | null;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the definition of a new list has somewhere to go: the numbering part the document was
|
|
47
|
+
* opened with, or one written from scratch, which only a package that can declare what the new
|
|
48
|
+
* part holds may take.
|
|
49
|
+
*/
|
|
50
|
+
export declare function canDefineNewList(session: SessionStore): boolean;
|
package/dist/docx/newLists.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// src/docx/newLists.ts
|
|
2
2
|
import { toParagraphFormat } from "../model/format.js";
|
|
3
|
+
import { NEW_LISTS_ATTR, newListsOf } from "../numbering/listRegistry.js";
|
|
3
4
|
import { parseNumbering } from "../numbering/parseNumbering.js";
|
|
5
|
+
import { R_NS } from "../ooxml/xml.js";
|
|
6
|
+
import { CONTENT_TYPES_PATH } from "./packageParts.js";
|
|
7
|
+
var NUMBERING_REL_TYPE = `${R_NS}/numbering`;
|
|
8
|
+
var NUMBERING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml";
|
|
4
9
|
function collectNumIds(node, into) {
|
|
5
10
|
const visit = (candidate) => {
|
|
6
11
|
if (candidate.type.name !== "paragraph") return true;
|
|
@@ -25,12 +30,30 @@ function newNumIds(doc, session) {
|
|
|
25
30
|
const atOpen = numIdsAtOpen(session);
|
|
26
31
|
return Array.from(numIdsIn(doc)).filter((numId) => !defined.has(numId) && !atOpen.has(numId)).sort((a, b) => a - b);
|
|
27
32
|
}
|
|
33
|
+
function startedLists(doc, session) {
|
|
34
|
+
const registered = newListsOf(doc.attrs[NEW_LISTS_ATTR]);
|
|
35
|
+
const defined = /* @__PURE__ */ new Map();
|
|
36
|
+
const unregistered = [];
|
|
37
|
+
for (const numId of newNumIds(doc, session)) {
|
|
38
|
+
const list = registered.get(numId);
|
|
39
|
+
if (list === void 0) unregistered.push(numId);
|
|
40
|
+
else defined.set(numId, list);
|
|
41
|
+
}
|
|
42
|
+
return { defined, unregistered };
|
|
43
|
+
}
|
|
28
44
|
function numberingPartOf(session) {
|
|
29
45
|
const path = session.numberingPartPath;
|
|
30
46
|
const bytes = path === null ? void 0 : session.parts.get(path);
|
|
31
47
|
return path === null || bytes === void 0 ? null : { path, bytes };
|
|
32
48
|
}
|
|
49
|
+
function canDefineNewList(session) {
|
|
50
|
+
return numberingPartOf(session) !== null || session.parts.has(CONTENT_TYPES_PATH);
|
|
51
|
+
}
|
|
33
52
|
export {
|
|
53
|
+
NUMBERING_CONTENT_TYPE,
|
|
54
|
+
NUMBERING_REL_TYPE,
|
|
55
|
+
canDefineNewList,
|
|
34
56
|
newNumIds,
|
|
35
|
-
numberingPartOf
|
|
57
|
+
numberingPartOf,
|
|
58
|
+
startedLists
|
|
36
59
|
};
|
package/dist/docx/notes.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/** Reads footnote and endnote bodies related from the main document story. */
|
|
2
2
|
export type NoteKind = "footnote" | "endnote";
|
|
3
|
+
/**
|
|
4
|
+
* One entry of a notes part as it arrived. What the note says is a story of its own, held on the
|
|
5
|
+
* document node under `footnote:<id>` or `endnote:<id>` (`docx/story`).
|
|
6
|
+
*/
|
|
3
7
|
export interface ImportedNote {
|
|
4
8
|
kind: NoteKind;
|
|
5
9
|
id: string;
|
|
6
10
|
label: string;
|
|
7
|
-
text: string;
|
|
8
11
|
type: string | null;
|
|
9
12
|
}
|
|
10
13
|
export interface ImportedNotePart {
|
|
11
14
|
partPath: string | null;
|
|
15
|
+
/** The raw text of the part, which is what the note stories are sliced out of. null for none */
|
|
16
|
+
xml: string | null;
|
|
12
17
|
ordered: readonly ImportedNote[];
|
|
13
18
|
byId: ReadonlyMap<string, ImportedNote>;
|
|
14
19
|
}
|
package/dist/docx/notes.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
decodeUtf8,
|
|
5
5
|
elementChildren,
|
|
6
6
|
parseXml,
|
|
7
|
-
R_NS
|
|
8
|
-
W_NS
|
|
7
|
+
R_NS
|
|
9
8
|
} from "../ooxml/xml.js";
|
|
10
9
|
import { relatedPartPath } from "./packageParts.js";
|
|
11
10
|
var EMPTY_PART = {
|
|
12
11
|
partPath: null,
|
|
12
|
+
xml: null,
|
|
13
13
|
ordered: [],
|
|
14
14
|
byId: /* @__PURE__ */ new Map()
|
|
15
15
|
};
|
|
@@ -17,23 +17,13 @@ var NO_NOTES = {
|
|
|
17
17
|
footnotes: EMPTY_PART,
|
|
18
18
|
endnotes: EMPTY_PART
|
|
19
19
|
};
|
|
20
|
-
function inlineText(node) {
|
|
21
|
-
if (node.localName === "t") return node.textContent ?? "";
|
|
22
|
-
if (node.localName === "tab") return " ";
|
|
23
|
-
if (node.localName === "br" || node.localName === "cr") return "\n";
|
|
24
|
-
if (node.localName === "noBreakHyphen") return "\u2011";
|
|
25
|
-
if (node.localName === "softHyphen") return "\xAD";
|
|
26
|
-
return elementChildren(node).map(inlineText).join("");
|
|
27
|
-
}
|
|
28
|
-
function noteText(el) {
|
|
29
|
-
return Array.from(el.getElementsByTagNameNS(W_NS, "p")).map(inlineText).join("\n");
|
|
30
|
-
}
|
|
31
20
|
function notePart(parts, mainPartPath, kind) {
|
|
32
21
|
const partPath = relatedPartPath(parts, mainPartPath, `${R_NS}/${kind}s`);
|
|
33
22
|
if (partPath === null) return EMPTY_PART;
|
|
34
23
|
const bytes = parts.get(partPath);
|
|
35
24
|
if (!bytes) return { ...EMPTY_PART, partPath };
|
|
36
|
-
const
|
|
25
|
+
const xml = decodeUtf8(bytes).text;
|
|
26
|
+
const root = parseXml(xml).documentElement;
|
|
37
27
|
const elements = elementChildren(root).filter((el) => el.localName === kind);
|
|
38
28
|
const ordered = elements.flatMap((el) => {
|
|
39
29
|
const id = attributeByLocalName(el, "id");
|
|
@@ -41,13 +31,13 @@ function notePart(parts, mainPartPath, kind) {
|
|
|
41
31
|
const type = attributeByLocalName(el, "type");
|
|
42
32
|
const regular = type === null || type === "normal";
|
|
43
33
|
const label = regular ? "?" : "";
|
|
44
|
-
return [{ kind, id, label,
|
|
34
|
+
return [{ kind, id, label, type }];
|
|
45
35
|
});
|
|
46
36
|
const byId = /* @__PURE__ */ new Map();
|
|
47
37
|
for (const note of ordered) {
|
|
48
38
|
if (!byId.has(note.id)) byId.set(note.id, note);
|
|
49
39
|
}
|
|
50
|
-
return { partPath, ordered, byId };
|
|
40
|
+
return { partPath, xml, ordered, byId };
|
|
51
41
|
}
|
|
52
42
|
function readNotes(parts, mainPartPath) {
|
|
53
43
|
return {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The numbering part with the definition
|
|
2
|
+
* The numbering part with the definition every list started during editing was started with
|
|
3
|
+
* spliced in, and the part itself when the document arrived without one.
|
|
3
4
|
*
|
|
4
|
-
* The original text is left as is and only the new definitions go in.
|
|
5
|
-
*
|
|
5
|
+
* The original text is left as is and only the new definitions go in. A document that has no
|
|
6
|
+
* numbering part gets one written from scratch, with the relationship that finds it and the
|
|
7
|
+
* declaration of what it holds asked of the context, so the first list a document takes costs it
|
|
8
|
+
* nothing outside this planner. Nothing is written when no list was started.
|
|
6
9
|
*/
|
|
7
10
|
import type { PartPlanner } from "./partPlan";
|
|
8
11
|
export declare const numberingPlanner: PartPlanner;
|
|
@@ -1,17 +1,48 @@
|
|
|
1
1
|
// src/docx/numberingPlanner.ts
|
|
2
2
|
import { addListDefinitions } from "../numbering/writeNumbering.js";
|
|
3
|
+
import { elementXml, xmlnsAttr } from "../ooxml/element.js";
|
|
4
|
+
import { NAMESPACES, wName } from "../ooxml/names.js";
|
|
5
|
+
import { ensureRootDeclarations } from "../ooxml/partSplice.js";
|
|
3
6
|
import { decodeUtf8, encodeUtf8 } from "../ooxml/xml.js";
|
|
4
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
NUMBERING_CONTENT_TYPE,
|
|
9
|
+
NUMBERING_REL_TYPE,
|
|
10
|
+
numberingPartOf,
|
|
11
|
+
startedLists
|
|
12
|
+
} from "./newLists.js";
|
|
13
|
+
import { availablePartPath } from "./packageParts.js";
|
|
14
|
+
import { directoryOf } from "./relationships.js";
|
|
15
|
+
function emptyNumberingPart() {
|
|
16
|
+
return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + elementXml(wName("numbering"), [xmlnsAttr("w")]);
|
|
17
|
+
}
|
|
5
18
|
var numberingPlanner = {
|
|
6
19
|
name: "numbering",
|
|
7
|
-
plan(doc, session) {
|
|
8
|
-
const
|
|
20
|
+
plan(doc, session, context) {
|
|
21
|
+
const { defined } = startedLists(doc, session);
|
|
22
|
+
if (defined.size === 0) return null;
|
|
9
23
|
const original = numberingPartOf(session);
|
|
10
|
-
if (
|
|
24
|
+
if (original === null) {
|
|
25
|
+
const path = session.numberingPartPath ?? availablePartPath(session.parts, session.mainPartPath, "numbering");
|
|
26
|
+
if (session.numberingPartPath === null) {
|
|
27
|
+
context.relationships.add({
|
|
28
|
+
type: NUMBERING_REL_TYPE,
|
|
29
|
+
target: path.slice(directoryOf(session.mainPartPath).length)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
context.contentTypes.addOverride(path, NUMBERING_CONTENT_TYPE);
|
|
33
|
+
return /* @__PURE__ */ new Map([
|
|
34
|
+
[
|
|
35
|
+
path,
|
|
36
|
+
encodeUtf8(addListDefinitions(emptyNumberingPart(), defined), false)
|
|
37
|
+
]
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
11
40
|
const { text, hadBom } = decodeUtf8(original.bytes);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
const rewritten = ensureRootDeclarations(
|
|
42
|
+
addListDefinitions(text, defined),
|
|
43
|
+
{ namespaces: { w: NAMESPACES.w } }
|
|
44
|
+
);
|
|
45
|
+
return /* @__PURE__ */ new Map([[original.path, encodeUtf8(rewritten, hadBom)]]);
|
|
15
46
|
}
|
|
16
47
|
};
|
|
17
48
|
export {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The paper a document is written on: its size and its margins.
|
|
3
3
|
*
|
|
4
|
-
* A document says so in
|
|
5
|
-
* only: the `w:sectPr` itself rides out
|
|
4
|
+
* A document says so in a `w:sectPr`, one per section (`./sections`). The values are read for the
|
|
5
|
+
* screen only: the `w:sectPr` itself rides out as the text it arrived as, so what a document
|
|
6
6
|
* declares is never rewritten from these numbers.
|
|
7
7
|
*
|
|
8
8
|
* Everything here is in twips (1/1440 inch), the unit `w:pgSz` and `w:pgMar` are written in.
|
|
@@ -35,6 +35,9 @@ export declare const A4_PORTRAIT: PageGeometry;
|
|
|
35
35
|
/**
|
|
36
36
|
* The geometry a `w:sectPr` element lays down, with anything it leaves unsaid taken from A4.
|
|
37
37
|
*
|
|
38
|
+
* The reading is deliberately forgiving: it runs against a document already opened, and a
|
|
39
|
+
* `w:sectPr` that cannot be read is a reason to draw A4, never a reason to refuse the file.
|
|
40
|
+
*
|
|
38
41
|
* `w:orient` is not applied on top of the size: Word writes the width and the height already
|
|
39
42
|
* swapped for a landscape section, so honouring both would turn the paper back upright.
|
|
40
43
|
*/
|
|
@@ -63,15 +66,3 @@ export declare function bodyWidth(geometry: PageGeometry): BodyWidth;
|
|
|
63
66
|
* It is what a caller with no document to ask still gets: 11906 - 1247 * 2 = 9412 dxa.
|
|
64
67
|
*/
|
|
65
68
|
export declare const A4_BODY_WIDTH: BodyWidth;
|
|
66
|
-
/**
|
|
67
|
-
* The geometry of the first section of an already parsed body.
|
|
68
|
-
*
|
|
69
|
-
* A document may hold a `w:sectPr` per section, each with a paper of its own. The screen draws
|
|
70
|
-
* one paper for the whole document, so the first section decides it; the rest ride out
|
|
71
|
-
* untouched in their preserved blocks and are drawn on the first section's paper.
|
|
72
|
-
* `site/content/docs/features.mdx` records that limit.
|
|
73
|
-
*
|
|
74
|
-
* The reading is deliberately forgiving: this runs against a document already opened, and a
|
|
75
|
-
* `w:sectPr` we cannot read is a reason to draw A4, never a reason to refuse the file.
|
|
76
|
-
*/
|
|
77
|
-
export declare function readBodyGeometry(body: Element): PageGeometry;
|
|
@@ -73,17 +73,6 @@ function bodyWidth(geometry) {
|
|
|
73
73
|
return { twips, px: twipsToPx(twips) };
|
|
74
74
|
}
|
|
75
75
|
var A4_BODY_WIDTH = bodyWidth(A4_PORTRAIT);
|
|
76
|
-
function readBodyGeometry(body) {
|
|
77
|
-
return readPageGeometry(firstSectPrIn(body));
|
|
78
|
-
}
|
|
79
|
-
function firstSectPrIn(root) {
|
|
80
|
-
const found = root.getElementsByTagName("w:sectPr").item(0);
|
|
81
|
-
if (found) return found;
|
|
82
|
-
for (const el of root.getElementsByTagName("*")) {
|
|
83
|
-
if (el.localName === "sectPr") return el;
|
|
84
|
-
}
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
76
|
export {
|
|
88
77
|
A4_BODY_WIDTH,
|
|
89
78
|
A4_PORTRAIT,
|
|
@@ -91,7 +80,6 @@ export {
|
|
|
91
80
|
bodyHeightTwips,
|
|
92
81
|
bodyWidth,
|
|
93
82
|
bodyWidthTwips,
|
|
94
|
-
readBodyGeometry,
|
|
95
83
|
readPageGeometry,
|
|
96
84
|
twipsToPx
|
|
97
85
|
};
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* undefined, and a caller holding the level alone has nothing to run.
|
|
12
12
|
*/
|
|
13
13
|
import { type XmlParser } from "../ooxml/xml";
|
|
14
|
-
import type
|
|
14
|
+
import { type EditableComments, type EditingProtection } from "../schema/protection";
|
|
15
15
|
import { type DocxBytes } from "./importDocx";
|
|
16
16
|
import type { SessionStore } from "./session";
|
|
17
17
|
import { type Story } from "./storyProjection";
|
|
@@ -59,8 +59,14 @@ export interface StoryPartKind {
|
|
|
59
59
|
rootKept(before: SessionStore, after: SessionStore): boolean;
|
|
60
60
|
/** The keys this file still stands behind, which is what an entry has to be keyed by */
|
|
61
61
|
referents(story: Story): ReadonlySet<string>;
|
|
62
|
-
/**
|
|
63
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Grammar alone: whether the entry is one this editor's writer could have put out, whoever it
|
|
64
|
+
* belongs to. `original` is the entry the file that arrived held under the same key, and null
|
|
65
|
+
* for one that appeared, because what the writer can put out depends on what it had to work
|
|
66
|
+
* with: it writes a comment's body out of the blocks it models and passes the rest through from
|
|
67
|
+
* the entry it read.
|
|
68
|
+
*/
|
|
69
|
+
wellFormed(entry: Element, original: Element | null): boolean;
|
|
64
70
|
/**
|
|
65
71
|
* Whether the entry came back differing from the one that arrived in nothing but a change this
|
|
66
72
|
* protection leaves to everyone. Such an entry is nobody's rewrite, so it is held neither to
|
|
@@ -70,9 +76,11 @@ export interface StoryPartKind {
|
|
|
70
76
|
/**
|
|
71
77
|
* Permission alone: whether `authorId` may have written (`original === null`) or rewritten this
|
|
72
78
|
* entry under `options`. `session` is the submission's, so a kind can look across at a sibling
|
|
73
|
-
* part, the way a comment's author resolves through the people part.
|
|
79
|
+
* part, the way a comment's author resolves through the people part. `unattributed` is the
|
|
80
|
+
* display names the file that arrived writes comments under while recording nobody for them,
|
|
81
|
+
* which is what an entry claiming no identity is held against.
|
|
74
82
|
*/
|
|
75
|
-
allowed(entry: Element, original: Element | null, authorId: string, options: PolicyOptions, session: SessionStore): boolean;
|
|
83
|
+
allowed(entry: Element, original: Element | null, authorId: string, options: PolicyOptions, session: SessionStore, unattributed: ReadonlySet<string>): boolean;
|
|
76
84
|
}
|
|
77
85
|
/** The two reasons the package comparison answers with, whatever the policy */
|
|
78
86
|
export type PackageReason = "part-changed" | "relationship-changed";
|
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
parseXml,
|
|
6
6
|
withXmlParser
|
|
7
7
|
} from "../ooxml/xml.js";
|
|
8
|
+
import {
|
|
9
|
+
unattributedCommentAuthors
|
|
10
|
+
} from "../schema/protection.js";
|
|
8
11
|
import { importDocx } from "./importDocx.js";
|
|
9
12
|
import { CONTENT_TYPES_PATH } from "./packageParts.js";
|
|
10
13
|
import {
|
|
@@ -31,9 +34,10 @@ function excusedPaths(policy, session) {
|
|
|
31
34
|
(path) => path !== null
|
|
32
35
|
);
|
|
33
36
|
}
|
|
34
|
-
function aroundTheStory(
|
|
35
|
-
const preserved = session.blocks.filter((block) => !isModelledBlock(block.node)).map((block) => block.xml).join("");
|
|
36
|
-
|
|
37
|
+
function aroundTheStory(story) {
|
|
38
|
+
const preserved = story.session.blocks.filter((block) => !isModelledBlock(block.node)).map((block) => block.xml).join("");
|
|
39
|
+
const sectPr = story.doc.attrs.sectPr;
|
|
40
|
+
return story.session.documentPrefix + preserved + (typeof sectPr === "string" ? sectPr : "") + story.session.documentSuffix;
|
|
37
41
|
}
|
|
38
42
|
function relationshipsKept(before, after, relTypes) {
|
|
39
43
|
if (new Set(before.map((entry) => entry.id)).size !== before.length || new Set(after.map((entry) => entry.id)).size !== after.length) {
|
|
@@ -77,41 +81,43 @@ function gainedPartsAreNew(policy, before, after) {
|
|
|
77
81
|
);
|
|
78
82
|
}
|
|
79
83
|
function packageKept(policy, before, after) {
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const was = before.session;
|
|
85
|
+
const now = after.session;
|
|
86
|
+
if (was.mainPartPath !== now.mainPartPath) {
|
|
87
|
+
return { ok: false, reason: "part-changed", part: was.mainPartPath };
|
|
82
88
|
}
|
|
83
|
-
const relsPath = relsPathOf(
|
|
89
|
+
const relsPath = relsPathOf(was.mainPartPath);
|
|
84
90
|
const untouched = /* @__PURE__ */ new Set([
|
|
85
|
-
|
|
91
|
+
was.mainPartPath,
|
|
86
92
|
relsPath,
|
|
87
93
|
CONTENT_TYPES_PATH,
|
|
88
|
-
...excusedPaths(policy,
|
|
89
|
-
...excusedPaths(policy,
|
|
94
|
+
...excusedPaths(policy, was),
|
|
95
|
+
...excusedPaths(policy, now)
|
|
90
96
|
]);
|
|
91
|
-
for (const path of /* @__PURE__ */ new Set([...
|
|
97
|
+
for (const path of /* @__PURE__ */ new Set([...was.parts.keys(), ...now.parts.keys()])) {
|
|
92
98
|
if (untouched.has(path)) continue;
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
99
|
+
const arrived = was.parts.get(path);
|
|
100
|
+
const submitted = now.parts.get(path);
|
|
101
|
+
if (arrived === void 0 || submitted === void 0 || !sameBytes(arrived, submitted)) {
|
|
96
102
|
return { ok: false, reason: "part-changed", part: path };
|
|
97
103
|
}
|
|
98
104
|
}
|
|
99
105
|
if (!relationshipsKept(
|
|
100
|
-
readRelationships(
|
|
101
|
-
readRelationships(
|
|
106
|
+
readRelationships(was.parts, relsPath),
|
|
107
|
+
readRelationships(now.parts, relsPath),
|
|
102
108
|
policy.parts.map((kind) => kind.relType)
|
|
103
|
-
) || !gainedPartsAreNew(policy,
|
|
109
|
+
) || !gainedPartsAreNew(policy, was, now)) {
|
|
104
110
|
return { ok: false, reason: "relationship-changed", part: relsPath };
|
|
105
111
|
}
|
|
106
112
|
if (!contentTypesKept(
|
|
107
|
-
contentTypes(
|
|
108
|
-
contentTypes(
|
|
113
|
+
contentTypes(was.parts.get(CONTENT_TYPES_PATH)),
|
|
114
|
+
contentTypes(now.parts.get(CONTENT_TYPES_PATH)),
|
|
109
115
|
policy.parts.map((kind) => kind.contentType)
|
|
110
116
|
)) {
|
|
111
117
|
return { ok: false, reason: "part-changed", part: CONTENT_TYPES_PATH };
|
|
112
118
|
}
|
|
113
119
|
if (aroundTheStory(before) !== aroundTheStory(after)) {
|
|
114
|
-
return { ok: false, reason: "part-changed", part:
|
|
120
|
+
return { ok: false, reason: "part-changed", part: was.mainPartPath };
|
|
115
121
|
}
|
|
116
122
|
return { ok: true };
|
|
117
123
|
}
|
|
@@ -120,18 +126,22 @@ function partKept(kind, before, after, authorId, options) {
|
|
|
120
126
|
const submitted = kind.entriesIn(after.session, "submitted");
|
|
121
127
|
if (submitted === null) return false;
|
|
122
128
|
if (!kind.rootKept(before.session, after.session)) return false;
|
|
129
|
+
const unattributed = unattributedCommentAuthors(
|
|
130
|
+
before.session.comments.ordered
|
|
131
|
+
);
|
|
123
132
|
const stoodBehindNow = kind.referents(after);
|
|
124
133
|
for (const [id, entry] of submitted) {
|
|
125
134
|
const original = arrived.get(id);
|
|
126
135
|
if (original && original.xml === entry.xml) continue;
|
|
127
136
|
if (!stoodBehindNow.has(id)) return false;
|
|
128
137
|
if (original && kind.anyonesChange(entry.el, original.el)) continue;
|
|
129
|
-
if (!kind.wellFormed(entry.el) || !kind.allowed(
|
|
138
|
+
if (!kind.wellFormed(entry.el, original?.el ?? null) || !kind.allowed(
|
|
130
139
|
entry.el,
|
|
131
140
|
original?.el ?? null,
|
|
132
141
|
authorId,
|
|
133
142
|
options,
|
|
134
|
-
after.session
|
|
143
|
+
after.session,
|
|
144
|
+
unattributed
|
|
135
145
|
)) {
|
|
136
146
|
return false;
|
|
137
147
|
}
|
|
@@ -155,7 +165,7 @@ function verifyChange(policy, original, submitted, authorId, { xmlParser, ...opt
|
|
|
155
165
|
return withXmlParser(xmlParser, () => {
|
|
156
166
|
const before = importDocx(original);
|
|
157
167
|
const after = importDocx(submitted);
|
|
158
|
-
const packaged = packageKept(policy, before
|
|
168
|
+
const packaged = packageKept(policy, before, after);
|
|
159
169
|
if (!packaged.ok) return packaged;
|
|
160
170
|
const story = policy.storyKept(before, after, authorId, options);
|
|
161
171
|
return story.ok ? partsKept(policy, before, after, authorId, options) : story;
|
package/dist/docx/scan.d.ts
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Finds the span each individual
|
|
2
|
+
* Finds the span each individual block occupies in the raw text of a part and slices it out.
|
|
3
3
|
*
|
|
4
4
|
* A block that was not edited is exported by writing this fragment back out untouched,
|
|
5
|
-
* which keeps its original bytes intact.
|
|
5
|
+
* which keeps its original bytes intact. The body of the main part is one such run of blocks;
|
|
6
|
+
* a comment and a footnote hold another (`docx/story`), and all of them are sliced here so that
|
|
7
|
+
* one story cannot end up compared or written by rules another does not follow.
|
|
6
8
|
*/
|
|
7
|
-
|
|
9
|
+
import { type Tag } from "../ooxml/tagScan";
|
|
10
|
+
export interface BlockSlice {
|
|
8
11
|
/** The tag name exactly as written (e.g. "w:p", "w:tbl", "w:sectPr") */
|
|
9
12
|
name: string;
|
|
10
13
|
/** The original XML fragment. It also carries the whitespace that sat between this block and the one before it */
|
|
11
14
|
xml: string;
|
|
12
15
|
}
|
|
13
|
-
export interface
|
|
14
|
-
/** Everything in the raw text before the first block */
|
|
16
|
+
export interface BlockScan {
|
|
17
|
+
/** Everything in the raw text before the first block, the container's own opening tag included */
|
|
15
18
|
prefix: string;
|
|
16
|
-
blocks:
|
|
17
|
-
/** Everything in the raw text after the last block */
|
|
19
|
+
blocks: BlockSlice[];
|
|
20
|
+
/** Everything in the raw text after the last block, the container's closing tag included */
|
|
18
21
|
suffix: string;
|
|
19
22
|
}
|
|
20
|
-
|
|
23
|
+
/** The tag whose children are the blocks, asked of every opening tag with the depth it stands at */
|
|
24
|
+
export type ContainerTest = (tag: Tag, depth: number) => boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The blocks the first container this test names holds, and the text on either side of them.
|
|
27
|
+
* null for a source holding no such container; a caller says in its own words what that means.
|
|
28
|
+
*/
|
|
29
|
+
export declare function scanBlocksIn(source: string, isContainer: ContainerTest): BlockScan | null;
|
|
30
|
+
export declare function scanBody(source: string): BlockScan;
|