vditor 3.11.1 → 3.11.3
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.
- package/README.md +13 -2
- package/dist/css/content-theme/ant-design.css +10 -0
- package/dist/css/content-theme/dark.css +10 -0
- package/dist/css/content-theme/light.css +13 -0
- package/dist/css/content-theme/wechat.css +10 -0
- package/dist/index.css +133 -4
- package/dist/index.js +715 -57
- package/dist/index.min.js +1 -1
- package/dist/js/i18n/es_ES.js +86 -0
- package/dist/js/i18n/ja_JP.js +2 -2
- package/dist/js/i18n/vi_VN.js +86 -0
- package/dist/js/i18n/zh_TW.js +8 -8
- package/dist/js/icons/ant.js +1 -1
- package/dist/js/icons/material.js +1 -1
- package/dist/js/lute/lute.min.js +87 -71
- package/dist/js/mermaid/mermaid.min.js +2027 -1047
- package/dist/js/wavedrom/LICENSE.WaveDrom +21 -0
- package/dist/js/wavedrom/wavedrom.min.js +9 -0
- package/dist/method.d.ts +2 -0
- package/dist/method.js +319 -8
- package/dist/method.min.js +1 -1
- package/dist/ts/constants.d.ts +4 -0
- package/dist/ts/markdown/adapterRender.d.ts +4 -0
- package/dist/ts/markdown/imageCaptionRender.d.ts +2 -0
- package/dist/ts/markdown/wavedromRender.d.ts +1 -0
- package/dist/ts/util/fixBrowserBehavior.d.ts +1 -0
- package/dist/types/index.d.ts +47 -3
- package/package.json +7 -4
- package/src/assets/less/_reset.less +103 -2
- package/src/assets/less/index.less +20 -0
- package/src/index.ts +4 -1
- package/src/method.ts +3 -0
- package/src/ts/constants.ts +5 -1
- package/src/ts/export/index.ts +2 -0
- package/src/ts/ir/index.ts +26 -0
- package/src/ts/ir/process.ts +24 -1
- package/src/ts/markdown/adapterRender.ts +4 -0
- package/src/ts/markdown/getHTML.ts +6 -3
- package/src/ts/markdown/imageCaptionRender.ts +175 -0
- package/src/ts/markdown/mermaidRender.ts +1 -1
- package/src/ts/markdown/previewRender.ts +8 -2
- package/src/ts/markdown/setLute.ts +3 -0
- package/src/ts/markdown/wavedromRender.ts +54 -0
- package/src/ts/preview/index.ts +5 -0
- package/src/ts/util/Options.ts +1 -1
- package/src/ts/util/fixBrowserBehavior.ts +165 -21
- package/src/ts/util/processCode.ts +3 -0
- package/src/ts/util/selection.ts +7 -2
- package/src/ts/wysiwyg/afterRenderEvent.ts +2 -0
- package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +23 -4
- package/src/ts/wysiwyg/input.ts +5 -0
- package/src/ts/wysiwyg/setHeading.ts +110 -6
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {Constants} from "../constants";
|
|
2
|
+
import {addScript} from "../util/addScript";
|
|
3
|
+
import {looseJsonParse} from "../util/function";
|
|
4
|
+
import {wavedromRenderAdapter} from "./adapterRender";
|
|
5
|
+
|
|
6
|
+
declare const wavedrom: {
|
|
7
|
+
renderWaveElement(index: number, source: object, element: HTMLElement, skin: object, notFirstSignal: boolean): void;
|
|
8
|
+
waveSkin: object;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let wavedromIndex = 0;
|
|
12
|
+
|
|
13
|
+
export const wavedromRender = (element: (HTMLElement | Document) = document, cdn = Constants.CDN) => {
|
|
14
|
+
const wavedromElements = wavedromRenderAdapter.getElements(element);
|
|
15
|
+
if (wavedromElements.length === 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
addScript(`${cdn}/dist/js/wavedrom/wavedrom.min.js?v=3.6.2`, "vditorWavedromScript").then(() => {
|
|
20
|
+
wavedromElements.forEach(async (item: HTMLElement) => {
|
|
21
|
+
if (item.parentElement.classList.contains("vditor-wysiwyg__pre") ||
|
|
22
|
+
item.parentElement.classList.contains("vditor-ir__marker--pre")) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const code = wavedromRenderAdapter.getCode(item);
|
|
27
|
+
if (item.getAttribute("data-processed") === "true" || code.trim() === "") {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let renderElement = item;
|
|
32
|
+
if (item.tagName === "CODE") {
|
|
33
|
+
renderElement = document.createElement("div");
|
|
34
|
+
Array.from(item.attributes).forEach((attribute) => {
|
|
35
|
+
renderElement.setAttribute(attribute.name, attribute.value);
|
|
36
|
+
});
|
|
37
|
+
item.replaceWith(renderElement);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const source = await looseJsonParse(code);
|
|
42
|
+
wavedrom.renderWaveElement(wavedromIndex, source, renderElement, wavedrom.waveSkin, false);
|
|
43
|
+
wavedromIndex++;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
const lineBreak = document.createElement("br");
|
|
47
|
+
renderElement.replaceChildren(document.createTextNode("wavedrom render error:"), lineBreak,
|
|
48
|
+
document.createTextNode(message));
|
|
49
|
+
renderElement.classList.add("vditor-reset--error");
|
|
50
|
+
}
|
|
51
|
+
renderElement.setAttribute("data-processed", "true");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
};
|
package/src/ts/preview/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {flowchartRender} from "../markdown/flowchartRender";
|
|
|
5
5
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
6
6
|
import {graphvizRender} from "../markdown/graphvizRender";
|
|
7
7
|
import {highlightRender} from "../markdown/highlightRender";
|
|
8
|
+
import {renderImageCaptions} from "../markdown/imageCaptionRender";
|
|
8
9
|
import {mathRender} from "../markdown/mathRender";
|
|
9
10
|
import {mediaRender} from "../markdown/mediaRender";
|
|
10
11
|
import {mermaidRender} from "../markdown/mermaidRender";
|
|
@@ -12,6 +13,7 @@ import {SMILESRender} from "../markdown/SMILESRender";
|
|
|
12
13
|
import {markmapRender} from "../markdown/markmapRender";
|
|
13
14
|
import {mindmapRender} from "../markdown/mindmapRender";
|
|
14
15
|
import {plantumlRender} from "../markdown/plantumlRender";
|
|
16
|
+
import {wavedromRender} from "../markdown/wavedromRender";
|
|
15
17
|
import {getEventName} from "../util/compatibility";
|
|
16
18
|
import {hasClosestByClassName, hasClosestByMatchTag} from "../util/hasClosest";
|
|
17
19
|
import {hasClosestByTag} from "../util/hasClosestByHeadings";
|
|
@@ -152,6 +154,7 @@ export class Preview {
|
|
|
152
154
|
|
|
153
155
|
if (value) {
|
|
154
156
|
this.previewElement.innerHTML = value;
|
|
157
|
+
renderImageCaptions(this.previewElement, "preview", vditor.options.preview.markdown.imageCaption);
|
|
155
158
|
return;
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -205,6 +208,7 @@ export class Preview {
|
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
private afterRender(vditor: IVditor, startTime: number) {
|
|
211
|
+
renderImageCaptions(this.previewElement, "preview", vditor.options.preview.markdown.imageCaption);
|
|
208
212
|
if (vditor.options.preview.parse) {
|
|
209
213
|
vditor.options.preview.parse(this.element);
|
|
210
214
|
}
|
|
@@ -229,6 +233,7 @@ export class Preview {
|
|
|
229
233
|
SMILESRender(vditor.preview.previewElement, vditor.options.cdn, vditor.options.theme);
|
|
230
234
|
flowchartRender(vditor.preview.previewElement, vditor.options.cdn);
|
|
231
235
|
graphvizRender(vditor.preview.previewElement, vditor.options.cdn);
|
|
236
|
+
wavedromRender(vditor.preview.previewElement, vditor.options.cdn);
|
|
232
237
|
chartRender(vditor.preview.previewElement, vditor.options.cdn, vditor.options.theme);
|
|
233
238
|
mindmapRender(vditor.preview.previewElement, vditor.options.cdn, vditor.options.theme);
|
|
234
239
|
plantumlRender(vditor.preview.previewElement, vditor.options.cdn);
|
package/src/ts/util/Options.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const fixCJKPosition = (range: Range, vditor: IVditor, event: KeyboardEve
|
|
|
52
52
|
|
|
53
53
|
// https://github.com/Vanessa219/vditor/issues/1289 WKWebView切换输入法产生六分之一空格,造成光标错位
|
|
54
54
|
if (pLiElement.nodeValue) {
|
|
55
|
-
pLiElement.nodeValue = pLiElement.nodeValue.replace(/\u2006/g,
|
|
55
|
+
pLiElement.nodeValue = pLiElement.nodeValue.replace(/\u2006/g, "");
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const zwspNode = document.createTextNode(Constants.ZWSP);
|
|
@@ -453,6 +453,41 @@ export const execAfterRender = (vditor: IVditor, options = {
|
|
|
453
453
|
}
|
|
454
454
|
};
|
|
455
455
|
|
|
456
|
+
const isEmptyListItem = (liElement: HTMLElement) => {
|
|
457
|
+
if (liElement.textContent.split(Constants.ZWSP).join("").trim() !== "") {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return !liElement.querySelector([
|
|
461
|
+
"audio", "canvas", "embed", "hr", "iframe", "img", "object", "ol", "pre", "svg", "table", "ul", "video",
|
|
462
|
+
"input:not([type='checkbox'])",
|
|
463
|
+
].join(", "));
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
export const exitEmptyListItem = (liElement: HTMLElement) => {
|
|
467
|
+
const listElement = liElement.parentElement;
|
|
468
|
+
const listParent = listElement.parentElement;
|
|
469
|
+
const paragraphElement = document.createElement("p");
|
|
470
|
+
paragraphElement.setAttribute("data-block", "0");
|
|
471
|
+
paragraphElement.innerHTML = "<wbr>";
|
|
472
|
+
|
|
473
|
+
let trailingListElement: HTMLElement | undefined;
|
|
474
|
+
if (liElement.nextElementSibling) {
|
|
475
|
+
trailingListElement = listElement.cloneNode(false) as HTMLElement;
|
|
476
|
+
while (liElement.nextElementSibling) {
|
|
477
|
+
trailingListElement.appendChild(liElement.nextElementSibling);
|
|
478
|
+
}
|
|
479
|
+
listParent.insertBefore(trailingListElement, listElement.nextSibling);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
liElement.remove();
|
|
483
|
+
if (listElement.childElementCount === 0) {
|
|
484
|
+
listParent.replaceChild(paragraphElement, listElement);
|
|
485
|
+
} else {
|
|
486
|
+
listParent.insertBefore(paragraphElement, trailingListElement || listElement.nextSibling);
|
|
487
|
+
}
|
|
488
|
+
return paragraphElement;
|
|
489
|
+
};
|
|
490
|
+
|
|
456
491
|
export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | false, event: KeyboardEvent) => {
|
|
457
492
|
const startContainer = range.startContainer;
|
|
458
493
|
const liElement = hasClosestByMatchTag(startContainer, "LI");
|
|
@@ -471,6 +506,28 @@ export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | f
|
|
|
471
506
|
return true;
|
|
472
507
|
}
|
|
473
508
|
|
|
509
|
+
// 嵌套列表末尾的空列表项回车后转为父列表项中的段落
|
|
510
|
+
// https://github.com/Vanessa219/vditor/issues/939
|
|
511
|
+
if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Enter" && range.collapsed &&
|
|
512
|
+
!liElement.nextElementSibling && liElement.parentElement.parentElement.tagName === "LI" &&
|
|
513
|
+
isEmptyListItem(liElement)) {
|
|
514
|
+
const paragraphElement = exitEmptyListItem(liElement);
|
|
515
|
+
setRangeByWbr(paragraphElement, range);
|
|
516
|
+
execAfterRender(vditor);
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 空列表项删除标记后退出当前列表
|
|
522
|
+
if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
|
|
523
|
+
liElement.textContent.trim().replace(Constants.ZWSP, "") === "" && range.toString() === "") {
|
|
524
|
+
const paragraphElement = exitEmptyListItem(liElement);
|
|
525
|
+
setRangeByWbr(paragraphElement, range);
|
|
526
|
+
execAfterRender(vditor);
|
|
527
|
+
event.preventDefault();
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
|
|
474
531
|
if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
|
|
475
532
|
!liElement.previousElementSibling && range.toString() === "" &&
|
|
476
533
|
getSelectPosition(liElement, vditor[vditor.currentMode].element, range).start === 0) {
|
|
@@ -488,20 +545,6 @@ export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | f
|
|
|
488
545
|
return true;
|
|
489
546
|
}
|
|
490
547
|
|
|
491
|
-
// 空列表删除后与上一级段落对齐
|
|
492
|
-
if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
|
|
493
|
-
liElement.textContent.trim().replace(Constants.ZWSP, "") === "" &&
|
|
494
|
-
range.toString() === "" && liElement.previousElementSibling?.tagName === "LI") {
|
|
495
|
-
liElement.previousElementSibling.insertAdjacentText("beforeend", "\n\n");
|
|
496
|
-
range.selectNodeContents(liElement.previousElementSibling);
|
|
497
|
-
range.collapse(false);
|
|
498
|
-
liElement.remove();
|
|
499
|
-
setRangeByWbr(vditor[vditor.currentMode].element, range);
|
|
500
|
-
execAfterRender(vditor);
|
|
501
|
-
event.preventDefault();
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
548
|
if (!isCtrl(event) && !event.altKey && event.key === "Tab") {
|
|
506
549
|
// 光标位于第一/零字符时,tab 用于列表的缩进
|
|
507
550
|
let isFirst = false;
|
|
@@ -1021,6 +1064,27 @@ export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEven
|
|
|
1021
1064
|
const startContainer = range.startContainer;
|
|
1022
1065
|
const blockquoteElement = hasClosestByMatchTag(startContainer, "BLOCKQUOTE");
|
|
1023
1066
|
if (blockquoteElement && range.toString() === "") {
|
|
1067
|
+
if (pElement && event.key === "Enter" && !isCtrl(event) && !event.shiftKey && !event.altKey &&
|
|
1068
|
+
pElement.parentElement.isSameNode(blockquoteElement) &&
|
|
1069
|
+
hasClosestByMatchTag(blockquoteElement.parentElement, "LI") &&
|
|
1070
|
+
(range.collapsed || !range.cloneContents().firstElementChild) &&
|
|
1071
|
+
pElement.innerHTML.replace(Constants.ZWSP, "") !== "\n" &&
|
|
1072
|
+
pElement.innerHTML.replace(Constants.ZWSP, "") !== "") {
|
|
1073
|
+
// 列表内的引用由浏览器处理回车时会新建列表项 https://github.com/Vanessa219/vditor/issues/1925
|
|
1074
|
+
const trailingRange = range.cloneRange();
|
|
1075
|
+
trailingRange.setEnd(pElement, pElement.childNodes.length);
|
|
1076
|
+
const paragraphElement = document.createElement("p");
|
|
1077
|
+
paragraphElement.setAttribute("data-block", "0");
|
|
1078
|
+
paragraphElement.appendChild(trailingRange.extractContents());
|
|
1079
|
+
pElement.insertAdjacentElement("afterend", paragraphElement);
|
|
1080
|
+
range.setStart(paragraphElement, 0);
|
|
1081
|
+
range.collapse(true);
|
|
1082
|
+
setSelectionFocus(range);
|
|
1083
|
+
execAfterRender(vditor);
|
|
1084
|
+
event.preventDefault();
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1024
1088
|
if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey &&
|
|
1025
1089
|
getSelectPosition(blockquoteElement, vditor[vditor.currentMode].element, range).start === 0) {
|
|
1026
1090
|
// Backspace: 光标位于引用中的第零个字符,仅删除引用标签
|
|
@@ -1068,9 +1132,15 @@ export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEven
|
|
|
1068
1132
|
return true;
|
|
1069
1133
|
}
|
|
1070
1134
|
|
|
1071
|
-
|
|
1135
|
+
const itemElement = blockquoteElement.parentElement.tagName === "LI" ? blockquoteElement.parentElement : null;
|
|
1136
|
+
const afterBlockElement = !blockquoteElement.nextElementSibling && itemElement?.nextElementSibling?.tagName === "LI" ?
|
|
1137
|
+
itemElement : blockquoteElement;
|
|
1138
|
+
if (insertAfterBlock(vditor, event, range, blockquoteElement, afterBlockElement)) {
|
|
1072
1139
|
return true;
|
|
1073
1140
|
}
|
|
1141
|
+
if (event.key === "ArrowUp" && itemElement && !blockquoteElement.previousElementSibling) {
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1074
1144
|
if (insertBeforeBlock(vditor, event, range, blockquoteElement, blockquoteElement)) {
|
|
1075
1145
|
return true;
|
|
1076
1146
|
}
|
|
@@ -1255,7 +1325,7 @@ export const fixFirefoxArrowUpTable = (event: KeyboardEvent, blockElement: false
|
|
|
1255
1325
|
return false;
|
|
1256
1326
|
};
|
|
1257
1327
|
|
|
1258
|
-
export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent) & {
|
|
1328
|
+
export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent) & {target: HTMLElement}, callback: {
|
|
1259
1329
|
pasteCode(code: string): void,
|
|
1260
1330
|
}) => {
|
|
1261
1331
|
if (vditor[vditor.currentMode].element.getAttribute("contenteditable") !== "true") {
|
|
@@ -1290,6 +1360,12 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
|
|
|
1290
1360
|
return ["", Lute.WalkContinue];
|
|
1291
1361
|
}
|
|
1292
1362
|
|
|
1363
|
+
const parent = node.__internal_object__.Parent;
|
|
1364
|
+
// 链接引用的目标地址由定义节点提供,不在引用位置重复渲染
|
|
1365
|
+
if (parent.Type === 33 && parent.LinkType === 3) {
|
|
1366
|
+
return ["", Lute.WalkContinue];
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1293
1369
|
if (vditor.options.upload.renderLinkDest) {
|
|
1294
1370
|
return vditor.options.upload.renderLinkDest(vditor, node, entering);
|
|
1295
1371
|
}
|
|
@@ -1404,6 +1480,11 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
|
|
|
1404
1480
|
if (textHTML.trim() !== "") {
|
|
1405
1481
|
const tempElement = document.createElement("div");
|
|
1406
1482
|
tempElement.innerHTML = textHTML;
|
|
1483
|
+
if (!vditor.options.upload.base64ToLink) {
|
|
1484
|
+
// word 复制的图文混合,替换为 link: <v:imagedata src="file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png" o:title="">
|
|
1485
|
+
await processVMLImage(vditor, tempElement, ("clipboardData" in event ? event.clipboardData : event.dataTransfer).getData("text/rtf"));
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1407
1488
|
tempElement.querySelectorAll("[style]").forEach((e) => {
|
|
1408
1489
|
e.removeAttribute("style");
|
|
1409
1490
|
});
|
|
@@ -1440,14 +1521,14 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
|
|
|
1440
1521
|
if (file && file.type.startsWith("image")) {
|
|
1441
1522
|
fileReader.readAsDataURL(file);
|
|
1442
1523
|
fileReader.onload = () => {
|
|
1443
|
-
let imgHTML =
|
|
1524
|
+
let imgHTML = "";
|
|
1444
1525
|
if (vditor.currentMode === "wysiwyg") {
|
|
1445
1526
|
imgHTML += `<img alt="${file.name}" src="${fileReader.result.toString()}">\n`;
|
|
1446
1527
|
} else {
|
|
1447
1528
|
imgHTML += `})\n`;
|
|
1448
1529
|
}
|
|
1449
1530
|
document.execCommand("insertHTML", false, imgHTML);
|
|
1450
|
-
}
|
|
1531
|
+
};
|
|
1451
1532
|
}
|
|
1452
1533
|
}
|
|
1453
1534
|
} else if (textPlain.trim() !== "" && files.length === 0) {
|
|
@@ -1458,11 +1539,11 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
|
|
|
1458
1539
|
if (vditor.currentMode === "ir") {
|
|
1459
1540
|
renderers.Md2VditorIRDOM = {renderLinkDest};
|
|
1460
1541
|
vditor.lute.SetJSRenderers({renderers});
|
|
1461
|
-
insertHTML(vditor.lute.Md2VditorIRDOM(textPlain), vditor);
|
|
1542
|
+
insertHTML(Lute.Sanitize(vditor.lute.Md2VditorIRDOM(textPlain)), vditor);
|
|
1462
1543
|
} else if (vditor.currentMode === "wysiwyg") {
|
|
1463
1544
|
renderers.Md2VditorDOM = {renderLinkDest};
|
|
1464
1545
|
vditor.lute.SetJSRenderers({renderers});
|
|
1465
|
-
insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
|
|
1546
|
+
insertHTML(Lute.Sanitize(vditor.lute.Md2VditorDOM(textPlain)), vditor);
|
|
1466
1547
|
} else {
|
|
1467
1548
|
renderers.Md2VditorSVDOM = {renderLinkDest};
|
|
1468
1549
|
vditor.lute.SetJSRenderers({renderers});
|
|
@@ -1499,3 +1580,66 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
|
|
|
1499
1580
|
scrollCenter(vditor);
|
|
1500
1581
|
}
|
|
1501
1582
|
};
|
|
1583
|
+
|
|
1584
|
+
const processVMLImage = async (vditor: IVditor, root: Element, rtfData: string) => {
|
|
1585
|
+
if (!rtfData) {
|
|
1586
|
+
return;
|
|
1587
|
+
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/;
|
|
1591
|
+
const regexPicture = new RegExp("(?:(" + regexPictureHeader.source + "))([\\da-fA-F\\s]+)\\}", "g");
|
|
1592
|
+
const regImages = rtfData.match(regexPicture);
|
|
1593
|
+
const images = [];
|
|
1594
|
+
if (regImages) {
|
|
1595
|
+
for (const image of regImages) {
|
|
1596
|
+
let imageType;
|
|
1597
|
+
|
|
1598
|
+
if (image.includes("\\pngblip")) {
|
|
1599
|
+
imageType = "image/png";
|
|
1600
|
+
} else if (image.includes("\\jpegblip")) {
|
|
1601
|
+
imageType = "image/jpeg";
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
if (imageType) {
|
|
1605
|
+
images.push({
|
|
1606
|
+
hex: image.replace(regexPictureHeader, "").replace(/[^\da-fA-F]/g, ""),
|
|
1607
|
+
type: imageType,
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
const shapes: Array<{shape: Element, img: Element}> = [];
|
|
1614
|
+
walk(root, (child: Element) => {
|
|
1615
|
+
if (child.tagName === "V:SHAPE") {
|
|
1616
|
+
walk(child, (sub) => {
|
|
1617
|
+
if (sub.tagName === "V:IMAGEDATA") shapes.push({shape: child, img: sub});
|
|
1618
|
+
});
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
for (let i = 0; i < shapes.length; i++) {
|
|
1623
|
+
const img = document.createElement("img");
|
|
1624
|
+
const newSrc = "data:" + images[i].type + ";base64," + btoa((images[i].hex.match(/\w{2}/g) || []).map(char => {
|
|
1625
|
+
return String.fromCharCode(parseInt(char, 16));
|
|
1626
|
+
}).join(""));
|
|
1627
|
+
img.src = newSrc;
|
|
1628
|
+
img.title = shapes[i].img.getAttribute("title");
|
|
1629
|
+
shapes[i].shape.parentNode.replaceChild(img, shapes[i].shape);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
const imgs = root.querySelectorAll("img");
|
|
1633
|
+
for (let i = 0; i < imgs.length; i++) {
|
|
1634
|
+
const src = imgs[i].src || "";
|
|
1635
|
+
if (src) imgs[i].src = await vditor.options.upload.base64ToLink(src);
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
const walk = (el: Element, fn: (el: Element) => boolean | void) => {
|
|
1640
|
+
const goNext = fn(el);
|
|
1641
|
+
if (goNext !== false)
|
|
1642
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
1643
|
+
walk(el.children[i], fn);
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
@@ -10,6 +10,7 @@ import {markmapRender} from "../markdown/markmapRender";
|
|
|
10
10
|
import {mindmapRender} from "../markdown/mindmapRender";
|
|
11
11
|
import {plantumlRender} from "../markdown/plantumlRender";
|
|
12
12
|
import {SMILESRender} from "../markdown/SMILESRender";
|
|
13
|
+
import {wavedromRender} from "../markdown/wavedromRender";
|
|
13
14
|
|
|
14
15
|
export const processPasteCode = (html: string, text: string, type = "sv") => {
|
|
15
16
|
const tempElement = document.createElement("div");
|
|
@@ -82,6 +83,8 @@ export const processCodeRender = (previewPanel: HTMLElement, vditor: IVditor) =>
|
|
|
82
83
|
plantumlRender(previewPanel, vditor.options.cdn);
|
|
83
84
|
} else if (language === "graphviz") {
|
|
84
85
|
graphvizRender(previewPanel, vditor.options.cdn);
|
|
86
|
+
} else if (language === "wavedrom") {
|
|
87
|
+
wavedromRender(previewPanel, vditor.options.cdn);
|
|
85
88
|
} else if (language === "math") {
|
|
86
89
|
mathRender(previewPanel, {cdn: vditor.options.cdn, math: vditor.options.preview.math});
|
|
87
90
|
} else {
|
package/src/ts/util/selection.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Constants} from "../constants";
|
|
2
2
|
import {isChrome} from "./compatibility";
|
|
3
|
-
import {hasClosestBlock, hasClosestByClassName} from "./hasClosest";
|
|
3
|
+
import {hasClosestBlock, hasClosestByClassName, hasClosestByMatchTag} from "./hasClosest";
|
|
4
4
|
|
|
5
5
|
export const getEditorRange = (vditor: IVditor) => {
|
|
6
6
|
let range: Range;
|
|
@@ -262,7 +262,12 @@ export const insertHTML = (html: string, vditor: IVditor) => {
|
|
|
262
262
|
if (!blockElement) {
|
|
263
263
|
vditor[vditor.currentMode].element.insertAdjacentHTML("beforeend", pasteElement.innerHTML);
|
|
264
264
|
} else {
|
|
265
|
-
|
|
265
|
+
const liElement = hasClosestByMatchTag(range.startContainer, "LI");
|
|
266
|
+
if (liElement && pasteElement.firstElementChild.tagName === "UL") {
|
|
267
|
+
liElement.insertAdjacentHTML("afterend", pasteElement.firstElementChild.innerHTML);
|
|
268
|
+
} else {
|
|
269
|
+
blockElement.insertAdjacentHTML("afterend", pasteElement.innerHTML);
|
|
270
|
+
}
|
|
266
271
|
}
|
|
267
272
|
setRangeByWbr(vditor[vditor.currentMode].element, range);
|
|
268
273
|
} else {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
2
|
+
import {renderImageCaptions} from "../markdown/imageCaptionRender";
|
|
2
3
|
import {accessLocalStorage} from "../util/compatibility";
|
|
3
4
|
|
|
4
5
|
export const afterRenderEvent = (vditor: IVditor, options = {
|
|
@@ -6,6 +7,7 @@ export const afterRenderEvent = (vditor: IVditor, options = {
|
|
|
6
7
|
enableHint: false,
|
|
7
8
|
enableInput: true,
|
|
8
9
|
}) => {
|
|
10
|
+
renderImageCaptions(vditor.wysiwyg.element, "wysiwyg", vditor.options.preview.markdown.imageCaption);
|
|
9
11
|
if (options.enableHint) {
|
|
10
12
|
vditor.hint.render(vditor);
|
|
11
13
|
}
|
|
@@ -33,6 +33,7 @@ import {afterRenderEvent} from "./afterRenderEvent";
|
|
|
33
33
|
import {removeBlockElement} from "./processKeydown";
|
|
34
34
|
import {renderToc} from "../util/toc";
|
|
35
35
|
import {getMarkdown} from "../markdown/getMarkdown";
|
|
36
|
+
import {renderImageCaptions} from "../markdown/imageCaptionRender";
|
|
36
37
|
|
|
37
38
|
export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
38
39
|
clearTimeout(vditor.wysiwyg.hlToolbarTimeoutId);
|
|
@@ -65,6 +66,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
65
66
|
if (footnotesElement) {
|
|
66
67
|
vditor.wysiwyg.popover.innerHTML = "";
|
|
67
68
|
genClose(footnotesElement, vditor);
|
|
69
|
+
customWysiwygToolbar(vditor, "footnotes-block")
|
|
68
70
|
setPopoverPosition(vditor, footnotesElement);
|
|
69
71
|
return;
|
|
70
72
|
}
|
|
@@ -195,6 +197,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
195
197
|
if (tocElement) {
|
|
196
198
|
vditor.wysiwyg.popover.innerHTML = "";
|
|
197
199
|
genClose(tocElement, vditor);
|
|
200
|
+
customWysiwygToolbar(vditor, "vditor-toc")
|
|
198
201
|
setPopoverPosition(vditor, tocElement);
|
|
199
202
|
return;
|
|
200
203
|
}
|
|
@@ -206,6 +209,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
206
209
|
genUp(range, blockquoteElement, vditor);
|
|
207
210
|
genDown(range, blockquoteElement, vditor);
|
|
208
211
|
genClose(blockquoteElement, vditor);
|
|
212
|
+
customWysiwygToolbar(vditor, "blockquote")
|
|
209
213
|
setPopoverPosition(vditor, blockquoteElement);
|
|
210
214
|
}
|
|
211
215
|
|
|
@@ -216,7 +220,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
216
220
|
genUp(range, liElement, vditor);
|
|
217
221
|
genDown(range, liElement, vditor);
|
|
218
222
|
genClose(liElement, vditor);
|
|
219
|
-
|
|
223
|
+
customWysiwygToolbar(vditor, "li")
|
|
220
224
|
setPopoverPosition(vditor, liElement);
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -549,6 +553,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
549
553
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
550
554
|
vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
|
|
551
555
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
|
|
556
|
+
customWysiwygToolbar(vditor, "table")
|
|
552
557
|
setPopoverPosition(vditor, tableElement);
|
|
553
558
|
}
|
|
554
559
|
|
|
@@ -596,6 +601,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
596
601
|
|
|
597
602
|
genClose(footnotesRefElement, vditor);
|
|
598
603
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
604
|
+
customWysiwygToolbar(vditor, "footnotes-ref")
|
|
599
605
|
setPopoverPosition(vditor, footnotesRefElement);
|
|
600
606
|
}
|
|
601
607
|
|
|
@@ -684,7 +690,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
684
690
|
}
|
|
685
691
|
const matchLangData: IHintData[] = [];
|
|
686
692
|
const key = language.value.substring(0, language.selectionStart);
|
|
687
|
-
(vditor.options.preview.hljs.langs ||
|
|
693
|
+
(vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
|
|
688
694
|
if (keyName.indexOf(key.toLowerCase()) > -1) {
|
|
689
695
|
matchLangData.push({
|
|
690
696
|
html: keyName,
|
|
@@ -696,6 +702,9 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
696
702
|
event.preventDefault();
|
|
697
703
|
};
|
|
698
704
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
|
|
705
|
+
customWysiwygToolbar(vditor, "code-block")
|
|
706
|
+
} else {
|
|
707
|
+
customWysiwygToolbar(vditor, "block")
|
|
699
708
|
}
|
|
700
709
|
setPopoverPosition(vditor, blockRenderElement);
|
|
701
710
|
} else {
|
|
@@ -735,6 +744,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
735
744
|
genDown(range, headingElement, vditor);
|
|
736
745
|
genClose(headingElement, vditor);
|
|
737
746
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
747
|
+
customWysiwygToolbar(vditor, "heading")
|
|
738
748
|
setPopoverPosition(vditor, headingElement);
|
|
739
749
|
}
|
|
740
750
|
|
|
@@ -763,7 +773,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
|
|
|
763
773
|
genUp(range, blockElement, vditor);
|
|
764
774
|
genDown(range, blockElement, vditor);
|
|
765
775
|
genClose(blockElement, vditor);
|
|
766
|
-
|
|
776
|
+
customWysiwygToolbar(vditor, "block")
|
|
767
777
|
setPopoverPosition(vditor, blockElement);
|
|
768
778
|
} else {
|
|
769
779
|
vditor.wysiwyg.popover.style.display = "none";
|
|
@@ -864,6 +874,7 @@ export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement,
|
|
|
864
874
|
genClose(linkRefElement, vditor);
|
|
865
875
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
866
876
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
|
|
877
|
+
customWysiwygToolbar(vditor, "link-ref")
|
|
867
878
|
setPopoverPosition(vditor, linkRefElement);
|
|
868
879
|
};
|
|
869
880
|
|
|
@@ -1058,6 +1069,7 @@ export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range
|
|
|
1058
1069
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
1059
1070
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
|
|
1060
1071
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
|
|
1072
|
+
customWysiwygToolbar(vditor, "a")
|
|
1061
1073
|
setPopoverPosition(vditor, aElement);
|
|
1062
1074
|
};
|
|
1063
1075
|
|
|
@@ -1068,6 +1080,7 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
|
|
|
1068
1080
|
imgElement.setAttribute("src", inputElement.value);
|
|
1069
1081
|
imgElement.setAttribute("alt", alt.value);
|
|
1070
1082
|
imgElement.setAttribute("title", title.value);
|
|
1083
|
+
renderImageCaptions(vditor.wysiwyg.element, "wysiwyg", vditor.options.preview.markdown.imageCaption);
|
|
1071
1084
|
if (typeof vditor.options.input === "function") {
|
|
1072
1085
|
vditor.options.input(getMarkdown(vditor));
|
|
1073
1086
|
}
|
|
@@ -1122,7 +1135,7 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
|
|
|
1122
1135
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
|
|
1123
1136
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
|
|
1124
1137
|
vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
|
|
1125
|
-
|
|
1138
|
+
customWysiwygToolbar(vditor, "image")
|
|
1126
1139
|
setPopoverPosition(vditor, imgElement);
|
|
1127
1140
|
};
|
|
1128
1141
|
|
|
@@ -1137,3 +1150,9 @@ const focusToElement = (event: KeyboardEvent, range: Range) => {
|
|
|
1137
1150
|
return true;
|
|
1138
1151
|
}
|
|
1139
1152
|
};
|
|
1153
|
+
|
|
1154
|
+
const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
|
|
1155
|
+
if (vditor.options.customWysiwygToolbar) {
|
|
1156
|
+
vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
|
|
1157
|
+
}
|
|
1158
|
+
};
|
package/src/ts/wysiwyg/input.ts
CHANGED
|
@@ -71,6 +71,7 @@ export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
|
|
|
71
71
|
|
|
72
72
|
const isWYSIWYGElement = blockElement.isEqualNode(vditor.wysiwyg.element);
|
|
73
73
|
const footnoteElement = hasClosestByAttribute(blockElement, "data-type", "footnotes-block");
|
|
74
|
+
const calloutElement = hasClosestByAttribute(range.startContainer, "data-type", "callout");
|
|
74
75
|
|
|
75
76
|
if (!isWYSIWYGElement) {
|
|
76
77
|
// 列表需要到最顶层
|
|
@@ -90,6 +91,10 @@ export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
|
|
|
90
91
|
blockElement = footnoteElement;
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
if (calloutElement) {
|
|
95
|
+
blockElement = calloutElement;
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
html = blockElement.outerHTML;
|
|
94
99
|
|
|
95
100
|
if (blockElement.tagName === "UL" || blockElement.tagName === "OL") {
|