vditor 3.11.3 → 4.0.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/README.md +1 -0
- package/dist/index.css +32 -79
- package/dist/index.js +3762 -3993
- package/dist/index.min.js +1 -1
- package/dist/js/lute/lute.min.js +2 -2
- package/dist/method.js +15 -6
- package/dist/method.min.js +1 -1
- package/dist/ts/sv/index.d.ts +1 -5
- package/dist/ts/sv/process.d.ts +1 -3
- package/dist/ts/upload/getElement.d.ts +1 -1
- package/dist/ts/upload/index.d.ts +2 -0
- package/dist/ts/util/editorCommonEvent.d.ts +1 -1
- package/dist/types/index.d.ts +7 -5
- package/package.json +1 -1
- package/src/assets/less/_sv.less +2 -98
- package/src/assets/less/_wysiwyg.less +30 -0
- package/src/index.ts +45 -14
- package/src/ts/hint/index.ts +9 -4
- package/src/ts/ir/highlightToolbarIR.ts +4 -4
- package/src/ts/markdown/getMarkdown.ts +3 -3
- package/src/ts/sv/index.ts +37 -76
- package/src/ts/sv/process.ts +48 -164
- package/src/ts/sv/processKeydown.ts +19 -186
- package/src/ts/toolbar/EditMode.ts +3 -10
- package/src/ts/toolbar/Emoji.ts +8 -1
- package/src/ts/toolbar/Record.ts +10 -2
- package/src/ts/toolbar/index.ts +3 -0
- package/src/ts/ui/initUI.ts +7 -3
- package/src/ts/undo/index.ts +32 -17
- package/src/ts/upload/index.ts +42 -10
- package/src/ts/util/editorCommonEvent.ts +18 -9
- package/src/ts/util/fixBrowserBehavior.ts +106 -36
- package/src/ts/util/selection.ts +11 -3
- package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +53 -1
- package/src/ts/wysiwyg/processKeydown.ts +47 -0
- package/dist/ts/sv/combineFootnote.d.ts +0 -6
- package/dist/ts/sv/inputEvent.d.ts +0 -1
- package/src/ts/sv/combineFootnote.ts +0 -27
- package/src/ts/sv/inputEvent.ts +0 -188
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import {isCtrl} from "../util/compatibility";
|
|
2
|
-
import {
|
|
3
|
-
import {hasClosestByAttribute} from "../util/hasClosest";
|
|
4
|
-
import {hasClosestByTag} from "../util/hasClosestByHeadings";
|
|
5
|
-
import {getEditorRange, getSelectPosition} from "../util/selection";
|
|
6
|
-
import {inputEvent} from "./inputEvent";
|
|
7
|
-
import {processAfterRender, processPreviousMarkers} from "./process";
|
|
2
|
+
import {processAfterRender} from "./process";
|
|
8
3
|
|
|
9
4
|
export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
|
|
10
5
|
vditor.sv.composingLock = event.isComposing;
|
|
@@ -16,187 +11,25 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
|
|
|
16
11
|
event.key !== "Shift" && event.key !== "CapsLock" && event.key !== "Escape" && !/^F\d{1,2}$/.test(event.key)) {
|
|
17
12
|
vditor.undo.recordFirstPosition(vditor, event);
|
|
18
13
|
}
|
|
19
|
-
// 仅处理以下快捷键操作
|
|
20
|
-
if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
|
|
21
|
-
&& !isCtrl(event) && event.key !== "Escape") {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
const range = getEditorRange(vditor);
|
|
25
|
-
let startContainer = range.startContainer;
|
|
26
|
-
if (range.startContainer.nodeType !== 3 && (range.startContainer as HTMLElement).tagName === "DIV") {
|
|
27
|
-
startContainer = range.startContainer.childNodes[range.startOffset - 1];
|
|
28
|
-
}
|
|
29
|
-
const textElement = hasClosestByAttribute(startContainer, "data-type", "text");
|
|
30
|
-
|
|
31
|
-
// blockquote
|
|
32
|
-
let blockquoteMarkerElement = hasClosestByAttribute(startContainer, "data-type", "blockquote-marker");
|
|
33
|
-
if (!blockquoteMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
|
|
34
|
-
textElement.previousElementSibling.getAttribute("data-type") === "blockquote-marker") {
|
|
35
|
-
blockquoteMarkerElement = textElement.previousElementSibling as HTMLElement;
|
|
36
|
-
}
|
|
37
|
-
// 回车逐个删除 blockquote marker 标记
|
|
38
|
-
if (blockquoteMarkerElement) {
|
|
39
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey &&
|
|
40
|
-
blockquoteMarkerElement.nextElementSibling.textContent.trim() === "" &&
|
|
41
|
-
getSelectPosition(blockquoteMarkerElement, vditor.sv.element, range).start ===
|
|
42
|
-
blockquoteMarkerElement.textContent.length) {
|
|
43
|
-
if (blockquoteMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
|
|
44
|
-
// 列表中存在多行 BQ 时,标记回车需跳出列表
|
|
45
|
-
blockquoteMarkerElement.previousElementSibling.setAttribute("data-action", "enter-remove");
|
|
46
|
-
}
|
|
47
|
-
blockquoteMarkerElement.remove();
|
|
48
|
-
processAfterRender(vditor);
|
|
49
|
-
event.preventDefault();
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
14
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const taskMarkerElement = hasClosestByAttribute(startContainer, "data-type", "task-marker") as HTMLElement;
|
|
57
|
-
let listLastMarkerElement = listMarkerElement;
|
|
58
|
-
if (!listLastMarkerElement) {
|
|
59
|
-
if (taskMarkerElement && taskMarkerElement.nextElementSibling.getAttribute("data-type") !== "task-marker") {
|
|
60
|
-
listLastMarkerElement = taskMarkerElement;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!listLastMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
|
|
64
|
-
(textElement.previousElementSibling.getAttribute("data-type") === "li-marker" ||
|
|
65
|
-
textElement.previousElementSibling.getAttribute("data-type") === "task-marker")) {
|
|
66
|
-
listLastMarkerElement = textElement.previousElementSibling as HTMLElement;
|
|
67
|
-
}
|
|
68
|
-
if (listLastMarkerElement) {
|
|
69
|
-
const startIndex = getSelectPosition(listLastMarkerElement, vditor.sv.element, range).start;
|
|
70
|
-
const isTask = listLastMarkerElement.getAttribute("data-type") === "task-marker";
|
|
71
|
-
let listFirstMarkerElement = listLastMarkerElement;
|
|
72
|
-
if (isTask) {
|
|
73
|
-
listFirstMarkerElement = listLastMarkerElement.previousElementSibling.previousElementSibling
|
|
74
|
-
.previousElementSibling as HTMLElement;
|
|
75
|
-
}
|
|
76
|
-
if (startIndex === listLastMarkerElement.textContent.length) {
|
|
77
|
-
// 回车清空列表标记符
|
|
78
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey &&
|
|
79
|
-
listLastMarkerElement.nextElementSibling.textContent.trim() === "") {
|
|
80
|
-
if (listFirstMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
|
|
81
|
-
listFirstMarkerElement.previousElementSibling.remove();
|
|
82
|
-
inputEvent(vditor);
|
|
83
|
-
} else {
|
|
84
|
-
if (isTask) {
|
|
85
|
-
listFirstMarkerElement.remove();
|
|
86
|
-
listLastMarkerElement.previousElementSibling.previousElementSibling.remove();
|
|
87
|
-
listLastMarkerElement.previousElementSibling.remove();
|
|
88
|
-
}
|
|
89
|
-
listLastMarkerElement.nextElementSibling.remove();
|
|
90
|
-
listLastMarkerElement.remove();
|
|
91
|
-
processAfterRender(vditor);
|
|
92
|
-
}
|
|
93
|
-
event.preventDefault();
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
// 第一个 marker 后 tab 进行缩进
|
|
97
|
-
if (event.key === "Tab") {
|
|
98
|
-
if (event.shiftKey) {
|
|
99
|
-
if (listFirstMarkerElement.previousElementSibling.getAttribute("data-type") === "padding") {
|
|
100
|
-
listFirstMarkerElement.previousElementSibling.remove();
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
listFirstMarkerElement.insertAdjacentHTML("beforebegin",
|
|
104
|
-
`<span data-type="padding">${listFirstMarkerElement.textContent.replace(/\S/g, " ")}</span>`);
|
|
105
|
-
}
|
|
106
|
-
if (/^\d/.test(listFirstMarkerElement.textContent)) {
|
|
107
|
-
listFirstMarkerElement.textContent = listFirstMarkerElement.textContent.replace(/^\d{1,}/, "1");
|
|
108
|
-
range.selectNodeContents(listLastMarkerElement.firstChild);
|
|
109
|
-
range.collapse(false);
|
|
110
|
-
}
|
|
111
|
-
inputEvent(vditor);
|
|
112
|
-
event.preventDefault();
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// tab
|
|
119
|
-
if (fixTab(vditor, range, event)) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
|
|
124
|
-
const spanElement = hasClosestByTag(startContainer, "SPAN");
|
|
125
|
-
// 回车
|
|
126
|
-
if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey && blockElement) {
|
|
127
|
-
let isFirst = false;
|
|
128
|
-
const newLineMatch = blockElement.textContent.match(/^\n+/);
|
|
129
|
-
if (getSelectPosition(blockElement, vditor.sv.element).start <= (newLineMatch ? newLineMatch[0].length : 0)) {
|
|
130
|
-
// 允许段落开始换行
|
|
131
|
-
isFirst = true;
|
|
132
|
-
}
|
|
133
|
-
let newLineText = "\n";
|
|
134
|
-
if (spanElement) {
|
|
135
|
-
if (spanElement.previousElementSibling?.getAttribute("data-action") === "enter-remove") {
|
|
136
|
-
// https://github.com/Vanessa219/vditor/issues/596
|
|
137
|
-
spanElement.previousElementSibling.remove();
|
|
138
|
-
processAfterRender(vditor);
|
|
139
|
-
event.preventDefault();
|
|
140
|
-
return true;
|
|
141
|
-
} else {
|
|
142
|
-
newLineText += processPreviousMarkers(spanElement);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
range.insertNode(document.createTextNode(newLineText));
|
|
146
|
-
range.collapse(false);
|
|
147
|
-
if (blockElement && blockElement.textContent.trim() !== "" && !isFirst) {
|
|
148
|
-
inputEvent(vditor);
|
|
149
|
-
} else {
|
|
150
|
-
processAfterRender(vditor);
|
|
151
|
-
}
|
|
152
|
-
event.preventDefault();
|
|
153
|
-
return true;
|
|
15
|
+
if (event.key !== "Tab" || isCtrl(event) || event.altKey) {
|
|
16
|
+
return false;
|
|
154
17
|
}
|
|
155
18
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
// 每一段第一个字符前
|
|
174
|
-
if (blockElement && getSelectPosition(blockElement, vditor.sv.element, range).start === 0 &&
|
|
175
|
-
blockElement.previousElementSibling) {
|
|
176
|
-
range.extractContents();
|
|
177
|
-
let previousLastElement = blockElement.previousElementSibling.lastElementChild;
|
|
178
|
-
if (previousLastElement.getAttribute("data-type") === "newline") {
|
|
179
|
-
previousLastElement.remove();
|
|
180
|
-
previousLastElement = blockElement.previousElementSibling.lastElementChild;
|
|
181
|
-
}
|
|
182
|
-
// 场景:末尾无法删除 [```\ntext\n```\n\n]
|
|
183
|
-
if (previousLastElement.getAttribute("data-type") !== "newline") {
|
|
184
|
-
previousLastElement.insertAdjacentHTML("afterend", blockElement.innerHTML);
|
|
185
|
-
blockElement.remove();
|
|
186
|
-
}
|
|
187
|
-
if (blockElement.textContent.trim() !== "" && !blockElement.previousElementSibling?.querySelector('[data-type="code-block-open-marker"]')) {
|
|
188
|
-
inputEvent(vditor);
|
|
189
|
-
} else {
|
|
190
|
-
if (previousLastElement.getAttribute("data-type") !== "newline") {
|
|
191
|
-
// https://github.com/Vanessa219/vditor/issues/597
|
|
192
|
-
range.selectNodeContents(previousLastElement.lastChild);
|
|
193
|
-
range.collapse(false);
|
|
194
|
-
}
|
|
195
|
-
processAfterRender(vditor);
|
|
196
|
-
}
|
|
197
|
-
event.preventDefault();
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return false;
|
|
19
|
+
const element = vditor.sv.element;
|
|
20
|
+
const start = element.selectionStart;
|
|
21
|
+
const end = element.selectionEnd;
|
|
22
|
+
if (event.shiftKey) {
|
|
23
|
+
const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
|
|
24
|
+
const indentation = element.value.substring(lineStart, start).match(/^(\t| {1,4})/);
|
|
25
|
+
if (indentation) {
|
|
26
|
+
element.setRangeText("", lineStart, lineStart + indentation[0].length, "end");
|
|
27
|
+
element.setSelectionRange(start - indentation[0].length, end - indentation[0].length);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
element.setRangeText(vditor.options.tab, start, end, "end");
|
|
31
|
+
}
|
|
32
|
+
processAfterRender(vditor);
|
|
33
|
+
event.preventDefault();
|
|
34
|
+
return true;
|
|
202
35
|
};
|
|
@@ -2,7 +2,7 @@ import {Constants} from "../constants";
|
|
|
2
2
|
import {processAfterRender} from "../ir/process";
|
|
3
3
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
4
4
|
import {mathRender} from "../markdown/mathRender";
|
|
5
|
-
import {processAfterRender as processSVAfterRender
|
|
5
|
+
import {processAfterRender as processSVAfterRender} from "../sv/process";
|
|
6
6
|
import {setPadding, setTypewriterPosition} from "../ui/initUI";
|
|
7
7
|
import {getEventName, updateHotkeyTip} from "../util/compatibility";
|
|
8
8
|
import {highlightToolbar} from "../util/highlightToolbar";
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
removeCurrentToolbar,
|
|
19
19
|
showToolbar, toggleSubMenu,
|
|
20
20
|
} from "./setToolbar";
|
|
21
|
-
import {combineFootnote} from "../sv/combineFootnote";
|
|
22
21
|
|
|
23
22
|
export const setEditMode = (vditor: IVditor, type: string, event: Event | string) => {
|
|
24
23
|
let markdownText;
|
|
@@ -114,16 +113,10 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
|
|
|
114
113
|
|
|
115
114
|
vditor.lute.SetVditorIR(false);
|
|
116
115
|
vditor.lute.SetVditorWYSIWYG(false);
|
|
117
|
-
vditor.lute.SetVditorSV(
|
|
116
|
+
vditor.lute.SetVditorSV(false);
|
|
118
117
|
|
|
119
118
|
vditor.currentMode = "sv";
|
|
120
|
-
|
|
121
|
-
if (svHTML === "<div data-block='0'></div>") {
|
|
122
|
-
// https://github.com/Vanessa219/vditor/issues/654 SV 模式 Placeholder 显示问题
|
|
123
|
-
svHTML = "";
|
|
124
|
-
}
|
|
125
|
-
vditor.sv.element.innerHTML = svHTML;
|
|
126
|
-
combineFootnote(vditor.sv.element)
|
|
119
|
+
vditor.sv.element.value = markdownText;
|
|
127
120
|
processSVAfterRender(vditor, {
|
|
128
121
|
enableAddUndoStack: true,
|
|
129
122
|
enableHint: false,
|
package/src/ts/toolbar/Emoji.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {MenuItem} from "./MenuItem";
|
|
|
6
6
|
import {toggleSubMenu} from "./setToolbar";
|
|
7
7
|
import {hasClosestBlock} from "../util/hasClosest";
|
|
8
8
|
import {modifyPre} from "../wysiwyg/inlineTag";
|
|
9
|
+
import {processPaste} from "../sv/process";
|
|
9
10
|
|
|
10
11
|
export class Emoji extends MenuItem {
|
|
11
12
|
public element: HTMLElement;
|
|
@@ -44,6 +45,12 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
|
|
|
44
45
|
if (btnElement) {
|
|
45
46
|
event.preventDefault();
|
|
46
47
|
const value = btnElement.getAttribute("data-value");
|
|
48
|
+
if (vditor.currentMode === "sv") {
|
|
49
|
+
processPaste(vditor, value);
|
|
50
|
+
(this.element.lastElementChild as HTMLElement).style.display = "none";
|
|
51
|
+
execAfterRender(vditor);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
47
54
|
const range = getEditorRange(vditor);
|
|
48
55
|
let html = value;
|
|
49
56
|
if (vditor.currentMode === "wysiwyg") {
|
|
@@ -51,7 +58,7 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
|
|
|
51
58
|
} else if (vditor.currentMode === "ir") {
|
|
52
59
|
html = vditor.lute.SpinVditorIRDOM(value);
|
|
53
60
|
}
|
|
54
|
-
if (value.indexOf(":") > -1
|
|
61
|
+
if (value.indexOf(":") > -1) {
|
|
55
62
|
const tempElement = document.createElement("div");
|
|
56
63
|
tempElement.innerHTML = html;
|
|
57
64
|
html = tempElement.firstElementChild.firstElementChild.outerHTML + " ";
|
package/src/ts/toolbar/Record.ts
CHANGED
|
@@ -34,7 +34,11 @@ export class Record extends MenuItem {
|
|
|
34
34
|
};
|
|
35
35
|
mediaRecorder.startRecordingNewWavFile();
|
|
36
36
|
vditor.tip.show(window.VditorI18n.recording);
|
|
37
|
-
|
|
37
|
+
if (vditor.currentMode === "sv") {
|
|
38
|
+
vditor.sv.element.disabled = true;
|
|
39
|
+
} else {
|
|
40
|
+
editorElement.setAttribute("contenteditable", "false");
|
|
41
|
+
}
|
|
38
42
|
this.element.children[0].classList.add("vditor-menu--current");
|
|
39
43
|
}).catch(() => {
|
|
40
44
|
vditor.tip.show(window.VditorI18n["record-tip"]);
|
|
@@ -51,7 +55,11 @@ export class Record extends MenuItem {
|
|
|
51
55
|
this.element.children[0].classList.remove("vditor-menu--current");
|
|
52
56
|
} else {
|
|
53
57
|
vditor.tip.show(window.VditorI18n.recording);
|
|
54
|
-
|
|
58
|
+
if (vditor.currentMode === "sv") {
|
|
59
|
+
vditor.sv.element.disabled = true;
|
|
60
|
+
} else {
|
|
61
|
+
editorElement.setAttribute("contenteditable", "false");
|
|
62
|
+
}
|
|
55
63
|
mediaRecorder.startRecordingNewWavFile();
|
|
56
64
|
this.element.children[0].classList.add("vditor-menu--current");
|
|
57
65
|
}
|
package/src/ts/toolbar/index.ts
CHANGED
|
@@ -181,6 +181,9 @@ export class Toolbar {
|
|
|
181
181
|
if (!menuItemObj) {
|
|
182
182
|
return;
|
|
183
183
|
}
|
|
184
|
+
menuItemObj.element.querySelectorAll("button").forEach((button) => {
|
|
185
|
+
button.setAttribute("type", "button");
|
|
186
|
+
});
|
|
184
187
|
let key = menuItem.name;
|
|
185
188
|
if (key === "br" || key === "|") {
|
|
186
189
|
key = key + index;
|
package/src/ts/ui/initUI.ts
CHANGED
|
@@ -146,9 +146,13 @@ export const setTypewriterPosition = (vditor: IVditor) => {
|
|
|
146
146
|
if (vditor.element.classList.contains("vditor--fullscreen")) {
|
|
147
147
|
height = window.innerHeight;
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
vditor
|
|
151
|
-
|
|
149
|
+
const editorBottom = ((height - vditor.toolbar.element.offsetHeight) / 2) + "px";
|
|
150
|
+
if (vditor.currentMode === "sv") {
|
|
151
|
+
vditor.sv.element.style.paddingBottom = editorBottom;
|
|
152
|
+
} else {
|
|
153
|
+
// 由于 Firefox padding-bottom bug,只能使用 :after
|
|
154
|
+
vditor[vditor.currentMode].element.style.setProperty("--editor-bottom", editorBottom);
|
|
155
|
+
}
|
|
152
156
|
};
|
|
153
157
|
|
|
154
158
|
let resizeCb: () => void;
|
package/src/ts/undo/index.ts
CHANGED
|
@@ -52,7 +52,8 @@ class Undo {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
public undo(vditor: IVditor) {
|
|
55
|
-
if (vditor
|
|
55
|
+
if (vditor.currentMode === "sv" ? vditor.sv.element.disabled :
|
|
56
|
+
vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
|
|
56
57
|
return;
|
|
57
58
|
}
|
|
58
59
|
if (this[vditor.currentMode].undoStack.length < 2) {
|
|
@@ -70,7 +71,8 @@ class Undo {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
public redo(vditor: IVditor) {
|
|
73
|
-
if (vditor
|
|
74
|
+
if (vditor.currentMode === "sv" ? vditor.sv.element.disabled :
|
|
75
|
+
vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
76
78
|
const state = this[vditor.currentMode].redoStack.pop();
|
|
@@ -82,7 +84,7 @@ class Undo {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
public recordFirstPosition(vditor: IVditor, event: KeyboardEvent) {
|
|
85
|
-
if (getSelection().rangeCount === 0) {
|
|
87
|
+
if (vditor.currentMode !== "sv" && getSelection().rangeCount === 0) {
|
|
86
88
|
return;
|
|
87
89
|
}
|
|
88
90
|
if (this[vditor.currentMode].undoStack.length !== 1 || this[vditor.currentMode].undoStack[0].length === 0 ||
|
|
@@ -147,8 +149,15 @@ class Undo {
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
this[vditor.currentMode].lastText = text;
|
|
150
|
-
vditor
|
|
151
|
-
|
|
152
|
+
if (vditor.currentMode === "sv") {
|
|
153
|
+
const caretIndex = text.indexOf("<wbr>");
|
|
154
|
+
vditor.sv.element.value = text.replace("<wbr>", "");
|
|
155
|
+
vditor.sv.element.focus();
|
|
156
|
+
if (caretIndex > -1) {
|
|
157
|
+
vditor.sv.element.setSelectionRange(caretIndex, caretIndex);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
vditor[vditor.currentMode].element.innerHTML = text;
|
|
152
161
|
vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview`)
|
|
153
162
|
.forEach((blockElement: HTMLElement) => {
|
|
154
163
|
if (blockElement.parentElement.querySelector(".language-echarts")) {
|
|
@@ -163,17 +172,16 @@ class Undo {
|
|
|
163
172
|
.forEach((blockElement: HTMLElement) => {
|
|
164
173
|
processCodeRender(blockElement, vditor);
|
|
165
174
|
});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
scrollCenter(vditor);
|
|
175
|
+
if (!vditor[vditor.currentMode].element.querySelector("wbr")) {
|
|
176
|
+
// Safari 第一次输入没有光标,需手动定位到结尾
|
|
177
|
+
const range = getSelection().getRangeAt(0);
|
|
178
|
+
range.setEndBefore(vditor[vditor.currentMode].element);
|
|
179
|
+
range.collapse(false);
|
|
180
|
+
} else {
|
|
181
|
+
setRangeByWbr(
|
|
182
|
+
vditor[vditor.currentMode].element, vditor[vditor.currentMode].element.ownerDocument.createRange());
|
|
183
|
+
scrollCenter(vditor);
|
|
184
|
+
}
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
renderToc(vditor);
|
|
@@ -183,7 +191,9 @@ class Undo {
|
|
|
183
191
|
enableHint: false,
|
|
184
192
|
enableInput: true,
|
|
185
193
|
});
|
|
186
|
-
|
|
194
|
+
if (vditor.currentMode !== "sv") {
|
|
195
|
+
highlightToolbar(vditor);
|
|
196
|
+
}
|
|
187
197
|
|
|
188
198
|
vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
|
|
189
199
|
.forEach((item: HTMLElement) => {
|
|
@@ -225,6 +235,11 @@ class Undo {
|
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
private addCaret(vditor: IVditor, setFocus = false) {
|
|
238
|
+
if (vditor.currentMode === "sv") {
|
|
239
|
+
const element = vditor.sv.element;
|
|
240
|
+
return element.value.substring(0, element.selectionStart) + "<wbr>" +
|
|
241
|
+
element.value.substring(element.selectionStart);
|
|
242
|
+
}
|
|
228
243
|
let cloneRange: Range;
|
|
229
244
|
if (getSelection().rangeCount !== 0 && !vditor[vditor.currentMode].element.querySelector("wbr")) {
|
|
230
245
|
const range = getSelection().getRangeAt(0);
|
package/src/ts/upload/index.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {getEditorRange, setSelectionFocus} from "../util/selection";
|
|
2
|
+
import {processAfterRender as processSVAfterRender, processPaste} from "../sv/process";
|
|
3
|
+
import {getElement} from "./getElement";
|
|
4
|
+
import {setHeaders} from "./setHeaders";
|
|
4
5
|
|
|
5
6
|
class Upload {
|
|
6
7
|
public element: HTMLElement;
|
|
7
8
|
public isUploading: boolean;
|
|
8
9
|
public range: Range;
|
|
10
|
+
public selectionEnd: number;
|
|
11
|
+
public selectionStart: number;
|
|
9
12
|
|
|
10
13
|
constructor() {
|
|
11
14
|
this.isUploading = false;
|
|
@@ -146,9 +149,25 @@ const genUploadedLabel = (responseText: string, vditor: IVditor) => {
|
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
if (!succFileText) {
|
|
153
|
+
if (vditor.currentMode === "sv") {
|
|
154
|
+
vditor.sv.element.setSelectionRange(vditor.upload.selectionStart, vditor.upload.selectionEnd);
|
|
155
|
+
} else {
|
|
156
|
+
setSelectionFocus(vditor.upload.range);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (vditor.currentMode === "sv") {
|
|
161
|
+
vditor.sv.element.setSelectionRange(vditor.upload.selectionStart, vditor.upload.selectionEnd);
|
|
162
|
+
processPaste(vditor, succFileText);
|
|
163
|
+
processSVAfterRender(vditor);
|
|
164
|
+
vditor.upload.selectionStart = vditor.sv.element.selectionStart;
|
|
165
|
+
vditor.upload.selectionEnd = vditor.sv.element.selectionEnd;
|
|
166
|
+
} else {
|
|
167
|
+
setSelectionFocus(vditor.upload.range);
|
|
168
|
+
document.execCommand("insertHTML", false, succFileText);
|
|
169
|
+
vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
|
|
170
|
+
}
|
|
152
171
|
};
|
|
153
172
|
|
|
154
173
|
const uploadFiles =
|
|
@@ -197,7 +216,12 @@ const uploadFiles =
|
|
|
197
216
|
}
|
|
198
217
|
const editorElement = getElement(vditor);
|
|
199
218
|
|
|
200
|
-
vditor.
|
|
219
|
+
if (vditor.currentMode === "sv") {
|
|
220
|
+
vditor.upload.selectionStart = vditor.sv.element.selectionStart;
|
|
221
|
+
vditor.upload.selectionEnd = vditor.sv.element.selectionEnd;
|
|
222
|
+
} else {
|
|
223
|
+
vditor.upload.range = getEditorRange(vditor);
|
|
224
|
+
}
|
|
201
225
|
|
|
202
226
|
const validateResult = validateFile(vditor, fileList);
|
|
203
227
|
if (validateResult.length === 0) {
|
|
@@ -229,11 +253,19 @@ const uploadFiles =
|
|
|
229
253
|
}
|
|
230
254
|
setHeaders(vditor, xhr);
|
|
231
255
|
vditor.upload.isUploading = true;
|
|
232
|
-
|
|
256
|
+
if (vditor.currentMode === "sv") {
|
|
257
|
+
vditor.sv.element.disabled = true;
|
|
258
|
+
} else {
|
|
259
|
+
editorElement.setAttribute("contenteditable", "false");
|
|
260
|
+
}
|
|
233
261
|
xhr.onreadystatechange = () => {
|
|
234
262
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
235
263
|
vditor.upload.isUploading = false;
|
|
236
|
-
|
|
264
|
+
if (vditor.currentMode === "sv") {
|
|
265
|
+
vditor.sv.element.disabled = false;
|
|
266
|
+
} else {
|
|
267
|
+
editorElement.setAttribute("contenteditable", "true");
|
|
268
|
+
}
|
|
237
269
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
238
270
|
if (vditor.options.upload.success) {
|
|
239
271
|
vditor.options.upload.success(editorElement, xhr.responseText);
|
|
@@ -270,4 +302,4 @@ const uploadFiles =
|
|
|
270
302
|
xhr.send(formData);
|
|
271
303
|
};
|
|
272
304
|
|
|
273
|
-
export {
|
|
305
|
+
export {Upload, uploadFiles};
|
|
@@ -45,18 +45,22 @@ export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
|
|
|
45
45
|
if (expandElement) {
|
|
46
46
|
expandElement.classList.remove("vditor-ir__node--expand");
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
vditor.wysiwyg.
|
|
48
|
+
vditor.ir.range = getEditorRange(vditor);
|
|
49
|
+
} else if (vditor.currentMode === "wysiwyg") {
|
|
50
|
+
if (!vditor.wysiwyg.selectPopover.contains(event.relatedTarget as HTMLElement)) {
|
|
51
|
+
vditor.wysiwyg.hideComment();
|
|
52
|
+
}
|
|
53
|
+
vditor.wysiwyg.range = getEditorRange(vditor);
|
|
51
54
|
}
|
|
52
|
-
vditor[vditor.currentMode].range = getEditorRange(vditor);
|
|
53
55
|
if (vditor.options.blur) {
|
|
54
56
|
vditor.options.blur(getMarkdown(vditor));
|
|
55
57
|
}
|
|
56
58
|
});
|
|
57
59
|
};
|
|
58
60
|
|
|
59
|
-
export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
|
|
61
|
+
export const dropEvent = (vditor: IVditor, editorElement: HTMLElement, pasteCode = (code: string) => {
|
|
62
|
+
document.execCommand("insertHTML", false, code);
|
|
63
|
+
}) => {
|
|
60
64
|
editorElement.addEventListener("dragstart", (event) => {
|
|
61
65
|
// 选中编辑器中的文字进行拖拽
|
|
62
66
|
event.dataTransfer.setData(Constants.DROP_EDITOR, Constants.DROP_EDITOR);
|
|
@@ -69,9 +73,7 @@ export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
|
|
|
69
73
|
} else if (event.dataTransfer.types.includes("Files") || event.dataTransfer.types.includes("text/html")) {
|
|
70
74
|
// 外部文件拖入编辑器中或者编辑器内选中文字拖拽
|
|
71
75
|
paste(vditor, event, {
|
|
72
|
-
pasteCode
|
|
73
|
-
document.execCommand("insertHTML", false, code);
|
|
74
|
-
},
|
|
76
|
+
pasteCode,
|
|
75
77
|
});
|
|
76
78
|
}
|
|
77
79
|
});
|
|
@@ -102,7 +104,14 @@ export const scrollCenter = (vditor: IVditor) => {
|
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
104
106
|
const editorElement = vditor[vditor.currentMode].element;
|
|
105
|
-
|
|
107
|
+
let cursorTop: number;
|
|
108
|
+
if (vditor.currentMode === "sv") {
|
|
109
|
+
const lineHeight = parseInt(getComputedStyle(vditor.sv.element).lineHeight, 10) || 22;
|
|
110
|
+
cursorTop = (vditor.sv.element.value.substring(0, vditor.sv.element.selectionStart).split("\n").length - 1) *
|
|
111
|
+
lineHeight;
|
|
112
|
+
} else {
|
|
113
|
+
cursorTop = getCursorPosition(editorElement).top;
|
|
114
|
+
}
|
|
106
115
|
if (vditor.options.height === "auto" && !vditor.element.classList.contains("vditor--fullscreen")) {
|
|
107
116
|
window.scrollTo(window.scrollX,
|
|
108
117
|
cursorTop + vditor.element.offsetTop + vditor.toolbar.element.offsetHeight - window.innerHeight / 2 + 10);
|