From 78ed35032911b748fced118e75f5ab154d617d40 Mon Sep 17 00:00:00 2001 From: Herbert <30955264+HerbertHe@users.noreply.github.com> Date: Thu, 20 May 2021 18:35:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20vditor=E6=94=AF=E6=8C=81lute=E7=9A=84Re?= =?UTF-8?q?nderJSON=E6=96=B9=E6=B3=95=20&=20=E6=94=AF=E6=8C=81=E5=AE=8C?= =?UTF-8?q?=E5=85=A8=E5=9B=BD=E9=99=85=E5=8C=96=20(#1008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat :sparkles: : 支持lute.RenderJSON()方法为vditor.exportJSON() * feat :sparkles: : 支持自定义国际化i18n * feat :sparkles: : 支持vditor完全国际化 * refactor :recycle: : 优化代码结构, 支持speechRender函数兼容实现 * docs :pencil: : 更新README * fix :bug: : 修复merge问题 * fix :bug: : 修复import路径问题 --- README.md | 156 +++- README_en_US.md | 154 +++- src/index.ts | 208 ++++-- src/ts/export/index.ts | 6 +- src/ts/markdown/codeRender.ts | 8 +- src/ts/markdown/previewRender.ts | 11 +- src/ts/markdown/speechRender.ts | 4 +- src/ts/preview/image.ts | 6 +- src/ts/preview/index.ts | 17 +- src/ts/toolbar/MenuItem.ts | 2 +- src/ts/toolbar/Record.ts | 10 +- src/ts/upload/index.ts | 17 +- src/ts/util/Options.ts | 475 ++++++------ src/ts/util/editorCommonEvent.ts | 2 +- src/ts/util/processCode.ts | 2 +- src/ts/wysiwyg/highlightToolbarWYSIWYG.ts | 832 +++++++++++++++++----- src/ts/wysiwyg/index.ts | 2 +- types/index.d.ts | 100 ++- 18 files changed, 1455 insertions(+), 557 deletions(-) diff --git a/README.md b/README.md index 972459e46..c7a7d3f79 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@


- - - + + +

@@ -200,7 +200,8 @@ Markdown 输出的 HTML 所展现的外观。内置 light,dark,wechat 3 套 | minHeight | 编辑区域最小高度 | - | | width | 编辑器总宽度,支持 % | 'auto' | | placeholder | 输入区域为空时的提示 | '' | -| lang | 多语言:en_US, ja_JP, ko_KR, ru_RU, zh_CN, zh_TW | 'zh_CN' | +| lang | 多语言:en_US, ja_JP, ko_KR, ru_RU, zh_CN | 'zh_CN' | +| i18n | 自定义语言包实现国际化(当 `lang === ""`时, 生效), 类型定义参考[ITips](#options.i18n) | [i18n['zh_CN']](https://github.com/Vanessa219/vditor/blob/master/src/ts/i18n/index.ts#L318) | | input(value: string) | 输入后触发 | - | | focus(value: string) | 聚焦后触发 | - | | blur(value: string) | 失焦后触发 | - | @@ -378,35 +379,35 @@ interface IHintExtend { * 文件上传的数据结构如下。后端返回的数据结构不一致时,可使用 `format` 进行转换。 ```js -// POST data -xhr.send(formData); // formData = FormData.append("file[]", File) -// return data -{ - "msg": "", - "code": 0, - "data": { - "errFiles": ['filename', 'filename2'], - "succMap": { - "filename3": "filepath3", - "filename3": "filepath3" - } - } +// POST data +xhr.send(formData); // formData = FormData.append("file[]", File) +// return data +{ + "msg": "", + "code": 0, + "data": { + "errFiles": ['filename', 'filename2'], + "succMap": { + "filename3": "filepath3", + "filename3": "filepath3" + } + } } ``` * 为了防止站外图片失效, `linkToImgUrl` 可将剪贴板中的站外图片地址传到服务器端进行保存处理,其数据结构如下: ```js -// POST data -xhr.send(JSON.stringify({url: src})); // src 为站外图片地址 -// return data -{ - msg: '', - code: 0, - data : { - originalURL: '', - url: '' - } +// POST data +xhr.send(JSON.stringify({url: src})); // src 为站外图片地址 +// return data +{ + msg: '', + code: 0, + data : { + originalURL: '', + url: '' + } } ``` @@ -482,6 +483,90 @@ if (xhr.status === 200) { | enable | 初始化是否展现大纲 | false | | position | 大纲位置:'left', 'right' | 'left' | +#### options.i18n + +```ts +interface ITips { + alignCenter: string + alignLeft: string + alignRight: string + alternateText: string + bold: string + both: string + check: string + close: string + code: string + "code-theme": string + column: string + comment: string + confirm: string + "content-theme": string + copied: string + copy: string + "delete-column": string + "delete-row": string + devtools: string + down: string + downloadTip: string + edit: string + "edit-mode": string + emoji: string + export: string + fileTypeError: string + footnoteRef: string + fullscreen: string + generate: string + headings: string + help: string + imageURL: string + indent: string + info: string + "inline-code": string + "insert-after": string + "insert-before": string + insertColumnLeft: string + insertColumnRight: string + insertRowAbove: string + insertRowBelow: string + instantRendering: string + italic: string + language: string + line: string + link: string + linkRef: string + list: string + more: string + nameEmpty: string + "ordered-list": string + outdent: string + outline: string + over: string + performanceTip: string + preview: string + quote: string + record: string + "record-tip": string + recording: string + redo: string + remove: string + row: string + spin: string + splitView: string + strike: string + table: string + textIsNotEmpty: string + title: string + tooltipText: string + undo: string + up: string + update: string + upload: string + uploadError: string + uploading: string + wysiwyg: string +} +``` + #### methods | | 说明 | @@ -514,6 +599,7 @@ if (xhr.status === 200) { | hlCommentIds(ids: string[]) | 高亮评论 | | unHlCommentIds(ids: string[]) | 取消评论高亮 | | removeCommentIds(removeIds: string[]) | 删除评论 | +| exportJSON(value: string) | 获取markdown导出为JSON格式的语法树 | #### static methods @@ -524,7 +610,7 @@ Vditor.mermaidRender(document) ``` ```js -import VditorPreview from 'vditor/dist/method.min' +import VditorPreview from 'vditor/dist/method.min' VditorPreview.mermaidRender(document) ``` @@ -536,12 +622,14 @@ markdown: string, // 需要渲染的 markdown 原文 options?: IPreviewOptions { mode: "dark" | "light"; anchor?: number; // 为标题添加锚点 0:不渲染;1:渲染于标题前;2:渲染于标题后,默认 0 - customEmoji?: { [key: string]: string }; // 自定义 emoji,默认为 {} - lang?: (keyof II18nLang); // 语言,默认为 'zh_CN' - emojiPath?: string; // 表情图片路径 - hljs?: IHljs; // 参见 options.preview.hljs + customEmoji?: { [key: string]: string }; // 自定义 emoji,默认为 {} + lang?: (keyof II18nLang); // 语言,默认为 'zh_CN' + i18n?: ITips; // 自定义国际化, 默认为 'i18n['zh_CN']' + emojiPath?: string; // 表情图片路径 + hljs?: IHljs; // 参见 options.preview.hljs speech?: { // 对选中后的内容进行阅读 enable?: boolean, + lang?: string // 语言类型, 默认等于 lang }; math?: IMath; // 数学公式渲染配置 cdn?: string; // 自建 CDN 地址 @@ -571,13 +659,15 @@ options?: IPreviewOptions { | highlightRender(hljsOption?: IHljs, element?: HTMLElement \| Document, cdn = options.cdn) | 为 element 中的代码块进行高亮渲染 | | mediaRender(element: HTMLElement) | 为[特定链接](https://ld246.com/article/1589813914768)分别渲染为视频、音频、嵌入的 iframe | | mathRender(element: HTMLElement, options?: {cdn?: string, math?: IMath}) | 对数学公式进行渲染 | -| speechRender(element: HTMLElement, lang?: (keyof II18nLang)) | 对选中的文字进行阅读 | +| speechRender(element: HTMLElement, speechLang?: string, lang?: (keyof II18n)) | 对选中的文字进行阅读 | | graphvizRender(element: HTMLElement, cdn?: string) | 对 graphviz 进行渲染 | | outlineRender(contentElement: HTMLElement, targetElement: Element) | 对大纲进行渲染 | | lazyLoadImageRender(element: (HTMLElement \| Document) = document) | 对启用懒加载的图片进行渲染 | | setCodeTheme(codeTheme: string, cdn = options.cdn) | 设置代码主题,codeTheme 参见 options.preview.hljs.style | | setContentTheme(contentTheme: string, path: string) | 设置内容主题,contentTheme 参见 options.preview.theme.list | +> Tip: `speechRender(element, speechLang, lang)` 函数当 `!!lang === true`时, `speechLang`的值不生效 + ## 🏗 开发文档 ### 原理相关 diff --git a/README_en_US.md b/README_en_US.md index f6dccc439..80ce6a50b 100644 --- a/README_en_US.md +++ b/README_en_US.md @@ -11,9 +11,9 @@ Easy-to-use Markdown editor, born to adapt to different application scenarios


- - - + + +

@@ -177,7 +177,8 @@ Can be filled with element `id` or element itself` HTMLElement` | minHeight | Editing area minimum height | - | | width | Total editor width, supports % | 'auto' | | placeholder | Tips when the input area is empty | '' | -| lang | i18n: en_US, ja_JP, ko_KR, ru_RU, zh_CN, zh_TW | 'zh_CN' | +| lang | i18n: en_US, ja_JP, ko_KR, ru_RU, zh_CN | 'zh_CN' | +| i18n | custom language package(Effective when `lang === ""`), The type definition [ITips](#options.i18n) | [i18n['zh_CN']](https://github.com/Vanessa219/vditor/blob/master/src/ts/i18n/index.ts#L318) | | input | Trigger after input (value: string) | - | | focus | Trigger after focusing (value: string) | - | | blur | Trigger after out of focus (value: string) | - | @@ -197,7 +198,7 @@ Can be filled with element `id` or element itself` HTMLElement` * Toolbar, you can use name for shorthand: `toolbar: ['emoji', 'br', 'bold', '|', 'line']`. See default [src/ts/util/Options.ts](https://github.com/Vanessa219/vditor/blob/master/src/ts/util/Options.ts) * name can be enumerated as: `emoji` , `headings` , `bold` , `italic` , `strike` , `|` , `line` , `quote` , `list` , `ordered-list` , `check` ,`outdent` ,`indent` , `code` , `inline-code`, `insert-after`, `insert-before`, `code-theme`, `content-theme`, `export`, `undo` , `redo` , `upload` , `link` , `table` , `record` , `edit-mode` , `both` , `preview` , `fullscreen` , `outline` , `devtools` , `info` , `help` , `br` -* When `name` is not in the enumeration, you can add a custom button in the following format: +* When `name` is not in the enumeration, you can add a custom button in the following format: ```js new Vditor('vditor', { @@ -354,35 +355,35 @@ interface IHintExtend { * The data structure of the file upload is as follows. When the data structure returned by the backend is inconsistent, you can use `format` for conversion. ```js -// POST data -xhr.send(formData); // formData = FormData.append("file[]", File) -// return data -{ - "msg": "", - "code": 0, - "data": { - "errFiles": ['filename', 'filename2'], - "succMap": { - "filename3": "filepath3", - "filename3": "filepath3" - } - } +// POST data +xhr.send(formData); // formData = FormData.append("file[]", File) +// return data +{ + "msg": "", + "code": 0, + "data": { + "errFiles": ['filename', 'filename2'], + "succMap": { + "filename3": "filepath3", + "filename3": "filepath3" + } + } } ``` * In order to prevent the off-site pictures from being invalid, `linkToImgUrl` can transfer the off-site picture addresses in the clipboard to the server for saving and processing. The data structure is as follows: ```js -// POST data +// POST data xhr.send(JSON.stringify({url: src})); // src is the address of the image outside the station -// return data -{ - msg: '', - code: 0, - data : { - originalURL: '', - url: '' - } +// return data +{ + msg: '', + code: 0, + data : { + originalURL: '', + url: '' + } } ``` @@ -436,6 +437,90 @@ xhr.send(JSON.stringify({url: src})); // src is the address of the image outside | enable | Initialize whether to show outline | false | | position | Outline location: 'left', 'right' | 'left' | +#### options.i18n + +```ts +interface ITips { + alignCenter: string + alignLeft: string + alignRight: string + alternateText: string + bold: string + both: string + check: string + close: string + code: string + "code-theme": string + column: string + comment: string + confirm: string + "content-theme": string + copied: string + copy: string + "delete-column": string + "delete-row": string + devtools: string + down: string + downloadTip: string + edit: string + "edit-mode": string + emoji: string + export: string + fileTypeError: string + footnoteRef: string + fullscreen: string + generate: string + headings: string + help: string + imageURL: string + indent: string + info: string + "inline-code": string + "insert-after": string + "insert-before": string + insertColumnLeft: string + insertColumnRight: string + insertRowAbove: string + insertRowBelow: string + instantRendering: string + italic: string + language: string + line: string + link: string + linkRef: string + list: string + more: string + nameEmpty: string + "ordered-list": string + outdent: string + outline: string + over: string + performanceTip: string + preview: string + quote: string + record: string + "record-tip": string + recording: string + redo: string + remove: string + row: string + spin: string + splitView: string + strike: string + table: string + textIsNotEmpty: string + title: string + tooltipText: string + undo: string + up: string + update: string + upload: string + uploadError: string + uploading: string + wysiwyg: string +} +``` + #### methods | | Explanation | @@ -468,6 +553,7 @@ xhr.send(JSON.stringify({url: src})); // src is the address of the image outside | hlCommentIds(ids: string[]) | Highlight comment by Ids | | unHlCommentIds(ids: string[]) | Cancel highlight comment by Ids | | removeCommentIds(removeIds: string[]) | Remove comment by Ids | +| exportJSON(value: string) | export markdown as Syntax Tree | #### static methods @@ -478,7 +564,7 @@ Vditor.mermaidRender(document) ``` ```js -import VditorPreview from 'vditor/dist/method.min' +import VditorPreview from 'vditor/dist/method.min' VditorPreview.mermaidRender(document) ``` @@ -491,11 +577,13 @@ options?: IPreviewOptions { mode: "dark" | "light"; anchor?: number; // 0: no render, 1: render left, 2: render right customEmoji?: { [key: string]: string }; // Custom emoji, default is {} - lang?: (keyof II18nLang); // Language, default is 'zh_CN' - emojiPath?: string; // Emoji picture path - hljs?: IHljs; // Refer to options.preview.hljs + lang?: (keyof II18nLang); // Language, default is 'zh_CN' + i18n?: ITips; // custom language package, default is i18n['zh_CN'] + emojiPath?: string; // Emoji picture path + hljs?: IHljs; // Refer to options.preview.hljs speech?: { // Read the selected content enable?: boolean, + lang?: string // speech language, default is 'zh_CN' }; math?: IMath; // Math formula rendering configuration transform?(html: string): string; // Callback method before rendering @@ -524,13 +612,15 @@ options?: IPreviewOptions { | highlightRender(hljsOption?: IHljs, element?: HTMLElement \| Document, cdn = options.cdn) | Highlight the code block in element | | mediaRender(element: HTMLElement) | Rendering as [specific link](https://ld246.com/article/1589813914768) as video, audio, embedded iframe | | mathRender(element: HTMLElement, options?: {cdn?: string, math?: IMath}) | Render math formulas | -| speechRender(element: HTMLElement, lang?: (keyof II18nLang)) | Read the selected text | +| speechRender(element: HTMLElement, speechLang?: string, lang?: (keyof II18n)) | Read the selected text | | graphvizRender(element: HTMLElement, cdn?: string) | Render graphviz | | lazyLoadImageRender(element: (HTMLElement \| Document) = document) | Render lazy load image | | setCodeTheme (codeTheme: string, cdn = options.cdn) | update code theme | | setContentTheme (contentTheme: string, path: string) | update content theme | | mindmapRender (element: (HTMLElement \| Document) = document, cdn = options.cdn, theme = options.theme) | Render mind map | +> Tip: Function `speechRender(element, speechLang, lang)`, When `!!lang === true`, `speechLang` is not effective! + ## 🏗 Developer Guide ### Principle related diff --git a/src/index.ts b/src/index.ts index 0de2ee9fe..97cc9122b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,13 +10,13 @@ import {processAfterRender} from "./ts/ir/process"; import {getHTML} from "./ts/markdown/getHTML"; import {getMarkdown} from "./ts/markdown/getMarkdown"; import {setLute} from "./ts/markdown/setLute"; -import {Outline} from "./ts/outline"; +import {Outline} from "./ts/outline/index"; import {Preview} from "./ts/preview/index"; import {Resize} from "./ts/resize/index"; import {Editor} from "./ts/sv/index"; import {inputEvent} from "./ts/sv/inputEvent"; import {processAfterRender as processSVAfterRender} from "./ts/sv/process"; -import {Tip} from "./ts/tip"; +import {Tip} from "./ts/tip/index"; import {Toolbar} from "./ts/toolbar/index"; import {disableToolbar, hidePanel} from "./ts/toolbar/setToolbar"; import {enableToolbar} from "./ts/toolbar/setToolbar"; @@ -25,20 +25,19 @@ import {setCodeTheme} from "./ts/ui/setCodeTheme"; import {setContentTheme} from "./ts/ui/setContentTheme"; import {setPreviewMode} from "./ts/ui/setPreviewMode"; import {setTheme} from "./ts/ui/setTheme"; -import {Undo} from "./ts/undo"; +import {Undo} from "./ts/undo/index"; import {Upload} from "./ts/upload/index"; import {addScript, addScriptSync} from "./ts/util/addScript"; import {getSelectText} from "./ts/util/getSelectText"; import {Options} from "./ts/util/Options"; import {processCodeRender} from "./ts/util/processCode"; import {getCursorPosition, getEditorRange} from "./ts/util/selection"; -import {WYSIWYG} from "./ts/wysiwyg"; +import {WYSIWYG} from "./ts/wysiwyg/index"; import {afterRenderEvent} from "./ts/wysiwyg/afterRenderEvent"; import {input} from "./ts/wysiwyg/input"; import {renderDomByMd} from "./ts/wysiwyg/renderDomByMd"; class Vditor extends VditorMethod { - public readonly version: string; public vditor: IVditor; @@ -58,7 +57,7 @@ class Vditor extends VditorMethod { }, }; } else if (!options.cache) { - options.cache = {id: `vditor${id}`}; + options.cache = { id: `vditor${id}` }; } else if (!options.cache.id) { options.cache.id = `vditor${id}`; } @@ -68,8 +67,17 @@ class Vditor extends VditorMethod { const getOptions = new Options(options); const mergedOptions = getOptions.merge(); - if (!["en_US", "ja_JP", "ko_KR", "ru_RU", "zh_CN", "zh_TW"].includes(mergedOptions.lang)) { - throw new Error("options.lang error, see https://ld246.com/article/1549638745630#options"); + // 支持自定义国际化 + + if ( + !!mergedOptions.lang && + !["en_US", "ja_JP", "ko_KR", "ru_RU", "zh_CN"].includes( + mergedOptions.lang + ) + ) { + throw new Error( + "options.lang error, see https://ld246.com/article/1549638745630#options" + ); } this.vditor = { @@ -79,7 +87,11 @@ class Vditor extends VditorMethod { lute: undefined, options: mergedOptions, originalInnerHTML: id.innerHTML, - outline: new Outline(i18n[mergedOptions.lang].outline), + outline: new Outline( + !!mergedOptions.lang + ? i18n[mergedOptions.lang].outline + : mergedOptions.i18n.outline + ), tip: new Tip(), }; @@ -101,48 +113,65 @@ class Vditor extends VditorMethod { this.vditor.upload = new Upload(); } - addScript(options._lutePath || `${mergedOptions.cdn}/dist/js/lute/lute.min.js`, "vditorLuteScript") - .then(() => { - this.vditor.lute = setLute({ - autoSpace: this.vditor.options.preview.markdown.autoSpace, - codeBlockPreview: this.vditor.options.preview.markdown.codeBlockPreview, - emojiSite: this.vditor.options.hint.emojiPath, - emojis: this.vditor.options.hint.emoji, - fixTermTypo: this.vditor.options.preview.markdown.fixTermTypo, - footnotes: this.vditor.options.preview.markdown.footnotes, - headingAnchor: false, - inlineMathDigit: this.vditor.options.preview.math.inlineDigit, - linkBase: this.vditor.options.preview.markdown.linkBase, - linkPrefix: this.vditor.options.preview.markdown.linkPrefix, - listStyle: this.vditor.options.preview.markdown.listStyle, - mark: this.vditor.options.preview.markdown.mark, - mathBlockPreview: this.vditor.options.preview.markdown.mathBlockPreview, - paragraphBeginningSpace: this.vditor.options.preview.markdown.paragraphBeginningSpace, - sanitize: this.vditor.options.preview.markdown.sanitize, - toc: this.vditor.options.preview.markdown.toc, - }); + addScript( + options._lutePath || + `${mergedOptions.cdn}/dist/js/lute/lute.min.js`, + "vditorLuteScript" + ).then(() => { + this.vditor.lute = setLute({ + autoSpace: this.vditor.options.preview.markdown.autoSpace, + codeBlockPreview: this.vditor.options.preview.markdown + .codeBlockPreview, + emojiSite: this.vditor.options.hint.emojiPath, + emojis: this.vditor.options.hint.emoji, + fixTermTypo: this.vditor.options.preview.markdown.fixTermTypo, + footnotes: this.vditor.options.preview.markdown.footnotes, + headingAnchor: false, + inlineMathDigit: this.vditor.options.preview.math.inlineDigit, + linkBase: this.vditor.options.preview.markdown.linkBase, + linkPrefix: this.vditor.options.preview.markdown.linkPrefix, + listStyle: this.vditor.options.preview.markdown.listStyle, + mark: this.vditor.options.preview.markdown.mark, + mathBlockPreview: this.vditor.options.preview.markdown + .mathBlockPreview, + paragraphBeginningSpace: this.vditor.options.preview.markdown + .paragraphBeginningSpace, + sanitize: this.vditor.options.preview.markdown.sanitize, + toc: this.vditor.options.preview.markdown.toc, + }); - this.vditor.preview = new Preview(this.vditor); + this.vditor.preview = new Preview(this.vditor); - initUI(this.vditor); + initUI(this.vditor); - if (mergedOptions.after) { - mergedOptions.after(); - } - if (mergedOptions.icon) { - // 防止初始化 2 个编辑器时加载 2 次 - addScriptSync(`${mergedOptions.cdn}/dist/js/icons/${mergedOptions.icon}.js`, "vditorIconScript"); - } - }); + if (mergedOptions.after) { + mergedOptions.after(); + } + if (mergedOptions.icon) { + // 防止初始化 2 个编辑器时加载 2 次 + addScriptSync( + `${mergedOptions.cdn}/dist/js/icons/${mergedOptions.icon}.js`, + "vditorIconScript" + ); + } + }); } /** 设置主题 */ - public setTheme(theme: "dark" | "classic", contentTheme?: string, codeTheme?: string, contentThemePath?: string) { + public setTheme( + theme: "dark" | "classic", + contentTheme?: string, + codeTheme?: string, + contentThemePath?: string + ) { this.vditor.options.theme = theme; setTheme(this.vditor); if (contentTheme) { this.vditor.options.preview.theme.current = contentTheme; - setContentTheme(contentTheme, contentThemePath || this.vditor.options.preview.theme.path); + setContentTheme( + contentTheme, + contentThemePath || this.vditor.options.preview.theme.path + ); } if (codeTheme) { this.vditor.options.preview.hljs.style = codeTheme; @@ -185,17 +214,37 @@ class Vditor extends VditorMethod { /** 禁用编辑器 */ public disabled() { hidePanel(this.vditor, ["subToolbar", "hint", "popover"]); - disableToolbar(this.vditor.toolbar.elements, Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", - "edit-mode"])); - this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "false"); + disableToolbar( + this.vditor.toolbar.elements, + Constants.EDIT_TOOLBARS.concat([ + "undo", + "redo", + "fullscreen", + "edit-mode", + ]) + ); + this.vditor[this.vditor.currentMode].element.setAttribute( + "contenteditable", + "false" + ); } /** 解除编辑器禁用 */ public enable() { - enableToolbar(this.vditor.toolbar.elements, Constants.EDIT_TOOLBARS.concat(["undo", "redo", "fullscreen", - "edit-mode"])); + enableToolbar( + this.vditor.toolbar.elements, + Constants.EDIT_TOOLBARS.concat([ + "undo", + "redo", + "fullscreen", + "edit-mode", + ]) + ); this.vditor.undo.resetIcon(this.vditor); - this.vditor[this.vditor.currentMode].element.setAttribute("contenteditable", "true"); + this.vditor[this.vditor.currentMode].element.setAttribute( + "contenteditable", + "true" + ); } /** 返回选中的字符串 */ @@ -237,8 +286,9 @@ class Vditor extends VditorMethod { /** 启用缓存 */ public enableCache() { if (!this.vditor.options.cache.id) { - throw new Error("need options.cache.id, see https://ld246.com/article/1549638745630#options"); - return; + throw new Error( + "need options.cache.id, see https://ld246.com/article/1549638745630#options" + ); } this.vditor.options.cache.enable = true; } @@ -248,6 +298,11 @@ class Vditor extends VditorMethod { return this.vditor.lute.HTML2Md(value); } + /** markdown转JSON输出 */ + public exportJSON(value: string) { + return this.vditor.lute.RenderJSON(value); + } + /** 获取 HTML */ public getHTML() { return getHTML(this.vditor); @@ -304,7 +359,9 @@ class Vditor extends VditorMethod { /** 设置编辑器内容 */ public setValue(markdown: string, clearStack = false) { if (this.vditor.currentMode === "sv") { - this.vditor.sv.element.innerHTML = this.vditor.lute.SpinVditorSVDOM(markdown); + this.vditor.sv.element.innerHTML = this.vditor.lute.SpinVditorSVDOM( + markdown + ); processSVAfterRender(this.vditor, { enableAddUndoStack: true, enableHint: false, @@ -317,9 +374,12 @@ class Vditor extends VditorMethod { enableInput: false, }); } else { - this.vditor.ir.element.innerHTML = this.vditor.lute.Md2VditorIRDOM(markdown); - this.vditor.ir.element.querySelectorAll(".vditor-ir__preview[data-render='2']").forEach( - (item: HTMLElement) => { + this.vditor.ir.element.innerHTML = this.vditor.lute.Md2VditorIRDOM( + markdown + ); + this.vditor.ir.element + .querySelectorAll(".vditor-ir__preview[data-render='2']") + .forEach((item: HTMLElement) => { processCodeRender(item, this.vditor); }); processAfterRender(this.vditor, { @@ -379,13 +439,17 @@ class Vditor extends VditorMethod { } }); }; - this.vditor.wysiwyg.element.querySelectorAll(".vditor-comment").forEach((item) => { - hlItem(item); - }); - if (this.vditor.preview.element.style.display !== "none") { - this.vditor.preview.element.querySelectorAll(".vditor-comment").forEach((item) => { + this.vditor.wysiwyg.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { hlItem(item); }); + if (this.vditor.preview.element.style.display !== "none") { + this.vditor.preview.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { + hlItem(item); + }); } } @@ -401,13 +465,17 @@ class Vditor extends VditorMethod { } }); }; - this.vditor.wysiwyg.element.querySelectorAll(".vditor-comment").forEach((item) => { - unHlItem(item); - }); - if (this.vditor.preview.element.style.display !== "none") { - this.vditor.preview.element.querySelectorAll(".vditor-comment").forEach((item) => { + this.vditor.wysiwyg.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { unHlItem(item); }); + if (this.vditor.preview.element.style.display !== "none") { + this.vditor.preview.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { + unHlItem(item); + }); } } @@ -433,13 +501,17 @@ class Vditor extends VditorMethod { } }; removeIds.forEach((removeId) => { - this.vditor.wysiwyg.element.querySelectorAll(".vditor-comment").forEach((item) => { - removeItem(item, removeId); - }); - if (this.vditor.preview.element.style.display !== "none") { - this.vditor.preview.element.querySelectorAll(".vditor-comment").forEach((item) => { + this.vditor.wysiwyg.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { removeItem(item, removeId); }); + if (this.vditor.preview.element.style.display !== "none") { + this.vditor.preview.element + .querySelectorAll(".vditor-comment") + .forEach((item) => { + removeItem(item, removeId); + }); } }); afterRenderEvent(this.vditor, { diff --git a/src/ts/export/index.ts b/src/ts/export/index.ts index d255b598c..187f9d77e 100644 --- a/src/ts/export/index.ts +++ b/src/ts/export/index.ts @@ -13,7 +13,7 @@ export const download = (vditor: IVditor, content: string, filename: string) => aElement.click(); aElement.remove(); } else { - vditor.tip.show(i18n[vditor.options.lang].downloadTip, 0); + vditor.tip.show(!!vditor.options.lang ? i18n[vditor.options.lang].downloadTip : vditor.options.i18n.downloadTip, 0); } }; @@ -23,7 +23,7 @@ export const exportMarkdown = (vditor: IVditor) => { }; export const exportPDF = (vditor: IVditor) => { - vditor.tip.show(i18n[vditor.options.lang].generate, 3800); + vditor.tip.show(!!vditor.options.lang ? i18n[vditor.options.lang].generate : vditor.options.i18n.generate, 3800); const iframe = document.querySelector("iframe"); iframe.contentDocument.open(); iframe.contentDocument.write(` @@ -61,7 +61,7 @@ export const exportHTML = (vditor: IVditor) => {