vditor 3.8.10 → 3.8.13

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.
Files changed (60) hide show
  1. package/README.md +3 -2
  2. package/dist/index.css +2 -2
  3. package/dist/index.min.js +1 -15469
  4. package/dist/js/highlight.js/solidity.min.js +73 -0
  5. package/dist/js/highlight.js/styles/abap.css +7 -7
  6. package/dist/js/highlight.js/styles/algol.css +7 -6
  7. package/dist/js/highlight.js/styles/algol_nu.css +7 -7
  8. package/dist/js/highlight.js/styles/arduino.css +7 -5
  9. package/dist/js/highlight.js/styles/autumn.css +7 -5
  10. package/dist/js/highlight.js/styles/borland.css +7 -6
  11. package/dist/js/highlight.js/styles/bw.css +7 -6
  12. package/dist/js/highlight.js/styles/colorful.css +7 -5
  13. package/dist/js/highlight.js/styles/dracula.css +9 -8
  14. package/dist/js/highlight.js/styles/emacs.css +7 -6
  15. package/dist/js/highlight.js/styles/friendly.css +7 -6
  16. package/dist/js/highlight.js/styles/fruity.css +7 -5
  17. package/dist/js/highlight.js/styles/github.css +7 -6
  18. package/dist/js/highlight.js/styles/igor.css +7 -5
  19. package/dist/js/highlight.js/styles/lovelace.css +7 -6
  20. package/dist/js/highlight.js/styles/manni.css +7 -6
  21. package/dist/js/highlight.js/styles/monokai.css +7 -6
  22. package/dist/js/highlight.js/styles/monokailight.css +7 -6
  23. package/dist/js/highlight.js/styles/murphy.css +7 -7
  24. package/dist/js/highlight.js/styles/native.css +7 -6
  25. package/dist/js/highlight.js/styles/paraiso-dark.css +7 -6
  26. package/dist/js/highlight.js/styles/paraiso-light.css +7 -6
  27. package/dist/js/highlight.js/styles/pastie.css +7 -7
  28. package/dist/js/highlight.js/styles/perldoc.css +7 -6
  29. package/dist/js/highlight.js/styles/pygments.css +7 -6
  30. package/dist/js/highlight.js/styles/rainbow_dash.css +7 -6
  31. package/dist/js/highlight.js/styles/rrt.css +7 -6
  32. package/dist/js/highlight.js/styles/solarized-dark.css +7 -6
  33. package/dist/js/highlight.js/styles/solarized-dark256.css +7 -6
  34. package/dist/js/highlight.js/styles/solarized-light.css +7 -6
  35. package/dist/js/highlight.js/styles/swapoff.css +7 -7
  36. package/dist/js/highlight.js/styles/tango.css +7 -7
  37. package/dist/js/highlight.js/styles/trac.css +7 -6
  38. package/dist/js/highlight.js/styles/vim.css +7 -6
  39. package/dist/js/highlight.js/styles/vs.css +7 -6
  40. package/dist/js/highlight.js/styles/xcode.css +7 -6
  41. package/dist/js/highlight.js/yul.min.js +30 -0
  42. package/dist/js/katex/mhchem.min.js +1 -0
  43. package/dist/js/lute/lute.min.js +47 -46
  44. package/dist/method.min.js +1 -2370
  45. package/dist/ts/toolbar/Emoji.d.ts +1 -1
  46. package/dist/types/index.d.ts +2 -0
  47. package/package.json +17 -11
  48. package/src/ts/constants.ts +59 -56
  49. package/src/ts/ir/index.ts +5 -0
  50. package/src/ts/markdown/highlightRender.ts +65 -61
  51. package/src/ts/markdown/mathRender.ts +29 -27
  52. package/src/ts/sv/index.ts +6 -0
  53. package/src/ts/toolbar/Emoji.ts +19 -20
  54. package/src/ts/ui/initUI.ts +4 -0
  55. package/src/ts/undo/index.ts +3 -0
  56. package/src/ts/upload/index.ts +1 -1
  57. package/src/ts/util/Options.ts +5 -0
  58. package/src/ts/util/fixBrowserBehavior.ts +1248 -1245
  59. package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +12 -18
  60. package/src/ts/wysiwyg/index.ts +2 -0
@@ -9,1451 +9,1454 @@ import {input} from "../wysiwyg/input";
9
9
  import {isCtrl, isFirefox} from "./compatibility";
10
10
  import {scrollCenter} from "./editorCommonEvent";
11
11
  import {
12
- getTopList,
13
- hasClosestBlock,
14
- hasClosestByAttribute,
15
- hasClosestByClassName,
16
- hasClosestByMatchTag,
12
+ getTopList,
13
+ hasClosestBlock,
14
+ hasClosestByAttribute,
15
+ hasClosestByClassName,
16
+ hasClosestByMatchTag,
17
17
  } from "./hasClosest";
18
18
  import {getLastNode} from "./hasClosest";
19
19
  import {highlightToolbar} from "./highlightToolbar";
20
20
  import {matchHotKey} from "./hotKey";
21
21
  import {processCodeRender, processPasteCode} from "./processCode";
22
22
  import {
23
- getEditorRange,
24
- getSelectPosition,
25
- insertHTML,
26
- setRangeByWbr,
27
- setSelectionByPosition, setSelectionFocus,
23
+ getEditorRange,
24
+ getSelectPosition,
25
+ insertHTML,
26
+ setRangeByWbr,
27
+ setSelectionByPosition, setSelectionFocus,
28
28
  } from "./selection";
29
29
 
30
30
  // https://github.com/Vanessa219/vditor/issues/508 软键盘无法删除空块
31
31
  export const fixGSKeyBackspace = (event: KeyboardEvent, vditor: IVditor, startContainer: Node) => {
32
- if (event.keyCode === 229 && event.code === "" && event.key === "Unidentified" && vditor.currentMode !== "sv") {
33
- const blockElement = hasClosestBlock(startContainer);
34
- // 移动端的标点符号都显示为 299,因此需限定为空删除的条件
35
- if (blockElement && blockElement.textContent.trim() === "") {
36
- vditor[vditor.currentMode].composingLock = true;
37
- return false;
38
- }
32
+ if (event.keyCode === 229 && event.code === "" && event.key === "Unidentified" && vditor.currentMode !== "sv") {
33
+ const blockElement = hasClosestBlock(startContainer);
34
+ // 移动端的标点符号都显示为 299,因此需限定为空删除的条件
35
+ if (blockElement && blockElement.textContent.trim() === "") {
36
+ vditor[vditor.currentMode].composingLock = true;
37
+ return false;
39
38
  }
40
- return true;
39
+ }
40
+ return true;
41
41
  };
42
42
 
43
43
  // https://github.com/Vanessa219/vditor/issues/361 代码块后输入中文
44
44
  export const fixCJKPosition = (range: Range, vditor: IVditor, event: KeyboardEvent) => {
45
- if (event.key === "Enter" || event.key === "Tab" || event.key === "Backspace" || event.key.indexOf("Arrow") > -1
46
- || isCtrl(event) || event.key === "Escape" || event.shiftKey || event.altKey) {
47
- return;
48
- }
49
- const pLiElement = hasClosestByMatchTag(range.startContainer, "P") ||
50
- hasClosestByMatchTag(range.startContainer, "LI");
51
- if (pLiElement && getSelectPosition(pLiElement, vditor[vditor.currentMode].element, range).start === 0) {
52
- const zwspNode = document.createTextNode(Constants.ZWSP);
53
- range.insertNode(zwspNode);
54
- range.setStartAfter(zwspNode);
55
- }
45
+ if (event.key === "Enter" || event.key === "Tab" || event.key === "Backspace" || event.key.indexOf("Arrow") > -1
46
+ || isCtrl(event) || event.key === "Escape" || event.shiftKey || event.altKey) {
47
+ return;
48
+ }
49
+ const pLiElement = hasClosestByMatchTag(range.startContainer, "P") ||
50
+ hasClosestByMatchTag(range.startContainer, "LI");
51
+ if (pLiElement && getSelectPosition(pLiElement, vditor[vditor.currentMode].element, range).start === 0) {
52
+ const zwspNode = document.createTextNode(Constants.ZWSP);
53
+ range.insertNode(zwspNode);
54
+ range.setStartAfter(zwspNode);
55
+ }
56
56
  };
57
57
 
58
58
  // https://github.com/Vanessa219/vditor/issues/381 光标在内联数学公式中无法向下移动
59
59
  export const fixCursorDownInlineMath = (range: Range, key: string) => {
60
- if (key === "ArrowDown" || key === "ArrowUp") {
61
- const inlineElement = hasClosestByAttribute(range.startContainer, "data-type", "math-inline") ||
62
- hasClosestByAttribute(range.startContainer, "data-type", "html-entity") ||
63
- hasClosestByAttribute(range.startContainer, "data-type", "html-inline");
64
- if (inlineElement) {
65
- if (key === "ArrowDown") {
66
- range.setStartAfter(inlineElement.parentElement);
67
- }
68
- if (key === "ArrowUp") {
69
- range.setStartBefore(inlineElement.parentElement);
70
- }
71
- }
60
+ if (key === "ArrowDown" || key === "ArrowUp") {
61
+ const inlineElement = hasClosestByAttribute(range.startContainer, "data-type", "math-inline") ||
62
+ hasClosestByAttribute(range.startContainer, "data-type", "html-entity") ||
63
+ hasClosestByAttribute(range.startContainer, "data-type", "html-inline");
64
+ if (inlineElement) {
65
+ if (key === "ArrowDown") {
66
+ range.setStartAfter(inlineElement.parentElement);
67
+ }
68
+ if (key === "ArrowUp") {
69
+ range.setStartBefore(inlineElement.parentElement);
70
+ }
72
71
  }
72
+ }
73
73
  };
74
74
 
75
75
  export const insertEmptyBlock = (vditor: IVditor, position: InsertPosition) => {
76
- const range = getEditorRange(vditor);
77
- const blockElement = hasClosestBlock(range.startContainer);
78
- if (blockElement) {
79
- blockElement.insertAdjacentHTML(position, `<p data-block="0">${Constants.ZWSP}<wbr>\n</p>`);
80
- setRangeByWbr(vditor[vditor.currentMode].element, range);
81
- highlightToolbar(vditor);
82
- execAfterRender(vditor);
83
- }
76
+ const range = getEditorRange(vditor);
77
+ const blockElement = hasClosestBlock(range.startContainer);
78
+ if (blockElement) {
79
+ blockElement.insertAdjacentHTML(position, `<p data-block="0">${Constants.ZWSP}<wbr>\n</p>`);
80
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
81
+ highlightToolbar(vditor);
82
+ execAfterRender(vditor);
83
+ }
84
84
  };
85
85
 
86
86
  export const isFirstCell = (cellElement: HTMLElement) => {
87
- const tableElement = hasClosestByMatchTag(cellElement, "TABLE") as HTMLTableElement;
88
- if (tableElement && tableElement.rows[0].cells[0].isSameNode(cellElement)) {
89
- return tableElement;
90
- }
91
- return false;
87
+ const tableElement = hasClosestByMatchTag(cellElement, "TABLE") as HTMLTableElement;
88
+ if (tableElement && tableElement.rows[0].cells[0].isSameNode(cellElement)) {
89
+ return tableElement;
90
+ }
91
+ return false;
92
92
  };
93
93
 
94
94
  export const isLastCell = (cellElement: HTMLElement) => {
95
- const tableElement = hasClosestByMatchTag(cellElement, "TABLE") as HTMLTableElement;
96
- if (tableElement && tableElement.lastElementChild.lastElementChild.lastElementChild.isSameNode(cellElement)) {
97
- return tableElement;
98
- }
99
- return false;
95
+ const tableElement = hasClosestByMatchTag(cellElement, "TABLE") as HTMLTableElement;
96
+ if (tableElement && tableElement.lastElementChild.lastElementChild.lastElementChild.isSameNode(cellElement)) {
97
+ return tableElement;
98
+ }
99
+ return false;
100
100
  };
101
101
 
102
102
  // 光标设置到前一个表格中
103
103
  const goPreviousCell = (cellElement: HTMLElement, range: Range, isSelected = true) => {
104
- let previousElement = cellElement.previousElementSibling;
105
- if (!previousElement) {
106
- if (cellElement.parentElement.previousElementSibling) {
107
- previousElement = cellElement.parentElement.previousElementSibling.lastElementChild;
108
- } else if (cellElement.parentElement.parentElement.tagName === "TBODY" &&
109
- cellElement.parentElement.parentElement.previousElementSibling) {
110
- previousElement = cellElement.parentElement
111
- .parentElement.previousElementSibling.lastElementChild.lastElementChild;
112
- } else {
113
- previousElement = null;
114
- }
104
+ let previousElement = cellElement.previousElementSibling;
105
+ if (!previousElement) {
106
+ if (cellElement.parentElement.previousElementSibling) {
107
+ previousElement = cellElement.parentElement.previousElementSibling.lastElementChild;
108
+ } else if (cellElement.parentElement.parentElement.tagName === "TBODY" &&
109
+ cellElement.parentElement.parentElement.previousElementSibling) {
110
+ previousElement = cellElement.parentElement
111
+ .parentElement.previousElementSibling.lastElementChild.lastElementChild;
112
+ } else {
113
+ previousElement = null;
115
114
  }
116
- if (previousElement) {
117
- range.selectNodeContents(previousElement);
118
- if (!isSelected) {
119
- range.collapse(false);
120
- }
121
- setSelectionFocus(range);
115
+ }
116
+ if (previousElement) {
117
+ range.selectNodeContents(previousElement);
118
+ if (!isSelected) {
119
+ range.collapse(false);
122
120
  }
123
- return previousElement;
121
+ setSelectionFocus(range);
122
+ }
123
+ return previousElement;
124
124
  };
125
125
 
126
126
  export const insertAfterBlock = (vditor: IVditor, event: KeyboardEvent, range: Range, element: HTMLElement,
127
127
  blockElement: HTMLElement) => {
128
- const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
129
- if ((event.key === "ArrowDown" && element.textContent.trimRight().substr(position.start).indexOf("\n") === -1) ||
130
- (event.key === "ArrowRight" && position.start >= element.textContent.trimRight().length)) {
131
- const nextElement = blockElement.nextElementSibling;
132
- if (!nextElement ||
133
- (nextElement && (nextElement.tagName === "TABLE" || nextElement.getAttribute("data-type")))) {
134
- blockElement.insertAdjacentHTML("afterend",
135
- `<p data-block="0">${Constants.ZWSP}<wbr></p>`);
136
- setRangeByWbr(vditor[vditor.currentMode].element, range);
137
- } else {
138
- range.selectNodeContents(nextElement);
139
- range.collapse(true);
140
- setSelectionFocus(range);
141
- }
142
- event.preventDefault();
143
- return true;
128
+ const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
129
+ if ((event.key === "ArrowDown" && element.textContent.trimRight().substr(position.start).indexOf("\n") === -1) ||
130
+ (event.key === "ArrowRight" && position.start >= element.textContent.trimRight().length)) {
131
+ const nextElement = blockElement.nextElementSibling;
132
+ if (!nextElement ||
133
+ (nextElement && (nextElement.tagName === "TABLE" || nextElement.getAttribute("data-type")))) {
134
+ blockElement.insertAdjacentHTML("afterend",
135
+ `<p data-block="0">${Constants.ZWSP}<wbr></p>`);
136
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
137
+ } else {
138
+ range.selectNodeContents(nextElement);
139
+ range.collapse(true);
140
+ setSelectionFocus(range);
144
141
  }
145
- return false;
142
+ event.preventDefault();
143
+ return true;
144
+ }
145
+ return false;
146
146
  };
147
147
 
148
148
  export const insertBeforeBlock = (vditor: IVditor, event: KeyboardEvent, range: Range, element: HTMLElement,
149
149
  blockElement: HTMLElement) => {
150
- const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
151
- if ((event.key === "ArrowUp" && element.textContent.substr(0, position.start).indexOf("\n") === -1) ||
152
- ((event.key === "ArrowLeft" || (event.key === "Backspace" && range.toString() === "")) &&
153
- position.start === 0)) {
154
- const previousElement = blockElement.previousElementSibling;
155
- // table || code
156
- if (!previousElement ||
157
- (previousElement && (previousElement.tagName === "TABLE" || previousElement.getAttribute("data-type")))) {
158
- blockElement.insertAdjacentHTML("beforebegin",
159
- `<p data-block="0">${Constants.ZWSP}<wbr></p>`);
160
- setRangeByWbr(vditor[vditor.currentMode].element, range);
161
- } else {
162
- range.selectNodeContents(previousElement);
163
- range.collapse(false);
164
- setSelectionFocus(range);
165
- }
166
- event.preventDefault();
167
- return true;
150
+ const position = getSelectPosition(element, vditor[vditor.currentMode].element, range);
151
+ if ((event.key === "ArrowUp" && element.textContent.substr(0, position.start).indexOf("\n") === -1) ||
152
+ ((event.key === "ArrowLeft" || (event.key === "Backspace" && range.toString() === "")) &&
153
+ position.start === 0)) {
154
+ const previousElement = blockElement.previousElementSibling;
155
+ // table || code
156
+ if (!previousElement ||
157
+ (previousElement && (previousElement.tagName === "TABLE" || previousElement.getAttribute("data-type")))) {
158
+ blockElement.insertAdjacentHTML("beforebegin",
159
+ `<p data-block="0">${Constants.ZWSP}<wbr></p>`);
160
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
161
+ } else {
162
+ range.selectNodeContents(previousElement);
163
+ range.collapse(false);
164
+ setSelectionFocus(range);
168
165
  }
169
- return false;
166
+ event.preventDefault();
167
+ return true;
168
+ }
169
+ return false;
170
170
  };
171
171
 
172
172
  export const listToggle = (vditor: IVditor, range: Range, type: string, cancel = true) => {
173
- const itemElement = hasClosestByMatchTag(range.startContainer, "LI");
174
- vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
175
- wbr.remove();
176
- });
177
- range.insertNode(document.createElement("wbr"));
178
-
179
- if (cancel && itemElement) {
180
- // 取消
181
- let pHTML = "";
182
- for (let i = 0; i < itemElement.parentElement.childElementCount; i++) {
183
- const inputElement = itemElement.parentElement.children[i].querySelector("input");
184
- if (inputElement) {
185
- inputElement.remove();
186
- }
187
- pHTML += `<p data-block="0">${itemElement.parentElement.children[i].innerHTML.trimLeft()}</p>`;
188
- }
189
- itemElement.parentElement.insertAdjacentHTML("beforebegin", pHTML);
190
- itemElement.parentElement.remove();
173
+ const itemElement = hasClosestByMatchTag(range.startContainer, "LI");
174
+ vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
175
+ wbr.remove();
176
+ });
177
+ range.insertNode(document.createElement("wbr"));
178
+
179
+ if (cancel && itemElement) {
180
+ // 取消
181
+ let pHTML = "";
182
+ for (let i = 0; i < itemElement.parentElement.childElementCount; i++) {
183
+ const inputElement = itemElement.parentElement.children[i].querySelector("input");
184
+ if (inputElement) {
185
+ inputElement.remove();
186
+ }
187
+ pHTML += `<p data-block="0">${itemElement.parentElement.children[i].innerHTML.trimLeft()}</p>`;
188
+ }
189
+ itemElement.parentElement.insertAdjacentHTML("beforebegin", pHTML);
190
+ itemElement.parentElement.remove();
191
+ } else {
192
+ if (!itemElement) {
193
+ // 添加
194
+ let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
195
+ if (!blockElement) {
196
+ vditor[vditor.currentMode].element.querySelector("wbr").remove();
197
+ blockElement = vditor[vditor.currentMode].element.querySelector("p");
198
+ blockElement.innerHTML = "<wbr>";
199
+ }
200
+ if (type === "check") {
201
+ blockElement.insertAdjacentHTML("beforebegin",
202
+ `<ul data-block="0"><li class="vditor-task"><input type="checkbox" /> ${blockElement.innerHTML}</li></ul>`);
203
+ blockElement.remove();
204
+ } else if (type === "list") {
205
+ blockElement.insertAdjacentHTML("beforebegin",
206
+ `<ul data-block="0"><li>${blockElement.innerHTML}</li></ul>`);
207
+ blockElement.remove();
208
+ } else if (type === "ordered-list") {
209
+ blockElement.insertAdjacentHTML("beforebegin",
210
+ `<ol data-block="0"><li>${blockElement.innerHTML}</li></ol>`);
211
+ blockElement.remove();
212
+ }
191
213
  } else {
192
- if (!itemElement) {
193
- // 添加
194
- let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
195
- if (!blockElement) {
196
- vditor[vditor.currentMode].element.querySelector("wbr").remove();
197
- blockElement = vditor[vditor.currentMode].element.querySelector("p");
198
- blockElement.innerHTML = "<wbr>";
199
- }
200
- if (type === "check") {
201
- blockElement.insertAdjacentHTML("beforebegin",
202
- `<ul data-block="0"><li class="vditor-task"><input type="checkbox" /> ${blockElement.innerHTML}</li></ul>`);
203
- blockElement.remove();
204
- } else if (type === "list") {
205
- blockElement.insertAdjacentHTML("beforebegin",
206
- `<ul data-block="0"><li>${blockElement.innerHTML}</li></ul>`);
207
- blockElement.remove();
208
- } else if (type === "ordered-list") {
209
- blockElement.insertAdjacentHTML("beforebegin",
210
- `<ol data-block="0"><li>${blockElement.innerHTML}</li></ol>`);
211
- blockElement.remove();
212
- }
214
+ // 切换
215
+ if (type === "check") {
216
+ itemElement.parentElement.querySelectorAll("li").forEach((item) => {
217
+ item.insertAdjacentHTML("afterbegin",
218
+ `<input type="checkbox" />${item.textContent.indexOf(" ") === 0 ? "" : " "}`);
219
+ item.classList.add("vditor-task");
220
+ });
221
+ } else {
222
+ if (itemElement.querySelector("input")) {
223
+ itemElement.parentElement.querySelectorAll("li").forEach((item) => {
224
+ item.querySelector("input").remove();
225
+ item.classList.remove("vditor-task");
226
+ });
227
+ }
228
+ let element;
229
+ if (type === "list") {
230
+ element = document.createElement("ul");
231
+ element.setAttribute("data-marker", "*");
213
232
  } else {
214
- // 切换
215
- if (type === "check") {
216
- itemElement.parentElement.querySelectorAll("li").forEach((item) => {
217
- item.insertAdjacentHTML("afterbegin",
218
- `<input type="checkbox" />${item.textContent.indexOf(" ") === 0 ? "" : " "}`);
219
- item.classList.add("vditor-task");
220
- });
221
- } else {
222
- if (itemElement.querySelector("input")) {
223
- itemElement.parentElement.querySelectorAll("li").forEach((item) => {
224
- item.querySelector("input").remove();
225
- item.classList.remove("vditor-task");
226
- });
227
- }
228
- let element;
229
- if (type === "list") {
230
- element = document.createElement("ul");
231
- element.setAttribute("data-marker", "*");
232
- } else {
233
- element = document.createElement("ol");
234
- element.setAttribute("data-marker", "1.");
235
- }
236
- element.setAttribute("data-block", "0");
237
- element.setAttribute("data-tight", itemElement.parentElement.getAttribute("data-tight"));
238
- element.innerHTML = itemElement.parentElement.innerHTML;
239
- itemElement.parentElement.parentNode.replaceChild(element, itemElement.parentElement);
240
- }
241
- }
233
+ element = document.createElement("ol");
234
+ element.setAttribute("data-marker", "1.");
235
+ }
236
+ element.setAttribute("data-block", "0");
237
+ element.setAttribute("data-tight", itemElement.parentElement.getAttribute("data-tight"));
238
+ element.innerHTML = itemElement.parentElement.innerHTML;
239
+ itemElement.parentElement.parentNode.replaceChild(element, itemElement.parentElement);
240
+ }
242
241
  }
242
+ }
243
243
  };
244
244
 
245
245
  export const listIndent = (vditor: IVditor, liElement: HTMLElement, range: Range) => {
246
- const previousElement = liElement.previousElementSibling;
247
- if (liElement && previousElement) {
248
- const liElements: HTMLElement[] = [liElement];
249
- Array.from(range.cloneContents().children).forEach((item, index) => {
250
- if (item.nodeType !== 3 && liElement && item.textContent.trim() !== ""
251
- && liElement.getAttribute("data-node-id") === item.getAttribute("data-node-id")) {
252
- if (index !== 0) {
253
- liElements.push(liElement);
254
- }
255
- liElement = liElement.nextElementSibling as HTMLElement;
256
- }
257
- });
258
-
259
- vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
260
- wbr.remove();
261
- });
262
- range.insertNode(document.createElement("wbr"));
263
- const liParentElement = previousElement.parentElement;
264
-
265
- let liHTML = "";
266
- liElements.forEach((item: HTMLElement) => {
267
- let marker = item.getAttribute("data-marker");
268
- if (marker.length !== 1) {
269
- marker = `1${marker.slice(-1)}`;
270
- }
271
- liHTML += `<li data-node-id="${item.getAttribute("data-node-id")}" data-marker="${marker}">${item.innerHTML}</li>`;
272
- item.remove();
273
- });
274
- previousElement.insertAdjacentHTML("beforeend",
275
- `<${liParentElement.tagName} data-block="0">${liHTML}</${liParentElement.tagName}>`);
246
+ const previousElement = liElement.previousElementSibling;
247
+ if (liElement && previousElement) {
248
+ const liElements: HTMLElement[] = [liElement];
249
+ Array.from(range.cloneContents().children).forEach((item, index) => {
250
+ if (item.nodeType !== 3 && liElement && item.textContent.trim() !== ""
251
+ && liElement.getAttribute("data-node-id") === item.getAttribute("data-node-id")) {
252
+ if (index !== 0) {
253
+ liElements.push(liElement);
254
+ }
255
+ liElement = liElement.nextElementSibling as HTMLElement;
256
+ }
257
+ });
276
258
 
277
- if (vditor.currentMode === "wysiwyg") {
278
- liParentElement.outerHTML = vditor.lute.SpinVditorDOM(liParentElement.outerHTML);
279
- } else {
280
- liParentElement.outerHTML = vditor.lute.SpinVditorIRDOM(liParentElement.outerHTML);
281
- }
259
+ vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
260
+ wbr.remove();
261
+ });
262
+ range.insertNode(document.createElement("wbr"));
263
+ const liParentElement = previousElement.parentElement;
264
+
265
+ let liHTML = "";
266
+ liElements.forEach((item: HTMLElement) => {
267
+ let marker = item.getAttribute("data-marker");
268
+ if (marker.length !== 1) {
269
+ marker = `1${marker.slice(-1)}`;
270
+ }
271
+ liHTML += `<li data-node-id="${item.getAttribute("data-node-id")}" data-marker="${marker}">${item.innerHTML}</li>`;
272
+ item.remove();
273
+ });
274
+ previousElement.insertAdjacentHTML("beforeend",
275
+ `<${liParentElement.tagName} data-block="0">${liHTML}</${liParentElement.tagName}>`);
282
276
 
283
- setRangeByWbr(vditor[vditor.currentMode].element, range);
284
- const tempTopListElement = getTopList(range.startContainer);
285
- if (tempTopListElement) {
286
- tempTopListElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
287
- .forEach((item: HTMLElement) => {
288
- processCodeRender(item, vditor);
289
- if (vditor.currentMode === "wysiwyg") {
290
- item.previousElementSibling.setAttribute("style", "display:none");
291
- }
292
- });
293
- }
294
- execAfterRender(vditor);
295
- highlightToolbar(vditor);
277
+ if (vditor.currentMode === "wysiwyg") {
278
+ liParentElement.outerHTML = vditor.lute.SpinVditorDOM(liParentElement.outerHTML);
296
279
  } else {
297
- vditor[vditor.currentMode].element.focus();
280
+ liParentElement.outerHTML = vditor.lute.SpinVditorIRDOM(liParentElement.outerHTML);
298
281
  }
299
- };
300
282
 
301
- export const listOutdent = (vditor: IVditor, liElement: HTMLElement, range: Range, topListElement: HTMLElement) => {
302
- const liParentLiElement = hasClosestByMatchTag(liElement.parentElement, "LI");
303
- if (liParentLiElement) {
304
- vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
305
- wbr.remove();
306
- });
307
- range.insertNode(document.createElement("wbr"));
308
-
309
- const liParentElement = liElement.parentElement;
310
- const liParentAfterElement = liParentElement.cloneNode() as HTMLElement;
311
- const liElements: HTMLElement[] = [liElement];
312
- Array.from(range.cloneContents().children).forEach((item, index) => {
313
- if (item.nodeType !== 3 && liElement && item.textContent.trim() !== "" &&
314
- liElement.getAttribute("data-node-id") === item.getAttribute("data-node-id")) {
315
- if (index !== 0) {
316
- liElements.push(liElement);
317
- }
318
- liElement = liElement.nextElementSibling as HTMLElement;
319
- }
320
- });
321
- let isMatch = false;
322
- let afterHTML = "";
323
- liParentElement.querySelectorAll("li").forEach((item) => {
324
- if (isMatch) {
325
- afterHTML += item.outerHTML;
326
- if (!item.nextElementSibling && !item.previousElementSibling) {
327
- item.parentElement.remove();
328
- } else {
329
- item.remove();
330
- }
331
- }
332
- if (item.isSameNode(liElements[liElements.length - 1])) {
333
- isMatch = true;
334
- }
335
- });
336
-
337
- liElements.reverse().forEach((item) => {
338
- liParentLiElement.insertAdjacentElement("afterend", item);
283
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
284
+ const tempTopListElement = getTopList(range.startContainer);
285
+ if (tempTopListElement) {
286
+ tempTopListElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
287
+ .forEach((item: HTMLElement) => {
288
+ processCodeRender(item, vditor);
289
+ if (vditor.currentMode === "wysiwyg") {
290
+ item.previousElementSibling.setAttribute("style", "display:none");
291
+ }
339
292
  });
293
+ }
294
+ execAfterRender(vditor);
295
+ highlightToolbar(vditor);
296
+ } else {
297
+ vditor[vditor.currentMode].element.focus();
298
+ }
299
+ };
340
300
 
341
- if (afterHTML) {
342
- liParentAfterElement.innerHTML = afterHTML;
343
- liElements[0].insertAdjacentElement("beforeend", liParentAfterElement);
344
- }
301
+ export const listOutdent = (vditor: IVditor, liElement: HTMLElement, range: Range, topListElement: HTMLElement) => {
302
+ const liParentLiElement = hasClosestByMatchTag(liElement.parentElement, "LI");
303
+ if (liParentLiElement) {
304
+ vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
305
+ wbr.remove();
306
+ });
307
+ range.insertNode(document.createElement("wbr"));
345
308
 
346
- if (vditor.currentMode === "wysiwyg") {
347
- topListElement.outerHTML = vditor.lute.SpinVditorDOM(topListElement.outerHTML);
309
+ const liParentElement = liElement.parentElement;
310
+ const liParentAfterElement = liParentElement.cloneNode() as HTMLElement;
311
+ const liElements: HTMLElement[] = [liElement];
312
+ Array.from(range.cloneContents().children).forEach((item, index) => {
313
+ if (item.nodeType !== 3 && liElement && item.textContent.trim() !== "" &&
314
+ liElement.getAttribute("data-node-id") === item.getAttribute("data-node-id")) {
315
+ if (index !== 0) {
316
+ liElements.push(liElement);
317
+ }
318
+ liElement = liElement.nextElementSibling as HTMLElement;
319
+ }
320
+ });
321
+ let isMatch = false;
322
+ let afterHTML = "";
323
+ liParentElement.querySelectorAll("li").forEach((item) => {
324
+ if (isMatch) {
325
+ afterHTML += item.outerHTML;
326
+ if (!item.nextElementSibling && !item.previousElementSibling) {
327
+ item.parentElement.remove();
348
328
  } else {
349
- topListElement.outerHTML = vditor.lute.SpinVditorIRDOM(topListElement.outerHTML);
329
+ item.remove();
350
330
  }
331
+ }
332
+ if (item.isSameNode(liElements[liElements.length - 1])) {
333
+ isMatch = true;
334
+ }
335
+ });
351
336
 
352
- setRangeByWbr(vditor[vditor.currentMode].element, range);
353
- const tempTopListElement = getTopList(range.startContainer);
354
- if (tempTopListElement) {
355
- tempTopListElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
356
- .forEach((item: HTMLElement) => {
357
- processCodeRender(item, vditor);
358
- if (vditor.currentMode === "wysiwyg") {
359
- item.previousElementSibling.setAttribute("style", "display:none");
360
- }
361
- });
362
- }
363
- execAfterRender(vditor);
364
- highlightToolbar(vditor);
337
+ liElements.reverse().forEach((item) => {
338
+ liParentLiElement.insertAdjacentElement("afterend", item);
339
+ });
340
+
341
+ if (afterHTML) {
342
+ liParentAfterElement.innerHTML = afterHTML;
343
+ liElements[0].insertAdjacentElement("beforeend", liParentAfterElement);
344
+ }
345
+
346
+ if (vditor.currentMode === "wysiwyg") {
347
+ topListElement.outerHTML = vditor.lute.SpinVditorDOM(topListElement.outerHTML);
365
348
  } else {
366
- vditor[vditor.currentMode].element.focus();
349
+ topListElement.outerHTML = vditor.lute.SpinVditorIRDOM(topListElement.outerHTML);
350
+ }
351
+
352
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
353
+ const tempTopListElement = getTopList(range.startContainer);
354
+ if (tempTopListElement) {
355
+ tempTopListElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
356
+ .forEach((item: HTMLElement) => {
357
+ processCodeRender(item, vditor);
358
+ if (vditor.currentMode === "wysiwyg") {
359
+ item.previousElementSibling.setAttribute("style", "display:none");
360
+ }
361
+ });
367
362
  }
363
+ execAfterRender(vditor);
364
+ highlightToolbar(vditor);
365
+ } else {
366
+ vditor[vditor.currentMode].element.focus();
367
+ }
368
368
  };
369
369
 
370
370
  export const setTableAlign = (tableElement: HTMLTableElement, type: string) => {
371
- const cell = getSelection().getRangeAt(0).startContainer.parentElement;
372
-
373
- const columnCnt = tableElement.rows[0].cells.length;
374
- const rowCnt = tableElement.rows.length;
375
- let currentColumn = 0;
376
-
377
- for (let i = 0; i < rowCnt; i++) {
378
- for (let j = 0; j < columnCnt; j++) {
379
- if (tableElement.rows[i].cells[j].isSameNode(cell)) {
380
- currentColumn = j;
381
- break;
382
- }
383
- }
384
- }
385
- for (let k = 0; k < rowCnt; k++) {
386
- tableElement.rows[k].cells[currentColumn].setAttribute("align", type);
371
+ const cell = getSelection().getRangeAt(0).startContainer.parentElement;
372
+
373
+ const columnCnt = tableElement.rows[0].cells.length;
374
+ const rowCnt = tableElement.rows.length;
375
+ let currentColumn = 0;
376
+
377
+ for (let i = 0; i < rowCnt; i++) {
378
+ for (let j = 0; j < columnCnt; j++) {
379
+ if (tableElement.rows[i].cells[j].isSameNode(cell)) {
380
+ currentColumn = j;
381
+ break;
382
+ }
387
383
  }
384
+ }
385
+ for (let k = 0; k < rowCnt; k++) {
386
+ tableElement.rows[k].cells[currentColumn].setAttribute("align", type);
387
+ }
388
388
  };
389
389
 
390
390
  export const isHrMD = (text: string) => {
391
- // - _ *
392
- const marker = text.trimRight().split("\n").pop();
393
- if (marker === "") {
391
+ // - _ *
392
+ const marker = text.trimRight().split("\n").pop();
393
+ if (marker === "") {
394
+ return false;
395
+ }
396
+ if (marker.replace(/ |-/g, "") === ""
397
+ || marker.replace(/ |_/g, "") === ""
398
+ || marker.replace(/ |\*/g, "") === "") {
399
+ if (marker.replace(/ /g, "").length > 2) {
400
+ if (marker.indexOf("-") > -1 && marker.trimLeft().indexOf(" ") === -1
401
+ && text.trimRight().split("\n").length > 1) {
402
+ // 满足 heading
394
403
  return false;
395
- }
396
- if (marker.replace(/ |-/g, "") === ""
397
- || marker.replace(/ |_/g, "") === ""
398
- || marker.replace(/ |\*/g, "") === "") {
399
- if (marker.replace(/ /g, "").length > 2) {
400
- if (marker.indexOf("-") > -1 && marker.trimLeft().indexOf(" ") === -1
401
- && text.trimRight().split("\n").length > 1) {
402
- // 满足 heading
403
- return false;
404
- }
405
- if (marker.indexOf(" ") === 0 || marker.indexOf("\t") === 0) {
406
- // 代码块
407
- return false;
408
- }
409
- return true;
410
- }
404
+ }
405
+ if (marker.indexOf(" ") === 0 || marker.indexOf("\t") === 0) {
406
+ // 代码块
411
407
  return false;
408
+ }
409
+ return true;
412
410
  }
413
411
  return false;
412
+ }
413
+ return false;
414
414
  };
415
415
 
416
416
  export const isHeadingMD = (text: string) => {
417
- // - =
418
- const textArray = text.trimRight().split("\n");
419
- text = textArray.pop();
417
+ // - =
418
+ const textArray = text.trimRight().split("\n");
419
+ text = textArray.pop();
420
420
 
421
- if (text.indexOf(" ") === 0 || text.indexOf("\t") === 0) {
422
- return false;
423
- }
421
+ if (text.indexOf(" ") === 0 || text.indexOf("\t") === 0) {
422
+ return false;
423
+ }
424
424
 
425
- text = text.trimLeft();
426
- if (text === "" || textArray.length === 0) {
427
- return false;
428
- }
429
- if (text.replace(/-/g, "") === ""
430
- || text.replace(/=/g, "") === "") {
431
- return true;
432
- }
425
+ text = text.trimLeft();
426
+ if (text === "" || textArray.length === 0) {
433
427
  return false;
428
+ }
429
+ if (text.replace(/-/g, "") === ""
430
+ || text.replace(/=/g, "") === "") {
431
+ return true;
432
+ }
433
+ return false;
434
434
  };
435
435
 
436
436
  export const execAfterRender = (vditor: IVditor, options = {
437
- enableAddUndoStack: true,
438
- enableHint: false,
439
- enableInput: true,
437
+ enableAddUndoStack: true,
438
+ enableHint: false,
439
+ enableInput: true,
440
440
  }) => {
441
- if (vditor.currentMode === "wysiwyg") {
442
- afterRenderEvent(vditor, options);
443
- } else if (vditor.currentMode === "ir") {
444
- processAfterRender(vditor, options);
445
- } else if (vditor.currentMode === "sv") {
446
- processSVAfterRender(vditor, options);
447
- }
441
+ if (vditor.currentMode === "wysiwyg") {
442
+ afterRenderEvent(vditor, options);
443
+ } else if (vditor.currentMode === "ir") {
444
+ processAfterRender(vditor, options);
445
+ } else if (vditor.currentMode === "sv") {
446
+ processSVAfterRender(vditor, options);
447
+ }
448
448
  };
449
449
 
450
450
  export const fixList = (range: Range, vditor: IVditor, pElement: HTMLElement | false, event: KeyboardEvent) => {
451
- const startContainer = range.startContainer;
452
- const liElement = hasClosestByMatchTag(startContainer, "LI");
453
- if (liElement) {
454
- if (!isCtrl(event) && !event.altKey && event.key === "Enter" &&
455
- // fix li 中有多个 P 时,在第一个 P 中换行会在下方生成新的 li
456
- (!event.shiftKey && pElement && liElement.contains(pElement) && pElement.nextElementSibling)) {
457
- if (liElement && !liElement.textContent.endsWith("\n")) {
458
- // li 结尾需 \n
459
- liElement.insertAdjacentText("beforeend", "\n");
460
- }
461
- range.insertNode(document.createTextNode("\n\n"));
462
- range.collapse(false);
463
- execAfterRender(vditor);
464
- event.preventDefault();
465
- return true;
466
- }
467
-
468
- if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
469
- !liElement.previousElementSibling && range.toString() === "" &&
470
- getSelectPosition(liElement, vditor[vditor.currentMode].element, range).start === 0) {
471
- // 光标位于点和第一个字符中间时,无法删除 li 元素
472
- if (liElement.nextElementSibling) {
473
- liElement.parentElement.insertAdjacentHTML("beforebegin",
474
- `<p data-block="0"><wbr>${liElement.innerHTML}</p>`);
475
- liElement.remove();
476
- } else {
477
- liElement.parentElement.outerHTML = `<p data-block="0"><wbr>${liElement.innerHTML}</p>`;
478
- }
479
- setRangeByWbr(vditor[vditor.currentMode].element, range);
480
- execAfterRender(vditor);
481
- event.preventDefault();
482
- return true;
483
- }
484
-
485
- // 空列表删除后与上一级段落对齐
486
- if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
487
- liElement.textContent.trim().replace(Constants.ZWSP, "") === "" &&
488
- range.toString() === "" && liElement.previousElementSibling?.tagName === "LI") {
489
- liElement.previousElementSibling.insertAdjacentText("beforeend", "\n\n");
490
- range.selectNodeContents(liElement.previousElementSibling);
491
- range.collapse(false);
492
- liElement.remove();
493
- setRangeByWbr(vditor[vditor.currentMode].element, range);
494
- execAfterRender(vditor);
495
- event.preventDefault();
496
- return true;
497
- }
451
+ const startContainer = range.startContainer;
452
+ const liElement = hasClosestByMatchTag(startContainer, "LI");
453
+ if (liElement) {
454
+ if (!isCtrl(event) && !event.altKey && event.key === "Enter" &&
455
+ // fix li 中有多个 P 时,在第一个 P 中换行会在下方生成新的 li
456
+ (!event.shiftKey && pElement && liElement.contains(pElement) && pElement.nextElementSibling)) {
457
+ if (liElement && !liElement.textContent.endsWith("\n")) {
458
+ // li 结尾需 \n
459
+ liElement.insertAdjacentText("beforeend", "\n");
460
+ }
461
+ range.insertNode(document.createTextNode("\n\n"));
462
+ range.collapse(false);
463
+ execAfterRender(vditor);
464
+ event.preventDefault();
465
+ return true;
466
+ }
498
467
 
499
- if (!isCtrl(event) && !event.altKey && event.key === "Tab") {
500
- // 光标位于第一/零字符时,tab 用于列表的缩进
501
- let isFirst = false;
502
- if (range.startOffset === 0
503
- && ((startContainer.nodeType === 3 && !startContainer.previousSibling)
504
- || (startContainer.nodeType !== 3 && startContainer.nodeName === "LI"))) {
505
- // 有序/无序列表
506
- isFirst = true;
507
- } else if (liElement.classList.contains("vditor-task") && range.startOffset === 1
508
- && startContainer.previousSibling.nodeType !== 3
509
- && (startContainer.previousSibling as HTMLElement).tagName === "INPUT") {
510
- // 任务列表
511
- isFirst = true;
512
- }
468
+ if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
469
+ !liElement.previousElementSibling && range.toString() === "" &&
470
+ getSelectPosition(liElement, vditor[vditor.currentMode].element, range).start === 0) {
471
+ // 光标位于点和第一个字符中间时,无法删除 li 元素
472
+ if (liElement.nextElementSibling) {
473
+ liElement.parentElement.insertAdjacentHTML("beforebegin",
474
+ `<p data-block="0"><wbr>${liElement.innerHTML}</p>`);
475
+ liElement.remove();
476
+ } else {
477
+ liElement.parentElement.outerHTML = `<p data-block="0"><wbr>${liElement.innerHTML}</p>`;
478
+ }
479
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
480
+ execAfterRender(vditor);
481
+ event.preventDefault();
482
+ return true;
483
+ }
513
484
 
514
- if (isFirst || range.toString() !== "") {
515
- if (event.shiftKey) {
516
- listOutdent(vditor, liElement, range, liElement.parentElement);
517
- } else {
518
- listIndent(vditor, liElement, range);
519
- }
520
- event.preventDefault();
521
- return true;
522
- }
523
- }
485
+ // 空列表删除后与上一级段落对齐
486
+ if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace" &&
487
+ liElement.textContent.trim().replace(Constants.ZWSP, "") === "" &&
488
+ range.toString() === "" && liElement.previousElementSibling?.tagName === "LI") {
489
+ liElement.previousElementSibling.insertAdjacentText("beforeend", "\n\n");
490
+ range.selectNodeContents(liElement.previousElementSibling);
491
+ range.collapse(false);
492
+ liElement.remove();
493
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
494
+ execAfterRender(vditor);
495
+ event.preventDefault();
496
+ return true;
524
497
  }
525
- return false;
526
- };
527
498
 
528
- // tab 处理: block code render, table, 列表第一个字符中的 tab 处理单独写在上面
529
- export const fixTab = (vditor: IVditor, range: Range, event: KeyboardEvent) => {
530
- if (vditor.options.tab && event.key === "Tab") {
499
+ if (!isCtrl(event) && !event.altKey && event.key === "Tab") {
500
+ // 光标位于第一/零字符时,tab 用于列表的缩进
501
+ let isFirst = false;
502
+ if (range.startOffset === 0
503
+ && ((startContainer.nodeType === 3 && !startContainer.previousSibling)
504
+ || (startContainer.nodeType !== 3 && startContainer.nodeName === "LI"))) {
505
+ // 有序/无序列表
506
+ isFirst = true;
507
+ } else if (liElement.classList.contains("vditor-task") && range.startOffset === 1
508
+ && startContainer.previousSibling.nodeType !== 3
509
+ && (startContainer.previousSibling as HTMLElement).tagName === "INPUT") {
510
+ // 任务列表
511
+ isFirst = true;
512
+ }
513
+
514
+ if (isFirst || range.toString() !== "") {
531
515
  if (event.shiftKey) {
532
- // TODO shift+tab
516
+ listOutdent(vditor, liElement, range, liElement.parentElement);
533
517
  } else {
534
- if (range.toString() === "") {
535
- range.insertNode(document.createTextNode(vditor.options.tab));
536
- range.collapse(false);
537
- } else {
538
- range.extractContents();
539
- range.insertNode(document.createTextNode(vditor.options.tab));
540
- range.collapse(false);
541
- }
518
+ listIndent(vditor, liElement, range);
542
519
  }
543
- setSelectionFocus(range);
544
- execAfterRender(vditor);
545
520
  event.preventDefault();
546
521
  return true;
522
+ }
547
523
  }
524
+ }
525
+ return false;
548
526
  };
549
527
 
550
- export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTMLElement | false, range: Range) => {
551
- if (!pElement) {
552
- return;
528
+ // tab 处理: block code render, table, 列表第一个字符中的 tab 处理单独写在上面
529
+ export const fixTab = (vditor: IVditor, range: Range, event: KeyboardEvent) => {
530
+ if (vditor.options.tab && event.key === "Tab") {
531
+ if (event.shiftKey) {
532
+ // TODO shift+tab
533
+ } else {
534
+ if (range.toString() === "") {
535
+ range.insertNode(document.createTextNode(vditor.options.tab));
536
+ range.collapse(false);
537
+ } else {
538
+ range.extractContents();
539
+ range.insertNode(document.createTextNode(vditor.options.tab));
540
+ range.collapse(false);
541
+ }
553
542
  }
554
- if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
555
- const pText = String.raw`${pElement.textContent}`.replace(/\\\|/g, "").trim();
556
- const pTextList = pText.split("|");
557
- if (pText.startsWith("|") && pText.endsWith("|") && pTextList.length > 3) {
558
- // table 自动完成
559
- let tableHeaderMD = pTextList.map(() => "---").join("|");
560
- tableHeaderMD =
561
- pElement.textContent + "\n" + tableHeaderMD.substring(3, tableHeaderMD.length - 3) + "\n|<wbr>";
562
- pElement.outerHTML = vditor.lute.SpinVditorDOM(tableHeaderMD);
563
- setRangeByWbr(vditor[vditor.currentMode].element, range);
564
- execAfterRender(vditor);
565
- scrollCenter(vditor);
566
- event.preventDefault();
567
- return true;
568
- }
569
-
570
- // hr 渲染
571
- if (isHrMD(pElement.innerHTML) && pElement.previousElementSibling) {
572
- // 软换行后 hr 前有内容
573
- let pInnerHTML = "";
574
- const innerHTMLList = pElement.innerHTML.trimRight().split("\n");
575
- if (innerHTMLList.length > 1) {
576
- innerHTMLList.pop();
577
- pInnerHTML = `<p data-block="0">${innerHTMLList.join("\n")}</p>`;
578
- }
543
+ setSelectionFocus(range);
544
+ execAfterRender(vditor);
545
+ event.preventDefault();
546
+ return true;
547
+ }
548
+ };
579
549
 
580
- pElement.insertAdjacentHTML("afterend",
581
- `${pInnerHTML}<hr data-block="0"><p data-block="0"><wbr>\n</p>`);
582
- pElement.remove();
583
- setRangeByWbr(vditor[vditor.currentMode].element, range);
584
- execAfterRender(vditor);
585
- scrollCenter(vditor);
586
- event.preventDefault();
587
- return true;
588
- }
550
+ export const fixMarkdown = (event: KeyboardEvent, vditor: IVditor, pElement: HTMLElement | false, range: Range) => {
551
+ if (!pElement) {
552
+ return;
553
+ }
554
+ if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
555
+ const pText = String.raw`${pElement.textContent}`.replace(/\\\|/g, "").trim();
556
+ const pTextList = pText.split("|");
557
+ if (pText.startsWith("|") && pText.endsWith("|") && pTextList.length > 3) {
558
+ // table 自动完成
559
+ let tableHeaderMD = pTextList.map(() => "---").join("|");
560
+ tableHeaderMD =
561
+ pElement.textContent + "\n" + tableHeaderMD.substring(3, tableHeaderMD.length - 3) + "\n|<wbr>";
562
+ pElement.outerHTML = vditor.lute.SpinVditorDOM(tableHeaderMD);
563
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
564
+ execAfterRender(vditor);
565
+ scrollCenter(vditor);
566
+ event.preventDefault();
567
+ return true;
568
+ }
589
569
 
590
- if (isHeadingMD(pElement.innerHTML)) {
591
- // heading 渲染
592
- if (vditor.currentMode === "wysiwyg") {
593
- pElement.outerHTML = vditor.lute.SpinVditorDOM(pElement.innerHTML + '<p data-block="0"><wbr>\n</p>');
594
- } else {
595
- pElement.outerHTML = vditor.lute.SpinVditorIRDOM(pElement.innerHTML + '<p data-block="0"><wbr>\n</p>');
596
- }
597
- setRangeByWbr(vditor[vditor.currentMode].element, range);
598
- execAfterRender(vditor);
599
- scrollCenter(vditor);
600
- event.preventDefault();
601
- return true;
602
- }
570
+ // hr 渲染
571
+ if (isHrMD(pElement.innerHTML) && pElement.previousElementSibling) {
572
+ // 软换行后 hr 前有内容
573
+ let pInnerHTML = "";
574
+ const innerHTMLList = pElement.innerHTML.trimRight().split("\n");
575
+ if (innerHTMLList.length > 1) {
576
+ innerHTMLList.pop();
577
+ pInnerHTML = `<p data-block="0">${innerHTMLList.join("\n")}</p>`;
578
+ }
579
+
580
+ pElement.insertAdjacentHTML("afterend",
581
+ `${pInnerHTML}<hr data-block="0"><p data-block="0"><wbr>\n</p>`);
582
+ pElement.remove();
583
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
584
+ execAfterRender(vditor);
585
+ scrollCenter(vditor);
586
+ event.preventDefault();
587
+ return true;
603
588
  }
604
589
 
605
- // 软换行会被切割 https://github.com/Vanessa219/vditor/issues/220
606
- if (range.collapsed && pElement.previousElementSibling && event.key === "Backspace" &&
607
- !isCtrl(event) && !event.altKey && !event.shiftKey &&
608
- pElement.textContent.trimRight().split("\n").length > 1 &&
609
- getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0) {
610
- const lastElement = getLastNode(pElement.previousElementSibling) as HTMLElement;
611
- if (!lastElement.textContent.endsWith("\n")) {
612
- lastElement.textContent = lastElement.textContent + "\n";
613
- }
614
- lastElement.parentElement.insertAdjacentHTML("beforeend", `<wbr>${pElement.innerHTML}`);
615
- pElement.remove();
616
- setRangeByWbr(vditor[vditor.currentMode].element, range);
617
- return false;
590
+ if (isHeadingMD(pElement.innerHTML)) {
591
+ // heading 渲染
592
+ if (vditor.currentMode === "wysiwyg") {
593
+ pElement.outerHTML = vditor.lute.SpinVditorDOM(pElement.innerHTML + '<p data-block="0"><wbr>\n</p>');
594
+ } else {
595
+ pElement.outerHTML = vditor.lute.SpinVditorIRDOM(pElement.innerHTML + '<p data-block="0"><wbr>\n</p>');
596
+ }
597
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
598
+ execAfterRender(vditor);
599
+ scrollCenter(vditor);
600
+ event.preventDefault();
601
+ return true;
602
+ }
603
+ }
604
+
605
+ // 软换行会被切割 https://github.com/Vanessa219/vditor/issues/220
606
+ if (range.collapsed && pElement.previousElementSibling && event.key === "Backspace" &&
607
+ !isCtrl(event) && !event.altKey && !event.shiftKey &&
608
+ pElement.textContent.trimRight().split("\n").length > 1 &&
609
+ getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0) {
610
+ const lastElement = getLastNode(pElement.previousElementSibling) as HTMLElement;
611
+ if (!lastElement.textContent.endsWith("\n")) {
612
+ lastElement.textContent = lastElement.textContent + "\n";
618
613
  }
614
+ lastElement.parentElement.insertAdjacentHTML("beforeend", `<wbr>${pElement.innerHTML}`);
615
+ pElement.remove();
616
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
619
617
  return false;
618
+ }
619
+ return false;
620
620
  };
621
621
 
622
622
  export const insertRow = (vditor: IVditor, range: Range, cellElement: HTMLElement) => {
623
- let rowHTML = "";
624
- for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
625
- rowHTML += `<td align="${cellElement.parentElement.children[m].getAttribute("align")}"> </td>`;
626
- }
627
- if (cellElement.tagName === "TH") {
628
- cellElement.parentElement.parentElement.insertAdjacentHTML("afterend",
629
- `<tbody><tr>${rowHTML}</tr></tbody>`);
630
- } else {
631
- cellElement.parentElement.insertAdjacentHTML("afterend", `<tr>${rowHTML}</tr>`);
632
- }
633
- execAfterRender(vditor);
623
+ let rowHTML = "";
624
+ for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
625
+ rowHTML += `<td align="${cellElement.parentElement.children[m].getAttribute("align")}"> </td>`;
626
+ }
627
+ if (cellElement.tagName === "TH") {
628
+ cellElement.parentElement.parentElement.insertAdjacentHTML("afterend",
629
+ `<tbody><tr>${rowHTML}</tr></tbody>`);
630
+ } else {
631
+ cellElement.parentElement.insertAdjacentHTML("afterend", `<tr>${rowHTML}</tr>`);
632
+ }
633
+ execAfterRender(vditor);
634
634
  };
635
635
 
636
636
  export const insertRowAbove = (vditor: IVditor, range: Range, cellElement: HTMLElement) => {
637
- let rowHTML = "";
638
- for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
639
- if (cellElement.tagName === "TH") {
640
- rowHTML += `<th align="${cellElement.parentElement.children[m].getAttribute("align")}"> </th>`;
641
- } else {
642
- rowHTML += `<td align="${cellElement.parentElement.children[m].getAttribute("align")}"> </td>`;
643
- }
644
- }
637
+ let rowHTML = "";
638
+ for (let m = 0; m < cellElement.parentElement.childElementCount; m++) {
645
639
  if (cellElement.tagName === "TH") {
646
- cellElement.parentElement.parentElement.insertAdjacentHTML("beforebegin", `<thead><tr>${rowHTML}</tr></thead>`);
647
-
648
- range.insertNode(document.createElement("wbr"));
649
- const theadHTML = cellElement.parentElement.innerHTML.replace(/<th>/g, "<td>").replace(/<\/th>/g, "</td>");
650
- cellElement.parentElement.parentElement.nextElementSibling.insertAdjacentHTML("afterbegin", theadHTML);
651
-
652
- cellElement.parentElement.parentElement.remove();
653
- setRangeByWbr(vditor.ir.element, range);
640
+ rowHTML += `<th align="${cellElement.parentElement.children[m].getAttribute("align")}"> </th>`;
654
641
  } else {
655
- cellElement.parentElement.insertAdjacentHTML("beforebegin", `<tr>${rowHTML}</tr>`);
642
+ rowHTML += `<td align="${cellElement.parentElement.children[m].getAttribute("align")}"> </td>`;
656
643
  }
657
- execAfterRender(vditor);
644
+ }
645
+ if (cellElement.tagName === "TH") {
646
+ cellElement.parentElement.parentElement.insertAdjacentHTML("beforebegin", `<thead><tr>${rowHTML}</tr></thead>`);
647
+
648
+ range.insertNode(document.createElement("wbr"));
649
+ const theadHTML = cellElement.parentElement.innerHTML.replace(/<th>/g, "<td>").replace(/<\/th>/g, "</td>");
650
+ cellElement.parentElement.parentElement.nextElementSibling.insertAdjacentHTML("afterbegin", theadHTML);
651
+
652
+ cellElement.parentElement.parentElement.remove();
653
+ setRangeByWbr(vditor.ir.element, range);
654
+ } else {
655
+ cellElement.parentElement.insertAdjacentHTML("beforebegin", `<tr>${rowHTML}</tr>`);
656
+ }
657
+ execAfterRender(vditor);
658
658
  };
659
659
 
660
660
  export const insertColumn =
661
- (vditor: IVditor, tableElement: HTMLTableElement, cellElement: HTMLElement, type: InsertPosition = "afterend") => {
662
- let index = 0;
663
- let previousElement = cellElement.previousElementSibling;
664
- while (previousElement) {
665
- index++;
666
- previousElement = previousElement.previousElementSibling;
667
- }
668
- for (let i = 0; i < tableElement.rows.length; i++) {
669
- if (i === 0) {
670
- tableElement.rows[i].cells[index].insertAdjacentHTML(type, "<th> </th>");
671
- } else {
672
- tableElement.rows[i].cells[index].insertAdjacentHTML(type, "<td> </td>");
673
- }
674
- }
675
- execAfterRender(vditor);
676
- };
661
+ (vditor: IVditor, tableElement: HTMLTableElement, cellElement: HTMLElement, type: InsertPosition = "afterend") => {
662
+ let index = 0;
663
+ let previousElement = cellElement.previousElementSibling;
664
+ while (previousElement) {
665
+ index++;
666
+ previousElement = previousElement.previousElementSibling;
667
+ }
668
+ for (let i = 0; i < tableElement.rows.length; i++) {
669
+ if (i === 0) {
670
+ tableElement.rows[i].cells[index].insertAdjacentHTML(type, "<th> </th>");
671
+ } else {
672
+ tableElement.rows[i].cells[index].insertAdjacentHTML(type, "<td> </td>");
673
+ }
674
+ }
675
+ execAfterRender(vditor);
676
+ };
677
677
  export const deleteRow = (vditor: IVditor, range: Range, cellElement: HTMLElement) => {
678
- if (cellElement.tagName === "TD") {
679
- const tbodyElement = cellElement.parentElement.parentElement;
680
- if (cellElement.parentElement.previousElementSibling) {
681
- range.selectNodeContents(cellElement.parentElement.previousElementSibling.lastElementChild);
682
- } else {
683
- range.selectNodeContents(tbodyElement.previousElementSibling.lastElementChild.lastElementChild);
684
- }
685
-
686
- if (tbodyElement.childElementCount === 1) {
687
- tbodyElement.remove();
688
- } else {
689
- cellElement.parentElement.remove();
690
- }
678
+ if (cellElement.tagName === "TD") {
679
+ const tbodyElement = cellElement.parentElement.parentElement;
680
+ if (cellElement.parentElement.previousElementSibling) {
681
+ range.selectNodeContents(cellElement.parentElement.previousElementSibling.lastElementChild);
682
+ } else {
683
+ range.selectNodeContents(tbodyElement.previousElementSibling.lastElementChild.lastElementChild);
684
+ }
691
685
 
692
- range.collapse(false);
693
- setSelectionFocus(range);
694
- execAfterRender(vditor);
686
+ if (tbodyElement.childElementCount === 1) {
687
+ tbodyElement.remove();
688
+ } else {
689
+ cellElement.parentElement.remove();
695
690
  }
691
+
692
+ range.collapse(false);
693
+ setSelectionFocus(range);
694
+ execAfterRender(vditor);
695
+ }
696
696
  };
697
697
 
698
698
  export const deleteColumn =
699
- (vditor: IVditor, range: Range, tableElement: HTMLTableElement, cellElement: HTMLElement) => {
700
- let index = 0;
701
- let previousElement = cellElement.previousElementSibling;
702
- while (previousElement) {
703
- index++;
704
- previousElement = previousElement.previousElementSibling;
705
- }
706
- if (cellElement.previousElementSibling || cellElement.nextElementSibling) {
707
- range.selectNodeContents(cellElement.previousElementSibling || cellElement.nextElementSibling);
708
- range.collapse(true);
709
- }
710
- for (let i = 0; i < tableElement.rows.length; i++) {
711
- const cells = tableElement.rows[i].cells;
712
- if (cells.length === 1) {
713
- tableElement.remove();
714
- highlightToolbar(vditor);
715
- break;
716
- }
717
- cells[index].remove();
718
- }
719
- setSelectionFocus(range);
720
- execAfterRender(vditor);
721
- };
699
+ (vditor: IVditor, range: Range, tableElement: HTMLTableElement, cellElement: HTMLElement) => {
700
+ let index = 0;
701
+ let previousElement = cellElement.previousElementSibling;
702
+ while (previousElement) {
703
+ index++;
704
+ previousElement = previousElement.previousElementSibling;
705
+ }
706
+ if (cellElement.previousElementSibling || cellElement.nextElementSibling) {
707
+ range.selectNodeContents(cellElement.previousElementSibling || cellElement.nextElementSibling);
708
+ range.collapse(true);
709
+ }
710
+ for (let i = 0; i < tableElement.rows.length; i++) {
711
+ const cells = tableElement.rows[i].cells;
712
+ if (cells.length === 1) {
713
+ tableElement.remove();
714
+ highlightToolbar(vditor);
715
+ break;
716
+ }
717
+ cells[index].remove();
718
+ }
719
+ setSelectionFocus(range);
720
+ execAfterRender(vditor);
721
+ };
722
722
 
723
723
  export const fixTable = (vditor: IVditor, event: KeyboardEvent, range: Range) => {
724
- const startContainer = range.startContainer;
725
- const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
726
- hasClosestByMatchTag(startContainer, "TH");
727
- if (cellElement) {
728
- // 换行或软换行:在 cell 中添加 br
729
- if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
730
- if (!cellElement.lastElementChild ||
731
- (cellElement.lastElementChild && (!cellElement.lastElementChild.isSameNode(cellElement.lastChild) ||
732
- cellElement.lastElementChild.tagName !== "BR"))) {
733
- cellElement.insertAdjacentHTML("beforeend", "<br>");
734
- }
735
- const brElement = document.createElement("br");
736
- range.insertNode(brElement);
737
- range.setStartAfter(brElement);
738
- execAfterRender(vditor);
739
- scrollCenter(vditor);
740
- event.preventDefault();
741
- return true;
742
- }
743
-
744
- // tab:光标移向下一个 cell
745
- if (event.key === "Tab") {
746
- if (event.shiftKey) {
747
- // shift + tab 光标移动到前一个 cell
748
- goPreviousCell(cellElement, range);
749
- event.preventDefault();
750
- return true;
751
- }
752
-
753
- let nextElement = cellElement.nextElementSibling;
754
- if (!nextElement) {
755
- if (cellElement.parentElement.nextElementSibling) {
756
- nextElement = cellElement.parentElement.nextElementSibling.firstElementChild;
757
- } else if (cellElement.parentElement.parentElement.tagName === "THEAD" &&
758
- cellElement.parentElement.parentElement.nextElementSibling) {
759
- nextElement =
760
- cellElement.parentElement.parentElement.nextElementSibling.firstElementChild.firstElementChild;
761
- } else {
762
- nextElement = null;
763
- }
764
- }
765
- if (nextElement) {
766
- range.selectNodeContents(nextElement);
767
- setSelectionFocus(range);
768
- }
769
- event.preventDefault();
770
- return true;
771
- }
772
-
773
- const tableElement = cellElement.parentElement.parentElement.parentElement as HTMLTableElement;
774
- if (event.key === "ArrowUp") {
775
- event.preventDefault();
776
- if (cellElement.tagName === "TH") {
777
- if (tableElement.previousElementSibling) {
778
- range.selectNodeContents(tableElement.previousElementSibling);
779
- range.collapse(false);
780
- setSelectionFocus(range);
781
- } else {
782
- insertEmptyBlock(vditor, "beforebegin");
783
- }
784
- return true;
785
- }
786
-
787
- let m = 0;
788
- const trElement = cellElement.parentElement as HTMLTableRowElement;
789
- for (; m < trElement.cells.length; m++) {
790
- if (trElement.cells[m].isSameNode(cellElement)) {
791
- break;
792
- }
793
- }
794
-
795
- let previousElement = trElement.previousElementSibling as HTMLTableRowElement;
796
- if (!previousElement) {
797
- previousElement = trElement.parentElement.previousElementSibling.firstChild as HTMLTableRowElement;
798
- }
799
- range.selectNodeContents(previousElement.cells[m]);
800
- range.collapse(false);
801
- setSelectionFocus(range);
802
- return true;
803
- }
804
-
805
- if (event.key === "ArrowDown") {
806
- event.preventDefault();
807
- const trElement = cellElement.parentElement as HTMLTableRowElement;
808
- if (!trElement.nextElementSibling && cellElement.tagName === "TD") {
809
- if (tableElement.nextElementSibling) {
810
- range.selectNodeContents(tableElement.nextElementSibling);
811
- range.collapse(true);
812
- setSelectionFocus(range);
813
- } else {
814
- insertEmptyBlock(vditor, "afterend");
815
- }
816
- return true;
817
- }
818
-
819
- let m = 0;
820
- for (; m < trElement.cells.length; m++) {
821
- if (trElement.cells[m].isSameNode(cellElement)) {
822
- break;
823
- }
824
- }
724
+ const startContainer = range.startContainer;
725
+ const cellElement = hasClosestByMatchTag(startContainer, "TD") ||
726
+ hasClosestByMatchTag(startContainer, "TH");
727
+ if (cellElement) {
728
+ // 换行或软换行:在 cell 中添加 br
729
+ if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
730
+ if (!cellElement.lastElementChild ||
731
+ (cellElement.lastElementChild && (!cellElement.lastElementChild.isSameNode(cellElement.lastChild) ||
732
+ cellElement.lastElementChild.tagName !== "BR"))) {
733
+ cellElement.insertAdjacentHTML("beforeend", "<br>");
734
+ }
735
+ const brElement = document.createElement("br");
736
+ range.insertNode(brElement);
737
+ range.setStartAfter(brElement);
738
+ execAfterRender(vditor);
739
+ scrollCenter(vditor);
740
+ event.preventDefault();
741
+ return true;
742
+ }
825
743
 
826
- let nextElement = trElement.nextElementSibling as HTMLTableRowElement;
827
- if (!nextElement) {
828
- nextElement = trElement.parentElement.nextElementSibling.firstChild as HTMLTableRowElement;
829
- }
830
- range.selectNodeContents(nextElement.cells[m]);
831
- range.collapse(true);
832
- setSelectionFocus(range);
833
- return true;
744
+ // tab:光标移向下一个 cell
745
+ if (event.key === "Tab") {
746
+ if (event.shiftKey) {
747
+ // shift + tab 光标移动到前一个 cell
748
+ goPreviousCell(cellElement, range);
749
+ event.preventDefault();
750
+ return true;
751
+ }
752
+
753
+ let nextElement = cellElement.nextElementSibling;
754
+ if (!nextElement) {
755
+ if (cellElement.parentElement.nextElementSibling) {
756
+ nextElement = cellElement.parentElement.nextElementSibling.firstElementChild;
757
+ } else if (cellElement.parentElement.parentElement.tagName === "THEAD" &&
758
+ cellElement.parentElement.parentElement.nextElementSibling) {
759
+ nextElement =
760
+ cellElement.parentElement.parentElement.nextElementSibling.firstElementChild.firstElementChild;
761
+ } else {
762
+ nextElement = null;
834
763
  }
764
+ }
765
+ if (nextElement) {
766
+ range.selectNodeContents(nextElement);
767
+ setSelectionFocus(range);
768
+ }
769
+ event.preventDefault();
770
+ return true;
771
+ }
835
772
 
836
- // focus row input, only wysiwyg
837
- if (vditor.currentMode === "wysiwyg" &&
838
- !isCtrl(event) && event.key === "Enter" && !event.shiftKey && event.altKey) {
839
- const inputElement = (vditor.wysiwyg.popover.querySelector(".vditor-input") as HTMLInputElement);
840
- inputElement.focus();
841
- inputElement.select();
842
- event.preventDefault();
843
- return true;
773
+ const tableElement = cellElement.parentElement.parentElement.parentElement as HTMLTableElement;
774
+ if (event.key === "ArrowUp") {
775
+ event.preventDefault();
776
+ if (cellElement.tagName === "TH") {
777
+ if (tableElement.previousElementSibling) {
778
+ range.selectNodeContents(tableElement.previousElementSibling);
779
+ range.collapse(false);
780
+ setSelectionFocus(range);
781
+ } else {
782
+ insertEmptyBlock(vditor, "beforebegin");
844
783
  }
784
+ return true;
785
+ }
786
+
787
+ let m = 0;
788
+ const trElement = cellElement.parentElement as HTMLTableRowElement;
789
+ for (; m < trElement.cells.length; m++) {
790
+ if (trElement.cells[m].isSameNode(cellElement)) {
791
+ break;
792
+ }
793
+ }
794
+
795
+ let previousElement = trElement.previousElementSibling as HTMLTableRowElement;
796
+ if (!previousElement) {
797
+ previousElement = trElement.parentElement.previousElementSibling.firstChild as HTMLTableRowElement;
798
+ }
799
+ range.selectNodeContents(previousElement.cells[m]);
800
+ range.collapse(false);
801
+ setSelectionFocus(range);
802
+ return true;
803
+ }
845
804
 
846
- // Backspace:光标移动到前一个 cell
847
- if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace"
848
- && range.startOffset === 0 && range.toString() === "") {
849
- const previousCellElement = goPreviousCell(cellElement, range, false);
850
- if (!previousCellElement && tableElement) {
851
- if (tableElement.textContent.trim() === "") {
852
- tableElement.outerHTML = `<p data-block="0"><wbr>\n</p>`;
853
- setRangeByWbr(vditor[vditor.currentMode].element, range);
854
- } else {
855
- range.setStartBefore(tableElement);
856
- range.collapse(true);
857
- }
858
- execAfterRender(vditor);
859
- }
860
- event.preventDefault();
861
- return true;
862
- }
863
- // 上方新添加一行
864
- if (matchHotKey("⇧⌘F", event)) {
865
- insertRowAbove(vditor, range, cellElement);
866
- event.preventDefault();
867
- return true;
805
+ if (event.key === "ArrowDown") {
806
+ event.preventDefault();
807
+ const trElement = cellElement.parentElement as HTMLTableRowElement;
808
+ if (!trElement.nextElementSibling && cellElement.tagName === "TD") {
809
+ if (tableElement.nextElementSibling) {
810
+ range.selectNodeContents(tableElement.nextElementSibling);
811
+ range.collapse(true);
812
+ setSelectionFocus(range);
813
+ } else {
814
+ insertEmptyBlock(vditor, "afterend");
868
815
  }
816
+ return true;
817
+ }
818
+
819
+ let m = 0;
820
+ for (; m < trElement.cells.length; m++) {
821
+ if (trElement.cells[m].isSameNode(cellElement)) {
822
+ break;
823
+ }
824
+ }
825
+
826
+ let nextElement = trElement.nextElementSibling as HTMLTableRowElement;
827
+ if (!nextElement) {
828
+ nextElement = trElement.parentElement.nextElementSibling.firstChild as HTMLTableRowElement;
829
+ }
830
+ range.selectNodeContents(nextElement.cells[m]);
831
+ range.collapse(true);
832
+ setSelectionFocus(range);
833
+ return true;
834
+ }
869
835
 
870
- // 下方新添加一行 https://github.com/Vanessa219/vditor/issues/46
871
- if (matchHotKey("⌘=", event)) {
872
- insertRow(vditor, range, cellElement);
873
- event.preventDefault();
874
- return true;
875
- }
836
+ // focus row input, only wysiwyg
837
+ if (vditor.currentMode === "wysiwyg" &&
838
+ !isCtrl(event) && event.key === "Enter" && !event.shiftKey && event.altKey) {
839
+ const inputElement = (vditor.wysiwyg.popover.querySelector(".vditor-input") as HTMLInputElement);
840
+ inputElement.focus();
841
+ inputElement.select();
842
+ event.preventDefault();
843
+ return true;
844
+ }
876
845
 
877
- // 左方新添加一列
878
- if (matchHotKey("⇧⌘G", event)) {
879
- insertColumn(vditor, tableElement, cellElement, "beforebegin");
880
- event.preventDefault();
881
- return true;
846
+ // Backspace:光标移动到前一个 cell
847
+ if (!isCtrl(event) && !event.shiftKey && !event.altKey && event.key === "Backspace"
848
+ && range.startOffset === 0 && range.toString() === "") {
849
+ const previousCellElement = goPreviousCell(cellElement, range, false);
850
+ if (!previousCellElement && tableElement) {
851
+ if (tableElement.textContent.trim() === "") {
852
+ tableElement.outerHTML = `<p data-block="0"><wbr>\n</p>`;
853
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
854
+ } else {
855
+ range.setStartBefore(tableElement);
856
+ range.collapse(true);
882
857
  }
858
+ execAfterRender(vditor);
859
+ }
860
+ event.preventDefault();
861
+ return true;
862
+ }
863
+ // 上方新添加一行
864
+ if (matchHotKey("⇧⌘F", event)) {
865
+ insertRowAbove(vditor, range, cellElement);
866
+ event.preventDefault();
867
+ return true;
868
+ }
883
869
 
884
- // 后方新添加一列
885
- if (matchHotKey("⇧⌘=", event)) {
886
- insertColumn(vditor, tableElement, cellElement);
887
- event.preventDefault();
888
- return true;
889
- }
870
+ // 下方新添加一行 https://github.com/Vanessa219/vditor/issues/46
871
+ if (matchHotKey("⌘=", event)) {
872
+ insertRow(vditor, range, cellElement);
873
+ event.preventDefault();
874
+ return true;
875
+ }
890
876
 
891
- // 删除当前行
892
- if (matchHotKey("⌘-", event)) {
893
- deleteRow(vditor, range, cellElement);
894
- event.preventDefault();
895
- return true;
896
- }
877
+ // 左方新添加一列
878
+ if (matchHotKey("⇧⌘G", event)) {
879
+ insertColumn(vditor, tableElement, cellElement, "beforebegin");
880
+ event.preventDefault();
881
+ return true;
882
+ }
897
883
 
898
- // 删除当前列
899
- if (matchHotKey("⇧⌘-", event)) {
900
- deleteColumn(vditor, range, tableElement, cellElement);
901
- event.preventDefault();
902
- return true;
903
- }
884
+ // 后方新添加一列
885
+ if (matchHotKey("⇧⌘=", event)) {
886
+ insertColumn(vditor, tableElement, cellElement);
887
+ event.preventDefault();
888
+ return true;
889
+ }
904
890
 
905
- // 剧左
906
- if (matchHotKey("⇧⌘L", event)) {
907
- if (vditor.currentMode === "ir") {
908
- setTableAlign(tableElement, "left");
909
- execAfterRender(vditor);
910
- event.preventDefault();
911
- return true;
912
- } else {
913
- const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="left"]');
914
- if (itemElement) {
915
- itemElement.click();
916
- event.preventDefault();
917
- return true;
918
- }
919
- }
920
- }
891
+ // 删除当前行
892
+ if (matchHotKey("⌘-", event)) {
893
+ deleteRow(vditor, range, cellElement);
894
+ event.preventDefault();
895
+ return true;
896
+ }
921
897
 
922
- // 剧中
923
- if (matchHotKey("⇧⌘C", event)) {
924
- if (vditor.currentMode === "ir") {
925
- setTableAlign(tableElement, "center");
926
- execAfterRender(vditor);
927
- event.preventDefault();
928
- return true;
929
- } else {
930
- const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="center"]');
931
- if (itemElement) {
932
- itemElement.click();
933
- event.preventDefault();
934
- return true;
935
- }
936
- }
937
- }
938
- // 剧右
939
- if (matchHotKey("⇧⌘R", event)) {
940
- if (vditor.currentMode === "ir") {
941
- setTableAlign(tableElement, "right");
942
- execAfterRender(vditor);
943
- event.preventDefault();
944
- return true;
945
- } else {
946
- const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="right"]');
947
- if (itemElement) {
948
- itemElement.click();
949
- event.preventDefault();
950
- return true;
951
- }
952
- }
953
- }
898
+ // 删除当前列
899
+ if (matchHotKey("⇧⌘-", event)) {
900
+ deleteColumn(vditor, range, tableElement, cellElement);
901
+ event.preventDefault();
902
+ return true;
954
903
  }
955
- return false;
956
- };
957
904
 
958
- export const fixCodeBlock = (vditor: IVditor, event: KeyboardEvent, codeRenderElement: HTMLElement, range: Range) => {
959
- // 行级代码块中 command + a,近对当前代码块进行全选
960
- if (codeRenderElement.tagName === "PRE" && matchHotKey("⌘A", event)) {
961
- range.selectNodeContents(codeRenderElement.firstElementChild);
905
+ // 剧左
906
+ if (matchHotKey("⇧⌘L", event)) {
907
+ if (vditor.currentMode === "ir") {
908
+ setTableAlign(tableElement, "left");
909
+ execAfterRender(vditor);
962
910
  event.preventDefault();
963
911
  return true;
912
+ } else {
913
+ const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="left"]');
914
+ if (itemElement) {
915
+ itemElement.click();
916
+ event.preventDefault();
917
+ return true;
918
+ }
919
+ }
964
920
  }
965
921
 
966
- // tab
967
- // TODO shift + tab, shift and 选中文字
968
- if (vditor.options.tab && event.key === "Tab" && !event.shiftKey && range.toString() === "") {
969
- range.insertNode(document.createTextNode(vditor.options.tab));
970
- range.collapse(false);
922
+ // 剧中
923
+ if (matchHotKey("⇧⌘C", event)) {
924
+ if (vditor.currentMode === "ir") {
925
+ setTableAlign(tableElement, "center");
971
926
  execAfterRender(vditor);
972
927
  event.preventDefault();
973
928
  return true;
929
+ } else {
930
+ const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="center"]');
931
+ if (itemElement) {
932
+ itemElement.click();
933
+ event.preventDefault();
934
+ return true;
935
+ }
936
+ }
974
937
  }
938
+ // 剧右
939
+ if (matchHotKey("⇧⌘R", event)) {
940
+ if (vditor.currentMode === "ir") {
941
+ setTableAlign(tableElement, "right");
942
+ execAfterRender(vditor);
943
+ event.preventDefault();
944
+ return true;
945
+ } else {
946
+ const itemElement: HTMLElement = vditor.wysiwyg.popover.querySelector('[data-type="right"]');
947
+ if (itemElement) {
948
+ itemElement.click();
949
+ event.preventDefault();
950
+ return true;
951
+ }
952
+ }
953
+ }
954
+ }
955
+ return false;
956
+ };
975
957
 
976
- // Backspace: 光标位于第零个字符,仅删除代码块标签
977
- if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
978
- const codePosition = getSelectPosition(codeRenderElement, vditor[vditor.currentMode].element, range);
979
- if ((codePosition.start === 0 ||
980
- (codePosition.start === 1 && codeRenderElement.innerText === "\n")) // 空代码块,光标在 \n 后
981
- && range.toString() === "") {
982
- codeRenderElement.parentElement.outerHTML =
983
- `<p data-block="0"><wbr>${codeRenderElement.firstElementChild.innerHTML}</p>`;
984
- setRangeByWbr(vditor[vditor.currentMode].element, range);
985
- execAfterRender(vditor);
986
- event.preventDefault();
987
- return true;
988
- }
958
+ export const fixCodeBlock = (vditor: IVditor, event: KeyboardEvent, codeRenderElement: HTMLElement, range: Range) => {
959
+ // 行级代码块中 command + a,近对当前代码块进行全选
960
+ if (codeRenderElement.tagName === "PRE" && matchHotKey("⌘A", event)) {
961
+ range.selectNodeContents(codeRenderElement.firstElementChild);
962
+ event.preventDefault();
963
+ return true;
964
+ }
965
+
966
+ // tab
967
+ // TODO shift + tab, shift and 选中文字
968
+ if (vditor.options.tab && event.key === "Tab" && !event.shiftKey && range.toString() === "") {
969
+ range.insertNode(document.createTextNode(vditor.options.tab));
970
+ range.collapse(false);
971
+ execAfterRender(vditor);
972
+ event.preventDefault();
973
+ return true;
974
+ }
975
+
976
+ // Backspace: 光标位于第零个字符,仅删除代码块标签
977
+ if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
978
+ const codePosition = getSelectPosition(codeRenderElement, vditor[vditor.currentMode].element, range);
979
+ if ((codePosition.start === 0 ||
980
+ (codePosition.start === 1 && codeRenderElement.innerText === "\n")) // 空代码块,光标在 \n 后
981
+ && range.toString() === "") {
982
+ codeRenderElement.parentElement.outerHTML =
983
+ `<p data-block="0"><wbr>${codeRenderElement.firstElementChild.innerHTML}</p>`;
984
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
985
+ execAfterRender(vditor);
986
+ event.preventDefault();
987
+ return true;
989
988
  }
989
+ }
990
990
 
991
- // 换行
992
- if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
993
- if (!codeRenderElement.firstElementChild.textContent.endsWith("\n")) {
994
- codeRenderElement.firstElementChild.insertAdjacentText("beforeend", "\n");
995
- }
996
- range.extractContents();
997
- range.insertNode(document.createTextNode("\n"));
998
- range.collapse(false);
999
- setSelectionFocus(range);
1000
- if (!isFirefox()) {
1001
- if (vditor.currentMode === "wysiwyg") {
1002
- input(vditor, range);
1003
- } else {
1004
- IRInput(vditor, range);
1005
- }
1006
- }
1007
- scrollCenter(vditor);
1008
- event.preventDefault();
1009
- return true;
991
+ // 换行
992
+ if (!isCtrl(event) && !event.altKey && event.key === "Enter") {
993
+ if (!codeRenderElement.firstElementChild.textContent.endsWith("\n")) {
994
+ codeRenderElement.firstElementChild.insertAdjacentText("beforeend", "\n");
1010
995
  }
1011
- return false;
996
+ range.extractContents();
997
+ range.insertNode(document.createTextNode("\n"));
998
+ range.collapse(false);
999
+ setSelectionFocus(range);
1000
+ if (!isFirefox()) {
1001
+ if (vditor.currentMode === "wysiwyg") {
1002
+ input(vditor, range);
1003
+ } else {
1004
+ IRInput(vditor, range);
1005
+ }
1006
+ }
1007
+ scrollCenter(vditor);
1008
+ event.preventDefault();
1009
+ return true;
1010
+ }
1011
+ return false;
1012
1012
  };
1013
1013
 
1014
1014
  export const fixBlockquote = (vditor: IVditor, range: Range, event: KeyboardEvent, pElement: HTMLElement | false) => {
1015
- const startContainer = range.startContainer;
1016
- const blockquoteElement = hasClosestByMatchTag(startContainer, "BLOCKQUOTE");
1017
- if (blockquoteElement && range.toString() === "") {
1018
- if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey &&
1019
- getSelectPosition(blockquoteElement, vditor[vditor.currentMode].element, range).start === 0) {
1020
- // Backspace: 光标位于引用中的第零个字符,仅删除引用标签
1021
- range.insertNode(document.createElement("wbr"));
1022
- blockquoteElement.outerHTML = blockquoteElement.innerHTML;
1023
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1024
- execAfterRender(vditor);
1025
- event.preventDefault();
1026
- return true;
1027
- }
1015
+ const startContainer = range.startContainer;
1016
+ const blockquoteElement = hasClosestByMatchTag(startContainer, "BLOCKQUOTE");
1017
+ if (blockquoteElement && range.toString() === "") {
1018
+ if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey &&
1019
+ getSelectPosition(blockquoteElement, vditor[vditor.currentMode].element, range).start === 0) {
1020
+ // Backspace: 光标位于引用中的第零个字符,仅删除引用标签
1021
+ range.insertNode(document.createElement("wbr"));
1022
+ blockquoteElement.outerHTML = blockquoteElement.innerHTML;
1023
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1024
+ execAfterRender(vditor);
1025
+ event.preventDefault();
1026
+ return true;
1027
+ }
1028
1028
 
1029
- if (pElement && event.key === "Enter" && !isCtrl(event) && !event.shiftKey && !event.altKey
1030
- && pElement.parentElement.tagName === "BLOCKQUOTE") {
1031
- // Enter: 空行回车应逐层跳出
1032
- let isEmpty = false;
1033
- if (pElement.innerHTML.replace(Constants.ZWSP, "") === "\n" ||
1034
- pElement.innerHTML.replace(Constants.ZWSP, "") === "") {
1035
- // 空 P
1036
- isEmpty = true;
1037
- pElement.remove();
1038
- } else if (pElement.innerHTML.endsWith("\n\n") &&
1039
- getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start ===
1040
- pElement.textContent.length - 1) {
1041
- // 软换行
1042
- pElement.innerHTML = pElement.innerHTML.substr(0, pElement.innerHTML.length - 2);
1043
- isEmpty = true;
1044
- }
1045
- if (isEmpty) {
1046
- // 需添加零宽字符,否则的话无法记录 undo
1047
- blockquoteElement.insertAdjacentHTML("afterend", `<p data-block="0">${Constants.ZWSP}<wbr>\n</p>`);
1048
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1049
- execAfterRender(vditor);
1050
- event.preventDefault();
1051
- return true;
1052
- }
1053
- }
1054
- const blockElement = hasClosestBlock(startContainer);
1055
- if (vditor.currentMode === "wysiwyg" && blockElement && matchHotKey("⇧⌘;", event)) {
1056
- // 插入 blockquote
1057
- range.insertNode(document.createElement("wbr"));
1058
- blockElement.outerHTML = `<blockquote data-block="0">${blockElement.outerHTML}</blockquote>`;
1059
- setRangeByWbr(vditor.wysiwyg.element, range);
1060
- afterRenderEvent(vditor);
1061
- event.preventDefault();
1062
- return true;
1063
- }
1029
+ if (pElement && event.key === "Enter" && !isCtrl(event) && !event.shiftKey && !event.altKey
1030
+ && pElement.parentElement.tagName === "BLOCKQUOTE") {
1031
+ // Enter: 空行回车应逐层跳出
1032
+ let isEmpty = false;
1033
+ if (pElement.innerHTML.replace(Constants.ZWSP, "") === "\n" ||
1034
+ pElement.innerHTML.replace(Constants.ZWSP, "") === "") {
1035
+ // 空 P
1036
+ isEmpty = true;
1037
+ pElement.remove();
1038
+ } else if (pElement.innerHTML.endsWith("\n\n") &&
1039
+ getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start ===
1040
+ pElement.textContent.length - 1) {
1041
+ // 软换行
1042
+ pElement.innerHTML = pElement.innerHTML.substr(0, pElement.innerHTML.length - 2);
1043
+ isEmpty = true;
1044
+ }
1045
+ if (isEmpty) {
1046
+ // 需添加零宽字符,否则的话无法记录 undo
1047
+ blockquoteElement.insertAdjacentHTML("afterend", `<p data-block="0">${Constants.ZWSP}<wbr>\n</p>`);
1048
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1049
+ execAfterRender(vditor);
1050
+ event.preventDefault();
1051
+ return true;
1052
+ }
1053
+ }
1054
+ const blockElement = hasClosestBlock(startContainer);
1055
+ if (vditor.currentMode === "wysiwyg" && blockElement && matchHotKey("⇧⌘;", event)) {
1056
+ // 插入 blockquote
1057
+ range.insertNode(document.createElement("wbr"));
1058
+ blockElement.outerHTML = `<blockquote data-block="0">${blockElement.outerHTML}</blockquote>`;
1059
+ setRangeByWbr(vditor.wysiwyg.element, range);
1060
+ afterRenderEvent(vditor);
1061
+ event.preventDefault();
1062
+ return true;
1063
+ }
1064
1064
 
1065
- if (insertAfterBlock(vditor, event, range, blockquoteElement, blockquoteElement)) {
1066
- return true;
1067
- }
1068
- if (insertBeforeBlock(vditor, event, range, blockquoteElement, blockquoteElement)) {
1069
- return true;
1070
- }
1065
+ if (insertAfterBlock(vditor, event, range, blockquoteElement, blockquoteElement)) {
1066
+ return true;
1071
1067
  }
1072
- return false;
1068
+ if (insertBeforeBlock(vditor, event, range, blockquoteElement, blockquoteElement)) {
1069
+ return true;
1070
+ }
1071
+ }
1072
+ return false;
1073
1073
  };
1074
1074
 
1075
1075
  export const fixTask = (vditor: IVditor, range: Range, event: KeyboardEvent) => {
1076
- const startContainer = range.startContainer;
1077
- const taskItemElement = hasClosestByClassName(startContainer, "vditor-task");
1078
- if (taskItemElement) {
1079
- if (matchHotKey("⇧⌘J", event)) {
1080
- // ctrl + shift: toggle checked
1081
- const inputElement = taskItemElement.firstElementChild as HTMLInputElement;
1082
- if (inputElement.checked) {
1083
- inputElement.removeAttribute("checked");
1084
- } else {
1085
- inputElement.setAttribute("checked", "checked");
1086
- }
1087
- execAfterRender(vditor);
1088
- event.preventDefault();
1089
- return true;
1076
+ const startContainer = range.startContainer;
1077
+ const taskItemElement = hasClosestByClassName(startContainer, "vditor-task");
1078
+ if (taskItemElement) {
1079
+ if (matchHotKey("⇧⌘J", event)) {
1080
+ // ctrl + shift: toggle checked
1081
+ const inputElement = taskItemElement.firstElementChild as HTMLInputElement;
1082
+ if (inputElement.checked) {
1083
+ inputElement.removeAttribute("checked");
1084
+ } else {
1085
+ inputElement.setAttribute("checked", "checked");
1086
+ }
1087
+ execAfterRender(vditor);
1088
+ event.preventDefault();
1089
+ return true;
1090
+ }
1091
+
1092
+ // Backspace: 在选择框前进行删除
1093
+ if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey && range.toString() === ""
1094
+ && range.startOffset === 1
1095
+ && ((startContainer.nodeType === 3 && startContainer.previousSibling &&
1096
+ (startContainer.previousSibling as HTMLElement).tagName === "INPUT")
1097
+ || startContainer.nodeType !== 3)) {
1098
+ const previousElement = taskItemElement.previousElementSibling;
1099
+ taskItemElement.querySelector("input").remove();
1100
+ if (previousElement) {
1101
+ const lastNode = getLastNode(previousElement);
1102
+ lastNode.parentElement.insertAdjacentHTML("beforeend", "<wbr>" + taskItemElement.innerHTML.trim());
1103
+ taskItemElement.remove();
1104
+ } else {
1105
+ taskItemElement.parentElement.insertAdjacentHTML("beforebegin",
1106
+ `<p data-block="0"><wbr>${taskItemElement.innerHTML.trim() || "\n"}</p>`);
1107
+ if (taskItemElement.nextElementSibling) {
1108
+ taskItemElement.remove();
1109
+ } else {
1110
+ taskItemElement.parentElement.remove();
1090
1111
  }
1112
+ }
1113
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1114
+ execAfterRender(vditor);
1115
+ event.preventDefault();
1116
+ return true;
1117
+ }
1091
1118
 
1092
- // Backspace: 在选择框前进行删除
1093
- if (event.key === "Backspace" && !isCtrl(event) && !event.shiftKey && !event.altKey && range.toString() === ""
1094
- && range.startOffset === 1
1095
- && ((startContainer.nodeType === 3 && startContainer.previousSibling &&
1096
- (startContainer.previousSibling as HTMLElement).tagName === "INPUT")
1097
- || startContainer.nodeType !== 3)) {
1098
- const previousElement = taskItemElement.previousElementSibling;
1099
- taskItemElement.querySelector("input").remove();
1100
- if (previousElement) {
1101
- const lastNode = getLastNode(previousElement);
1102
- lastNode.parentElement.insertAdjacentHTML("beforeend", "<wbr>" + taskItemElement.innerHTML.trim());
1103
- taskItemElement.remove();
1104
- } else {
1105
- taskItemElement.parentElement.insertAdjacentHTML("beforebegin",
1106
- `<p data-block="0"><wbr>${taskItemElement.innerHTML.trim() || "\n"}</p>`);
1107
- if (taskItemElement.nextElementSibling) {
1108
- taskItemElement.remove();
1119
+ if (event.key === "Enter" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
1120
+ if (taskItemElement.textContent.trim() === "") {
1121
+ // 当前任务列表无文字
1122
+ if (hasClosestByClassName(taskItemElement.parentElement, "vditor-task")) {
1123
+ // 为子元素时,需进行反向缩进
1124
+ const topListElement = getTopList(startContainer);
1125
+ if (topListElement) {
1126
+ listOutdent(vditor, taskItemElement, range, topListElement);
1127
+ }
1128
+ } else {
1129
+ // 仅有一级任务列表
1130
+ if (taskItemElement.nextElementSibling) {
1131
+ // 任务列表下方还有元素,需要使用用段落隔断
1132
+ let afterHTML = "";
1133
+ let beforeHTML = "";
1134
+ let isAfter = false;
1135
+ Array.from(taskItemElement.parentElement.children).forEach((taskItem) => {
1136
+ if (taskItemElement.isSameNode(taskItem)) {
1137
+ isAfter = true;
1138
+ } else {
1139
+ if (isAfter) {
1140
+ afterHTML += taskItem.outerHTML;
1109
1141
  } else {
1110
- taskItemElement.parentElement.remove();
1142
+ beforeHTML += taskItem.outerHTML;
1111
1143
  }
1144
+ }
1145
+ });
1146
+ const parentTagName = taskItemElement.parentElement.tagName;
1147
+ const dataMarker = taskItemElement.parentElement.tagName === "OL" ? "" : ` data-marker="${taskItemElement.parentElement.getAttribute("data-marker")}"`;
1148
+ let startAttribute = "";
1149
+ if (beforeHTML) {
1150
+ startAttribute = taskItemElement.parentElement.tagName === "UL" ? "" : ` start="1"`;
1151
+ beforeHTML = `<${parentTagName} data-tight="true"${dataMarker} data-block="0">${beforeHTML}</${parentTagName}>`;
1112
1152
  }
1113
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1114
- execAfterRender(vditor);
1115
- event.preventDefault();
1116
- return true;
1117
- }
1118
-
1119
- if (event.key === "Enter" && !isCtrl(event) && !event.shiftKey && !event.altKey) {
1120
- if (taskItemElement.textContent.trim() === "") {
1121
- // 当前任务列表无文字
1122
- if (hasClosestByClassName(taskItemElement.parentElement, "vditor-task")) {
1123
- // 为子元素时,需进行反向缩进
1124
- const topListElement = getTopList(startContainer);
1125
- if (topListElement) {
1126
- listOutdent(vditor, taskItemElement, range, topListElement);
1127
- }
1128
- } else {
1129
- // 仅有一级任务列表
1130
- if (taskItemElement.nextElementSibling) {
1131
- // 任务列表下方还有元素,需要使用用段落隔断
1132
- let afterHTML = "";
1133
- let beforeHTML = "";
1134
- let isAfter = false;
1135
- Array.from(taskItemElement.parentElement.children).forEach((taskItem) => {
1136
- if (taskItemElement.isSameNode(taskItem)) {
1137
- isAfter = true;
1138
- } else {
1139
- if (isAfter) {
1140
- afterHTML += taskItem.outerHTML;
1141
- } else {
1142
- beforeHTML += taskItem.outerHTML;
1143
- }
1144
- }
1145
- });
1146
- const parentTagName = taskItemElement.parentElement.tagName;
1147
- const dataMarker = taskItemElement.parentElement.tagName === "OL" ? "" : ` data-marker="${taskItemElement.parentElement.getAttribute("data-marker")}"`;
1148
- let startAttribute = "";
1149
- if (beforeHTML) {
1150
- startAttribute = taskItemElement.parentElement.tagName === "UL" ? "" : ` start="1"`;
1151
- beforeHTML = `<${parentTagName} data-tight="true"${dataMarker} data-block="0">${beforeHTML}</${parentTagName}>`;
1152
- }
1153
- // <p data-block="0">\n<wbr></p> => <p data-block="0"><wbr>\n</p>
1154
- // https://github.com/Vanessa219/vditor/issues/430
1155
- taskItemElement.parentElement.outerHTML = `${beforeHTML}<p data-block="0"><wbr>\n</p><${parentTagName}
1153
+ // <p data-block="0">\n<wbr></p> => <p data-block="0"><wbr>\n</p>
1154
+ // https://github.com/Vanessa219/vditor/issues/430
1155
+ taskItemElement.parentElement.outerHTML = `${beforeHTML}<p data-block="0"><wbr>\n</p><${parentTagName}
1156
1156
  data-tight="true"${dataMarker} data-block="0"${startAttribute}>${afterHTML}</${parentTagName}>`;
1157
- } else {
1158
- // 任务列表下方无任务列表元素
1159
- taskItemElement.parentElement.insertAdjacentHTML("afterend", `<p data-block="0"><wbr>\n</p>`);
1160
- if (taskItemElement.parentElement.querySelectorAll("li").length === 1) {
1161
- // 任务列表仅有一项时,使用 p 元素替换
1162
- taskItemElement.parentElement.remove();
1163
- } else {
1164
- // 任务列表有多项时,当前任务列表位于最后一项,移除该任务列表
1165
- taskItemElement.remove();
1166
- }
1167
- }
1168
- }
1169
- } else if (startContainer.nodeType !== 3 && range.startOffset === 0 &&
1170
- (startContainer.firstChild as HTMLElement).tagName === "INPUT") {
1171
- // 光标位于 input 之前
1172
- range.setStart(startContainer.childNodes[1], 1);
1157
+ } else {
1158
+ // 任务列表下方无任务列表元素
1159
+ taskItemElement.parentElement.insertAdjacentHTML("afterend", `<p data-block="0"><wbr>\n</p>`);
1160
+ if (taskItemElement.parentElement.querySelectorAll("li").length === 1) {
1161
+ // 任务列表仅有一项时,使用 p 元素替换
1162
+ taskItemElement.parentElement.remove();
1173
1163
  } else {
1174
- // 当前任务列表有文字,光标后的文字需添加到新任务列表中
1175
- range.setEndAfter(taskItemElement.lastChild);
1176
- taskItemElement.insertAdjacentHTML("afterend", `<li class="vditor-task" data-marker="${taskItemElement.getAttribute("data-marker")}"><input type="checkbox"> <wbr></li>`);
1177
- document.querySelector("wbr").after(range.extractContents());
1164
+ // 任务列表有多项时,当前任务列表位于最后一项,移除该任务列表
1165
+ taskItemElement.remove();
1178
1166
  }
1179
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1180
- execAfterRender(vditor);
1181
- scrollCenter(vditor);
1182
- event.preventDefault();
1183
- return true;
1184
- }
1167
+ }
1168
+ }
1169
+ } else if (startContainer.nodeType !== 3 && range.startOffset === 0 &&
1170
+ (startContainer.firstChild as HTMLElement).tagName === "INPUT") {
1171
+ // 光标位于 input 之前
1172
+ range.setStart(startContainer.childNodes[1], 1);
1173
+ } else {
1174
+ // 当前任务列表有文字,光标后的文字需添加到新任务列表中
1175
+ range.setEndAfter(taskItemElement.lastChild);
1176
+ taskItemElement.insertAdjacentHTML("afterend", `<li class="vditor-task" data-marker="${taskItemElement.getAttribute("data-marker")}"><input type="checkbox"> <wbr></li>`);
1177
+ document.querySelector("wbr").after(range.extractContents());
1178
+ }
1179
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1180
+ execAfterRender(vditor);
1181
+ scrollCenter(vditor);
1182
+ event.preventDefault();
1183
+ return true;
1185
1184
  }
1186
- return false;
1185
+ }
1186
+ return false;
1187
1187
  };
1188
1188
 
1189
1189
  export const fixDelete = (vditor: IVditor, range: Range, event: KeyboardEvent, pElement: HTMLElement | false) => {
1190
- if (range.startContainer.nodeType !== 3) {
1191
- // 光标位于 hr 前,hr 前有内容
1192
- const rangeElement = (range.startContainer as HTMLElement).children[range.startOffset];
1193
- if (rangeElement && rangeElement.tagName === "HR") {
1194
- range.selectNodeContents(rangeElement.previousElementSibling);
1195
- range.collapse(false);
1196
- event.preventDefault();
1197
- return true;
1198
- }
1190
+ if (range.startContainer.nodeType !== 3) {
1191
+ // 光标位于 hr 前,hr 前有内容
1192
+ const rangeElement = (range.startContainer as HTMLElement).children[range.startOffset];
1193
+ if (rangeElement && rangeElement.tagName === "HR") {
1194
+ range.selectNodeContents(rangeElement.previousElementSibling);
1195
+ range.collapse(false);
1196
+ event.preventDefault();
1197
+ return true;
1199
1198
  }
1200
-
1201
- if (pElement) {
1202
- const previousElement = pElement.previousElementSibling;
1203
- if (previousElement && getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0 &&
1204
- ((isFirefox() && previousElement.tagName === "HR") || previousElement.tagName === "TABLE")) {
1205
- if (previousElement.tagName === "TABLE") {
1206
- // table 后删除 https://github.com/Vanessa219/vditor/issues/243
1207
- const lastCellElement = previousElement.lastElementChild.lastElementChild.lastElementChild;
1208
- lastCellElement.innerHTML =
1209
- lastCellElement.innerHTML.trimLeft() + "<wbr>" + pElement.textContent.trim();
1210
- pElement.remove();
1211
- } else {
1212
- // 光标位于 hr 后进行删除
1213
- previousElement.remove();
1214
- }
1215
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1216
- execAfterRender(vditor);
1217
- event.preventDefault();
1218
- return true;
1219
- }
1199
+ }
1200
+
1201
+ if (pElement) {
1202
+ const previousElement = pElement.previousElementSibling;
1203
+ if (previousElement && getSelectPosition(pElement, vditor[vditor.currentMode].element, range).start === 0 &&
1204
+ ((isFirefox() && previousElement.tagName === "HR") || previousElement.tagName === "TABLE")) {
1205
+ if (previousElement.tagName === "TABLE") {
1206
+ // table 后删除 https://github.com/Vanessa219/vditor/issues/243
1207
+ const lastCellElement = previousElement.lastElementChild.lastElementChild.lastElementChild;
1208
+ lastCellElement.innerHTML =
1209
+ lastCellElement.innerHTML.trimLeft() + "<wbr>" + pElement.textContent.trim();
1210
+ pElement.remove();
1211
+ } else {
1212
+ // 光标位于 hr 后进行删除
1213
+ previousElement.remove();
1214
+ }
1215
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1216
+ execAfterRender(vditor);
1217
+ event.preventDefault();
1218
+ return true;
1220
1219
  }
1221
- return false;
1220
+ }
1221
+ return false;
1222
1222
  };
1223
1223
 
1224
1224
  export const fixHR = (range: Range) => {
1225
- if (isFirefox() && range.startContainer.nodeType !== 3 &&
1226
- (range.startContainer as HTMLElement).tagName === "HR") {
1227
- range.setStartBefore(range.startContainer);
1228
- }
1225
+ if (isFirefox() && range.startContainer.nodeType !== 3 &&
1226
+ (range.startContainer as HTMLElement).tagName === "HR") {
1227
+ range.setStartBefore(range.startContainer);
1228
+ }
1229
1229
  };
1230
1230
 
1231
1231
  // firefox https://github.com/Vanessa219/vditor/issues/407
1232
1232
  export const fixFirefoxArrowUpTable = (event: KeyboardEvent, blockElement: false | HTMLElement, range: Range) => {
1233
- if (!isFirefox()) {
1234
- return false;
1235
- }
1236
- if (event.key === "ArrowUp" && blockElement && blockElement.previousElementSibling?.tagName === "TABLE") {
1237
- const tableElement = blockElement.previousElementSibling as HTMLTableElement;
1238
- range.selectNodeContents(tableElement.rows[tableElement.rows.length - 1].lastElementChild);
1239
- range.collapse(false);
1240
- event.preventDefault();
1241
- return true;
1242
- }
1243
- if (event.key === "ArrowDown" && blockElement && blockElement.nextElementSibling?.tagName === "TABLE") {
1244
- range.selectNodeContents((blockElement.nextElementSibling as HTMLTableElement).rows[0].cells[0]);
1245
- range.collapse(true);
1246
- event.preventDefault();
1247
- return true;
1248
- }
1233
+ if (!isFirefox()) {
1249
1234
  return false;
1235
+ }
1236
+ if (event.key === "ArrowUp" && blockElement && blockElement.previousElementSibling?.tagName === "TABLE") {
1237
+ const tableElement = blockElement.previousElementSibling as HTMLTableElement;
1238
+ range.selectNodeContents(tableElement.rows[tableElement.rows.length - 1].lastElementChild);
1239
+ range.collapse(false);
1240
+ event.preventDefault();
1241
+ return true;
1242
+ }
1243
+ if (event.key === "ArrowDown" && blockElement && blockElement.nextElementSibling?.tagName === "TABLE") {
1244
+ range.selectNodeContents((blockElement.nextElementSibling as HTMLTableElement).rows[0].cells[0]);
1245
+ range.collapse(true);
1246
+ event.preventDefault();
1247
+ return true;
1248
+ }
1249
+ return false;
1250
1250
  };
1251
1251
 
1252
1252
  export const paste = async (vditor: IVditor, event: (ClipboardEvent | DragEvent) & { target: HTMLElement }, callback: {
1253
- pasteCode(code: string): void,
1253
+ pasteCode(code: string): void,
1254
1254
  }) => {
1255
- event.stopPropagation();
1256
- event.preventDefault();
1257
- let textHTML;
1258
- let textPlain;
1259
- let files;
1260
- if ("clipboardData" in event) {
1261
- textHTML = event.clipboardData.getData("text/html");
1262
- textPlain = event.clipboardData.getData("text/plain");
1263
- files = event.clipboardData.files;
1264
- } else {
1265
- textHTML = event.dataTransfer.getData("text/html");
1266
- textPlain = event.dataTransfer.getData("text/plain");
1267
- if (event.dataTransfer.types.includes("Files")) {
1268
- files = event.dataTransfer.items;
1269
- }
1255
+ if (vditor[vditor.currentMode].element.getAttribute("contenteditable") !== "true") {
1256
+ return;
1257
+ }
1258
+ event.stopPropagation();
1259
+ event.preventDefault();
1260
+ let textHTML;
1261
+ let textPlain;
1262
+ let files;
1263
+ if ("clipboardData" in event) {
1264
+ textHTML = event.clipboardData.getData("text/html");
1265
+ textPlain = event.clipboardData.getData("text/plain");
1266
+ files = event.clipboardData.files;
1267
+ } else {
1268
+ textHTML = event.dataTransfer.getData("text/html");
1269
+ textPlain = event.dataTransfer.getData("text/plain");
1270
+ if (event.dataTransfer.types.includes("Files")) {
1271
+ files = event.dataTransfer.items;
1272
+ }
1273
+ }
1274
+ const renderers: {
1275
+ HTML2VditorDOM?: ILuteRender,
1276
+ HTML2VditorIRDOM?: ILuteRender,
1277
+ Md2VditorDOM?: ILuteRender,
1278
+ Md2VditorIRDOM?: ILuteRender,
1279
+ Md2VditorSVDOM?: ILuteRender,
1280
+ } = {};
1281
+ const renderLinkDest: ILuteRenderCallback = (node, entering) => {
1282
+ if (!entering) {
1283
+ return ["", Lute.WalkContinue];
1270
1284
  }
1271
- const renderers: {
1272
- HTML2VditorDOM?: ILuteRender,
1273
- HTML2VditorIRDOM?: ILuteRender,
1274
- Md2VditorDOM?: ILuteRender,
1275
- Md2VditorIRDOM?: ILuteRender,
1276
- Md2VditorSVDOM?: ILuteRender,
1277
- } = {};
1278
- const renderLinkDest: ILuteRenderCallback = (node, entering) => {
1279
- if (!entering) {
1280
- return ["", Lute.WalkContinue];
1281
- }
1282
1285
 
1283
- const src = node.TokensStr();
1284
- if (node.__internal_object__.Parent.Type === 34 && src && src.indexOf("file://") === -1 &&
1285
- vditor.options.upload.linkToImgUrl) {
1286
- const xhr = new XMLHttpRequest();
1287
- xhr.open("POST", vditor.options.upload.linkToImgUrl);
1288
- if (vditor.options.upload.token) {
1289
- xhr.setRequestHeader("X-Upload-Token", vditor.options.upload.token);
1290
- }
1291
- if (vditor.options.upload.withCredentials) {
1292
- xhr.withCredentials = true;
1293
- }
1294
- setHeaders(vditor, xhr);
1295
- xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
1296
- xhr.onreadystatechange = () => {
1297
- if (xhr.readyState === XMLHttpRequest.DONE) {
1298
- if (xhr.status === 200) {
1299
- let responseText = xhr.responseText;
1300
- if (vditor.options.upload.linkToImgFormat) {
1301
- responseText = vditor.options.upload.linkToImgFormat(xhr.responseText);
1302
- }
1303
- const responseJSON = JSON.parse(responseText);
1304
- if (responseJSON.code !== 0) {
1305
- vditor.tip.show(responseJSON.msg);
1306
- return;
1307
- }
1308
- const original = responseJSON.data.originalURL;
1309
- if (vditor.currentMode === "sv") {
1310
- vditor.sv.element.querySelectorAll(".vditor-sv__marker--link")
1311
- .forEach((item: HTMLElement) => {
1312
- if (item.textContent === original) {
1313
- item.textContent = responseJSON.data.url;
1314
- }
1315
- });
1316
- } else {
1317
- const imgElement: HTMLImageElement =
1318
- vditor[vditor.currentMode].element.querySelector(`img[src="${original}"]`);
1319
- imgElement.src = responseJSON.data.url;
1320
- if (vditor.currentMode === "ir") {
1321
- imgElement.previousElementSibling.previousElementSibling.innerHTML =
1322
- responseJSON.data.url;
1323
- }
1324
- }
1325
- execAfterRender(vditor);
1326
- } else {
1327
- vditor.tip.show(xhr.responseText);
1328
- }
1329
- if (vditor.options.upload.linkToImgCallback) {
1330
- vditor.options.upload.linkToImgCallback(xhr.responseText);
1331
- }
1332
- }
1333
- };
1334
- xhr.send(JSON.stringify({url: src}));
1335
- }
1336
- if (vditor.currentMode === "ir") {
1337
- return [`<span class="vditor-ir__marker vditor-ir__marker--link">${src}</span>`, Lute.WalkContinue];
1338
- } else if (vditor.currentMode === "wysiwyg") {
1339
- return ["", Lute.WalkContinue];
1340
- } else {
1341
- return [`<span class="vditor-sv__marker--link">${src}</span>`, Lute.WalkContinue];
1342
- }
1343
- };
1344
-
1345
- // 浏览器地址栏拷贝处理
1346
- if (textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
1347
- `<a href="${textPlain}">${textPlain}</a>` ||
1348
- textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
1349
- `<!--StartFragment--><a href="${textPlain}">${textPlain}</a><!--EndFragment-->`) {
1350
- textHTML = "";
1351
- }
1352
-
1353
- // process word
1354
- const doc = new DOMParser().parseFromString(textHTML, "text/html");
1355
- if (doc.body) {
1356
- textHTML = doc.body.innerHTML;
1357
- }
1358
- textHTML = Lute.Sanitize(textHTML);
1359
- vditor.wysiwyg.getComments(vditor);
1360
-
1361
- // process code
1362
- const height = vditor[vditor.currentMode].element.scrollHeight;
1363
- const code = processPasteCode(textHTML, textPlain, vditor.currentMode);
1364
- const codeElement = vditor.currentMode === "sv" ?
1365
- hasClosestByAttribute(event.target, "data-type", "code-block") :
1366
- hasClosestByMatchTag(event.target, "CODE");
1367
- if (codeElement) {
1368
- // 粘贴在代码位置
1369
- if (vditor.currentMode === "sv") {
1370
- document.execCommand("insertHTML", false, textPlain.replace(/&/g, "&amp;").replace(/</g, "&lt;"));
1371
- } else {
1372
- const position = getSelectPosition(event.target, vditor[vditor.currentMode].element);
1373
- if (codeElement.parentElement.tagName !== "PRE") {
1374
- // https://github.com/Vanessa219/vditor/issues/463
1375
- textPlain += Constants.ZWSP;
1376
- }
1377
- codeElement.textContent = codeElement.textContent.substring(0, position.start)
1378
- + textPlain + codeElement.textContent.substring(position.end);
1379
- setSelectionByPosition(position.start + textPlain.length, position.start + textPlain.length,
1380
- codeElement.parentElement);
1381
- if (codeElement.parentElement?.nextElementSibling.classList
1382
- .contains(`vditor-${vditor.currentMode}__preview`)) {
1383
- codeElement.parentElement.nextElementSibling.innerHTML = codeElement.outerHTML;
1384
- processCodeRender(codeElement.parentElement.nextElementSibling as HTMLElement, vditor);
1286
+ const src = node.TokensStr();
1287
+ if (node.__internal_object__.Parent.Type === 34 && src && src.indexOf("file://") === -1 &&
1288
+ vditor.options.upload.linkToImgUrl) {
1289
+ const xhr = new XMLHttpRequest();
1290
+ xhr.open("POST", vditor.options.upload.linkToImgUrl);
1291
+ if (vditor.options.upload.token) {
1292
+ xhr.setRequestHeader("X-Upload-Token", vditor.options.upload.token);
1293
+ }
1294
+ if (vditor.options.upload.withCredentials) {
1295
+ xhr.withCredentials = true;
1296
+ }
1297
+ setHeaders(vditor, xhr);
1298
+ xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
1299
+ xhr.onreadystatechange = () => {
1300
+ if (xhr.readyState === XMLHttpRequest.DONE) {
1301
+ if (xhr.status === 200) {
1302
+ let responseText = xhr.responseText;
1303
+ if (vditor.options.upload.linkToImgFormat) {
1304
+ responseText = vditor.options.upload.linkToImgFormat(xhr.responseText);
1385
1305
  }
1386
- }
1387
- } else if (code) {
1388
- callback.pasteCode(code);
1389
- } else {
1390
- if (textHTML.trim() !== "") {
1391
- const tempElement = document.createElement("div");
1392
- tempElement.innerHTML = textHTML;
1393
- tempElement.querySelectorAll("[style]").forEach((e) => {
1394
- e.removeAttribute("style");
1395
- });
1396
- tempElement.querySelectorAll(".vditor-copy").forEach((e) => {
1397
- e.remove();
1398
- });
1399
- if (vditor.currentMode === "ir") {
1400
- renderers.HTML2VditorIRDOM = {renderLinkDest};
1401
- vditor.lute.SetJSRenderers({renderers});
1402
- insertHTML(vditor.lute.HTML2VditorIRDOM(tempElement.innerHTML), vditor);
1403
- } else if (vditor.currentMode === "wysiwyg") {
1404
- renderers.HTML2VditorDOM = {renderLinkDest};
1405
- vditor.lute.SetJSRenderers({renderers});
1406
- insertHTML(vditor.lute.HTML2VditorDOM(tempElement.innerHTML), vditor);
1407
- } else {
1408
- renderers.Md2VditorSVDOM = {renderLinkDest};
1409
- vditor.lute.SetJSRenderers({renderers});
1410
- processPaste(vditor, vditor.lute.HTML2Md(tempElement.innerHTML).trimRight());
1306
+ const responseJSON = JSON.parse(responseText);
1307
+ if (responseJSON.code !== 0) {
1308
+ vditor.tip.show(responseJSON.msg);
1309
+ return;
1411
1310
  }
1412
- vditor.outline.render(vditor);
1413
- } else if (files.length > 0 && (vditor.options.upload.url || vditor.options.upload.handler)) {
1414
- await uploadFiles(vditor, files);
1415
- } else if (textPlain.trim() !== "" && files.length === 0) {
1416
- if (vditor.currentMode === "ir") {
1417
- renderers.Md2VditorIRDOM = {renderLinkDest};
1418
- vditor.lute.SetJSRenderers({renderers});
1419
- insertHTML(vditor.lute.Md2VditorIRDOM(textPlain), vditor);
1420
- } else if (vditor.currentMode === "wysiwyg") {
1421
- renderers.Md2VditorDOM = {renderLinkDest};
1422
- vditor.lute.SetJSRenderers({renderers});
1423
- insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
1311
+ const original = responseJSON.data.originalURL;
1312
+ if (vditor.currentMode === "sv") {
1313
+ vditor.sv.element.querySelectorAll(".vditor-sv__marker--link")
1314
+ .forEach((item: HTMLElement) => {
1315
+ if (item.textContent === original) {
1316
+ item.textContent = responseJSON.data.url;
1317
+ }
1318
+ });
1424
1319
  } else {
1425
- renderers.Md2VditorSVDOM = {renderLinkDest};
1426
- vditor.lute.SetJSRenderers({renderers});
1427
- processPaste(vditor, textPlain);
1320
+ const imgElement: HTMLImageElement =
1321
+ vditor[vditor.currentMode].element.querySelector(`img[src="${original}"]`);
1322
+ imgElement.src = responseJSON.data.url;
1323
+ if (vditor.currentMode === "ir") {
1324
+ imgElement.previousElementSibling.previousElementSibling.innerHTML =
1325
+ responseJSON.data.url;
1326
+ }
1428
1327
  }
1429
- vditor.outline.render(vditor);
1430
- }
1328
+ execAfterRender(vditor);
1329
+ } else {
1330
+ vditor.tip.show(xhr.responseText);
1331
+ }
1332
+ if (vditor.options.upload.linkToImgCallback) {
1333
+ vditor.options.upload.linkToImgCallback(xhr.responseText);
1334
+ }
1335
+ }
1336
+ };
1337
+ xhr.send(JSON.stringify({url: src}));
1431
1338
  }
1432
- if (vditor.currentMode !== "sv") {
1433
- const blockElement = hasClosestBlock(getEditorRange(vditor).startContainer);
1434
- if (blockElement) {
1435
- // https://github.com/Vanessa219/vditor/issues/591
1436
- const range = getEditorRange(vditor);
1437
- vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
1438
- wbr.remove();
1439
- });
1440
- range.insertNode(document.createElement("wbr"));
1441
- if (vditor.currentMode === "wysiwyg") {
1442
- blockElement.outerHTML = vditor.lute.SpinVditorDOM(blockElement.outerHTML);
1443
- } else {
1444
- blockElement.outerHTML = vditor.lute.SpinVditorIRDOM(blockElement.outerHTML);
1445
- }
1446
- setRangeByWbr(vditor[vditor.currentMode].element, range);
1447
- }
1448
- vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
1449
- .forEach((item: HTMLElement) => {
1450
- processCodeRender(item, vditor);
1451
- });
1339
+ if (vditor.currentMode === "ir") {
1340
+ return [`<span class="vditor-ir__marker vditor-ir__marker--link">${src}</span>`, Lute.WalkContinue];
1341
+ } else if (vditor.currentMode === "wysiwyg") {
1342
+ return ["", Lute.WalkContinue];
1343
+ } else {
1344
+ return [`<span class="vditor-sv__marker--link">${src}</span>`, Lute.WalkContinue];
1452
1345
  }
1453
- vditor.wysiwyg.triggerRemoveComment(vditor);
1454
- execAfterRender(vditor);
1455
- if (vditor[vditor.currentMode].element.scrollHeight - height >
1456
- Math.min(vditor[vditor.currentMode].element.clientHeight, window.innerHeight) / 2) {
1457
- scrollCenter(vditor);
1346
+ };
1347
+
1348
+ // 浏览器地址栏拷贝处理
1349
+ if (textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
1350
+ `<a href="${textPlain}">${textPlain}</a>` ||
1351
+ textHTML.replace(/&amp;/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
1352
+ `<!--StartFragment--><a href="${textPlain}">${textPlain}</a><!--EndFragment-->`) {
1353
+ textHTML = "";
1354
+ }
1355
+
1356
+ // process word
1357
+ const doc = new DOMParser().parseFromString(textHTML, "text/html");
1358
+ if (doc.body) {
1359
+ textHTML = doc.body.innerHTML;
1360
+ }
1361
+ textHTML = Lute.Sanitize(textHTML);
1362
+ vditor.wysiwyg.getComments(vditor);
1363
+
1364
+ // process code
1365
+ const height = vditor[vditor.currentMode].element.scrollHeight;
1366
+ const code = processPasteCode(textHTML, textPlain, vditor.currentMode);
1367
+ const codeElement = vditor.currentMode === "sv" ?
1368
+ hasClosestByAttribute(event.target, "data-type", "code-block") :
1369
+ hasClosestByMatchTag(event.target, "CODE");
1370
+ if (codeElement) {
1371
+ // 粘贴在代码位置
1372
+ if (vditor.currentMode === "sv") {
1373
+ document.execCommand("insertHTML", false, textPlain.replace(/&/g, "&amp;").replace(/</g, "&lt;"));
1374
+ } else {
1375
+ const position = getSelectPosition(event.target, vditor[vditor.currentMode].element);
1376
+ if (codeElement.parentElement.tagName !== "PRE") {
1377
+ // https://github.com/Vanessa219/vditor/issues/463
1378
+ textPlain += Constants.ZWSP;
1379
+ }
1380
+ codeElement.textContent = codeElement.textContent.substring(0, position.start)
1381
+ + textPlain + codeElement.textContent.substring(position.end);
1382
+ setSelectionByPosition(position.start + textPlain.length, position.start + textPlain.length,
1383
+ codeElement.parentElement);
1384
+ if (codeElement.parentElement?.nextElementSibling.classList
1385
+ .contains(`vditor-${vditor.currentMode}__preview`)) {
1386
+ codeElement.parentElement.nextElementSibling.innerHTML = codeElement.outerHTML;
1387
+ processCodeRender(codeElement.parentElement.nextElementSibling as HTMLElement, vditor);
1388
+ }
1389
+ }
1390
+ } else if (code) {
1391
+ callback.pasteCode(code);
1392
+ } else {
1393
+ if (textHTML.trim() !== "") {
1394
+ const tempElement = document.createElement("div");
1395
+ tempElement.innerHTML = textHTML;
1396
+ tempElement.querySelectorAll("[style]").forEach((e) => {
1397
+ e.removeAttribute("style");
1398
+ });
1399
+ tempElement.querySelectorAll(".vditor-copy").forEach((e) => {
1400
+ e.remove();
1401
+ });
1402
+ if (vditor.currentMode === "ir") {
1403
+ renderers.HTML2VditorIRDOM = {renderLinkDest};
1404
+ vditor.lute.SetJSRenderers({renderers});
1405
+ insertHTML(vditor.lute.HTML2VditorIRDOM(tempElement.innerHTML), vditor);
1406
+ } else if (vditor.currentMode === "wysiwyg") {
1407
+ renderers.HTML2VditorDOM = {renderLinkDest};
1408
+ vditor.lute.SetJSRenderers({renderers});
1409
+ insertHTML(vditor.lute.HTML2VditorDOM(tempElement.innerHTML), vditor);
1410
+ } else {
1411
+ renderers.Md2VditorSVDOM = {renderLinkDest};
1412
+ vditor.lute.SetJSRenderers({renderers});
1413
+ processPaste(vditor, vditor.lute.HTML2Md(tempElement.innerHTML).trimRight());
1414
+ }
1415
+ vditor.outline.render(vditor);
1416
+ } else if (files.length > 0 && (vditor.options.upload.url || vditor.options.upload.handler)) {
1417
+ await uploadFiles(vditor, files);
1418
+ } else if (textPlain.trim() !== "" && files.length === 0) {
1419
+ if (vditor.currentMode === "ir") {
1420
+ renderers.Md2VditorIRDOM = {renderLinkDest};
1421
+ vditor.lute.SetJSRenderers({renderers});
1422
+ insertHTML(vditor.lute.Md2VditorIRDOM(textPlain), vditor);
1423
+ } else if (vditor.currentMode === "wysiwyg") {
1424
+ renderers.Md2VditorDOM = {renderLinkDest};
1425
+ vditor.lute.SetJSRenderers({renderers});
1426
+ insertHTML(vditor.lute.Md2VditorDOM(textPlain), vditor);
1427
+ } else {
1428
+ renderers.Md2VditorSVDOM = {renderLinkDest};
1429
+ vditor.lute.SetJSRenderers({renderers});
1430
+ processPaste(vditor, textPlain);
1431
+ }
1432
+ vditor.outline.render(vditor);
1433
+ }
1434
+ }
1435
+ if (vditor.currentMode !== "sv") {
1436
+ const blockElement = hasClosestBlock(getEditorRange(vditor).startContainer);
1437
+ if (blockElement) {
1438
+ // https://github.com/Vanessa219/vditor/issues/591
1439
+ const range = getEditorRange(vditor);
1440
+ vditor[vditor.currentMode].element.querySelectorAll("wbr").forEach((wbr) => {
1441
+ wbr.remove();
1442
+ });
1443
+ range.insertNode(document.createElement("wbr"));
1444
+ if (vditor.currentMode === "wysiwyg") {
1445
+ blockElement.outerHTML = vditor.lute.SpinVditorDOM(blockElement.outerHTML);
1446
+ } else {
1447
+ blockElement.outerHTML = vditor.lute.SpinVditorIRDOM(blockElement.outerHTML);
1448
+ }
1449
+ setRangeByWbr(vditor[vditor.currentMode].element, range);
1458
1450
  }
1451
+ vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
1452
+ .forEach((item: HTMLElement) => {
1453
+ processCodeRender(item, vditor);
1454
+ });
1455
+ }
1456
+ vditor.wysiwyg.triggerRemoveComment(vditor);
1457
+ execAfterRender(vditor);
1458
+ if (vditor[vditor.currentMode].element.scrollHeight - height >
1459
+ Math.min(vditor[vditor.currentMode].element.clientHeight, window.innerHeight) / 2) {
1460
+ scrollCenter(vditor);
1461
+ }
1459
1462
  };