@portone/docx-editor 0.4.0 → 0.5.1
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 +201 -0
- package/CONTRIBUTING.md +2 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +1 -1
- package/dist/DocxEditor.js +44 -25
- 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 +65 -28
- package/dist/docx/exportDocx.js +10 -14
- 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/styles.d.ts +0 -2
- package/dist/docx/formatting/styles.js +2 -15
- package/dist/docx/headersFooters.d.ts +89 -20
- package/dist/docx/headersFooters.js +163 -172
- package/dist/docx/identities.js +17 -26
- package/dist/docx/importDocx.d.ts +1 -1
- package/dist/docx/importDocx.js +100 -48
- package/dist/docx/importParagraph.d.ts +18 -5
- package/dist/docx/importParagraph.js +75 -128
- package/dist/docx/importPolicy.d.ts +70 -0
- package/dist/docx/importPolicy.js +195 -0
- package/dist/docx/importPreserved.d.ts +30 -0
- package/dist/docx/importPreserved.js +54 -0
- package/dist/docx/importTable.js +56 -12
- package/dist/docx/invariants.js +16 -18
- package/dist/docx/notes.d.ts +6 -1
- package/dist/docx/notes.js +6 -16
- 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 +9 -4
- package/dist/docx/serializeParagraph.js +23 -58
- 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 +23 -6
- package/dist/docx/session.js +26 -5
- 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/tableTemplate.d.ts +25 -0
- package/dist/docx/tableTemplate.js +30 -16
- package/dist/docx/wrappers.d.ts +67 -0
- package/dist/docx/wrappers.js +117 -0
- package/dist/editor/clipboard/blockReaders.d.ts +74 -0
- package/dist/editor/clipboard/blockReaders.js +193 -0
- package/dist/editor/clipboard/htmlReader.d.ts +26 -0
- package/dist/editor/clipboard/htmlReader.js +376 -0
- package/dist/editor/clipboard/inlineFormatting.d.ts +0 -7
- package/dist/editor/clipboard/inlineFormatting.js +1 -7
- 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 +188 -0
- package/dist/editor/clipboard/readContext.d.ts +34 -0
- package/dist/editor/clipboard/readContext.js +25 -0
- package/dist/editor/clipboard/readers.d.ts +33 -0
- package/dist/editor/clipboard/readers.js +37 -0
- package/dist/editor/clipboard/source.d.ts +12 -0
- package/dist/editor/clipboard/source.js +21 -0
- package/dist/editor/commands/comments/editing.d.ts +26 -6
- package/dist/editor/commands/comments/editing.js +177 -106
- 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/index.d.ts +2 -2
- package/dist/editor/commands/index.js +2 -0
- package/dist/editor/commands/linkCommands.js +20 -5
- package/dist/editor/commands/listCommands.d.ts +6 -0
- package/dist/editor/commands/listCommands.js +3 -2
- package/dist/editor/commands/lockCommands.js +59 -16
- package/dist/editor/commands/noteQueries.d.ts +22 -5
- package/dist/editor/commands/noteQueries.js +34 -6
- package/dist/editor/commands/paragraphCommands.js +2 -2
- package/dist/editor/createEditor.js +13 -3
- package/dist/editor/documentStyles.d.ts +26 -4
- package/dist/editor/documentStyles.js +9 -4
- 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/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 +15 -8
- package/dist/editor/plugins/linkPanel.d.ts +7 -3
- package/dist/editor/plugins/linkPanel.js +13 -27
- package/dist/editor/plugins/panelState.d.ts +67 -0
- package/dist/editor/plugins/panelState.js +54 -0
- package/dist/editor/plugins/tableContextMenu.d.ts +1 -1
- package/dist/editor/plugins/tableContextMenu.js +30 -47
- package/dist/editor/plugins/textContextMenu.d.ts +1 -1
- package/dist/editor/plugins/textContextMenu.js +30 -47
- 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 +6 -0
- package/dist/ooxml/names.js +2 -0
- package/dist/ooxml/props.d.ts +10 -0
- package/dist/ooxml/props.js +28 -4
- package/dist/ooxml/rangeMarkers.d.ts +5 -0
- package/dist/ooxml/rangeMarkers.js +24 -0
- package/dist/page/PageGuides.d.ts +7 -3
- package/dist/page/PageGuides.js +13 -24
- package/dist/page/pageLayout.d.ts +64 -5
- package/dist/page/pageLayout.js +90 -29
- package/dist/page/usePageLayout.d.ts +14 -4
- package/dist/page/usePageLayout.js +19 -20
- package/dist/schema/attrRoles.js +46 -23
- package/dist/schema/clipboard.d.ts +72 -0
- package/dist/schema/clipboard.js +288 -0
- package/dist/schema/docxSchema.d.ts +9 -1
- package/dist/schema/docxSchema.js +217 -165
- 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/locks.d.ts +3 -0
- package/dist/schema/locks.js +29 -12
- 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.d.ts +14 -0
- package/dist/schema/rendering.js +27 -12
- package/dist/schema/stories.d.ts +49 -0
- package/dist/schema/stories.js +78 -0
- package/dist/schema/wrappers.d.ts +58 -0
- package/dist/schema/wrappers.js +77 -0
- package/dist/styles/classNames.d.ts +3 -5
- package/dist/styles/classNames.js +3 -5
- package/dist/styles.css +74 -12
- 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 +5 -4
- package/dist/editor/externalClipboard.d.ts +0 -22
- package/dist/editor/externalClipboard.js +0 -486
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// src/editor/clipboard/blockReaders.ts
|
|
2
|
+
import {
|
|
3
|
+
createTableNodeFrom,
|
|
4
|
+
isTableSide,
|
|
5
|
+
MAX_TABLE_SIDE
|
|
6
|
+
} from "../../docx/tableTemplate.js";
|
|
7
|
+
import { MAX_ILVL } from "../../numbering/listTemplate.js";
|
|
8
|
+
import { docxSchema } from "../../schema/index.js";
|
|
9
|
+
import { contextFor } from "./inlineFormatting.js";
|
|
10
|
+
var CELL_TAGS = /* @__PURE__ */ new Set(["TD", "TH"]);
|
|
11
|
+
var ROW_GROUP_TAGS = /* @__PURE__ */ new Set(["THEAD", "TBODY", "TFOOT"]);
|
|
12
|
+
function rowsOf(table) {
|
|
13
|
+
return [...table.children].flatMap((child) => {
|
|
14
|
+
if (!(child instanceof HTMLElement)) return [];
|
|
15
|
+
if (child.tagName === "TR") return [child];
|
|
16
|
+
if (!ROW_GROUP_TAGS.has(child.tagName)) return [];
|
|
17
|
+
return [...child.children].flatMap(
|
|
18
|
+
(row) => row instanceof HTMLElement && row.tagName === "TR" ? [row] : []
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function cellsOf(row) {
|
|
23
|
+
return [...row.children].flatMap(
|
|
24
|
+
(cell) => cell instanceof HTMLElement && CELL_TAGS.has(cell.tagName) ? [cell] : []
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function spanOf(element, name, limit) {
|
|
28
|
+
const value = Number.parseInt(element.getAttribute(name) ?? "", 10);
|
|
29
|
+
return Number.isFinite(value) && value > 1 ? Math.min(value, limit) : 1;
|
|
30
|
+
}
|
|
31
|
+
function gridOf(table) {
|
|
32
|
+
const rows = rowsOf(table).filter((row) => cellsOf(row).length > 0);
|
|
33
|
+
if (!isTableSide(rows.length)) return null;
|
|
34
|
+
const covered = rows.map(() => /* @__PURE__ */ new Set());
|
|
35
|
+
const cells = rows.map(() => []);
|
|
36
|
+
let cols = 0;
|
|
37
|
+
rows.forEach((row, top) => {
|
|
38
|
+
let left = 0;
|
|
39
|
+
for (const element of cellsOf(row)) {
|
|
40
|
+
while (covered[top]?.has(left)) left += 1;
|
|
41
|
+
const colspan = spanOf(element, "colspan", MAX_TABLE_SIDE);
|
|
42
|
+
const rowspan = Math.min(
|
|
43
|
+
spanOf(element, "rowspan", MAX_TABLE_SIDE),
|
|
44
|
+
rows.length - top
|
|
45
|
+
);
|
|
46
|
+
for (let covering = top; covering < top + rowspan; covering += 1) {
|
|
47
|
+
for (let column = left; column < left + colspan; column += 1) {
|
|
48
|
+
covered[covering]?.add(column);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
cells[top]?.push({
|
|
52
|
+
element,
|
|
53
|
+
rect: { top, bottom: top + rowspan, left, right: left + colspan }
|
|
54
|
+
});
|
|
55
|
+
left += colspan;
|
|
56
|
+
cols = Math.max(cols, left);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return isTableSide(cols) ? cells : null;
|
|
60
|
+
}
|
|
61
|
+
function captionOf(table) {
|
|
62
|
+
for (const child of table.children) {
|
|
63
|
+
if (child instanceof HTMLElement && child.tagName === "CAPTION") {
|
|
64
|
+
return child;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function rowParagraphs(table, inline, host) {
|
|
70
|
+
return rowsOf(table).filter((row) => cellsOf(row).length > 0).map(
|
|
71
|
+
(row) => host.paragraph(
|
|
72
|
+
cellsOf(row).flatMap((cell, index) => [
|
|
73
|
+
...index === 0 ? [] : [docxSchema.text(" ", [docxSchema.marks.tab.create()])],
|
|
74
|
+
...host.readInline(cell.childNodes, contextFor(inline, cell))
|
|
75
|
+
])
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
var tableBlockReader = {
|
|
80
|
+
read: (element, inline, host) => {
|
|
81
|
+
if (element.tagName !== "TABLE") return null;
|
|
82
|
+
const inside = contextFor(inline, element);
|
|
83
|
+
const caption = captionOf(element);
|
|
84
|
+
const above = caption === null ? [] : [
|
|
85
|
+
host.paragraph(
|
|
86
|
+
host.readInline(caption.childNodes, contextFor(inside, caption))
|
|
87
|
+
)
|
|
88
|
+
];
|
|
89
|
+
const grid = host.inTable ? null : gridOf(element);
|
|
90
|
+
if (grid === null) {
|
|
91
|
+
return [...above, ...rowParagraphs(element, inside, host)];
|
|
92
|
+
}
|
|
93
|
+
const cells = grid.map(
|
|
94
|
+
(row) => row.map(({ element: cell, rect }) => ({
|
|
95
|
+
rect,
|
|
96
|
+
content: host.readCell(cell, contextFor(inside, cell))
|
|
97
|
+
}))
|
|
98
|
+
);
|
|
99
|
+
return [...above, createTableNodeFrom(cells, host.context.geometry)];
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var WORD_LIST = /mso-list:\s*l(?<list>\d+)\s+level(?<level>\d+)\s+lfo(?<applied>\d+)/i;
|
|
103
|
+
var IGNORED_MARKER = /mso-list:\s*Ignore/i;
|
|
104
|
+
var MARKER_START = /^\[if\s+!supportLists\]/i;
|
|
105
|
+
var MARKER_END = /^\[endif\]/i;
|
|
106
|
+
var COUNTED_MARKER = /^\s*(?:\d+|[A-Za-z]+)\s*[.)]\s*/;
|
|
107
|
+
function wordListItemOf(element) {
|
|
108
|
+
const found = WORD_LIST.exec(element.getAttribute("style") ?? "")?.groups;
|
|
109
|
+
if (found === void 0) return null;
|
|
110
|
+
const level = Number.parseInt(found.level ?? "", 10);
|
|
111
|
+
return {
|
|
112
|
+
key: `${found.list}/${found.applied}`,
|
|
113
|
+
// Word counts its levels from one, and no document has more levels than the model holds
|
|
114
|
+
level: Number.isFinite(level) ? Math.min(Math.max(level, 1), MAX_ILVL + 1) - 1 : 0
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function withoutMarker(element) {
|
|
118
|
+
const content = [];
|
|
119
|
+
let marker = "";
|
|
120
|
+
let inMarker = false;
|
|
121
|
+
for (const child of element.childNodes) {
|
|
122
|
+
if (child.nodeType === child.COMMENT_NODE) {
|
|
123
|
+
const data = child.nodeValue ?? "";
|
|
124
|
+
if (MARKER_START.test(data)) inMarker = true;
|
|
125
|
+
else if (MARKER_END.test(data)) inMarker = false;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const ignored = child instanceof HTMLElement && IGNORED_MARKER.test(child.getAttribute("style") ?? "");
|
|
129
|
+
if (inMarker || ignored) marker += child.textContent ?? "";
|
|
130
|
+
else content.push(child);
|
|
131
|
+
}
|
|
132
|
+
return { marker, content };
|
|
133
|
+
}
|
|
134
|
+
var SYMBOL_FONTS = /symbol|wingdings|webdings/i;
|
|
135
|
+
function leadingNode(element) {
|
|
136
|
+
for (const child of element.childNodes) {
|
|
137
|
+
const blank = child.nodeType === child.TEXT_NODE && (child.nodeValue ?? "").trim() === "";
|
|
138
|
+
if (!blank) return child;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function firstText(element) {
|
|
143
|
+
for (const child of element.childNodes) {
|
|
144
|
+
if (child.nodeType === child.TEXT_NODE) {
|
|
145
|
+
if ((child.nodeValue ?? "").trim() !== "") return child;
|
|
146
|
+
} else if (child instanceof HTMLElement) {
|
|
147
|
+
const found = firstText(child);
|
|
148
|
+
if (found !== null) return found;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function leadingMarker(element) {
|
|
154
|
+
const copy = element.cloneNode(true);
|
|
155
|
+
if (!(copy instanceof HTMLElement)) return null;
|
|
156
|
+
const lead = leadingNode(copy);
|
|
157
|
+
if (lead instanceof HTMLElement && SYMBOL_FONTS.test(lead.getAttribute("style") ?? "")) {
|
|
158
|
+
const marker = lead.textContent ?? "";
|
|
159
|
+
lead.remove();
|
|
160
|
+
return { marker, content: [...copy.childNodes] };
|
|
161
|
+
}
|
|
162
|
+
const text = firstText(copy);
|
|
163
|
+
const counted = COUNTED_MARKER.exec(text?.nodeValue ?? "");
|
|
164
|
+
if (text === null || counted === null) return null;
|
|
165
|
+
text.nodeValue = (text.nodeValue ?? "").slice(counted[0].length);
|
|
166
|
+
return { marker: counted[0], content: [...copy.childNodes] };
|
|
167
|
+
}
|
|
168
|
+
var wordListReader = {
|
|
169
|
+
read: (element, inline, host) => {
|
|
170
|
+
if (host.context.source !== "word") return null;
|
|
171
|
+
const item = wordListItemOf(element);
|
|
172
|
+
if (item === null) return null;
|
|
173
|
+
const marked = withoutMarker(element);
|
|
174
|
+
const { marker, content } = marked.marker === "" ? leadingMarker(element) ?? marked : marked;
|
|
175
|
+
const kind = COUNTED_MARKER.test(marker) ? "numbered" : "bullet";
|
|
176
|
+
return [
|
|
177
|
+
host.paragraph(host.readInline(content, contextFor(inline, element)), {
|
|
178
|
+
kind,
|
|
179
|
+
numId: host.listNumber(item.key, kind),
|
|
180
|
+
level: item.level
|
|
181
|
+
})
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var DEFAULT_BLOCK_READERS = [
|
|
186
|
+
tableBlockReader,
|
|
187
|
+
wordListReader
|
|
188
|
+
];
|
|
189
|
+
export {
|
|
190
|
+
DEFAULT_BLOCK_READERS,
|
|
191
|
+
tableBlockReader,
|
|
192
|
+
wordListReader
|
|
193
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Slice } from "prosemirror-model";
|
|
2
|
+
import type { Transaction } from "prosemirror-state";
|
|
3
|
+
import { type NewLists } from "../../numbering/listRegistry";
|
|
4
|
+
import { type HtmlBlockReader } from "./blockReaders";
|
|
5
|
+
import type { ListKinds } from "./internalChannel";
|
|
6
|
+
import type { HtmlReadContext } from "./readContext";
|
|
7
|
+
/** What a paste puts in: the content, and the definitions of the lists it started */
|
|
8
|
+
export interface PastedContent {
|
|
9
|
+
slice: Slice;
|
|
10
|
+
newLists: NewLists;
|
|
11
|
+
/**
|
|
12
|
+
* What the numbers this slice's paragraphs name meant where it was copied
|
|
13
|
+
* (`./internalChannel`). Absent for a reading that gave the lists it read numbers of its own,
|
|
14
|
+
* which knows what kind each is from the markup it read them out of.
|
|
15
|
+
*/
|
|
16
|
+
listKinds?: ListKinds;
|
|
17
|
+
}
|
|
18
|
+
/** The content one piece of already parsed markup reads as, or null when it reads as nothing */
|
|
19
|
+
export declare function readHtml(root: ParentNode, context: HtmlReadContext, blockReaders?: readonly HtmlBlockReader[]): PastedContent | null;
|
|
20
|
+
/** The same reading, for a caller holding the markup as a string rather than as elements */
|
|
21
|
+
export declare function readHtmlSlice(context: HtmlReadContext, source: string): PastedContent | null;
|
|
22
|
+
/**
|
|
23
|
+
* The transaction a paste is put in with: the content, and the definitions of the lists it started
|
|
24
|
+
* recorded on the document node so that the export writes them out and undo takes them back.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withPastedContent(tr: Transaction, content: PastedContent): Transaction;
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// src/editor/clipboard/htmlReader.ts
|
|
2
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
3
|
+
import {
|
|
4
|
+
paragraphAttrsFor
|
|
5
|
+
} from "../../docx/formatting.js";
|
|
6
|
+
import {
|
|
7
|
+
withListNumbering,
|
|
8
|
+
withParagraphStyle
|
|
9
|
+
} from "../../docx/paraProps.js";
|
|
10
|
+
import {
|
|
11
|
+
listsWorn,
|
|
12
|
+
NEW_LISTS_ATTR,
|
|
13
|
+
newListsValue
|
|
14
|
+
} from "../../numbering/listRegistry.js";
|
|
15
|
+
import {
|
|
16
|
+
allocateList,
|
|
17
|
+
MAX_ILVL,
|
|
18
|
+
templateList
|
|
19
|
+
} from "../../numbering/listTemplate.js";
|
|
20
|
+
import { styleIdOf } from "../../ooxml/props.js";
|
|
21
|
+
import { docxSchema } from "../../schema/index.js";
|
|
22
|
+
import { COPIED_STYLE_ATTRIBUTE } from "../../schema/clipboard.js";
|
|
23
|
+
import { editorClassNames } from "../../styles/classNames.js";
|
|
24
|
+
import { numIdsIn } from "../commands/listCommands.js";
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_BLOCK_READERS
|
|
27
|
+
} from "./blockReaders.js";
|
|
28
|
+
import { PASTED_IMAGE_ATTRIBUTE } from "./images.js";
|
|
29
|
+
import {
|
|
30
|
+
appendInline,
|
|
31
|
+
contextFor,
|
|
32
|
+
marksFor,
|
|
33
|
+
withInlineStyle
|
|
34
|
+
} from "./inlineFormatting.js";
|
|
35
|
+
import { detectHtmlSource } from "./source.js";
|
|
36
|
+
var BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
37
|
+
"ADDRESS",
|
|
38
|
+
"ARTICLE",
|
|
39
|
+
"ASIDE",
|
|
40
|
+
"BLOCKQUOTE",
|
|
41
|
+
"DIV",
|
|
42
|
+
"H1",
|
|
43
|
+
"H2",
|
|
44
|
+
"H3",
|
|
45
|
+
"H4",
|
|
46
|
+
"H5",
|
|
47
|
+
"H6",
|
|
48
|
+
"LI",
|
|
49
|
+
"P",
|
|
50
|
+
"PRE",
|
|
51
|
+
"SECTION"
|
|
52
|
+
]);
|
|
53
|
+
var BLOCK_SELECTOR = [...BLOCK_TAGS, "OL", "UL", "TABLE"].map((tag) => tag.toLowerCase()).join(",");
|
|
54
|
+
var HEADING_LEVELS = {
|
|
55
|
+
H1: 1,
|
|
56
|
+
H2: 2,
|
|
57
|
+
H3: 3,
|
|
58
|
+
H4: 4,
|
|
59
|
+
H5: 5,
|
|
60
|
+
H6: 6
|
|
61
|
+
};
|
|
62
|
+
var HEADING_FALLBACKS = {
|
|
63
|
+
1: { bold: true, fontSizePt: 24 },
|
|
64
|
+
2: { bold: true, fontSizePt: 18 },
|
|
65
|
+
3: { bold: true, fontSizePt: 14 },
|
|
66
|
+
4: { bold: true, fontSizePt: 12 },
|
|
67
|
+
5: { bold: true, fontSizePt: 10 },
|
|
68
|
+
6: { bold: true, fontSizePt: 8 }
|
|
69
|
+
};
|
|
70
|
+
function normalizedStyleName(value) {
|
|
71
|
+
return value.toLowerCase().replace(/[\s_-]+/g, "");
|
|
72
|
+
}
|
|
73
|
+
function copiedStyleId(element) {
|
|
74
|
+
if (!element.classList.contains(editorClassNames.paragraph)) return null;
|
|
75
|
+
return element.getAttribute(COPIED_STYLE_ATTRIBUTE) ?? styleIdOf(element.getAttribute("data-ppr"));
|
|
76
|
+
}
|
|
77
|
+
function headingLevel(element, sourceStyleId) {
|
|
78
|
+
const direct = HEADING_LEVELS[element.tagName];
|
|
79
|
+
if (direct !== void 0) return direct;
|
|
80
|
+
const match = /^heading([1-6])$/.exec(
|
|
81
|
+
normalizedStyleName(sourceStyleId ?? "")
|
|
82
|
+
);
|
|
83
|
+
return match ? Number.parseInt(match[1] ?? "", 10) : null;
|
|
84
|
+
}
|
|
85
|
+
function destinationStyleId(styles, sourceStyleId, level) {
|
|
86
|
+
if (sourceStyleId && styles.some((style) => style.id === sourceStyleId)) {
|
|
87
|
+
return sourceStyleId;
|
|
88
|
+
}
|
|
89
|
+
if (level === null) return null;
|
|
90
|
+
const wanted = `heading${level}`;
|
|
91
|
+
return styles.find(
|
|
92
|
+
(style) => normalizedStyleName(style.id) === wanted || normalizedStyleName(style.name) === wanted
|
|
93
|
+
)?.id ?? null;
|
|
94
|
+
}
|
|
95
|
+
function blockContext(context, parent, element, preserveParagraphStyle) {
|
|
96
|
+
const editorParagraph = element.classList.contains(
|
|
97
|
+
editorClassNames.paragraph
|
|
98
|
+
);
|
|
99
|
+
const sourceStyleId = copiedStyleId(element);
|
|
100
|
+
const level = headingLevel(element, sourceStyleId);
|
|
101
|
+
if (!preserveParagraphStyle && editorParagraph) {
|
|
102
|
+
return {
|
|
103
|
+
inline: contextFor(parent, element),
|
|
104
|
+
paragraph: null
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const styleId = destinationStyleId(
|
|
108
|
+
context.paragraphStyles,
|
|
109
|
+
sourceStyleId,
|
|
110
|
+
level
|
|
111
|
+
);
|
|
112
|
+
const paragraph = styleId === null ? null : withParagraphStyle(null, styleId);
|
|
113
|
+
const usesDestinationStyle = editorParagraph && (sourceStyleId === null || paragraph !== null);
|
|
114
|
+
let inline = parent;
|
|
115
|
+
if (paragraph === null && level !== null) {
|
|
116
|
+
inline = withInlineStyle(inline, HEADING_FALLBACKS[level] ?? {});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
inline: contextFor(inline, element, !usesDestinationStyle),
|
|
120
|
+
paragraph
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function listParagraphProps(list) {
|
|
124
|
+
if (!list || list.numId === null) return null;
|
|
125
|
+
return withListNumbering(null, {
|
|
126
|
+
numbering: { numId: list.numId, ilvl: Math.min(MAX_ILVL, list.level) },
|
|
127
|
+
indent: { kind: "keep" }
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function paragraphAttrs(formatting, list, paragraph) {
|
|
131
|
+
const props = listParagraphProps(list) ?? paragraph;
|
|
132
|
+
return props ? { pPr: props.pPr, ...paragraphAttrsFor(props.pPr, formatting) } : null;
|
|
133
|
+
}
|
|
134
|
+
var HtmlReader = class {
|
|
135
|
+
constructor(context, preserveParagraphStyles, blockReaders) {
|
|
136
|
+
this.context = context;
|
|
137
|
+
this.preserveParagraphStyles = preserveParagraphStyles;
|
|
138
|
+
this.blockReaders = blockReaders;
|
|
139
|
+
this.formatting = context.formatting;
|
|
140
|
+
this.used = new Set(context.numbering.used);
|
|
141
|
+
this.canCreateLists = context.numbering.canCreate;
|
|
142
|
+
}
|
|
143
|
+
context;
|
|
144
|
+
preserveParagraphStyles;
|
|
145
|
+
blockReaders;
|
|
146
|
+
blocks = [];
|
|
147
|
+
used;
|
|
148
|
+
canCreateLists;
|
|
149
|
+
/** How deep the reading stands inside the tables it is reading */
|
|
150
|
+
tableDepth = 0;
|
|
151
|
+
/** The number each list a block reader named has taken, so its items join one list */
|
|
152
|
+
listNumbers = /* @__PURE__ */ new Map();
|
|
153
|
+
/**
|
|
154
|
+
* What the pasted paragraphs are resolved against: the document's context, holding the
|
|
155
|
+
* definition of each pasted list as it is read, so that an item is drawn against the list it
|
|
156
|
+
* is joining rather than against a list nothing yet defines.
|
|
157
|
+
*/
|
|
158
|
+
formatting;
|
|
159
|
+
/** The definitions of every list started while editing, the pasted ones among them */
|
|
160
|
+
get newLists() {
|
|
161
|
+
return this.formatting.numbering.added;
|
|
162
|
+
}
|
|
163
|
+
read(root) {
|
|
164
|
+
this.readFlow(root, { style: {}, href: null, preserveWhitespace: false });
|
|
165
|
+
return this.blocks;
|
|
166
|
+
}
|
|
167
|
+
appendInline(target, node, context) {
|
|
168
|
+
appendInline(target, node, context, (content, element, inline) => {
|
|
169
|
+
if (this.context.source === "word" && element.tagName === "O:P") {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
if (element.tagName !== "IMG") return false;
|
|
173
|
+
const token = element.getAttribute(PASTED_IMAGE_ATTRIBUTE);
|
|
174
|
+
const image = token === null ? void 0 : this.context.images.get(token);
|
|
175
|
+
if (!image) return true;
|
|
176
|
+
content.push(
|
|
177
|
+
docxSchema.nodes.image.create(
|
|
178
|
+
{
|
|
179
|
+
...image,
|
|
180
|
+
// The source drawing may point at a relationship in another package.
|
|
181
|
+
xml: null
|
|
182
|
+
},
|
|
183
|
+
null,
|
|
184
|
+
marksFor(inline)
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
return true;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* One paragraph of the reading. An item of a list this document may not start keeps its marker
|
|
192
|
+
* as text, since the list it names is one the document will not be numbering.
|
|
193
|
+
*/
|
|
194
|
+
paragraphNode(content, list = null, paragraph = null) {
|
|
195
|
+
const marker = list && list.numId === null ? [docxSchema.text(list.kind === "bullet" ? "\u2022 " : "1. ")] : [];
|
|
196
|
+
return docxSchema.nodes.paragraph.create(
|
|
197
|
+
paragraphAttrs(this.formatting, list, paragraph),
|
|
198
|
+
[...marker, ...content]
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
addParagraph(content, list = null, paragraph = null) {
|
|
202
|
+
this.blocks.push(this.paragraphNode(content, list, paragraph));
|
|
203
|
+
}
|
|
204
|
+
/** The blocks a nested reading produces, kept apart from the ones read so far */
|
|
205
|
+
collect(read) {
|
|
206
|
+
const outer = this.blocks;
|
|
207
|
+
this.blocks = [];
|
|
208
|
+
read();
|
|
209
|
+
const inner = this.blocks;
|
|
210
|
+
this.blocks = outer;
|
|
211
|
+
return inner;
|
|
212
|
+
}
|
|
213
|
+
/** What the first reader to answer for this element reads it as, or null where none does */
|
|
214
|
+
readBlock(element, context, host) {
|
|
215
|
+
for (const reader of this.blockReaders) {
|
|
216
|
+
const read = reader.read(element, context, host);
|
|
217
|
+
if (read !== null) return read;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* What a block reader may ask of this reading. One is made per level of the reading, since a
|
|
223
|
+
* reader asks whether it stands inside a table and that answer is the level's own.
|
|
224
|
+
*/
|
|
225
|
+
hostView() {
|
|
226
|
+
return {
|
|
227
|
+
context: this.context,
|
|
228
|
+
inTable: this.tableDepth > 0,
|
|
229
|
+
readCell: (cell, inline) => {
|
|
230
|
+
this.tableDepth += 1;
|
|
231
|
+
const blocks = this.collect(() => this.readFlow(cell, inline));
|
|
232
|
+
this.tableDepth -= 1;
|
|
233
|
+
return blocks;
|
|
234
|
+
},
|
|
235
|
+
readInline: (nodes, inline) => {
|
|
236
|
+
const target = [];
|
|
237
|
+
for (const node of nodes) this.appendInline(target, node, inline);
|
|
238
|
+
return target;
|
|
239
|
+
},
|
|
240
|
+
paragraph: (content, list) => this.paragraphNode(content, list ?? null),
|
|
241
|
+
listNumber: (key, kind) => {
|
|
242
|
+
const known = this.listNumbers.get(key);
|
|
243
|
+
if (known !== void 0) return known;
|
|
244
|
+
const numId = this.takeNumId(kind);
|
|
245
|
+
this.listNumbers.set(key, numId);
|
|
246
|
+
return numId;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
readFlow(parent, context, paragraph = null) {
|
|
251
|
+
let inline = [];
|
|
252
|
+
const flush = () => {
|
|
253
|
+
if (inline.length === 0) return;
|
|
254
|
+
this.addParagraph(inline, null, paragraph);
|
|
255
|
+
inline = [];
|
|
256
|
+
};
|
|
257
|
+
const host = this.hostView();
|
|
258
|
+
for (const child of parent.childNodes) {
|
|
259
|
+
const element = child.nodeType === child.ELEMENT_NODE ? child : null;
|
|
260
|
+
const read = element && this.readBlock(element, context, host);
|
|
261
|
+
if (read) {
|
|
262
|
+
flush();
|
|
263
|
+
this.blocks.push(...read);
|
|
264
|
+
} else if (element?.tagName === "UL" || element?.tagName === "OL") {
|
|
265
|
+
flush();
|
|
266
|
+
this.readList(element, context, 0, null);
|
|
267
|
+
} else if (element && (BLOCK_TAGS.has(element.tagName) || element.querySelector(BLOCK_SELECTOR) !== null)) {
|
|
268
|
+
flush();
|
|
269
|
+
const before = this.blocks.length;
|
|
270
|
+
const block = blockContext(
|
|
271
|
+
this.context,
|
|
272
|
+
context,
|
|
273
|
+
element,
|
|
274
|
+
this.preserveParagraphStyles
|
|
275
|
+
);
|
|
276
|
+
this.readFlow(element, block.inline, block.paragraph);
|
|
277
|
+
if (this.blocks.length === before) {
|
|
278
|
+
this.addParagraph([], null, block.paragraph);
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
this.appendInline(inline, child, context);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
flush();
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The number a pasted list takes, registered with the definition that list is drawn and written
|
|
288
|
+
* with. A pasted list is started here just as the list button starts one.
|
|
289
|
+
*/
|
|
290
|
+
takeNumId(kind) {
|
|
291
|
+
if (!this.canCreateLists) return null;
|
|
292
|
+
const started = allocateList(
|
|
293
|
+
this.formatting.numbering,
|
|
294
|
+
this.used,
|
|
295
|
+
templateList(kind)
|
|
296
|
+
);
|
|
297
|
+
this.formatting = { ...this.formatting, numbering: started.numbering };
|
|
298
|
+
this.used.add(started.numId);
|
|
299
|
+
return started.numId;
|
|
300
|
+
}
|
|
301
|
+
readList(list, context, level, inherited) {
|
|
302
|
+
const kind = list.tagName === "UL" ? "bullet" : "numbered";
|
|
303
|
+
const listContext = {
|
|
304
|
+
kind,
|
|
305
|
+
numId: inherited?.kind === kind ? inherited.numId : this.takeNumId(kind),
|
|
306
|
+
level
|
|
307
|
+
};
|
|
308
|
+
const items = [...list.children].flatMap(
|
|
309
|
+
(child) => child instanceof HTMLElement && child.tagName === "LI" ? [child] : []
|
|
310
|
+
);
|
|
311
|
+
const host = this.hostView();
|
|
312
|
+
for (const item of items) {
|
|
313
|
+
const itemContext = contextFor(context, item);
|
|
314
|
+
const content = [];
|
|
315
|
+
const after = [];
|
|
316
|
+
for (const child of item.childNodes) {
|
|
317
|
+
const nested = child instanceof HTMLElement ? child : null;
|
|
318
|
+
if (nested?.tagName === "UL" || nested?.tagName === "OL") continue;
|
|
319
|
+
const read = nested && this.readBlock(nested, itemContext, host);
|
|
320
|
+
if (read) after.push(...read);
|
|
321
|
+
else this.appendInline(content, child, itemContext);
|
|
322
|
+
}
|
|
323
|
+
this.addParagraph(content, listContext);
|
|
324
|
+
this.blocks.push(...after);
|
|
325
|
+
for (const nested of Array.from(item.children)) {
|
|
326
|
+
if (nested.tagName === "UL" || nested.tagName === "OL") {
|
|
327
|
+
this.readList(
|
|
328
|
+
nested,
|
|
329
|
+
itemContext,
|
|
330
|
+
level + 1,
|
|
331
|
+
listContext
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
function sliceDepth(root) {
|
|
339
|
+
const marker = root.querySelector("[data-pm-slice]");
|
|
340
|
+
const openStart = Number.parseInt(
|
|
341
|
+
marker?.getAttribute("data-pm-slice")?.split(/\s+/, 1)[0] ?? "",
|
|
342
|
+
10
|
|
343
|
+
);
|
|
344
|
+
if (Number.isFinite(openStart)) return openStart === 0 ? 0 : 1;
|
|
345
|
+
return root.querySelector(BLOCK_SELECTOR) ? 0 : 1;
|
|
346
|
+
}
|
|
347
|
+
function readHtml(root, context, blockReaders = DEFAULT_BLOCK_READERS) {
|
|
348
|
+
const open = sliceDepth(root);
|
|
349
|
+
const reader = new HtmlReader(
|
|
350
|
+
{ ...context, source: detectHtmlSource(root) },
|
|
351
|
+
open === 0,
|
|
352
|
+
blockReaders
|
|
353
|
+
);
|
|
354
|
+
const blocks = reader.read(root);
|
|
355
|
+
return blocks.length === 0 ? null : {
|
|
356
|
+
slice: new Slice(Fragment.fromArray([...blocks]), open, open),
|
|
357
|
+
newLists: reader.newLists
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function readHtmlSlice(context, source) {
|
|
361
|
+
if (source.trim() === "") return null;
|
|
362
|
+
const template = context.document.createElement("template");
|
|
363
|
+
template.innerHTML = source;
|
|
364
|
+
return readHtml(template.content, context);
|
|
365
|
+
}
|
|
366
|
+
function withPastedContent(tr, content) {
|
|
367
|
+
return tr.setDocAttribute(
|
|
368
|
+
NEW_LISTS_ATTR,
|
|
369
|
+
newListsValue(listsWorn(content.newLists, numIdsIn(tr.doc)))
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
export {
|
|
373
|
+
readHtml,
|
|
374
|
+
readHtmlSlice,
|
|
375
|
+
withPastedContent
|
|
376
|
+
};
|
|
@@ -15,13 +15,6 @@ export interface InlineContext {
|
|
|
15
15
|
preserveWhitespace: boolean;
|
|
16
16
|
}
|
|
17
17
|
export type InlineElementReader = (target: PMNode[], element: HTMLElement, context: InlineContext) => boolean;
|
|
18
|
-
/**
|
|
19
|
-
* The address a link may carry, whichever way it travels.
|
|
20
|
-
*
|
|
21
|
-
* A copy writes one out and a paste reads one in, and an address this rule turns down is one
|
|
22
|
-
* neither end should act on, so both ask here.
|
|
23
|
-
*/
|
|
24
|
-
export declare function safeHref(value: string | null): string | null;
|
|
25
18
|
export declare function contextFor(parent: InlineContext, element: HTMLElement, readElementStyle?: boolean): InlineContext;
|
|
26
19
|
export declare function withInlineStyle(context: InlineContext, style: InlineStyle): InlineContext;
|
|
27
20
|
export declare function marksFor(context: InlineContext): Mark[];
|
|
@@ -3,6 +3,7 @@ import { rPrOf } from "../../docx/formatting.js";
|
|
|
3
3
|
import { readRunProps } from "../../docx/runProps.js";
|
|
4
4
|
import { HALF_POINTS_PER_PT, ST_HpsMeasure } from "../../ooxml/simpleTypes.js";
|
|
5
5
|
import { docxSchema } from "../../schema/index.js";
|
|
6
|
+
import { safeHref } from "../../schema/clipboard.js";
|
|
6
7
|
import { editorClassNames } from "../../styles/classNames.js";
|
|
7
8
|
var IGNORED_TAGS = /* @__PURE__ */ new Set([
|
|
8
9
|
"HEAD",
|
|
@@ -135,12 +136,6 @@ function styleOf(parent, element) {
|
|
|
135
136
|
if (background !== null) style.background = background;
|
|
136
137
|
return style;
|
|
137
138
|
}
|
|
138
|
-
function safeHref(value) {
|
|
139
|
-
const href = value?.trim() ?? "";
|
|
140
|
-
if (href === "") return null;
|
|
141
|
-
if (/^(?:https?|mailto|tel):/i.test(href)) return href;
|
|
142
|
-
return /^(?:[./]|#)/.test(href) ? href : null;
|
|
143
|
-
}
|
|
144
139
|
function contextFor(parent, element, readElementStyle = true) {
|
|
145
140
|
const copiedEditorLink = element.classList.contains(editorClassNames.link) ? element.getAttribute("data-href") : null;
|
|
146
141
|
return {
|
|
@@ -217,6 +212,5 @@ export {
|
|
|
217
212
|
appendInline,
|
|
218
213
|
contextFor,
|
|
219
214
|
marksFor,
|
|
220
|
-
safeHref,
|
|
221
215
|
withInlineStyle
|
|
222
216
|
};
|
|
@@ -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;
|