vditor 3.8.7 → 3.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vditor v3.8.7 - A markdown editor written in TypeScript.
2
+ * Vditor v3.8.8 - A markdown editor written in TypeScript.
3
3
  *
4
4
  * MIT License
5
5
  *
@@ -45,7 +45,7 @@ return /******/ (() => { // webpackBootstrap
45
45
  /* harmony export */ "g": () => (/* binding */ Constants)
46
46
  /* harmony export */ });
47
47
  /* unused harmony export VDITOR_VERSION */
48
- var _VDITOR_VERSION = (/* unused pure expression or super */ null && ("3.8.7"));
48
+ var _VDITOR_VERSION = (/* unused pure expression or super */ null && ("3.8.8"));
49
49
 
50
50
  var Constants = /** @class */ (function () {
51
51
  function Constants() {
@@ -71,7 +71,7 @@ var Constants = /** @class */ (function () {
71
71
  "swift", "ini", "typescript", "vbnet", "yaml",
72
72
  "ada", "clojure", "dart", "erb", "fortran", "gradle", "haskell", "julia", "julia-repl", "lisp", "matlab",
73
73
  "pgsql", "powershell", "sql_more", "stata", "cmake", "mathematica"];
74
- Constants.CDN = "https://cdn.jsdelivr.net/npm/vditor@" + "3.8.7";
74
+ Constants.CDN = "https://cdn.jsdelivr.net/npm/vditor@" + "3.8.8";
75
75
  Constants.MARKDOWN_OPTIONS = {
76
76
  autoSpace: false,
77
77
  codeBlockPreview: true,
@@ -2194,7 +2194,7 @@ var md2html = function (mdText, options) {
2194
2194
  });
2195
2195
  };
2196
2196
  var previewRender = function (previewElement, markdown, options) { return __awaiter(void 0, void 0, void 0, function () {
2197
- var mergedOptions, html;
2197
+ var mergedOptions, html, i18nScriptPrefix, i18nScriptID_1;
2198
2198
  return __generator(this, function (_a) {
2199
2199
  switch (_a.label) {
2200
2200
  case 0:
@@ -2212,7 +2212,14 @@ var previewRender = function (previewElement, markdown, options) { return __awai
2212
2212
  throw new Error("options.lang error, see https://ld246.com/article/1549638745630#options");
2213
2213
  }
2214
2214
  else {
2215
- (0,addScript/* addScriptSync */.J)(mergedOptions.cdn + "/dist/js/i18n/" + mergedOptions.lang + ".js", "vditorI18nScript");
2215
+ i18nScriptPrefix = "vditorI18nScript";
2216
+ i18nScriptID_1 = i18nScriptPrefix + mergedOptions.lang;
2217
+ document.querySelectorAll("head script[id^=\"" + i18nScriptPrefix + "\"]").forEach(function (el) {
2218
+ if (el.id !== i18nScriptID_1) {
2219
+ document.head.removeChild(el);
2220
+ }
2221
+ });
2222
+ (0,addScript/* addScript */.G)(mergedOptions.cdn + "/dist/js/i18n/" + mergedOptions.lang + ".js", i18nScriptID_1);
2216
2223
  }
2217
2224
  }
2218
2225
  else {
@@ -7,3 +7,4 @@ declare global {
7
7
  export declare const initUI: (vditor: IVditor) => void;
8
8
  export declare const setPadding: (vditor: IVditor) => void;
9
9
  export declare const setTypewriterPosition: (vditor: IVditor) => void;
10
+ export declare function UIUnbindListener(): void;
@@ -9,11 +9,13 @@ declare class WYSIWYG {
9
9
  preventInput: boolean;
10
10
  composingLock: boolean;
11
11
  commentIds: string[];
12
+ private scrollListener;
12
13
  constructor(vditor: IVditor);
13
14
  getComments(vditor: IVditor, getData?: boolean): ICommentsData[];
14
15
  triggerRemoveComment(vditor: IVditor): void;
15
16
  showComment(): void;
16
17
  hideComment(): void;
18
+ unbindListener(): void;
17
19
  private copy;
18
20
  private bindEvent;
19
21
  }
@@ -372,7 +372,7 @@ interface IUpload {
372
372
  validate?(files: File[]): string | boolean;
373
373
 
374
374
  /** 自定义上传,当发生错误时返回错误信息 */
375
- handler?(files: File[]): string | null;
375
+ handler?(files: File[]): string | null | Promise<string> | Promise<null>;
376
376
 
377
377
  /** 对服务端返回的数据进行转换,以满足内置的数据结构 */
378
378
  format?(files: File[], responseText: string): string;
@@ -381,7 +381,7 @@ interface IUpload {
381
381
  linkToImgFormat?(responseText: string): string;
382
382
 
383
383
  /** 将上传的文件处理后再返回 */
384
- file?(files: File[]): File[];
384
+ file?(files: File[]): File[] | Promise<File[]>;
385
385
 
386
386
  /** 图片地址上传后的回调 */
387
387
  linkToImgCallback?(responseText: string): void;
@@ -537,7 +537,7 @@ interface IHintData {
537
537
  interface IHintExtend {
538
538
  key: string;
539
539
 
540
- hint?(value: string): IHintData[];
540
+ hint?(value: string): IHintData[] | Promise<IHintData[]>;
541
541
  }
542
542
 
543
543
  /** @link https://ld246.com/article/1549638745630#options-hint */
@@ -747,6 +747,7 @@ interface IVditor {
747
747
  triggerRemoveComment(vditor: IVditor): void,
748
748
  showComment(): void,
749
749
  hideComment(): void,
750
+ unbindListener(): void,
750
751
  };
751
752
  ir?: {
752
753
  range: Range,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vditor",
3
- "version": "3.8.7",
3
+ "version": "3.8.8",
4
4
  "description": "♏ 易于使用的 Markdown 编辑器,为适配不同的应用场景而生",
5
5
  "author": "Vanessa <v@b3log.org> (http://vanessa.b3log.org)",
6
6
  "homepage": "https://b3log.org/vditor",
package/src/index.ts CHANGED
@@ -19,7 +19,7 @@ import {Tip} from "./ts/tip/index";
19
19
  import {Toolbar} from "./ts/toolbar/index";
20
20
  import {disableToolbar, hidePanel} from "./ts/toolbar/setToolbar";
21
21
  import {enableToolbar} from "./ts/toolbar/setToolbar";
22
- import {initUI} from "./ts/ui/initUI";
22
+ import {initUI, UIUnbindListener} from "./ts/ui/initUI";
23
23
  import {setCodeTheme} from "./ts/ui/setCodeTheme";
24
24
  import {setContentTheme} from "./ts/ui/setContentTheme";
25
25
  import {setPreviewMode} from "./ts/ui/setPreviewMode";
@@ -73,7 +73,14 @@ class Vditor extends VditorMethod {
73
73
  "options.lang error, see https://ld246.com/article/1549638745630#options",
74
74
  );
75
75
  } else {
76
- addScript(`${mergedOptions.cdn}/dist/js/i18n/${mergedOptions.lang}.js`, "vditorI18nScript").then(() => {
76
+ const i18nScriptPrefix = "vditorI18nScript";
77
+ const i18nScriptID = i18nScriptPrefix + mergedOptions.lang;
78
+ document.querySelectorAll(`head script[id^="${i18nScriptPrefix}"]`).forEach((el) => {
79
+ if (el.id !== i18nScriptID) {
80
+ document.head.removeChild(el);
81
+ }
82
+ });
83
+ addScript(`${mergedOptions.cdn}/dist/js/i18n/${mergedOptions.lang}.js`, i18nScriptID).then(() => {
77
84
  this.init(id as HTMLElement, mergedOptions);
78
85
  });
79
86
  }
@@ -322,6 +329,9 @@ class Vditor extends VditorMethod {
322
329
  this.vditor.element.removeAttribute("style");
323
330
  document.getElementById("vditorIconScript").remove();
324
331
  this.clearCache();
332
+
333
+ UIUnbindListener();
334
+ this.vditor.wysiwyg.unbindListener();
325
335
  }
326
336
 
327
337
  /** 获取评论 ID */
package/src/ts/.DS_Store CHANGED
Binary file
@@ -59,8 +59,8 @@ export class Hint {
59
59
  vditor.options.hint.extend.forEach((item) => {
60
60
  if (item.key === this.splitChar) {
61
61
  clearTimeout(this.timeId);
62
- this.timeId = window.setTimeout(() => {
63
- this.genHTML(item.hint(key), key, vditor);
62
+ this.timeId = window.setTimeout(async () => {
63
+ this.genHTML(await item.hint(key), key, vditor);
64
64
  }, vditor.options.hint.delay);
65
65
  }
66
66
  });
@@ -148,15 +148,24 @@ ${i === 0 ? "class='vditor-hint--current'" : ""}> ${html}</button>`;
148
148
  return;
149
149
  }
150
150
  }
151
- if (vditor.currentMode === "wysiwyg" && range.startContainer.nodeType !== 3 &&
152
- (range.startContainer as HTMLElement).firstElementChild.classList.contains("vditor-input")) {
153
- const inputElement = (range.startContainer as HTMLElement).firstElementChild as HTMLInputElement;
154
- inputElement.value = value.trimRight();
155
- range.selectNodeContents(inputElement);
156
- range.collapse(false);
157
- inputElement.dispatchEvent(new CustomEvent("input"));
158
- this.recentLanguage = value.trimRight();
159
- return;
151
+ if (vditor.currentMode === "wysiwyg" && range.startContainer.nodeType !== 3 ) {
152
+ const startContainer = range.startContainer as HTMLElement;
153
+ let inputElement: HTMLInputElement;
154
+ if (startContainer.classList.contains("vditor-input")) {
155
+ inputElement = startContainer as HTMLInputElement;
156
+ } else {
157
+ inputElement = startContainer.firstElementChild as HTMLInputElement;
158
+ }
159
+ if (inputElement && inputElement.classList.contains("vditor-input")) {
160
+ inputElement.value = value.trimRight();
161
+ range.selectNodeContents(inputElement);
162
+ range.collapse(false);
163
+ // {detail: 1}用于标识这个自定义事件是在编程语言选择后触发的
164
+ // 用于在鼠标选择语言后,自动聚焦到代码输入框
165
+ inputElement.dispatchEvent(new CustomEvent("input", {detail: 1}));
166
+ this.recentLanguage = value.trimRight();
167
+ return;
168
+ }
160
169
  }
161
170
 
162
171
  range.setStart(range.startContainer, this.lastIndex);
@@ -90,7 +90,14 @@ export const previewRender = async (previewElement: HTMLDivElement, markdown: st
90
90
  "options.lang error, see https://ld246.com/article/1549638745630#options",
91
91
  );
92
92
  } else {
93
- addScriptSync(`${mergedOptions.cdn}/dist/js/i18n/${mergedOptions.lang}.js`, "vditorI18nScript");
93
+ const i18nScriptPrefix = "vditorI18nScript";
94
+ const i18nScriptID = i18nScriptPrefix + mergedOptions.lang;
95
+ document.querySelectorAll(`head script[id^="${i18nScriptPrefix}"]`).forEach((el) => {
96
+ if (el.id !== i18nScriptID) {
97
+ document.head.removeChild(el);
98
+ }
99
+ });
100
+ addScript(`${mergedOptions.cdn}/dist/js/i18n/${mergedOptions.lang}.js`, i18nScriptID);
94
101
  }
95
102
  } else {
96
103
  window.VditorI18n = mergedOptions.i18n;
@@ -23,7 +23,6 @@ export class Preview {
23
23
  constructor(vditor: IVditor) {
24
24
  this.element = document.createElement("div");
25
25
  this.element.className = `vditor-preview`;
26
-
27
26
  const previewElement = document.createElement("div");
28
27
  previewElement.className = "vditor-reset";
29
28
  if (vditor.options.classes.preview) {
@@ -16,7 +16,7 @@ export class Info extends MenuItem {
16
16
  <div>&nbsp;&nbsp;</div>
17
17
  <div style="flex: 1;min-width: 250px">
18
18
  Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。
19
- 它使用 TypeScript 实现,支持原生 JavaScript、Vue、React、Angular,提供<a target="_blank" href="https://b3log.org/siyuan">桌面版</a>。
19
+ 它使用 TypeScript 实现,支持原生 JavaScript 以及 Vue、React、Angular 和 Svelte 等框架。
20
20
  </div>
21
21
  </div>
22
22
  <div style="display: flex;flex-wrap: wrap;">
@@ -1,5 +1,6 @@
1
1
  import {Constants} from "../constants";
2
2
  import {setEditMode} from "../toolbar/EditMode";
3
+ import {hidePanel} from "../toolbar/setToolbar";
3
4
  import {accessLocalStorage} from "../util/compatibility";
4
5
  import {setContentTheme} from "./setContentTheme";
5
6
  import {setTheme} from "./setTheme";
@@ -67,13 +68,17 @@ export const initUI = (vditor: IVditor) => {
67
68
 
68
69
  vditor.element.appendChild(contentElement);
69
70
 
71
+ contentElement.addEventListener("click", () => {
72
+ hidePanel(vditor, ["subToolbar"]);
73
+ });
74
+
70
75
  if (vditor.toolbar.elements.export) {
71
76
  // for export pdf
72
77
  vditor.element.insertAdjacentHTML("beforeend",
73
78
  '<iframe style="width: 100%;height: 0;border: 0"></iframe>');
74
79
  }
75
80
 
76
- setEditMode(vditor, vditor.options.mode, afterRender(vditor, contentElement));
81
+ setEditMode(vditor, vditor.options.mode, afterRender(vditor));
77
82
 
78
83
  document.execCommand("DefaultParagraphSeparator", false, "p");
79
84
 
@@ -114,10 +119,13 @@ export const setPadding = (vditor: IVditor) => {
114
119
  vditor.ir.element.style.padding = `10px ${Math.max(minPadding, padding)}px`;
115
120
  }
116
121
 
117
- if (vditor.preview.element.style.display !== "block" || vditor.currentMode === "sv") {
122
+ if (vditor.preview.element.style.display !== "block") {
118
123
  vditor.toolbar.element.style.paddingLeft = Math.max(5,
119
124
  parseInt(vditor[vditor.currentMode].element.style.paddingLeft || "0", 10) +
120
125
  (vditor.options.outline.position === "left" ? vditor.outline.element.offsetWidth : 0)) + "px";
126
+ } else {
127
+ vditor.toolbar.element.style.paddingLeft = (5 +
128
+ (vditor.options.outline.position === "left" ? vditor.outline.element.offsetWidth : 0)) + "px";
121
129
  }
122
130
  };
123
131
 
@@ -141,10 +149,16 @@ export const setTypewriterPosition = (vditor: IVditor) => {
141
149
  ((height - vditor.toolbar.element.offsetHeight) / 2) + "px");
142
150
  };
143
151
 
144
- const afterRender = (vditor: IVditor, contentElement: HTMLElement) => {
145
- setTypewriterPosition(vditor);
152
+ let resizeCb: () => void;
153
+
154
+ export function UIUnbindListener() {
155
+ window.removeEventListener("resize", resizeCb);
156
+ }
146
157
 
147
- window.addEventListener("resize", () => {
158
+ const afterRender = (vditor: IVditor) => {
159
+ setTypewriterPosition(vditor);
160
+ UIUnbindListener();
161
+ window.addEventListener("resize", resizeCb = () => {
148
162
  setPadding(vditor);
149
163
  setTypewriterPosition(vditor);
150
164
  });
@@ -154,7 +154,7 @@ const uploadFiles =
154
154
  }
155
155
 
156
156
  if (vditor.options.upload.handler) {
157
- const isValidate = vditor.options.upload.handler(fileList);
157
+ const isValidate = await vditor.options.upload.handler(fileList);
158
158
  if (typeof isValidate === "string") {
159
159
  vditor.tip.show(isValidate);
160
160
  return;
@@ -22,7 +22,7 @@ export const focusEvent = (vditor: IVditor, editorElement: HTMLElement) => {
22
22
  if (vditor.options.focus) {
23
23
  vditor.options.focus(getMarkdown(vditor));
24
24
  }
25
- hidePanel(vditor, ["subToolbar"]);
25
+ hidePanel(vditor, ["subToolbar", "hint"]);
26
26
  });
27
27
  };
28
28
 
@@ -62,7 +62,7 @@ export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
62
62
  if (event.dataTransfer.getData(Constants.DROP_EDITOR)) {
63
63
  // 编辑器内选中文字拖拽
64
64
  execAfterRender(vditor);
65
- } else if (event.dataTransfer.types[0] === "Files" || event.dataTransfer.types.includes("text/html")) {
65
+ } else if (event.dataTransfer.types.includes("Files") || event.dataTransfer.types.includes("text/html")) {
66
66
  // 外部文件拖入编辑器中或者编辑器内选中文字拖拽
67
67
  paste(vditor, event, {
68
68
  pasteCode: (code: string) => {
@@ -977,7 +977,7 @@ export const fixCodeBlock = (vditor: IVditor, event: KeyboardEvent, codeRenderEl
977
977
  if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
978
978
  const codePosition = getSelectPosition(codeRenderElement, vditor[vditor.currentMode].element, range);
979
979
  if ((codePosition.start === 0 ||
980
- (codePosition.start === 1 && codeRenderElement.innerText === "\n")) // 空代码块,光标在 \n 后
980
+ (codePosition.start === 1 && codeRenderElement.innerText === "\n")) // 空代码块,光标在 \n 后
981
981
  && range.toString() === "") {
982
982
  codeRenderElement.parentElement.outerHTML =
983
983
  `<p data-block="0"><wbr>${codeRenderElement.firstElementChild.innerHTML}</p>`;
@@ -1093,7 +1093,7 @@ export const fixTask = (vditor: IVditor, range: Range, event: KeyboardEvent) =>
1093
1093
  if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey && range.toString() === ""
1094
1094
  && range.startOffset === 1
1095
1095
  && ((startContainer.nodeType === 3 && startContainer.previousSibling &&
1096
- (startContainer.previousSibling as HTMLElement).tagName === "INPUT")
1096
+ (startContainer.previousSibling as HTMLElement).tagName === "INPUT")
1097
1097
  || startContainer.nodeType !== 3)) {
1098
1098
  const previousElement = taskItemElement.previousElementSibling;
1099
1099
  taskItemElement.querySelector("input").remove();
@@ -1264,7 +1264,7 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1264
1264
  } else {
1265
1265
  textHTML = event.dataTransfer.getData("text/html");
1266
1266
  textPlain = event.dataTransfer.getData("text/plain");
1267
- if (event.dataTransfer.types[0] === "Files") {
1267
+ if (event.dataTransfer.types.includes("Files")) {
1268
1268
  files = event.dataTransfer.items;
1269
1269
  }
1270
1270
  }
@@ -1410,7 +1410,7 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1410
1410
  processPaste(vditor, vditor.lute.HTML2Md(tempElement.innerHTML).trimRight());
1411
1411
  }
1412
1412
  vditor.outline.render(vditor);
1413
- } else if (files.length > 0 && vditor.options.upload.url) {
1413
+ } else if (files.length > 0 && (vditor.options.upload.url || vditor.options.upload.handler)) {
1414
1414
  await uploadFiles(vditor, files);
1415
1415
  } else if (textPlain.trim() !== "" && files.length === 0) {
1416
1416
  if (vditor.currentMode === "ir") {
@@ -618,7 +618,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
618
618
  codeElement.className.indexOf("language-") > -1
619
619
  ? codeElement.className.split("-")[1].split(" ")[0]
620
620
  : "";
621
- language.oninput = () => {
621
+ language.oninput = (e: InputEvent) => {
622
622
  if (language.value.trim() !== "") {
623
623
  codeElement.className = `language-${language.value}`;
624
624
  } else {
@@ -631,6 +631,13 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
631
631
  processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
632
632
  }
633
633
  afterRenderEvent(vditor);
634
+ // 当鼠标点选语言时,触发自定义input事件
635
+ if (e.detail === 1) {
636
+ // 选择语言后,输入焦点切换到代码输入框
637
+ range.setStart(codeElement.firstChild, 0);
638
+ range.collapse(true);
639
+ setSelectionFocus(range);
640
+ }
634
641
  };
635
642
  language.onkeydown = (event: KeyboardEvent) => {
636
643
  if (event.isComposing) {
@@ -647,17 +654,18 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
647
654
  event.preventDefault();
648
655
  return;
649
656
  }
657
+ vditor.hint.select(event, vditor);
650
658
  if (
651
659
  !isCtrl(event) &&
652
660
  !event.shiftKey &&
653
- event.altKey &&
654
661
  event.key === "Enter"
655
662
  ) {
656
663
  range.setStart(codeElement.firstChild, 0);
657
664
  range.collapse(true);
658
665
  setSelectionFocus(range);
666
+ event.preventDefault();
667
+ event.stopPropagation();
659
668
  }
660
- vditor.hint.select(event, vditor);
661
669
  };
662
670
  language.onkeyup = (event: KeyboardEvent) => {
663
671
  if (
@@ -39,6 +39,7 @@ class WYSIWYG {
39
39
  public preventInput: boolean;
40
40
  public composingLock = false;
41
41
  public commentIds: string[] = [];
42
+ private scrollListener: () => void;
42
43
 
43
44
  constructor(vditor: IVditor) {
44
45
  const divElement = document.createElement("div");
@@ -195,6 +196,10 @@ class WYSIWYG {
195
196
  this.selectPopover.setAttribute("style", "display:none");
196
197
  }
197
198
 
199
+ public unbindListener() {
200
+ window.removeEventListener("scroll", this.scrollListener);
201
+ }
202
+
198
203
  private copy(event: ClipboardEvent, vditor: IVditor) {
199
204
  const range = getSelection().getRangeAt(0);
200
205
  if (range.toString() === "") {
@@ -238,7 +243,8 @@ class WYSIWYG {
238
243
  }
239
244
 
240
245
  private bindEvent(vditor: IVditor) {
241
- window.addEventListener("scroll", () => {
246
+ this.unbindListener();
247
+ window.addEventListener("scroll", this.scrollListener = () => {
242
248
  hidePanel(vditor, ["hint"]);
243
249
  if (this.popover.style.display !== "block" || this.selectPopover.style.display !== "block") {
244
250
  return;
@@ -331,7 +337,7 @@ class WYSIWYG {
331
337
  this.preventInput = false;
332
338
  return;
333
339
  }
334
- if (this.composingLock || event.data === "‘" || event.data === "“" || event.data === "《") {
340
+ if (this.composingLock || event.data === "‘" || event.data === "“" || event.data === "《") {
335
341
  return;
336
342
  }
337
343
  const range = getSelection().getRangeAt(0);