From f20b7aa61c525af873d583dfd87c8143591b65e5 Mon Sep 17 00:00:00 2001 From: klarkxy <278370456@qq.com> Date: Fri, 10 Apr 2026 16:18:01 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=B0=E5=A2=9E=20WYSIWY?= =?UTF-8?q?G=20=E7=BC=96=E8=BE=91=E5=99=A8=E5=8A=9F=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=B7=A5=E5=85=B7=E6=A0=8F=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + web/admin/kindeditor.php | 4 +- web/bsadmin/kindeditor.php | 4 +- web/include/vditor-adapter.js | 24 +- web/include/vditor/LICENSE | 21 + web/include/vditor/README.md | 680 ++++++++ web/include/vditor/package.json | 76 + .../vditor/src/assets/less/_content.less | 279 +++ web/include/vditor/src/assets/less/_hint.less | 67 + web/include/vditor/src/assets/less/_ir.less | 275 +++ .../vditor/src/assets/less/_panel.less | 110 ++ .../vditor/src/assets/less/_reset.less | 610 +++++++ web/include/vditor/src/assets/less/_sv.less | 132 ++ .../vditor/src/assets/less/_toolbar.less | 180 ++ .../vditor/src/assets/less/_tooltipped.less | 171 ++ .../vditor/src/assets/less/_wysiwyg.less | 195 +++ web/include/vditor/src/assets/less/index.less | 88 + web/include/vditor/src/index.ts | 553 ++++++ web/include/vditor/src/method.ts | 68 + web/include/vditor/src/ts/constants.ts | 69 + web/include/vditor/src/ts/devtools/index.ts | 83 + web/include/vditor/src/ts/export/index.ts | 83 + web/include/vditor/src/ts/hint/index.ts | 276 +++ web/include/vditor/src/ts/ir/expandMarker.ts | 79 + .../vditor/src/ts/ir/highlightToolbarIR.ts | 97 ++ web/include/vditor/src/ts/ir/index.ts | 265 +++ web/include/vditor/src/ts/ir/input.ts | 246 +++ web/include/vditor/src/ts/ir/process.ts | 222 +++ .../vditor/src/ts/ir/processKeydown.ts | 238 +++ .../vditor/src/ts/markdown/abcRender.ts | 27 + .../vditor/src/ts/markdown/adapterRender.ts | 37 + .../vditor/src/ts/markdown/anchorRender.ts | 19 + .../vditor/src/ts/markdown/chartRender.ts | 38 + .../vditor/src/ts/markdown/codeRender.ts | 60 + .../vditor/src/ts/markdown/flowchartRender.ts | 25 + web/include/vditor/src/ts/markdown/getHTML.ts | 11 + .../vditor/src/ts/markdown/getMarkdown.ts | 12 + .../vditor/src/ts/markdown/graphvizRender.ts | 49 + .../vditor/src/ts/markdown/highlightRender.ts | 90 + .../src/ts/markdown/lazyLoadImageRender.ts | 56 + .../vditor/src/ts/markdown/markmapRender.ts | 57 + .../vditor/src/ts/markdown/mathRender.ts | 148 ++ .../vditor/src/ts/markdown/mediaRender.ts | 105 ++ .../vditor/src/ts/markdown/mermaidRender.ts | 60 + .../vditor/src/ts/markdown/mindmapRender.ts | 74 + .../vditor/src/ts/markdown/outlineRender.ts | 110 ++ .../vditor/src/ts/markdown/plantumlRender.ts | 32 + .../vditor/src/ts/markdown/previewRender.ts | 167 ++ web/include/vditor/src/ts/markdown/setLute.ts | 24 + .../vditor/src/ts/markdown/speechRender.ts | 107 ++ web/include/vditor/src/ts/outline/index.ts | 45 + web/include/vditor/src/ts/preview/image.ts | 50 + web/include/vditor/src/ts/preview/index.ts | 285 ++++ web/include/vditor/src/ts/resize/index.ts | 57 + .../vditor/src/ts/sv/combineFootnote.ts | 27 + web/include/vditor/src/ts/sv/index.ts | 125 ++ web/include/vditor/src/ts/sv/inputEvent.ts | 188 +++ web/include/vditor/src/ts/sv/process.ts | 215 +++ .../vditor/src/ts/sv/processKeydown.ts | 202 +++ web/include/vditor/src/ts/tip/index.ts | 39 + web/include/vditor/src/ts/toolbar/Both.ts | 28 + web/include/vditor/src/ts/toolbar/Br.ts | 8 + .../vditor/src/ts/toolbar/CodeTheme.ts | 36 + .../vditor/src/ts/toolbar/ContentTheme.ts | 35 + web/include/vditor/src/ts/toolbar/Counter.ts | 40 + web/include/vditor/src/ts/toolbar/Custom.ts | 17 + web/include/vditor/src/ts/toolbar/Devtools.ts | 28 + web/include/vditor/src/ts/toolbar/Divider.ts | 8 + web/include/vditor/src/ts/toolbar/EditMode.ts | 195 +++ web/include/vditor/src/ts/toolbar/Emoji.ts | 74 + web/include/vditor/src/ts/toolbar/Export.ts | 41 + .../vditor/src/ts/toolbar/Fullscreen.ts | 66 + web/include/vditor/src/ts/toolbar/Headings.ts | 73 + web/include/vditor/src/ts/toolbar/Help.ts | 30 + web/include/vditor/src/ts/toolbar/Indent.ts | 25 + web/include/vditor/src/ts/toolbar/Info.ts | 43 + .../vditor/src/ts/toolbar/InsertAfter.ts | 18 + .../vditor/src/ts/toolbar/InsertBefore.ts | 18 + web/include/vditor/src/ts/toolbar/MenuItem.ts | 54 + web/include/vditor/src/ts/toolbar/Outdent.ts | 24 + web/include/vditor/src/ts/toolbar/Outline.ts | 21 + web/include/vditor/src/ts/toolbar/Preview.ts | 55 + web/include/vditor/src/ts/toolbar/Record.ts | 60 + web/include/vditor/src/ts/toolbar/Redo.ts | 18 + web/include/vditor/src/ts/toolbar/Undo.ts | 18 + web/include/vditor/src/ts/toolbar/Upload.ts | 41 + web/include/vditor/src/ts/toolbar/index.ts | 192 +++ .../vditor/src/ts/toolbar/setToolbar.ts | 120 ++ web/include/vditor/src/ts/ui/initUI.ts | 180 ++ web/include/vditor/src/ts/ui/setCodeTheme.ts | 16 + .../vditor/src/ts/ui/setContentTheme.ts | 15 + .../vditor/src/ts/ui/setPreviewMode.ts | 31 + web/include/vditor/src/ts/ui/setTheme.ts | 7 + web/include/vditor/src/ts/undo/index.ts | 260 +++ .../vditor/src/ts/upload/getElement.ts | 10 + web/include/vditor/src/ts/upload/index.ts | 260 +++ .../vditor/src/ts/upload/setHeaders.ts | 10 + web/include/vditor/src/ts/util/Options.ts | 454 +++++ web/include/vditor/src/ts/util/RecordMedia.ts | 170 ++ web/include/vditor/src/ts/util/addScript.ts | 43 + web/include/vditor/src/ts/util/addStyle.ts | 10 + web/include/vditor/src/ts/util/code160to32.ts | 4 + .../vditor/src/ts/util/compatibility.ts | 67 + .../vditor/src/ts/util/editorCommonEvent.ts | 261 +++ .../vditor/src/ts/util/fixBrowserBehavior.ts | 1501 +++++++++++++++++ web/include/vditor/src/ts/util/function.ts | 15 + .../vditor/src/ts/util/getSelectText.ts | 8 + web/include/vditor/src/ts/util/hasClosest.ts | 154 ++ .../src/ts/util/hasClosestByHeadings.ts | 27 + .../vditor/src/ts/util/highlightToolbar.ts | 10 + web/include/vditor/src/ts/util/hotKey.ts | 54 + web/include/vditor/src/ts/util/log.ts | 6 + web/include/vditor/src/ts/util/merge.ts | 18 + web/include/vditor/src/ts/util/processCode.ts | 98 ++ web/include/vditor/src/ts/util/selection.ts | 275 +++ web/include/vditor/src/ts/util/toc.ts | 83 + .../vditor/src/ts/wysiwyg/afterRenderEvent.ts | 41 + .../src/ts/wysiwyg/highlightToolbarWYSIWYG.ts | 1139 +++++++++++++ web/include/vditor/src/ts/wysiwyg/index.ts | 575 +++++++ .../vditor/src/ts/wysiwyg/inlineTag.ts | 129 ++ web/include/vditor/src/ts/wysiwyg/input.ts | 212 +++ .../vditor/src/ts/wysiwyg/processKeydown.ts | 363 ++++ .../vditor/src/ts/wysiwyg/renderDomByMd.ts | 18 + .../vditor/src/ts/wysiwyg/setHeading.ts | 43 + web/include/vditor/src/ts/wysiwyg/showCode.ts | 32 + .../vditor/src/ts/wysiwyg/toolbarEvent.ts | 333 ++++ web/swadmin/kindeditor.php | 4 +- web/template/syzoj/problem.php | 72 - 128 files changed, 16048 insertions(+), 84 deletions(-) create mode 100644 web/include/vditor/LICENSE create mode 100644 web/include/vditor/README.md create mode 100644 web/include/vditor/package.json create mode 100644 web/include/vditor/src/assets/less/_content.less create mode 100644 web/include/vditor/src/assets/less/_hint.less create mode 100644 web/include/vditor/src/assets/less/_ir.less create mode 100644 web/include/vditor/src/assets/less/_panel.less create mode 100644 web/include/vditor/src/assets/less/_reset.less create mode 100644 web/include/vditor/src/assets/less/_sv.less create mode 100644 web/include/vditor/src/assets/less/_toolbar.less create mode 100644 web/include/vditor/src/assets/less/_tooltipped.less create mode 100644 web/include/vditor/src/assets/less/_wysiwyg.less create mode 100644 web/include/vditor/src/assets/less/index.less create mode 100644 web/include/vditor/src/index.ts create mode 100644 web/include/vditor/src/method.ts create mode 100644 web/include/vditor/src/ts/constants.ts create mode 100644 web/include/vditor/src/ts/devtools/index.ts create mode 100644 web/include/vditor/src/ts/export/index.ts create mode 100644 web/include/vditor/src/ts/hint/index.ts create mode 100644 web/include/vditor/src/ts/ir/expandMarker.ts create mode 100644 web/include/vditor/src/ts/ir/highlightToolbarIR.ts create mode 100644 web/include/vditor/src/ts/ir/index.ts create mode 100644 web/include/vditor/src/ts/ir/input.ts create mode 100644 web/include/vditor/src/ts/ir/process.ts create mode 100644 web/include/vditor/src/ts/ir/processKeydown.ts create mode 100644 web/include/vditor/src/ts/markdown/abcRender.ts create mode 100644 web/include/vditor/src/ts/markdown/adapterRender.ts create mode 100644 web/include/vditor/src/ts/markdown/anchorRender.ts create mode 100644 web/include/vditor/src/ts/markdown/chartRender.ts create mode 100644 web/include/vditor/src/ts/markdown/codeRender.ts create mode 100644 web/include/vditor/src/ts/markdown/flowchartRender.ts create mode 100644 web/include/vditor/src/ts/markdown/getHTML.ts create mode 100644 web/include/vditor/src/ts/markdown/getMarkdown.ts create mode 100644 web/include/vditor/src/ts/markdown/graphvizRender.ts create mode 100644 web/include/vditor/src/ts/markdown/highlightRender.ts create mode 100644 web/include/vditor/src/ts/markdown/lazyLoadImageRender.ts create mode 100644 web/include/vditor/src/ts/markdown/markmapRender.ts create mode 100644 web/include/vditor/src/ts/markdown/mathRender.ts create mode 100644 web/include/vditor/src/ts/markdown/mediaRender.ts create mode 100644 web/include/vditor/src/ts/markdown/mermaidRender.ts create mode 100644 web/include/vditor/src/ts/markdown/mindmapRender.ts create mode 100644 web/include/vditor/src/ts/markdown/outlineRender.ts create mode 100644 web/include/vditor/src/ts/markdown/plantumlRender.ts create mode 100644 web/include/vditor/src/ts/markdown/previewRender.ts create mode 100644 web/include/vditor/src/ts/markdown/setLute.ts create mode 100644 web/include/vditor/src/ts/markdown/speechRender.ts create mode 100644 web/include/vditor/src/ts/outline/index.ts create mode 100644 web/include/vditor/src/ts/preview/image.ts create mode 100644 web/include/vditor/src/ts/preview/index.ts create mode 100644 web/include/vditor/src/ts/resize/index.ts create mode 100644 web/include/vditor/src/ts/sv/combineFootnote.ts create mode 100644 web/include/vditor/src/ts/sv/index.ts create mode 100644 web/include/vditor/src/ts/sv/inputEvent.ts create mode 100644 web/include/vditor/src/ts/sv/process.ts create mode 100644 web/include/vditor/src/ts/sv/processKeydown.ts create mode 100644 web/include/vditor/src/ts/tip/index.ts create mode 100644 web/include/vditor/src/ts/toolbar/Both.ts create mode 100644 web/include/vditor/src/ts/toolbar/Br.ts create mode 100644 web/include/vditor/src/ts/toolbar/CodeTheme.ts create mode 100644 web/include/vditor/src/ts/toolbar/ContentTheme.ts create mode 100644 web/include/vditor/src/ts/toolbar/Counter.ts create mode 100644 web/include/vditor/src/ts/toolbar/Custom.ts create mode 100644 web/include/vditor/src/ts/toolbar/Devtools.ts create mode 100644 web/include/vditor/src/ts/toolbar/Divider.ts create mode 100644 web/include/vditor/src/ts/toolbar/EditMode.ts create mode 100644 web/include/vditor/src/ts/toolbar/Emoji.ts create mode 100644 web/include/vditor/src/ts/toolbar/Export.ts create mode 100644 web/include/vditor/src/ts/toolbar/Fullscreen.ts create mode 100644 web/include/vditor/src/ts/toolbar/Headings.ts create mode 100644 web/include/vditor/src/ts/toolbar/Help.ts create mode 100644 web/include/vditor/src/ts/toolbar/Indent.ts create mode 100644 web/include/vditor/src/ts/toolbar/Info.ts create mode 100644 web/include/vditor/src/ts/toolbar/InsertAfter.ts create mode 100644 web/include/vditor/src/ts/toolbar/InsertBefore.ts create mode 100644 web/include/vditor/src/ts/toolbar/MenuItem.ts create mode 100644 web/include/vditor/src/ts/toolbar/Outdent.ts create mode 100644 web/include/vditor/src/ts/toolbar/Outline.ts create mode 100644 web/include/vditor/src/ts/toolbar/Preview.ts create mode 100644 web/include/vditor/src/ts/toolbar/Record.ts create mode 100644 web/include/vditor/src/ts/toolbar/Redo.ts create mode 100644 web/include/vditor/src/ts/toolbar/Undo.ts create mode 100644 web/include/vditor/src/ts/toolbar/Upload.ts create mode 100644 web/include/vditor/src/ts/toolbar/index.ts create mode 100644 web/include/vditor/src/ts/toolbar/setToolbar.ts create mode 100644 web/include/vditor/src/ts/ui/initUI.ts create mode 100644 web/include/vditor/src/ts/ui/setCodeTheme.ts create mode 100644 web/include/vditor/src/ts/ui/setContentTheme.ts create mode 100644 web/include/vditor/src/ts/ui/setPreviewMode.ts create mode 100644 web/include/vditor/src/ts/ui/setTheme.ts create mode 100644 web/include/vditor/src/ts/undo/index.ts create mode 100644 web/include/vditor/src/ts/upload/getElement.ts create mode 100644 web/include/vditor/src/ts/upload/index.ts create mode 100644 web/include/vditor/src/ts/upload/setHeaders.ts create mode 100644 web/include/vditor/src/ts/util/Options.ts create mode 100644 web/include/vditor/src/ts/util/RecordMedia.ts create mode 100644 web/include/vditor/src/ts/util/addScript.ts create mode 100644 web/include/vditor/src/ts/util/addStyle.ts create mode 100644 web/include/vditor/src/ts/util/code160to32.ts create mode 100644 web/include/vditor/src/ts/util/compatibility.ts create mode 100644 web/include/vditor/src/ts/util/editorCommonEvent.ts create mode 100644 web/include/vditor/src/ts/util/fixBrowserBehavior.ts create mode 100644 web/include/vditor/src/ts/util/function.ts create mode 100644 web/include/vditor/src/ts/util/getSelectText.ts create mode 100644 web/include/vditor/src/ts/util/hasClosest.ts create mode 100644 web/include/vditor/src/ts/util/hasClosestByHeadings.ts create mode 100644 web/include/vditor/src/ts/util/highlightToolbar.ts create mode 100644 web/include/vditor/src/ts/util/hotKey.ts create mode 100644 web/include/vditor/src/ts/util/log.ts create mode 100644 web/include/vditor/src/ts/util/merge.ts create mode 100644 web/include/vditor/src/ts/util/processCode.ts create mode 100644 web/include/vditor/src/ts/util/selection.ts create mode 100644 web/include/vditor/src/ts/util/toc.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/afterRenderEvent.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/index.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/inlineTag.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/input.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/processKeydown.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/renderDomByMd.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/setHeading.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/showCode.ts create mode 100644 web/include/vditor/src/ts/wysiwyg/toolbarEvent.ts diff --git a/.gitignore b/.gitignore index 33ed58b..d805f47 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ core/judged/judgehub core/judge_client/judge_client core/judge_client/judge_client.http +node_modules +dist \ No newline at end of file diff --git a/web/admin/kindeditor.php b/web/admin/kindeditor.php index ced2656..e4ae463 100644 --- a/web/admin/kindeditor.php +++ b/web/admin/kindeditor.php @@ -6,9 +6,9 @@ selector: 'textarea.kindeditor', uploadUrl: '../kindeditor/php/upload_json.php', accept: '*/*', - mode: 'sv', + mode: 'wysiwyg', preview: { - mode: 'both', + mode: 'editor', delay: 0, actions: [], markdown: { diff --git a/web/bsadmin/kindeditor.php b/web/bsadmin/kindeditor.php index 62cb78e..b00361a 100644 --- a/web/bsadmin/kindeditor.php +++ b/web/bsadmin/kindeditor.php @@ -6,10 +6,10 @@ selector: 'textarea.kindeditor', uploadUrl: '../kindeditor/php/upload_json.php', accept: '*/*', - mode: 'sv', + mode: 'wysiwyg', ctrlEnterFormName: 'example', preview: { - mode: 'both', + mode: 'editor', delay: 0, actions: [], markdown: { diff --git a/web/include/vditor-adapter.js b/web/include/vditor-adapter.js index 8e4fc26..8fb5711 100644 --- a/web/include/vditor-adapter.js +++ b/web/include/vditor-adapter.js @@ -6,10 +6,25 @@ return; } - var DEFAULT_CDN = global.HUSTOJ_VDITOR_CDN || 'https://cdn.jsdelivr.net/npm/vditor@3.10.6'; + var currentScriptRef = documentRef.currentScript || (function () { + var scripts = documentRef.getElementsByTagName('script'); + return scripts.length > 0 ? scripts[scripts.length - 1] : null; + })(); + var DEFAULT_CDN = global.HUSTOJ_VDITOR_CDN || getLocalCdnBase(); + global.HUSTOJ_VDITOR_CDN = DEFAULT_CDN; var assetsPromise = null; var styleInjected = false; + function getLocalCdnBase() { + var scriptSrc = currentScriptRef && currentScriptRef.src; + + if (!scriptSrc) { + return 'include/vditor'; + } + + return scriptSrc.replace(/\/vditor-adapter\.js(?:[?#].*)?$/, '/vditor'); + } + function injectAdapterStyle() { var style; @@ -85,6 +100,7 @@ function ensureAssets(options) { var cdn = (options && options.cdn) || DEFAULT_CDN; + global.HUSTOJ_VDITOR_CDN = cdn; if (global.Vditor) { injectAdapterStyle(); return Promise.resolve(global.Vditor); @@ -95,7 +111,7 @@ ensureStyleTag(cdn + '/dist/index.css'); injectAdapterStyle(); - assetsPromise = ensureScript(cdn + '/dist/index.min.js').then(function () { + assetsPromise = ensureScript(cdn + '/dist/index.js').then(function () { return global.Vditor; }); return assetsPromise; @@ -447,10 +463,6 @@ '|', 'undo', 'redo', - '|', - 'edit-mode', - 'both', - 'preview', 'fullscreen', 'outline' ]; diff --git a/web/include/vditor/LICENSE b/web/include/vditor/LICENSE new file mode 100644 index 0000000..b28e5ea --- /dev/null +++ b/web/include/vditor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 B3log 开源, b3log.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/web/include/vditor/README.md b/web/include/vditor/README.md new file mode 100644 index 0000000..b193430 --- /dev/null +++ b/web/include/vditor/README.md @@ -0,0 +1,680 @@ +
+
+
+
+易于使用的 Markdown 编辑器,为适配不同的应用场景而生
+
+
+
+
+
+
+
+
+
+
+
+
${Constants.ZWSP}
${quoteElement.innerHTML}
` : quoteElement.innerHTML; + } + } else if (commandName === "link") { + const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a") as HTMLElement; + if (aElement) { + const aTextElement = hasClosestByClassName(range.startContainer, "vditor-ir__link"); + if (aTextElement) { + range.insertNode(document.createElement("wbr")); + aElement.outerHTML = aTextElement.innerHTML; + } else { + aElement.outerHTML = aElement.querySelector(".vditor-ir__link").innerHTML + "${blockElement.outerHTML}`; + useHighlight = false; + actionBtn.classList.add("vditor-menu--current"); + } + } else if (commandName === "link") { + let html; + if (range.toString() === "") { + html = `${prefix}
[ToC]
" + tocHTML); + } else if (vditor.currentMode === "ir" && !vditor.preview.element.contains(contentElement)) { + tempElement.innerHTML = vditor.lute.SpinVditorIRDOM("[ToC]
" + tocHTML); + } else { + tempElement.innerHTML = vditor.lute.HTML2VditorDOM("[ToC]
" + tocHTML); + } + vditor.lute.SetToC(vditor.options.preview.markdown.toc); + } else { + targetElement.classList.add("vditor-outline"); + const lute = Lute.New(); + lute.SetToC(true); + tempElement.innerHTML = lute.HTML2VditorDOM("[ToC]
" + tocHTML); + } + const headingsElement = tempElement.firstElementChild.querySelectorAll("li > span[data-target-id]"); + headingsElement.forEach((item, index) => { + if (item.nextElementSibling && item.nextElementSibling.tagName === "UL") { + let iconHTML = ""; + if (!document.getElementById("vditorIconScript")) { + iconHTML = ''; + } + item.innerHTML = `${iconHTML}${item.innerHTML}`; + } else { + item.innerHTML = `${item.innerHTML}`; + } + item.setAttribute("data-target-id", ids[index]); + }); + tocHTML = tempElement.firstElementChild.innerHTML; + if (headingsElement.length === 0) { + targetElement.innerHTML = ""; + return tocHTML; + } + targetElement.innerHTML = tocHTML; + if (vditor) { + mathRender(targetElement as HTMLElement, { + cdn: vditor.options.cdn, + math: vditor.options.preview.math, + }); + } + targetElement.firstElementChild.addEventListener("click", (event: Event) => { + let target = event.target as HTMLElement; + while (target && !target.isEqualNode(targetElement)) { + if (target.classList.contains("vditor-outline__action")) { + if (target.classList.contains("vditor-outline__action--close")) { + target.classList.remove("vditor-outline__action--close"); + target.parentElement.nextElementSibling.setAttribute("style", "display:block"); + } else { + target.classList.add("vditor-outline__action--close"); + target.parentElement.nextElementSibling.setAttribute("style", "display:none"); + } + event.preventDefault(); + event.stopPropagation(); + break; + } else if (target.getAttribute("data-target-id")) { + event.preventDefault(); + event.stopPropagation(); + const idElement = document.getElementById(target.getAttribute("data-target-id")); + if (!idElement) { + return; + } + if (vditor) { + if (vditor.options.height === "auto") { + let windowScrollY = idElement.offsetTop + vditor.element.offsetTop; + if (!vditor.options.toolbarConfig.pin) { + windowScrollY += vditor.toolbar.element.offsetHeight; + } + window.scrollTo(window.scrollX, windowScrollY); + } else { + if (vditor.element.offsetTop < window.scrollY) { + window.scrollTo(window.scrollX, vditor.element.offsetTop); + } + if (vditor.preview.element.contains(contentElement)) { + contentElement.parentElement.scrollTop = idElement.offsetTop; + } else { + contentElement.scrollTop = idElement.offsetTop; + } + } + } else { + window.scrollTo(window.scrollX, idElement.offsetTop); + } + break; + } + target = target.parentElement; + } + }); + return tocHTML; +}; diff --git a/web/include/vditor/src/ts/markdown/plantumlRender.ts b/web/include/vditor/src/ts/markdown/plantumlRender.ts new file mode 100644 index 0000000..550f276 --- /dev/null +++ b/web/include/vditor/src/ts/markdown/plantumlRender.ts @@ -0,0 +1,32 @@ +import {Constants} from "../constants"; +import {addScript} from "../util/addScript"; +import {plantumlRenderAdapter} from "./adapterRender"; + +declare const plantumlEncoder: { + encode(options: string): string, +}; + +export const plantumlRender = (element: (HTMLElement | Document) = document, cdn = Constants.CDN) => { + const plantumlElements = plantumlRenderAdapter.getElements(element); + if (plantumlElements.length === 0) { + return; + } + addScript(`${cdn}/dist/js/plantuml/plantuml-encoder.min.js`, "vditorPlantumlScript").then(() => { + plantumlElements.forEach((e: HTMLDivElement) => { + if (e.parentElement.classList.contains("vditor-wysiwyg__pre") || + e.parentElement.classList.contains("vditor-ir__marker--pre")) { + return; + } + const text = plantumlRenderAdapter.getCode(e).trim(); + if (!text) { + return; + } + try { + e.innerHTML = ``; + } catch (error) { + e.className = "vditor-reset--error"; + e.innerHTML = `plantuml render error:+ 下一代的 Markdown 编辑器,为未来而构建 +
+
+ <audio controls="controls" src="${path}"></audio>${Constants.ZWSP}
${Constants.ZWSP}
${Constants.ZWSP}
${itemElement.parentElement.children[i].innerHTML.trimLeft()}
`; + } + itemElement.parentElement.insertAdjacentHTML("beforebegin", pHTML); + itemElement.parentElement.remove(); + } else { + if (!itemElement) { + // 添加 + let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0"); + if (!blockElement) { + vditor[vditor.currentMode].element.querySelector("wbr").remove(); + blockElement = vditor[vditor.currentMode].element.querySelector("p"); + blockElement.innerHTML = "${innerHTMLList.join("\n")}
`; + } + + pElement.insertAdjacentHTML("afterend", + `${pInnerHTML}${Constants.ZWSP}
${blockElement.outerHTML}`; + setRangeByWbr(vditor.wysiwyg.element, range); + afterRenderEvent(vditor); + event.preventDefault(); + return true; + } + + if (insertAfterBlock(vditor, event, range, blockquoteElement, blockquoteElement)) { + return true; + } + if (insertBeforeBlock(vditor, event, range, blockquoteElement, blockquoteElement)) { + return true; + } + } + return false; +}; + +export const fixTask = (vditor: IVditor, range: Range, event: KeyboardEvent) => { + const startContainer = range.startContainer; + const taskItemElement = hasClosestByClassName(startContainer, "vditor-task"); + if (taskItemElement) { + if (matchHotKey("⇧⌘J", event)) { + // ctrl + shift: toggle checked + const inputElement = taskItemElement.firstElementChild as HTMLInputElement; + if (inputElement.checked) { + inputElement.removeAttribute("checked"); + } else { + inputElement.setAttribute("checked", "checked"); + } + execAfterRender(vditor); + event.preventDefault(); + return true; + } + + // Backspace: 在选择框前进行删除 + if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey && range.toString() === "" + && range.startOffset === 1 + && ((startContainer.nodeType === 3 && startContainer.previousSibling && + (startContainer.previousSibling as HTMLElement).tagName === "INPUT") + || startContainer.nodeType !== 3)) { + const previousElement = taskItemElement.previousElementSibling; + taskItemElement.querySelector("input").remove(); + if (previousElement) { + const lastNode = getLastNode(previousElement); + lastNode.parentElement.insertAdjacentHTML("beforeend", "
\n
') === 0) { + // Xcode + isCode = true; + } + if (tempElement.childElementCount === 1 && tempElement.firstElementChild.tagName === "TABLE" && + tempElement.querySelector(".line-number") && tempElement.querySelector(".line-content")) { + // 网页源码 + isCode = true; + } + + if (isCode) { + const code = text || html; + if (/\n/.test(code) || pres.length === 1) { + if (type === "wysiwyg") { + return `
${
+ code.replace(/&/g, "&").replace(/ 获取位置信息
+ const parent = range.startContainer.parentElement;
+ if (parent && parent.getClientRects().length > 0) {
+ cursorRect = parent.getClientRects()[0];
+ } else {
+ return {
+ left: 0,
+ top: 0,
+ };
+ }
+ } else {
+ const children = (range.startContainer as Element).children;
+ if (children[range.startOffset] &&
+ children[range.startOffset].getClientRects().length > 0) {
+ // markdown 模式回车
+ cursorRect = children[range.startOffset].getClientRects()[0];
+ } else if (range.startContainer.childNodes.length > 0) {
+ // in table or code block
+ const cloneRange = range.cloneRange();
+ range.selectNode(range.startContainer.childNodes[Math.max(0, range.startOffset - 1)]);
+ cursorRect = range.getClientRects()[0];
+ range.setEnd(cloneRange.endContainer, cloneRange.endOffset);
+ range.setStart(cloneRange.startContainer, cloneRange.startOffset);
+ } else {
+ cursorRect = (range.startContainer as HTMLElement).getClientRects()[0];
+ }
+ if (!cursorRect) {
+ let parentElement = range.startContainer.childNodes[range.startOffset] as HTMLElement;
+ while (!parentElement.getClientRects ||
+ (parentElement.getClientRects && parentElement.getClientRects().length === 0)) {
+ parentElement = parentElement.parentElement;
+ }
+ cursorRect = parentElement.getClientRects()[0];
+ }
+ }
+
+ } else {
+ cursorRect = range.getClientRects()[0];
+ }
+
+ return {
+ left: cursorRect.left - parentRect.left,
+ top: cursorRect.top - parentRect.top,
+ };
+};
+
+export const selectIsEditor = (editor: HTMLElement, range?: Range) => {
+ if (!range) {
+ if (getSelection().rangeCount === 0) {
+ return false;
+ } else {
+ range = getSelection().getRangeAt(0);
+ }
+ }
+ const container = range.commonAncestorContainer;
+
+ return editor.isEqualNode(container) || editor.contains(container);
+};
+
+export const setSelectionFocus = (range: Range) => {
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+};
+
+export const getSelectPosition = (selectElement: HTMLElement, editorElement: HTMLElement, range?: Range) => {
+ const position = {
+ end: 0,
+ start: 0,
+ };
+
+ if (!range) {
+ if (getSelection().rangeCount === 0) {
+ return position;
+ }
+ range = window.getSelection().getRangeAt(0);
+ }
+
+ if (selectIsEditor(editorElement, range)) {
+ const preSelectionRange = range.cloneRange();
+ if (selectElement.childNodes[0] && selectElement.childNodes[0].childNodes[0]) {
+ preSelectionRange.setStart(selectElement.childNodes[0].childNodes[0], 0);
+ } else {
+ preSelectionRange.selectNodeContents(selectElement);
+ }
+ preSelectionRange.setEnd(range.startContainer, range.startOffset);
+ position.start = preSelectionRange.toString().length;
+ position.end = position.start + range.toString().length;
+ }
+ return position;
+};
+
+export const setSelectionByPosition = (start: number, end: number, editor: HTMLElement) => {
+ let charIndex = 0;
+ let line = 0;
+ let pNode = editor.childNodes[line];
+ let foundStart = false;
+ let stop = false;
+ start = Math.max(0, start);
+ end = Math.max(0, end);
+
+ const range = editor.ownerDocument.createRange();
+ range.setStart(pNode || editor, 0);
+ range.collapse(true);
+
+ while (!stop && pNode) {
+ const nextCharIndex = charIndex + pNode.textContent.length;
+ if (!foundStart && start >= charIndex && start <= nextCharIndex) {
+ if (start === 0) {
+ range.setStart(pNode, 0);
+ } else {
+ if (pNode.childNodes[0].nodeType === 3) {
+ range.setStart(pNode.childNodes[0], start - charIndex);
+ } else if (pNode.nextSibling) {
+ range.setStartBefore(pNode.nextSibling);
+ } else {
+ range.setStartAfter(pNode);
+ }
+ }
+ foundStart = true;
+ if (start === end) {
+ stop = true;
+ break;
+ }
+ }
+ if (foundStart && end >= charIndex && end <= nextCharIndex) {
+ if (end === 0) {
+ range.setEnd(pNode, 0);
+ } else {
+ if (pNode.childNodes[0].nodeType === 3) {
+ range.setEnd(pNode.childNodes[0], end - charIndex);
+ } else if (pNode.nextSibling) {
+ range.setEndBefore(pNode.nextSibling);
+ } else {
+ range.setEndAfter(pNode);
+ }
+ }
+ stop = true;
+ }
+ charIndex = nextCharIndex;
+ pNode = editor.childNodes[++line];
+ }
+
+ if (!stop && editor.childNodes[line - 1]) {
+ range.setStartBefore(editor.childNodes[line - 1]);
+ }
+
+ setSelectionFocus(range);
+ return range;
+};
+
+export const setRangeByWbr = (element: HTMLElement, range: Range) => {
+ const wbrElement = element.querySelector("wbr");
+ if (!wbrElement) {
+ return;
+ }
+ if (!wbrElement.previousElementSibling) {
+ if (wbrElement.previousSibling) {
+ // text ${Constants.ZWSP} ${node.innerHTML} ${node.outerHTML} ${node.outerHTML} ``` ``` ${blockElement.innerHTML} ${quoteElement.innerHTML}
https://github.com/Vanessa219/vditor/issues/400
+ range.setStartBefore(wbrElement.nextSibling);
+ }
+ } else {
+ // 内容为空
+ range.setStart(wbrElement.parentElement, 0);
+ }
+ } else {
+ if (wbrElement.previousElementSibling.isSameNode(wbrElement.previousSibling)) {
+ if (wbrElement.previousElementSibling.lastChild) {
+ // text ");
+ } else {
+ tableElement.rows[i].lastElementChild.insertAdjacentHTML("afterend", " ");
+ }
+ }
+ } else {
+ for (let k = oldColumn - 1; k >= column; k--) {
+ tableElement.rows[i].cells[k].remove();
+ }
+ }
+ }
+ }
+
+ if (oldRow !== row) {
+ const rowDiff = row - oldRow;
+ if (rowDiff > 0) {
+ let rowHTML = "";
+ for (let m = 0; m < column; m++) {
+ rowHTML += " ");
+ }
+ }
+ } else {
+ for (let m = oldRow - 1; m >= row; m--) {
+ tableElement.rows[m].remove();
+ if (tableElement.rows.length === 1) {
+ tableElement.querySelector("tbody").remove();
+ }
+ }
+ }
+ }
+ if (typeof vditor.options.input === "function") {
+ vditor.options.input(getMarkdown(vditor));
+ }
+ };
+
+ const setAlign = (type: string) => {
+ setTableAlign(tableElement, type);
+ if (type === "right") {
+ left.classList.remove("vditor-icon--current");
+ center.classList.remove("vditor-icon--current");
+ right.classList.add("vditor-icon--current");
+ } else if (type === "center") {
+ left.classList.remove("vditor-icon--current");
+ right.classList.remove("vditor-icon--current");
+ center.classList.add("vditor-icon--current");
+ } else {
+ center.classList.remove("vditor-icon--current");
+ right.classList.remove("vditor-icon--current");
+ left.classList.add("vditor-icon--current");
+ }
+ setSelectionFocus(range);
+ afterRenderEvent(vditor);
+ };
+
+ const td = hasClosestByMatchTag(typeElement, "TD");
+ const th = hasClosestByMatchTag(typeElement, "TH");
+ let alignType = "left";
+ if (td) {
+ alignType = td.getAttribute("align") || "left";
+ } else if (th) {
+ alignType = th.getAttribute("align") || "center";
+ }
+
+ const left = document.createElement("button");
+ left.setAttribute("type", "button");
+ left.setAttribute("aria-label", window.VditorI18n.alignLeft + "<" + updateHotkeyTip("⇧⌘L") + ">");
+ left.setAttribute("data-type", "left");
+ left.innerHTML =
+ '';
+ left.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
+ (alignType === "left" ? " vditor-icon--current" : "");
+ left.onclick = () => {
+ setAlign("left");
+ };
+
+ const center = document.createElement("button");
+ center.setAttribute("type", "button");
+ center.setAttribute("aria-label", window.VditorI18n.alignCenter + "<" + updateHotkeyTip("⇧⌘C") + ">");
+ center.setAttribute("data-type", "center");
+ center.innerHTML =
+ '';
+ center.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
+ (alignType === "center" ? " vditor-icon--current" : "");
+ center.onclick = () => {
+ setAlign("center");
+ };
+
+ const right = document.createElement("button");
+ right.setAttribute("type", "button");
+ right.setAttribute("aria-label", window.VditorI18n.alignRight + "<" + updateHotkeyTip("⇧⌘R") + ">");
+ right.setAttribute("data-type", "right");
+ right.innerHTML =
+ '';
+ right.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
+ (alignType === "right" ? " vditor-icon--current" : "");
+ right.onclick = () => {
+ setAlign("right");
+ };
+
+ const insertRowElement = document.createElement("button");
+ insertRowElement.setAttribute("type", "button");
+ insertRowElement.setAttribute("aria-label", window.VditorI18n.insertRowBelow + "<" + updateHotkeyTip("⌘=") + ">");
+ insertRowElement.setAttribute("data-type", "insertRow");
+ insertRowElement.innerHTML =
+ '';
+ insertRowElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ insertRowElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ insertRow(vditor, range, cellElement);
+ }
+ };
+
+ const insertRowBElement = document.createElement("button");
+ insertRowBElement.setAttribute("type", "button");
+ insertRowBElement.setAttribute("aria-label",
+ window.VditorI18n.insertRowAbove + "<" + updateHotkeyTip("⇧⌘F") + ">");
+ insertRowBElement.setAttribute("data-type", "insertRow");
+ insertRowBElement.innerHTML =
+ '';
+ insertRowBElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ insertRowBElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ insertRowAbove(vditor, range, cellElement);
+ }
+ };
+
+ const insertColumnElement = document.createElement("button");
+ insertColumnElement.setAttribute("type", "button");
+ insertColumnElement.setAttribute("aria-label", window.VditorI18n.insertColumnRight + "<" + updateHotkeyTip("⇧⌘=") + ">");
+ insertColumnElement.setAttribute("data-type", "insertColumn");
+ insertColumnElement.innerHTML =
+ '';
+ insertColumnElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ insertColumnElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ insertColumn(vditor, tableElement, cellElement);
+ }
+ };
+
+ const insertColumnBElement = document.createElement("button");
+ insertColumnBElement.setAttribute("type", "button");
+ insertColumnBElement.setAttribute("aria-label", window.VditorI18n.insertColumnLeft + "<" + updateHotkeyTip("⇧⌘G") + ">");
+ insertColumnBElement.setAttribute("data-type", "insertColumn");
+ insertColumnBElement.innerHTML =
+ '';
+ insertColumnBElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ insertColumnBElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ insertColumn(vditor, tableElement, cellElement, "beforebegin");
+ }
+ };
+
+ const deleteRowElement = document.createElement("button");
+ deleteRowElement.setAttribute("type", "button");
+ deleteRowElement.setAttribute("aria-label", window.VditorI18n["delete-row"] + "<" + updateHotkeyTip("⌘-") + ">");
+ deleteRowElement.setAttribute("data-type", "deleteRow");
+ deleteRowElement.innerHTML =
+ '';
+ deleteRowElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ deleteRowElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ deleteRow(vditor, range, cellElement);
+ }
+ };
+
+ const deleteColumnElement = document.createElement("button");
+ deleteColumnElement.setAttribute("type", "button");
+ deleteColumnElement.setAttribute("aria-label", window.VditorI18n["delete-column"] + "<" + updateHotkeyTip("⇧⌘-") + ">");
+ deleteColumnElement.setAttribute("data-type", "deleteColumn");
+ deleteColumnElement.innerHTML =
+ '';
+ deleteColumnElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ deleteColumnElement.onclick = () => {
+ const startContainer = getSelection().getRangeAt(0)
+ .startContainer;
+ const cellElement =
+ hasClosestByMatchTag(startContainer, "TD") ||
+ hasClosestByMatchTag(startContainer, "TH");
+ if (cellElement) {
+ deleteColumn(vditor, range, tableElement, cellElement);
+ }
+ };
+
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", window.VditorI18n.row);
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input = document.createElement("input");
+ inputWrap.appendChild(input);
+ input.type = "number";
+ input.min = "1";
+ input.className = "vditor-input";
+ input.style.width = "42px";
+ input.style.textAlign = "center";
+ input.setAttribute("placeholder", window.VditorI18n.row);
+ input.value = tableElement.rows.length.toString();
+ input.oninput = () => {
+ updateTable();
+ };
+ input.onkeydown = (event) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (event.key === "Tab") {
+ input2.focus();
+ input2.select();
+ event.preventDefault();
+ return;
+ }
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ };
+
+ const input2Wrap = document.createElement("span");
+ input2Wrap.setAttribute("aria-label", window.VditorI18n.column);
+ input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input2 = document.createElement("input");
+ input2Wrap.appendChild(input2);
+ input2.type = "number";
+ input2.min = "1";
+ input2.className = "vditor-input";
+ input2.style.width = "42px";
+ input2.style.textAlign = "center";
+ input2.setAttribute("placeholder", window.VditorI18n.column);
+ input2.value = tableElement.rows[0].cells.length.toString();
+ input2.oninput = () => {
+ updateTable();
+ };
+ input2.onkeydown = (event) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (event.key === "Tab") {
+ input.focus();
+ input.select();
+ event.preventDefault();
+ return;
+ }
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ };
+
+ genUp(range, tableElement, vditor);
+ genDown(range, tableElement, vditor);
+ genClose(tableElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", left);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", center);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", right);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowBElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnBElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteRowElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteColumnElement);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
+ setPopoverPosition(vditor, tableElement);
+ }
+
+ // link ref popover
+ const linkRefElement = hasClosestByAttribute(typeElement, "data-type", "link-ref");
+ if (linkRefElement) {
+ genLinkRefPopover(vditor, linkRefElement, range);
+ }
+
+ // footnote popover
+ const footnotesRefElement = hasClosestByAttribute(typeElement, "data-type", "footnotes-ref");
+ if (footnotesRefElement) {
+ const lang: keyof II18n | "" = vditor.options.lang;
+ const options: IOptions = vditor.options;
+ vditor.wysiwyg.popover.innerHTML = "";
+
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input = document.createElement("input");
+ inputWrap.appendChild(input);
+ input.className = "vditor-input";
+ input.setAttribute("placeholder", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
+ input.style.width = "120px";
+ input.value = footnotesRefElement.getAttribute("data-footnotes-label");
+ input.oninput = () => {
+ if (input.value.trim() !== "") {
+ footnotesRefElement.setAttribute("data-footnotes-label", input.value);
+ }
+ if (typeof vditor.options.input === "function") {
+ vditor.options.input(getMarkdown(vditor));
+ }
+ };
+ input.onkeydown = (event) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ };
+
+ genClose(footnotesRefElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ setPopoverPosition(vditor, footnotesRefElement);
+ }
+
+ // block popover: math-inline, math-block, html-block, html-inline, code-block, html-entity
+ let blockRenderElement = hasClosestByClassName(typeElement, "vditor-wysiwyg__block") as HTMLElement;
+ const isBlock = blockRenderElement ? blockRenderElement.getAttribute("data-type").indexOf("block") > -1 : false;
+ vditor.wysiwyg.element
+ .querySelectorAll(".vditor-wysiwyg__preview")
+ .forEach((itemElement) => {
+ if (!blockRenderElement || (blockRenderElement && isBlock && !blockRenderElement.contains(itemElement))) {
+ const previousElement = itemElement.previousElementSibling as HTMLElement;
+ previousElement.style.display = "none";
+ }
+ });
+ if (blockRenderElement && isBlock) {
+ vditor.wysiwyg.popover.innerHTML = "";
+ genUp(range, blockRenderElement, vditor);
+ genDown(range, blockRenderElement, vditor);
+ genClose(blockRenderElement, vditor);
+
+ if (blockRenderElement.getAttribute("data-type") === "code-block") {
+ const languageWrap = document.createElement("span");
+ languageWrap.setAttribute("aria-label", window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
+ languageWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const language = document.createElement("input");
+ languageWrap.appendChild(language);
+
+ const codeElement =
+ blockRenderElement.firstElementChild.firstElementChild;
+
+ language.className = "vditor-input";
+ language.setAttribute("placeholder",
+ window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
+ language.value =
+ codeElement.className.indexOf("language-") > -1
+ ? codeElement.className.split("-")[1].split(" ")[0]
+ : "";
+ language.oninput = (e: InputEvent) => {
+ if (language.value.trim() !== "") {
+ codeElement.className = `language-${language.value}`;
+ } else {
+ codeElement.className = "";
+ vditor.hint.recentLanguage = "";
+ }
+ if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
+ blockRenderElement.lastElementChild.innerHTML =
+ blockRenderElement.firstElementChild.innerHTML;
+ processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
+ }
+ afterRenderEvent(vditor);
+ // 当鼠标点选语言时,触发自定义input事件
+ if (e.detail === 1) {
+ // 选择语言后,输入焦点切换到代码输入框
+ range.setStart(codeElement.firstChild, 0);
+ range.collapse(true);
+ setSelectionFocus(range);
+ }
+ };
+ language.onkeydown = (event: KeyboardEvent) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (
+ event.key === "Escape" &&
+ vditor.hint.element.style.display === "block"
+ ) {
+ vditor.hint.element.style.display = "none";
+ event.preventDefault();
+ return;
+ }
+ vditor.hint.select(event, vditor);
+ focusToElement(event, range);
+ };
+ language.onkeyup = (event: KeyboardEvent) => {
+ if (
+ event.isComposing ||
+ event.key === "Enter" ||
+ event.key === "ArrowUp" ||
+ event.key === "Escape" ||
+ event.key === "ArrowDown"
+ ) {
+ return;
+ }
+ const matchLangData: IHintData[] = [];
+ const key = language.value.substring(0, language.selectionStart);
+ (vditor.options.preview.hljs.langs || Constants.CODE_LANGUAGES).forEach((keyName) => {
+ if (keyName.indexOf(key.toLowerCase()) > -1) {
+ matchLangData.push({
+ html: keyName,
+ value: keyName,
+ });
+ }
+ });
+ vditor.hint.genHTML(matchLangData, key, vditor);
+ event.preventDefault();
+ };
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
+ }
+ setPopoverPosition(vditor, blockRenderElement);
+ } else {
+ blockRenderElement = undefined;
+ }
+ if (headingElement) {
+ vditor.wysiwyg.popover.innerHTML = "";
+
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input = document.createElement("input");
+ inputWrap.appendChild(input);
+ input.className = "vditor-input";
+ input.setAttribute("placeholder", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
+ input.style.width = "120px";
+ input.value = headingElement.getAttribute("data-id") || "";
+ input.oninput = () => {
+ headingElement.setAttribute("data-id", input.value);
+ if (typeof vditor.options.input === "function") {
+ vditor.options.input(getMarkdown(vditor));
+ }
+ };
+ input.onkeydown = (event) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ };
+
+ genUp(range, headingElement, vditor);
+ genDown(range, headingElement, vditor);
+ genClose(headingElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ setPopoverPosition(vditor, headingElement);
+ }
+
+ // a popover
+ if (aElement) {
+ genAPopover(vditor, aElement, range);
+ }
+
+ if (
+ !blockquoteElement &&
+ !liElement &&
+ !tableElement &&
+ !blockRenderElement &&
+ !aElement &&
+ !linkRefElement &&
+ !footnotesRefElement &&
+ !headingElement &&
+ !tocElement
+ ) {
+ const blockElement = hasClosestByAttribute(typeElement, "data-block", "0");
+ if (
+ blockElement &&
+ blockElement.parentElement.isEqualNode(vditor.wysiwyg.element)
+ ) {
+ vditor.wysiwyg.popover.innerHTML = "";
+ genUp(range, blockElement, vditor);
+ genDown(range, blockElement, vditor);
+ genClose(blockElement, vditor);
+
+ setPopoverPosition(vditor, blockElement);
+ } else {
+ vditor.wysiwyg.popover.style.display = "none";
+ }
+ }
+
+ // 反斜杠特殊处理
+ vditor.wysiwyg.element
+ .querySelectorAll('span[data-type="backslash"] > span')
+ .forEach((item: HTMLElement) => {
+ item.style.display = "none";
+ });
+ const backslashElement = hasClosestByAttribute(range.startContainer, "data-type", "backslash");
+ if (backslashElement) {
+ backslashElement.querySelector("span").style.display = "inline";
+ }
+ }, 200);
+};
+
+const setPopoverPosition = (vditor: IVditor, element: HTMLElement) => {
+ let targetElement = element;
+ const tableElement = hasClosestByMatchTag(element, "TABLE");
+ if (tableElement) {
+ targetElement = tableElement;
+ }
+ vditor.wysiwyg.popover.style.left = "0";
+ vditor.wysiwyg.popover.style.display = "block";
+ vditor.wysiwyg.popover.style.top =
+ Math.max(-8, targetElement.offsetTop - 21 - vditor.wysiwyg.element.scrollTop) + "px";
+ vditor.wysiwyg.popover.style.left =
+ Math.min(targetElement.offsetLeft, vditor.wysiwyg.element.clientWidth - vditor.wysiwyg.popover.clientWidth) + "px";
+ vditor.wysiwyg.popover.setAttribute("data-top", (targetElement.offsetTop - 21).toString());
+};
+
+export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement, range = getSelection().getRangeAt(0)) => {
+ vditor.wysiwyg.popover.innerHTML = "";
+ const updateLinkRef = () => {
+ if (input.value.trim() !== "") {
+ if (linkRefElement.tagName === "IMG") {
+ linkRefElement.setAttribute("alt", input.value);
+ } else {
+ linkRefElement.textContent = input.value;
+ }
+ }
+ // data-link-label
+ if (input1.value.trim() !== "") {
+ linkRefElement.setAttribute("data-link-label", input1.value);
+ }
+ if (typeof vditor.options.input === "function") {
+ vditor.options.input(getMarkdown(vditor));
+ }
+ };
+
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input = document.createElement("input");
+ inputWrap.appendChild(input);
+ input.className = "vditor-input";
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
+ input.style.width = "120px";
+ input.value =
+ linkRefElement.getAttribute("alt") || linkRefElement.textContent;
+ input.oninput = () => {
+ updateLinkRef();
+ };
+ input.onkeydown = (event) => {
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ linkHotkey(vditor, linkRefElement, event, input1);
+ };
+
+ const input1Wrap = document.createElement("span");
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.linkRef);
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input1 = document.createElement("input");
+ input1Wrap.appendChild(input1);
+ input1.className = "vditor-input";
+ input1.setAttribute("placeholder", window.VditorI18n.linkRef);
+ input1.value = linkRefElement.getAttribute("data-link-label");
+ input1.oninput = () => {
+ updateLinkRef();
+ };
+ input1.onkeydown = (event) => {
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ linkHotkey(vditor, linkRefElement, event, input);
+ };
+
+ genClose(linkRefElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
+ setPopoverPosition(vditor, linkRefElement);
+};
+
+const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
+ const previousElement = element.previousElementSibling;
+ if (
+ !previousElement ||
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
+ element.tagName !== "LI")
+ ) {
+ return;
+ }
+ const upElement = document.createElement("button");
+ upElement.setAttribute("type", "button");
+ upElement.setAttribute("data-type", "up");
+ upElement.setAttribute("aria-label", window.VditorI18n.up + "<" + updateHotkeyTip("⇧⌘U") + ">");
+ upElement.innerHTML = '';
+ upElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ upElement.onclick = () => {
+ range.insertNode(document.createElement("wbr"));
+ previousElement.insertAdjacentElement("beforebegin", element);
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ afterRenderEvent(vditor);
+ highlightToolbarWYSIWYG(vditor);
+ scrollCenter(vditor);
+ };
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
+};
+
+const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
+ const nextElement = element.nextElementSibling;
+ if (
+ !nextElement ||
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
+ element.tagName !== "LI")
+ ) {
+ return;
+ }
+ const downElement = document.createElement("button");
+ downElement.setAttribute("type", "button");
+ downElement.setAttribute("data-type", "down");
+ downElement.setAttribute("aria-label", window.VditorI18n.down + "<" + updateHotkeyTip("⇧⌘D") + ">");
+ downElement.innerHTML =
+ '';
+ downElement.className =
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ downElement.onclick = () => {
+ range.insertNode(document.createElement("wbr"));
+ nextElement.insertAdjacentElement("afterend", element);
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ afterRenderEvent(vditor);
+ highlightToolbarWYSIWYG(vditor);
+ scrollCenter(vditor);
+ };
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
+};
+
+const genClose = (element: HTMLElement, vditor: IVditor) => {
+ const close = document.createElement("button");
+ close.setAttribute("type", "button");
+ close.setAttribute("data-type", "remove");
+ close.setAttribute("aria-label", window.VditorI18n.remove + "<" + updateHotkeyTip("⇧⌘X") + ">");
+ close.innerHTML =
+ '';
+ close.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
+ close.onclick = () => {
+ const range = getEditorRange(vditor);
+ range.setStartAfter(element);
+ setSelectionFocus(range);
+ element.remove();
+ afterRenderEvent(vditor);
+ highlightToolbarWYSIWYG(vditor);
+ if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
+ renderToc(vditor);
+ }
+ };
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
+};
+
+const linkHotkey = (
+ vditor: IVditor,
+ element: HTMLElement,
+ event: KeyboardEvent,
+ nextInputElement: HTMLInputElement,
+) => {
+ if (event.isComposing) {
+ return;
+ }
+ if (event.key === "Tab") {
+ nextInputElement.focus();
+ nextInputElement.select();
+ event.preventDefault();
+ return;
+ }
+ if (
+ !isCtrl(event) &&
+ !event.shiftKey &&
+ event.altKey &&
+ event.key === "Enter"
+ ) {
+ const range = getEditorRange(vditor);
+ // firefox 不会打断 link https://github.com/Vanessa219/vditor/issues/193
+ element.insertAdjacentHTML("afterend", Constants.ZWSP);
+ range.setStartAfter(element.nextSibling);
+ range.collapse(true);
+ setSelectionFocus(range);
+ event.preventDefault();
+ }
+};
+
+export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range) => {
+ vditor.wysiwyg.popover.innerHTML = "";
+
+ const updateA = () => {
+ if (input.value.trim() !== "") {
+ aElement.innerHTML = input.value;
+ }
+ aElement.setAttribute("href", input1.value);
+ aElement.setAttribute("title", input2.value);
+ afterRenderEvent(vditor);
+ };
+
+ aElement.querySelectorAll("[data-marker]").forEach((item: HTMLElement) => {
+ item.removeAttribute("data-marker");
+ });
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input = document.createElement("input");
+ inputWrap.appendChild(input);
+ input.className = "vditor-input";
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
+ input.style.width = "120px";
+ input.value = aElement.innerHTML || "";
+ input.oninput = () => {
+ updateA();
+ };
+ input.onkeydown = (event) => {
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ linkHotkey(vditor, aElement, event, input1);
+ };
+
+ const input1Wrap = document.createElement("span");
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.link);
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input1 = document.createElement("input");
+ input1Wrap.appendChild(input1);
+ input1.className = "vditor-input";
+ input1.setAttribute("placeholder", window.VditorI18n.link);
+ input1.value = aElement.getAttribute("href") || "";
+ input1.oninput = () => {
+ updateA();
+ };
+ input1.onkeydown = (event) => {
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ linkHotkey(vditor, aElement, event, input2);
+ };
+
+ const input2Wrap = document.createElement("span");
+ input2Wrap.setAttribute("aria-label", window.VditorI18n.tooltipText);
+ input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const input2 = document.createElement("input");
+ input2Wrap.appendChild(input2);
+ input2.className = "vditor-input";
+ input2.setAttribute("placeholder", window.VditorI18n.tooltipText);
+ input2.style.width = "60px";
+ input2.value = aElement.getAttribute("title") || "";
+ input2.oninput = () => {
+ updateA();
+ };
+ input2.onkeydown = (event) => {
+ if (removeBlockElement(vditor, event)) {
+ return;
+ }
+ if (focusToElement(event, range)) {
+ return;
+ }
+ linkHotkey(vditor, aElement, event, input);
+ };
+
+ genClose(aElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
+ setPopoverPosition(vditor, aElement);
+};
+
+export const genImagePopover = (event: Event, vditor: IVditor) => {
+ const imgElement = event.target as HTMLImageElement;
+ vditor.wysiwyg.popover.innerHTML = "";
+ const updateImg = () => {
+ imgElement.setAttribute("src", inputElement.value);
+ imgElement.setAttribute("alt", alt.value);
+ imgElement.setAttribute("title", title.value);
+ if (typeof vditor.options.input === "function") {
+ vditor.options.input(getMarkdown(vditor));
+ }
+ };
+
+ const inputWrap = document.createElement("span");
+ inputWrap.setAttribute("aria-label", window.VditorI18n.imageURL);
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const inputElement = document.createElement("input");
+ inputWrap.appendChild(inputElement);
+ inputElement.className = "vditor-input";
+ inputElement.setAttribute("placeholder", window.VditorI18n.imageURL);
+ inputElement.value = imgElement.getAttribute("src") || "";
+ inputElement.oninput = () => {
+ updateImg();
+ };
+ inputElement.onkeydown = (elementEvent) => {
+ removeBlockElement(vditor, elementEvent);
+ };
+
+ const altWrap = document.createElement("span");
+ altWrap.setAttribute("aria-label", window.VditorI18n.alternateText);
+ altWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const alt = document.createElement("input");
+ altWrap.appendChild(alt);
+ alt.className = "vditor-input";
+ alt.setAttribute("placeholder", window.VditorI18n.alternateText);
+ alt.style.width = "52px";
+ alt.value = imgElement.getAttribute("alt") || "";
+ alt.oninput = () => {
+ updateImg();
+ };
+ alt.onkeydown = (elementEvent) => {
+ removeBlockElement(vditor, elementEvent);
+ };
+
+ const titleWrap = document.createElement("span");
+ titleWrap.setAttribute("aria-label", window.VditorI18n.title);
+ titleWrap.className = "vditor-tooltipped vditor-tooltipped__n";
+ const title = document.createElement("input");
+ titleWrap.appendChild(title);
+ title.className = "vditor-input";
+ title.setAttribute("placeholder", window.VditorI18n.title);
+ title.value = imgElement.getAttribute("title") || "";
+ title.oninput = () => {
+ updateImg();
+ };
+ title.onkeydown = (elementEvent) => {
+ removeBlockElement(vditor, elementEvent);
+ };
+ genClose(imgElement, vditor);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
+
+ setPopoverPosition(vditor, imgElement);
+};
+
+
+const focusToElement = (event: KeyboardEvent, range: Range) => {
+ if ((!isCtrl(event) && !event.shiftKey && event.key === "Enter") || event.key === "Escape") {
+ if (range) {
+ setSelectionFocus(range);
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ return true;
+ }
+};
diff --git a/web/include/vditor/src/ts/wysiwyg/index.ts b/web/include/vditor/src/ts/wysiwyg/index.ts
new file mode 100644
index 0000000..bd3aabe
--- /dev/null
+++ b/web/include/vditor/src/ts/wysiwyg/index.ts
@@ -0,0 +1,575 @@
+import {Constants} from "../constants";
+import {hidePanel} from "../toolbar/setToolbar";
+import {isCtrl, isFirefox} from "../util/compatibility";
+import {
+ blurEvent,
+ copyEvent, cutEvent, dblclickEvent,
+ dropEvent,
+ focusEvent,
+ hotkeyEvent,
+ scrollCenter,
+ selectEvent,
+} from "../util/editorCommonEvent";
+import {isHeadingMD, isHrMD, paste} from "../util/fixBrowserBehavior";
+import {
+ hasClosestBlock, hasClosestByAttribute,
+ hasClosestByClassName, hasClosestByMatchTag,
+} from "../util/hasClosest";
+import {hasClosestByHeadings} from "../util/hasClosestByHeadings";
+import {
+ getCursorPosition,
+ getEditorRange,
+ getSelectPosition,
+ setRangeByWbr,
+} from "../util/selection";
+import {clickToc, renderToc} from "../util/toc";
+import {afterRenderEvent} from "./afterRenderEvent";
+import {genImagePopover, genLinkRefPopover, highlightToolbarWYSIWYG} from "./highlightToolbarWYSIWYG";
+import {getRenderElementNextNode, modifyPre} from "./inlineTag";
+import {input} from "./input";
+import {showCode} from "./showCode";
+import {getMarkdown} from "../markdown/getMarkdown";
+
+class WYSIWYG {
+ public range: Range;
+ public element: HTMLPreElement;
+ public popover: HTMLDivElement;
+ public selectPopover: HTMLDivElement;
+ public afterRenderTimeoutId: number;
+ public hlToolbarTimeoutId: number;
+ public preventInput: boolean;
+ public composingLock = false;
+ public commentIds: string[] = [];
+ private scrollListener: () => void;
+
+ constructor(vditor: IVditor) {
+ const divElement = document.createElement("div");
+ divElement.className = "vditor-wysiwyg";
+
+ divElement.innerHTML = `
+
+ ";
+ }
+ for (let l = 0; l < rowDiff; l++) {
+ if (tableElement.querySelector("tbody")) {
+ tableElement
+ .querySelector("tbody")
+ .insertAdjacentHTML("beforeend", rowHTML);
+ } else {
+ tableElement
+ .querySelector("thead")
+ .insertAdjacentHTML("afterend", rowHTML + "/g, "");
+
+ if (html === '
";
+ }
+ if (blockElement.tagName === "BLOCKQUOTE" || blockElement.classList.contains("vditor-reset")) {
+ blockElement.innerHTML = `<${tagName} data-block="0">${blockElement.innerHTML.trim()}${tagName}>`;
+ } else {
+ blockElement.outerHTML = `<${tagName} data-block="0">${blockElement.innerHTML.trim()}${tagName}>`;
+ }
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ renderToc(vditor);
+ }
+};
+
+export const removeHeading = (vditor: IVditor) => {
+ const range = getSelection().getRangeAt(0);
+ let blockElement = hasClosestBlock(range.startContainer);
+ if (!blockElement) {
+ blockElement = range.startContainer.childNodes[range.startOffset] as HTMLElement;
+ }
+ if (blockElement) {
+ range.insertNode(document.createElement("wbr"));
+ blockElement.outerHTML = `${liElement.innerHTML}
`;
+ } else {
+ blockElement.outerHTML = `${blockElement.outerHTML}
`;
+ }
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ }
+ } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") {
+ listToggle(vditor, range, commandName, false);
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ useHighlight = false;
+ removeCurrentToolbar(vditor.toolbar.elements, ["check", "list", "ordered-list"]);
+ actionBtn.classList.add("vditor-menu--current");
+ } else if (commandName === "inline-code") {
+ if (range.toString() === "") {
+ const node = document.createElement("code");
+ node.textContent = Constants.ZWSP;
+ range.insertNode(node);
+ range.setStart(node.firstChild, 1);
+ range.collapse(true);
+ setSelectionFocus(range);
+ } else if (range.startContainer.nodeType === 3) {
+ const node = document.createElement("code");
+ range.surroundContents(node);
+ range.insertNode(node);
+ setSelectionFocus(range);
+ }
+ actionBtn.classList.add("vditor-menu--current");
+ } else if (commandName === "code") {
+ const node = document.createElement("div");
+ node.className = "vditor-wysiwyg__block";
+ node.setAttribute("data-type", "code-block");
+ node.setAttribute("data-block", "0");
+ node.setAttribute("data-marker", "```");
+ if (range.toString() === "") {
+ node.innerHTML = "
";
+ } else {
+ node.innerHTML = `
`;
+ range.deleteContents();
+ }
+ range.insertNode(node);
+ if (blockElement) {
+ blockElement.outerHTML = vditor.lute.SpinVditorDOM(blockElement.outerHTML);
+ }
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ vditor.wysiwyg.element.querySelectorAll(".vditor-wysiwyg__preview[data-render='2']").forEach(
+ (item: HTMLElement) => {
+ processCodeRender(item, vditor);
+ });
+ actionBtn.classList.add("vditor-menu--disabled");
+ } else if (commandName === "link") {
+ if (range.toString() === "") {
+ const aElement = document.createElement("a");
+ aElement.innerText = Constants.ZWSP;
+ range.insertNode(aElement);
+ range.setStart(aElement.firstChild, 1);
+ range.collapse(true);
+ genAPopover(vditor, aElement, range);
+ const textInputElement = vditor.wysiwyg.popover.querySelector("input");
+ textInputElement.value = "";
+ textInputElement.focus();
+ useRender = false;
+ } else {
+ const node = document.createElement("a");
+ node.setAttribute("href", "");
+ node.innerHTML = range.toString();
+ range.surroundContents(node);
+ range.insertNode(node);
+ setSelectionFocus(range);
+ genAPopover(vditor, node, range);
+ const textInputElements = vditor.wysiwyg.popover.querySelectorAll("input");
+ textInputElements[0].value = node.innerText;
+ textInputElements[1].focus();
+ }
+ useHighlight = false;
+ actionBtn.classList.add("vditor-menu--current");
+ } else if (commandName === "table") {
+ let tableHTML = `${range.toString()}
`;
+ if (range.toString().trim() === "") {
+ if (blockElement && blockElement.innerHTML.trim().replace(Constants.ZWSP, "") === "") {
+ blockElement.outerHTML = tableHTML;
+ } else {
+ document.execCommand("insertHTML", false, tableHTML);
+ }
+ range.selectNode(vditor.wysiwyg.element.querySelector("wbr").previousSibling);
+ vditor.wysiwyg.element.querySelector("wbr").remove();
+ setSelectionFocus(range);
+ } else {
+ tableHTML = `col1 col2 col3
";
+ document.execCommand("insertHTML", false, tableHTML);
+ setRangeByWbr(vditor.wysiwyg.element, range);
+ }
+ useHighlight = false;
+ actionBtn.classList.add("vditor-menu--disabled");
+ } else if (commandName === "line") {
+ if (blockElement) {
+ const hrHTML = '`;
+ const tableText = range.toString().split("\n");
+ const delimiter = tableText[0].split(",").length > tableText[0].split("\t").length ? "," : "\t";
+
+ tableText.forEach((rows, index) => {
+ if (index === 0) {
+ rows.split(delimiter).forEach((header, subIndex) => {
+ if (subIndex === 0) {
+ tableHTML += ` ";
+ } else {
+ if (index === 1) {
+ tableHTML += "${header} `;
+ } else {
+ tableHTML += `${header} `;
+ }
+ });
+ tableHTML += "";
+ } else {
+ tableHTML += " ";
+ }
+ rows.split(delimiter).forEach((cell) => {
+ tableHTML += ` `;
+ }
+ });
+ tableHTML += "${cell} `;
+ });
+ tableHTML += `