@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
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
// src/docx/comments/grammar.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
elementXml,
|
|
5
|
-
xmlnsAttr
|
|
6
|
-
} from "../../ooxml/element.js";
|
|
7
|
-
import { NAMESPACES, qualify, wName } from "../../ooxml/names.js";
|
|
2
|
+
import { attrsText, elementXml } from "../../ooxml/element.js";
|
|
3
|
+
import { NAMESPACES, qualify } from "../../ooxml/names.js";
|
|
8
4
|
import {
|
|
9
5
|
attributeByLocalName,
|
|
10
6
|
elementChildren,
|
|
11
|
-
escapeXml,
|
|
12
7
|
parseXml,
|
|
13
8
|
W_NS
|
|
14
9
|
} from "../../ooxml/xml.js";
|
|
@@ -16,7 +11,6 @@ import { COMMENT_AUTHOR_PROVIDER, W14_NS, W15_NS } from "./constants.js";
|
|
|
16
11
|
var ELEMENT_NODE = 1;
|
|
17
12
|
var TEXT_NODE = 3;
|
|
18
13
|
var XMLNS_NS = "http://www.w3.org/2000/xmlns/";
|
|
19
|
-
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
20
14
|
var nameKey = (namespace, localName) => `${namespace ?? ""} ${localName}`;
|
|
21
15
|
var COMMENT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
22
16
|
nameKey(W_NS, "id"),
|
|
@@ -29,12 +23,6 @@ var COMMENT_EX_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
29
23
|
nameKey(W15_NS, "paraIdParent"),
|
|
30
24
|
nameKey(W15_NS, "done")
|
|
31
25
|
]);
|
|
32
|
-
var PARAGRAPH_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
33
|
-
nameKey(W14_NS, "paraId")
|
|
34
|
-
]);
|
|
35
|
-
var TEXT_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
36
|
-
nameKey(XML_NS, "space")
|
|
37
|
-
]);
|
|
38
26
|
var PERSON_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
39
27
|
nameKey(W15_NS, "author")
|
|
40
28
|
]);
|
|
@@ -42,7 +30,6 @@ var PRESENCE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
42
30
|
nameKey(W15_NS, "providerId"),
|
|
43
31
|
nameKey(W15_NS, "userId")
|
|
44
32
|
]);
|
|
45
|
-
var NO_ATTRIBUTES = /* @__PURE__ */ new Set();
|
|
46
33
|
var THREAD_KEY = /^[0-9A-Fa-f]{8}$/;
|
|
47
34
|
function threadKeyOrNone(el, localName) {
|
|
48
35
|
const value = attributeByLocalName(el, localName);
|
|
@@ -57,6 +44,11 @@ function declarationWritten(attr) {
|
|
|
57
44
|
if (!attr.name.startsWith("xmlns:")) return false;
|
|
58
45
|
return WRITTEN_NAMESPACES.get(attr.name.slice("xmlns:".length)) === attr.value;
|
|
59
46
|
}
|
|
47
|
+
function declarationsWritten(el) {
|
|
48
|
+
return Array.from(el.attributes).every(
|
|
49
|
+
(attr) => !attr.name.startsWith("xmlns") || declarationWritten(attr)
|
|
50
|
+
) && Array.from(el.children).every(declarationsWritten);
|
|
51
|
+
}
|
|
60
52
|
function attributesWithin(el, allowed) {
|
|
61
53
|
return Array.from(el.attributes).every(
|
|
62
54
|
(attr) => attr.namespaceURI === XMLNS_NS ? declarationWritten(attr) : allowed.has(nameKey(attr.namespaceURI, attr.localName))
|
|
@@ -70,22 +62,13 @@ function holdsElementsOnly(el) {
|
|
|
70
62
|
(node) => node.nodeType === ELEMENT_NODE
|
|
71
63
|
);
|
|
72
64
|
}
|
|
73
|
-
function
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (index > 0) pieces.push(elementXml(wName("br"), []));
|
|
81
|
-
if (line.length > 0 || lines.length === 1) {
|
|
82
|
-
pieces.push(
|
|
83
|
-
elementXml(wName("t"), [["xml:space", "preserve"]], [escapeXml(line)])
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
const key = paraId === null ? [] : [xmlnsAttr("w14"), [qualify("w14", "paraId"), paraId]];
|
|
88
|
-
return elementXml(wName("p"), key, [elementXml(wName("r"), [], pieces)]);
|
|
65
|
+
function isLayoutText(node) {
|
|
66
|
+
return node.nodeType === TEXT_NODE && /^[ \t\r\n]*$/.test(node.nodeValue ?? "");
|
|
67
|
+
}
|
|
68
|
+
function holdsElementsAndLayout(el) {
|
|
69
|
+
return Array.from(el.childNodes).every(
|
|
70
|
+
(node) => node.nodeType === ELEMENT_NODE || isLayoutText(node)
|
|
71
|
+
);
|
|
89
72
|
}
|
|
90
73
|
var PARAGRAPH_OR_SKIPPED = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<([^\s<>/:="']+:)?p(?=[\s/>])[^>]*>/g;
|
|
91
74
|
function lastBodyParagraph(comment) {
|
|
@@ -132,25 +115,6 @@ function withThreadKey(commentXml, paraId, arrived) {
|
|
|
132
115
|
const keyed = opening[0].slice(0, selfClosing ? -2 : -1) + ` ${attrsText([[qualify("w14", "paraId"), paraId]])}` + (selfClosing ? "/>" : ">");
|
|
133
116
|
return commentXml.slice(0, opening.index) + keyed + commentXml.slice(opening.index + opening[0].length);
|
|
134
117
|
}
|
|
135
|
-
function readRunText(run) {
|
|
136
|
-
if (!holdsElementsOnly(run)) return null;
|
|
137
|
-
const pieces = [];
|
|
138
|
-
for (const child of Array.from(run.children)) {
|
|
139
|
-
if (isNamed(child, W_NS, "br")) {
|
|
140
|
-
if (!attributesWithin(child, NO_ATTRIBUTES) || child.childNodes.length > 0) {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
pieces.push("\n");
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (!isNamed(child, W_NS, "t")) return null;
|
|
147
|
-
if (!attributesWithin(child, TEXT_ATTRIBUTES) || child.getAttributeNS(XML_NS, "space") !== "preserve" || !holdsTextOnly(child)) {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
pieces.push(child.textContent ?? "");
|
|
151
|
-
}
|
|
152
|
-
return pieces.join("");
|
|
153
|
-
}
|
|
154
118
|
function wellFormedCommentExtension(entry) {
|
|
155
119
|
return attributesWithin(entry, COMMENT_EX_ATTRIBUTES) && entry.childNodes.length === 0 && threadKeyOrNone(entry, "paraId") && threadKeyOrNone(entry, "paraIdParent") && // The writer says a thread is settled or open, and nothing else `ST_OnOff` would take
|
|
156
120
|
["0", "1", null].includes(attributeByLocalName(entry, "done"));
|
|
@@ -167,21 +131,6 @@ function recordedIdentity(person) {
|
|
|
167
131
|
const [presence] = Array.from(person.children);
|
|
168
132
|
return presence === void 0 ? null : attributeByLocalName(presence, "userId");
|
|
169
133
|
}
|
|
170
|
-
function readStrictCommentBody(comment) {
|
|
171
|
-
if (!holdsElementsOnly(comment)) return null;
|
|
172
|
-
const paragraphs = Array.from(comment.children);
|
|
173
|
-
if (paragraphs.length !== 1) return null;
|
|
174
|
-
const [paragraph] = paragraphs;
|
|
175
|
-
if (!isNamed(paragraph, W_NS, "p")) return null;
|
|
176
|
-
if (!attributesWithin(paragraph, PARAGRAPH_ATTRIBUTES)) return null;
|
|
177
|
-
if (!holdsElementsOnly(paragraph)) return null;
|
|
178
|
-
const runs = Array.from(paragraph.children);
|
|
179
|
-
if (runs.length !== 1) return null;
|
|
180
|
-
const [run] = runs;
|
|
181
|
-
if (!isNamed(run, W_NS, "r")) return null;
|
|
182
|
-
if (!attributesWithin(run, NO_ATTRIBUTES)) return null;
|
|
183
|
-
return readRunText(run);
|
|
184
|
-
}
|
|
185
134
|
function renderCommentExtension(comment) {
|
|
186
135
|
if (comment.extensionXml !== null) return comment.extensionXml;
|
|
187
136
|
const parent = "parentParaId" in comment ? [[qualify("w15", "paraIdParent"), comment.parentParaId]] : [];
|
|
@@ -211,10 +160,12 @@ export {
|
|
|
211
160
|
arrivedEntries,
|
|
212
161
|
attributesWithin,
|
|
213
162
|
declarationWritten,
|
|
163
|
+
declarationsWritten,
|
|
164
|
+
holdsElementsAndLayout,
|
|
165
|
+
holdsElementsOnly,
|
|
166
|
+
isLayoutText,
|
|
214
167
|
lastBodyParagraph,
|
|
215
|
-
readStrictCommentBody,
|
|
216
168
|
recordedIdentity,
|
|
217
|
-
renderCommentBody,
|
|
218
169
|
renderCommentExtension,
|
|
219
170
|
renderPerson,
|
|
220
171
|
wellFormedCommentExtension,
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Node as PMNode } from "prosemirror-model";
|
|
5
5
|
import type { ImportedComments } from "./reading";
|
|
6
|
+
/**
|
|
7
|
+
* A comment as the reference node in the story carries it: who wrote it, where its thread state
|
|
8
|
+
* hangs, and the replies under it. What it says is not here - a body is a story of its own, held
|
|
9
|
+
* on the document node under `comment:<id>` (`docx/story`).
|
|
10
|
+
*/
|
|
6
11
|
export interface CommentReferenceData {
|
|
7
12
|
id: string;
|
|
8
13
|
author: string | null;
|
|
9
14
|
authorId: string | null;
|
|
10
15
|
initials: string | null;
|
|
11
16
|
date: string | null;
|
|
12
|
-
text: string;
|
|
13
|
-
commentXml: string | null;
|
|
14
|
-
imported: boolean;
|
|
15
17
|
paraId: string;
|
|
16
18
|
resolved: boolean;
|
|
17
19
|
extensionXml: string | null;
|
|
@@ -24,9 +26,6 @@ export interface CommentReplyData {
|
|
|
24
26
|
authorId: string | null;
|
|
25
27
|
initials: string | null;
|
|
26
28
|
date: string | null;
|
|
27
|
-
text: string;
|
|
28
|
-
commentXml: string | null;
|
|
29
|
-
imported: boolean;
|
|
30
29
|
paraId: string;
|
|
31
30
|
parentParaId: string;
|
|
32
31
|
extensionXml: string | null;
|
|
@@ -26,9 +26,6 @@ function importedCommentReplies(comments, rootId) {
|
|
|
26
26
|
authorId: reply.authorId,
|
|
27
27
|
initials: reply.initials,
|
|
28
28
|
date: reply.date,
|
|
29
|
-
text: reply.text,
|
|
30
|
-
commentXml: reply.xml,
|
|
31
|
-
imported: true,
|
|
32
29
|
paraId,
|
|
33
30
|
parentParaId,
|
|
34
31
|
extensionXml: reply.extensionXml
|
|
@@ -59,9 +56,6 @@ function replyData(value) {
|
|
|
59
56
|
authorId: nullableString(entry.authorId),
|
|
60
57
|
initials: nullableString(entry.initials),
|
|
61
58
|
date: nullableString(entry.date),
|
|
62
|
-
text: nullableString(entry.text) ?? "",
|
|
63
|
-
commentXml: nullableString(entry.commentXml),
|
|
64
|
-
imported: entry.imported === true,
|
|
65
59
|
paraId,
|
|
66
60
|
parentParaId,
|
|
67
61
|
extensionXml: nullableString(entry.extensionXml)
|
|
@@ -79,9 +73,6 @@ function referenceData(node) {
|
|
|
79
73
|
authorId: nullableString(node.attrs.authorId),
|
|
80
74
|
initials: nullableString(node.attrs.initials),
|
|
81
75
|
date: nullableString(node.attrs.date),
|
|
82
|
-
text: nullableString(node.attrs.text) ?? "",
|
|
83
|
-
commentXml: nullableString(node.attrs.commentXml),
|
|
84
|
-
imported: node.attrs.imported === true,
|
|
85
76
|
paraId: nullableString(node.attrs.paraId) ?? "00000001",
|
|
86
77
|
resolved: node.attrs.resolved === true,
|
|
87
78
|
extensionXml: nullableString(node.attrs.extensionXml),
|
|
@@ -11,16 +11,17 @@
|
|
|
11
11
|
* have made the change. Asking them as one would refuse a moderator rewriting somebody else's
|
|
12
12
|
* comment under `editableComments: "all"`, which is a thing the editor lets them do.
|
|
13
13
|
*/
|
|
14
|
-
import type
|
|
14
|
+
import { type EditableComments } from "../../schema/protection";
|
|
15
15
|
import type { StoryPartKind } from "../protectionPolicy";
|
|
16
16
|
import { type ImportedPeople } from "./people";
|
|
17
17
|
/**
|
|
18
18
|
* Whether this editor's writer could have put the entry out, whoever it belongs to.
|
|
19
19
|
*
|
|
20
|
-
* Shape alone: which attributes it carries and what stands inside it
|
|
21
|
-
*
|
|
20
|
+
* Shape alone: which attributes it carries and what stands inside it, judged against the entry the
|
|
21
|
+
* original file held under the same id. Who may have written it is `entryAllowed`'s question.
|
|
22
|
+
* Each kind is read where it is written (`./grammar`).
|
|
22
23
|
*/
|
|
23
|
-
export declare function wellFormedEntry(entry: Element): boolean;
|
|
24
|
+
export declare function wellFormedEntry(entry: Element, original: Element | null): boolean;
|
|
24
25
|
/**
|
|
25
26
|
* Whether the only thing between the two is that a thread gained its key.
|
|
26
27
|
*
|
|
@@ -34,13 +35,18 @@ export declare function threadKeyAlone(entry: Element, original: Element): boole
|
|
|
34
35
|
* Whether `authorId` could have written or rewritten this entry.
|
|
35
36
|
*
|
|
36
37
|
* Permission alone: whether the shape is one this editor writes is `wellFormedEntry`'s question.
|
|
37
|
-
* An entry that appeared has to be this author
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* An entry that appeared has to be one this author could have written, which
|
|
39
|
+
* `commentAdditionAllowed` decides here as it does over the story. One that arrived keeps the
|
|
40
|
+
* identity it arrived with; settling its thread or replying to it belong to everyone and leave
|
|
41
|
+
* what it says alone, while rewriting what it says is its author's, a moderator's, or anyone's
|
|
42
|
+
* where no identity was recorded for it. Both are the rules the editor holds to
|
|
43
|
+
* (`schema/protection`), and the two have to answer alike or a file the editor wrote would be
|
|
44
|
+
* turned down here.
|
|
45
|
+
*
|
|
46
|
+
* `unattributed` is read from the file that arrived, since it says what the submission is judged
|
|
47
|
+
* against; `people` is the submission's, since it says who the submission names.
|
|
42
48
|
*/
|
|
43
|
-
export declare function entryAllowed(entry: Element, original: Element | null, authorId: string, editableComments: EditableComments, people: ImportedPeople): boolean;
|
|
49
|
+
export declare function entryAllowed(entry: Element, original: Element | null, authorId: string, editableComments: EditableComments, people: ImportedPeople, unattributed: ReadonlySet<string>): boolean;
|
|
44
50
|
export declare const commentsPart: StoryPartKind;
|
|
45
51
|
export declare const commentsExtendedPart: StoryPartKind;
|
|
46
52
|
export declare const peoplePart: StoryPartKind;
|
|
@@ -7,7 +7,16 @@ import {
|
|
|
7
7
|
serializeXml,
|
|
8
8
|
W_NS
|
|
9
9
|
} from "../../ooxml/xml.js";
|
|
10
|
+
import { isPreservedNode } from "../../schema/preservedFragments.js";
|
|
11
|
+
import {
|
|
12
|
+
commentAdditionAllowed
|
|
13
|
+
} from "../../schema/protection.js";
|
|
14
|
+
import { NO_FORMATTING } from "../formatting.js";
|
|
15
|
+
import { NO_IMPORT_SOURCES } from "../importParagraph.js";
|
|
10
16
|
import { availablePartPath } from "../packageParts.js";
|
|
17
|
+
import { serializeBlock } from "../serializeBlock.js";
|
|
18
|
+
import { buildBlock } from "../story.js";
|
|
19
|
+
import { isModelledBlock } from "../storyProjection.js";
|
|
11
20
|
import {
|
|
12
21
|
COMMENTS_CONTENT_TYPE,
|
|
13
22
|
COMMENTS_EXTENDED_CONTENT_TYPE,
|
|
@@ -21,9 +30,11 @@ import {
|
|
|
21
30
|
import {
|
|
22
31
|
attributesWithin,
|
|
23
32
|
COMMENT_ATTRIBUTES,
|
|
33
|
+
declarationsWritten,
|
|
24
34
|
declarationWritten,
|
|
35
|
+
holdsElementsAndLayout,
|
|
36
|
+
isLayoutText,
|
|
25
37
|
lastBodyParagraph,
|
|
26
|
-
readStrictCommentBody,
|
|
27
38
|
recordedIdentity,
|
|
28
39
|
wellFormedCommentExtension,
|
|
29
40
|
wellFormedPerson
|
|
@@ -42,9 +53,46 @@ function sameAttributes(entry, original, names) {
|
|
|
42
53
|
(name) => attributeByLocalName(entry, name) === attributeByLocalName(original, name)
|
|
43
54
|
);
|
|
44
55
|
}
|
|
45
|
-
function
|
|
56
|
+
function holdsPreserved(node) {
|
|
57
|
+
if (isPreservedNode(node)) return true;
|
|
58
|
+
let found = false;
|
|
59
|
+
node.descendants((child) => {
|
|
60
|
+
if (isPreservedNode(child)) found = true;
|
|
61
|
+
return !found;
|
|
62
|
+
});
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
var BLOCK_DECLARATIONS = Object.entries(NAMESPACES).map(([prefix, uri]) => `xmlns:${prefix}="${uri}"`).join(" ");
|
|
66
|
+
function normalizedBlock(xml) {
|
|
67
|
+
try {
|
|
68
|
+
const root = parseXml(
|
|
69
|
+
`<b ${BLOCK_DECLARATIONS}>${xml}</b>`
|
|
70
|
+
).documentElement;
|
|
71
|
+
const [block] = Array.from(root.children);
|
|
72
|
+
return block === void 0 || root.children.length !== 1 ? null : serializeXml(block);
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function writtenBlock(el) {
|
|
78
|
+
if (!declarationsWritten(el)) return false;
|
|
79
|
+
const node = buildBlock(el, "", NO_IMPORT_SOURCES, NO_FORMATTING);
|
|
80
|
+
if (!isModelledBlock(node) || holdsPreserved(node)) return false;
|
|
81
|
+
const written = normalizedBlock(serializeBlock(node));
|
|
82
|
+
return written !== null && written === normalizedBlock(serializeXml(el));
|
|
83
|
+
}
|
|
84
|
+
function wellFormedStoryBody(entry, original) {
|
|
85
|
+
if (!holdsElementsAndLayout(entry)) return false;
|
|
86
|
+
const kept = new Set(
|
|
87
|
+
original === null ? [] : Array.from(original.children, serializeXml)
|
|
88
|
+
);
|
|
89
|
+
return Array.from(entry.children).every(
|
|
90
|
+
(block) => kept.has(serializeXml(block)) || writtenBlock(block)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
function wellFormedEntry(entry, original) {
|
|
46
94
|
if (entry.namespaceURI === W_NS && entry.localName === "comment") {
|
|
47
|
-
return attributesWithin(entry, COMMENT_ATTRIBUTES) &&
|
|
95
|
+
return attributesWithin(entry, COMMENT_ATTRIBUTES) && wellFormedStoryBody(entry, original);
|
|
48
96
|
}
|
|
49
97
|
if (entry.namespaceURI === W15_NS && entry.localName === "commentEx") {
|
|
50
98
|
return wellFormedCommentExtension(entry);
|
|
@@ -67,11 +115,17 @@ function threadKeyAlone(entry, original) {
|
|
|
67
115
|
const kept = before === null || after === before;
|
|
68
116
|
return kept && withoutThreadKey(entry) === withoutThreadKey(original);
|
|
69
117
|
}
|
|
70
|
-
function entryAllowed(entry, original, authorId, editableComments, people) {
|
|
118
|
+
function entryAllowed(entry, original, authorId, editableComments, people, unattributed) {
|
|
71
119
|
if (entry.namespaceURI === W_NS && entry.localName === "comment") {
|
|
72
120
|
const author = attributeByLocalName(entry, "author");
|
|
73
121
|
const recorded = author === null ? null : commentAuthorId(people, author);
|
|
74
|
-
if (original === null)
|
|
122
|
+
if (original === null) {
|
|
123
|
+
return commentAdditionAllowed(
|
|
124
|
+
{ author, authorId: recorded },
|
|
125
|
+
authorId,
|
|
126
|
+
unattributed
|
|
127
|
+
);
|
|
128
|
+
}
|
|
75
129
|
const paraId = lastParagraphId(original);
|
|
76
130
|
if (!sameAttributes(entry, original, COMMENT_IDENTITY) || paraId !== null && lastParagraphId(entry) !== paraId) {
|
|
77
131
|
return false;
|
|
@@ -83,23 +137,20 @@ function entryAllowed(entry, original, authorId, editableComments, people) {
|
|
|
83
137
|
return sameAttributes(entry, original, ["paraId", "paraIdParent"]);
|
|
84
138
|
}
|
|
85
139
|
if (entry.namespaceURI === W15_NS && entry.localName === "person") {
|
|
86
|
-
|
|
140
|
+
const author = attributeByLocalName(entry, "author");
|
|
141
|
+
return original === null && recordedIdentity(entry) === authorId && author !== null && !unattributed.has(author);
|
|
87
142
|
}
|
|
88
143
|
return false;
|
|
89
144
|
}
|
|
90
|
-
var TEXT_NODE = 3;
|
|
91
145
|
var COMPATIBILITY = /* @__PURE__ */ new Map([
|
|
92
146
|
["xmlns:w14", NAMESPACES.w14],
|
|
93
147
|
["xmlns:mc", NAMESPACES.mc]
|
|
94
148
|
]);
|
|
95
149
|
var IGNORABLE = "mc:Ignorable";
|
|
96
150
|
var IGNORED = "w14";
|
|
97
|
-
function isLayout(node) {
|
|
98
|
-
return node.nodeType === TEXT_NODE && /^[ \t\r\n]*$/.test(node.nodeValue ?? "");
|
|
99
|
-
}
|
|
100
151
|
function surroundingNodes(parent) {
|
|
101
152
|
return parent === null ? [] : Array.from(parent.childNodes).filter(
|
|
102
|
-
(node) => !isElement(node) && !
|
|
153
|
+
(node) => !isElement(node) && !isLayoutText(node)
|
|
103
154
|
);
|
|
104
155
|
}
|
|
105
156
|
function sameNodes(now, was) {
|
|
@@ -192,12 +243,13 @@ function storyPart(shape) {
|
|
|
192
243
|
referents: shape.referents,
|
|
193
244
|
wellFormed: wellFormedEntry,
|
|
194
245
|
anyonesChange: threadKeyAlone,
|
|
195
|
-
allowed: (entry, original, authorId, options, session) => entryAllowed(
|
|
246
|
+
allowed: (entry, original, authorId, options, session, unattributed) => entryAllowed(
|
|
196
247
|
entry,
|
|
197
248
|
original,
|
|
198
249
|
authorId,
|
|
199
250
|
options.editableComments,
|
|
200
|
-
session.comments.people
|
|
251
|
+
session.comments.people,
|
|
252
|
+
unattributed
|
|
201
253
|
)
|
|
202
254
|
};
|
|
203
255
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* part by, so a name stands for one identity per file: a name the part already records is read as
|
|
9
9
|
* it stands and never appended to. Appending a second person for it would leave a file naming two
|
|
10
10
|
* identities for one name, where a reader keying by name hands one author's comments to the other.
|
|
11
|
+
* A name the file's comments already write under carries the same weight, recorded or not: a
|
|
12
|
+
* person appended for it would claim those comments too.
|
|
11
13
|
*/
|
|
12
14
|
import type { PartPlanContext } from "../partPlan";
|
|
13
15
|
import type { StoryPartKind } from "../protectionPolicy";
|
|
@@ -33,10 +35,16 @@ export declare function readPeople(parts: Map<string, Uint8Array>, mainPartPath:
|
|
|
33
35
|
*/
|
|
34
36
|
export declare function commentAuthorId(people: ImportedPeople, author: string): string | null;
|
|
35
37
|
/**
|
|
36
|
-
* The authors
|
|
37
|
-
*
|
|
38
|
+
* The authors to record a person for, each under the name they write as: the ones the comments
|
|
39
|
+
* name one identity for and the part does not record already. A name the part records is left as
|
|
40
|
+
* it stands, whichever provider recorded it and whatever it resolves to. So is a name the comments
|
|
41
|
+
* carry under more than one identity, one identity and none among them: recording it would hand
|
|
42
|
+
* the comments carrying no identity to whoever the name then resolved to, and a document saved by
|
|
43
|
+
* a Word that wrote no people part carries every one of its comments that way.
|
|
44
|
+
* `unattributed` also reserves names from the original Comments part, including preserved entries
|
|
45
|
+
* the current editable bodies do not carry. The planner and export invariants use this same set.
|
|
38
46
|
*/
|
|
39
|
-
export declare function unrecordedAuthors(bodies: Iterable<CommentReferenceData | CommentReplyData>, people: ImportedPeople): Map<string, string>;
|
|
47
|
+
export declare function unrecordedAuthors(bodies: Iterable<CommentReferenceData | CommentReplyData>, people: ImportedPeople, unattributed: ReadonlySet<string>): Map<string, string>;
|
|
40
48
|
/**
|
|
41
49
|
* Plans the people part, its relationship and its content type for every identity the current
|
|
42
50
|
* comments carry that the document has not recorded. Null when it has recorded them all, which
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
encodeUtf8,
|
|
11
11
|
parseXml
|
|
12
12
|
} from "../../ooxml/xml.js";
|
|
13
|
+
import { unattributedCommentAuthors } from "../../schema/protection.js";
|
|
13
14
|
import { relatedPartPath } from "../packageParts.js";
|
|
14
15
|
import { directoryOf } from "../relationships.js";
|
|
15
16
|
import { COMMENT_AUTHOR_PROVIDER, PEOPLE_REL_TYPE, W15_NS } from "./constants.js";
|
|
@@ -51,12 +52,21 @@ function readPeople(parts, mainPartPath) {
|
|
|
51
52
|
function commentAuthorId(people, author) {
|
|
52
53
|
return people.byAuthor.get(author) ?? null;
|
|
53
54
|
}
|
|
54
|
-
function unrecordedAuthors(bodies, people) {
|
|
55
|
-
const
|
|
55
|
+
function unrecordedAuthors(bodies, people, unattributed) {
|
|
56
|
+
const identities = /* @__PURE__ */ new Map();
|
|
56
57
|
for (const body of bodies) {
|
|
57
|
-
if (body.author === null
|
|
58
|
-
if (people.byAuthor.has(body.author))
|
|
59
|
-
|
|
58
|
+
if (body.author === null) continue;
|
|
59
|
+
if (people.byAuthor.has(body.author) || unattributed.has(body.author))
|
|
60
|
+
continue;
|
|
61
|
+
const ids = identities.get(body.author) ?? /* @__PURE__ */ new Set();
|
|
62
|
+
ids.add(body.authorId);
|
|
63
|
+
identities.set(body.author, ids);
|
|
64
|
+
}
|
|
65
|
+
const unrecorded = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const [author, ids] of identities) {
|
|
67
|
+
if (ids.size !== 1) continue;
|
|
68
|
+
const only = Array.from(ids)[0] ?? null;
|
|
69
|
+
if (only !== null) unrecorded.set(author, only);
|
|
60
70
|
}
|
|
61
71
|
return unrecorded;
|
|
62
72
|
}
|
|
@@ -94,7 +104,11 @@ function peopleXml(people, added) {
|
|
|
94
104
|
}
|
|
95
105
|
function planPeoplePart(part, bodies, session, context) {
|
|
96
106
|
const people = session.comments.people;
|
|
97
|
-
const added = unrecordedAuthors(
|
|
107
|
+
const added = unrecordedAuthors(
|
|
108
|
+
bodies,
|
|
109
|
+
people,
|
|
110
|
+
unattributedCommentAuthors(session.comments.ordered)
|
|
111
|
+
);
|
|
98
112
|
if (added.size === 0) return null;
|
|
99
113
|
const parts = /* @__PURE__ */ new Map();
|
|
100
114
|
const addingPart = part.pathIn(session) === null;
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
commentAdditionsBy,
|
|
4
4
|
commentEditsOwned,
|
|
5
5
|
commentIdentitiesKept,
|
|
6
|
+
unattributedCommentAuthors,
|
|
6
7
|
withoutComments
|
|
7
8
|
} from "../../schema/protection.js";
|
|
8
9
|
import {
|
|
@@ -18,7 +19,12 @@ function sameBody(before, after) {
|
|
|
18
19
|
}
|
|
19
20
|
function commentsStoryKept(before, after, authorId, { editableComments }) {
|
|
20
21
|
if (!sameBody(before, after)) return refused("body-changed");
|
|
21
|
-
if (!commentIdentitiesKept(before.doc, after.doc) || !commentAdditionsBy(
|
|
22
|
+
if (!commentIdentitiesKept(before.doc, after.doc) || !commentAdditionsBy(
|
|
23
|
+
before.doc,
|
|
24
|
+
after.doc,
|
|
25
|
+
authorId,
|
|
26
|
+
unattributedCommentAuthors(before.session.comments.ordered)
|
|
27
|
+
)) {
|
|
22
28
|
return refused("comment-author-forged");
|
|
23
29
|
}
|
|
24
30
|
if (!commentEditsOwned(before.doc, after.doc, {
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Reads comment and comment-extension package parts.
|
|
3
3
|
*/
|
|
4
4
|
import { type ImportedPeople } from "./people";
|
|
5
|
+
/**
|
|
6
|
+
* One entry of the Comments part as it arrived, everything about it but what it says: that is a
|
|
7
|
+
* story of its own, sliced and read the way a body block is (`docx/story`).
|
|
8
|
+
*/
|
|
5
9
|
export interface ImportedComment {
|
|
6
10
|
id: string;
|
|
7
11
|
author: string | null;
|
|
@@ -13,8 +17,6 @@ export interface ImportedComment {
|
|
|
13
17
|
authorId: string | null;
|
|
14
18
|
initials: string | null;
|
|
15
19
|
date: string | null;
|
|
16
|
-
text: string;
|
|
17
|
-
xml: string;
|
|
18
20
|
paraId: string | null;
|
|
19
21
|
parentParaId: string | null;
|
|
20
22
|
resolved: boolean;
|
|
@@ -4,8 +4,7 @@ import {
|
|
|
4
4
|
decodeUtf8,
|
|
5
5
|
elementChildren,
|
|
6
6
|
parseXml,
|
|
7
|
-
serializeXml
|
|
8
|
-
W_NS
|
|
7
|
+
serializeXml
|
|
9
8
|
} from "../../ooxml/xml.js";
|
|
10
9
|
import { relatedPartPath } from "../packageParts.js";
|
|
11
10
|
import { COMMENTS_EXTENDED_REL_TYPE, COMMENTS_REL_TYPE } from "./constants.js";
|
|
@@ -15,16 +14,6 @@ import {
|
|
|
15
14
|
NO_PEOPLE,
|
|
16
15
|
readPeople
|
|
17
16
|
} from "./people.js";
|
|
18
|
-
function inlineCommentText(node) {
|
|
19
|
-
if (node.localName === "t") return node.textContent ?? "";
|
|
20
|
-
if (node.localName === "tab") return " ";
|
|
21
|
-
if (node.localName === "br" || node.localName === "cr") return "\n";
|
|
22
|
-
return elementChildren(node).map(inlineCommentText).join("");
|
|
23
|
-
}
|
|
24
|
-
function commentText(el) {
|
|
25
|
-
const paragraphs = Array.from(el.getElementsByTagNameNS(W_NS, "p"));
|
|
26
|
-
return paragraphs.map(inlineCommentText).join("\n");
|
|
27
|
-
}
|
|
28
17
|
var NO_COMMENTS = {
|
|
29
18
|
partPath: null,
|
|
30
19
|
xml: null,
|
|
@@ -117,8 +106,6 @@ function readComments(parts, mainPartPath) {
|
|
|
117
106
|
authorId: author === null ? null : commentAuthorId(people, author),
|
|
118
107
|
initials: attributeByLocalName(el, "initials"),
|
|
119
108
|
date: attributeByLocalName(el, "date"),
|
|
120
|
-
text: commentText(el),
|
|
121
|
-
xml: serializeXml(el),
|
|
122
109
|
paraId,
|
|
123
110
|
parentParaId: extension?.parentParaId ?? null,
|
|
124
111
|
resolved: extension?.resolved ?? false,
|