Liyuan Li 5 years ago
parent
commit
febe38dfd7

+ 11 - 17
src/index.ts

@@ -12,10 +12,10 @@ import {setLute} from "./ts/markdown/setLute";
 import {Outline} from "./ts/outline";
 import {Preview} from "./ts/preview/index";
 import {Resize} from "./ts/resize/index";
-import {getSelectText} from "./ts/sv/getSelectText";
+import {getSelectText} from "./ts/util/getSelectText";
 import {html2md} from "./ts/sv/html2md";
 import {Editor} from "./ts/sv/index";
-import {insertText} from "./ts/sv/insertText";
+import {inputEvent} from "./ts/sv/inputEvent";
 import {processAfterRender as processSVAfterRender} from "./ts/sv/process";
 import {Tip} from "./ts/tip";
 import {Toolbar} from "./ts/toolbar/index";
@@ -264,37 +264,31 @@ class Vditor extends VditorMethod {
         if (window.getSelection().isCollapsed) {
             return;
         }
-        if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, "", "", true);
-        } else {
-            document.execCommand("delete", false);
-        }
+        document.execCommand("delete", false);
     }
 
     /** 更新选中内容 */
     public updateValue(value: string) {
-        if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, value, "", true);
-        } else {
-            document.execCommand("insertHTML", false, value);
-        }
+        document.execCommand("insertHTML", false, value);
     }
 
     /** 在焦点处插入内容,并默认进行 Markdown 渲染 */
     public insertValue(value: string, render = true) {
+        const range = getEditorRange(this.vditor[this.vditor.currentMode].element);
+        range.collapse(true);
         if (this.vditor.currentMode === "sv") {
-            insertText(this.vditor, value, "");
+            this.vditor.sv.preventInput = true;
+            document.execCommand("insertHTML", false, value);
+            if (render) {
+                inputEvent(this.vditor);
+            }
         } else if (this.vditor.currentMode === "wysiwyg") {
-            const range = getEditorRange(this.vditor.wysiwyg.element);
-            range.collapse(true);
             this.vditor.wysiwyg.preventInput = true;
             document.execCommand("insertHTML", false, value);
             if (render) {
                 input(this.vditor, getSelection().getRangeAt(0));
             }
         } else if (this.vditor.currentMode === "ir") {
-            const range = getEditorRange(this.vditor.ir.element);
-            range.collapse(true);
             this.vditor.ir.preventInput = true;
             document.execCommand("insertHTML", false, value);
             if (render) {

+ 2 - 16
src/ts/ir/index.ts

@@ -1,7 +1,6 @@
 import {Constants} from "../constants";
-import {uploadFiles} from "../upload";
 import {isCtrl, isFirefox} from "../util/compatibility";
-import {blurEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
+import {blurEvent, dropEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
 import {paste} from "../util/fixBrowserBehavior";
 import {hasClosestByClassName} from "../util/hasClosest";
 import {
@@ -35,6 +34,7 @@ class IR {
         blurEvent(vditor, this.element);
         hotkeyEvent(vditor, this.element);
         selectEvent(vditor, this.element);
+        dropEvent(vditor, this.element);
     }
 
     private bindEvent(vditor: IVditor) {
@@ -61,20 +61,6 @@ class IR {
             });
         });
 
-        if (vditor.options.upload.url || vditor.options.upload.handler) {
-            this.element.addEventListener("drop",
-                (event: CustomEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
-                    if (event.dataTransfer.types[0] !== "Files") {
-                        return;
-                    }
-                    const files = event.dataTransfer.items;
-                    if (files.length > 0) {
-                        uploadFiles(vditor, files);
-                    }
-                    event.preventDefault();
-                });
-        }
-
         this.element.addEventListener("compositionstart", (event: InputEvent) => {
             this.composingLock = true;
         });

+ 3 - 1
src/ts/markdown/getMarkdown.ts

@@ -1,6 +1,8 @@
+import {code160to32} from "../util/code160to32";
+
 export const getMarkdown = (vditor: IVditor) => {
     if (vditor.currentMode === "sv") {
-        return vditor.sv.element.textContent;
+        return code160to32(`${vditor.sv.element.textContent}\n`.replace(/\n\n$/, "\n"));
     } else if (vditor.currentMode === "wysiwyg") {
         return vditor.lute.VditorDOM2Md(vditor.wysiwyg.element.innerHTML);
     } else if (vditor.currentMode === "ir") {

+ 0 - 36
src/ts/sv/getCurrentLinePosition.ts

@@ -1,36 +0,0 @@
-export const getCurrentLinePosition = (position: { start: number, end: number }, text: string) => {
-
-    // find start
-    let start = position.start - 1;
-    let findStart = false;
-    while (!findStart && start > -1) {
-        // 防止光标在末尾
-        if (text.charAt(start) === "\n" && text.length !== start + 1) {
-            start++;
-            findStart = true;
-        } else if (start === 0) {
-            findStart = true;
-        } else {
-            start--;
-        }
-    }
-
-    // find end
-    let end = position.end;
-    let findEnd = false;
-    while (!findEnd && end <= text.length) {
-        if (text.charAt(end) === "\n") {
-            end++;
-            findEnd = true;
-        } else if (end === text.length) {
-            findEnd = true;
-        } else {
-            end++;
-        }
-    }
-
-    return {
-        end: Math.min(end, text.length),
-        start: Math.max(0, start),
-    };
-};

+ 21 - 1
src/ts/sv/html2md.ts

@@ -1,7 +1,27 @@
 import {setHeaders} from "../upload/setHeaders";
 import {processPasteCode} from "../util/processCode";
 import {insertText} from "./insertText";
-import {setSelectionByInlineText} from "./setSelection";
+import {setSelectionFocus} from "../util/selection";
+
+const setSelectionByInlineText = (text: string, childNodes: NodeListOf<ChildNode>) => {
+    let offset = 0;
+    let startIndex = 0;
+    Array.from(childNodes).some((node: HTMLElement, index: number) => {
+        startIndex = node.textContent.indexOf(text);
+        if (startIndex > -1 && childNodes[index].childNodes[0].nodeType === 3) {
+            offset = index;
+            return true;
+        }
+    });
+    if (startIndex < 0) {
+        return;
+    }
+    const range = document.createRange();
+    range.setStart(childNodes[offset].childNodes[0], startIndex);
+    range.setEnd(childNodes[offset].childNodes[0], startIndex + text.length);
+    setSelectionFocus(range);
+};
+
 
 // 直接使用 API 或 setOriginal 时不需要对图片进行服务器上传,直接转换。
 // 目前使用 textPlain 判断是否来自 API 或 setOriginal

+ 8 - 19
src/ts/sv/index.ts

@@ -1,7 +1,7 @@
 import {uploadFiles} from "../upload/index";
 import {isCtrl, isFirefox} from "../util/compatibility";
-import {blurEvent, focusEvent, hotkeyEvent, selectEvent} from "../util/editorCommonEvent";
-import {getSelectText} from "./getSelectText";
+import {blurEvent, dropEvent, focusEvent, hotkeyEvent, selectEvent} from "../util/editorCommonEvent";
+import {getSelectText} from "../util/getSelectText";
 import {highlightToolbarSV} from "./highlightToolbarSV";
 import {html2md} from "./html2md";
 import {inputEvent} from "./inputEvent";
@@ -9,10 +9,10 @@ import {insertText} from "./insertText";
 
 class Editor {
     public element: HTMLPreElement;
-    public range: Range;
     public composingLock: boolean = false;
     public processTimeoutId: number;
     public hlToolbarTimeoutId: number;
+    public preventInput: boolean;
 
     constructor(vditor: IVditor) {
         this.element = document.createElement("pre");
@@ -27,6 +27,7 @@ class Editor {
         blurEvent(vditor, this.element);
         hotkeyEvent(vditor, this.element);
         selectEvent(vditor, this.element);
+        dropEvent(vditor, this.element);
     }
 
     private bindEvent(vditor: IVditor) {
@@ -78,22 +79,6 @@ class Editor {
             insertText(vditor, textPlain, "", true);
         });
 
-        if (vditor.options.upload.url || vditor.options.upload.handler) {
-            this.element.addEventListener("drop", (event: CustomEvent & { dataTransfer?: DataTransfer }) => {
-                if (event.dataTransfer.types[0] !== "Files") {
-                    insertText(vditor, getSelection().toString(), "", false);
-                    event.preventDefault();
-                    return;
-                }
-                const files = event.dataTransfer.items;
-                if (files.length === 0) {
-                    return;
-                }
-                uploadFiles(vditor, files);
-                event.preventDefault();
-            });
-        }
-
         this.element.addEventListener("scroll", () => {
             if (vditor.preview.element.style.display !== "block") {
                 return;
@@ -126,6 +111,10 @@ class Editor {
             if (this.composingLock) {
                 return;
             }
+            if (this.preventInput) {
+                this.preventInput = false;
+                return;
+            }
             inputEvent(vditor, event);
         });
 

+ 21 - 19
src/ts/sv/process.ts

@@ -1,7 +1,7 @@
 import {getMarkdown} from "../markdown/getMarkdown";
 import {accessLocalStorage} from "../util/compatibility";
-import {hasClosestBlock, hasClosestByAttribute, hasClosestByMatchTag} from "../util/hasClosest";
-import {getEditorRange, setRangeByWbr, setSelectionFocus} from "../util/selection";
+import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
+import {getEditorRange, setRangeByWbr} from "../util/selection";
 import {highlightToolbarSV} from "./highlightToolbarSV";
 import {inputEvent} from "./inputEvent";
 
@@ -48,15 +48,14 @@ export const processAfterRender = (vditor: IVditor, options = {
 
 export const processHeading = (vditor: IVditor, value: string) => {
     const range = getSelection().getRangeAt(0);
-    const headingElement = hasClosestBlock(range.startContainer) || range.startContainer as HTMLElement;
+    const headingElement = hasClosestByAttribute(range.startContainer, "data-type", "heading") ||
+        range.startContainer as HTMLElement;
     if (headingElement) {
+        const headingMarkerElement = headingElement.querySelector(".vditor-sv__marker--heading");
+        range.selectNodeContents(headingMarkerElement);
         if (value === "") {
-            const headingMarkerElement = headingElement.querySelector(".vditor-sv__marker--heading");
-            range.selectNodeContents(headingMarkerElement);
             document.execCommand("delete");
         } else {
-            range.selectNodeContents(headingElement);
-            range.collapse(true);
             document.execCommand("insertHTML", false, value);
         }
         highlightToolbarSV(vditor);
@@ -83,11 +82,14 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
     // 移除
     if (actionBtn.classList.contains("vditor-menu--current")) {
         if (commandName === "quote") {
-            const quoteElement = hasClosestByMatchTag(typeElement, "BLOCKQUOTE");
+            const quoteElement = hasClosestByAttribute(range.startContainer, "data-type", "blockquote");
             if (quoteElement) {
-                range.insertNode(document.createElement("wbr"));
-                quoteElement.outerHTML = quoteElement.innerHTML.trim() === "" ?
-                    `<p data-block="0">${quoteElement.innerHTML}</p>` : quoteElement.innerHTML;
+                quoteElement.querySelectorAll('[data-type="blockquote-line"]').forEach((item: HTMLElement) => {
+                    item.firstElementChild.remove();
+                });
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
             }
         } else if (commandName === "link") {
             const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a") as HTMLElement;
@@ -132,8 +134,10 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
         const blockElement = hasClosestBlock(range.startContainer);
         if (commandName === "line") {
             if (blockElement) {
-                const hrHTML = '<hr data-block="0"><p data-block="0"><wbr>\n</p>';
-                if (blockElement.innerHTML.trim() === "") {
+                const hrHTML = `<div data-type="thematic-break" class="vditor-sv__marker"><span class="vditor-sv__marker">---
+
+</span></div><wbr>`;
+                if (blockElement.textContent.trim() === "") {
                     blockElement.outerHTML = hrHTML;
                 } else {
                     blockElement.insertAdjacentHTML("afterend", hrHTML);
@@ -141,8 +145,10 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
             }
         } else if (commandName === "quote") {
             if (blockElement) {
-                range.insertNode(document.createElement("wbr"));
-                blockElement.outerHTML = `<blockquote data-block="0">${blockElement.outerHTML}</blockquote>`;
+                blockElement.insertAdjacentText("afterbegin", "> ");
+                inputEvent(vditor);
+                highlightToolbarSV(vditor);
+                return;
             }
         } else if (commandName === "link") {
             let html;
@@ -166,10 +172,6 @@ export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: stri
                 html = "\n" + html;
             }
             document.execCommand("insertHTML", false, html);
-            if (commandName === "table") {
-                range.selectNodeContents(getSelection().getRangeAt(0).startContainer.parentElement);
-                setSelectionFocus(range);
-            }
             highlightToolbarSV(vditor);
             return;
         } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") {

+ 0 - 20
src/ts/sv/setSelection.ts

@@ -1,20 +0,0 @@
-import {setSelectionFocus} from "../util/selection";
-
-export const setSelectionByInlineText = (text: string, childNodes: NodeListOf<ChildNode>) => {
-    let offset = 0;
-    let startIndex = 0;
-    Array.from(childNodes).some((node: HTMLElement, index: number) => {
-        startIndex = node.textContent.indexOf(text);
-        if (startIndex > -1 && childNodes[index].childNodes[0].nodeType === 3) {
-            offset = index;
-            return true;
-        }
-    });
-    if (startIndex < 0) {
-        return;
-    }
-    const range = document.createRange();
-    range.setStart(childNodes[offset].childNodes[0], startIndex);
-    range.setEnd(childNodes[offset].childNodes[0], startIndex + text.length);
-    setSelectionFocus(range);
-};

+ 1 - 1
src/ts/toolbar/Record.ts

@@ -2,8 +2,8 @@ import {Constants} from "../constants";
 import {i18n} from "../i18n/index";
 import {uploadFiles} from "../upload/index";
 import {getEventName} from "../util/compatibility";
+import {RecordMedia} from "../util/RecordMedia";
 import {MenuItem} from "./MenuItem";
-import {RecordMedia} from "./RecordMedia";
 
 export class Record extends MenuItem {
     constructor(vditor: IVditor, menuItem: IMenuItem) {

+ 1 - 6
src/ts/upload/index.ts

@@ -1,5 +1,4 @@
 import {i18n} from "../i18n/index";
-import {insertText} from "../sv/insertText";
 import {getEditorRange, setSelectionFocus} from "../util/selection";
 import {getElement} from "./getElement";
 import {setHeaders} from "./setHeaders";
@@ -135,11 +134,7 @@ const genUploadedLabel = (responseText: string, vditor: IVditor) => {
         }
     });
     setSelectionFocus(vditor.upload.range);
-    if (vditor.currentMode !== "sv") {
-        document.execCommand("insertHTML", false, succFileText);
-    } else {
-        insertText(vditor, succFileText, "", true);
-    }
+    document.execCommand("insertHTML", false, succFileText);
     vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
 };
 

+ 0 - 0
src/ts/toolbar/RecordMedia.ts → src/ts/util/RecordMedia.ts


+ 20 - 5
src/ts/util/editorCommonEvent.ts

@@ -1,8 +1,8 @@
 import {processHeading} from "../ir/process";
 import {processKeydown as irProcessKeydown} from "../ir/processKeydown";
 import {getMarkdown} from "../markdown/getMarkdown";
-import {getSelectText} from "../sv/getSelectText";
-import {insertText} from "../sv/insertText";
+import {getSelectText} from "./getSelectText";
+import {processHeading as processHeadingSV} from "../sv/process";
 import {processKeydown as mdProcessKeydown} from "../sv/processKeydown";
 import {setEditMode} from "../toolbar/EditMode";
 import {hidePanel} from "../toolbar/setToolbar";
@@ -13,6 +13,7 @@ import {removeHeading, setHeading} from "../wysiwyg/setHeading";
 import {getEventName, isCtrl} from "./compatibility";
 import {hasClosestByMatchTag} from "./hasClosest";
 import {matchHotKey} from "./hotKey";
+import {uploadFiles} from "../upload";
 
 export const focusEvent = (vditor: IVditor, editorElement: HTMLElement) => {
     editorElement.addEventListener("focus", () => {
@@ -31,6 +32,22 @@ export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
     });
 };
 
+export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
+    if (vditor.options.upload.url || vditor.options.upload.handler) {
+        editorElement.addEventListener("drop",
+            (event: CustomEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
+                if (event.dataTransfer.types[0] !== "Files") {
+                    return;
+                }
+                const files = event.dataTransfer.items;
+                if (files.length > 0) {
+                    uploadFiles(vditor, files);
+                }
+                event.preventDefault();
+            });
+    }
+};
+
 export const scrollCenter = (vditor: IVditor) => {
     if (!vditor.options.typewriterMode) {
         return;
@@ -109,9 +126,7 @@ export const hotkeyEvent = (vditor: IVditor, editorElement: HTMLElement) => {
                 }
                 afterRenderEvent(vditor);
             } else if (vditor.currentMode === "sv") {
-                insertText(vditor,
-                    "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ",
-                    "", false, true);
+                processHeadingSV(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
             } else if (vditor.currentMode === "ir") {
                 processHeading(vditor, "#".repeat(parseInt(event.code.replace("Digit", ""), 10)) + " ");
             }

+ 1 - 1
src/ts/sv/getSelectText.ts → src/ts/util/getSelectText.ts

@@ -1,4 +1,4 @@
-import {selectIsEditor} from "../util/selection";
+import {selectIsEditor} from "./selection";
 
 export const getSelectText = (editor: HTMLElement, range?: Range) => {
     if (selectIsEditor(editor, range)) {

+ 3 - 1
src/ts/util/highlightToolbar.ts

@@ -35,7 +35,9 @@ export const highlightToolbarIRSV = (vditor: IVditor, processLi: (node: HTMLElem
             setCurrentToolbar(vditor.toolbar.elements, ["headings"]);
         }
 
-        const quoteElement = hasClosestByMatchTag(typeElement, "BLOCKQUOTE");
+        const quoteElement =
+            vditor.currentMode === "sv" ? hasClosestByAttribute(typeElement, "data-type", "blockquote") :
+                hasClosestByMatchTag(typeElement, "BLOCKQUOTE");
         if (quoteElement) {
             setCurrentToolbar(vditor.toolbar.elements, ["quote"]);
         }

+ 2 - 16
src/ts/wysiwyg/index.ts

@@ -1,8 +1,7 @@
 import {Constants} from "../constants";
 import {hidePanel} from "../toolbar/setToolbar";
-import {uploadFiles} from "../upload";
 import {isCtrl, isFirefox} from "../util/compatibility";
-import {blurEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
+import {blurEvent, dropEvent, focusEvent, hotkeyEvent, scrollCenter, selectEvent} from "../util/editorCommonEvent";
 import {isHeadingMD, isHrMD, paste, renderToc} from "../util/fixBrowserBehavior";
 import {
     hasClosestBlock, hasClosestByAttribute,
@@ -46,23 +45,10 @@ class WYSIWYG {
         blurEvent(vditor, this.element);
         hotkeyEvent(vditor, this.element);
         selectEvent(vditor, this.element);
+        dropEvent(vditor, this.element);
     }
 
     private bindEvent(vditor: IVditor) {
-        if (vditor.options.upload.url || vditor.options.upload.handler) {
-            this.element.addEventListener("drop",
-                (event: CustomEvent & { dataTransfer?: DataTransfer, target: HTMLElement }) => {
-                    if (event.dataTransfer.types[0] !== "Files") {
-                        return;
-                    }
-                    const files = event.dataTransfer.items;
-                    if (files.length > 0) {
-                        uploadFiles(vditor, files);
-                    }
-                    event.preventDefault();
-                });
-        }
-
         window.addEventListener("scroll", () => {
             hidePanel(vditor, ["hint"]);
             if (this.popover.style.display !== "block") {

+ 1 - 0
types/index.d.ts

@@ -579,5 +579,6 @@ interface IVditor {
         processTimeoutId: number,
         hlToolbarTimeoutId: number,
         composingLock: boolean,
+        preventInput: boolean,
     };
 }