vditor 3.11.0 → 3.11.2

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.
@@ -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);
@@ -1080,7 +1080,7 @@ export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEven
1080
1080
 
1081
1081
  export const fixTask = (vditor: IVditor, range: Range, event: KeyboardEvent) => {
1082
1082
  const startContainer = range.startContainer;
1083
- const taskItemElement = hasClosestByMatchTag(startContainer, "li");
1083
+ const taskItemElement = hasClosestByMatchTag(startContainer, "LI");
1084
1084
  if (taskItemElement && taskItemElement.classList.contains("vditor-task")) {
1085
1085
  if (matchHotKey("⇧⌘J", event)) {
1086
1086
  // ctrl + shift: toggle checked
@@ -1255,7 +1255,7 @@ export const fixFirefoxArrowUpTable = (event: KeyboardEvent, blockElement: false
1255
1255
  return false;
1256
1256
  };
1257
1257
 
1258
- export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent) & { target: HTMLElement }, callback: {
1258
+ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent) & {target: HTMLElement}, callback: {
1259
1259
  pasteCode(code: string): void,
1260
1260
  }) => {
1261
1261
  if (vditor[vditor.currentMode].element.getAttribute("contenteditable") !== "true") {
@@ -1404,6 +1404,11 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1404
1404
  if (textHTML.trim() !== "") {
1405
1405
  const tempElement = document.createElement("div");
1406
1406
  tempElement.innerHTML = textHTML;
1407
+ if (!vditor.options.upload.base64ToLink) {
1408
+ // word 复制的图文混合,替换为 link: <v:imagedata src="file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png" o:title="">
1409
+ await processVMLImage(vditor, tempElement, ("clipboardData" in event ? event.clipboardData : event.dataTransfer).getData("text/rtf"));
1410
+ }
1411
+
1407
1412
  tempElement.querySelectorAll("[style]").forEach((e) => {
1408
1413
  e.removeAttribute("style");
1409
1414
  });
@@ -1440,14 +1445,14 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1440
1445
  if (file && file.type.startsWith("image")) {
1441
1446
  fileReader.readAsDataURL(file);
1442
1447
  fileReader.onload = () => {
1443
- let imgHTML = ''
1448
+ let imgHTML = "";
1444
1449
  if (vditor.currentMode === "wysiwyg") {
1445
1450
  imgHTML += `<img alt="${file.name}" src="${fileReader.result.toString()}">\n`;
1446
1451
  } else {
1447
1452
  imgHTML += `![${file.name}](${fileReader.result.toString()})\n`;
1448
1453
  }
1449
1454
  document.execCommand("insertHTML", false, imgHTML);
1450
- }
1455
+ };
1451
1456
  }
1452
1457
  }
1453
1458
  } else if (textPlain.trim() !== "" && files.length === 0) {
@@ -1458,11 +1463,11 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1458
1463
  if (vditor.currentMode === "ir") {
1459
1464
  renderers.Md2VditorIRDOM = {renderLinkDest};
1460
1465
  vditor.lute.SetJSRenderers({renderers});
1461
- insertHTML(vditor.lute.Md2VditorIRDOM(textPlain), vditor);
1466
+ insertHTML(Lute.Sanitize(vditor.lute.Md2VditorIRDOM(textPlain)), vditor);
1462
1467
  } else if (vditor.currentMode === "wysiwyg") {
1463
1468
  renderers.Md2VditorDOM = {renderLinkDest};
1464
1469
  vditor.lute.SetJSRenderers({renderers});
1465
- insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
1470
+ insertHTML(Lute.Sanitize(vditor.lute.Md2VditorDOM(textPlain)), vditor);
1466
1471
  } else {
1467
1472
  renderers.Md2VditorSVDOM = {renderLinkDest};
1468
1473
  vditor.lute.SetJSRenderers({renderers});
@@ -1499,3 +1504,66 @@ export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent)
1499
1504
  scrollCenter(vditor);
1500
1505
  }
1501
1506
  };
1507
+
1508
+ const processVMLImage = async (vditor: IVditor, root: Element, rtfData: string) => {
1509
+ if (!rtfData) {
1510
+ return;
1511
+
1512
+ }
1513
+
1514
+ const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/;
1515
+ const regexPicture = new RegExp("(?:(" + regexPictureHeader.source + "))([\\da-fA-F\\s]+)\\}", "g");
1516
+ const regImages = rtfData.match(regexPicture);
1517
+ const images = [];
1518
+ if (regImages) {
1519
+ for (const image of regImages) {
1520
+ let imageType;
1521
+
1522
+ if (image.includes("\\pngblip")) {
1523
+ imageType = "image/png";
1524
+ } else if (image.includes("\\jpegblip")) {
1525
+ imageType = "image/jpeg";
1526
+ }
1527
+
1528
+ if (imageType) {
1529
+ images.push({
1530
+ hex: image.replace(regexPictureHeader, "").replace(/[^\da-fA-F]/g, ""),
1531
+ type: imageType,
1532
+ });
1533
+ }
1534
+ }
1535
+ }
1536
+
1537
+ const shapes: Array<{shape: Element, img: Element}> = [];
1538
+ walk(root, (child: Element) => {
1539
+ if (child.tagName === "V:SHAPE") {
1540
+ walk(child, (sub) => {
1541
+ if (sub.tagName === "V:IMAGEDATA") shapes.push({shape: child, img: sub});
1542
+ });
1543
+ return false;
1544
+ }
1545
+ });
1546
+ for (let i = 0; i < shapes.length; i++) {
1547
+ const img = document.createElement("img");
1548
+ const newSrc = "data:" + images[i].type + ";base64," + btoa((images[i].hex.match(/\w{2}/g) || []).map(char => {
1549
+ return String.fromCharCode(parseInt(char, 16));
1550
+ }).join(""));
1551
+ img.src = newSrc;
1552
+ img.title = shapes[i].img.getAttribute("title");
1553
+ shapes[i].shape.parentNode.replaceChild(img, shapes[i].shape);
1554
+ }
1555
+
1556
+ const imgs = root.querySelectorAll("img");
1557
+ for (let i = 0; i < imgs.length; i++) {
1558
+ const src = imgs[i].src || "";
1559
+ if (src) imgs[i].src = await vditor.options.upload.base64ToLink(src);
1560
+ }
1561
+ };
1562
+
1563
+ const walk = (el: Element, fn: (el: Element) => boolean | void) => {
1564
+ const goNext = fn(el);
1565
+ if (goNext !== false)
1566
+ for (let i = 0; i < el.children.length; i++) {
1567
+ walk(el.children[i], fn);
1568
+ }
1569
+ };
@@ -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
- blockElement.insertAdjacentHTML("afterend", pasteElement.innerHTML);
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 {
@@ -65,6 +65,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
65
65
  if (footnotesElement) {
66
66
  vditor.wysiwyg.popover.innerHTML = "";
67
67
  genClose(footnotesElement, vditor);
68
+ customWysiwygToolbar(vditor, "footnotes-block")
68
69
  setPopoverPosition(vditor, footnotesElement);
69
70
  return;
70
71
  }
@@ -195,6 +196,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
195
196
  if (tocElement) {
196
197
  vditor.wysiwyg.popover.innerHTML = "";
197
198
  genClose(tocElement, vditor);
199
+ customWysiwygToolbar(vditor, "vditor-toc")
198
200
  setPopoverPosition(vditor, tocElement);
199
201
  return;
200
202
  }
@@ -206,6 +208,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
206
208
  genUp(range, blockquoteElement, vditor);
207
209
  genDown(range, blockquoteElement, vditor);
208
210
  genClose(blockquoteElement, vditor);
211
+ customWysiwygToolbar(vditor, "blockquote")
209
212
  setPopoverPosition(vditor, blockquoteElement);
210
213
  }
211
214
 
@@ -216,7 +219,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
216
219
  genUp(range, liElement, vditor);
217
220
  genDown(range, liElement, vditor);
218
221
  genClose(liElement, vditor);
219
-
222
+ customWysiwygToolbar(vditor, "li")
220
223
  setPopoverPosition(vditor, liElement);
221
224
  }
222
225
 
@@ -549,6 +552,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
549
552
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
550
553
  vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
551
554
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
555
+ customWysiwygToolbar(vditor, "table")
552
556
  setPopoverPosition(vditor, tableElement);
553
557
  }
554
558
 
@@ -596,6 +600,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
596
600
 
597
601
  genClose(footnotesRefElement, vditor);
598
602
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
603
+ customWysiwygToolbar(vditor, "footnotes-ref")
599
604
  setPopoverPosition(vditor, footnotesRefElement);
600
605
  }
601
606
 
@@ -684,7 +689,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
684
689
  }
685
690
  const matchLangData: IHintData[] = [];
686
691
  const key = language.value.substring(0, language.selectionStart);
687
- (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
692
+ (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
688
693
  if (keyName.indexOf(key.toLowerCase()) > -1) {
689
694
  matchLangData.push({
690
695
  html: keyName,
@@ -696,6 +701,9 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
696
701
  event.preventDefault();
697
702
  };
698
703
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
704
+ customWysiwygToolbar(vditor, "code-block")
705
+ } else {
706
+ customWysiwygToolbar(vditor, "block")
699
707
  }
700
708
  setPopoverPosition(vditor, blockRenderElement);
701
709
  } else {
@@ -735,6 +743,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
735
743
  genDown(range, headingElement, vditor);
736
744
  genClose(headingElement, vditor);
737
745
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
746
+ customWysiwygToolbar(vditor, "heading")
738
747
  setPopoverPosition(vditor, headingElement);
739
748
  }
740
749
 
@@ -763,7 +772,7 @@ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
763
772
  genUp(range, blockElement, vditor);
764
773
  genDown(range, blockElement, vditor);
765
774
  genClose(blockElement, vditor);
766
-
775
+ customWysiwygToolbar(vditor, "block")
767
776
  setPopoverPosition(vditor, blockElement);
768
777
  } else {
769
778
  vditor.wysiwyg.popover.style.display = "none";
@@ -864,6 +873,7 @@ export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement,
864
873
  genClose(linkRefElement, vditor);
865
874
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
866
875
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
876
+ customWysiwygToolbar(vditor, "link-ref")
867
877
  setPopoverPosition(vditor, linkRefElement);
868
878
  };
869
879
 
@@ -1058,6 +1068,7 @@ export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range
1058
1068
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1059
1069
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
1060
1070
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
1071
+ customWysiwygToolbar(vditor, "a")
1061
1072
  setPopoverPosition(vditor, aElement);
1062
1073
  };
1063
1074
 
@@ -1122,7 +1133,7 @@ export const genImagePopover = (event: Event, vditor: IVditor) => {
1122
1133
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1123
1134
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
1124
1135
  vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
1125
-
1136
+ customWysiwygToolbar(vditor, "image")
1126
1137
  setPopoverPosition(vditor, imgElement);
1127
1138
  };
1128
1139
 
@@ -1137,3 +1148,7 @@ const focusToElement = (event: KeyboardEvent, range: Range) => {
1137
1148
  return true;
1138
1149
  }
1139
1150
  };
1151
+
1152
+ const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
1153
+ vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
1154
+ };