vditor 3.11.3 → 4.0.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/README.md +2 -0
- package/dist/css/content-theme/ant-design.css +7 -22
- package/dist/css/content-theme/dark.css +7 -16
- package/dist/css/content-theme/light.css +7 -15
- package/dist/css/content-theme/wechat.css +7 -19
- package/dist/index.css +132 -135
- package/dist/index.js +3791 -4022
- package/dist/index.min.js +1 -1
- package/dist/js/lute/lute.min.js +2 -2
- package/dist/js/mermaid/mermaid.min.js +516 -467
- package/dist/method.js +41 -33
- package/dist/method.min.js +1 -1
- package/dist/ts/constants.d.ts +1 -0
- 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 +10 -5
- package/package.json +1 -1
- package/src/assets/less/_reset.less +80 -28
- package/src/assets/less/_sv.less +2 -98
- package/src/assets/less/_wysiwyg.less +30 -0
- package/src/assets/less/index.less +14 -20
- package/src/index.ts +46 -14
- package/src/ts/constants.ts +1 -0
- package/src/ts/export/index.ts +1 -0
- 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/markdown/mediaRender.ts +23 -38
- package/src/ts/markdown/mermaidRender.ts +1 -1
- package/src/ts/markdown/previewRender.ts +1 -0
- package/src/ts/markdown/setLute.ts +1 -0
- 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,13 +1,15 @@
|
|
|
1
1
|
import {getSearch} from "../util/function";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const replaceMedia = (element: HTMLElement, tag: "video" | "audio" | "iframe", url: string) => {
|
|
4
|
+
const media = element.ownerDocument.createElement(tag);
|
|
5
|
+
if (tag === "iframe") {
|
|
6
|
+
media.className = "iframe__video";
|
|
7
|
+
} else {
|
|
8
|
+
media.setAttribute("controls", "controls");
|
|
9
|
+
}
|
|
10
|
+
// 链接属性经 DOM 解码后可能包含引号,直接设置属性以保留 URL 的数据语义。
|
|
11
|
+
media.setAttribute("src", url);
|
|
12
|
+
element.replaceWith(media);
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
const iframeRender = (element: HTMLElement, url: string) => {
|
|
@@ -21,33 +23,20 @@ const iframeRender = (element: HTMLElement, url: string) => {
|
|
|
21
23
|
const tedMatch = url.match(/(?:www\.|\/\/)ted\.com\/talks\/(\w+)/);
|
|
22
24
|
|
|
23
25
|
if (youtubeMatch && youtubeMatch[1].length === 11) {
|
|
24
|
-
element
|
|
25
|
-
|
|
26
|
-
(youtubeMatch[2] ? "?start=" + youtubeMatch[2] : "")}"></iframe>`);
|
|
27
|
-
element.remove();
|
|
26
|
+
replaceMedia(element, "iframe", `//www.youtube.com/embed/${youtubeMatch[1] +
|
|
27
|
+
(youtubeMatch[2] ? "?start=" + youtubeMatch[2] : "")}`);
|
|
28
28
|
} else if (youkuMatch && youkuMatch[1]) {
|
|
29
|
-
element
|
|
30
|
-
`<iframe class="iframe__video" src="//player.youku.com/embed/${youkuMatch[1]}"></iframe>`);
|
|
31
|
-
element.remove();
|
|
29
|
+
replaceMedia(element, "iframe", `//player.youku.com/embed/${youkuMatch[1]}`);
|
|
32
30
|
} else if (qqMatch && qqMatch[1]) {
|
|
33
|
-
element
|
|
34
|
-
`<iframe class="iframe__video" src="https://v.qq.com/txp/iframe/player.html?vid=${qqMatch[1]}"></iframe>`);
|
|
35
|
-
element.remove();
|
|
31
|
+
replaceMedia(element, "iframe", `https://v.qq.com/txp/iframe/player.html?vid=${qqMatch[1]}`);
|
|
36
32
|
} else if (coubMatch && coubMatch[1]) {
|
|
37
|
-
element
|
|
38
|
-
|
|
39
|
-
src="//coub.com/embed/${coubMatch[1]}?muted=false&autostart=false&originalSize=true&startWithHD=true"></iframe>`);
|
|
40
|
-
element.remove();
|
|
33
|
+
replaceMedia(element, "iframe",
|
|
34
|
+
`//coub.com/embed/${coubMatch[1]}?muted=false&autostart=false&originalSize=true&startWithHD=true`);
|
|
41
35
|
} else if (facebookMatch && facebookMatch[0]) {
|
|
42
|
-
element
|
|
43
|
-
|
|
44
|
-
src="https://www.facebook.com/plugins/video.php?href=${encodeURIComponent(facebookMatch[0])}"></iframe>`);
|
|
45
|
-
element.remove();
|
|
36
|
+
replaceMedia(element, "iframe",
|
|
37
|
+
`https://www.facebook.com/plugins/video.php?href=${encodeURIComponent(facebookMatch[0])}`);
|
|
46
38
|
} else if (dailymotionMatch && dailymotionMatch[2]) {
|
|
47
|
-
element
|
|
48
|
-
`<iframe class="iframe__video"
|
|
49
|
-
src="https://www.dailymotion.com/embed/video/${dailymotionMatch[2]}"></iframe>`);
|
|
50
|
-
element.remove();
|
|
39
|
+
replaceMedia(element, "iframe", `https://www.dailymotion.com/embed/video/${dailymotionMatch[2]}`);
|
|
51
40
|
} else if (url.indexOf("bilibili.com") > -1 && (url.indexOf("bvid=") > -1 || (bilibiliMatch && bilibiliMatch[1]))) {
|
|
52
41
|
const params: IObject = {
|
|
53
42
|
bvid: getSearch("bvid", url) || (bilibiliMatch && bilibiliMatch[1]),
|
|
@@ -75,13 +64,9 @@ const iframeRender = (element: HTMLElement, url: string) => {
|
|
|
75
64
|
src += "&";
|
|
76
65
|
}
|
|
77
66
|
});
|
|
78
|
-
element
|
|
79
|
-
`<iframe class="iframe__video" src="${src}"></iframe>`);
|
|
80
|
-
element.remove();
|
|
67
|
+
replaceMedia(element, "iframe", src);
|
|
81
68
|
} else if (tedMatch && tedMatch[1]) {
|
|
82
|
-
element
|
|
83
|
-
`<iframe class="iframe__video" src="//embed.ted.com/talks/${tedMatch[1]}"></iframe>`);
|
|
84
|
-
element.remove();
|
|
69
|
+
replaceMedia(element, "iframe", `//embed.ted.com/talks/${tedMatch[1]}`);
|
|
85
70
|
}
|
|
86
71
|
};
|
|
87
72
|
|
|
@@ -95,9 +80,9 @@ export const mediaRender = (element: HTMLElement) => {
|
|
|
95
80
|
return;
|
|
96
81
|
}
|
|
97
82
|
if (url.match(/^.+.(mp4|m4v|ogg|ogv|webm)$/)) {
|
|
98
|
-
|
|
83
|
+
replaceMedia(aElement, "video", url);
|
|
99
84
|
} else if (url.match(/^.+.(mp3|wav|flac)$/)) {
|
|
100
|
-
|
|
85
|
+
replaceMedia(aElement, "audio", url);
|
|
101
86
|
} else {
|
|
102
87
|
iframeRender(aElement, url);
|
|
103
88
|
}
|
|
@@ -13,7 +13,7 @@ export const mermaidRender = (element: (HTMLElement | Document) = document, cdn
|
|
|
13
13
|
if (mermaidElements.length === 0) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
addScript(`${cdn}/dist/js/mermaid/mermaid.min.js?v=11.
|
|
16
|
+
addScript(`${cdn}/dist/js/mermaid/mermaid.min.js?v=11.17.2`, "vditorMermaidScript").then(() => {
|
|
17
17
|
const config: any = {
|
|
18
18
|
securityLevel: "loose", // 升级后无 https://github.com/siyuan-note/siyuan/issues/3587,可使用该选项
|
|
19
19
|
altFontFamily: "sans-serif",
|
|
@@ -62,6 +62,7 @@ export const md2html = (mdText: string, options?: IPreviewOptions) => {
|
|
|
62
62
|
const lute = setLute({
|
|
63
63
|
autoSpace: mergedOptions.markdown.autoSpace,
|
|
64
64
|
callout: mergedOptions.markdown.callout,
|
|
65
|
+
inlineMath: mergedOptions.markdown.inlineMath,
|
|
65
66
|
gfmAutoLink: mergedOptions.markdown.gfmAutoLink,
|
|
66
67
|
codeBlockPreview: mergedOptions.markdown.codeBlockPreview,
|
|
67
68
|
emojiSite: mergedOptions.emojiPath,
|
|
@@ -6,6 +6,7 @@ export const setLute = (options: ILuteOptions) => {
|
|
|
6
6
|
lute.SetInlineMathAllowDigitAfterOpenMarker(options.inlineMathDigit);
|
|
7
7
|
lute.SetAutoSpace(options.autoSpace);
|
|
8
8
|
lute.SetCallout(options.callout);
|
|
9
|
+
lute.SetInlineMath(options.inlineMath);
|
|
9
10
|
lute.SetToC(options.toc);
|
|
10
11
|
lute.SetFootnotes(options.footnotes);
|
|
11
12
|
lute.SetFixTermTypo(options.fixTermTypo);
|
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
|
};
|