@yltrcc/vditor 0.2.0 → 0.3.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.
@@ -1,1163 +1,1163 @@
1
- import {Constants} from "../constants";
2
- import {disableToolbar} from "../toolbar/setToolbar";
3
- import {enableToolbar} from "../toolbar/setToolbar";
4
- import {removeCurrentToolbar} from "../toolbar/setToolbar";
5
- import {setCurrentToolbar} from "../toolbar/setToolbar";
6
- import {isCtrl, updateHotkeyTip} from "../util/compatibility";
7
- import {scrollCenter} from "../util/editorCommonEvent";
8
- import {
9
- deleteColumn,
10
- deleteRow,
11
- insertColumn,
12
- insertRow,
13
- insertRowAbove,
14
- setTableAlign,
15
- } from "../util/fixBrowserBehavior";
16
- import {
17
- hasClosestByAttribute,
18
- hasClosestByClassName,
19
- hasClosestByMatchTag,
20
- } from "../util/hasClosest";
21
- import {
22
- hasClosestByHeadings,
23
- hasClosestByTag,
24
- } from "../util/hasClosestByHeadings";
25
- import {processCodeRender} from "../util/processCode";
26
- import {
27
- getEditorRange,
28
- selectIsEditor,
29
- setRangeByWbr,
30
- setSelectionFocus,
31
- } from "../util/selection";
32
- import {afterRenderEvent} from "./afterRenderEvent";
33
- import {removeBlockElement} from "./processKeydown";
34
- import {renderToc} from "../util/toc";
35
- import {getMarkdown} from "../markdown/getMarkdown";
36
-
37
- export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
38
- clearTimeout(vditor.wysiwyg.hlToolbarTimeoutId);
39
- vditor.wysiwyg.hlToolbarTimeoutId = window.setTimeout(() => {
40
- if (
41
- vditor.wysiwyg.element.getAttribute("contenteditable") === "false"
42
- ) {
43
- return;
44
- }
45
- if (!selectIsEditor(vditor.wysiwyg.element)) {
46
- return;
47
- }
48
-
49
- removeCurrentToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
50
- enableToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
51
-
52
- const range = getSelection().getRangeAt(0);
53
- let typeElement = range.startContainer as HTMLElement;
54
- if (range.startContainer.nodeType === 3) {
55
- typeElement = range.startContainer.parentElement;
56
- } else {
57
- typeElement = typeElement.childNodes[
58
- range.startOffset >= typeElement.childNodes.length
59
- ? typeElement.childNodes.length - 1
60
- : range.startOffset
61
- ] as HTMLElement;
62
- }
63
-
64
- const footnotesElement = hasClosestByAttribute(typeElement, "data-type", "footnotes-block");
65
- if (footnotesElement) {
66
- vditor.wysiwyg.popover.innerHTML = "";
67
- genClose(footnotesElement, vditor);
68
- customWysiwygToolbar(vditor, "footnotes-block")
69
- setPopoverPosition(vditor, footnotesElement);
70
- return;
71
- }
72
-
73
- // 工具栏高亮和禁用
74
- const liElement = hasClosestByMatchTag(typeElement, "LI");
75
- if (liElement) {
76
- if (liElement.classList.contains("vditor-task")) {
77
- setCurrentToolbar(vditor.toolbar.elements, ["check"]);
78
- } else if (liElement.parentElement.tagName === "OL") {
79
- setCurrentToolbar(vditor.toolbar.elements, ["ordered-list"]);
80
- } else if (liElement.parentElement.tagName === "UL") {
81
- setCurrentToolbar(vditor.toolbar.elements, ["list"]);
82
- }
83
- enableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
84
- } else {
85
- disableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
86
- }
87
-
88
- if (hasClosestByMatchTag(typeElement, "BLOCKQUOTE")) {
89
- setCurrentToolbar(vditor.toolbar.elements, ["quote"]);
90
- }
91
-
92
- if (
93
- hasClosestByMatchTag(typeElement, "B") ||
94
- hasClosestByMatchTag(typeElement, "STRONG")
95
- ) {
96
- setCurrentToolbar(vditor.toolbar.elements, ["bold"]);
97
- }
98
-
99
- if (
100
- hasClosestByMatchTag(typeElement, "I") ||
101
- hasClosestByMatchTag(typeElement, "EM")
102
- ) {
103
- setCurrentToolbar(vditor.toolbar.elements, ["italic"]);
104
- }
105
-
106
- if (
107
- hasClosestByMatchTag(typeElement, "STRIKE") ||
108
- hasClosestByMatchTag(typeElement, "S")
109
- ) {
110
- setCurrentToolbar(vditor.toolbar.elements, ["strike"]);
111
- }
112
-
113
- // comments
114
- vditor.wysiwyg.element
115
- .querySelectorAll(".vditor-comment--focus")
116
- .forEach((item) => {
117
- item.classList.remove("vditor-comment--focus");
118
- });
119
- const commentElement = hasClosestByClassName(typeElement, "vditor-comment");
120
- if (commentElement) {
121
- let ids = commentElement.getAttribute("data-cmtids").split(" ");
122
- if (ids.length > 1 && commentElement.nextSibling.isSameNode(commentElement.nextElementSibling)) {
123
- const nextIds = commentElement.nextElementSibling
124
- .getAttribute("data-cmtids")
125
- .split(" ");
126
- ids.find((id) => {
127
- if (nextIds.includes(id)) {
128
- ids = [id];
129
- return true;
130
- }
131
- });
132
- }
133
- vditor.wysiwyg.element
134
- .querySelectorAll(".vditor-comment")
135
- .forEach((item) => {
136
- if (item.getAttribute("data-cmtids").indexOf(ids[0]) > -1) {
137
- item.classList.add("vditor-comment--focus");
138
- }
139
- });
140
- }
141
-
142
- const aElement = hasClosestByMatchTag(typeElement, "A");
143
- if (aElement) {
144
- setCurrentToolbar(vditor.toolbar.elements, ["link"]);
145
- }
146
- const tableElement = hasClosestByMatchTag(typeElement, "TABLE") as HTMLTableElement;
147
- const headingElement = hasClosestByHeadings(typeElement) as HTMLElement;
148
- if (hasClosestByMatchTag(typeElement, "CODE")) {
149
- if (hasClosestByMatchTag(typeElement, "PRE")) {
150
- disableToolbar(vditor.toolbar.elements, [
151
- "headings",
152
- "bold",
153
- "italic",
154
- "strike",
155
- "line",
156
- "quote",
157
- "list",
158
- "ordered-list",
159
- "check",
160
- "code",
161
- "inline-code",
162
- "upload",
163
- "link",
164
- "table",
165
- "record",
166
- ]);
167
- setCurrentToolbar(vditor.toolbar.elements, ["code"]);
168
- } else {
169
- disableToolbar(vditor.toolbar.elements, [
170
- "headings",
171
- "bold",
172
- "italic",
173
- "strike",
174
- "line",
175
- "quote",
176
- "list",
177
- "ordered-list",
178
- "check",
179
- "code",
180
- "upload",
181
- "link",
182
- "table",
183
- "record",
184
- ]);
185
- setCurrentToolbar(vditor.toolbar.elements, ["inline-code"]);
186
- }
187
- } else if (headingElement) {
188
- disableToolbar(vditor.toolbar.elements, ["bold"]);
189
- setCurrentToolbar(vditor.toolbar.elements, ["headings"]);
190
- } else if (tableElement) {
191
- disableToolbar(vditor.toolbar.elements, ["table"]);
192
- }
193
-
194
- // toc popover
195
- const tocElement = hasClosestByClassName(typeElement, "vditor-toc") as HTMLElement;
196
- if (tocElement) {
197
- vditor.wysiwyg.popover.innerHTML = "";
198
- genClose(tocElement, vditor);
199
- customWysiwygToolbar(vditor, "vditor-toc")
200
- setPopoverPosition(vditor, tocElement);
201
- return;
202
- }
203
-
204
- // quote popover
205
- const blockquoteElement = hasClosestByTag(typeElement, "BLOCKQUOTE") as HTMLTableElement;
206
- if (blockquoteElement) {
207
- vditor.wysiwyg.popover.innerHTML = "";
208
- genUp(range, blockquoteElement, vditor);
209
- genDown(range, blockquoteElement, vditor);
210
- genClose(blockquoteElement, vditor);
211
- customWysiwygToolbar(vditor, "blockquote")
212
- setPopoverPosition(vditor, blockquoteElement);
213
- }
214
-
215
- // list item popover
216
- if (liElement) {
217
- vditor.wysiwyg.popover.innerHTML = "";
218
-
219
- genUp(range, liElement, vditor);
220
- genDown(range, liElement, vditor);
221
- genClose(liElement, vditor);
222
- customWysiwygToolbar(vditor, "li")
223
- setPopoverPosition(vditor, liElement);
224
- }
225
-
226
- // table popover
227
- if (tableElement) {
228
- const lang: keyof II18n | "" = vditor.options.lang;
229
- const options: IOptions = vditor.options;
230
- vditor.wysiwyg.popover.innerHTML = "";
231
- const updateTable = () => {
232
- const oldRow = tableElement.rows.length;
233
- const oldColumn = tableElement.rows[0].cells.length;
234
- const row = parseInt(input.value, 10) || oldRow;
235
- const column = parseInt(input2.value, 10) || oldColumn;
236
-
237
- if (row === oldRow && oldColumn === column) {
238
- return;
239
- }
240
-
241
- if (oldColumn !== column) {
242
- const columnDiff = column - oldColumn;
243
- for (let i = 0; i < tableElement.rows.length; i++) {
244
- if (columnDiff > 0) {
245
- for (let j = 0; j < columnDiff; j++) {
246
- if (i === 0) {
247
- tableElement.rows[i].lastElementChild.insertAdjacentHTML("afterend", "<th> </th>");
248
- } else {
249
- tableElement.rows[i].lastElementChild.insertAdjacentHTML("afterend", "<td> </td>");
250
- }
251
- }
252
- } else {
253
- for (let k = oldColumn - 1; k >= column; k--) {
254
- tableElement.rows[i].cells[k].remove();
255
- }
256
- }
257
- }
258
- }
259
-
260
- if (oldRow !== row) {
261
- const rowDiff = row - oldRow;
262
- if (rowDiff > 0) {
263
- let rowHTML = "<tr>";
264
- for (let m = 0; m < column; m++) {
265
- rowHTML += "<td> </td>";
266
- }
267
- for (let l = 0; l < rowDiff; l++) {
268
- if (tableElement.querySelector("tbody")) {
269
- tableElement
270
- .querySelector("tbody")
271
- .insertAdjacentHTML("beforeend", rowHTML);
272
- } else {
273
- tableElement
274
- .querySelector("thead")
275
- .insertAdjacentHTML("afterend", rowHTML + "</tr>");
276
- }
277
- }
278
- } else {
279
- for (let m = oldRow - 1; m >= row; m--) {
280
- tableElement.rows[m].remove();
281
- if (tableElement.rows.length === 1) {
282
- tableElement.querySelector("tbody").remove();
283
- }
284
- }
285
- }
286
- }
287
- if (typeof vditor.options.input === "function") {
288
- vditor.options.input(getMarkdown(vditor));
289
- }
290
- };
291
-
292
- const setAlign = (type: string) => {
293
- setTableAlign(tableElement, type);
294
- if (type === "right") {
295
- left.classList.remove("vditor-icon--current");
296
- center.classList.remove("vditor-icon--current");
297
- right.classList.add("vditor-icon--current");
298
- } else if (type === "center") {
299
- left.classList.remove("vditor-icon--current");
300
- right.classList.remove("vditor-icon--current");
301
- center.classList.add("vditor-icon--current");
302
- } else {
303
- center.classList.remove("vditor-icon--current");
304
- right.classList.remove("vditor-icon--current");
305
- left.classList.add("vditor-icon--current");
306
- }
307
- setSelectionFocus(range);
308
- afterRenderEvent(vditor);
309
- };
310
-
311
- const td = hasClosestByMatchTag(typeElement, "TD");
312
- const th = hasClosestByMatchTag(typeElement, "TH");
313
- let alignType = "left";
314
- if (td) {
315
- alignType = td.getAttribute("align") || "left";
316
- } else if (th) {
317
- alignType = th.getAttribute("align") || "center";
318
- }
319
-
320
- const left = document.createElement("button");
321
- left.setAttribute("type", "button");
322
- left.setAttribute("aria-label", window.VditorI18n.alignLeft + "<" + updateHotkeyTip("⇧⌘L") + ">");
323
- left.setAttribute("data-type", "left");
324
- left.innerHTML =
325
- '<svg><use xlink:href="#vditor-icon-align-left"></use></svg>';
326
- left.className =
327
- "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
328
- (alignType === "left" ? " vditor-icon--current" : "");
329
- left.onclick = () => {
330
- setAlign("left");
331
- };
332
-
333
- const center = document.createElement("button");
334
- center.setAttribute("type", "button");
335
- center.setAttribute("aria-label", window.VditorI18n.alignCenter + "<" + updateHotkeyTip("⇧⌘C") + ">");
336
- center.setAttribute("data-type", "center");
337
- center.innerHTML =
338
- '<svg><use xlink:href="#vditor-icon-align-center"></use></svg>';
339
- center.className =
340
- "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
341
- (alignType === "center" ? " vditor-icon--current" : "");
342
- center.onclick = () => {
343
- setAlign("center");
344
- };
345
-
346
- const right = document.createElement("button");
347
- right.setAttribute("type", "button");
348
- right.setAttribute("aria-label", window.VditorI18n.alignRight + "<" + updateHotkeyTip("⇧⌘R") + ">");
349
- right.setAttribute("data-type", "right");
350
- right.innerHTML =
351
- '<svg><use xlink:href="#vditor-icon-align-right"></use></svg>';
352
- right.className =
353
- "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
354
- (alignType === "right" ? " vditor-icon--current" : "");
355
- right.onclick = () => {
356
- setAlign("right");
357
- };
358
-
359
- const insertRowElement = document.createElement("button");
360
- insertRowElement.setAttribute("type", "button");
361
- insertRowElement.setAttribute("aria-label", window.VditorI18n.insertRowBelow + "<" + updateHotkeyTip("⌘=") + ">");
362
- insertRowElement.setAttribute("data-type", "insertRow");
363
- insertRowElement.innerHTML =
364
- '<svg><use xlink:href="#vditor-icon-insert-row"></use></svg>';
365
- insertRowElement.className =
366
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
367
- insertRowElement.onclick = () => {
368
- const startContainer = getSelection().getRangeAt(0)
369
- .startContainer;
370
- const cellElement =
371
- hasClosestByMatchTag(startContainer, "TD") ||
372
- hasClosestByMatchTag(startContainer, "TH");
373
- if (cellElement) {
374
- insertRow(vditor, range, cellElement);
375
- }
376
- };
377
-
378
- const insertRowBElement = document.createElement("button");
379
- insertRowBElement.setAttribute("type", "button");
380
- insertRowBElement.setAttribute("aria-label",
381
- window.VditorI18n.insertRowAbove + "<" + updateHotkeyTip("⇧⌘F") + ">");
382
- insertRowBElement.setAttribute("data-type", "insertRow");
383
- insertRowBElement.innerHTML =
384
- '<svg><use xlink:href="#vditor-icon-insert-rowb"></use></svg>';
385
- insertRowBElement.className =
386
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
387
- insertRowBElement.onclick = () => {
388
- const startContainer = getSelection().getRangeAt(0)
389
- .startContainer;
390
- const cellElement =
391
- hasClosestByMatchTag(startContainer, "TD") ||
392
- hasClosestByMatchTag(startContainer, "TH");
393
- if (cellElement) {
394
- insertRowAbove(vditor, range, cellElement);
395
- }
396
- };
397
-
398
- const insertColumnElement = document.createElement("button");
399
- insertColumnElement.setAttribute("type", "button");
400
- insertColumnElement.setAttribute("aria-label", window.VditorI18n.insertColumnRight + "<" + updateHotkeyTip("⇧⌘=") + ">");
401
- insertColumnElement.setAttribute("data-type", "insertColumn");
402
- insertColumnElement.innerHTML =
403
- '<svg><use xlink:href="#vditor-icon-insert-column"></use></svg>';
404
- insertColumnElement.className =
405
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
406
- insertColumnElement.onclick = () => {
407
- const startContainer = getSelection().getRangeAt(0)
408
- .startContainer;
409
- const cellElement =
410
- hasClosestByMatchTag(startContainer, "TD") ||
411
- hasClosestByMatchTag(startContainer, "TH");
412
- if (cellElement) {
413
- insertColumn(vditor, tableElement, cellElement);
414
- }
415
- };
416
-
417
- const insertColumnBElement = document.createElement("button");
418
- insertColumnBElement.setAttribute("type", "button");
419
- insertColumnBElement.setAttribute("aria-label", window.VditorI18n.insertColumnLeft + "<" + updateHotkeyTip("⇧⌘G") + ">");
420
- insertColumnBElement.setAttribute("data-type", "insertColumn");
421
- insertColumnBElement.innerHTML =
422
- '<svg><use xlink:href="#vditor-icon-insert-columnb"></use></svg>';
423
- insertColumnBElement.className =
424
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
425
- insertColumnBElement.onclick = () => {
426
- const startContainer = getSelection().getRangeAt(0)
427
- .startContainer;
428
- const cellElement =
429
- hasClosestByMatchTag(startContainer, "TD") ||
430
- hasClosestByMatchTag(startContainer, "TH");
431
- if (cellElement) {
432
- insertColumn(vditor, tableElement, cellElement, "beforebegin");
433
- }
434
- };
435
-
436
- const deleteRowElement = document.createElement("button");
437
- deleteRowElement.setAttribute("type", "button");
438
- deleteRowElement.setAttribute("aria-label", window.VditorI18n["delete-row"] + "<" + updateHotkeyTip("⌘-") + ">");
439
- deleteRowElement.setAttribute("data-type", "deleteRow");
440
- deleteRowElement.innerHTML =
441
- '<svg><use xlink:href="#vditor-icon-delete-row"></use></svg>';
442
- deleteRowElement.className =
443
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
444
- deleteRowElement.onclick = () => {
445
- const startContainer = getSelection().getRangeAt(0)
446
- .startContainer;
447
- const cellElement =
448
- hasClosestByMatchTag(startContainer, "TD") ||
449
- hasClosestByMatchTag(startContainer, "TH");
450
- if (cellElement) {
451
- deleteRow(vditor, range, cellElement);
452
- }
453
- };
454
-
455
- const deleteColumnElement = document.createElement("button");
456
- deleteColumnElement.setAttribute("type", "button");
457
- deleteColumnElement.setAttribute("aria-label", window.VditorI18n["delete-column"] + "<" + updateHotkeyTip("⇧⌘-") + ">");
458
- deleteColumnElement.setAttribute("data-type", "deleteColumn");
459
- deleteColumnElement.innerHTML =
460
- '<svg><use xlink:href="#vditor-icon-delete-column"></use></svg>';
461
- deleteColumnElement.className =
462
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
463
- deleteColumnElement.onclick = () => {
464
- const startContainer = getSelection().getRangeAt(0)
465
- .startContainer;
466
- const cellElement =
467
- hasClosestByMatchTag(startContainer, "TD") ||
468
- hasClosestByMatchTag(startContainer, "TH");
469
- if (cellElement) {
470
- deleteColumn(vditor, range, tableElement, cellElement);
471
- }
472
- };
473
-
474
- const inputWrap = document.createElement("span");
475
- inputWrap.setAttribute("aria-label", window.VditorI18n.row);
476
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
477
- const input = document.createElement("input");
478
- inputWrap.appendChild(input);
479
- input.type = "number";
480
- input.min = "1";
481
- input.className = "vditor-input";
482
- input.style.width = "42px";
483
- input.style.textAlign = "center";
484
- input.setAttribute("placeholder", window.VditorI18n.row);
485
- input.value = tableElement.rows.length.toString();
486
- input.oninput = () => {
487
- updateTable();
488
- };
489
- input.onkeydown = (event) => {
490
- if (event.isComposing) {
491
- return;
492
- }
493
- if (event.key === "Tab") {
494
- input2.focus();
495
- input2.select();
496
- event.preventDefault();
497
- return;
498
- }
499
- if (removeBlockElement(vditor, event)) {
500
- return;
501
- }
502
- if (focusToElement(event, range)) {
503
- return;
504
- }
505
- };
506
-
507
- const input2Wrap = document.createElement("span");
508
- input2Wrap.setAttribute("aria-label", window.VditorI18n.column);
509
- input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
510
- const input2 = document.createElement("input");
511
- input2Wrap.appendChild(input2);
512
- input2.type = "number";
513
- input2.min = "1";
514
- input2.className = "vditor-input";
515
- input2.style.width = "42px";
516
- input2.style.textAlign = "center";
517
- input2.setAttribute("placeholder", window.VditorI18n.column);
518
- input2.value = tableElement.rows[0].cells.length.toString();
519
- input2.oninput = () => {
520
- updateTable();
521
- };
522
- input2.onkeydown = (event) => {
523
- if (event.isComposing) {
524
- return;
525
- }
526
- if (event.key === "Tab") {
527
- input.focus();
528
- input.select();
529
- event.preventDefault();
530
- return;
531
- }
532
- if (removeBlockElement(vditor, event)) {
533
- return;
534
- }
535
- if (focusToElement(event, range)) {
536
- return;
537
- }
538
- };
539
-
540
- genUp(range, tableElement, vditor);
541
- genDown(range, tableElement, vditor);
542
- genClose(tableElement, vditor);
543
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", left);
544
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", center);
545
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", right);
546
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowBElement);
547
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowElement);
548
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnBElement);
549
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnElement);
550
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteRowElement);
551
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteColumnElement);
552
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
553
- vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
554
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
555
- customWysiwygToolbar(vditor, "table")
556
- setPopoverPosition(vditor, tableElement);
557
- }
558
-
559
- // link ref popover
560
- const linkRefElement = hasClosestByAttribute(typeElement, "data-type", "link-ref");
561
- if (linkRefElement) {
562
- genLinkRefPopover(vditor, linkRefElement, range);
563
- }
564
-
565
- // footnote popover
566
- const footnotesRefElement = hasClosestByAttribute(typeElement, "data-type", "footnotes-ref");
567
- if (footnotesRefElement) {
568
- const lang: keyof II18n | "" = vditor.options.lang;
569
- const options: IOptions = vditor.options;
570
- vditor.wysiwyg.popover.innerHTML = "";
571
-
572
- const inputWrap = document.createElement("span");
573
- inputWrap.setAttribute("aria-label", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
574
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
575
- const input = document.createElement("input");
576
- inputWrap.appendChild(input);
577
- input.className = "vditor-input";
578
- input.setAttribute("placeholder", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
579
- input.style.width = "120px";
580
- input.value = footnotesRefElement.getAttribute("data-footnotes-label");
581
- input.oninput = () => {
582
- if (input.value.trim() !== "") {
583
- footnotesRefElement.setAttribute("data-footnotes-label", input.value);
584
- }
585
- if (typeof vditor.options.input === "function") {
586
- vditor.options.input(getMarkdown(vditor));
587
- }
588
- };
589
- input.onkeydown = (event) => {
590
- if (event.isComposing) {
591
- return;
592
- }
593
- if (removeBlockElement(vditor, event)) {
594
- return;
595
- }
596
- if (focusToElement(event, range)) {
597
- return;
598
- }
599
- };
600
-
601
- genClose(footnotesRefElement, vditor);
602
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
603
- customWysiwygToolbar(vditor, "footnotes-ref")
604
- setPopoverPosition(vditor, footnotesRefElement);
605
- }
606
-
607
- // block popover: math-inline, math-block, html-block, html-inline, code-block, html-entity
608
- let blockRenderElement = hasClosestByClassName(typeElement, "vditor-wysiwyg__block") as HTMLElement;
609
- const isBlock = blockRenderElement ? blockRenderElement.getAttribute("data-type").indexOf("block") > -1 : false;
610
- vditor.wysiwyg.element
611
- .querySelectorAll(".vditor-wysiwyg__preview")
612
- .forEach((itemElement) => {
613
- if (!blockRenderElement || (blockRenderElement && isBlock && !blockRenderElement.contains(itemElement))) {
614
- const previousElement = itemElement.previousElementSibling as HTMLElement;
615
- previousElement.style.display = "none";
616
- }
617
- });
618
- if (blockRenderElement && isBlock) {
619
- // 如果代码块处于编辑状态,则不显示工具栏
620
- const isCodeBlockEditing = blockRenderElement.classList.contains("vditor-wysiwyg__block--editing");
621
- if (isCodeBlockEditing) {
622
- vditor.wysiwyg.popover.style.display = "none";
623
- return;
624
- }
625
-
626
- vditor.wysiwyg.popover.innerHTML = "";
627
- genUp(range, blockRenderElement, vditor);
628
- genDown(range, blockRenderElement, vditor);
629
- genClose(blockRenderElement, vditor);
630
-
631
- if (blockRenderElement.getAttribute("data-type") === "code-block") {
632
- const languageWrap = document.createElement("span");
633
- languageWrap.setAttribute("aria-label", window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
634
- languageWrap.className = "vditor-tooltipped vditor-tooltipped__n";
635
- const language = document.createElement("input");
636
- languageWrap.appendChild(language);
637
-
638
- const codeElement =
639
- blockRenderElement.firstElementChild.firstElementChild;
640
-
641
- language.className = "vditor-input";
642
- language.setAttribute("placeholder",
643
- window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
644
- language.value =
645
- codeElement.className.indexOf("language-") > -1
646
- ? codeElement.className.split("-")[1].split(" ")[0]
647
- : "";
648
- language.oninput = (e: InputEvent) => {
649
- if (language.value.trim() !== "") {
650
- codeElement.className = `language-${language.value}`;
651
- } else {
652
- codeElement.className = "";
653
- vditor.hint.recentLanguage = "";
654
- }
655
- if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
656
- blockRenderElement.lastElementChild.innerHTML =
657
- blockRenderElement.firstElementChild.innerHTML;
658
- processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
659
- }
660
- afterRenderEvent(vditor);
661
- // 当鼠标点选语言时,触发自定义input事件
662
- if (e.detail === 1) {
663
- // 选择语言后,输入焦点切换到代码输入框
664
- range.setStart(codeElement.firstChild, 0);
665
- range.collapse(true);
666
- setSelectionFocus(range);
667
- }
668
- };
669
- language.onkeydown = (event: KeyboardEvent) => {
670
- if (event.isComposing) {
671
- return;
672
- }
673
- if (removeBlockElement(vditor, event)) {
674
- return;
675
- }
676
- if (
677
- event.key === "Escape" &&
678
- vditor.hint.element.style.display === "block"
679
- ) {
680
- vditor.hint.element.style.display = "none";
681
- event.preventDefault();
682
- return;
683
- }
684
- vditor.hint.select(event, vditor);
685
- focusToElement(event, range);
686
- };
687
- language.onkeyup = (event: KeyboardEvent) => {
688
- if (
689
- event.isComposing ||
690
- event.key === "Enter" ||
691
- event.key === "ArrowUp" ||
692
- event.key === "Escape" ||
693
- event.key === "ArrowDown"
694
- ) {
695
- return;
696
- }
697
- const matchLangData: IHintData[] = [];
698
- const key = language.value.substring(0, language.selectionStart);
699
- (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
700
- if (keyName.indexOf(key.toLowerCase()) > -1) {
701
- matchLangData.push({
702
- html: keyName,
703
- value: keyName,
704
- });
705
- }
706
- });
707
- vditor.hint.genHTML(matchLangData, key, vditor);
708
- event.preventDefault();
709
- };
710
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
711
- customWysiwygToolbar(vditor, "code-block")
712
- } else {
713
- customWysiwygToolbar(vditor, "block")
714
- }
715
- setPopoverPosition(vditor, blockRenderElement);
716
- } else {
717
- blockRenderElement = undefined;
718
- }
719
- if (headingElement) {
720
- vditor.wysiwyg.popover.innerHTML = "";
721
-
722
- const inputWrap = document.createElement("span");
723
- inputWrap.setAttribute("aria-label", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
724
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
725
- const input = document.createElement("input");
726
- inputWrap.appendChild(input);
727
- input.className = "vditor-input";
728
- input.setAttribute("placeholder", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
729
- input.style.width = "120px";
730
- input.value = headingElement.getAttribute("data-id") || "";
731
- input.oninput = () => {
732
- headingElement.setAttribute("data-id", input.value);
733
- if (typeof vditor.options.input === "function") {
734
- vditor.options.input(getMarkdown(vditor));
735
- }
736
- };
737
- input.onkeydown = (event) => {
738
- if (event.isComposing) {
739
- return;
740
- }
741
- if (removeBlockElement(vditor, event)) {
742
- return;
743
- }
744
- if (focusToElement(event, range)) {
745
- return;
746
- }
747
- };
748
-
749
- genUp(range, headingElement, vditor);
750
- genDown(range, headingElement, vditor);
751
- genClose(headingElement, vditor);
752
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
753
- customWysiwygToolbar(vditor, "heading")
754
- setPopoverPosition(vditor, headingElement);
755
- }
756
-
757
- // a popover
758
- if (aElement) {
759
- genAPopover(vditor, aElement, range);
760
- }
761
-
762
- if (
763
- !blockquoteElement &&
764
- !liElement &&
765
- !tableElement &&
766
- !blockRenderElement &&
767
- !aElement &&
768
- !linkRefElement &&
769
- !footnotesRefElement &&
770
- !headingElement &&
771
- !tocElement
772
- ) {
773
- const blockElement = hasClosestByAttribute(typeElement, "data-block", "0");
774
- if (
775
- blockElement &&
776
- blockElement.parentElement.isEqualNode(vditor.wysiwyg.element)
777
- ) {
778
- vditor.wysiwyg.popover.innerHTML = "";
779
- genUp(range, blockElement, vditor);
780
- genDown(range, blockElement, vditor);
781
- genClose(blockElement, vditor);
782
- customWysiwygToolbar(vditor, "block")
783
- setPopoverPosition(vditor, blockElement);
784
- } else {
785
- vditor.wysiwyg.popover.style.display = "none";
786
- }
787
- }
788
-
789
- // 反斜杠特殊处理
790
- vditor.wysiwyg.element
791
- .querySelectorAll('span[data-type="backslash"] > span')
792
- .forEach((item: HTMLElement) => {
793
- item.style.display = "none";
794
- });
795
- const backslashElement = hasClosestByAttribute(range.startContainer, "data-type", "backslash");
796
- if (backslashElement) {
797
- backslashElement.querySelector("span").style.display = "inline";
798
- }
799
- }, 200);
800
- };
801
-
802
- const setPopoverPosition = (vditor: IVditor, element: HTMLElement) => {
803
- let targetElement = element;
804
- const tableElement = hasClosestByMatchTag(element, "TABLE");
805
- if (tableElement) {
806
- targetElement = tableElement;
807
- }
808
- vditor.wysiwyg.popover.style.left = "0";
809
- vditor.wysiwyg.popover.style.display = "block";
810
- vditor.wysiwyg.popover.style.top =
811
- Math.max(-8, targetElement.offsetTop - 21 - vditor.wysiwyg.element.scrollTop) + "px";
812
- vditor.wysiwyg.popover.style.left =
813
- Math.min(targetElement.offsetLeft, vditor.wysiwyg.element.clientWidth - vditor.wysiwyg.popover.clientWidth) + "px";
814
- vditor.wysiwyg.popover.setAttribute("data-top", (targetElement.offsetTop - 21).toString());
815
- };
816
-
817
- export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement, range = getSelection().getRangeAt(0)) => {
818
- vditor.wysiwyg.popover.innerHTML = "";
819
- const updateLinkRef = () => {
820
- if (input.value.trim() !== "") {
821
- if (linkRefElement.tagName === "IMG") {
822
- linkRefElement.setAttribute("alt", input.value);
823
- } else {
824
- linkRefElement.textContent = input.value;
825
- }
826
- }
827
- // data-link-label
828
- if (input1.value.trim() !== "") {
829
- linkRefElement.setAttribute("data-link-label", input1.value);
830
- }
831
- if (typeof vditor.options.input === "function") {
832
- vditor.options.input(getMarkdown(vditor));
833
- }
834
- };
835
-
836
- const inputWrap = document.createElement("span");
837
- inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
838
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
839
- const input = document.createElement("input");
840
- inputWrap.appendChild(input);
841
- input.className = "vditor-input";
842
- input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
843
- input.style.width = "120px";
844
- input.value =
845
- linkRefElement.getAttribute("alt") || linkRefElement.textContent;
846
- input.oninput = () => {
847
- updateLinkRef();
848
- };
849
- input.onkeydown = (event) => {
850
- if (removeBlockElement(vditor, event)) {
851
- return;
852
- }
853
- if (focusToElement(event, range)) {
854
- return;
855
- }
856
- linkHotkey(vditor, linkRefElement, event, input1);
857
- };
858
-
859
- const input1Wrap = document.createElement("span");
860
- input1Wrap.setAttribute("aria-label", window.VditorI18n.linkRef);
861
- input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
862
- const input1 = document.createElement("input");
863
- input1Wrap.appendChild(input1);
864
- input1.className = "vditor-input";
865
- input1.setAttribute("placeholder", window.VditorI18n.linkRef);
866
- input1.value = linkRefElement.getAttribute("data-link-label");
867
- input1.oninput = () => {
868
- updateLinkRef();
869
- };
870
- input1.onkeydown = (event) => {
871
- if (removeBlockElement(vditor, event)) {
872
- return;
873
- }
874
- if (focusToElement(event, range)) {
875
- return;
876
- }
877
- linkHotkey(vditor, linkRefElement, event, input);
878
- };
879
-
880
- genClose(linkRefElement, vditor);
881
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
882
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
883
- customWysiwygToolbar(vditor, "link-ref")
884
- setPopoverPosition(vditor, linkRefElement);
885
- };
886
-
887
- const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
888
- const previousElement = element.previousElementSibling;
889
- if (
890
- !previousElement ||
891
- (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
892
- element.tagName !== "LI")
893
- ) {
894
- return;
895
- }
896
- const upElement = document.createElement("button");
897
- upElement.setAttribute("type", "button");
898
- upElement.setAttribute("data-type", "up");
899
- upElement.setAttribute("aria-label", window.VditorI18n.up + "<" + updateHotkeyTip("⇧⌘U") + ">");
900
- upElement.innerHTML = '<svg><use xlink:href="#vditor-icon-up"></use></svg>';
901
- upElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
902
- upElement.onclick = () => {
903
- range.insertNode(document.createElement("wbr"));
904
- previousElement.insertAdjacentElement("beforebegin", element);
905
- setRangeByWbr(vditor.wysiwyg.element, range);
906
- afterRenderEvent(vditor);
907
- highlightToolbarWYSIWYG(vditor);
908
- scrollCenter(vditor);
909
- };
910
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
911
- };
912
-
913
- const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
914
- const nextElement = element.nextElementSibling;
915
- if (
916
- !nextElement ||
917
- (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
918
- element.tagName !== "LI")
919
- ) {
920
- return;
921
- }
922
- const downElement = document.createElement("button");
923
- downElement.setAttribute("type", "button");
924
- downElement.setAttribute("data-type", "down");
925
- downElement.setAttribute("aria-label", window.VditorI18n.down + "<" + updateHotkeyTip("⇧⌘D") + ">");
926
- downElement.innerHTML =
927
- '<svg><use xlink:href="#vditor-icon-down"></use></svg>';
928
- downElement.className =
929
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
930
- downElement.onclick = () => {
931
- range.insertNode(document.createElement("wbr"));
932
- nextElement.insertAdjacentElement("afterend", element);
933
- setRangeByWbr(vditor.wysiwyg.element, range);
934
- afterRenderEvent(vditor);
935
- highlightToolbarWYSIWYG(vditor);
936
- scrollCenter(vditor);
937
- };
938
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
939
- };
940
-
941
- const genClose = (element: HTMLElement, vditor: IVditor) => {
942
- const close = document.createElement("button");
943
- close.setAttribute("type", "button");
944
- close.setAttribute("data-type", "remove");
945
- close.setAttribute("aria-label", window.VditorI18n.remove + "<" + updateHotkeyTip("⇧⌘X") + ">");
946
- close.innerHTML =
947
- '<svg><use xlink:href="#vditor-icon-trashcan"></use></svg>';
948
- close.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
949
- close.onclick = () => {
950
- const range = getEditorRange(vditor);
951
- range.setStartAfter(element);
952
- setSelectionFocus(range);
953
- element.remove();
954
- afterRenderEvent(vditor);
955
- highlightToolbarWYSIWYG(vditor);
956
- if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
957
- renderToc(vditor);
958
- }
959
- };
960
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
961
- };
962
-
963
- const linkHotkey = (
964
- vditor: IVditor,
965
- element: HTMLElement,
966
- event: KeyboardEvent,
967
- nextInputElement: HTMLInputElement,
968
- ) => {
969
- if (event.isComposing) {
970
- return;
971
- }
972
- if (event.key === "Tab") {
973
- nextInputElement.focus();
974
- nextInputElement.select();
975
- event.preventDefault();
976
- return;
977
- }
978
- if (
979
- !isCtrl(event) &&
980
- !event.shiftKey &&
981
- event.altKey &&
982
- event.key === "Enter"
983
- ) {
984
- const range = getEditorRange(vditor);
985
- // firefox 不会打断 link https://github.com/Vanessa219/vditor/issues/193
986
- element.insertAdjacentHTML("afterend", Constants.ZWSP);
987
- range.setStartAfter(element.nextSibling);
988
- range.collapse(true);
989
- setSelectionFocus(range);
990
- event.preventDefault();
991
- }
992
- };
993
-
994
- export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range) => {
995
- vditor.wysiwyg.popover.innerHTML = "";
996
-
997
- const updateA = () => {
998
- if (input.value.trim() !== "") {
999
- aElement.innerHTML = input.value;
1000
- }
1001
- aElement.setAttribute("href", input1.value);
1002
- aElement.setAttribute("title", input2.value);
1003
- afterRenderEvent(vditor);
1004
- };
1005
-
1006
- aElement.querySelectorAll("[data-marker]").forEach((item: HTMLElement) => {
1007
- item.removeAttribute("data-marker");
1008
- });
1009
- const inputWrap = document.createElement("span");
1010
- inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
1011
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1012
- const input = document.createElement("input");
1013
- inputWrap.appendChild(input);
1014
- input.className = "vditor-input";
1015
- input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
1016
- input.style.width = "120px";
1017
- input.value = aElement.innerHTML || "";
1018
- input.oninput = () => {
1019
- updateA();
1020
- };
1021
- input.onkeydown = (event) => {
1022
- if (removeBlockElement(vditor, event)) {
1023
- return;
1024
- }
1025
- if (focusToElement(event, range)) {
1026
- return;
1027
- }
1028
- linkHotkey(vditor, aElement, event, input1);
1029
- };
1030
-
1031
- const input1Wrap = document.createElement("span");
1032
- input1Wrap.setAttribute("aria-label", window.VditorI18n.link);
1033
- input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1034
- const input1 = document.createElement("input");
1035
- input1Wrap.appendChild(input1);
1036
- input1.className = "vditor-input";
1037
- input1.setAttribute("placeholder", window.VditorI18n.link);
1038
- input1.value = aElement.getAttribute("href") || "";
1039
- input1.oninput = () => {
1040
- updateA();
1041
- };
1042
- input1.onkeydown = (event) => {
1043
- if (removeBlockElement(vditor, event)) {
1044
- return;
1045
- }
1046
- if (focusToElement(event, range)) {
1047
- return;
1048
- }
1049
- linkHotkey(vditor, aElement, event, input2);
1050
- };
1051
-
1052
- const input2Wrap = document.createElement("span");
1053
- input2Wrap.setAttribute("aria-label", window.VditorI18n.tooltipText);
1054
- input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1055
- const input2 = document.createElement("input");
1056
- input2Wrap.appendChild(input2);
1057
- input2.className = "vditor-input";
1058
- input2.setAttribute("placeholder", window.VditorI18n.tooltipText);
1059
- input2.style.width = "60px";
1060
- input2.value = aElement.getAttribute("title") || "";
1061
- input2.oninput = () => {
1062
- updateA();
1063
- };
1064
- input2.onkeydown = (event) => {
1065
- if (removeBlockElement(vditor, event)) {
1066
- return;
1067
- }
1068
- if (focusToElement(event, range)) {
1069
- return;
1070
- }
1071
- linkHotkey(vditor, aElement, event, input);
1072
- };
1073
-
1074
- genClose(aElement, vditor);
1075
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1076
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
1077
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
1078
- customWysiwygToolbar(vditor, "a")
1079
- setPopoverPosition(vditor, aElement);
1080
- };
1081
-
1082
- export const genImagePopover = (event: Event, vditor: IVditor) => {
1083
- const imgElement = event.target as HTMLImageElement;
1084
- vditor.wysiwyg.popover.innerHTML = "";
1085
- const updateImg = () => {
1086
- imgElement.setAttribute("src", inputElement.value);
1087
- imgElement.setAttribute("alt", alt.value);
1088
- imgElement.setAttribute("title", title.value);
1089
- if (typeof vditor.options.input === "function") {
1090
- vditor.options.input(getMarkdown(vditor));
1091
- }
1092
- };
1093
-
1094
- const inputWrap = document.createElement("span");
1095
- inputWrap.setAttribute("aria-label", window.VditorI18n.imageURL);
1096
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1097
- const inputElement = document.createElement("input");
1098
- inputWrap.appendChild(inputElement);
1099
- inputElement.className = "vditor-input";
1100
- inputElement.setAttribute("placeholder", window.VditorI18n.imageURL);
1101
- inputElement.value = imgElement.getAttribute("src") || "";
1102
- inputElement.oninput = () => {
1103
- updateImg();
1104
- };
1105
- inputElement.onkeydown = (elementEvent) => {
1106
- removeBlockElement(vditor, elementEvent);
1107
- };
1108
-
1109
- const altWrap = document.createElement("span");
1110
- altWrap.setAttribute("aria-label", window.VditorI18n.alternateText);
1111
- altWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1112
- const alt = document.createElement("input");
1113
- altWrap.appendChild(alt);
1114
- alt.className = "vditor-input";
1115
- alt.setAttribute("placeholder", window.VditorI18n.alternateText);
1116
- alt.style.width = "52px";
1117
- alt.value = imgElement.getAttribute("alt") || "";
1118
- alt.oninput = () => {
1119
- updateImg();
1120
- };
1121
- alt.onkeydown = (elementEvent) => {
1122
- removeBlockElement(vditor, elementEvent);
1123
- };
1124
-
1125
- const titleWrap = document.createElement("span");
1126
- titleWrap.setAttribute("aria-label", window.VditorI18n.title);
1127
- titleWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1128
- const title = document.createElement("input");
1129
- titleWrap.appendChild(title);
1130
- title.className = "vditor-input";
1131
- title.setAttribute("placeholder", window.VditorI18n.title);
1132
- title.value = imgElement.getAttribute("title") || "";
1133
- title.oninput = () => {
1134
- updateImg();
1135
- };
1136
- title.onkeydown = (elementEvent) => {
1137
- removeBlockElement(vditor, elementEvent);
1138
- };
1139
- genClose(imgElement, vditor);
1140
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1141
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
1142
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
1143
- customWysiwygToolbar(vditor, "image")
1144
- setPopoverPosition(vditor, imgElement);
1145
- };
1146
-
1147
-
1148
- const focusToElement = (event: KeyboardEvent, range: Range) => {
1149
- if ((!isCtrl(event) && !event.shiftKey && event.key === "Enter") || event.key === "Escape") {
1150
- if (range) {
1151
- setSelectionFocus(range);
1152
- }
1153
- event.preventDefault();
1154
- event.stopPropagation();
1155
- return true;
1156
- }
1157
- };
1158
-
1159
- const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
1160
- if (vditor.options.customWysiwygToolbar) {
1161
- vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
1162
- }
1163
- };
1
+ import {Constants} from "../constants";
2
+ import {disableToolbar} from "../toolbar/setToolbar";
3
+ import {enableToolbar} from "../toolbar/setToolbar";
4
+ import {removeCurrentToolbar} from "../toolbar/setToolbar";
5
+ import {setCurrentToolbar} from "../toolbar/setToolbar";
6
+ import {isCtrl, updateHotkeyTip} from "../util/compatibility";
7
+ import {scrollCenter} from "../util/editorCommonEvent";
8
+ import {
9
+ deleteColumn,
10
+ deleteRow,
11
+ insertColumn,
12
+ insertRow,
13
+ insertRowAbove,
14
+ setTableAlign,
15
+ } from "../util/fixBrowserBehavior";
16
+ import {
17
+ hasClosestByAttribute,
18
+ hasClosestByClassName,
19
+ hasClosestByMatchTag,
20
+ } from "../util/hasClosest";
21
+ import {
22
+ hasClosestByHeadings,
23
+ hasClosestByTag,
24
+ } from "../util/hasClosestByHeadings";
25
+ import {processCodeRender} from "../util/processCode";
26
+ import {
27
+ getEditorRange,
28
+ selectIsEditor,
29
+ setRangeByWbr,
30
+ setSelectionFocus,
31
+ } from "../util/selection";
32
+ import {afterRenderEvent} from "./afterRenderEvent";
33
+ import {removeBlockElement} from "./processKeydown";
34
+ import {renderToc} from "../util/toc";
35
+ import {getMarkdown} from "../markdown/getMarkdown";
36
+
37
+ export const highlightToolbarWYSIWYG = (vditor: IVditor) => {
38
+ clearTimeout(vditor.wysiwyg.hlToolbarTimeoutId);
39
+ vditor.wysiwyg.hlToolbarTimeoutId = window.setTimeout(() => {
40
+ if (
41
+ vditor.wysiwyg.element.getAttribute("contenteditable") === "false"
42
+ ) {
43
+ return;
44
+ }
45
+ if (!selectIsEditor(vditor.wysiwyg.element)) {
46
+ return;
47
+ }
48
+
49
+ removeCurrentToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
50
+ enableToolbar(vditor.toolbar.elements, Constants.EDIT_TOOLBARS);
51
+
52
+ const range = getSelection().getRangeAt(0);
53
+ let typeElement = range.startContainer as HTMLElement;
54
+ if (range.startContainer.nodeType === 3) {
55
+ typeElement = range.startContainer.parentElement;
56
+ } else {
57
+ typeElement = typeElement.childNodes[
58
+ range.startOffset >= typeElement.childNodes.length
59
+ ? typeElement.childNodes.length - 1
60
+ : range.startOffset
61
+ ] as HTMLElement;
62
+ }
63
+
64
+ const footnotesElement = hasClosestByAttribute(typeElement, "data-type", "footnotes-block");
65
+ if (footnotesElement) {
66
+ vditor.wysiwyg.popover.innerHTML = "";
67
+ genClose(footnotesElement, vditor);
68
+ customWysiwygToolbar(vditor, "footnotes-block")
69
+ setPopoverPosition(vditor, footnotesElement);
70
+ return;
71
+ }
72
+
73
+ // 工具栏高亮和禁用
74
+ const liElement = hasClosestByMatchTag(typeElement, "LI");
75
+ if (liElement) {
76
+ if (liElement.classList.contains("vditor-task")) {
77
+ setCurrentToolbar(vditor.toolbar.elements, ["check"]);
78
+ } else if (liElement.parentElement.tagName === "OL") {
79
+ setCurrentToolbar(vditor.toolbar.elements, ["ordered-list"]);
80
+ } else if (liElement.parentElement.tagName === "UL") {
81
+ setCurrentToolbar(vditor.toolbar.elements, ["list"]);
82
+ }
83
+ enableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
84
+ } else {
85
+ disableToolbar(vditor.toolbar.elements, ["outdent", "indent"]);
86
+ }
87
+
88
+ if (hasClosestByMatchTag(typeElement, "BLOCKQUOTE")) {
89
+ setCurrentToolbar(vditor.toolbar.elements, ["quote"]);
90
+ }
91
+
92
+ if (
93
+ hasClosestByMatchTag(typeElement, "B") ||
94
+ hasClosestByMatchTag(typeElement, "STRONG")
95
+ ) {
96
+ setCurrentToolbar(vditor.toolbar.elements, ["bold"]);
97
+ }
98
+
99
+ if (
100
+ hasClosestByMatchTag(typeElement, "I") ||
101
+ hasClosestByMatchTag(typeElement, "EM")
102
+ ) {
103
+ setCurrentToolbar(vditor.toolbar.elements, ["italic"]);
104
+ }
105
+
106
+ if (
107
+ hasClosestByMatchTag(typeElement, "STRIKE") ||
108
+ hasClosestByMatchTag(typeElement, "S")
109
+ ) {
110
+ setCurrentToolbar(vditor.toolbar.elements, ["strike"]);
111
+ }
112
+
113
+ // comments
114
+ vditor.wysiwyg.element
115
+ .querySelectorAll(".vditor-comment--focus")
116
+ .forEach((item) => {
117
+ item.classList.remove("vditor-comment--focus");
118
+ });
119
+ const commentElement = hasClosestByClassName(typeElement, "vditor-comment");
120
+ if (commentElement) {
121
+ let ids = commentElement.getAttribute("data-cmtids").split(" ");
122
+ if (ids.length > 1 && commentElement.nextSibling.isSameNode(commentElement.nextElementSibling)) {
123
+ const nextIds = commentElement.nextElementSibling
124
+ .getAttribute("data-cmtids")
125
+ .split(" ");
126
+ ids.find((id) => {
127
+ if (nextIds.includes(id)) {
128
+ ids = [id];
129
+ return true;
130
+ }
131
+ });
132
+ }
133
+ vditor.wysiwyg.element
134
+ .querySelectorAll(".vditor-comment")
135
+ .forEach((item) => {
136
+ if (item.getAttribute("data-cmtids").indexOf(ids[0]) > -1) {
137
+ item.classList.add("vditor-comment--focus");
138
+ }
139
+ });
140
+ }
141
+
142
+ const aElement = hasClosestByMatchTag(typeElement, "A");
143
+ if (aElement) {
144
+ setCurrentToolbar(vditor.toolbar.elements, ["link"]);
145
+ }
146
+ const tableElement = hasClosestByMatchTag(typeElement, "TABLE") as HTMLTableElement;
147
+ const headingElement = hasClosestByHeadings(typeElement) as HTMLElement;
148
+ if (hasClosestByMatchTag(typeElement, "CODE")) {
149
+ if (hasClosestByMatchTag(typeElement, "PRE")) {
150
+ disableToolbar(vditor.toolbar.elements, [
151
+ "headings",
152
+ "bold",
153
+ "italic",
154
+ "strike",
155
+ "line",
156
+ "quote",
157
+ "list",
158
+ "ordered-list",
159
+ "check",
160
+ "code",
161
+ "inline-code",
162
+ "upload",
163
+ "link",
164
+ "table",
165
+ "record",
166
+ ]);
167
+ setCurrentToolbar(vditor.toolbar.elements, ["code"]);
168
+ } else {
169
+ disableToolbar(vditor.toolbar.elements, [
170
+ "headings",
171
+ "bold",
172
+ "italic",
173
+ "strike",
174
+ "line",
175
+ "quote",
176
+ "list",
177
+ "ordered-list",
178
+ "check",
179
+ "code",
180
+ "upload",
181
+ "link",
182
+ "table",
183
+ "record",
184
+ ]);
185
+ setCurrentToolbar(vditor.toolbar.elements, ["inline-code"]);
186
+ }
187
+ } else if (headingElement) {
188
+ disableToolbar(vditor.toolbar.elements, ["bold"]);
189
+ setCurrentToolbar(vditor.toolbar.elements, ["headings"]);
190
+ } else if (tableElement) {
191
+ disableToolbar(vditor.toolbar.elements, ["table"]);
192
+ }
193
+
194
+ // toc popover
195
+ const tocElement = hasClosestByClassName(typeElement, "vditor-toc") as HTMLElement;
196
+ if (tocElement) {
197
+ vditor.wysiwyg.popover.innerHTML = "";
198
+ genClose(tocElement, vditor);
199
+ customWysiwygToolbar(vditor, "vditor-toc")
200
+ setPopoverPosition(vditor, tocElement);
201
+ return;
202
+ }
203
+
204
+ // quote popover
205
+ const blockquoteElement = hasClosestByTag(typeElement, "BLOCKQUOTE") as HTMLTableElement;
206
+ if (blockquoteElement) {
207
+ vditor.wysiwyg.popover.innerHTML = "";
208
+ genUp(range, blockquoteElement, vditor);
209
+ genDown(range, blockquoteElement, vditor);
210
+ genClose(blockquoteElement, vditor);
211
+ customWysiwygToolbar(vditor, "blockquote")
212
+ setPopoverPosition(vditor, blockquoteElement);
213
+ }
214
+
215
+ // list item popover
216
+ if (liElement) {
217
+ vditor.wysiwyg.popover.innerHTML = "";
218
+
219
+ genUp(range, liElement, vditor);
220
+ genDown(range, liElement, vditor);
221
+ genClose(liElement, vditor);
222
+ customWysiwygToolbar(vditor, "li")
223
+ setPopoverPosition(vditor, liElement);
224
+ }
225
+
226
+ // table popover
227
+ if (tableElement) {
228
+ const lang: keyof II18n | "" = vditor.options.lang;
229
+ const options: IOptions = vditor.options;
230
+ vditor.wysiwyg.popover.innerHTML = "";
231
+ const updateTable = () => {
232
+ const oldRow = tableElement.rows.length;
233
+ const oldColumn = tableElement.rows[0].cells.length;
234
+ const row = parseInt(input.value, 10) || oldRow;
235
+ const column = parseInt(input2.value, 10) || oldColumn;
236
+
237
+ if (row === oldRow && oldColumn === column) {
238
+ return;
239
+ }
240
+
241
+ if (oldColumn !== column) {
242
+ const columnDiff = column - oldColumn;
243
+ for (let i = 0; i < tableElement.rows.length; i++) {
244
+ if (columnDiff > 0) {
245
+ for (let j = 0; j < columnDiff; j++) {
246
+ if (i === 0) {
247
+ tableElement.rows[i].lastElementChild.insertAdjacentHTML("afterend", "<th> </th>");
248
+ } else {
249
+ tableElement.rows[i].lastElementChild.insertAdjacentHTML("afterend", "<td> </td>");
250
+ }
251
+ }
252
+ } else {
253
+ for (let k = oldColumn - 1; k >= column; k--) {
254
+ tableElement.rows[i].cells[k].remove();
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ if (oldRow !== row) {
261
+ const rowDiff = row - oldRow;
262
+ if (rowDiff > 0) {
263
+ let rowHTML = "<tr>";
264
+ for (let m = 0; m < column; m++) {
265
+ rowHTML += "<td> </td>";
266
+ }
267
+ for (let l = 0; l < rowDiff; l++) {
268
+ if (tableElement.querySelector("tbody")) {
269
+ tableElement
270
+ .querySelector("tbody")
271
+ .insertAdjacentHTML("beforeend", rowHTML);
272
+ } else {
273
+ tableElement
274
+ .querySelector("thead")
275
+ .insertAdjacentHTML("afterend", rowHTML + "</tr>");
276
+ }
277
+ }
278
+ } else {
279
+ for (let m = oldRow - 1; m >= row; m--) {
280
+ tableElement.rows[m].remove();
281
+ if (tableElement.rows.length === 1) {
282
+ tableElement.querySelector("tbody").remove();
283
+ }
284
+ }
285
+ }
286
+ }
287
+ if (typeof vditor.options.input === "function") {
288
+ vditor.options.input(getMarkdown(vditor));
289
+ }
290
+ };
291
+
292
+ const setAlign = (type: string) => {
293
+ setTableAlign(tableElement, type);
294
+ if (type === "right") {
295
+ left.classList.remove("vditor-icon--current");
296
+ center.classList.remove("vditor-icon--current");
297
+ right.classList.add("vditor-icon--current");
298
+ } else if (type === "center") {
299
+ left.classList.remove("vditor-icon--current");
300
+ right.classList.remove("vditor-icon--current");
301
+ center.classList.add("vditor-icon--current");
302
+ } else {
303
+ center.classList.remove("vditor-icon--current");
304
+ right.classList.remove("vditor-icon--current");
305
+ left.classList.add("vditor-icon--current");
306
+ }
307
+ setSelectionFocus(range);
308
+ afterRenderEvent(vditor);
309
+ };
310
+
311
+ const td = hasClosestByMatchTag(typeElement, "TD");
312
+ const th = hasClosestByMatchTag(typeElement, "TH");
313
+ let alignType = "left";
314
+ if (td) {
315
+ alignType = td.getAttribute("align") || "left";
316
+ } else if (th) {
317
+ alignType = th.getAttribute("align") || "center";
318
+ }
319
+
320
+ const left = document.createElement("button");
321
+ left.setAttribute("type", "button");
322
+ left.setAttribute("aria-label", window.VditorI18n.alignLeft + "<" + updateHotkeyTip("⇧⌘L") + ">");
323
+ left.setAttribute("data-type", "left");
324
+ left.innerHTML =
325
+ '<svg><use xlink:href="#vditor-icon-align-left"></use></svg>';
326
+ left.className =
327
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
328
+ (alignType === "left" ? " vditor-icon--current" : "");
329
+ left.onclick = () => {
330
+ setAlign("left");
331
+ };
332
+
333
+ const center = document.createElement("button");
334
+ center.setAttribute("type", "button");
335
+ center.setAttribute("aria-label", window.VditorI18n.alignCenter + "<" + updateHotkeyTip("⇧⌘C") + ">");
336
+ center.setAttribute("data-type", "center");
337
+ center.innerHTML =
338
+ '<svg><use xlink:href="#vditor-icon-align-center"></use></svg>';
339
+ center.className =
340
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
341
+ (alignType === "center" ? " vditor-icon--current" : "");
342
+ center.onclick = () => {
343
+ setAlign("center");
344
+ };
345
+
346
+ const right = document.createElement("button");
347
+ right.setAttribute("type", "button");
348
+ right.setAttribute("aria-label", window.VditorI18n.alignRight + "<" + updateHotkeyTip("⇧⌘R") + ">");
349
+ right.setAttribute("data-type", "right");
350
+ right.innerHTML =
351
+ '<svg><use xlink:href="#vditor-icon-align-right"></use></svg>';
352
+ right.className =
353
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n" +
354
+ (alignType === "right" ? " vditor-icon--current" : "");
355
+ right.onclick = () => {
356
+ setAlign("right");
357
+ };
358
+
359
+ const insertRowElement = document.createElement("button");
360
+ insertRowElement.setAttribute("type", "button");
361
+ insertRowElement.setAttribute("aria-label", window.VditorI18n.insertRowBelow + "<" + updateHotkeyTip("⌘=") + ">");
362
+ insertRowElement.setAttribute("data-type", "insertRow");
363
+ insertRowElement.innerHTML =
364
+ '<svg><use xlink:href="#vditor-icon-insert-row"></use></svg>';
365
+ insertRowElement.className =
366
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
367
+ insertRowElement.onclick = () => {
368
+ const startContainer = getSelection().getRangeAt(0)
369
+ .startContainer;
370
+ const cellElement =
371
+ hasClosestByMatchTag(startContainer, "TD") ||
372
+ hasClosestByMatchTag(startContainer, "TH");
373
+ if (cellElement) {
374
+ insertRow(vditor, range, cellElement);
375
+ }
376
+ };
377
+
378
+ const insertRowBElement = document.createElement("button");
379
+ insertRowBElement.setAttribute("type", "button");
380
+ insertRowBElement.setAttribute("aria-label",
381
+ window.VditorI18n.insertRowAbove + "<" + updateHotkeyTip("⇧⌘F") + ">");
382
+ insertRowBElement.setAttribute("data-type", "insertRow");
383
+ insertRowBElement.innerHTML =
384
+ '<svg><use xlink:href="#vditor-icon-insert-rowb"></use></svg>';
385
+ insertRowBElement.className =
386
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
387
+ insertRowBElement.onclick = () => {
388
+ const startContainer = getSelection().getRangeAt(0)
389
+ .startContainer;
390
+ const cellElement =
391
+ hasClosestByMatchTag(startContainer, "TD") ||
392
+ hasClosestByMatchTag(startContainer, "TH");
393
+ if (cellElement) {
394
+ insertRowAbove(vditor, range, cellElement);
395
+ }
396
+ };
397
+
398
+ const insertColumnElement = document.createElement("button");
399
+ insertColumnElement.setAttribute("type", "button");
400
+ insertColumnElement.setAttribute("aria-label", window.VditorI18n.insertColumnRight + "<" + updateHotkeyTip("⇧⌘=") + ">");
401
+ insertColumnElement.setAttribute("data-type", "insertColumn");
402
+ insertColumnElement.innerHTML =
403
+ '<svg><use xlink:href="#vditor-icon-insert-column"></use></svg>';
404
+ insertColumnElement.className =
405
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
406
+ insertColumnElement.onclick = () => {
407
+ const startContainer = getSelection().getRangeAt(0)
408
+ .startContainer;
409
+ const cellElement =
410
+ hasClosestByMatchTag(startContainer, "TD") ||
411
+ hasClosestByMatchTag(startContainer, "TH");
412
+ if (cellElement) {
413
+ insertColumn(vditor, tableElement, cellElement);
414
+ }
415
+ };
416
+
417
+ const insertColumnBElement = document.createElement("button");
418
+ insertColumnBElement.setAttribute("type", "button");
419
+ insertColumnBElement.setAttribute("aria-label", window.VditorI18n.insertColumnLeft + "<" + updateHotkeyTip("⇧⌘G") + ">");
420
+ insertColumnBElement.setAttribute("data-type", "insertColumn");
421
+ insertColumnBElement.innerHTML =
422
+ '<svg><use xlink:href="#vditor-icon-insert-columnb"></use></svg>';
423
+ insertColumnBElement.className =
424
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
425
+ insertColumnBElement.onclick = () => {
426
+ const startContainer = getSelection().getRangeAt(0)
427
+ .startContainer;
428
+ const cellElement =
429
+ hasClosestByMatchTag(startContainer, "TD") ||
430
+ hasClosestByMatchTag(startContainer, "TH");
431
+ if (cellElement) {
432
+ insertColumn(vditor, tableElement, cellElement, "beforebegin");
433
+ }
434
+ };
435
+
436
+ const deleteRowElement = document.createElement("button");
437
+ deleteRowElement.setAttribute("type", "button");
438
+ deleteRowElement.setAttribute("aria-label", window.VditorI18n["delete-row"] + "<" + updateHotkeyTip("⌘-") + ">");
439
+ deleteRowElement.setAttribute("data-type", "deleteRow");
440
+ deleteRowElement.innerHTML =
441
+ '<svg><use xlink:href="#vditor-icon-delete-row"></use></svg>';
442
+ deleteRowElement.className =
443
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
444
+ deleteRowElement.onclick = () => {
445
+ const startContainer = getSelection().getRangeAt(0)
446
+ .startContainer;
447
+ const cellElement =
448
+ hasClosestByMatchTag(startContainer, "TD") ||
449
+ hasClosestByMatchTag(startContainer, "TH");
450
+ if (cellElement) {
451
+ deleteRow(vditor, range, cellElement);
452
+ }
453
+ };
454
+
455
+ const deleteColumnElement = document.createElement("button");
456
+ deleteColumnElement.setAttribute("type", "button");
457
+ deleteColumnElement.setAttribute("aria-label", window.VditorI18n["delete-column"] + "<" + updateHotkeyTip("⇧⌘-") + ">");
458
+ deleteColumnElement.setAttribute("data-type", "deleteColumn");
459
+ deleteColumnElement.innerHTML =
460
+ '<svg><use xlink:href="#vditor-icon-delete-column"></use></svg>';
461
+ deleteColumnElement.className =
462
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
463
+ deleteColumnElement.onclick = () => {
464
+ const startContainer = getSelection().getRangeAt(0)
465
+ .startContainer;
466
+ const cellElement =
467
+ hasClosestByMatchTag(startContainer, "TD") ||
468
+ hasClosestByMatchTag(startContainer, "TH");
469
+ if (cellElement) {
470
+ deleteColumn(vditor, range, tableElement, cellElement);
471
+ }
472
+ };
473
+
474
+ const inputWrap = document.createElement("span");
475
+ inputWrap.setAttribute("aria-label", window.VditorI18n.row);
476
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
477
+ const input = document.createElement("input");
478
+ inputWrap.appendChild(input);
479
+ input.type = "number";
480
+ input.min = "1";
481
+ input.className = "vditor-input";
482
+ input.style.width = "42px";
483
+ input.style.textAlign = "center";
484
+ input.setAttribute("placeholder", window.VditorI18n.row);
485
+ input.value = tableElement.rows.length.toString();
486
+ input.oninput = () => {
487
+ updateTable();
488
+ };
489
+ input.onkeydown = (event) => {
490
+ if (event.isComposing) {
491
+ return;
492
+ }
493
+ if (event.key === "Tab") {
494
+ input2.focus();
495
+ input2.select();
496
+ event.preventDefault();
497
+ return;
498
+ }
499
+ if (removeBlockElement(vditor, event)) {
500
+ return;
501
+ }
502
+ if (focusToElement(event, range)) {
503
+ return;
504
+ }
505
+ };
506
+
507
+ const input2Wrap = document.createElement("span");
508
+ input2Wrap.setAttribute("aria-label", window.VditorI18n.column);
509
+ input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
510
+ const input2 = document.createElement("input");
511
+ input2Wrap.appendChild(input2);
512
+ input2.type = "number";
513
+ input2.min = "1";
514
+ input2.className = "vditor-input";
515
+ input2.style.width = "42px";
516
+ input2.style.textAlign = "center";
517
+ input2.setAttribute("placeholder", window.VditorI18n.column);
518
+ input2.value = tableElement.rows[0].cells.length.toString();
519
+ input2.oninput = () => {
520
+ updateTable();
521
+ };
522
+ input2.onkeydown = (event) => {
523
+ if (event.isComposing) {
524
+ return;
525
+ }
526
+ if (event.key === "Tab") {
527
+ input.focus();
528
+ input.select();
529
+ event.preventDefault();
530
+ return;
531
+ }
532
+ if (removeBlockElement(vditor, event)) {
533
+ return;
534
+ }
535
+ if (focusToElement(event, range)) {
536
+ return;
537
+ }
538
+ };
539
+
540
+ genUp(range, tableElement, vditor);
541
+ genDown(range, tableElement, vditor);
542
+ genClose(tableElement, vditor);
543
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", left);
544
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", center);
545
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", right);
546
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowBElement);
547
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertRowElement);
548
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnBElement);
549
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", insertColumnElement);
550
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteRowElement);
551
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", deleteColumnElement);
552
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
553
+ vditor.wysiwyg.popover.insertAdjacentHTML("beforeend", " x ");
554
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
555
+ customWysiwygToolbar(vditor, "table")
556
+ setPopoverPosition(vditor, tableElement);
557
+ }
558
+
559
+ // link ref popover
560
+ const linkRefElement = hasClosestByAttribute(typeElement, "data-type", "link-ref");
561
+ if (linkRefElement) {
562
+ genLinkRefPopover(vditor, linkRefElement, range);
563
+ }
564
+
565
+ // footnote popover
566
+ const footnotesRefElement = hasClosestByAttribute(typeElement, "data-type", "footnotes-ref");
567
+ if (footnotesRefElement) {
568
+ const lang: keyof II18n | "" = vditor.options.lang;
569
+ const options: IOptions = vditor.options;
570
+ vditor.wysiwyg.popover.innerHTML = "";
571
+
572
+ const inputWrap = document.createElement("span");
573
+ inputWrap.setAttribute("aria-label", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
574
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
575
+ const input = document.createElement("input");
576
+ inputWrap.appendChild(input);
577
+ input.className = "vditor-input";
578
+ input.setAttribute("placeholder", window.VditorI18n.footnoteRef + "<" + updateHotkeyTip("⌥Enter") + ">");
579
+ input.style.width = "120px";
580
+ input.value = footnotesRefElement.getAttribute("data-footnotes-label");
581
+ input.oninput = () => {
582
+ if (input.value.trim() !== "") {
583
+ footnotesRefElement.setAttribute("data-footnotes-label", input.value);
584
+ }
585
+ if (typeof vditor.options.input === "function") {
586
+ vditor.options.input(getMarkdown(vditor));
587
+ }
588
+ };
589
+ input.onkeydown = (event) => {
590
+ if (event.isComposing) {
591
+ return;
592
+ }
593
+ if (removeBlockElement(vditor, event)) {
594
+ return;
595
+ }
596
+ if (focusToElement(event, range)) {
597
+ return;
598
+ }
599
+ };
600
+
601
+ genClose(footnotesRefElement, vditor);
602
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
603
+ customWysiwygToolbar(vditor, "footnotes-ref")
604
+ setPopoverPosition(vditor, footnotesRefElement);
605
+ }
606
+
607
+ // block popover: math-inline, math-block, html-block, html-inline, code-block, html-entity
608
+ let blockRenderElement = hasClosestByClassName(typeElement, "vditor-wysiwyg__block") as HTMLElement;
609
+ const isBlock = blockRenderElement ? blockRenderElement.getAttribute("data-type").indexOf("block") > -1 : false;
610
+ vditor.wysiwyg.element
611
+ .querySelectorAll(".vditor-wysiwyg__preview")
612
+ .forEach((itemElement) => {
613
+ if (!blockRenderElement || (blockRenderElement && isBlock && !blockRenderElement.contains(itemElement))) {
614
+ const previousElement = itemElement.previousElementSibling as HTMLElement;
615
+ previousElement.style.display = "none";
616
+ }
617
+ });
618
+ if (blockRenderElement && isBlock) {
619
+ // 如果代码块处于编辑状态,则不显示工具栏
620
+ const isCodeBlockEditing = blockRenderElement.classList.contains("vditor-wysiwyg__block--editing");
621
+ if (isCodeBlockEditing) {
622
+ vditor.wysiwyg.popover.style.display = "none";
623
+ return;
624
+ }
625
+
626
+ vditor.wysiwyg.popover.innerHTML = "";
627
+ genUp(range, blockRenderElement, vditor);
628
+ genDown(range, blockRenderElement, vditor);
629
+ genClose(blockRenderElement, vditor);
630
+
631
+ if (blockRenderElement.getAttribute("data-type") === "code-block") {
632
+ const languageWrap = document.createElement("span");
633
+ languageWrap.setAttribute("aria-label", window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
634
+ languageWrap.className = "vditor-tooltipped vditor-tooltipped__n";
635
+ const language = document.createElement("input");
636
+ languageWrap.appendChild(language);
637
+
638
+ const codeElement =
639
+ blockRenderElement.firstElementChild.firstElementChild;
640
+
641
+ language.className = "vditor-input";
642
+ language.setAttribute("placeholder",
643
+ window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
644
+ language.value =
645
+ codeElement.className.indexOf("language-") > -1
646
+ ? codeElement.className.split("-")[1].split(" ")[0]
647
+ : "";
648
+ language.oninput = (e: InputEvent) => {
649
+ if (language.value.trim() !== "") {
650
+ codeElement.className = `language-${language.value}`;
651
+ } else {
652
+ codeElement.className = "";
653
+ vditor.hint.recentLanguage = "";
654
+ }
655
+ if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
656
+ blockRenderElement.lastElementChild.innerHTML =
657
+ blockRenderElement.firstElementChild.innerHTML;
658
+ processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
659
+ }
660
+ afterRenderEvent(vditor);
661
+ // 当鼠标点选语言时,触发自定义input事件
662
+ if (e.detail === 1) {
663
+ // 选择语言后,输入焦点切换到代码输入框
664
+ range.setStart(codeElement.firstChild, 0);
665
+ range.collapse(true);
666
+ setSelectionFocus(range);
667
+ }
668
+ };
669
+ language.onkeydown = (event: KeyboardEvent) => {
670
+ if (event.isComposing) {
671
+ return;
672
+ }
673
+ if (removeBlockElement(vditor, event)) {
674
+ return;
675
+ }
676
+ if (
677
+ event.key === "Escape" &&
678
+ vditor.hint.element.style.display === "block"
679
+ ) {
680
+ vditor.hint.element.style.display = "none";
681
+ event.preventDefault();
682
+ return;
683
+ }
684
+ vditor.hint.select(event, vditor);
685
+ focusToElement(event, range);
686
+ };
687
+ language.onkeyup = (event: KeyboardEvent) => {
688
+ if (
689
+ event.isComposing ||
690
+ event.key === "Enter" ||
691
+ event.key === "ArrowUp" ||
692
+ event.key === "Escape" ||
693
+ event.key === "ArrowDown"
694
+ ) {
695
+ return;
696
+ }
697
+ const matchLangData: IHintData[] = [];
698
+ const key = language.value.substring(0, language.selectionStart);
699
+ (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
700
+ if (keyName.indexOf(key.toLowerCase()) > -1) {
701
+ matchLangData.push({
702
+ html: keyName,
703
+ value: keyName,
704
+ });
705
+ }
706
+ });
707
+ vditor.hint.genHTML(matchLangData, key, vditor);
708
+ event.preventDefault();
709
+ };
710
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
711
+ customWysiwygToolbar(vditor, "code-block")
712
+ } else {
713
+ customWysiwygToolbar(vditor, "block")
714
+ }
715
+ setPopoverPosition(vditor, blockRenderElement);
716
+ } else {
717
+ blockRenderElement = undefined;
718
+ }
719
+ if (headingElement) {
720
+ vditor.wysiwyg.popover.innerHTML = "";
721
+
722
+ const inputWrap = document.createElement("span");
723
+ inputWrap.setAttribute("aria-label", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
724
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
725
+ const input = document.createElement("input");
726
+ inputWrap.appendChild(input);
727
+ input.className = "vditor-input";
728
+ input.setAttribute("placeholder", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
729
+ input.style.width = "120px";
730
+ input.value = headingElement.getAttribute("data-id") || "";
731
+ input.oninput = () => {
732
+ headingElement.setAttribute("data-id", input.value);
733
+ if (typeof vditor.options.input === "function") {
734
+ vditor.options.input(getMarkdown(vditor));
735
+ }
736
+ };
737
+ input.onkeydown = (event) => {
738
+ if (event.isComposing) {
739
+ return;
740
+ }
741
+ if (removeBlockElement(vditor, event)) {
742
+ return;
743
+ }
744
+ if (focusToElement(event, range)) {
745
+ return;
746
+ }
747
+ };
748
+
749
+ genUp(range, headingElement, vditor);
750
+ genDown(range, headingElement, vditor);
751
+ genClose(headingElement, vditor);
752
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
753
+ customWysiwygToolbar(vditor, "heading")
754
+ setPopoverPosition(vditor, headingElement);
755
+ }
756
+
757
+ // a popover
758
+ if (aElement) {
759
+ genAPopover(vditor, aElement, range);
760
+ }
761
+
762
+ if (
763
+ !blockquoteElement &&
764
+ !liElement &&
765
+ !tableElement &&
766
+ !blockRenderElement &&
767
+ !aElement &&
768
+ !linkRefElement &&
769
+ !footnotesRefElement &&
770
+ !headingElement &&
771
+ !tocElement
772
+ ) {
773
+ const blockElement = hasClosestByAttribute(typeElement, "data-block", "0");
774
+ if (
775
+ blockElement &&
776
+ blockElement.parentElement.isEqualNode(vditor.wysiwyg.element)
777
+ ) {
778
+ vditor.wysiwyg.popover.innerHTML = "";
779
+ genUp(range, blockElement, vditor);
780
+ genDown(range, blockElement, vditor);
781
+ genClose(blockElement, vditor);
782
+ customWysiwygToolbar(vditor, "block")
783
+ setPopoverPosition(vditor, blockElement);
784
+ } else {
785
+ vditor.wysiwyg.popover.style.display = "none";
786
+ }
787
+ }
788
+
789
+ // 反斜杠特殊处理
790
+ vditor.wysiwyg.element
791
+ .querySelectorAll('span[data-type="backslash"] > span')
792
+ .forEach((item: HTMLElement) => {
793
+ item.style.display = "none";
794
+ });
795
+ const backslashElement = hasClosestByAttribute(range.startContainer, "data-type", "backslash");
796
+ if (backslashElement) {
797
+ backslashElement.querySelector("span").style.display = "inline";
798
+ }
799
+ }, 200);
800
+ };
801
+
802
+ const setPopoverPosition = (vditor: IVditor, element: HTMLElement) => {
803
+ let targetElement = element;
804
+ const tableElement = hasClosestByMatchTag(element, "TABLE");
805
+ if (tableElement) {
806
+ targetElement = tableElement;
807
+ }
808
+ vditor.wysiwyg.popover.style.left = "0";
809
+ vditor.wysiwyg.popover.style.display = "block";
810
+ vditor.wysiwyg.popover.style.top =
811
+ Math.max(-8, targetElement.offsetTop - 21 - vditor.wysiwyg.element.scrollTop) + "px";
812
+ vditor.wysiwyg.popover.style.left =
813
+ Math.min(targetElement.offsetLeft, vditor.wysiwyg.element.clientWidth - vditor.wysiwyg.popover.clientWidth) + "px";
814
+ vditor.wysiwyg.popover.setAttribute("data-top", (targetElement.offsetTop - 21).toString());
815
+ };
816
+
817
+ export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement, range = getSelection().getRangeAt(0)) => {
818
+ vditor.wysiwyg.popover.innerHTML = "";
819
+ const updateLinkRef = () => {
820
+ if (input.value.trim() !== "") {
821
+ if (linkRefElement.tagName === "IMG") {
822
+ linkRefElement.setAttribute("alt", input.value);
823
+ } else {
824
+ linkRefElement.textContent = input.value;
825
+ }
826
+ }
827
+ // data-link-label
828
+ if (input1.value.trim() !== "") {
829
+ linkRefElement.setAttribute("data-link-label", input1.value);
830
+ }
831
+ if (typeof vditor.options.input === "function") {
832
+ vditor.options.input(getMarkdown(vditor));
833
+ }
834
+ };
835
+
836
+ const inputWrap = document.createElement("span");
837
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
838
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
839
+ const input = document.createElement("input");
840
+ inputWrap.appendChild(input);
841
+ input.className = "vditor-input";
842
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
843
+ input.style.width = "120px";
844
+ input.value =
845
+ linkRefElement.getAttribute("alt") || linkRefElement.textContent;
846
+ input.oninput = () => {
847
+ updateLinkRef();
848
+ };
849
+ input.onkeydown = (event) => {
850
+ if (removeBlockElement(vditor, event)) {
851
+ return;
852
+ }
853
+ if (focusToElement(event, range)) {
854
+ return;
855
+ }
856
+ linkHotkey(vditor, linkRefElement, event, input1);
857
+ };
858
+
859
+ const input1Wrap = document.createElement("span");
860
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.linkRef);
861
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
862
+ const input1 = document.createElement("input");
863
+ input1Wrap.appendChild(input1);
864
+ input1.className = "vditor-input";
865
+ input1.setAttribute("placeholder", window.VditorI18n.linkRef);
866
+ input1.value = linkRefElement.getAttribute("data-link-label");
867
+ input1.oninput = () => {
868
+ updateLinkRef();
869
+ };
870
+ input1.onkeydown = (event) => {
871
+ if (removeBlockElement(vditor, event)) {
872
+ return;
873
+ }
874
+ if (focusToElement(event, range)) {
875
+ return;
876
+ }
877
+ linkHotkey(vditor, linkRefElement, event, input);
878
+ };
879
+
880
+ genClose(linkRefElement, vditor);
881
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
882
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
883
+ customWysiwygToolbar(vditor, "link-ref")
884
+ setPopoverPosition(vditor, linkRefElement);
885
+ };
886
+
887
+ const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
888
+ const previousElement = element.previousElementSibling;
889
+ if (
890
+ !previousElement ||
891
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
892
+ element.tagName !== "LI")
893
+ ) {
894
+ return;
895
+ }
896
+ const upElement = document.createElement("button");
897
+ upElement.setAttribute("type", "button");
898
+ upElement.setAttribute("data-type", "up");
899
+ upElement.setAttribute("aria-label", window.VditorI18n.up + "<" + updateHotkeyTip("⇧⌘U") + ">");
900
+ upElement.innerHTML = '<svg><use xlink:href="#vditor-icon-up"></use></svg>';
901
+ upElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
902
+ upElement.onclick = () => {
903
+ range.insertNode(document.createElement("wbr"));
904
+ previousElement.insertAdjacentElement("beforebegin", element);
905
+ setRangeByWbr(vditor.wysiwyg.element, range);
906
+ afterRenderEvent(vditor);
907
+ highlightToolbarWYSIWYG(vditor);
908
+ scrollCenter(vditor);
909
+ };
910
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
911
+ };
912
+
913
+ const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
914
+ const nextElement = element.nextElementSibling;
915
+ if (
916
+ !nextElement ||
917
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
918
+ element.tagName !== "LI")
919
+ ) {
920
+ return;
921
+ }
922
+ const downElement = document.createElement("button");
923
+ downElement.setAttribute("type", "button");
924
+ downElement.setAttribute("data-type", "down");
925
+ downElement.setAttribute("aria-label", window.VditorI18n.down + "<" + updateHotkeyTip("⇧⌘D") + ">");
926
+ downElement.innerHTML =
927
+ '<svg><use xlink:href="#vditor-icon-down"></use></svg>';
928
+ downElement.className =
929
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
930
+ downElement.onclick = () => {
931
+ range.insertNode(document.createElement("wbr"));
932
+ nextElement.insertAdjacentElement("afterend", element);
933
+ setRangeByWbr(vditor.wysiwyg.element, range);
934
+ afterRenderEvent(vditor);
935
+ highlightToolbarWYSIWYG(vditor);
936
+ scrollCenter(vditor);
937
+ };
938
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
939
+ };
940
+
941
+ const genClose = (element: HTMLElement, vditor: IVditor) => {
942
+ const close = document.createElement("button");
943
+ close.setAttribute("type", "button");
944
+ close.setAttribute("data-type", "remove");
945
+ close.setAttribute("aria-label", window.VditorI18n.remove + "<" + updateHotkeyTip("⇧⌘X") + ">");
946
+ close.innerHTML =
947
+ '<svg><use xlink:href="#vditor-icon-trashcan"></use></svg>';
948
+ close.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
949
+ close.onclick = () => {
950
+ const range = getEditorRange(vditor);
951
+ range.setStartAfter(element);
952
+ setSelectionFocus(range);
953
+ element.remove();
954
+ afterRenderEvent(vditor);
955
+ highlightToolbarWYSIWYG(vditor);
956
+ if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
957
+ renderToc(vditor);
958
+ }
959
+ };
960
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
961
+ };
962
+
963
+ const linkHotkey = (
964
+ vditor: IVditor,
965
+ element: HTMLElement,
966
+ event: KeyboardEvent,
967
+ nextInputElement: HTMLInputElement,
968
+ ) => {
969
+ if (event.isComposing) {
970
+ return;
971
+ }
972
+ if (event.key === "Tab") {
973
+ nextInputElement.focus();
974
+ nextInputElement.select();
975
+ event.preventDefault();
976
+ return;
977
+ }
978
+ if (
979
+ !isCtrl(event) &&
980
+ !event.shiftKey &&
981
+ event.altKey &&
982
+ event.key === "Enter"
983
+ ) {
984
+ const range = getEditorRange(vditor);
985
+ // firefox 不会打断 link https://github.com/Vanessa219/vditor/issues/193
986
+ element.insertAdjacentHTML("afterend", Constants.ZWSP);
987
+ range.setStartAfter(element.nextSibling);
988
+ range.collapse(true);
989
+ setSelectionFocus(range);
990
+ event.preventDefault();
991
+ }
992
+ };
993
+
994
+ export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range) => {
995
+ vditor.wysiwyg.popover.innerHTML = "";
996
+
997
+ const updateA = () => {
998
+ if (input.value.trim() !== "") {
999
+ aElement.innerHTML = input.value;
1000
+ }
1001
+ aElement.setAttribute("href", input1.value);
1002
+ aElement.setAttribute("title", input2.value);
1003
+ afterRenderEvent(vditor);
1004
+ };
1005
+
1006
+ aElement.querySelectorAll("[data-marker]").forEach((item: HTMLElement) => {
1007
+ item.removeAttribute("data-marker");
1008
+ });
1009
+ const inputWrap = document.createElement("span");
1010
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
1011
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1012
+ const input = document.createElement("input");
1013
+ inputWrap.appendChild(input);
1014
+ input.className = "vditor-input";
1015
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
1016
+ input.style.width = "120px";
1017
+ input.value = aElement.innerHTML || "";
1018
+ input.oninput = () => {
1019
+ updateA();
1020
+ };
1021
+ input.onkeydown = (event) => {
1022
+ if (removeBlockElement(vditor, event)) {
1023
+ return;
1024
+ }
1025
+ if (focusToElement(event, range)) {
1026
+ return;
1027
+ }
1028
+ linkHotkey(vditor, aElement, event, input1);
1029
+ };
1030
+
1031
+ const input1Wrap = document.createElement("span");
1032
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.link);
1033
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1034
+ const input1 = document.createElement("input");
1035
+ input1Wrap.appendChild(input1);
1036
+ input1.className = "vditor-input";
1037
+ input1.setAttribute("placeholder", window.VditorI18n.link);
1038
+ input1.value = aElement.getAttribute("href") || "";
1039
+ input1.oninput = () => {
1040
+ updateA();
1041
+ };
1042
+ input1.onkeydown = (event) => {
1043
+ if (removeBlockElement(vditor, event)) {
1044
+ return;
1045
+ }
1046
+ if (focusToElement(event, range)) {
1047
+ return;
1048
+ }
1049
+ linkHotkey(vditor, aElement, event, input2);
1050
+ };
1051
+
1052
+ const input2Wrap = document.createElement("span");
1053
+ input2Wrap.setAttribute("aria-label", window.VditorI18n.tooltipText);
1054
+ input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1055
+ const input2 = document.createElement("input");
1056
+ input2Wrap.appendChild(input2);
1057
+ input2.className = "vditor-input";
1058
+ input2.setAttribute("placeholder", window.VditorI18n.tooltipText);
1059
+ input2.style.width = "60px";
1060
+ input2.value = aElement.getAttribute("title") || "";
1061
+ input2.oninput = () => {
1062
+ updateA();
1063
+ };
1064
+ input2.onkeydown = (event) => {
1065
+ if (removeBlockElement(vditor, event)) {
1066
+ return;
1067
+ }
1068
+ if (focusToElement(event, range)) {
1069
+ return;
1070
+ }
1071
+ linkHotkey(vditor, aElement, event, input);
1072
+ };
1073
+
1074
+ genClose(aElement, vditor);
1075
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1076
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
1077
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
1078
+ customWysiwygToolbar(vditor, "a")
1079
+ setPopoverPosition(vditor, aElement);
1080
+ };
1081
+
1082
+ export const genImagePopover = (event: Event, vditor: IVditor) => {
1083
+ const imgElement = event.target as HTMLImageElement;
1084
+ vditor.wysiwyg.popover.innerHTML = "";
1085
+ const updateImg = () => {
1086
+ imgElement.setAttribute("src", inputElement.value);
1087
+ imgElement.setAttribute("alt", alt.value);
1088
+ imgElement.setAttribute("title", title.value);
1089
+ if (typeof vditor.options.input === "function") {
1090
+ vditor.options.input(getMarkdown(vditor));
1091
+ }
1092
+ };
1093
+
1094
+ const inputWrap = document.createElement("span");
1095
+ inputWrap.setAttribute("aria-label", window.VditorI18n.imageURL);
1096
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1097
+ const inputElement = document.createElement("input");
1098
+ inputWrap.appendChild(inputElement);
1099
+ inputElement.className = "vditor-input";
1100
+ inputElement.setAttribute("placeholder", window.VditorI18n.imageURL);
1101
+ inputElement.value = imgElement.getAttribute("src") || "";
1102
+ inputElement.oninput = () => {
1103
+ updateImg();
1104
+ };
1105
+ inputElement.onkeydown = (elementEvent) => {
1106
+ removeBlockElement(vditor, elementEvent);
1107
+ };
1108
+
1109
+ const altWrap = document.createElement("span");
1110
+ altWrap.setAttribute("aria-label", window.VditorI18n.alternateText);
1111
+ altWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1112
+ const alt = document.createElement("input");
1113
+ altWrap.appendChild(alt);
1114
+ alt.className = "vditor-input";
1115
+ alt.setAttribute("placeholder", window.VditorI18n.alternateText);
1116
+ alt.style.width = "52px";
1117
+ alt.value = imgElement.getAttribute("alt") || "";
1118
+ alt.oninput = () => {
1119
+ updateImg();
1120
+ };
1121
+ alt.onkeydown = (elementEvent) => {
1122
+ removeBlockElement(vditor, elementEvent);
1123
+ };
1124
+
1125
+ const titleWrap = document.createElement("span");
1126
+ titleWrap.setAttribute("aria-label", window.VditorI18n.title);
1127
+ titleWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1128
+ const title = document.createElement("input");
1129
+ titleWrap.appendChild(title);
1130
+ title.className = "vditor-input";
1131
+ title.setAttribute("placeholder", window.VditorI18n.title);
1132
+ title.value = imgElement.getAttribute("title") || "";
1133
+ title.oninput = () => {
1134
+ updateImg();
1135
+ };
1136
+ title.onkeydown = (elementEvent) => {
1137
+ removeBlockElement(vditor, elementEvent);
1138
+ };
1139
+ genClose(imgElement, vditor);
1140
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1141
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
1142
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
1143
+ customWysiwygToolbar(vditor, "image")
1144
+ setPopoverPosition(vditor, imgElement);
1145
+ };
1146
+
1147
+
1148
+ const focusToElement = (event: KeyboardEvent, range: Range) => {
1149
+ if ((!isCtrl(event) && !event.shiftKey && event.key === "Enter") || event.key === "Escape") {
1150
+ if (range) {
1151
+ setSelectionFocus(range);
1152
+ }
1153
+ event.preventDefault();
1154
+ event.stopPropagation();
1155
+ return true;
1156
+ }
1157
+ };
1158
+
1159
+ const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
1160
+ if (vditor.options.customWysiwygToolbar) {
1161
+ vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
1162
+ }
1163
+ };