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) => {