@8btc/mditor 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +16 -8
- package/dist/index.js +405 -98
- package/dist/index.min.js +1 -1
- package/dist/method.d.ts +10 -10
- package/dist/method.js +3 -3
- package/dist/method.min.js +1 -1
- package/dist/ts/util/attachLineNumbers.d.ts +26 -4
- package/dist/types/index.d.ts +13 -1
- package/package.json +1 -1
- package/src/assets/less/_line-number.less +4 -4
- package/src/index.ts +17 -0
- package/src/ts/ir/input.ts +180 -66
- package/src/ts/ir/process.ts +4 -2
- package/src/ts/sv/inputEvent.ts +169 -64
- package/src/ts/sv/process.ts +4 -2
- package/src/ts/util/attachLineNumbers.ts +32 -6
- package/src/ts/wysiwyg/afterRenderEvent.ts +7 -2
- package/src/ts/wysiwyg/index.ts +494 -262
- package/src/ts/wysiwyg/input.ts +148 -64
- package/src/ts/wysiwyg/renderDomByMd.ts +26 -11
package/src/ts/wysiwyg/index.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { hidePanel } from "../toolbar/setToolbar";
|
|
|
3
3
|
import { isCtrl, isFirefox } from "../util/compatibility";
|
|
4
4
|
import {
|
|
5
5
|
blurEvent,
|
|
6
|
-
copyEvent,
|
|
6
|
+
copyEvent,
|
|
7
|
+
cutEvent,
|
|
8
|
+
dblclickEvent,
|
|
7
9
|
dropEvent,
|
|
8
10
|
focusEvent,
|
|
9
11
|
hotkeyEvent,
|
|
@@ -12,19 +14,26 @@ import {
|
|
|
12
14
|
} from "../util/editorCommonEvent";
|
|
13
15
|
import { isHeadingMD, isHrMD, paste } from "../util/fixBrowserBehavior";
|
|
14
16
|
import {
|
|
15
|
-
hasClosestBlock,
|
|
16
|
-
|
|
17
|
+
hasClosestBlock,
|
|
18
|
+
hasClosestByAttribute,
|
|
19
|
+
hasClosestByClassName,
|
|
20
|
+
hasClosestByMatchTag,
|
|
17
21
|
} from "../util/hasClosest";
|
|
18
22
|
import { hasClosestByHeadings } from "../util/hasClosestByHeadings";
|
|
19
23
|
import {
|
|
20
24
|
getCursorPosition,
|
|
21
25
|
getEditorRange,
|
|
22
26
|
getSelectPosition,
|
|
23
|
-
setRangeByWbr,
|
|
24
|
-
|
|
27
|
+
setRangeByWbr,
|
|
28
|
+
setSelectionFocus,
|
|
29
|
+
} from "../util/selection";
|
|
25
30
|
import { clickToc, renderToc } from "../util/toc";
|
|
26
31
|
import { afterRenderEvent } from "./afterRenderEvent";
|
|
27
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
genImagePopover,
|
|
34
|
+
genLinkRefPopover,
|
|
35
|
+
highlightToolbarWYSIWYG,
|
|
36
|
+
} from "./highlightToolbarWYSIWYG";
|
|
28
37
|
import { getRenderElementNextNode, modifyPre } from "./inlineTag";
|
|
29
38
|
import { input } from "./input";
|
|
30
39
|
import { showCode } from "./showCode";
|
|
@@ -60,7 +69,8 @@ class WYSIWYG {
|
|
|
60
69
|
</div>`;
|
|
61
70
|
|
|
62
71
|
this.element = divElement.firstElementChild as HTMLPreElement;
|
|
63
|
-
this.popover = divElement.firstElementChild
|
|
72
|
+
this.popover = divElement.firstElementChild
|
|
73
|
+
.nextElementSibling as HTMLDivElement;
|
|
64
74
|
this.selectPopover = divElement.lastElementChild as HTMLDivElement;
|
|
65
75
|
|
|
66
76
|
this.bindEvent(vditor);
|
|
@@ -84,64 +94,104 @@ class WYSIWYG {
|
|
|
84
94
|
let blockEndElement: HTMLElement;
|
|
85
95
|
let removeStart = false;
|
|
86
96
|
let removeEnd = false;
|
|
87
|
-
contents.childNodes.forEach(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
contents.childNodes.forEach(
|
|
98
|
+
(item: HTMLElement, index: number) => {
|
|
99
|
+
let wrap = false;
|
|
100
|
+
if (item.nodeType === 3) {
|
|
101
|
+
wrap = true;
|
|
102
|
+
} else if (!item.classList.contains("vditor-comment")) {
|
|
103
|
+
wrap = true;
|
|
104
|
+
} else if (item.classList.contains("vditor-comment")) {
|
|
105
|
+
item.setAttribute(
|
|
106
|
+
"data-cmtids",
|
|
107
|
+
item.getAttribute("data-cmtids") + " " + id
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
if (wrap) {
|
|
111
|
+
if (
|
|
112
|
+
item.nodeType !== 3 &&
|
|
113
|
+
item.getAttribute("data-block") === "0" &&
|
|
114
|
+
index === 0 &&
|
|
115
|
+
rangeClone.startOffset > 0
|
|
116
|
+
) {
|
|
117
|
+
item.innerHTML = `<span class="vditor-comment" data-cmtids="${id}">${item.innerHTML}</span>`;
|
|
118
|
+
blockStartElement = item;
|
|
119
|
+
} else if (
|
|
120
|
+
item.nodeType !== 3 &&
|
|
121
|
+
item.getAttribute("data-block") === "0" &&
|
|
122
|
+
index === contents.childNodes.length - 1 &&
|
|
123
|
+
rangeClone.endOffset <
|
|
124
|
+
rangeClone.endContainer.textContent.length
|
|
125
|
+
) {
|
|
126
|
+
item.innerHTML = `<span class="vditor-comment" data-cmtids="${id}">${item.innerHTML}</span>`;
|
|
127
|
+
blockEndElement = item;
|
|
128
|
+
} else if (
|
|
129
|
+
item.nodeType !== 3 &&
|
|
130
|
+
item.getAttribute("data-block") === "0"
|
|
131
|
+
) {
|
|
132
|
+
if (index === 0) {
|
|
133
|
+
removeStart = true;
|
|
134
|
+
} else if (
|
|
135
|
+
index ===
|
|
136
|
+
contents.childNodes.length - 1
|
|
137
|
+
) {
|
|
138
|
+
removeEnd = true;
|
|
139
|
+
}
|
|
140
|
+
item.innerHTML = `<span class="vditor-comment" data-cmtids="${id}">${item.innerHTML}</span>`;
|
|
141
|
+
} else {
|
|
142
|
+
const commentElement =
|
|
143
|
+
document.createElement("span");
|
|
144
|
+
commentElement.classList.add("vditor-comment");
|
|
145
|
+
commentElement.setAttribute("data-cmtids", id);
|
|
146
|
+
item.parentNode.insertBefore(
|
|
147
|
+
commentElement,
|
|
148
|
+
item
|
|
149
|
+
);
|
|
150
|
+
commentElement.appendChild(item);
|
|
113
151
|
}
|
|
114
|
-
item.innerHTML =
|
|
115
|
-
`<span class="vditor-comment" data-cmtids="${id}">${item.innerHTML}</span>`;
|
|
116
|
-
} else {
|
|
117
|
-
const commentElement = document.createElement("span");
|
|
118
|
-
commentElement.classList.add("vditor-comment");
|
|
119
|
-
commentElement.setAttribute("data-cmtids", id);
|
|
120
|
-
item.parentNode.insertBefore(commentElement, item);
|
|
121
|
-
commentElement.appendChild(item);
|
|
122
152
|
}
|
|
123
153
|
}
|
|
124
|
-
|
|
154
|
+
);
|
|
125
155
|
const startElement = hasClosestBlock(rangeClone.startContainer);
|
|
126
156
|
if (startElement) {
|
|
127
157
|
if (blockStartElement) {
|
|
128
|
-
startElement.insertAdjacentHTML(
|
|
158
|
+
startElement.insertAdjacentHTML(
|
|
159
|
+
"beforeend",
|
|
160
|
+
blockStartElement.innerHTML
|
|
161
|
+
);
|
|
129
162
|
blockStartElement.remove();
|
|
130
|
-
} else if (
|
|
163
|
+
} else if (
|
|
164
|
+
startElement.textContent
|
|
165
|
+
.trim()
|
|
166
|
+
.replace(Constants.ZWSP, "") === "" &&
|
|
167
|
+
removeStart
|
|
168
|
+
) {
|
|
131
169
|
startElement.remove();
|
|
132
170
|
}
|
|
133
171
|
}
|
|
134
172
|
const endElement = hasClosestBlock(rangeClone.endContainer);
|
|
135
173
|
if (endElement) {
|
|
136
174
|
if (blockEndElement) {
|
|
137
|
-
endElement.insertAdjacentHTML(
|
|
175
|
+
endElement.insertAdjacentHTML(
|
|
176
|
+
"afterbegin",
|
|
177
|
+
blockEndElement.innerHTML
|
|
178
|
+
);
|
|
138
179
|
blockEndElement.remove();
|
|
139
|
-
} else if (
|
|
180
|
+
} else if (
|
|
181
|
+
endElement.textContent
|
|
182
|
+
.trim()
|
|
183
|
+
.replace(Constants.ZWSP, "") === "" &&
|
|
184
|
+
removeEnd
|
|
185
|
+
) {
|
|
140
186
|
endElement.remove();
|
|
141
187
|
}
|
|
142
188
|
}
|
|
143
189
|
range.insertNode(contents);
|
|
144
|
-
vditor.options.comment.add(
|
|
190
|
+
vditor.options.comment.add(
|
|
191
|
+
id,
|
|
192
|
+
range.toString(),
|
|
193
|
+
this.getComments(vditor, true)
|
|
194
|
+
);
|
|
145
195
|
afterRenderEvent(vditor, {
|
|
146
196
|
enableAddUndoStack: true,
|
|
147
197
|
enableHint: false,
|
|
@@ -156,8 +206,9 @@ class WYSIWYG {
|
|
|
156
206
|
if (vditor.currentMode === "wysiwyg" && vditor.options.comment.enable) {
|
|
157
207
|
this.commentIds = [];
|
|
158
208
|
this.element.querySelectorAll(".vditor-comment").forEach((item) => {
|
|
159
|
-
this.commentIds =
|
|
160
|
-
|
|
209
|
+
this.commentIds = this.commentIds.concat(
|
|
210
|
+
item.getAttribute("data-cmtids").split(" ")
|
|
211
|
+
);
|
|
161
212
|
});
|
|
162
213
|
this.commentIds = Array.from(new Set(this.commentIds));
|
|
163
214
|
|
|
@@ -166,8 +217,11 @@ class WYSIWYG {
|
|
|
166
217
|
this.commentIds.forEach((id) => {
|
|
167
218
|
comments.push({
|
|
168
219
|
id,
|
|
169
|
-
top:
|
|
170
|
-
|
|
220
|
+
top: (
|
|
221
|
+
this.element.querySelector(
|
|
222
|
+
`.vditor-comment[data-cmtids="${id}"]`
|
|
223
|
+
) as HTMLElement
|
|
224
|
+
).offsetTop,
|
|
171
225
|
});
|
|
172
226
|
});
|
|
173
227
|
return comments;
|
|
@@ -182,7 +236,11 @@ class WYSIWYG {
|
|
|
182
236
|
const s = new Set(b);
|
|
183
237
|
return a.filter((x) => !s.has(x));
|
|
184
238
|
};
|
|
185
|
-
if (
|
|
239
|
+
if (
|
|
240
|
+
vditor.currentMode === "wysiwyg" &&
|
|
241
|
+
vditor.options.comment.enable &&
|
|
242
|
+
vditor.wysiwyg.commentIds.length > 0
|
|
243
|
+
) {
|
|
186
244
|
const oldIds = JSON.parse(JSON.stringify(this.commentIds));
|
|
187
245
|
this.getComments(vditor);
|
|
188
246
|
const removedIds = difference(oldIds, this.commentIds);
|
|
@@ -194,7 +252,10 @@ class WYSIWYG {
|
|
|
194
252
|
|
|
195
253
|
public showComment() {
|
|
196
254
|
const position = getCursorPosition(this.element);
|
|
197
|
-
this.selectPopover.setAttribute(
|
|
255
|
+
this.selectPopover.setAttribute(
|
|
256
|
+
"style",
|
|
257
|
+
`left:${position.left}px;display:block;top:${Math.max(-8, position.top - 21)}px`
|
|
258
|
+
);
|
|
198
259
|
}
|
|
199
260
|
|
|
200
261
|
public hideComment() {
|
|
@@ -221,7 +282,11 @@ class WYSIWYG {
|
|
|
221
282
|
|
|
222
283
|
const codeElement = hasClosestByMatchTag(range.startContainer, "CODE");
|
|
223
284
|
const codeEndElement = hasClosestByMatchTag(range.endContainer, "CODE");
|
|
224
|
-
if (
|
|
285
|
+
if (
|
|
286
|
+
codeElement &&
|
|
287
|
+
codeEndElement &&
|
|
288
|
+
codeEndElement.isSameNode(codeElement)
|
|
289
|
+
) {
|
|
225
290
|
let codeText = "";
|
|
226
291
|
if (codeElement.parentElement.tagName === "PRE") {
|
|
227
292
|
codeText = range.toString();
|
|
@@ -240,20 +305,33 @@ class WYSIWYG {
|
|
|
240
305
|
if (aTitle) {
|
|
241
306
|
aTitle = ` "${aTitle}"`;
|
|
242
307
|
}
|
|
243
|
-
event.clipboardData.setData(
|
|
244
|
-
|
|
308
|
+
event.clipboardData.setData(
|
|
309
|
+
"text/plain",
|
|
310
|
+
`[${range.toString()}](${aElement.getAttribute("href")}${aTitle})`
|
|
311
|
+
);
|
|
245
312
|
event.clipboardData.setData("text/html", "");
|
|
246
313
|
return;
|
|
247
314
|
}
|
|
248
315
|
|
|
249
316
|
// 数学公式选区:仅复制可见文本
|
|
250
|
-
const startPreview = hasClosestByClassName(
|
|
251
|
-
|
|
317
|
+
const startPreview = hasClosestByClassName(
|
|
318
|
+
range.startContainer,
|
|
319
|
+
"vditor-wysiwyg__preview"
|
|
320
|
+
) as HTMLElement;
|
|
321
|
+
const endPreview = hasClosestByClassName(
|
|
322
|
+
range.endContainer,
|
|
323
|
+
"vditor-wysiwyg__preview"
|
|
324
|
+
) as HTMLElement;
|
|
252
325
|
const isMathPreview = (el: HTMLElement) => {
|
|
253
326
|
const first = el.firstElementChild as HTMLElement | null;
|
|
254
327
|
return !!first && first.classList.contains("language-math");
|
|
255
328
|
};
|
|
256
|
-
if (
|
|
329
|
+
if (
|
|
330
|
+
startPreview &&
|
|
331
|
+
endPreview &&
|
|
332
|
+
startPreview.isSameNode(endPreview) &&
|
|
333
|
+
isMathPreview(startPreview)
|
|
334
|
+
) {
|
|
257
335
|
event.clipboardData.setData("text/plain", range.toString());
|
|
258
336
|
event.clipboardData.setData("text/html", "");
|
|
259
337
|
return;
|
|
@@ -262,77 +340,122 @@ class WYSIWYG {
|
|
|
262
340
|
// 默认:转换为 Markdown 文本
|
|
263
341
|
const tempElement = document.createElement("div");
|
|
264
342
|
tempElement.appendChild(range.cloneContents());
|
|
265
|
-
event.clipboardData.setData(
|
|
343
|
+
event.clipboardData.setData(
|
|
344
|
+
"text/plain",
|
|
345
|
+
vditor.lute.VditorDOM2Md(tempElement.innerHTML).trim()
|
|
346
|
+
);
|
|
266
347
|
event.clipboardData.setData("text/html", "");
|
|
267
348
|
}
|
|
268
349
|
|
|
269
350
|
private bindEvent(vditor: IVditor) {
|
|
270
351
|
this.unbindListener();
|
|
271
|
-
window.addEventListener(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
352
|
+
window.addEventListener(
|
|
353
|
+
"scroll",
|
|
354
|
+
(this.scrollListener = () => {
|
|
355
|
+
hidePanel(vditor, ["hint"]);
|
|
356
|
+
if (
|
|
357
|
+
this.popover.style.display !== "block" ||
|
|
358
|
+
this.selectPopover.style.display !== "block"
|
|
359
|
+
) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const top = parseInt(this.popover.getAttribute("data-top"), 10);
|
|
363
|
+
if (vditor.options.height !== "auto") {
|
|
364
|
+
if (
|
|
365
|
+
vditor.options.toolbarConfig.pin &&
|
|
366
|
+
vditor.toolbar.element.getBoundingClientRect().top === 0
|
|
367
|
+
) {
|
|
368
|
+
const popoverTop =
|
|
369
|
+
Math.max(
|
|
370
|
+
window.scrollY - vditor.element.offsetTop - 8,
|
|
371
|
+
Math.min(
|
|
372
|
+
top - vditor.wysiwyg.element.scrollTop,
|
|
373
|
+
this.element.clientHeight - 21
|
|
374
|
+
)
|
|
375
|
+
) + "px";
|
|
376
|
+
if (this.popover.style.display === "block") {
|
|
377
|
+
this.popover.style.top = popoverTop;
|
|
378
|
+
}
|
|
379
|
+
if (this.selectPopover.style.display === "block") {
|
|
380
|
+
this.selectPopover.style.top = popoverTop;
|
|
381
|
+
}
|
|
286
382
|
}
|
|
383
|
+
return;
|
|
384
|
+
} else if (!vditor.options.toolbarConfig.pin) {
|
|
385
|
+
return;
|
|
287
386
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
387
|
+
const popoverTop1 =
|
|
388
|
+
Math.max(
|
|
389
|
+
top,
|
|
390
|
+
window.scrollY - vditor.element.offsetTop - 8
|
|
391
|
+
) + "px";
|
|
392
|
+
if (this.popover.style.display === "block") {
|
|
393
|
+
this.popover.style.top = popoverTop1;
|
|
394
|
+
}
|
|
395
|
+
if (this.selectPopover.style.display === "block") {
|
|
396
|
+
this.selectPopover.style.top = popoverTop1;
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
);
|
|
300
400
|
|
|
301
401
|
this.element.addEventListener("scroll", () => {
|
|
302
402
|
hidePanel(vditor, ["hint"]);
|
|
303
|
-
if (
|
|
403
|
+
if (
|
|
404
|
+
vditor.options.comment &&
|
|
405
|
+
vditor.options.comment.enable &&
|
|
406
|
+
vditor.options.comment.scroll
|
|
407
|
+
) {
|
|
304
408
|
vditor.options.comment.scroll(vditor.wysiwyg.element.scrollTop);
|
|
305
409
|
}
|
|
306
410
|
if (this.popover.style.display !== "block") {
|
|
307
411
|
return;
|
|
308
412
|
}
|
|
309
|
-
const top =
|
|
413
|
+
const top =
|
|
414
|
+
parseInt(this.popover.getAttribute("data-top"), 10) -
|
|
415
|
+
vditor.wysiwyg.element.scrollTop;
|
|
310
416
|
let max = -8;
|
|
311
|
-
if (
|
|
417
|
+
if (
|
|
418
|
+
vditor.options.toolbarConfig.pin &&
|
|
419
|
+
vditor.toolbar.element.getBoundingClientRect().top === 0
|
|
420
|
+
) {
|
|
312
421
|
max = window.scrollY - vditor.element.offsetTop + max;
|
|
313
422
|
}
|
|
314
|
-
const topPx =
|
|
423
|
+
const topPx =
|
|
424
|
+
Math.max(max, Math.min(top, this.element.clientHeight - 21)) +
|
|
425
|
+
"px";
|
|
315
426
|
this.popover.style.top = topPx;
|
|
316
427
|
this.selectPopover.style.top = topPx;
|
|
317
428
|
});
|
|
318
429
|
|
|
319
|
-
this.element.addEventListener(
|
|
320
|
-
paste
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
blockElement
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
430
|
+
this.element.addEventListener(
|
|
431
|
+
"paste",
|
|
432
|
+
(event: ClipboardEvent & { target: HTMLElement }) => {
|
|
433
|
+
paste(vditor, event, {
|
|
434
|
+
pasteCode: (code: string) => {
|
|
435
|
+
const range = getEditorRange(vditor);
|
|
436
|
+
const node = document.createElement("template");
|
|
437
|
+
node.innerHTML = code;
|
|
438
|
+
range.insertNode(node.content.cloneNode(true));
|
|
439
|
+
const blockElement = hasClosestByAttribute(
|
|
440
|
+
range.startContainer,
|
|
441
|
+
"data-block",
|
|
442
|
+
"0"
|
|
443
|
+
);
|
|
444
|
+
if (blockElement) {
|
|
445
|
+
blockElement.outerHTML = vditor.lute.SpinVditorDOM(
|
|
446
|
+
blockElement.outerHTML
|
|
447
|
+
);
|
|
448
|
+
} else {
|
|
449
|
+
vditor.wysiwyg.element.innerHTML =
|
|
450
|
+
vditor.lute.SpinVditorDOM(
|
|
451
|
+
vditor.wysiwyg.element.innerHTML
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
setRangeByWbr(vditor.wysiwyg.element, range);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
);
|
|
336
459
|
|
|
337
460
|
// 中文处理
|
|
338
461
|
this.element.addEventListener("compositionstart", () => {
|
|
@@ -340,7 +463,9 @@ class WYSIWYG {
|
|
|
340
463
|
});
|
|
341
464
|
|
|
342
465
|
this.element.addEventListener("compositionend", (event: InputEvent) => {
|
|
343
|
-
const headingElement = hasClosestByHeadings(
|
|
466
|
+
const headingElement = hasClosestByHeadings(
|
|
467
|
+
getSelection().getRangeAt(0).startContainer
|
|
468
|
+
);
|
|
344
469
|
if (headingElement && headingElement.textContent === "") {
|
|
345
470
|
// heading 为空删除 https://github.com/Vanessa219/vditor/issues/150
|
|
346
471
|
renderToc(vditor);
|
|
@@ -353,7 +478,10 @@ class WYSIWYG {
|
|
|
353
478
|
});
|
|
354
479
|
|
|
355
480
|
this.element.addEventListener("input", (event: InputEvent) => {
|
|
356
|
-
if (
|
|
481
|
+
if (
|
|
482
|
+
event.inputType === "deleteByDrag" ||
|
|
483
|
+
event.inputType === "insertFromDrop"
|
|
484
|
+
) {
|
|
357
485
|
// https://github.com/Vanessa219/vditor/issues/801 编辑器内容拖拽问题
|
|
358
486
|
return;
|
|
359
487
|
}
|
|
@@ -362,7 +490,12 @@ class WYSIWYG {
|
|
|
362
490
|
afterRenderEvent(vditor);
|
|
363
491
|
return;
|
|
364
492
|
}
|
|
365
|
-
if (
|
|
493
|
+
if (
|
|
494
|
+
this.composingLock ||
|
|
495
|
+
event.data === "‘" ||
|
|
496
|
+
event.data === "“" ||
|
|
497
|
+
event.data === "《"
|
|
498
|
+
) {
|
|
366
499
|
afterRenderEvent(vditor);
|
|
367
500
|
return;
|
|
368
501
|
}
|
|
@@ -378,14 +511,27 @@ class WYSIWYG {
|
|
|
378
511
|
}
|
|
379
512
|
|
|
380
513
|
// 前后空格处理
|
|
381
|
-
const startOffset = getSelectPosition(
|
|
514
|
+
const startOffset = getSelectPosition(
|
|
515
|
+
blockElement,
|
|
516
|
+
vditor.wysiwyg.element,
|
|
517
|
+
range
|
|
518
|
+
).start;
|
|
382
519
|
|
|
383
520
|
// 开始可以输入空格
|
|
384
521
|
let startSpace = true;
|
|
385
|
-
for (
|
|
386
|
-
|
|
522
|
+
for (
|
|
523
|
+
let i = startOffset - 1;
|
|
524
|
+
i >
|
|
525
|
+
blockElement.textContent
|
|
526
|
+
.substr(0, startOffset)
|
|
527
|
+
.lastIndexOf("\n");
|
|
528
|
+
i--
|
|
529
|
+
) {
|
|
530
|
+
if (
|
|
531
|
+
blockElement.textContent.charAt(i) !== " " &&
|
|
387
532
|
// 多个 tab 前删除不形成代码块 https://github.com/Vanessa219/vditor/issues/162 1
|
|
388
|
-
blockElement.textContent.charAt(i) !== "\t"
|
|
533
|
+
blockElement.textContent.charAt(i) !== "\t"
|
|
534
|
+
) {
|
|
389
535
|
startSpace = false;
|
|
390
536
|
break;
|
|
391
537
|
}
|
|
@@ -396,8 +542,15 @@ class WYSIWYG {
|
|
|
396
542
|
|
|
397
543
|
// 结尾可以输入空格
|
|
398
544
|
let endSpace = true;
|
|
399
|
-
for (
|
|
400
|
-
|
|
545
|
+
for (
|
|
546
|
+
let i = startOffset - 1;
|
|
547
|
+
i < blockElement.textContent.length;
|
|
548
|
+
i++
|
|
549
|
+
) {
|
|
550
|
+
if (
|
|
551
|
+
blockElement.textContent.charAt(i) !== " " &&
|
|
552
|
+
blockElement.textContent.charAt(i) !== "\n"
|
|
553
|
+
) {
|
|
401
554
|
endSpace = false;
|
|
402
555
|
break;
|
|
403
556
|
}
|
|
@@ -408,196 +561,275 @@ class WYSIWYG {
|
|
|
408
561
|
endSpace = false;
|
|
409
562
|
}
|
|
410
563
|
|
|
411
|
-
const headingElement = hasClosestByHeadings(
|
|
564
|
+
const headingElement = hasClosestByHeadings(
|
|
565
|
+
getSelection().getRangeAt(0).startContainer
|
|
566
|
+
);
|
|
412
567
|
if (headingElement && headingElement.textContent === "") {
|
|
413
568
|
// heading 为空删除 https://github.com/Vanessa219/vditor/issues/150
|
|
414
569
|
renderToc(vditor);
|
|
415
570
|
headingElement.remove();
|
|
416
571
|
}
|
|
417
572
|
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
573
|
+
if (
|
|
574
|
+
(startSpace &&
|
|
575
|
+
blockElement.getAttribute("data-type") !== "code-block") ||
|
|
576
|
+
endSpace ||
|
|
577
|
+
isHeadingMD(blockElement.innerHTML) ||
|
|
578
|
+
(isHrMD(blockElement.innerHTML) &&
|
|
579
|
+
blockElement.previousElementSibling)
|
|
580
|
+
) {
|
|
421
581
|
if (typeof vditor.options.input === "function") {
|
|
422
582
|
vditor.options.input(getMarkdown(vditor));
|
|
423
583
|
}
|
|
424
584
|
return;
|
|
425
585
|
}
|
|
426
586
|
// https://github.com/Vanessa219/vditor/issues/1565
|
|
427
|
-
if (
|
|
587
|
+
if (
|
|
588
|
+
event.inputType === "insertParagraph" &&
|
|
589
|
+
this.element.innerHTML === "<p><br></p><p><br></p>"
|
|
590
|
+
) {
|
|
428
591
|
blockElement.previousElementSibling.remove();
|
|
429
592
|
}
|
|
430
593
|
|
|
431
594
|
input(vditor, range, event);
|
|
432
595
|
});
|
|
433
596
|
|
|
434
|
-
this.element.addEventListener(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
checkElement.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
597
|
+
this.element.addEventListener(
|
|
598
|
+
"click",
|
|
599
|
+
(event: MouseEvent & { target: HTMLElement }) => {
|
|
600
|
+
if (event.target.tagName === "INPUT") {
|
|
601
|
+
const checkElement = event.target as HTMLInputElement;
|
|
602
|
+
if (checkElement.checked) {
|
|
603
|
+
checkElement.setAttribute("checked", "checked");
|
|
604
|
+
} else {
|
|
605
|
+
checkElement.removeAttribute("checked");
|
|
606
|
+
}
|
|
607
|
+
this.preventInput = true;
|
|
608
|
+
if (getSelection().rangeCount > 0) {
|
|
609
|
+
setSelectionFocus(getSelection().getRangeAt(0));
|
|
610
|
+
}
|
|
611
|
+
afterRenderEvent(vditor);
|
|
612
|
+
return;
|
|
445
613
|
}
|
|
446
|
-
afterRenderEvent(vditor);
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
614
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
615
|
+
if (
|
|
616
|
+
event.target.tagName === "IMG" &&
|
|
617
|
+
// plantuml 图片渲染不进行提示
|
|
618
|
+
!event.target.parentElement.classList.contains(
|
|
619
|
+
"vditor-wysiwyg__preview"
|
|
620
|
+
)
|
|
621
|
+
) {
|
|
622
|
+
if (event.target.getAttribute("data-type") === "link-ref") {
|
|
623
|
+
genLinkRefPopover(vditor, event.target);
|
|
624
|
+
} else {
|
|
625
|
+
genImagePopover(event, vditor);
|
|
626
|
+
}
|
|
627
|
+
return;
|
|
457
628
|
}
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
629
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
630
|
+
// 打开链接
|
|
631
|
+
const a = hasClosestByMatchTag(event.target, "A");
|
|
632
|
+
if (a) {
|
|
633
|
+
if (vditor.options.link.click) {
|
|
634
|
+
vditor.options.link.click(a);
|
|
635
|
+
} else if (vditor.options.link.isOpen) {
|
|
636
|
+
window.open(a.getAttribute("href"));
|
|
637
|
+
}
|
|
638
|
+
event.preventDefault();
|
|
639
|
+
return;
|
|
468
640
|
}
|
|
469
|
-
event.preventDefault();
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
641
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
642
|
+
const range = getEditorRange(vditor);
|
|
643
|
+
if (
|
|
644
|
+
event.target.isEqualNode(this.element) &&
|
|
645
|
+
this.element.lastElementChild &&
|
|
646
|
+
range.collapsed
|
|
647
|
+
) {
|
|
648
|
+
const lastRect =
|
|
649
|
+
this.element.lastElementChild.getBoundingClientRect();
|
|
650
|
+
if (event.y > lastRect.top + lastRect.height) {
|
|
651
|
+
if (
|
|
652
|
+
this.element.lastElementChild.tagName === "P" &&
|
|
653
|
+
this.element.lastElementChild.textContent
|
|
654
|
+
.trim()
|
|
655
|
+
.replace(Constants.ZWSP, "") === ""
|
|
656
|
+
) {
|
|
657
|
+
range.selectNodeContents(
|
|
658
|
+
this.element.lastElementChild
|
|
659
|
+
);
|
|
660
|
+
range.collapse(false);
|
|
661
|
+
} else {
|
|
662
|
+
this.element.insertAdjacentHTML(
|
|
663
|
+
"beforeend",
|
|
664
|
+
`<p data-block="0">${Constants.ZWSP}<wbr></p>`
|
|
665
|
+
);
|
|
666
|
+
setRangeByWbr(this.element, range);
|
|
667
|
+
}
|
|
485
668
|
}
|
|
486
669
|
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
highlightToolbarWYSIWYG(vditor);
|
|
490
670
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
previewElement =
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
671
|
+
highlightToolbarWYSIWYG(vditor);
|
|
672
|
+
|
|
673
|
+
// 点击后光标落于预览区,需展开代码块(无选区时)
|
|
674
|
+
let previewElement = hasClosestByClassName(
|
|
675
|
+
event.target,
|
|
676
|
+
"vditor-wysiwyg__preview"
|
|
677
|
+
);
|
|
678
|
+
if (!previewElement) {
|
|
679
|
+
previewElement = hasClosestByClassName(
|
|
680
|
+
getEditorRange(vditor).startContainer,
|
|
681
|
+
"vditor-wysiwyg__preview"
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
if (previewElement) {
|
|
685
|
+
const hasSelection =
|
|
686
|
+
getSelectText(vditor.wysiwyg.element).trim().length > 0;
|
|
687
|
+
if (!hasSelection) {
|
|
688
|
+
showCode(previewElement, vditor);
|
|
689
|
+
}
|
|
501
690
|
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
clickToc(event, vditor);
|
|
505
|
-
});
|
|
506
691
|
|
|
507
|
-
|
|
508
|
-
if (event.isComposing || isCtrl(event)) {
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
// 除 md 处理、cell 内换行、table 添加新行/列、代码块语言切换、block render 换行、跳出/逐层跳出 blockquote、h6 换行、
|
|
512
|
-
// 任务列表换行、软换行外需在换行时调整文档位置
|
|
513
|
-
if (event.key === "Enter") {
|
|
514
|
-
scrollCenter(vditor);
|
|
515
|
-
}
|
|
516
|
-
if ((event.key === "Backspace" || event.key === "Delete") &&
|
|
517
|
-
vditor.wysiwyg.element.innerHTML !== "" && vditor.wysiwyg.element.childNodes.length === 1 &&
|
|
518
|
-
vditor.wysiwyg.element.firstElementChild && vditor.wysiwyg.element.firstElementChild.tagName === "P"
|
|
519
|
-
&& vditor.wysiwyg.element.firstElementChild.childElementCount === 0
|
|
520
|
-
&& (vditor.wysiwyg.element.textContent === "" || vditor.wysiwyg.element.textContent === "\n")) {
|
|
521
|
-
// 为空时显示 placeholder
|
|
522
|
-
vditor.wysiwyg.element.innerHTML = "";
|
|
692
|
+
clickToc(event, vditor);
|
|
523
693
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
694
|
+
);
|
|
695
|
+
|
|
696
|
+
this.element.addEventListener(
|
|
697
|
+
"keyup",
|
|
698
|
+
(event: KeyboardEvent & { target: HTMLElement }) => {
|
|
699
|
+
if (event.isComposing || isCtrl(event)) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
// 除 md 处理、cell 内换行、table 添加新行/列、代码块语言切换、block render 换行、跳出/逐层跳出 blockquote、h6 换行、
|
|
703
|
+
// 任务列表换行、软换行外需在换行时调整文档位置
|
|
704
|
+
if (event.key === "Enter") {
|
|
705
|
+
scrollCenter(vditor);
|
|
706
|
+
}
|
|
707
|
+
if (
|
|
708
|
+
(event.key === "Backspace" || event.key === "Delete") &&
|
|
709
|
+
vditor.wysiwyg.element.innerHTML !== "" &&
|
|
710
|
+
vditor.wysiwyg.element.childNodes.length === 1 &&
|
|
711
|
+
vditor.wysiwyg.element.firstElementChild &&
|
|
712
|
+
vditor.wysiwyg.element.firstElementChild.tagName === "P" &&
|
|
713
|
+
vditor.wysiwyg.element.firstElementChild
|
|
714
|
+
.childElementCount === 0 &&
|
|
715
|
+
(vditor.wysiwyg.element.textContent === "" ||
|
|
716
|
+
vditor.wysiwyg.element.textContent === "\n")
|
|
717
|
+
) {
|
|
718
|
+
// 为空时显示 placeholder
|
|
719
|
+
vditor.wysiwyg.element.innerHTML = "";
|
|
720
|
+
}
|
|
721
|
+
const range = getEditorRange(vditor);
|
|
722
|
+
if (event.key === "Backspace") {
|
|
723
|
+
// firefox headings https://github.com/Vanessa219/vditor/issues/211
|
|
724
|
+
if (
|
|
725
|
+
isFirefox() &&
|
|
726
|
+
range.startContainer.textContent === "\n" &&
|
|
727
|
+
range.startOffset === 1
|
|
728
|
+
) {
|
|
729
|
+
range.startContainer.textContent = "";
|
|
730
|
+
}
|
|
529
731
|
}
|
|
530
|
-
}
|
|
531
732
|
|
|
532
|
-
|
|
533
|
-
|
|
733
|
+
// 没有被块元素包裹
|
|
734
|
+
modifyPre(vditor, range);
|
|
534
735
|
|
|
535
|
-
|
|
736
|
+
highlightToolbarWYSIWYG(vditor);
|
|
536
737
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
738
|
+
if (
|
|
739
|
+
event.key !== "ArrowDown" &&
|
|
740
|
+
event.key !== "ArrowRight" &&
|
|
741
|
+
event.key !== "Backspace" &&
|
|
742
|
+
event.key !== "ArrowLeft" &&
|
|
743
|
+
event.key !== "ArrowUp"
|
|
744
|
+
) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
541
747
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
748
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
749
|
+
vditor.hint.render(vditor);
|
|
750
|
+
}
|
|
545
751
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
if (
|
|
552
|
-
previewElement
|
|
752
|
+
// 上下左右,删除遇到块预览的处理
|
|
753
|
+
let previewElement = hasClosestByClassName(
|
|
754
|
+
range.startContainer,
|
|
755
|
+
"vditor-wysiwyg__preview"
|
|
756
|
+
);
|
|
757
|
+
if (
|
|
758
|
+
!previewElement &&
|
|
759
|
+
range.startContainer.nodeType !== 3 &&
|
|
760
|
+
range.startOffset > 0
|
|
761
|
+
) {
|
|
762
|
+
// table 前删除遇到代码块
|
|
763
|
+
const blockRenderElement =
|
|
764
|
+
range.startContainer as HTMLElement;
|
|
765
|
+
if (
|
|
766
|
+
blockRenderElement.classList.contains(
|
|
767
|
+
"vditor-wysiwyg__block"
|
|
768
|
+
)
|
|
769
|
+
) {
|
|
770
|
+
previewElement =
|
|
771
|
+
blockRenderElement.lastElementChild as HTMLElement;
|
|
772
|
+
}
|
|
553
773
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
774
|
+
if (!previewElement) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const previousElement =
|
|
778
|
+
previewElement.previousElementSibling as HTMLElement;
|
|
779
|
+
if (previousElement.style.display === "none") {
|
|
780
|
+
if (
|
|
781
|
+
event.key === "ArrowDown" ||
|
|
782
|
+
event.key === "ArrowRight"
|
|
783
|
+
) {
|
|
784
|
+
showCode(previewElement, vditor);
|
|
785
|
+
} else {
|
|
786
|
+
showCode(previewElement, vditor, false);
|
|
787
|
+
}
|
|
788
|
+
return;
|
|
564
789
|
}
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
790
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
791
|
+
let codeElement =
|
|
792
|
+
previewElement.previousElementSibling as HTMLElement;
|
|
793
|
+
if (codeElement.tagName === "PRE") {
|
|
794
|
+
codeElement = codeElement.firstElementChild as HTMLElement;
|
|
795
|
+
}
|
|
572
796
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
797
|
+
if (event.key === "ArrowDown" || event.key === "ArrowRight") {
|
|
798
|
+
const blockRenderElement = previewElement.parentElement;
|
|
799
|
+
let nextNode = getRenderElementNextNode(
|
|
800
|
+
blockRenderElement
|
|
801
|
+
) as HTMLElement;
|
|
802
|
+
if (nextNode && nextNode.nodeType !== 3) {
|
|
803
|
+
// 下一节点依旧为代码渲染块
|
|
804
|
+
const nextRenderElement = nextNode.querySelector(
|
|
805
|
+
".vditor-wysiwyg__preview"
|
|
806
|
+
) as HTMLElement;
|
|
807
|
+
if (nextRenderElement) {
|
|
808
|
+
showCode(nextRenderElement, vditor);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
582
811
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
812
|
+
// 跳过渲染块,光标移动到下一个节点
|
|
813
|
+
if (nextNode.nodeType === 3) {
|
|
814
|
+
// inline
|
|
815
|
+
while (
|
|
816
|
+
nextNode.textContent.length === 0 &&
|
|
817
|
+
nextNode.nextSibling
|
|
818
|
+
) {
|
|
819
|
+
// https://github.com/Vanessa219/vditor/issues/100 2
|
|
820
|
+
nextNode = nextNode.nextSibling as HTMLElement;
|
|
821
|
+
}
|
|
822
|
+
range.setStart(nextNode, 1);
|
|
823
|
+
} else {
|
|
824
|
+
// block
|
|
825
|
+
range.setStart(nextNode.firstChild, 0);
|
|
590
826
|
}
|
|
591
|
-
range.setStart(nextNode, 1);
|
|
592
827
|
} else {
|
|
593
|
-
|
|
594
|
-
range.
|
|
828
|
+
range.selectNodeContents(codeElement);
|
|
829
|
+
range.collapse(false);
|
|
595
830
|
}
|
|
596
|
-
} else {
|
|
597
|
-
range.selectNodeContents(codeElement);
|
|
598
|
-
range.collapse(false);
|
|
599
831
|
}
|
|
600
|
-
|
|
832
|
+
);
|
|
601
833
|
}
|
|
602
834
|
}
|
|
603
835
|
|