@yltrcc/vditor 0.0.6 → 0.0.8

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,1156 +1,1156 @@
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
- vditor.wysiwyg.popover.innerHTML = "";
620
- genUp(range, blockRenderElement, vditor);
621
- genDown(range, blockRenderElement, vditor);
622
- genClose(blockRenderElement, vditor);
623
-
624
- if (blockRenderElement.getAttribute("data-type") === "code-block") {
625
- const languageWrap = document.createElement("span");
626
- languageWrap.setAttribute("aria-label", window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
627
- languageWrap.className = "vditor-tooltipped vditor-tooltipped__n";
628
- const language = document.createElement("input");
629
- languageWrap.appendChild(language);
630
-
631
- const codeElement =
632
- blockRenderElement.firstElementChild.firstElementChild;
633
-
634
- language.className = "vditor-input";
635
- language.setAttribute("placeholder",
636
- window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
637
- language.value =
638
- codeElement.className.indexOf("language-") > -1
639
- ? codeElement.className.split("-")[1].split(" ")[0]
640
- : "";
641
- language.oninput = (e: InputEvent) => {
642
- if (language.value.trim() !== "") {
643
- codeElement.className = `language-${language.value}`;
644
- } else {
645
- codeElement.className = "";
646
- vditor.hint.recentLanguage = "";
647
- }
648
- if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
649
- blockRenderElement.lastElementChild.innerHTML =
650
- blockRenderElement.firstElementChild.innerHTML;
651
- processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
652
- }
653
- afterRenderEvent(vditor);
654
- // 当鼠标点选语言时,触发自定义input事件
655
- if (e.detail === 1) {
656
- // 选择语言后,输入焦点切换到代码输入框
657
- range.setStart(codeElement.firstChild, 0);
658
- range.collapse(true);
659
- setSelectionFocus(range);
660
- }
661
- };
662
- language.onkeydown = (event: KeyboardEvent) => {
663
- if (event.isComposing) {
664
- return;
665
- }
666
- if (removeBlockElement(vditor, event)) {
667
- return;
668
- }
669
- if (
670
- event.key === "Escape" &&
671
- vditor.hint.element.style.display === "block"
672
- ) {
673
- vditor.hint.element.style.display = "none";
674
- event.preventDefault();
675
- return;
676
- }
677
- vditor.hint.select(event, vditor);
678
- focusToElement(event, range);
679
- };
680
- language.onkeyup = (event: KeyboardEvent) => {
681
- if (
682
- event.isComposing ||
683
- event.key === "Enter" ||
684
- event.key === "ArrowUp" ||
685
- event.key === "Escape" ||
686
- event.key === "ArrowDown"
687
- ) {
688
- return;
689
- }
690
- const matchLangData: IHintData[] = [];
691
- const key = language.value.substring(0, language.selectionStart);
692
- (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
693
- if (keyName.indexOf(key.toLowerCase()) > -1) {
694
- matchLangData.push({
695
- html: keyName,
696
- value: keyName,
697
- });
698
- }
699
- });
700
- vditor.hint.genHTML(matchLangData, key, vditor);
701
- event.preventDefault();
702
- };
703
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
704
- customWysiwygToolbar(vditor, "code-block")
705
- } else {
706
- customWysiwygToolbar(vditor, "block")
707
- }
708
- setPopoverPosition(vditor, blockRenderElement);
709
- } else {
710
- blockRenderElement = undefined;
711
- }
712
- if (headingElement) {
713
- vditor.wysiwyg.popover.innerHTML = "";
714
-
715
- const inputWrap = document.createElement("span");
716
- inputWrap.setAttribute("aria-label", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
717
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
718
- const input = document.createElement("input");
719
- inputWrap.appendChild(input);
720
- input.className = "vditor-input";
721
- input.setAttribute("placeholder", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
722
- input.style.width = "120px";
723
- input.value = headingElement.getAttribute("data-id") || "";
724
- input.oninput = () => {
725
- headingElement.setAttribute("data-id", input.value);
726
- if (typeof vditor.options.input === "function") {
727
- vditor.options.input(getMarkdown(vditor));
728
- }
729
- };
730
- input.onkeydown = (event) => {
731
- if (event.isComposing) {
732
- return;
733
- }
734
- if (removeBlockElement(vditor, event)) {
735
- return;
736
- }
737
- if (focusToElement(event, range)) {
738
- return;
739
- }
740
- };
741
-
742
- genUp(range, headingElement, vditor);
743
- genDown(range, headingElement, vditor);
744
- genClose(headingElement, vditor);
745
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
746
- customWysiwygToolbar(vditor, "heading")
747
- setPopoverPosition(vditor, headingElement);
748
- }
749
-
750
- // a popover
751
- if (aElement) {
752
- genAPopover(vditor, aElement, range);
753
- }
754
-
755
- if (
756
- !blockquoteElement &&
757
- !liElement &&
758
- !tableElement &&
759
- !blockRenderElement &&
760
- !aElement &&
761
- !linkRefElement &&
762
- !footnotesRefElement &&
763
- !headingElement &&
764
- !tocElement
765
- ) {
766
- const blockElement = hasClosestByAttribute(typeElement, "data-block", "0");
767
- if (
768
- blockElement &&
769
- blockElement.parentElement.isEqualNode(vditor.wysiwyg.element)
770
- ) {
771
- vditor.wysiwyg.popover.innerHTML = "";
772
- genUp(range, blockElement, vditor);
773
- genDown(range, blockElement, vditor);
774
- genClose(blockElement, vditor);
775
- customWysiwygToolbar(vditor, "block")
776
- setPopoverPosition(vditor, blockElement);
777
- } else {
778
- vditor.wysiwyg.popover.style.display = "none";
779
- }
780
- }
781
-
782
- // 反斜杠特殊处理
783
- vditor.wysiwyg.element
784
- .querySelectorAll('span[data-type="backslash"] > span')
785
- .forEach((item: HTMLElement) => {
786
- item.style.display = "none";
787
- });
788
- const backslashElement = hasClosestByAttribute(range.startContainer, "data-type", "backslash");
789
- if (backslashElement) {
790
- backslashElement.querySelector("span").style.display = "inline";
791
- }
792
- }, 200);
793
- };
794
-
795
- const setPopoverPosition = (vditor: IVditor, element: HTMLElement) => {
796
- let targetElement = element;
797
- const tableElement = hasClosestByMatchTag(element, "TABLE");
798
- if (tableElement) {
799
- targetElement = tableElement;
800
- }
801
- vditor.wysiwyg.popover.style.left = "0";
802
- vditor.wysiwyg.popover.style.display = "block";
803
- vditor.wysiwyg.popover.style.top =
804
- Math.max(-8, targetElement.offsetTop - 21 - vditor.wysiwyg.element.scrollTop) + "px";
805
- vditor.wysiwyg.popover.style.left =
806
- Math.min(targetElement.offsetLeft, vditor.wysiwyg.element.clientWidth - vditor.wysiwyg.popover.clientWidth) + "px";
807
- vditor.wysiwyg.popover.setAttribute("data-top", (targetElement.offsetTop - 21).toString());
808
- };
809
-
810
- export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement, range = getSelection().getRangeAt(0)) => {
811
- vditor.wysiwyg.popover.innerHTML = "";
812
- const updateLinkRef = () => {
813
- if (input.value.trim() !== "") {
814
- if (linkRefElement.tagName === "IMG") {
815
- linkRefElement.setAttribute("alt", input.value);
816
- } else {
817
- linkRefElement.textContent = input.value;
818
- }
819
- }
820
- // data-link-label
821
- if (input1.value.trim() !== "") {
822
- linkRefElement.setAttribute("data-link-label", input1.value);
823
- }
824
- if (typeof vditor.options.input === "function") {
825
- vditor.options.input(getMarkdown(vditor));
826
- }
827
- };
828
-
829
- const inputWrap = document.createElement("span");
830
- inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
831
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
832
- const input = document.createElement("input");
833
- inputWrap.appendChild(input);
834
- input.className = "vditor-input";
835
- input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
836
- input.style.width = "120px";
837
- input.value =
838
- linkRefElement.getAttribute("alt") || linkRefElement.textContent;
839
- input.oninput = () => {
840
- updateLinkRef();
841
- };
842
- input.onkeydown = (event) => {
843
- if (removeBlockElement(vditor, event)) {
844
- return;
845
- }
846
- if (focusToElement(event, range)) {
847
- return;
848
- }
849
- linkHotkey(vditor, linkRefElement, event, input1);
850
- };
851
-
852
- const input1Wrap = document.createElement("span");
853
- input1Wrap.setAttribute("aria-label", window.VditorI18n.linkRef);
854
- input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
855
- const input1 = document.createElement("input");
856
- input1Wrap.appendChild(input1);
857
- input1.className = "vditor-input";
858
- input1.setAttribute("placeholder", window.VditorI18n.linkRef);
859
- input1.value = linkRefElement.getAttribute("data-link-label");
860
- input1.oninput = () => {
861
- updateLinkRef();
862
- };
863
- input1.onkeydown = (event) => {
864
- if (removeBlockElement(vditor, event)) {
865
- return;
866
- }
867
- if (focusToElement(event, range)) {
868
- return;
869
- }
870
- linkHotkey(vditor, linkRefElement, event, input);
871
- };
872
-
873
- genClose(linkRefElement, vditor);
874
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
875
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
876
- customWysiwygToolbar(vditor, "link-ref")
877
- setPopoverPosition(vditor, linkRefElement);
878
- };
879
-
880
- const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
881
- const previousElement = element.previousElementSibling;
882
- if (
883
- !previousElement ||
884
- (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
885
- element.tagName !== "LI")
886
- ) {
887
- return;
888
- }
889
- const upElement = document.createElement("button");
890
- upElement.setAttribute("type", "button");
891
- upElement.setAttribute("data-type", "up");
892
- upElement.setAttribute("aria-label", window.VditorI18n.up + "<" + updateHotkeyTip("⇧⌘U") + ">");
893
- upElement.innerHTML = '<svg><use xlink:href="#vditor-icon-up"></use></svg>';
894
- upElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
895
- upElement.onclick = () => {
896
- range.insertNode(document.createElement("wbr"));
897
- previousElement.insertAdjacentElement("beforebegin", element);
898
- setRangeByWbr(vditor.wysiwyg.element, range);
899
- afterRenderEvent(vditor);
900
- highlightToolbarWYSIWYG(vditor);
901
- scrollCenter(vditor);
902
- };
903
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
904
- };
905
-
906
- const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
907
- const nextElement = element.nextElementSibling;
908
- if (
909
- !nextElement ||
910
- (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
911
- element.tagName !== "LI")
912
- ) {
913
- return;
914
- }
915
- const downElement = document.createElement("button");
916
- downElement.setAttribute("type", "button");
917
- downElement.setAttribute("data-type", "down");
918
- downElement.setAttribute("aria-label", window.VditorI18n.down + "<" + updateHotkeyTip("⇧⌘D") + ">");
919
- downElement.innerHTML =
920
- '<svg><use xlink:href="#vditor-icon-down"></use></svg>';
921
- downElement.className =
922
- "vditor-icon vditor-tooltipped vditor-tooltipped__n";
923
- downElement.onclick = () => {
924
- range.insertNode(document.createElement("wbr"));
925
- nextElement.insertAdjacentElement("afterend", element);
926
- setRangeByWbr(vditor.wysiwyg.element, range);
927
- afterRenderEvent(vditor);
928
- highlightToolbarWYSIWYG(vditor);
929
- scrollCenter(vditor);
930
- };
931
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
932
- };
933
-
934
- const genClose = (element: HTMLElement, vditor: IVditor) => {
935
- const close = document.createElement("button");
936
- close.setAttribute("type", "button");
937
- close.setAttribute("data-type", "remove");
938
- close.setAttribute("aria-label", window.VditorI18n.remove + "<" + updateHotkeyTip("⇧⌘X") + ">");
939
- close.innerHTML =
940
- '<svg><use xlink:href="#vditor-icon-trashcan"></use></svg>';
941
- close.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
942
- close.onclick = () => {
943
- const range = getEditorRange(vditor);
944
- range.setStartAfter(element);
945
- setSelectionFocus(range);
946
- element.remove();
947
- afterRenderEvent(vditor);
948
- highlightToolbarWYSIWYG(vditor);
949
- if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
950
- renderToc(vditor);
951
- }
952
- };
953
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
954
- };
955
-
956
- const linkHotkey = (
957
- vditor: IVditor,
958
- element: HTMLElement,
959
- event: KeyboardEvent,
960
- nextInputElement: HTMLInputElement,
961
- ) => {
962
- if (event.isComposing) {
963
- return;
964
- }
965
- if (event.key === "Tab") {
966
- nextInputElement.focus();
967
- nextInputElement.select();
968
- event.preventDefault();
969
- return;
970
- }
971
- if (
972
- !isCtrl(event) &&
973
- !event.shiftKey &&
974
- event.altKey &&
975
- event.key === "Enter"
976
- ) {
977
- const range = getEditorRange(vditor);
978
- // firefox 不会打断 link https://github.com/Vanessa219/vditor/issues/193
979
- element.insertAdjacentHTML("afterend", Constants.ZWSP);
980
- range.setStartAfter(element.nextSibling);
981
- range.collapse(true);
982
- setSelectionFocus(range);
983
- event.preventDefault();
984
- }
985
- };
986
-
987
- export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range) => {
988
- vditor.wysiwyg.popover.innerHTML = "";
989
-
990
- const updateA = () => {
991
- if (input.value.trim() !== "") {
992
- aElement.innerHTML = input.value;
993
- }
994
- aElement.setAttribute("href", input1.value);
995
- aElement.setAttribute("title", input2.value);
996
- afterRenderEvent(vditor);
997
- };
998
-
999
- aElement.querySelectorAll("[data-marker]").forEach((item: HTMLElement) => {
1000
- item.removeAttribute("data-marker");
1001
- });
1002
- const inputWrap = document.createElement("span");
1003
- inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
1004
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1005
- const input = document.createElement("input");
1006
- inputWrap.appendChild(input);
1007
- input.className = "vditor-input";
1008
- input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
1009
- input.style.width = "120px";
1010
- input.value = aElement.innerHTML || "";
1011
- input.oninput = () => {
1012
- updateA();
1013
- };
1014
- input.onkeydown = (event) => {
1015
- if (removeBlockElement(vditor, event)) {
1016
- return;
1017
- }
1018
- if (focusToElement(event, range)) {
1019
- return;
1020
- }
1021
- linkHotkey(vditor, aElement, event, input1);
1022
- };
1023
-
1024
- const input1Wrap = document.createElement("span");
1025
- input1Wrap.setAttribute("aria-label", window.VditorI18n.link);
1026
- input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1027
- const input1 = document.createElement("input");
1028
- input1Wrap.appendChild(input1);
1029
- input1.className = "vditor-input";
1030
- input1.setAttribute("placeholder", window.VditorI18n.link);
1031
- input1.value = aElement.getAttribute("href") || "";
1032
- input1.oninput = () => {
1033
- updateA();
1034
- };
1035
- input1.onkeydown = (event) => {
1036
- if (removeBlockElement(vditor, event)) {
1037
- return;
1038
- }
1039
- if (focusToElement(event, range)) {
1040
- return;
1041
- }
1042
- linkHotkey(vditor, aElement, event, input2);
1043
- };
1044
-
1045
- const input2Wrap = document.createElement("span");
1046
- input2Wrap.setAttribute("aria-label", window.VditorI18n.tooltipText);
1047
- input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1048
- const input2 = document.createElement("input");
1049
- input2Wrap.appendChild(input2);
1050
- input2.className = "vditor-input";
1051
- input2.setAttribute("placeholder", window.VditorI18n.tooltipText);
1052
- input2.style.width = "60px";
1053
- input2.value = aElement.getAttribute("title") || "";
1054
- input2.oninput = () => {
1055
- updateA();
1056
- };
1057
- input2.onkeydown = (event) => {
1058
- if (removeBlockElement(vditor, event)) {
1059
- return;
1060
- }
1061
- if (focusToElement(event, range)) {
1062
- return;
1063
- }
1064
- linkHotkey(vditor, aElement, event, input);
1065
- };
1066
-
1067
- genClose(aElement, vditor);
1068
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1069
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
1070
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
1071
- customWysiwygToolbar(vditor, "a")
1072
- setPopoverPosition(vditor, aElement);
1073
- };
1074
-
1075
- export const genImagePopover = (event: Event, vditor: IVditor) => {
1076
- const imgElement = event.target as HTMLImageElement;
1077
- vditor.wysiwyg.popover.innerHTML = "";
1078
- const updateImg = () => {
1079
- imgElement.setAttribute("src", inputElement.value);
1080
- imgElement.setAttribute("alt", alt.value);
1081
- imgElement.setAttribute("title", title.value);
1082
- if (typeof vditor.options.input === "function") {
1083
- vditor.options.input(getMarkdown(vditor));
1084
- }
1085
- };
1086
-
1087
- const inputWrap = document.createElement("span");
1088
- inputWrap.setAttribute("aria-label", window.VditorI18n.imageURL);
1089
- inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1090
- const inputElement = document.createElement("input");
1091
- inputWrap.appendChild(inputElement);
1092
- inputElement.className = "vditor-input";
1093
- inputElement.setAttribute("placeholder", window.VditorI18n.imageURL);
1094
- inputElement.value = imgElement.getAttribute("src") || "";
1095
- inputElement.oninput = () => {
1096
- updateImg();
1097
- };
1098
- inputElement.onkeydown = (elementEvent) => {
1099
- removeBlockElement(vditor, elementEvent);
1100
- };
1101
-
1102
- const altWrap = document.createElement("span");
1103
- altWrap.setAttribute("aria-label", window.VditorI18n.alternateText);
1104
- altWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1105
- const alt = document.createElement("input");
1106
- altWrap.appendChild(alt);
1107
- alt.className = "vditor-input";
1108
- alt.setAttribute("placeholder", window.VditorI18n.alternateText);
1109
- alt.style.width = "52px";
1110
- alt.value = imgElement.getAttribute("alt") || "";
1111
- alt.oninput = () => {
1112
- updateImg();
1113
- };
1114
- alt.onkeydown = (elementEvent) => {
1115
- removeBlockElement(vditor, elementEvent);
1116
- };
1117
-
1118
- const titleWrap = document.createElement("span");
1119
- titleWrap.setAttribute("aria-label", window.VditorI18n.title);
1120
- titleWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1121
- const title = document.createElement("input");
1122
- titleWrap.appendChild(title);
1123
- title.className = "vditor-input";
1124
- title.setAttribute("placeholder", window.VditorI18n.title);
1125
- title.value = imgElement.getAttribute("title") || "";
1126
- title.oninput = () => {
1127
- updateImg();
1128
- };
1129
- title.onkeydown = (elementEvent) => {
1130
- removeBlockElement(vditor, elementEvent);
1131
- };
1132
- genClose(imgElement, vditor);
1133
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1134
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
1135
- vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
1136
- customWysiwygToolbar(vditor, "image")
1137
- setPopoverPosition(vditor, imgElement);
1138
- };
1139
-
1140
-
1141
- const focusToElement = (event: KeyboardEvent, range: Range) => {
1142
- if ((!isCtrl(event) && !event.shiftKey && event.key === "Enter") || event.key === "Escape") {
1143
- if (range) {
1144
- setSelectionFocus(range);
1145
- }
1146
- event.preventDefault();
1147
- event.stopPropagation();
1148
- return true;
1149
- }
1150
- };
1151
-
1152
- const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
1153
- if (vditor.options.customWysiwygToolbar) {
1154
- vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
1155
- }
1156
- };
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
+ vditor.wysiwyg.popover.innerHTML = "";
620
+ genUp(range, blockRenderElement, vditor);
621
+ genDown(range, blockRenderElement, vditor);
622
+ genClose(blockRenderElement, vditor);
623
+
624
+ if (blockRenderElement.getAttribute("data-type") === "code-block") {
625
+ const languageWrap = document.createElement("span");
626
+ languageWrap.setAttribute("aria-label", window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
627
+ languageWrap.className = "vditor-tooltipped vditor-tooltipped__n";
628
+ const language = document.createElement("input");
629
+ languageWrap.appendChild(language);
630
+
631
+ const codeElement =
632
+ blockRenderElement.firstElementChild.firstElementChild;
633
+
634
+ language.className = "vditor-input";
635
+ language.setAttribute("placeholder",
636
+ window.VditorI18n.language + "<" + updateHotkeyTip("⌥Enter") + ">");
637
+ language.value =
638
+ codeElement.className.indexOf("language-") > -1
639
+ ? codeElement.className.split("-")[1].split(" ")[0]
640
+ : "";
641
+ language.oninput = (e: InputEvent) => {
642
+ if (language.value.trim() !== "") {
643
+ codeElement.className = `language-${language.value}`;
644
+ } else {
645
+ codeElement.className = "";
646
+ vditor.hint.recentLanguage = "";
647
+ }
648
+ if (blockRenderElement.lastElementChild.classList.contains("vditor-wysiwyg__preview")) {
649
+ blockRenderElement.lastElementChild.innerHTML =
650
+ blockRenderElement.firstElementChild.innerHTML;
651
+ processCodeRender(blockRenderElement.lastElementChild as HTMLElement, vditor);
652
+ }
653
+ afterRenderEvent(vditor);
654
+ // 当鼠标点选语言时,触发自定义input事件
655
+ if (e.detail === 1) {
656
+ // 选择语言后,输入焦点切换到代码输入框
657
+ range.setStart(codeElement.firstChild, 0);
658
+ range.collapse(true);
659
+ setSelectionFocus(range);
660
+ }
661
+ };
662
+ language.onkeydown = (event: KeyboardEvent) => {
663
+ if (event.isComposing) {
664
+ return;
665
+ }
666
+ if (removeBlockElement(vditor, event)) {
667
+ return;
668
+ }
669
+ if (
670
+ event.key === "Escape" &&
671
+ vditor.hint.element.style.display === "block"
672
+ ) {
673
+ vditor.hint.element.style.display = "none";
674
+ event.preventDefault();
675
+ return;
676
+ }
677
+ vditor.hint.select(event, vditor);
678
+ focusToElement(event, range);
679
+ };
680
+ language.onkeyup = (event: KeyboardEvent) => {
681
+ if (
682
+ event.isComposing ||
683
+ event.key === "Enter" ||
684
+ event.key === "ArrowUp" ||
685
+ event.key === "Escape" ||
686
+ event.key === "ArrowDown"
687
+ ) {
688
+ return;
689
+ }
690
+ const matchLangData: IHintData[] = [];
691
+ const key = language.value.substring(0, language.selectionStart);
692
+ (vditor.options.preview.hljs.langs || Constants.ALIAS_CODE_LANGUAGES.concat((window.hljs?.listLanguages() ?? []).sort())).forEach((keyName) => {
693
+ if (keyName.indexOf(key.toLowerCase()) > -1) {
694
+ matchLangData.push({
695
+ html: keyName,
696
+ value: keyName,
697
+ });
698
+ }
699
+ });
700
+ vditor.hint.genHTML(matchLangData, key, vditor);
701
+ event.preventDefault();
702
+ };
703
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", languageWrap);
704
+ customWysiwygToolbar(vditor, "code-block")
705
+ } else {
706
+ customWysiwygToolbar(vditor, "block")
707
+ }
708
+ setPopoverPosition(vditor, blockRenderElement);
709
+ } else {
710
+ blockRenderElement = undefined;
711
+ }
712
+ if (headingElement) {
713
+ vditor.wysiwyg.popover.innerHTML = "";
714
+
715
+ const inputWrap = document.createElement("span");
716
+ inputWrap.setAttribute("aria-label", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
717
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
718
+ const input = document.createElement("input");
719
+ inputWrap.appendChild(input);
720
+ input.className = "vditor-input";
721
+ input.setAttribute("placeholder", "ID" + "<" + updateHotkeyTip("⌥Enter") + ">");
722
+ input.style.width = "120px";
723
+ input.value = headingElement.getAttribute("data-id") || "";
724
+ input.oninput = () => {
725
+ headingElement.setAttribute("data-id", input.value);
726
+ if (typeof vditor.options.input === "function") {
727
+ vditor.options.input(getMarkdown(vditor));
728
+ }
729
+ };
730
+ input.onkeydown = (event) => {
731
+ if (event.isComposing) {
732
+ return;
733
+ }
734
+ if (removeBlockElement(vditor, event)) {
735
+ return;
736
+ }
737
+ if (focusToElement(event, range)) {
738
+ return;
739
+ }
740
+ };
741
+
742
+ genUp(range, headingElement, vditor);
743
+ genDown(range, headingElement, vditor);
744
+ genClose(headingElement, vditor);
745
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
746
+ customWysiwygToolbar(vditor, "heading")
747
+ setPopoverPosition(vditor, headingElement);
748
+ }
749
+
750
+ // a popover
751
+ if (aElement) {
752
+ genAPopover(vditor, aElement, range);
753
+ }
754
+
755
+ if (
756
+ !blockquoteElement &&
757
+ !liElement &&
758
+ !tableElement &&
759
+ !blockRenderElement &&
760
+ !aElement &&
761
+ !linkRefElement &&
762
+ !footnotesRefElement &&
763
+ !headingElement &&
764
+ !tocElement
765
+ ) {
766
+ const blockElement = hasClosestByAttribute(typeElement, "data-block", "0");
767
+ if (
768
+ blockElement &&
769
+ blockElement.parentElement.isEqualNode(vditor.wysiwyg.element)
770
+ ) {
771
+ vditor.wysiwyg.popover.innerHTML = "";
772
+ genUp(range, blockElement, vditor);
773
+ genDown(range, blockElement, vditor);
774
+ genClose(blockElement, vditor);
775
+ customWysiwygToolbar(vditor, "block")
776
+ setPopoverPosition(vditor, blockElement);
777
+ } else {
778
+ vditor.wysiwyg.popover.style.display = "none";
779
+ }
780
+ }
781
+
782
+ // 反斜杠特殊处理
783
+ vditor.wysiwyg.element
784
+ .querySelectorAll('span[data-type="backslash"] > span')
785
+ .forEach((item: HTMLElement) => {
786
+ item.style.display = "none";
787
+ });
788
+ const backslashElement = hasClosestByAttribute(range.startContainer, "data-type", "backslash");
789
+ if (backslashElement) {
790
+ backslashElement.querySelector("span").style.display = "inline";
791
+ }
792
+ }, 200);
793
+ };
794
+
795
+ const setPopoverPosition = (vditor: IVditor, element: HTMLElement) => {
796
+ let targetElement = element;
797
+ const tableElement = hasClosestByMatchTag(element, "TABLE");
798
+ if (tableElement) {
799
+ targetElement = tableElement;
800
+ }
801
+ vditor.wysiwyg.popover.style.left = "0";
802
+ vditor.wysiwyg.popover.style.display = "block";
803
+ vditor.wysiwyg.popover.style.top =
804
+ Math.max(-8, targetElement.offsetTop - 21 - vditor.wysiwyg.element.scrollTop) + "px";
805
+ vditor.wysiwyg.popover.style.left =
806
+ Math.min(targetElement.offsetLeft, vditor.wysiwyg.element.clientWidth - vditor.wysiwyg.popover.clientWidth) + "px";
807
+ vditor.wysiwyg.popover.setAttribute("data-top", (targetElement.offsetTop - 21).toString());
808
+ };
809
+
810
+ export const genLinkRefPopover = (vditor: IVditor, linkRefElement: HTMLElement, range = getSelection().getRangeAt(0)) => {
811
+ vditor.wysiwyg.popover.innerHTML = "";
812
+ const updateLinkRef = () => {
813
+ if (input.value.trim() !== "") {
814
+ if (linkRefElement.tagName === "IMG") {
815
+ linkRefElement.setAttribute("alt", input.value);
816
+ } else {
817
+ linkRefElement.textContent = input.value;
818
+ }
819
+ }
820
+ // data-link-label
821
+ if (input1.value.trim() !== "") {
822
+ linkRefElement.setAttribute("data-link-label", input1.value);
823
+ }
824
+ if (typeof vditor.options.input === "function") {
825
+ vditor.options.input(getMarkdown(vditor));
826
+ }
827
+ };
828
+
829
+ const inputWrap = document.createElement("span");
830
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
831
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
832
+ const input = document.createElement("input");
833
+ inputWrap.appendChild(input);
834
+ input.className = "vditor-input";
835
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
836
+ input.style.width = "120px";
837
+ input.value =
838
+ linkRefElement.getAttribute("alt") || linkRefElement.textContent;
839
+ input.oninput = () => {
840
+ updateLinkRef();
841
+ };
842
+ input.onkeydown = (event) => {
843
+ if (removeBlockElement(vditor, event)) {
844
+ return;
845
+ }
846
+ if (focusToElement(event, range)) {
847
+ return;
848
+ }
849
+ linkHotkey(vditor, linkRefElement, event, input1);
850
+ };
851
+
852
+ const input1Wrap = document.createElement("span");
853
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.linkRef);
854
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
855
+ const input1 = document.createElement("input");
856
+ input1Wrap.appendChild(input1);
857
+ input1.className = "vditor-input";
858
+ input1.setAttribute("placeholder", window.VditorI18n.linkRef);
859
+ input1.value = linkRefElement.getAttribute("data-link-label");
860
+ input1.oninput = () => {
861
+ updateLinkRef();
862
+ };
863
+ input1.onkeydown = (event) => {
864
+ if (removeBlockElement(vditor, event)) {
865
+ return;
866
+ }
867
+ if (focusToElement(event, range)) {
868
+ return;
869
+ }
870
+ linkHotkey(vditor, linkRefElement, event, input);
871
+ };
872
+
873
+ genClose(linkRefElement, vditor);
874
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
875
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
876
+ customWysiwygToolbar(vditor, "link-ref")
877
+ setPopoverPosition(vditor, linkRefElement);
878
+ };
879
+
880
+ const genUp = (range: Range, element: HTMLElement, vditor: IVditor) => {
881
+ const previousElement = element.previousElementSibling;
882
+ if (
883
+ !previousElement ||
884
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
885
+ element.tagName !== "LI")
886
+ ) {
887
+ return;
888
+ }
889
+ const upElement = document.createElement("button");
890
+ upElement.setAttribute("type", "button");
891
+ upElement.setAttribute("data-type", "up");
892
+ upElement.setAttribute("aria-label", window.VditorI18n.up + "<" + updateHotkeyTip("⇧⌘U") + ">");
893
+ upElement.innerHTML = '<svg><use xlink:href="#vditor-icon-up"></use></svg>';
894
+ upElement.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
895
+ upElement.onclick = () => {
896
+ range.insertNode(document.createElement("wbr"));
897
+ previousElement.insertAdjacentElement("beforebegin", element);
898
+ setRangeByWbr(vditor.wysiwyg.element, range);
899
+ afterRenderEvent(vditor);
900
+ highlightToolbarWYSIWYG(vditor);
901
+ scrollCenter(vditor);
902
+ };
903
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", upElement);
904
+ };
905
+
906
+ const genDown = (range: Range, element: HTMLElement, vditor: IVditor) => {
907
+ const nextElement = element.nextElementSibling;
908
+ if (
909
+ !nextElement ||
910
+ (!element.parentElement.isEqualNode(vditor.wysiwyg.element) &&
911
+ element.tagName !== "LI")
912
+ ) {
913
+ return;
914
+ }
915
+ const downElement = document.createElement("button");
916
+ downElement.setAttribute("type", "button");
917
+ downElement.setAttribute("data-type", "down");
918
+ downElement.setAttribute("aria-label", window.VditorI18n.down + "<" + updateHotkeyTip("⇧⌘D") + ">");
919
+ downElement.innerHTML =
920
+ '<svg><use xlink:href="#vditor-icon-down"></use></svg>';
921
+ downElement.className =
922
+ "vditor-icon vditor-tooltipped vditor-tooltipped__n";
923
+ downElement.onclick = () => {
924
+ range.insertNode(document.createElement("wbr"));
925
+ nextElement.insertAdjacentElement("afterend", element);
926
+ setRangeByWbr(vditor.wysiwyg.element, range);
927
+ afterRenderEvent(vditor);
928
+ highlightToolbarWYSIWYG(vditor);
929
+ scrollCenter(vditor);
930
+ };
931
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", downElement);
932
+ };
933
+
934
+ const genClose = (element: HTMLElement, vditor: IVditor) => {
935
+ const close = document.createElement("button");
936
+ close.setAttribute("type", "button");
937
+ close.setAttribute("data-type", "remove");
938
+ close.setAttribute("aria-label", window.VditorI18n.remove + "<" + updateHotkeyTip("⇧⌘X") + ">");
939
+ close.innerHTML =
940
+ '<svg><use xlink:href="#vditor-icon-trashcan"></use></svg>';
941
+ close.className = "vditor-icon vditor-tooltipped vditor-tooltipped__n";
942
+ close.onclick = () => {
943
+ const range = getEditorRange(vditor);
944
+ range.setStartAfter(element);
945
+ setSelectionFocus(range);
946
+ element.remove();
947
+ afterRenderEvent(vditor);
948
+ highlightToolbarWYSIWYG(vditor);
949
+ if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(element.tagName)) {
950
+ renderToc(vditor);
951
+ }
952
+ };
953
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", close);
954
+ };
955
+
956
+ const linkHotkey = (
957
+ vditor: IVditor,
958
+ element: HTMLElement,
959
+ event: KeyboardEvent,
960
+ nextInputElement: HTMLInputElement,
961
+ ) => {
962
+ if (event.isComposing) {
963
+ return;
964
+ }
965
+ if (event.key === "Tab") {
966
+ nextInputElement.focus();
967
+ nextInputElement.select();
968
+ event.preventDefault();
969
+ return;
970
+ }
971
+ if (
972
+ !isCtrl(event) &&
973
+ !event.shiftKey &&
974
+ event.altKey &&
975
+ event.key === "Enter"
976
+ ) {
977
+ const range = getEditorRange(vditor);
978
+ // firefox 不会打断 link https://github.com/Vanessa219/vditor/issues/193
979
+ element.insertAdjacentHTML("afterend", Constants.ZWSP);
980
+ range.setStartAfter(element.nextSibling);
981
+ range.collapse(true);
982
+ setSelectionFocus(range);
983
+ event.preventDefault();
984
+ }
985
+ };
986
+
987
+ export const genAPopover = (vditor: IVditor, aElement: HTMLElement, range: Range) => {
988
+ vditor.wysiwyg.popover.innerHTML = "";
989
+
990
+ const updateA = () => {
991
+ if (input.value.trim() !== "") {
992
+ aElement.innerHTML = input.value;
993
+ }
994
+ aElement.setAttribute("href", input1.value);
995
+ aElement.setAttribute("title", input2.value);
996
+ afterRenderEvent(vditor);
997
+ };
998
+
999
+ aElement.querySelectorAll("[data-marker]").forEach((item: HTMLElement) => {
1000
+ item.removeAttribute("data-marker");
1001
+ });
1002
+ const inputWrap = document.createElement("span");
1003
+ inputWrap.setAttribute("aria-label", window.VditorI18n.textIsNotEmpty);
1004
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1005
+ const input = document.createElement("input");
1006
+ inputWrap.appendChild(input);
1007
+ input.className = "vditor-input";
1008
+ input.setAttribute("placeholder", window.VditorI18n.textIsNotEmpty);
1009
+ input.style.width = "120px";
1010
+ input.value = aElement.innerHTML || "";
1011
+ input.oninput = () => {
1012
+ updateA();
1013
+ };
1014
+ input.onkeydown = (event) => {
1015
+ if (removeBlockElement(vditor, event)) {
1016
+ return;
1017
+ }
1018
+ if (focusToElement(event, range)) {
1019
+ return;
1020
+ }
1021
+ linkHotkey(vditor, aElement, event, input1);
1022
+ };
1023
+
1024
+ const input1Wrap = document.createElement("span");
1025
+ input1Wrap.setAttribute("aria-label", window.VditorI18n.link);
1026
+ input1Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1027
+ const input1 = document.createElement("input");
1028
+ input1Wrap.appendChild(input1);
1029
+ input1.className = "vditor-input";
1030
+ input1.setAttribute("placeholder", window.VditorI18n.link);
1031
+ input1.value = aElement.getAttribute("href") || "";
1032
+ input1.oninput = () => {
1033
+ updateA();
1034
+ };
1035
+ input1.onkeydown = (event) => {
1036
+ if (removeBlockElement(vditor, event)) {
1037
+ return;
1038
+ }
1039
+ if (focusToElement(event, range)) {
1040
+ return;
1041
+ }
1042
+ linkHotkey(vditor, aElement, event, input2);
1043
+ };
1044
+
1045
+ const input2Wrap = document.createElement("span");
1046
+ input2Wrap.setAttribute("aria-label", window.VditorI18n.tooltipText);
1047
+ input2Wrap.className = "vditor-tooltipped vditor-tooltipped__n";
1048
+ const input2 = document.createElement("input");
1049
+ input2Wrap.appendChild(input2);
1050
+ input2.className = "vditor-input";
1051
+ input2.setAttribute("placeholder", window.VditorI18n.tooltipText);
1052
+ input2.style.width = "60px";
1053
+ input2.value = aElement.getAttribute("title") || "";
1054
+ input2.oninput = () => {
1055
+ updateA();
1056
+ };
1057
+ input2.onkeydown = (event) => {
1058
+ if (removeBlockElement(vditor, event)) {
1059
+ return;
1060
+ }
1061
+ if (focusToElement(event, range)) {
1062
+ return;
1063
+ }
1064
+ linkHotkey(vditor, aElement, event, input);
1065
+ };
1066
+
1067
+ genClose(aElement, vditor);
1068
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1069
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input1Wrap);
1070
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", input2Wrap);
1071
+ customWysiwygToolbar(vditor, "a")
1072
+ setPopoverPosition(vditor, aElement);
1073
+ };
1074
+
1075
+ export const genImagePopover = (event: Event, vditor: IVditor) => {
1076
+ const imgElement = event.target as HTMLImageElement;
1077
+ vditor.wysiwyg.popover.innerHTML = "";
1078
+ const updateImg = () => {
1079
+ imgElement.setAttribute("src", inputElement.value);
1080
+ imgElement.setAttribute("alt", alt.value);
1081
+ imgElement.setAttribute("title", title.value);
1082
+ if (typeof vditor.options.input === "function") {
1083
+ vditor.options.input(getMarkdown(vditor));
1084
+ }
1085
+ };
1086
+
1087
+ const inputWrap = document.createElement("span");
1088
+ inputWrap.setAttribute("aria-label", window.VditorI18n.imageURL);
1089
+ inputWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1090
+ const inputElement = document.createElement("input");
1091
+ inputWrap.appendChild(inputElement);
1092
+ inputElement.className = "vditor-input";
1093
+ inputElement.setAttribute("placeholder", window.VditorI18n.imageURL);
1094
+ inputElement.value = imgElement.getAttribute("src") || "";
1095
+ inputElement.oninput = () => {
1096
+ updateImg();
1097
+ };
1098
+ inputElement.onkeydown = (elementEvent) => {
1099
+ removeBlockElement(vditor, elementEvent);
1100
+ };
1101
+
1102
+ const altWrap = document.createElement("span");
1103
+ altWrap.setAttribute("aria-label", window.VditorI18n.alternateText);
1104
+ altWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1105
+ const alt = document.createElement("input");
1106
+ altWrap.appendChild(alt);
1107
+ alt.className = "vditor-input";
1108
+ alt.setAttribute("placeholder", window.VditorI18n.alternateText);
1109
+ alt.style.width = "52px";
1110
+ alt.value = imgElement.getAttribute("alt") || "";
1111
+ alt.oninput = () => {
1112
+ updateImg();
1113
+ };
1114
+ alt.onkeydown = (elementEvent) => {
1115
+ removeBlockElement(vditor, elementEvent);
1116
+ };
1117
+
1118
+ const titleWrap = document.createElement("span");
1119
+ titleWrap.setAttribute("aria-label", window.VditorI18n.title);
1120
+ titleWrap.className = "vditor-tooltipped vditor-tooltipped__n";
1121
+ const title = document.createElement("input");
1122
+ titleWrap.appendChild(title);
1123
+ title.className = "vditor-input";
1124
+ title.setAttribute("placeholder", window.VditorI18n.title);
1125
+ title.value = imgElement.getAttribute("title") || "";
1126
+ title.oninput = () => {
1127
+ updateImg();
1128
+ };
1129
+ title.onkeydown = (elementEvent) => {
1130
+ removeBlockElement(vditor, elementEvent);
1131
+ };
1132
+ genClose(imgElement, vditor);
1133
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", inputWrap);
1134
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", altWrap);
1135
+ vditor.wysiwyg.popover.insertAdjacentElement("beforeend", titleWrap);
1136
+ customWysiwygToolbar(vditor, "image")
1137
+ setPopoverPosition(vditor, imgElement);
1138
+ };
1139
+
1140
+
1141
+ const focusToElement = (event: KeyboardEvent, range: Range) => {
1142
+ if ((!isCtrl(event) && !event.shiftKey && event.key === "Enter") || event.key === "Escape") {
1143
+ if (range) {
1144
+ setSelectionFocus(range);
1145
+ }
1146
+ event.preventDefault();
1147
+ event.stopPropagation();
1148
+ return true;
1149
+ }
1150
+ };
1151
+
1152
+ const customWysiwygToolbar = (vditor: IVditor, type: TWYSISYGToolbar) => {
1153
+ if (vditor.options.customWysiwygToolbar) {
1154
+ vditor.options.customWysiwygToolbar(type, vditor.wysiwyg.popover);
1155
+ }
1156
+ };