vditor 3.11.3 → 4.0.0

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 (39) hide show
  1. package/README.md +1 -0
  2. package/dist/index.css +32 -79
  3. package/dist/index.js +3762 -3993
  4. package/dist/index.min.js +1 -1
  5. package/dist/js/lute/lute.min.js +2 -2
  6. package/dist/method.js +15 -6
  7. package/dist/method.min.js +1 -1
  8. package/dist/ts/sv/index.d.ts +1 -5
  9. package/dist/ts/sv/process.d.ts +1 -3
  10. package/dist/ts/upload/getElement.d.ts +1 -1
  11. package/dist/ts/upload/index.d.ts +2 -0
  12. package/dist/ts/util/editorCommonEvent.d.ts +1 -1
  13. package/dist/types/index.d.ts +7 -5
  14. package/package.json +1 -1
  15. package/src/assets/less/_sv.less +2 -98
  16. package/src/assets/less/_wysiwyg.less +30 -0
  17. package/src/index.ts +45 -14
  18. package/src/ts/hint/index.ts +9 -4
  19. package/src/ts/ir/highlightToolbarIR.ts +4 -4
  20. package/src/ts/markdown/getMarkdown.ts +3 -3
  21. package/src/ts/sv/index.ts +37 -76
  22. package/src/ts/sv/process.ts +48 -164
  23. package/src/ts/sv/processKeydown.ts +19 -186
  24. package/src/ts/toolbar/EditMode.ts +3 -10
  25. package/src/ts/toolbar/Emoji.ts +8 -1
  26. package/src/ts/toolbar/Record.ts +10 -2
  27. package/src/ts/toolbar/index.ts +3 -0
  28. package/src/ts/ui/initUI.ts +7 -3
  29. package/src/ts/undo/index.ts +32 -17
  30. package/src/ts/upload/index.ts +42 -10
  31. package/src/ts/util/editorCommonEvent.ts +18 -9
  32. package/src/ts/util/fixBrowserBehavior.ts +106 -36
  33. package/src/ts/util/selection.ts +11 -3
  34. package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +53 -1
  35. package/src/ts/wysiwyg/processKeydown.ts +47 -0
  36. package/dist/ts/sv/combineFootnote.d.ts +0 -6
  37. package/dist/ts/sv/inputEvent.d.ts +0 -1
  38. package/src/ts/sv/combineFootnote.ts +0 -27
  39. package/src/ts/sv/inputEvent.ts +0 -188
@@ -1,10 +1,5 @@
1
1
  import {isCtrl} from "../util/compatibility";
2
- import {fixTab} from "../util/fixBrowserBehavior";
3
- import {hasClosestByAttribute} from "../util/hasClosest";
4
- import {hasClosestByTag} from "../util/hasClosestByHeadings";
5
- import {getEditorRange, getSelectPosition} from "../util/selection";
6
- import {inputEvent} from "./inputEvent";
7
- import {processAfterRender, processPreviousMarkers} from "./process";
2
+ import {processAfterRender} from "./process";
8
3
 
9
4
  export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
10
5
  vditor.sv.composingLock = event.isComposing;
@@ -16,187 +11,25 @@ export const processKeydown = (vditor: IVditor, event: KeyboardEvent) => {
16
11
  event.key !== "Shift" && event.key !== "CapsLock" && event.key !== "Escape" && !/^F\d{1,2}$/.test(event.key)) {
17
12
  vditor.undo.recordFirstPosition(vditor, event);
18
13
  }
19
- // 仅处理以下快捷键操作
20
- if (event.key !== "Enter" && event.key !== "Tab" && event.key !== "Backspace" && event.key.indexOf("Arrow") === -1
21
- && !isCtrl(event) && event.key !== "Escape") {
22
- return false;
23
- }
24
- const range = getEditorRange(vditor);
25
- let startContainer = range.startContainer;
26
- if (range.startContainer.nodeType !== 3 && (range.startContainer as HTMLElement).tagName === "DIV") {
27
- startContainer = range.startContainer.childNodes[range.startOffset - 1];
28
- }
29
- const textElement = hasClosestByAttribute(startContainer, "data-type", "text");
30
-
31
- // blockquote
32
- let blockquoteMarkerElement = hasClosestByAttribute(startContainer, "data-type", "blockquote-marker");
33
- if (!blockquoteMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
34
- textElement.previousElementSibling.getAttribute("data-type") === "blockquote-marker") {
35
- blockquoteMarkerElement = textElement.previousElementSibling as HTMLElement;
36
- }
37
- // 回车逐个删除 blockquote marker 标记
38
- if (blockquoteMarkerElement) {
39
- if (event.key === "Enter" && !isCtrl(event) && !event.altKey &&
40
- blockquoteMarkerElement.nextElementSibling.textContent.trim() === "" &&
41
- getSelectPosition(blockquoteMarkerElement, vditor.sv.element, range).start ===
42
- blockquoteMarkerElement.textContent.length) {
43
- if (blockquoteMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
44
- // 列表中存在多行 BQ 时,标记回车需跳出列表
45
- blockquoteMarkerElement.previousElementSibling.setAttribute("data-action", "enter-remove");
46
- }
47
- blockquoteMarkerElement.remove();
48
- processAfterRender(vditor);
49
- event.preventDefault();
50
- return true;
51
- }
52
- }
53
14
 
54
- // list item
55
- const listMarkerElement = hasClosestByAttribute(startContainer, "data-type", "li-marker") as HTMLElement;
56
- const taskMarkerElement = hasClosestByAttribute(startContainer, "data-type", "task-marker") as HTMLElement;
57
- let listLastMarkerElement = listMarkerElement;
58
- if (!listLastMarkerElement) {
59
- if (taskMarkerElement && taskMarkerElement.nextElementSibling.getAttribute("data-type") !== "task-marker") {
60
- listLastMarkerElement = taskMarkerElement;
61
- }
62
- }
63
- if (!listLastMarkerElement && range.startOffset === 0 && textElement && textElement.previousElementSibling &&
64
- (textElement.previousElementSibling.getAttribute("data-type") === "li-marker" ||
65
- textElement.previousElementSibling.getAttribute("data-type") === "task-marker")) {
66
- listLastMarkerElement = textElement.previousElementSibling as HTMLElement;
67
- }
68
- if (listLastMarkerElement) {
69
- const startIndex = getSelectPosition(listLastMarkerElement, vditor.sv.element, range).start;
70
- const isTask = listLastMarkerElement.getAttribute("data-type") === "task-marker";
71
- let listFirstMarkerElement = listLastMarkerElement;
72
- if (isTask) {
73
- listFirstMarkerElement = listLastMarkerElement.previousElementSibling.previousElementSibling
74
- .previousElementSibling as HTMLElement;
75
- }
76
- if (startIndex === listLastMarkerElement.textContent.length) {
77
- // 回车清空列表标记符
78
- if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey &&
79
- listLastMarkerElement.nextElementSibling.textContent.trim() === "") {
80
- if (listFirstMarkerElement.previousElementSibling?.getAttribute("data-type") === "padding") {
81
- listFirstMarkerElement.previousElementSibling.remove();
82
- inputEvent(vditor);
83
- } else {
84
- if (isTask) {
85
- listFirstMarkerElement.remove();
86
- listLastMarkerElement.previousElementSibling.previousElementSibling.remove();
87
- listLastMarkerElement.previousElementSibling.remove();
88
- }
89
- listLastMarkerElement.nextElementSibling.remove();
90
- listLastMarkerElement.remove();
91
- processAfterRender(vditor);
92
- }
93
- event.preventDefault();
94
- return true;
95
- }
96
- // 第一个 marker 后 tab 进行缩进
97
- if (event.key === "Tab") {
98
- if (event.shiftKey) {
99
- if (listFirstMarkerElement.previousElementSibling.getAttribute("data-type") === "padding") {
100
- listFirstMarkerElement.previousElementSibling.remove();
101
- }
102
- } else {
103
- listFirstMarkerElement.insertAdjacentHTML("beforebegin",
104
- `<span data-type="padding">${listFirstMarkerElement.textContent.replace(/\S/g, " ")}</span>`);
105
- }
106
- if (/^\d/.test(listFirstMarkerElement.textContent)) {
107
- listFirstMarkerElement.textContent = listFirstMarkerElement.textContent.replace(/^\d{1,}/, "1");
108
- range.selectNodeContents(listLastMarkerElement.firstChild);
109
- range.collapse(false);
110
- }
111
- inputEvent(vditor);
112
- event.preventDefault();
113
- return true;
114
- }
115
- }
116
- }
117
-
118
- // tab
119
- if (fixTab(vditor, range, event)) {
120
- return true;
121
- }
122
-
123
- const blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
124
- const spanElement = hasClosestByTag(startContainer, "SPAN");
125
- // 回车
126
- if (event.key === "Enter" && !isCtrl(event) && !event.altKey && !event.shiftKey && blockElement) {
127
- let isFirst = false;
128
- const newLineMatch = blockElement.textContent.match(/^\n+/);
129
- if (getSelectPosition(blockElement, vditor.sv.element).start <= (newLineMatch ? newLineMatch[0].length : 0)) {
130
- // 允许段落开始换行
131
- isFirst = true;
132
- }
133
- let newLineText = "\n";
134
- if (spanElement) {
135
- if (spanElement.previousElementSibling?.getAttribute("data-action") === "enter-remove") {
136
- // https://github.com/Vanessa219/vditor/issues/596
137
- spanElement.previousElementSibling.remove();
138
- processAfterRender(vditor);
139
- event.preventDefault();
140
- return true;
141
- } else {
142
- newLineText += processPreviousMarkers(spanElement);
143
- }
144
- }
145
- range.insertNode(document.createTextNode(newLineText));
146
- range.collapse(false);
147
- if (blockElement && blockElement.textContent.trim() !== "" && !isFirst) {
148
- inputEvent(vditor);
149
- } else {
150
- processAfterRender(vditor);
151
- }
152
- event.preventDefault();
153
- return true;
15
+ if (event.key !== "Tab" || isCtrl(event) || event.altKey) {
16
+ return false;
154
17
  }
155
18
 
156
- // 删除后光标前有 newline 的处理
157
- if (event.key === "Backspace" && !isCtrl(event) && !event.altKey && !event.shiftKey) {
158
- if (spanElement && spanElement.previousElementSibling?.getAttribute("data-type") === "newline" &&
159
- getSelectPosition(spanElement, vditor.sv.element, range).start === 1 &&
160
- // 飘号的处理需在 inputEvent 中,否则上下飘号对不齐
161
- spanElement.getAttribute("data-type").indexOf("code-block-") === -1) {
162
- // 光标在每一行的第一个字符后
163
- range.setStart(spanElement, 0);
164
- range.extractContents();
165
- if (spanElement.textContent.trim() !== "") {
166
- inputEvent(vditor);
167
- } else {
168
- processAfterRender(vditor);
169
- }
170
- event.preventDefault();
171
- return true;
172
- }
173
- // 每一段第一个字符前
174
- if (blockElement && getSelectPosition(blockElement, vditor.sv.element, range).start === 0 &&
175
- blockElement.previousElementSibling) {
176
- range.extractContents();
177
- let previousLastElement = blockElement.previousElementSibling.lastElementChild;
178
- if (previousLastElement.getAttribute("data-type") === "newline") {
179
- previousLastElement.remove();
180
- previousLastElement = blockElement.previousElementSibling.lastElementChild;
181
- }
182
- // 场景:末尾无法删除 [```\ntext\n```\n\n]
183
- if (previousLastElement.getAttribute("data-type") !== "newline") {
184
- previousLastElement.insertAdjacentHTML("afterend", blockElement.innerHTML);
185
- blockElement.remove();
186
- }
187
- if (blockElement.textContent.trim() !== "" && !blockElement.previousElementSibling?.querySelector('[data-type="code-block-open-marker"]')) {
188
- inputEvent(vditor);
189
- } else {
190
- if (previousLastElement.getAttribute("data-type") !== "newline") {
191
- // https://github.com/Vanessa219/vditor/issues/597
192
- range.selectNodeContents(previousLastElement.lastChild);
193
- range.collapse(false);
194
- }
195
- processAfterRender(vditor);
196
- }
197
- event.preventDefault();
198
- return true;
199
- }
200
- }
201
- return false;
19
+ const element = vditor.sv.element;
20
+ const start = element.selectionStart;
21
+ const end = element.selectionEnd;
22
+ if (event.shiftKey) {
23
+ const lineStart = element.value.lastIndexOf("\n", start - 1) + 1;
24
+ const indentation = element.value.substring(lineStart, start).match(/^(\t| {1,4})/);
25
+ if (indentation) {
26
+ element.setRangeText("", lineStart, lineStart + indentation[0].length, "end");
27
+ element.setSelectionRange(start - indentation[0].length, end - indentation[0].length);
28
+ }
29
+ } else {
30
+ element.setRangeText(vditor.options.tab, start, end, "end");
31
+ }
32
+ processAfterRender(vditor);
33
+ event.preventDefault();
34
+ return true;
202
35
  };
@@ -2,7 +2,7 @@ import {Constants} from "../constants";
2
2
  import {processAfterRender} from "../ir/process";
3
3
  import {getMarkdown} from "../markdown/getMarkdown";
4
4
  import {mathRender} from "../markdown/mathRender";
5
- import {processAfterRender as processSVAfterRender, processSpinVditorSVDOM} from "../sv/process";
5
+ import {processAfterRender as processSVAfterRender} from "../sv/process";
6
6
  import {setPadding, setTypewriterPosition} from "../ui/initUI";
7
7
  import {getEventName, updateHotkeyTip} from "../util/compatibility";
8
8
  import {highlightToolbar} from "../util/highlightToolbar";
@@ -18,7 +18,6 @@ import {
18
18
  removeCurrentToolbar,
19
19
  showToolbar, toggleSubMenu,
20
20
  } from "./setToolbar";
21
- import {combineFootnote} from "../sv/combineFootnote";
22
21
 
23
22
  export const setEditMode = (vditor: IVditor, type: string, event: Event | string) => {
24
23
  let markdownText;
@@ -114,16 +113,10 @@ export const setEditMode = (vditor: IVditor, type: string, event: Event | string
114
113
 
115
114
  vditor.lute.SetVditorIR(false);
116
115
  vditor.lute.SetVditorWYSIWYG(false);
117
- vditor.lute.SetVditorSV(true);
116
+ vditor.lute.SetVditorSV(false);
118
117
 
119
118
  vditor.currentMode = "sv";
120
- let svHTML = processSpinVditorSVDOM(markdownText, vditor);
121
- if (svHTML === "<div data-block='0'></div>") {
122
- // https://github.com/Vanessa219/vditor/issues/654 SV 模式 Placeholder 显示问题
123
- svHTML = "";
124
- }
125
- vditor.sv.element.innerHTML = svHTML;
126
- combineFootnote(vditor.sv.element)
119
+ vditor.sv.element.value = markdownText;
127
120
  processSVAfterRender(vditor, {
128
121
  enableAddUndoStack: true,
129
122
  enableHint: false,
@@ -6,6 +6,7 @@ import {MenuItem} from "./MenuItem";
6
6
  import {toggleSubMenu} from "./setToolbar";
7
7
  import {hasClosestBlock} from "../util/hasClosest";
8
8
  import {modifyPre} from "../wysiwyg/inlineTag";
9
+ import {processPaste} from "../sv/process";
9
10
 
10
11
  export class Emoji extends MenuItem {
11
12
  public element: HTMLElement;
@@ -44,6 +45,12 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
44
45
  if (btnElement) {
45
46
  event.preventDefault();
46
47
  const value = btnElement.getAttribute("data-value");
48
+ if (vditor.currentMode === "sv") {
49
+ processPaste(vditor, value);
50
+ (this.element.lastElementChild as HTMLElement).style.display = "none";
51
+ execAfterRender(vditor);
52
+ return;
53
+ }
47
54
  const range = getEditorRange(vditor);
48
55
  let html = value;
49
56
  if (vditor.currentMode === "wysiwyg") {
@@ -51,7 +58,7 @@ data-value=":${key}: " data-key=":${key}:" class="vditor-emojis__icon" src="${em
51
58
  } else if (vditor.currentMode === "ir") {
52
59
  html = vditor.lute.SpinVditorIRDOM(value);
53
60
  }
54
- if (value.indexOf(":") > -1 && vditor.currentMode !== "sv") {
61
+ if (value.indexOf(":") > -1) {
55
62
  const tempElement = document.createElement("div");
56
63
  tempElement.innerHTML = html;
57
64
  html = tempElement.firstElementChild.firstElementChild.outerHTML + " ";
@@ -34,7 +34,11 @@ export class Record extends MenuItem {
34
34
  };
35
35
  mediaRecorder.startRecordingNewWavFile();
36
36
  vditor.tip.show(window.VditorI18n.recording);
37
- editorElement.setAttribute("contenteditable", "false");
37
+ if (vditor.currentMode === "sv") {
38
+ vditor.sv.element.disabled = true;
39
+ } else {
40
+ editorElement.setAttribute("contenteditable", "false");
41
+ }
38
42
  this.element.children[0].classList.add("vditor-menu--current");
39
43
  }).catch(() => {
40
44
  vditor.tip.show(window.VditorI18n["record-tip"]);
@@ -51,7 +55,11 @@ export class Record extends MenuItem {
51
55
  this.element.children[0].classList.remove("vditor-menu--current");
52
56
  } else {
53
57
  vditor.tip.show(window.VditorI18n.recording);
54
- editorElement.setAttribute("contenteditable", "false");
58
+ if (vditor.currentMode === "sv") {
59
+ vditor.sv.element.disabled = true;
60
+ } else {
61
+ editorElement.setAttribute("contenteditable", "false");
62
+ }
55
63
  mediaRecorder.startRecordingNewWavFile();
56
64
  this.element.children[0].classList.add("vditor-menu--current");
57
65
  }
@@ -181,6 +181,9 @@ export class Toolbar {
181
181
  if (!menuItemObj) {
182
182
  return;
183
183
  }
184
+ menuItemObj.element.querySelectorAll("button").forEach((button) => {
185
+ button.setAttribute("type", "button");
186
+ });
184
187
  let key = menuItem.name;
185
188
  if (key === "br" || key === "|") {
186
189
  key = key + index;
@@ -146,9 +146,13 @@ export const setTypewriterPosition = (vditor: IVditor) => {
146
146
  if (vditor.element.classList.contains("vditor--fullscreen")) {
147
147
  height = window.innerHeight;
148
148
  }
149
- // 由于 Firefox padding-bottom bug,只能使用 :after
150
- vditor[vditor.currentMode].element.style.setProperty("--editor-bottom",
151
- ((height - vditor.toolbar.element.offsetHeight) / 2) + "px");
149
+ const editorBottom = ((height - vditor.toolbar.element.offsetHeight) / 2) + "px";
150
+ if (vditor.currentMode === "sv") {
151
+ vditor.sv.element.style.paddingBottom = editorBottom;
152
+ } else {
153
+ // 由于 Firefox padding-bottom bug,只能使用 :after
154
+ vditor[vditor.currentMode].element.style.setProperty("--editor-bottom", editorBottom);
155
+ }
152
156
  };
153
157
 
154
158
  let resizeCb: () => void;
@@ -52,7 +52,8 @@ class Undo {
52
52
  }
53
53
 
54
54
  public undo(vditor: IVditor) {
55
- if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
55
+ if (vditor.currentMode === "sv" ? vditor.sv.element.disabled :
56
+ vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
56
57
  return;
57
58
  }
58
59
  if (this[vditor.currentMode].undoStack.length < 2) {
@@ -70,7 +71,8 @@ class Undo {
70
71
  }
71
72
 
72
73
  public redo(vditor: IVditor) {
73
- if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
74
+ if (vditor.currentMode === "sv" ? vditor.sv.element.disabled :
75
+ vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
74
76
  return;
75
77
  }
76
78
  const state = this[vditor.currentMode].redoStack.pop();
@@ -82,7 +84,7 @@ class Undo {
82
84
  }
83
85
 
84
86
  public recordFirstPosition(vditor: IVditor, event: KeyboardEvent) {
85
- if (getSelection().rangeCount === 0) {
87
+ if (vditor.currentMode !== "sv" && getSelection().rangeCount === 0) {
86
88
  return;
87
89
  }
88
90
  if (this[vditor.currentMode].undoStack.length !== 1 || this[vditor.currentMode].undoStack[0].length === 0 ||
@@ -147,8 +149,15 @@ class Undo {
147
149
  }
148
150
 
149
151
  this[vditor.currentMode].lastText = text;
150
- vditor[vditor.currentMode].element.innerHTML = text;
151
- if (vditor.currentMode !== "sv") {
152
+ if (vditor.currentMode === "sv") {
153
+ const caretIndex = text.indexOf("<wbr>");
154
+ vditor.sv.element.value = text.replace("<wbr>", "");
155
+ vditor.sv.element.focus();
156
+ if (caretIndex > -1) {
157
+ vditor.sv.element.setSelectionRange(caretIndex, caretIndex);
158
+ }
159
+ } else {
160
+ vditor[vditor.currentMode].element.innerHTML = text;
152
161
  vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview`)
153
162
  .forEach((blockElement: HTMLElement) => {
154
163
  if (blockElement.parentElement.querySelector(".language-echarts")) {
@@ -163,17 +172,16 @@ class Undo {
163
172
  .forEach((blockElement: HTMLElement) => {
164
173
  processCodeRender(blockElement, vditor);
165
174
  });
166
- }
167
-
168
- if (!vditor[vditor.currentMode].element.querySelector("wbr")) {
169
- // Safari 第一次输入没有光标,需手动定位到结尾
170
- const range = getSelection().getRangeAt(0);
171
- range.setEndBefore(vditor[vditor.currentMode].element);
172
- range.collapse(false);
173
- } else {
174
- setRangeByWbr(
175
- vditor[vditor.currentMode].element, vditor[vditor.currentMode].element.ownerDocument.createRange());
176
- scrollCenter(vditor);
175
+ if (!vditor[vditor.currentMode].element.querySelector("wbr")) {
176
+ // Safari 第一次输入没有光标,需手动定位到结尾
177
+ const range = getSelection().getRangeAt(0);
178
+ range.setEndBefore(vditor[vditor.currentMode].element);
179
+ range.collapse(false);
180
+ } else {
181
+ setRangeByWbr(
182
+ vditor[vditor.currentMode].element, vditor[vditor.currentMode].element.ownerDocument.createRange());
183
+ scrollCenter(vditor);
184
+ }
177
185
  }
178
186
 
179
187
  renderToc(vditor);
@@ -183,7 +191,9 @@ class Undo {
183
191
  enableHint: false,
184
192
  enableInput: true,
185
193
  });
186
- highlightToolbar(vditor);
194
+ if (vditor.currentMode !== "sv") {
195
+ highlightToolbar(vditor);
196
+ }
187
197
 
188
198
  vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
189
199
  .forEach((item: HTMLElement) => {
@@ -225,6 +235,11 @@ class Undo {
225
235
  }
226
236
 
227
237
  private addCaret(vditor: IVditor, setFocus = false) {
238
+ if (vditor.currentMode === "sv") {
239
+ const element = vditor.sv.element;
240
+ return element.value.substring(0, element.selectionStart) + "<wbr>" +
241
+ element.value.substring(element.selectionStart);
242
+ }
228
243
  let cloneRange: Range;
229
244
  if (getSelection().rangeCount !== 0 && !vditor[vditor.currentMode].element.querySelector("wbr")) {
230
245
  const range = getSelection().getRangeAt(0);
@@ -1,11 +1,14 @@
1
- import { getEditorRange, setSelectionFocus } from "../util/selection";
2
- import { getElement } from "./getElement";
3
- import { setHeaders } from "./setHeaders";
1
+ import {getEditorRange, setSelectionFocus} from "../util/selection";
2
+ import {processAfterRender as processSVAfterRender, processPaste} from "../sv/process";
3
+ import {getElement} from "./getElement";
4
+ import {setHeaders} from "./setHeaders";
4
5
 
5
6
  class Upload {
6
7
  public element: HTMLElement;
7
8
  public isUploading: boolean;
8
9
  public range: Range;
10
+ public selectionEnd: number;
11
+ public selectionStart: number;
9
12
 
10
13
  constructor() {
11
14
  this.isUploading = false;
@@ -146,9 +149,25 @@ const genUploadedLabel = (responseText: string, vditor: IVditor) => {
146
149
  }
147
150
  }
148
151
  });
149
- setSelectionFocus(vditor.upload.range);
150
- document.execCommand("insertHTML", false, succFileText);
151
- vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
152
+ if (!succFileText) {
153
+ if (vditor.currentMode === "sv") {
154
+ vditor.sv.element.setSelectionRange(vditor.upload.selectionStart, vditor.upload.selectionEnd);
155
+ } else {
156
+ setSelectionFocus(vditor.upload.range);
157
+ }
158
+ return;
159
+ }
160
+ if (vditor.currentMode === "sv") {
161
+ vditor.sv.element.setSelectionRange(vditor.upload.selectionStart, vditor.upload.selectionEnd);
162
+ processPaste(vditor, succFileText);
163
+ processSVAfterRender(vditor);
164
+ vditor.upload.selectionStart = vditor.sv.element.selectionStart;
165
+ vditor.upload.selectionEnd = vditor.sv.element.selectionEnd;
166
+ } else {
167
+ setSelectionFocus(vditor.upload.range);
168
+ document.execCommand("insertHTML", false, succFileText);
169
+ vditor.upload.range = getSelection().getRangeAt(0).cloneRange();
170
+ }
152
171
  };
153
172
 
154
173
  const uploadFiles =
@@ -197,7 +216,12 @@ const uploadFiles =
197
216
  }
198
217
  const editorElement = getElement(vditor);
199
218
 
200
- vditor.upload.range = getEditorRange(vditor);
219
+ if (vditor.currentMode === "sv") {
220
+ vditor.upload.selectionStart = vditor.sv.element.selectionStart;
221
+ vditor.upload.selectionEnd = vditor.sv.element.selectionEnd;
222
+ } else {
223
+ vditor.upload.range = getEditorRange(vditor);
224
+ }
201
225
 
202
226
  const validateResult = validateFile(vditor, fileList);
203
227
  if (validateResult.length === 0) {
@@ -229,11 +253,19 @@ const uploadFiles =
229
253
  }
230
254
  setHeaders(vditor, xhr);
231
255
  vditor.upload.isUploading = true;
232
- editorElement.setAttribute("contenteditable", "false");
256
+ if (vditor.currentMode === "sv") {
257
+ vditor.sv.element.disabled = true;
258
+ } else {
259
+ editorElement.setAttribute("contenteditable", "false");
260
+ }
233
261
  xhr.onreadystatechange = () => {
234
262
  if (xhr.readyState === XMLHttpRequest.DONE) {
235
263
  vditor.upload.isUploading = false;
236
- editorElement.setAttribute("contenteditable", "true");
264
+ if (vditor.currentMode === "sv") {
265
+ vditor.sv.element.disabled = false;
266
+ } else {
267
+ editorElement.setAttribute("contenteditable", "true");
268
+ }
237
269
  if (xhr.status >= 200 && xhr.status < 300) {
238
270
  if (vditor.options.upload.success) {
239
271
  vditor.options.upload.success(editorElement, xhr.responseText);
@@ -270,4 +302,4 @@ const uploadFiles =
270
302
  xhr.send(formData);
271
303
  };
272
304
 
273
- export { Upload, uploadFiles };
305
+ export {Upload, uploadFiles};
@@ -45,18 +45,22 @@ export const blurEvent = (vditor: IVditor, editorElement: HTMLElement) => {
45
45
  if (expandElement) {
46
46
  expandElement.classList.remove("vditor-ir__node--expand");
47
47
  }
48
- } else if (vditor.currentMode === "wysiwyg" &&
49
- !vditor.wysiwyg.selectPopover.contains(event.relatedTarget as HTMLElement)) {
50
- vditor.wysiwyg.hideComment();
48
+ vditor.ir.range = getEditorRange(vditor);
49
+ } else if (vditor.currentMode === "wysiwyg") {
50
+ if (!vditor.wysiwyg.selectPopover.contains(event.relatedTarget as HTMLElement)) {
51
+ vditor.wysiwyg.hideComment();
52
+ }
53
+ vditor.wysiwyg.range = getEditorRange(vditor);
51
54
  }
52
- vditor[vditor.currentMode].range = getEditorRange(vditor);
53
55
  if (vditor.options.blur) {
54
56
  vditor.options.blur(getMarkdown(vditor));
55
57
  }
56
58
  });
57
59
  };
58
60
 
59
- export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
61
+ export const dropEvent = (vditor: IVditor, editorElement: HTMLElement, pasteCode = (code: string) => {
62
+ document.execCommand("insertHTML", false, code);
63
+ }) => {
60
64
  editorElement.addEventListener("dragstart", (event) => {
61
65
  // 选中编辑器中的文字进行拖拽
62
66
  event.dataTransfer.setData(Constants.DROP_EDITOR, Constants.DROP_EDITOR);
@@ -69,9 +73,7 @@ export const dropEvent = (vditor: IVditor, editorElement: HTMLElement) => {
69
73
  } else if (event.dataTransfer.types.includes("Files") || event.dataTransfer.types.includes("text/html")) {
70
74
  // 外部文件拖入编辑器中或者编辑器内选中文字拖拽
71
75
  paste(vditor, event, {
72
- pasteCode: (code: string) => {
73
- document.execCommand("insertHTML", false, code);
74
- },
76
+ pasteCode,
75
77
  });
76
78
  }
77
79
  });
@@ -102,7 +104,14 @@ export const scrollCenter = (vditor: IVditor) => {
102
104
  return;
103
105
  }
104
106
  const editorElement = vditor[vditor.currentMode].element;
105
- const cursorTop = getCursorPosition(editorElement).top;
107
+ let cursorTop: number;
108
+ if (vditor.currentMode === "sv") {
109
+ const lineHeight = parseInt(getComputedStyle(vditor.sv.element).lineHeight, 10) || 22;
110
+ cursorTop = (vditor.sv.element.value.substring(0, vditor.sv.element.selectionStart).split("\n").length - 1) *
111
+ lineHeight;
112
+ } else {
113
+ cursorTop = getCursorPosition(editorElement).top;
114
+ }
106
115
  if (vditor.options.height === "auto" && !vditor.element.classList.contains("vditor--fullscreen")) {
107
116
  window.scrollTo(window.scrollX,
108
117
  cursorTop + vditor.element.offsetTop + vditor.toolbar.element.offsetHeight - window.innerHeight / 2 + 10);