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
package/src/index.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {Outline} from "./ts/outline/index";
|
|
|
13
13
|
import {Preview} from "./ts/preview/index";
|
|
14
14
|
import {Resize} from "./ts/resize/index";
|
|
15
15
|
import {Editor} from "./ts/sv/index";
|
|
16
|
-
import {inputEvent} from "./ts/sv/inputEvent";
|
|
17
16
|
import {processAfterRender as processSVAfterRender, processPaste} from "./ts/sv/process";
|
|
18
17
|
import {Tip} from "./ts/tip/index";
|
|
19
18
|
import {Toolbar} from "./ts/toolbar/index";
|
|
@@ -167,10 +166,11 @@ class Vditor extends VditorMethod {
|
|
|
167
166
|
this.vditor.toolbar.elements,
|
|
168
167
|
Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", "edit-mode"]),
|
|
169
168
|
);
|
|
170
|
-
this.vditor
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
if (this.vditor.currentMode === "sv") {
|
|
170
|
+
this.vditor.sv.element.disabled = true;
|
|
171
|
+
} else {
|
|
172
|
+
this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "false");
|
|
173
|
+
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/** 解除编辑器禁用 */
|
|
@@ -180,7 +180,11 @@ class Vditor extends VditorMethod {
|
|
|
180
180
|
Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", "edit-mode"]),
|
|
181
181
|
);
|
|
182
182
|
this.vditor.undo.resetIcon(this.vditor);
|
|
183
|
-
this.vditor
|
|
183
|
+
if (this.vditor.currentMode === "sv") {
|
|
184
|
+
this.vditor.sv.element.disabled = false;
|
|
185
|
+
} else {
|
|
186
|
+
this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "true");
|
|
187
|
+
}
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
/** 返回选中的字符串 */
|
|
@@ -188,7 +192,10 @@ class Vditor extends VditorMethod {
|
|
|
188
192
|
if (this.vditor.currentMode === "wysiwyg") {
|
|
189
193
|
return getSelectText(this.vditor.wysiwyg.element);
|
|
190
194
|
} else if (this.vditor.currentMode === "sv") {
|
|
191
|
-
return
|
|
195
|
+
return this.vditor.sv.element.value.substring(
|
|
196
|
+
this.vditor.sv.element.selectionStart,
|
|
197
|
+
this.vditor.sv.element.selectionEnd,
|
|
198
|
+
);
|
|
192
199
|
} else if (this.vditor.currentMode === "ir") {
|
|
193
200
|
return getSelectText(this.vditor.ir.element);
|
|
194
201
|
}
|
|
@@ -201,6 +208,15 @@ class Vditor extends VditorMethod {
|
|
|
201
208
|
|
|
202
209
|
/** 获取焦点位置 */
|
|
203
210
|
public getCursorPosition() {
|
|
211
|
+
if (this.vditor.currentMode === "sv") {
|
|
212
|
+
const element = this.vditor.sv.element;
|
|
213
|
+
const lineHeight = parseInt(getComputedStyle(element).lineHeight, 10) || 22;
|
|
214
|
+
const line = element.value.substring(0, element.selectionStart).split("\n").length - 1;
|
|
215
|
+
return {
|
|
216
|
+
left: 0,
|
|
217
|
+
top: line * lineHeight - element.scrollTop,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
204
220
|
return getCursorPosition(this.vditor[this.vditor.currentMode].element);
|
|
205
221
|
}
|
|
206
222
|
|
|
@@ -258,6 +274,14 @@ class Vditor extends VditorMethod {
|
|
|
258
274
|
|
|
259
275
|
/** 删除选中内容 */
|
|
260
276
|
public deleteValue() {
|
|
277
|
+
if (this.vditor.currentMode === "sv") {
|
|
278
|
+
if (this.vditor.sv.element.selectionStart === this.vditor.sv.element.selectionEnd) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
processPaste(this.vditor, "");
|
|
282
|
+
processSVAfterRender(this.vditor);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
261
285
|
if (window.getSelection().isCollapsed) {
|
|
262
286
|
return;
|
|
263
287
|
}
|
|
@@ -266,11 +290,23 @@ class Vditor extends VditorMethod {
|
|
|
266
290
|
|
|
267
291
|
/** 更新选中内容 */
|
|
268
292
|
public updateValue(value: string) {
|
|
293
|
+
if (this.vditor.currentMode === "sv") {
|
|
294
|
+
processPaste(this.vditor, value);
|
|
295
|
+
processSVAfterRender(this.vditor);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
269
298
|
document.execCommand("insertHTML", false, value);
|
|
270
299
|
}
|
|
271
300
|
|
|
272
301
|
/** 在焦点处插入内容,并默认进行 Markdown 渲染 */
|
|
273
302
|
public insertValue(value: string, render = true) {
|
|
303
|
+
if (this.vditor.currentMode === "sv") {
|
|
304
|
+
processPaste(this.vditor, value);
|
|
305
|
+
if (render) {
|
|
306
|
+
processSVAfterRender(this.vditor);
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
274
310
|
const range = getEditorRange(this.vditor);
|
|
275
311
|
range.collapse(true);
|
|
276
312
|
// https://github.com/Vanessa219/vditor/issues/716
|
|
@@ -279,12 +315,7 @@ class Vditor extends VditorMethod {
|
|
|
279
315
|
tmpElement.innerHTML = value;
|
|
280
316
|
range.insertNode(tmpElement.content.cloneNode(true));
|
|
281
317
|
range.collapse(false);
|
|
282
|
-
if (this.vditor.currentMode === "
|
|
283
|
-
this.vditor.sv.preventInput = true;
|
|
284
|
-
if (render) {
|
|
285
|
-
inputEvent(this.vditor);
|
|
286
|
-
}
|
|
287
|
-
} else if (this.vditor.currentMode === "wysiwyg") {
|
|
318
|
+
if (this.vditor.currentMode === "wysiwyg") {
|
|
288
319
|
// 由于 https://github.com/Vanessa219/vditor/issues/1566 不能使用 this.vditor.wysiwyg.preventInput = true;
|
|
289
320
|
if (render) {
|
|
290
321
|
input(this.vditor, getSelection().getRangeAt(0));
|
|
@@ -314,7 +345,7 @@ class Vditor extends VditorMethod {
|
|
|
314
345
|
/** 设置编辑器内容 */
|
|
315
346
|
public setValue(markdown: string, clearStack = false) {
|
|
316
347
|
if (this.vditor.currentMode === "sv") {
|
|
317
|
-
this.vditor.sv.element.
|
|
348
|
+
this.vditor.sv.element.value = markdown;
|
|
318
349
|
processSVAfterRender(this.vditor, {
|
|
319
350
|
enableAddUndoStack: true,
|
|
320
351
|
enableHint: false,
|
package/src/ts/hint/index.ts
CHANGED
|
@@ -23,6 +23,10 @@ export class Hint {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
public render(vditor: IVditor) {
|
|
26
|
+
if (vditor.currentMode === "sv") {
|
|
27
|
+
this.element.style.display = "none";
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
26
30
|
if (!window.getSelection().focusNode) {
|
|
27
31
|
return;
|
|
28
32
|
}
|
|
@@ -128,6 +132,9 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
|
|
|
128
132
|
|
|
129
133
|
public fillEmoji = (element: HTMLElement, vditor: IVditor) => {
|
|
130
134
|
this.element.style.display = "none";
|
|
135
|
+
if (vditor.currentMode === "sv") {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
131
138
|
|
|
132
139
|
const value = decodeURIComponent(element.getAttribute("data-value"));
|
|
133
140
|
const range: Range = window.getSelection().getRangeAt(0);
|
|
@@ -172,9 +179,7 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
|
|
|
172
179
|
range.deleteContents();
|
|
173
180
|
|
|
174
181
|
if (vditor.options.hint.parse) {
|
|
175
|
-
if (vditor.currentMode === "
|
|
176
|
-
insertHTML(vditor.lute.SpinVditorSVDOM(value), vditor);
|
|
177
|
-
} else if (vditor.currentMode === "wysiwyg") {
|
|
182
|
+
if (vditor.currentMode === "wysiwyg") {
|
|
178
183
|
insertHTML(vditor.lute.SpinVditorDOM(value), vditor);
|
|
179
184
|
} else {
|
|
180
185
|
insertHTML(vditor.lute.SpinVditorIRDOM(value), vditor);
|
|
@@ -182,7 +187,7 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
|
|
|
182
187
|
} else {
|
|
183
188
|
insertHTML(value, vditor);
|
|
184
189
|
}
|
|
185
|
-
if (this.splitChar === ":" && value.indexOf(":") > -1
|
|
190
|
+
if (this.splitChar === ":" && value.indexOf(":") > -1) {
|
|
186
191
|
range.insertNode(document.createTextNode(" "));
|
|
187
192
|
}
|
|
188
193
|
range.collapse(false);
|
|
@@ -5,12 +5,12 @@ import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
|
|
|
5
5
|
import {getEditorRange, selectIsEditor} from "../util/selection";
|
|
6
6
|
|
|
7
7
|
export const highlightToolbarIR = (vditor: IVditor) => {
|
|
8
|
-
clearTimeout(vditor
|
|
9
|
-
vditor
|
|
10
|
-
if (vditor
|
|
8
|
+
clearTimeout(vditor.ir.hlToolbarTimeoutId);
|
|
9
|
+
vditor.ir.hlToolbarTimeoutId = window.setTimeout(() => {
|
|
10
|
+
if (vditor.ir.element.getAttribute("contenteditable") === "false") {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
if (!selectIsEditor(vditor
|
|
13
|
+
if (!selectIsEditor(vditor.ir.element)) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -2,11 +2,11 @@ import {code160to32} from "../util/code160to32";
|
|
|
2
2
|
|
|
3
3
|
export const getMarkdown = (vditor: IVditor) => {
|
|
4
4
|
if (vditor.currentMode === "sv") {
|
|
5
|
-
return code160to32(`${vditor.sv.element.
|
|
5
|
+
return code160to32(`${vditor.sv.element.value}\n`.replace(/\n\n$/, "\n"));
|
|
6
6
|
} else if (vditor.currentMode === "wysiwyg") {
|
|
7
|
-
return vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML);
|
|
7
|
+
return code160to32(vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML));
|
|
8
8
|
} else if (vditor.currentMode === "ir") {
|
|
9
|
-
return vditor.lute.VditorIRDOM2Md(vditor.ir.element.innerHTML);
|
|
9
|
+
return code160to32(vditor.lute.VditorIRDOM2Md(vditor.ir.element.innerHTML));
|
|
10
10
|
}
|
|
11
11
|
return "";
|
|
12
12
|
};
|
package/src/ts/sv/index.ts
CHANGED
|
@@ -1,55 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
copyEvent, cutEvent,
|
|
5
|
-
dropEvent,
|
|
6
|
-
focusEvent,
|
|
7
|
-
hotkeyEvent,
|
|
8
|
-
scrollCenter,
|
|
9
|
-
selectEvent,
|
|
10
|
-
} from "../util/editorCommonEvent";
|
|
1
|
+
import {getMarkdown} from "../markdown/getMarkdown";
|
|
2
|
+
import {isFirefox} from "../util/compatibility";
|
|
3
|
+
import {dropEvent, focusEvent, hotkeyEvent} from "../util/editorCommonEvent";
|
|
11
4
|
import {paste} from "../util/fixBrowserBehavior";
|
|
12
|
-
import {
|
|
13
|
-
import {inputEvent} from "./inputEvent";
|
|
14
|
-
import {processAfterRender} from "./process";
|
|
5
|
+
import {processAfterRender, processPaste} from "./process";
|
|
15
6
|
|
|
16
7
|
class Editor {
|
|
17
|
-
public
|
|
18
|
-
public element: HTMLPreElement;
|
|
8
|
+
public element: HTMLTextAreaElement;
|
|
19
9
|
public composingLock: boolean = false;
|
|
20
10
|
public processTimeoutId: number;
|
|
21
|
-
public hlToolbarTimeoutId: number;
|
|
22
|
-
public preventInput: boolean;
|
|
23
11
|
|
|
24
12
|
constructor(vditor: IVditor) {
|
|
25
|
-
this.element = document.createElement("
|
|
13
|
+
this.element = document.createElement("textarea");
|
|
26
14
|
this.element.className = "vditor-sv vditor-reset";
|
|
27
|
-
this.element.
|
|
28
|
-
this.element.
|
|
29
|
-
this.element.setAttribute("spellcheck", "false");
|
|
15
|
+
this.element.placeholder = vditor.options.placeholder;
|
|
16
|
+
this.element.spellcheck = false;
|
|
30
17
|
|
|
31
18
|
this.bindEvent(vditor);
|
|
32
|
-
|
|
33
19
|
focusEvent(vditor, this.element);
|
|
34
|
-
blurEvent(vditor, this.element);
|
|
35
20
|
hotkeyEvent(vditor, this.element);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
cutEvent(vditor, this.element, this.copy);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private copy(event: ClipboardEvent, vditor: IVditor) {
|
|
43
|
-
event.stopPropagation();
|
|
44
|
-
event.preventDefault();
|
|
45
|
-
event.clipboardData.setData("text/plain", getSelectText(vditor[vditor.currentMode].element));
|
|
21
|
+
dropEvent(vditor, this.element, (code: string) => {
|
|
22
|
+
processPaste(vditor, code.replace(/</g, "<").replace(/&/g, "&"));
|
|
23
|
+
});
|
|
46
24
|
}
|
|
47
25
|
|
|
48
26
|
private bindEvent(vditor: IVditor) {
|
|
49
|
-
this.element.addEventListener("
|
|
27
|
+
this.element.addEventListener("blur", () => {
|
|
28
|
+
if (vditor.options.blur) {
|
|
29
|
+
vditor.options.blur(getMarkdown(vditor));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.element.addEventListener("paste", (event: ClipboardEvent & {target: HTMLElement}) => {
|
|
50
34
|
paste(vditor, event, {
|
|
51
35
|
pasteCode: (code: string) => {
|
|
52
|
-
|
|
36
|
+
processPaste(vditor, code.replace(/</g, "<").replace(/&/g, "&"));
|
|
53
37
|
},
|
|
54
38
|
});
|
|
55
39
|
});
|
|
@@ -62,61 +46,38 @@ class Editor {
|
|
|
62
46
|
const textHeight = this.element.clientHeight;
|
|
63
47
|
const textScrollHeight = this.element.scrollHeight - parseFloat(this.element.style.paddingBottom || "0");
|
|
64
48
|
const preview = vditor.preview.element;
|
|
65
|
-
if (
|
|
66
|
-
preview.scrollTop = (textScrollTop + textHeight) *
|
|
67
|
-
preview.scrollHeight / textScrollHeight - textHeight;
|
|
49
|
+
if (textScrollTop / textHeight > 0.5) {
|
|
50
|
+
preview.scrollTop = (textScrollTop + textHeight) * preview.scrollHeight / textScrollHeight - textHeight;
|
|
68
51
|
} else {
|
|
69
|
-
preview.scrollTop = textScrollTop *
|
|
70
|
-
preview.scrollHeight / textScrollHeight;
|
|
52
|
+
preview.scrollTop = textScrollTop * preview.scrollHeight / textScrollHeight;
|
|
71
53
|
}
|
|
72
54
|
});
|
|
73
55
|
|
|
74
|
-
this.element.addEventListener("compositionstart", (
|
|
56
|
+
this.element.addEventListener("compositionstart", () => {
|
|
75
57
|
this.composingLock = true;
|
|
76
58
|
});
|
|
77
59
|
|
|
78
|
-
this.element.addEventListener("compositionend", (
|
|
60
|
+
this.element.addEventListener("compositionend", () => {
|
|
61
|
+
this.composingLock = false;
|
|
79
62
|
if (!isFirefox()) {
|
|
80
|
-
|
|
63
|
+
processAfterRender(vditor);
|
|
81
64
|
}
|
|
82
|
-
this.composingLock = false;
|
|
83
65
|
});
|
|
84
66
|
|
|
85
|
-
this.element.addEventListener("input", (
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (this.composingLock || event.data === "‘" || event.data === "“" || event.data === "《") {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (this.preventInput) {
|
|
94
|
-
this.preventInput = false;
|
|
95
|
-
processAfterRender(vditor, {
|
|
96
|
-
enableAddUndoStack: true,
|
|
97
|
-
enableHint: true,
|
|
98
|
-
enableInput: true,
|
|
99
|
-
});
|
|
100
|
-
return;
|
|
67
|
+
this.element.addEventListener("input", () => {
|
|
68
|
+
if (!this.composingLock) {
|
|
69
|
+
processAfterRender(vditor);
|
|
101
70
|
}
|
|
102
|
-
inputEvent(vditor, event);
|
|
103
71
|
});
|
|
104
72
|
|
|
105
|
-
this.element.addEventListener("
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
&& (vditor.sv.element.firstElementChild.textContent === "" || vditor.sv.element.textContent === "\n")) {
|
|
114
|
-
// 为空时显示 placeholder
|
|
115
|
-
vditor.sv.element.innerHTML = "";
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (event.key === "Enter") {
|
|
119
|
-
scrollCenter(vditor);
|
|
73
|
+
this.element.addEventListener("select", () => {
|
|
74
|
+
const selectedText = this.element.value.substring(this.element.selectionStart, this.element.selectionEnd);
|
|
75
|
+
if (selectedText.trim()) {
|
|
76
|
+
if (vditor.options.select) {
|
|
77
|
+
vditor.options.select(selectedText);
|
|
78
|
+
}
|
|
79
|
+
} else if (vditor.options.unSelect) {
|
|
80
|
+
vditor.options.unSelect();
|
|
120
81
|
}
|
|
121
82
|
});
|
|
122
83
|
}
|
package/src/ts/sv/process.ts
CHANGED
|
@@ -1,104 +1,14 @@
|
|
|
1
1
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
2
2
|
import {accessLocalStorage} from "../util/compatibility";
|
|
3
|
-
import {scrollCenter} from "../util/editorCommonEvent";
|
|
4
|
-
import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
|
|
5
|
-
import {hasClosestByTag} from "../util/hasClosestByHeadings";
|
|
6
|
-
import {log} from "../util/log";
|
|
7
|
-
import {getEditorRange, setRangeByWbr} from "../util/selection";
|
|
8
|
-
import {inputEvent} from "./inputEvent";
|
|
9
|
-
import {combineFootnote} from "./combineFootnote";
|
|
10
3
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
range.insertNode(document.createTextNode(Lute.Caret));
|
|
16
|
-
range.insertNode(document.createTextNode(text));
|
|
17
|
-
let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
|
|
18
|
-
if (!blockElement) {
|
|
19
|
-
blockElement = vditor.sv.element;
|
|
20
|
-
}
|
|
21
|
-
let spinHTML = vditor.lute.SpinVditorSVDOM(blockElement.textContent)
|
|
22
|
-
spinHTML = "<div data-block='0'>" +
|
|
23
|
-
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
|
|
24
|
-
"</div>";
|
|
25
|
-
if (blockElement.isEqualNode(vditor.sv.element)) {
|
|
26
|
-
blockElement.innerHTML = spinHTML;
|
|
27
|
-
} else {
|
|
28
|
-
blockElement.outerHTML = spinHTML;
|
|
29
|
-
}
|
|
30
|
-
combineFootnote(vditor.sv.element)
|
|
31
|
-
setRangeByWbr(vditor.sv.element, range);
|
|
32
|
-
|
|
33
|
-
scrollCenter(vditor);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const getSideByType = (spanNode: Node, type: string, isPrevious = true) => {
|
|
37
|
-
let sideElement = spanNode as Element;
|
|
38
|
-
if (sideElement.nodeType === 3) {
|
|
39
|
-
sideElement = sideElement.parentElement;
|
|
40
|
-
}
|
|
41
|
-
while (sideElement) {
|
|
42
|
-
if (sideElement.getAttribute("data-type") === type) {
|
|
43
|
-
return sideElement;
|
|
44
|
-
}
|
|
45
|
-
if (isPrevious) {
|
|
46
|
-
sideElement = sideElement.previousElementSibling;
|
|
47
|
-
} else {
|
|
48
|
-
sideElement = sideElement.nextElementSibling;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
4
|
+
export const replaceSelection = (vditor: IVditor, text: string, selectionMode: SelectionMode = "end") => {
|
|
5
|
+
const element = vditor.sv.element;
|
|
6
|
+
element.setRangeText(text, element.selectionStart, element.selectionEnd, selectionMode);
|
|
7
|
+
element.focus();
|
|
52
8
|
};
|
|
53
9
|
|
|
54
|
-
export const
|
|
55
|
-
|
|
56
|
-
const spinHTML = vditor.lute.SpinVditorSVDOM(html)
|
|
57
|
-
html = "<div data-block='0'>" +
|
|
58
|
-
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
|
|
59
|
-
"</div>";
|
|
60
|
-
log("SpinVditorSVDOM", html, "result", vditor.options.debugger);
|
|
61
|
-
return html;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const processPreviousMarkers = (spanElement: HTMLElement) => {
|
|
65
|
-
const spanType = spanElement.getAttribute("data-type");
|
|
66
|
-
let previousElement = spanElement.previousElementSibling;
|
|
67
|
-
// 有内容的子列表/标题,在其 marker 后换行
|
|
68
|
-
let markerText = (spanType && spanType !== "text" && spanType !== "table" && spanType !== "heading-marker" &&
|
|
69
|
-
spanType !== "newline" && spanType !== "yaml-front-matter-open-marker" && spanType !== "yaml-front-matter-close-marker"
|
|
70
|
-
&& spanType !== "code-block-info" && spanType !== "code-block-close-marker" && spanType !== "code-block-open-marker") ?
|
|
71
|
-
spanElement.textContent : "";
|
|
72
|
-
let hasNL = false;
|
|
73
|
-
if (spanType === "newline") {
|
|
74
|
-
hasNL = true;
|
|
75
|
-
}
|
|
76
|
-
while (previousElement && !hasNL) {
|
|
77
|
-
const previousType = previousElement.getAttribute("data-type");
|
|
78
|
-
if (previousType === "li-marker" || previousType === "blockquote-marker" || previousType === "task-marker" ||
|
|
79
|
-
previousType === "padding") {
|
|
80
|
-
const previousText = previousElement.textContent;
|
|
81
|
-
if (previousType === "li-marker" &&
|
|
82
|
-
(spanType === "code-block-open-marker" || spanType === "code-block-info")) {
|
|
83
|
-
// https://github.com/Vanessa219/vditor/issues/586
|
|
84
|
-
markerText = previousText.replace(/\S/g, " ") + markerText;
|
|
85
|
-
} else if (spanType === "code-block-close-marker" &&
|
|
86
|
-
previousElement.nextElementSibling.isSameNode(spanElement)) {
|
|
87
|
-
// https://github.com/Vanessa219/vditor/issues/594
|
|
88
|
-
const openMarker = getSideByType(spanElement, "code-block-open-marker");
|
|
89
|
-
if (openMarker && openMarker.previousElementSibling) {
|
|
90
|
-
previousElement = openMarker.previousElementSibling;
|
|
91
|
-
markerText = previousText + markerText;
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
markerText = previousText + markerText;
|
|
95
|
-
}
|
|
96
|
-
} else if (previousType === "newline") {
|
|
97
|
-
hasNL = true;
|
|
98
|
-
}
|
|
99
|
-
previousElement = previousElement.previousElementSibling;
|
|
100
|
-
}
|
|
101
|
-
return markerText;
|
|
10
|
+
export const processPaste = (vditor: IVditor, text: string) => {
|
|
11
|
+
replaceSelection(vditor, text);
|
|
102
12
|
};
|
|
103
13
|
|
|
104
14
|
export const processAfterRender = (vditor: IVditor, options = {
|
|
@@ -106,10 +16,6 @@ export const processAfterRender = (vditor: IVditor, options = {
|
|
|
106
16
|
enableHint: false,
|
|
107
17
|
enableInput: true,
|
|
108
18
|
}) => {
|
|
109
|
-
if (options.enableHint) {
|
|
110
|
-
vditor.hint.render(vditor);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
19
|
vditor.preview.render(vditor);
|
|
114
20
|
|
|
115
21
|
const text = getMarkdown(vditor);
|
|
@@ -141,75 +47,53 @@ export const processAfterRender = (vditor: IVditor, options = {
|
|
|
141
47
|
};
|
|
142
48
|
|
|
143
49
|
export const processHeading = (vditor: IVditor, value: string) => {
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
50
|
+
const element = vditor.sv.element;
|
|
51
|
+
const lineStart = element.value.lastIndexOf("\n", element.selectionStart - 1) + 1;
|
|
52
|
+
const lineEndIndex = element.value.indexOf("\n", element.selectionEnd);
|
|
53
|
+
const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
|
|
54
|
+
const selectedLines = element.value.substring(lineStart, lineEnd);
|
|
55
|
+
const heading = value ? value + selectedLines.replace(/^#{1,6}\s+/, "") : selectedLines.replace(/^#{1,6}\s+/, "");
|
|
56
|
+
element.setRangeText(heading, lineStart, lineEnd, "select");
|
|
57
|
+
element.focus();
|
|
58
|
+
processAfterRender(vditor);
|
|
151
59
|
};
|
|
152
60
|
|
|
153
61
|
export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: string, suffix: string) => {
|
|
154
|
-
const
|
|
62
|
+
const element = vditor.sv.element;
|
|
155
63
|
const commandName = actionBtn.getAttribute("data-type");
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (!blockElement) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (commandName === "link") {
|
|
168
|
-
let html;
|
|
169
|
-
if (range.toString() === "") {
|
|
170
|
-
html = `${prefix}${Lute.Caret}${suffix}`;
|
|
171
|
-
} else {
|
|
172
|
-
html = `${prefix}${range.toString()}${suffix.replace(")", Lute.Caret + ")")}`;
|
|
173
|
-
}
|
|
174
|
-
document.execCommand("insertHTML", false, html);
|
|
175
|
-
return;
|
|
176
|
-
} else if (commandName === "italic" || commandName === "bold" || commandName === "strike" ||
|
|
177
|
-
commandName === "inline-code" || commandName === "code" || commandName === "table" || commandName === "line") {
|
|
178
|
-
let html;
|
|
179
|
-
// https://github.com/Vanessa219/vditor/issues/563 代码块不需要后面的 ```
|
|
180
|
-
if (range.toString() === "") {
|
|
181
|
-
html = `${prefix}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
|
|
182
|
-
} else {
|
|
183
|
-
html = `${prefix}${range.toString()}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
|
|
184
|
-
}
|
|
185
|
-
if (commandName === "table" || (commandName === "code" && spanElement && spanElement.textContent !== "")) {
|
|
186
|
-
html = "\n\n" + html;
|
|
187
|
-
} else if (commandName === "line") {
|
|
188
|
-
html = `\n\n${prefix}\n${Lute.Caret}`;
|
|
189
|
-
}
|
|
190
|
-
document.execCommand("insertHTML", false, html);
|
|
191
|
-
return;
|
|
192
|
-
} else if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
|
|
64
|
+
const start = element.selectionStart;
|
|
65
|
+
const end = element.selectionEnd;
|
|
66
|
+
const selectedText = element.value.substring(start, end);
|
|
67
|
+
let text = "";
|
|
68
|
+
let selectionStart = start + prefix.length;
|
|
69
|
+
let selectionEnd = selectionStart + selectedText.length;
|
|
70
|
+
|
|
71
|
+
if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
|
|
193
72
|
commandName === "quote") {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
73
|
+
const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
|
|
74
|
+
const lineEndIndex = element.value.indexOf("\n", end);
|
|
75
|
+
const lineEnd = lineEndIndex === -1 ? element.value.length : lineEndIndex;
|
|
76
|
+
const lines = element.value.substring(lineStart, lineEnd);
|
|
77
|
+
text = lines.split("\n").map((line) => prefix + line).join("\n");
|
|
78
|
+
element.setRangeText(text, lineStart, lineEnd, "select");
|
|
79
|
+
} else if (commandName === "line") {
|
|
80
|
+
text = `${start === 0 ? "" : "\n\n"}${prefix}\n`;
|
|
81
|
+
element.setRangeText(text, start, end, "end");
|
|
82
|
+
} else if (commandName === "code") {
|
|
83
|
+
text = `${prefix}\n${selectedText}${suffix}`;
|
|
84
|
+
element.setRangeText(text, start, end, "end");
|
|
85
|
+
selectionStart = start + prefix.length + 1;
|
|
86
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
87
|
+
element.setSelectionRange(selectionStart, selectionEnd);
|
|
88
|
+
} else if (commandName === "table") {
|
|
89
|
+
text = `${start === 0 ? "" : "\n\n"}${prefix}${suffix}`;
|
|
90
|
+
element.setRangeText(text, start, end, "end");
|
|
91
|
+
} else {
|
|
92
|
+
text = `${prefix}${selectedText}${suffix}`;
|
|
93
|
+
element.setRangeText(text, start, end, "end");
|
|
94
|
+
element.setSelectionRange(selectionStart, selectionEnd);
|
|
212
95
|
}
|
|
213
|
-
|
|
96
|
+
|
|
97
|
+
element.focus();
|
|
214
98
|
processAfterRender(vditor);
|
|
215
99
|
};
|