@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
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// src/editor/clipboard/htmlReader.ts
|
|
2
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
3
|
+
import {
|
|
4
|
+
paragraphAttrsFor,
|
|
5
|
+
styleIdOf
|
|
6
|
+
} from "../../docx/formatting.js";
|
|
7
|
+
import {
|
|
8
|
+
withListNumbering,
|
|
9
|
+
withParagraphStyle
|
|
10
|
+
} from "../../docx/paraProps.js";
|
|
11
|
+
import {
|
|
12
|
+
listsWorn,
|
|
13
|
+
NEW_LISTS_ATTR,
|
|
14
|
+
newListsValue
|
|
15
|
+
} from "../../numbering/listRegistry.js";
|
|
16
|
+
import {
|
|
17
|
+
allocateList,
|
|
18
|
+
MAX_ILVL,
|
|
19
|
+
templateList
|
|
20
|
+
} from "../../numbering/listTemplate.js";
|
|
21
|
+
import { docxSchema } from "../../schema/index.js";
|
|
22
|
+
import { editorClassNames } from "../../styles/classNames.js";
|
|
23
|
+
import { numIdsIn } from "../commands/listCommands.js";
|
|
24
|
+
import { PASTED_IMAGE_ATTRIBUTE } from "./images.js";
|
|
25
|
+
import {
|
|
26
|
+
appendInline,
|
|
27
|
+
contextFor,
|
|
28
|
+
marksFor,
|
|
29
|
+
withInlineStyle
|
|
30
|
+
} from "./inlineFormatting.js";
|
|
31
|
+
var BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
32
|
+
"ADDRESS",
|
|
33
|
+
"ARTICLE",
|
|
34
|
+
"ASIDE",
|
|
35
|
+
"BLOCKQUOTE",
|
|
36
|
+
"DIV",
|
|
37
|
+
"H1",
|
|
38
|
+
"H2",
|
|
39
|
+
"H3",
|
|
40
|
+
"H4",
|
|
41
|
+
"H5",
|
|
42
|
+
"H6",
|
|
43
|
+
"LI",
|
|
44
|
+
"P",
|
|
45
|
+
"PRE",
|
|
46
|
+
"SECTION"
|
|
47
|
+
]);
|
|
48
|
+
var HEADING_LEVELS = {
|
|
49
|
+
H1: 1,
|
|
50
|
+
H2: 2,
|
|
51
|
+
H3: 3,
|
|
52
|
+
H4: 4,
|
|
53
|
+
H5: 5,
|
|
54
|
+
H6: 6
|
|
55
|
+
};
|
|
56
|
+
var HEADING_FALLBACKS = {
|
|
57
|
+
1: { bold: true, fontSizePt: 24 },
|
|
58
|
+
2: { bold: true, fontSizePt: 18 },
|
|
59
|
+
3: { bold: true, fontSizePt: 14 },
|
|
60
|
+
4: { bold: true, fontSizePt: 12 },
|
|
61
|
+
5: { bold: true, fontSizePt: 10 },
|
|
62
|
+
6: { bold: true, fontSizePt: 8 }
|
|
63
|
+
};
|
|
64
|
+
var COPIED_STYLE_ATTRIBUTE = "data-style";
|
|
65
|
+
function normalizedStyleName(value) {
|
|
66
|
+
return value.toLowerCase().replace(/[\s_-]+/g, "");
|
|
67
|
+
}
|
|
68
|
+
function copiedStyleId(element) {
|
|
69
|
+
if (!element.classList.contains(editorClassNames.paragraph)) return null;
|
|
70
|
+
return element.getAttribute(COPIED_STYLE_ATTRIBUTE) ?? styleIdOf(element.getAttribute("data-ppr"));
|
|
71
|
+
}
|
|
72
|
+
function headingLevel(element, sourceStyleId) {
|
|
73
|
+
const direct = HEADING_LEVELS[element.tagName];
|
|
74
|
+
if (direct !== void 0) return direct;
|
|
75
|
+
const match = /^heading([1-6])$/.exec(
|
|
76
|
+
normalizedStyleName(sourceStyleId ?? "")
|
|
77
|
+
);
|
|
78
|
+
return match ? Number.parseInt(match[1] ?? "", 10) : null;
|
|
79
|
+
}
|
|
80
|
+
function destinationStyleId(styles, sourceStyleId, level) {
|
|
81
|
+
if (sourceStyleId && styles.some((style) => style.id === sourceStyleId)) {
|
|
82
|
+
return sourceStyleId;
|
|
83
|
+
}
|
|
84
|
+
if (level === null) return null;
|
|
85
|
+
const wanted = `heading${level}`;
|
|
86
|
+
return styles.find(
|
|
87
|
+
(style) => normalizedStyleName(style.id) === wanted || normalizedStyleName(style.name) === wanted
|
|
88
|
+
)?.id ?? null;
|
|
89
|
+
}
|
|
90
|
+
function blockContext(context, parent, element, preserveParagraphStyle) {
|
|
91
|
+
const editorParagraph = element.classList.contains(
|
|
92
|
+
editorClassNames.paragraph
|
|
93
|
+
);
|
|
94
|
+
const sourceStyleId = copiedStyleId(element);
|
|
95
|
+
const level = headingLevel(element, sourceStyleId);
|
|
96
|
+
if (!preserveParagraphStyle && editorParagraph) {
|
|
97
|
+
return {
|
|
98
|
+
inline: contextFor(parent, element),
|
|
99
|
+
paragraph: null
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const styleId = destinationStyleId(
|
|
103
|
+
context.paragraphStyles,
|
|
104
|
+
sourceStyleId,
|
|
105
|
+
level
|
|
106
|
+
);
|
|
107
|
+
const paragraph = styleId === null ? null : withParagraphStyle(null, styleId);
|
|
108
|
+
const usesDestinationStyle = editorParagraph && (sourceStyleId === null || paragraph !== null);
|
|
109
|
+
let inline = parent;
|
|
110
|
+
if (paragraph === null && level !== null) {
|
|
111
|
+
inline = withInlineStyle(inline, HEADING_FALLBACKS[level] ?? {});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
inline: contextFor(inline, element, !usesDestinationStyle),
|
|
115
|
+
paragraph
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function listParagraphProps(list) {
|
|
119
|
+
if (!list || list.numId === null) return null;
|
|
120
|
+
return withListNumbering(null, {
|
|
121
|
+
numbering: { numId: list.numId, ilvl: Math.min(MAX_ILVL, list.level) },
|
|
122
|
+
indent: { kind: "keep" }
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function paragraphAttrs(formatting, list, paragraph) {
|
|
126
|
+
const props = listParagraphProps(list) ?? paragraph;
|
|
127
|
+
return props ? { pPr: props.pPr, ...paragraphAttrsFor(props.pPr, formatting) } : null;
|
|
128
|
+
}
|
|
129
|
+
var HtmlReader = class {
|
|
130
|
+
constructor(context, preserveParagraphStyles) {
|
|
131
|
+
this.context = context;
|
|
132
|
+
this.preserveParagraphStyles = preserveParagraphStyles;
|
|
133
|
+
this.formatting = context.formatting;
|
|
134
|
+
this.used = new Set(context.numbering.used);
|
|
135
|
+
this.canCreateLists = context.numbering.canCreate;
|
|
136
|
+
}
|
|
137
|
+
context;
|
|
138
|
+
preserveParagraphStyles;
|
|
139
|
+
blocks = [];
|
|
140
|
+
used;
|
|
141
|
+
canCreateLists;
|
|
142
|
+
/**
|
|
143
|
+
* What the pasted paragraphs are resolved against: the document's context, holding the
|
|
144
|
+
* definition of each pasted list as it is read, so that an item is drawn against the list it
|
|
145
|
+
* is joining rather than against a list nothing yet defines.
|
|
146
|
+
*/
|
|
147
|
+
formatting;
|
|
148
|
+
/** The definitions of every list started while editing, the pasted ones among them */
|
|
149
|
+
get newLists() {
|
|
150
|
+
return this.formatting.numbering.added;
|
|
151
|
+
}
|
|
152
|
+
read(root) {
|
|
153
|
+
this.readFlow(root, { style: {}, href: null, preserveWhitespace: false });
|
|
154
|
+
return this.blocks;
|
|
155
|
+
}
|
|
156
|
+
appendInline(target, node, context) {
|
|
157
|
+
appendInline(target, node, context, (content, element, inline) => {
|
|
158
|
+
if (element.tagName !== "IMG") return false;
|
|
159
|
+
const token = element.getAttribute(PASTED_IMAGE_ATTRIBUTE);
|
|
160
|
+
const image = token === null ? void 0 : this.context.images.get(token);
|
|
161
|
+
if (!image) return true;
|
|
162
|
+
content.push(
|
|
163
|
+
docxSchema.nodes.image.create(
|
|
164
|
+
{
|
|
165
|
+
...image,
|
|
166
|
+
// The source drawing may point at a relationship in another package.
|
|
167
|
+
xml: null
|
|
168
|
+
},
|
|
169
|
+
null,
|
|
170
|
+
marksFor(inline)
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
return true;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
addParagraph(content, list = null, paragraph = null) {
|
|
177
|
+
if (list && list.numId === null) {
|
|
178
|
+
const marker = list.kind === "bullet" ? "\u2022 " : "1. ";
|
|
179
|
+
content.unshift(docxSchema.text(marker));
|
|
180
|
+
}
|
|
181
|
+
this.blocks.push(
|
|
182
|
+
docxSchema.nodes.paragraph.create(
|
|
183
|
+
paragraphAttrs(this.formatting, list, paragraph),
|
|
184
|
+
content
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
readFlow(parent, context, paragraph = null) {
|
|
189
|
+
let inline = [];
|
|
190
|
+
const flush = () => {
|
|
191
|
+
if (inline.length === 0) return;
|
|
192
|
+
this.addParagraph(inline, null, paragraph);
|
|
193
|
+
inline = [];
|
|
194
|
+
};
|
|
195
|
+
for (const child of parent.childNodes) {
|
|
196
|
+
const element = child.nodeType === child.ELEMENT_NODE ? child : null;
|
|
197
|
+
if (element?.tagName === "UL" || element?.tagName === "OL") {
|
|
198
|
+
flush();
|
|
199
|
+
this.readList(element, context, 0, null);
|
|
200
|
+
} else if (element && BLOCK_TAGS.has(element.tagName)) {
|
|
201
|
+
flush();
|
|
202
|
+
const before = this.blocks.length;
|
|
203
|
+
const block = blockContext(
|
|
204
|
+
this.context,
|
|
205
|
+
context,
|
|
206
|
+
element,
|
|
207
|
+
this.preserveParagraphStyles
|
|
208
|
+
);
|
|
209
|
+
this.readFlow(element, block.inline, block.paragraph);
|
|
210
|
+
if (this.blocks.length === before) {
|
|
211
|
+
this.addParagraph([], null, block.paragraph);
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
this.appendInline(inline, child, context);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
flush();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The number a pasted list takes, registered with the definition that list is drawn and written
|
|
221
|
+
* with. A pasted list is started here just as the list button starts one.
|
|
222
|
+
*/
|
|
223
|
+
takeNumId(kind) {
|
|
224
|
+
if (!this.canCreateLists) return null;
|
|
225
|
+
const started = allocateList(
|
|
226
|
+
this.formatting.numbering,
|
|
227
|
+
this.used,
|
|
228
|
+
templateList(kind)
|
|
229
|
+
);
|
|
230
|
+
this.formatting = { ...this.formatting, numbering: started.numbering };
|
|
231
|
+
this.used.add(started.numId);
|
|
232
|
+
return started.numId;
|
|
233
|
+
}
|
|
234
|
+
readList(list, context, level, inherited) {
|
|
235
|
+
const kind = list.tagName === "UL" ? "bullet" : "numbered";
|
|
236
|
+
const listContext = {
|
|
237
|
+
kind,
|
|
238
|
+
numId: inherited?.kind === kind ? inherited.numId : this.takeNumId(kind),
|
|
239
|
+
level
|
|
240
|
+
};
|
|
241
|
+
const items = Array.from(list.children).filter(
|
|
242
|
+
(child) => child.tagName === "LI"
|
|
243
|
+
);
|
|
244
|
+
for (const item of items) {
|
|
245
|
+
const itemContext = contextFor(context, item);
|
|
246
|
+
const content = [];
|
|
247
|
+
for (const child of item.childNodes) {
|
|
248
|
+
const nested = child.nodeType === child.ELEMENT_NODE ? child : null;
|
|
249
|
+
if (nested?.tagName === "UL" || nested?.tagName === "OL") continue;
|
|
250
|
+
this.appendInline(content, child, itemContext);
|
|
251
|
+
}
|
|
252
|
+
this.addParagraph(content, listContext);
|
|
253
|
+
for (const nested of Array.from(item.children)) {
|
|
254
|
+
if (nested.tagName === "UL" || nested.tagName === "OL") {
|
|
255
|
+
this.readList(
|
|
256
|
+
nested,
|
|
257
|
+
itemContext,
|
|
258
|
+
level + 1,
|
|
259
|
+
listContext
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
function sliceDepth(root) {
|
|
267
|
+
const marker = root.querySelector("[data-pm-slice]");
|
|
268
|
+
const openStart = Number.parseInt(
|
|
269
|
+
marker?.getAttribute("data-pm-slice")?.split(/\s+/, 1)[0] ?? "",
|
|
270
|
+
10
|
|
271
|
+
);
|
|
272
|
+
if (Number.isFinite(openStart)) return openStart === 0 ? 0 : 1;
|
|
273
|
+
const blocks = [...BLOCK_TAGS, "OL", "UL"].map((tag) => tag.toLowerCase());
|
|
274
|
+
return root.querySelector(blocks.join(",")) ? 0 : 1;
|
|
275
|
+
}
|
|
276
|
+
function readHtml(root, context) {
|
|
277
|
+
const open = sliceDepth(root);
|
|
278
|
+
const reader = new HtmlReader(context, open === 0);
|
|
279
|
+
const blocks = reader.read(root);
|
|
280
|
+
return blocks.length === 0 ? null : {
|
|
281
|
+
slice: new Slice(Fragment.fromArray([...blocks]), open, open),
|
|
282
|
+
newLists: reader.newLists
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function readHtmlSlice(context, source) {
|
|
286
|
+
if (source.trim() === "") return null;
|
|
287
|
+
const template = context.document.createElement("template");
|
|
288
|
+
template.innerHTML = source;
|
|
289
|
+
return readHtml(template.content, context);
|
|
290
|
+
}
|
|
291
|
+
function withPastedContent(tr, content) {
|
|
292
|
+
return tr.setDocAttribute(
|
|
293
|
+
NEW_LISTS_ATTR,
|
|
294
|
+
newListsValue(listsWorn(content.newLists, numIdsIn(tr.doc)))
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
COPIED_STYLE_ATTRIBUTE,
|
|
299
|
+
readHtml,
|
|
300
|
+
readHtmlSlice,
|
|
301
|
+
withPastedContent
|
|
302
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The channel a copy made in this editor comes back through.
|
|
3
|
+
*
|
|
4
|
+
* What leaves for the clipboard is HTML, and HTML carries only what a reader can make out of it:
|
|
5
|
+
* a paragraph's own properties, a table's grid, the relationship a link hung off are none of them
|
|
6
|
+
* writable there without publishing the document's insides. So the slice itself stays here, under
|
|
7
|
+
* a name the copied HTML carries, and a paste that hands that name back is given what was copied.
|
|
8
|
+
*
|
|
9
|
+
* The name is trusted only alongside the session it was copied from: another open document -
|
|
10
|
+
* a second editor on the same page among them - numbers its lists and relates its images against
|
|
11
|
+
* its own file, so a slice of it means something else here, and such a paste falls to the reader
|
|
12
|
+
* that reads the markup instead. A token nothing kept, a token from a copy since replaced, and a
|
|
13
|
+
* token invented by whoever wrote the markup all miss the same way.
|
|
14
|
+
*
|
|
15
|
+
* The slice is kept alongside the kind of each list its paragraphs name, which is the one thing
|
|
16
|
+
* about the copy that only the editor it was copied from can answer: a number says nothing about
|
|
17
|
+
* whether it counted or bulleted, and the definition that said so is the source document's. It is
|
|
18
|
+
* read here because this is where both ends are the same session.
|
|
19
|
+
*/
|
|
20
|
+
import type { Slice } from "prosemirror-model";
|
|
21
|
+
import type { ListKind } from "../../numbering/listTemplate";
|
|
22
|
+
import type { Numbering } from "../../numbering/parseNumbering";
|
|
23
|
+
/** Where the copied HTML carries the name of the slice this editor kept */
|
|
24
|
+
export declare const INTERNAL_TOKEN_ATTRIBUTE = "data-docx-clip";
|
|
25
|
+
/** The kind of list each numbering id of a slice stood for where the slice was copied */
|
|
26
|
+
export type ListKinds = ReadonlyMap<number, ListKind>;
|
|
27
|
+
export declare const NO_LIST_KINDS: ListKinds;
|
|
28
|
+
/**
|
|
29
|
+
* The two ways a copy leaves the editor.
|
|
30
|
+
*
|
|
31
|
+
* A drag is serialized through the very same path a copy takes, so the one the drag wrote would
|
|
32
|
+
* replace the one the clipboard still holds, and a drag begun and let go would send the next paste
|
|
33
|
+
* of that clipboard to the reader that reads markup. They are kept apart instead, and each is
|
|
34
|
+
* replaced only by the next copy made the same way.
|
|
35
|
+
*/
|
|
36
|
+
export type CopyRoute = "clipboard" | "drag";
|
|
37
|
+
/** What one name stands for: the slice that was copied, and what its list numbers meant there */
|
|
38
|
+
export interface RecalledCopy {
|
|
39
|
+
readonly slice: Slice;
|
|
40
|
+
readonly listKinds: ListKinds;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Keeps the copied slice under a fresh name, and answers with the name to write into the markup.
|
|
44
|
+
* Null for a state built without a document: with no session to bind the name to there is nothing
|
|
45
|
+
* to tell this editor's copy from another's, and the copy travels as markup alone.
|
|
46
|
+
*/
|
|
47
|
+
export declare function rememberCopied(route: CopyRoute, slice: Slice, sessionId: string | null, numbering: Numbering): string | null;
|
|
48
|
+
/** The copy that name stands for, and only when the session asking is the one that copied it */
|
|
49
|
+
export declare function recallCopied(token: string | null, sessionId: string | null): RecalledCopy | null;
|
|
50
|
+
/**
|
|
51
|
+
* The name the pasted markup carries, if it carries one.
|
|
52
|
+
*
|
|
53
|
+
* The copy writes it on the first element it draws. `prosemirror-view` may wrap that element in
|
|
54
|
+
* the parents its tag needs (a row in a table) and descends back through them before parsing, so
|
|
55
|
+
* the element handed here is either the one carrying the name or its parent.
|
|
56
|
+
*/
|
|
57
|
+
export declare function internalTokenOf(dom: Node): string | null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/editor/clipboard/internalChannel.ts
|
|
2
|
+
import { docxSchema } from "../../schema/index.js";
|
|
3
|
+
import { listKindOf, listRefOf } from "../commands/listCommands.js";
|
|
4
|
+
var INTERNAL_TOKEN_ATTRIBUTE = "data-docx-clip";
|
|
5
|
+
var NO_LIST_KINDS = /* @__PURE__ */ new Map();
|
|
6
|
+
var copied = /* @__PURE__ */ new Map();
|
|
7
|
+
var copies = 0;
|
|
8
|
+
function nextToken() {
|
|
9
|
+
copies += 1;
|
|
10
|
+
return `c${copies}-${Math.random().toString(36).slice(2, 10)}`;
|
|
11
|
+
}
|
|
12
|
+
function rememberCopied(route, slice, sessionId, numbering) {
|
|
13
|
+
if (sessionId === null) return null;
|
|
14
|
+
const token = nextToken();
|
|
15
|
+
copied.set(route, {
|
|
16
|
+
token,
|
|
17
|
+
sessionId,
|
|
18
|
+
slice,
|
|
19
|
+
listKinds: listKindsIn(slice, numbering)
|
|
20
|
+
});
|
|
21
|
+
return token;
|
|
22
|
+
}
|
|
23
|
+
function recallCopied(token, sessionId) {
|
|
24
|
+
if (token === null || sessionId === null) return null;
|
|
25
|
+
const found = [...copied.values()].find(
|
|
26
|
+
(copy) => copy.token === token && copy.sessionId === sessionId
|
|
27
|
+
);
|
|
28
|
+
return found === void 0 ? null : { slice: found.slice, listKinds: found.listKinds };
|
|
29
|
+
}
|
|
30
|
+
function listKindsIn(slice, numbering) {
|
|
31
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
32
|
+
const read = (node) => {
|
|
33
|
+
if (node.type === docxSchema.nodes.paragraph) {
|
|
34
|
+
const ref = listRefOf(node);
|
|
35
|
+
const kind = ref === null ? null : listKindOf(numbering, ref);
|
|
36
|
+
if (ref !== null && kind !== null) kinds.set(ref.numId, kind);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
node.forEach(read);
|
|
40
|
+
};
|
|
41
|
+
slice.content.forEach(read);
|
|
42
|
+
return kinds.size === 0 ? NO_LIST_KINDS : kinds;
|
|
43
|
+
}
|
|
44
|
+
function internalTokenOf(dom) {
|
|
45
|
+
if (dom instanceof Element) {
|
|
46
|
+
const own = dom.getAttribute(INTERNAL_TOKEN_ATTRIBUTE);
|
|
47
|
+
if (own !== null) return own;
|
|
48
|
+
}
|
|
49
|
+
const first = dom instanceof Element || dom instanceof DocumentFragment ? dom.firstElementChild : null;
|
|
50
|
+
return first?.getAttribute(INTERNAL_TOKEN_ATTRIBUTE) ?? null;
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
INTERNAL_TOKEN_ATTRIBUTE,
|
|
54
|
+
NO_LIST_KINDS,
|
|
55
|
+
internalTokenOf,
|
|
56
|
+
recallCopied,
|
|
57
|
+
rememberCopied
|
|
58
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a slice has to obey before it may be put into this document.
|
|
3
|
+
*
|
|
4
|
+
* A slice reaching a paste says where its blocks came from, which list its paragraphs belong to,
|
|
5
|
+
* which comment its markers open, and what everything is drawn with. None of those mean anything
|
|
6
|
+
* outside the document that wrote them: a block naming a fragment of the file would be written
|
|
7
|
+
* from that fragment a second time, a list number naming no definition here would be exported
|
|
8
|
+
* bare, and a comment marker copied along would open a range the comment part never opens. So a
|
|
9
|
+
* paste is a normalization rather than an insertion, and each rule stands as one normalizer over
|
|
10
|
+
* the whole slice, in the order they are declared.
|
|
11
|
+
*
|
|
12
|
+
* A move drop is the exception the first two rules read: content dragged from one place in this
|
|
13
|
+
* very document to another is the same content, so it keeps the identity it had and the anchors it
|
|
14
|
+
* stood in. The source of a move is deleted along with the drop, so nothing is duplicated.
|
|
15
|
+
*/
|
|
16
|
+
import { type Node as PMNode, Slice } from "prosemirror-model";
|
|
17
|
+
import type { EditorState } from "prosemirror-state";
|
|
18
|
+
import { type ListKind } from "../../numbering/listTemplate";
|
|
19
|
+
import type { PastedContent } from "./htmlReader";
|
|
20
|
+
import { type ListKinds } from "./internalChannel";
|
|
21
|
+
/** What one normalizer answers about the document the slice is going into */
|
|
22
|
+
export interface NormalizeContext {
|
|
23
|
+
readonly state: EditorState;
|
|
24
|
+
/** A drop that moves what it carries rather than copying it, which keeps its source identity */
|
|
25
|
+
readonly move: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The list numbers this document answers for: the ones it defines, the ones a list started
|
|
28
|
+
* while editing was registered under, the ones its paragraphs wear, and the ones this very
|
|
29
|
+
* paste has started.
|
|
30
|
+
*/
|
|
31
|
+
readonly knownLists: ReadonlySet<number>;
|
|
32
|
+
/**
|
|
33
|
+
* What the numbers the slice names meant where it was copied, for the numbers this document
|
|
34
|
+
* cannot answer for. Empty for a slice that came in as markup, whose lists were given numbers
|
|
35
|
+
* of this document as they were read.
|
|
36
|
+
*/
|
|
37
|
+
readonly listKinds: ListKinds;
|
|
38
|
+
/**
|
|
39
|
+
* A number for a list this slice starts, registered with the definition it is started with, as
|
|
40
|
+
* the list button starts one. Null in a document with nowhere to write a definition.
|
|
41
|
+
*/
|
|
42
|
+
startList(kind: ListKind): number | null;
|
|
43
|
+
}
|
|
44
|
+
export type SliceNormalizer = (slice: Slice, context: NormalizeContext) => Slice;
|
|
45
|
+
/** The slice with every node rewritten depth first, and the ones mapped to null taken out */
|
|
46
|
+
export declare function mapSliceNodes(slice: Slice, f: (node: PMNode) => PMNode | null): Slice;
|
|
47
|
+
/**
|
|
48
|
+
* Takes the source identity off what is pasted.
|
|
49
|
+
*
|
|
50
|
+
* A block names the fragment of the opened file it came from, and the export writes that fragment
|
|
51
|
+
* back verbatim rather than building one. Two blocks naming the same fragment would therefore go
|
|
52
|
+
* out as the same bytes twice, identifiers and all, so a pasted copy names none and is written
|
|
53
|
+
* from the model instead. A preserved block holding its own XML is that XML wherever it stands and
|
|
54
|
+
* travels whole; one that only names a fragment has nothing of its own to travel with, so it goes.
|
|
55
|
+
*/
|
|
56
|
+
export declare const dropSourceIdentity: SliceNormalizer;
|
|
57
|
+
/**
|
|
58
|
+
* Gives a pasted list a number this document defines.
|
|
59
|
+
*
|
|
60
|
+
* A number the destination knows nothing about is exported as a list nothing defines, which the
|
|
61
|
+
* export invariants refuse. The paragraphs that shared such a number still share the one they are
|
|
62
|
+
* given, so a pasted list stays one list, started the way the list button starts one and drawn and
|
|
63
|
+
* written from the destination's own definition from here on.
|
|
64
|
+
*
|
|
65
|
+
* Which of the two kinds it is started as is the one thing the number alone does not say. A copy
|
|
66
|
+
* made in this same session was remembered alongside what its numbers meant there
|
|
67
|
+
* (`./internalChannel`), so a bulleted list comes back bulleted; a number arriving any other way
|
|
68
|
+
* has no kind to be read off it and counts.
|
|
69
|
+
*/
|
|
70
|
+
export declare const rekeyNumbering: SliceNormalizer;
|
|
71
|
+
/**
|
|
72
|
+
* Takes the anchors off what is pasted.
|
|
73
|
+
*
|
|
74
|
+
* A comment marker, a bookmark and a note reference all point at something written elsewhere in
|
|
75
|
+
* the package: the comment, the bookmark's other end, the note's body. A copy of the marker alone
|
|
76
|
+
* points at the same thing a second time, which is a duplicate identifier the file may not hold
|
|
77
|
+
* and a range the reader cannot close. Nothing here duplicates what they point at, so the anchor
|
|
78
|
+
* goes and the text it stood in stays, which is what Word does with a note it has no body for.
|
|
79
|
+
*
|
|
80
|
+
* A move drop duplicates nothing: the source goes as the drop lands, so the one anchor there was
|
|
81
|
+
* travels with the text it opened. Taking it off would delete a marker the document has no way to
|
|
82
|
+
* write back, which is a change the preserved guard refuses whole, and the drag would do nothing.
|
|
83
|
+
*/
|
|
84
|
+
export declare const detachAnchors: SliceNormalizer;
|
|
85
|
+
/**
|
|
86
|
+
* Works out again what the pasted paragraphs are drawn with.
|
|
87
|
+
*
|
|
88
|
+
* The values a paragraph and the runs in it carry were resolved against the styles, the defaults
|
|
89
|
+
* and the list definitions of wherever the slice came from, this document's own list definitions
|
|
90
|
+
* after the numbers have just been reissued among them. Resolving them here is what makes the
|
|
91
|
+
* numbering the paste registers the numbering the document is then found to wear.
|
|
92
|
+
*/
|
|
93
|
+
export declare const rederiveDisplay: SliceNormalizer;
|
|
94
|
+
export declare const DEFAULT_NORMALIZERS: readonly SliceNormalizer[];
|
|
95
|
+
/**
|
|
96
|
+
* The pasted content as this document may hold it, and the definitions of the lists the paste
|
|
97
|
+
* starts - the ones a reading started, and the ones a reissued number was taken for.
|
|
98
|
+
*/
|
|
99
|
+
export declare function normalizePasted(content: PastedContent, state: EditorState, move: boolean, normalizers?: readonly SliceNormalizer[]): PastedContent;
|