leksy-editor 2.6.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/plugin.js DELETED
@@ -1,1355 +0,0 @@
1
- import { EMOJI_CATEGORIES, FONT_SIZE_OPTIONS, FONTS, FORMAT_OPTIONS, MIMETYPE, REGEX, SPECIAL_CHARACTERS, SVG, UNORDERED_LIST_OPTIONS, ORDERED_LIST_OPTIONS, CLASSES } from "./constant"
2
- import { giphy, pexels, tenor } from "./gallery";
3
- import { formatLink, changeAllToolbarState, changeToolbarHtmlByName, changeToolbarStateByName, changeToolbarValueByName, cleanHTML, constructEmbedUrl, extractSocialMediaId, isLinkValid, openModal, transformTextStyle, insertTOC, applyFormatBlockInListItem, normalizeHeadingWrappedLists, } from "./utilities";
4
-
5
- const applyTextFormat = (core, command) => {
6
- core.elements.editor.focus();
7
- core.elements.iframeWindow.execCommand(command);
8
- core.updateCaretPosition()
9
-
10
- const isActive = core.elements.iframeWindow.queryCommandState(command);
11
- if (isActive) changeToolbarStateByName(core, 'active', [command])
12
- else changeToolbarStateByName(core, 'inactive', [command])
13
- }
14
-
15
- const applyList = (core, { command, activePlugin, inactivePlugin }) => {
16
- core.elements.iframeWindow.execCommand(command);
17
- normalizeHeadingWrappedLists(core);
18
- core.elements.editor.focus();
19
- core.updateCaretPosition()
20
-
21
- const isActive = core.elements.iframeWindow.queryCommandState(command);
22
- changeToolbarStateByName(core, isActive ? 'active' : 'inactive', [activePlugin])
23
- changeToolbarStateByName(core, 'inactive', [inactivePlugin])
24
- }
25
-
26
- const applyOrderList = (core) => applyList(core, {
27
- command: 'insertOrderedList',
28
- activePlugin: 'ordered_list',
29
- inactivePlugin: 'unordered_list'
30
- })
31
-
32
- const applyUnorderedList = (core) => applyList(core, {
33
- command: 'insertUnorderedList',
34
- activePlugin: 'unordered_list',
35
- inactivePlugin: 'ordered_list'
36
- })
37
-
38
- const PLUGINS = {
39
- 'undo': {
40
- title: 'Undo',
41
- icon: SVG.UNDO,
42
- type: 'button',
43
- click: (event, core) => {
44
- core.history.undo()
45
- core.updateCaretPosition()
46
- core.elements.editor.focus();
47
- }
48
- },
49
- 'redo': {
50
- title: 'Redo',
51
- icon: SVG.REDO,
52
- type: 'button',
53
- click: (event, core) => {
54
- core.history.redo()
55
- core.updateCaretPosition()
56
- core.elements.editor.focus();
57
- }
58
- },
59
- 'bold': {
60
- title: 'Bold',
61
- icon: SVG.BOLD,
62
- type: 'button',
63
- click: (event, core) => {
64
- applyTextFormat(core, 'bold')
65
- }
66
- },
67
- 'underline': {
68
- title: 'Underline',
69
- icon: SVG.UNDERLINE,
70
- type: 'button',
71
- click: (event, core) => {
72
- applyTextFormat(core, 'underline')
73
- }
74
- },
75
- 'italic': {
76
- title: 'Italic',
77
- icon: SVG.ITALIC,
78
- type: 'button',
79
- click: (event, core) => {
80
- applyTextFormat(core, 'italic')
81
- }
82
- },
83
- 'strike': {
84
- title: 'Strike',
85
- icon: SVG.STRIKE,
86
- type: 'button',
87
- click: (event, core) => {
88
- core.elements.editor.focus();
89
- core.elements.iframeWindow.execCommand('strikeThrough');
90
- core.updateCaretPosition()
91
-
92
- const isActive = core.elements.iframeWindow.queryCommandState('strikeThrough');
93
- if (isActive) changeToolbarStateByName(core, 'active', ['strike'])
94
- else changeToolbarStateByName(core, 'inactive', ['strike'])
95
- }
96
- },
97
- 'align_justify': {
98
- title: 'Align Justify',
99
- icon: SVG.ALIGN_JUSTIFY,
100
- type: 'button',
101
- click: (event, core) => {
102
- core.elements.editor.focus();
103
- core.elements.iframeWindow.execCommand('justifyFull');
104
- core.resizerHandler?.()
105
- core.updateCaretPosition()
106
-
107
- changeToolbarStateByName(core, 'active', ['align_justify'])
108
- changeToolbarStateByName(core, 'inactive', ['align_left', 'align_right', 'align_center'])
109
- }
110
- },
111
- 'align_left': {
112
- title: 'Align Left',
113
- icon: SVG.ALIGN_LEFT,
114
- type: 'button',
115
- click: (event, core) => {
116
- core.elements.editor.focus();
117
- core.elements.iframeWindow.execCommand('justifyLeft');
118
- core.resizerHandler?.()
119
- core.updateCaretPosition()
120
-
121
- changeToolbarStateByName(core, 'active', ['align_left'])
122
- changeToolbarStateByName(core, 'inactive', ['align_justify', 'align_right', 'align_center'])
123
- }
124
- },
125
- 'align_right': {
126
- title: 'Align Right',
127
- icon: SVG.ALIGN_RIGHT,
128
- type: 'button',
129
- click: (event, core) => {
130
- core.elements.editor.focus();
131
- core.elements.iframeWindow.execCommand('justifyRight');
132
- core.resizerHandler?.()
133
- core.updateCaretPosition()
134
-
135
- changeToolbarStateByName(core, 'active', ['align_right'])
136
- changeToolbarStateByName(core, 'inactive', ['align_justify', 'align_left', 'align_center'])
137
- }
138
- },
139
- 'align_center': {
140
- title: 'Align Center',
141
- icon: SVG.ALIGN_CENTER,
142
- type: 'button',
143
- click: (event, core) => {
144
- core.elements.editor.focus();
145
- core.elements.iframeWindow.execCommand('justifyCenter');
146
- core.resizerHandler?.()
147
- core.updateCaretPosition()
148
-
149
- changeToolbarStateByName(core, 'active', ['align_center'])
150
- changeToolbarStateByName(core, 'inactive', ['align_justify', 'align_left', 'align_right'])
151
- }
152
- },
153
- 'code_view': {
154
- title: 'Code View',
155
- icon: SVG.CODE_VIEW,
156
- type: 'button',
157
- click: (event, core, options) => {
158
- if (core.state.isCodeViewOpen) {
159
- core.elements.codeview.style.display = 'none'
160
- core.elements.iframeContainer.style.display = 'block'
161
- if (core.elements.stepper) core.elements.stepper.style.display = 'flex'
162
- const _cleanHTML = cleanHTML(core.elements.codeview.value, options, core)
163
- core.onChange(_cleanHTML);
164
- core.elements.editor.innerHTML = _cleanHTML
165
- core.elements.editor.focus()
166
- changeAllToolbarState(core, 'enabled')
167
- } else {
168
- core.hidePlaceholder()
169
- core.elements.iframeContainer.style.display = 'none'
170
- if (core.elements.stepper) core.elements.stepper.style.display = 'none'
171
- core.elements.codeview.value = core.html
172
- core.elements.codeview.style.display = 'block'
173
- core.elements.codeview.focus()
174
- changeAllToolbarState(core, 'disabled', ['code_view'])
175
- }
176
- core.state.isCodeViewOpen = !core.state.isCodeViewOpen
177
- core.updateCaretPosition()
178
- }
179
- },
180
- 'remove_format': {
181
- title: "Remove Format",
182
- icon: SVG.REMOVE_FORMAT,
183
- type: 'button',
184
- click: (event, core) => {
185
- core.elements.iframeWindow.execCommand('removeFormat');
186
- core.elements.editor.focus();
187
- core.updateCaretPosition()
188
- }
189
- },
190
- 'subscript': {
191
- title: 'Sub Script',
192
- icon: SVG.SUBSCRIPT,
193
- type: 'button',
194
- click: (event, core) => {
195
- core.elements.editor.focus();
196
- core.elements.iframeWindow.execCommand('subscript');
197
- core.updateCaretPosition()
198
-
199
- const isActive = core.elements.iframeWindow.queryCommandState('subscript');
200
- if (isActive) changeToolbarStateByName(core, 'active', ['subscript'])
201
- else changeToolbarStateByName(core, 'inactive', ['subscript'])
202
- changeToolbarStateByName(core, 'inactive', ['superscript'])
203
- }
204
- },
205
- 'superscript': {
206
- title: 'Super Script',
207
- icon: SVG.SUPERSCRIPT,
208
- type: 'button',
209
- click: (event, core) => {
210
- core.elements.editor.focus();
211
- core.elements.iframeWindow.execCommand('superscript');
212
- core.updateCaretPosition()
213
-
214
- const isActive = core.elements.iframeWindow.queryCommandState('superscript');
215
- if (isActive) changeToolbarStateByName(core, 'active', ['superscript'])
216
- else changeToolbarStateByName(core, 'inactive', ['superscript'])
217
- changeToolbarStateByName(core, 'inactive', ['subscript'])
218
- }
219
- },
220
- 'font': {
221
- title: 'Font',
222
- icon: 'Font',
223
- type: 'select',
224
- options: Object.keys(FONTS).map(font => ({
225
- label: FONTS[font], value: font
226
- })),
227
- width: '140px',
228
- click: (name, core, options) => {
229
- core.elements.editor.focus();
230
- core.elements.iframeWindow.execCommand('fontName', false, name);
231
-
232
- core.updateCaretPosition()
233
- changeToolbarValueByName(core, 'font', name)
234
- }
235
- },
236
- 'font-size': {
237
- title: 'Font Size',
238
- icon: 'Font Size',
239
- type: 'select',
240
- options: Object.keys(FONT_SIZE_OPTIONS).map(size => ({
241
- label: FONT_SIZE_OPTIONS[size], value: size
242
- })),
243
- width: '100px',
244
- click: (size, core, options) => {
245
- core.elements.editor.focus();
246
- core.elements.iframeWindow.execCommand('fontSize', false, parseInt(size));
247
-
248
- core.updateCaretPosition()
249
- changeToolbarValueByName(core, 'font-size', size)
250
- }
251
- },
252
- 'format-block': {
253
- title: 'Format Block',
254
- icon: 'Format',
255
- type: 'select',
256
- options: Object.keys(FORMAT_OPTIONS).map(format => ({
257
- label: FORMAT_OPTIONS[format], value: format
258
- })),
259
- width: '110px',
260
- click: (tag, core, options) => {
261
- core.elements.editor.focus();
262
- const isFormattedInList = applyFormatBlockInListItem(core, tag);
263
- if (!isFormattedInList) {
264
- core.elements.iframeWindow.execCommand('formatBlock', false, tag);
265
- }
266
- normalizeHeadingWrappedLists(core);
267
-
268
- core.updateCaretPosition()
269
- changeToolbarValueByName(core, 'format-block', tag)
270
- }
271
- },
272
- 'ordered_list': {
273
- title: "Ordered List",
274
- icon: SVG.LIST_NUMBER,
275
- type: 'button-select',
276
- options: Object.keys(ORDERED_LIST_OPTIONS).map(list => ({
277
- label: ORDERED_LIST_OPTIONS[list], value: list
278
- })),
279
- click: (value, core, options) => {
280
- core.elements.editor.focus();
281
- const isActive = core.elements.iframeWindow.queryCommandState('insertOrderedList');
282
- if (!isActive) {
283
- core.elements.iframeWindow.execCommand('insertOrderedList');
284
- normalizeHeadingWrappedLists(core);
285
- }
286
- const selection = core.elements.iframeWindow.getSelection();
287
- if (selection.rangeCount > 0) {
288
- let node = selection.anchorNode;
289
- while (node && node.nodeName !== 'OL' && node !== core.elements.editor) {
290
- node = node.parentNode;
291
- }
292
- if (node?.nodeName === 'OL') {
293
- node.setAttribute('type', value);
294
- }
295
- }
296
- core.updateCaretPosition()
297
- changeToolbarStateByName(core, 'active', ['ordered_list'])
298
- changeToolbarStateByName(core, 'inactive', ['unordered_list'])
299
- },
300
- mainClick: (event, core, options) => {
301
- core.elements.editor.focus();
302
- applyOrderList(core)
303
- }
304
- },
305
- 'unordered_list': {
306
- title: "Unordered List",
307
- icon: SVG.LIST_BULLETS,
308
- type: 'button-select',
309
- options: Object.keys(UNORDERED_LIST_OPTIONS).map(list => ({
310
- label: UNORDERED_LIST_OPTIONS[list], value: list
311
- })),
312
- click: (value, core, options) => {
313
- core.elements.editor.focus();
314
- const isActive = core.elements.iframeWindow.queryCommandState('insertUnorderedList');
315
- if (!isActive) {
316
- core.elements.iframeWindow.execCommand('insertUnorderedList');
317
- normalizeHeadingWrappedLists(core);
318
- }
319
- const selection = core.elements.iframeWindow.getSelection();
320
- if (selection.rangeCount > 0) {
321
- let node = selection.anchorNode;
322
- while (node && node.nodeName !== 'UL' && node !== core.elements.editor) {
323
- node = node.parentNode;
324
- }
325
- if (node?.nodeName === 'UL') {
326
- node.style.listStyleType = value;
327
- }
328
- }
329
- core.updateCaretPosition()
330
- changeToolbarStateByName(core, 'active', ['unordered_list'])
331
- changeToolbarStateByName(core, 'inactive', ['ordered_list'])
332
- },
333
- mainClick: (event, core, options) => {
334
- core.elements.editor.focus();
335
- applyUnorderedList(core)
336
- }
337
- },
338
- 'line_height': {
339
- title: 'Line Height',
340
- icon: SVG.LINE_HEIGHT,
341
- type: 'dropdown',
342
- options: [
343
- { label: "Normal", value: "normal" },
344
- { label: "1.0", value: "1.0" },
345
- { label: "1.5", value: "1.5" },
346
- { label: "2.0", value: "2.0" },
347
- { label: "2.5", value: "2.5" },
348
- { label: "3.0", value: "3.0" },
349
- ],
350
- click: (event, core, options) => {
351
- core.elements.editor.focus();
352
- const selection = core.elements.iframeWindow.getSelection();
353
- const range = selection.getRangeAt(0);
354
- const selectedNode = range.commonAncestorContainer;
355
- // Traverse up to the block element
356
- let blockElement = selectedNode;
357
- while (blockElement && blockElement.nodeType === Node.TEXT_NODE) {
358
- blockElement = blockElement.parentNode;
359
- }
360
- if (blockElement && blockElement !== core.elements.editor) blockElement.style.lineHeight = event.target.getAttribute('data-value');
361
-
362
- core.updateCaretPosition()
363
- },
364
- },
365
- 'font_color': {
366
- title: 'Font color',
367
- icon: SVG.FONT_COLOR, // Assuming SVG icon for color picker
368
- type: "color",
369
- change: (event, core, options) => {
370
- core.elements.editor.focus();
371
- core.elements.iframeWindow.execCommand('foreColor', false, event.target.value); // Apply color to selected text
372
- core.updateCaretPosition()
373
- }
374
- },
375
- 'remove_font_color': {
376
- title: 'Remove Font Color',
377
- icon: `${SVG.REMOVE_FORMAT}${SVG.FONT_COLOR}`,
378
- type: "reset-color",
379
- click: (event, core, options) => {
380
- if (!core.state.range) return;
381
- core.elements.editor.focus();
382
- const container = document.createElement('div');
383
- const clonedContents = core.state.range.cloneContents();
384
- container.appendChild(clonedContents);
385
- if (container.innerHTML === container.innerText) {
386
- const parentEl = core.state.range.startContainer.parentElement;
387
- parentEl.removeAttribute('color');
388
- parentEl.style.removeProperty('color');
389
-
390
- } else {
391
- container.querySelectorAll('[style]').forEach((el) => {
392
- el.style.removeProperty('color');
393
- if (!el.getAttribute('style')) {
394
- el.removeAttribute('style');
395
- }
396
- });
397
- container.querySelectorAll('font[color]').forEach((el) => {
398
- el.removeAttribute('color');
399
- });
400
- }
401
- core.state.range.deleteContents();
402
- const frag = document.createDocumentFragment();
403
- Array.from(container.childNodes).forEach((node) => {
404
- frag.appendChild(node);
405
- });
406
- core.state.range.insertNode(frag);
407
- core.updateCaretPosition();
408
- }
409
-
410
- },
411
- 'highlight_color': {
412
- title: 'Highlight Color',
413
- icon: SVG.HIGHLIGHT_COLOR,
414
- type: "color",
415
- change: (event, core, options) => {
416
- core.elements.editor.focus();
417
- core.elements.iframeWindow.execCommand('hiliteColor', false, event.target.value); // Apply color to selected text
418
- core.updateCaretPosition()
419
- }
420
- },
421
- 'remove_highlight_color': {
422
- title: 'Remove Highlight Color',
423
- icon: `${SVG.REMOVE_FORMAT}${SVG.HIGHLIGHT_COLOR}`,
424
- type: "reset-color",
425
- click: (event, core, options) => {
426
- core.elements.editor.focus();
427
- core.elements.iframeWindow.execCommand('hiliteColor', false, "transparent"); // Apply color to selected text
428
- core.updateCaretPosition()
429
- }
430
- },
431
- 'text_style': {
432
- title: 'Text Style',
433
- icon: SVG.TEXT_STYLE,
434
- type: 'dropdown',
435
- options: [
436
- { label: 'Block Quote', value: 'blockquote' },
437
- { label: 'Code', value: 'code' },
438
- { label: 'Translucent', value: 'translucent' },
439
- { label: 'Shadow', value: 'shadow' },
440
- { label: 'Upper Case', value: 'upper_case' },
441
- { label: 'Lower Case', value: 'lower_case' },
442
- { label: 'Title Case', value: 'title_case' },
443
- { label: 'Button (Green)', value: 'button_green' },
444
- { label: 'Button (Black)', value: 'button_black' }
445
- ],
446
- click: (event, core, options) => {
447
- if (!core.state.range) return
448
- switch (event.target.getAttribute('data-value')) {
449
- case 'code': {
450
- const code = document.createElement('code');
451
- code.textContent = core.state.range.toString();
452
- core.state.range.deleteContents();
453
- core.state.range.insertNode(code);
454
- break;
455
- }
456
- case 'translucent': {
457
- const translucent = document.createElement('span');
458
- translucent.textContent = core.state.range.toString();
459
- translucent.style.backgroundColor = 'rgba(200, 151, 231, 0.3)'; // Adjust opacity as needed
460
- core.state.range.deleteContents();
461
- core.state.range.insertNode(translucent);
462
- break;
463
- }
464
- case 'shadow': {
465
- const shadow = document.createElement('span');
466
- shadow.textContent = core.state.range.toString();
467
- shadow.style.textShadow = '2px 2px 4px rgba(0, 0, 0, 0.3)'; // Adjust opacity as needed
468
- core.state.range.deleteContents();
469
- core.state.range.insertNode(shadow);
470
- break;
471
- }
472
- case 'blockquote': {
473
- const code = document.createElement('blockquote');
474
- code.textContent = core.state.range.toString();
475
- core.state.range.deleteContents();
476
- core.state.range.insertNode(code);
477
- break;
478
- }
479
- case 'upper_case': {
480
- transformTextStyle(core, 'upper_case');
481
- break;
482
- }
483
- case 'lower_case': {
484
- transformTextStyle(core, 'lower_case');
485
- break;
486
- }
487
- case 'title_case': {
488
- transformTextStyle(core, 'title_case');
489
- break;
490
- }
491
- case 'button_green': {
492
- core.updateCaretPosition()
493
- const span = document.createElement('span');
494
- span.textContent = core.state.range.toString();
495
- span.style.cssText = 'background-color: #28a745; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
496
- span.contentEditable = 'false';
497
- let element = core.state.range.commonAncestorContainer;
498
- if (element.nodeType === Node.TEXT_NODE) element = element.parentElement;
499
- if (element.nodeName === 'A') {
500
- element.style.cssText = 'background-color: #28a745; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
501
- } else if (span.textContent && !['TR', 'TBODY', 'THEAD', 'TABLE'].includes(element.nodeName)) {
502
- core.state.range.deleteContents();
503
- core.state.range.insertNode(span);
504
- }
505
- break;
506
- }
507
- case 'button_black': {
508
- core.updateCaretPosition()
509
- const span = document.createElement('span');
510
- span.textContent = core.state.range.toString();
511
- span.style.cssText = 'background-color: black; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
512
- span.contentEditable = 'false';
513
- let element = core.state.range.commonAncestorContainer;
514
- if (element.nodeType === Node.TEXT_NODE) element = element.parentElement;
515
- if (element.nodeName === 'A') {
516
- element.style.cssText = 'background-color: black; color: white; font-weight: bold; padding: 8px; border-radius: 4px; text-decoration: none; display: inline-block; cursor: pointer;';
517
- } else if (span.textContent && !['TR', 'TBODY', 'THEAD', 'TABLE'].includes(element.nodeName)) {
518
- core.state.range.deleteContents();
519
- core.state.range.insertNode(span);
520
- }
521
- break;
522
- }
523
- }
524
- core.elements.editor.focus();
525
- core.updateCaretPosition()
526
- }
527
- },
528
- 'paragraph_style': {
529
- title: 'Paragraph Style',
530
- icon: SVG.PARAGRAPH_STYLE, // Assuming SVG icon for paragraph style
531
- type: 'dropdown',
532
- options: [
533
- { label: 'Spaced', value: 'letter-spacing: 2px;' },
534
- { label: 'Neon', value: `color: #FD7500; text-shadow: 0 0 5px #02B59F,0 0 10px #022422,0 0 15px #FF9F03,0 0 20px #02B59F,0 0 25px #017562;font-weight: bold;` },
535
- { label: 'Bordered', value: 'border: 2px solid black; padding: 5px;' }
536
- ],
537
- click: (event, core, option) => {
538
- core.elements.editor.focus();
539
- const selection = core.elements.iframeWindow.getSelection();
540
- const range = selection.getRangeAt(0);
541
- const selectedNode = range.commonAncestorContainer;
542
- // Traverse up to the block element
543
- let blockElement = selectedNode;
544
- while (blockElement && blockElement.nodeType === Node.TEXT_NODE) {
545
- blockElement = blockElement.parentNode;
546
- }
547
- if (blockElement && blockElement !== core.elements.editor) blockElement.style.cssText = event.target.getAttribute('data-value');
548
-
549
- core.updateCaretPosition()
550
- }
551
- },
552
- 'outdent': {
553
- title: 'Outdent',
554
- icon: SVG.OUTDENT,
555
- type: 'button',
556
- click: (event, core, options) => {
557
- if (!core.state.range) return
558
-
559
- const selectedNode = core.state.range.commonAncestorContainer;
560
-
561
- // Traverse up to the block element
562
- let blockElement = selectedNode;
563
- while (blockElement && blockElement.nodeType === Node.TEXT_NODE) {
564
- blockElement = blockElement.parentNode;
565
- }
566
-
567
- if (blockElement) {
568
- // Decrease the margin-left (outdent)
569
- const currentIndent = parseInt(blockElement.style.marginLeft || '0', 10);
570
- blockElement.style.marginLeft = Math.max(0, currentIndent - 40) + 'px';
571
- }
572
- core.updateCaretPosition()
573
- core.elements.editor.focus();
574
- }
575
- },
576
- 'indent': {
577
- title: 'Indent',
578
- icon: SVG.INDENT,
579
- type: 'button',
580
- click: (event, core, options) => {
581
- if (!core.state.range) return
582
-
583
- const selectedNode = core.state.range.commonAncestorContainer;
584
-
585
- // Traverse up to the block element
586
- let blockElement = selectedNode;
587
- while (blockElement && blockElement.nodeType === Node.TEXT_NODE) {
588
- blockElement = blockElement.parentNode;
589
- }
590
-
591
- if (blockElement) {
592
- // Increase the margin-left (indent)
593
- const currentIndent = parseInt(blockElement.style.marginLeft || '0', 10);
594
- blockElement.style.marginLeft = (currentIndent + 40) + 'px';
595
- }
596
- core.updateCaretPosition()
597
- core.elements.editor.focus();
598
- }
599
- },
600
- 'link': {
601
- title: 'Link',
602
- icon: SVG.LINK,
603
- type: 'button',
604
- click: (event, core, options) => {
605
- const onInputKeydown = (event) => {
606
- if (event.key === 'Enter') {
607
- event.preventDefault();
608
- button.click();
609
- }
610
- }
611
-
612
-
613
- /* --------------- If image or text is already have link then show that link and text in respective input fields ----------------- */
614
- let anchorLink = ''
615
- let anchorText = '';
616
- let underAnchor = false;
617
- if (core.elements.selectedElement && core.elements.selectedElement.tagName === 'IMG') {
618
- const imgElement = core.elements.selectedElement;
619
- if (imgElement) {
620
- const imgParentNode = imgElement.parentNode
621
- if (imgParentNode.nodeName === 'A') {
622
- anchorLink = imgParentNode.href
623
- anchorText = imgElement.alt || ''
624
- underAnchor = true
625
- }
626
- }
627
- } else if (core.state.range) {
628
- const selection = core.state.selection;
629
- if (selection.rangeCount > 0) {
630
- const range = core.state.range;
631
- let parentElement = range.commonAncestorContainer;
632
-
633
- // If the commonAncestorContainer is a text node, use its parent element
634
- if (parentElement.nodeType === Node.TEXT_NODE) {
635
- parentElement = parentElement.parentNode;
636
- }
637
-
638
- if (parentElement.nodeName === 'A') {
639
- anchorLink = parentElement.getAttribute('href')
640
- anchorText = parentElement.innerText || ''
641
- underAnchor = true
642
- }
643
- }
644
- }
645
- const body = document.createElement('div')
646
- const linkLabel = document.createElement("label");
647
- linkLabel.innerText = "Link";
648
- linkLabel.style.display = "block";
649
- linkLabel.style.paddingLeft = "4px";
650
-
651
- const inputLink = document.createElement('input');
652
- inputLink.value = anchorLink
653
- inputLink.type = 'text'
654
- inputLink.placeholder = 'Insert link, mail, phone no'
655
- inputLink.addEventListener('keydown', onInputKeydown);
656
-
657
- const textLabel = document.createElement("label");
658
- textLabel.innerText = "Link Text";
659
- textLabel.style.display = "block";
660
- textLabel.style.paddingLeft = "4px";
661
- textLabel.style.marginTop = "8px";
662
-
663
- const inputText = document.createElement('input');
664
- inputText.value = anchorText
665
- inputText.type = 'text'
666
- inputText.placeholder = 'Link text'
667
- inputText.addEventListener('keydown', onInputKeydown);
668
-
669
- const span = document.createElement('span');
670
- span.className = 'warning'
671
- body.append(linkLabel, inputLink, span, textLabel, inputText);
672
-
673
- const footer = document.createElement('div')
674
- const button = document.createElement('button')
675
- button.type = 'button';
676
- button.className = 'submit';
677
- button.innerText = 'Add';
678
- footer.append(button)
679
-
680
- const modal = openModal({
681
- title: "Link",
682
- bodyNode: body,
683
- footerNode: footer,
684
-
685
- }, core, options)
686
- inputLink.focus()
687
-
688
- button.onclick = () => {
689
- const link = formatLink(inputLink.value);
690
- const value = inputText.value
691
- if (isLinkValid(link)) {
692
- if (underAnchor) {
693
- if (core.elements.selectedElement && core.elements.selectedElement.tagName === 'IMG') {
694
- const imgElement = core.elements.selectedElement;
695
- if (imgElement) {
696
- const imgParentNode = imgElement.parentNode
697
- if (imgParentNode.nodeName === 'A') {
698
- imgParentNode.href = link
699
- imgElement.alt = value
700
- }
701
- }
702
- } else if (core.state.range) {
703
- const selection = core.state.selection;
704
- if (selection.rangeCount > 0) {
705
- const range = core.state.range;
706
- let parentElement = range.commonAncestorContainer;
707
-
708
- // If the commonAncestorContainer is a text node, use its parent element
709
- if (parentElement.nodeType === Node.TEXT_NODE) {
710
- parentElement = parentElement.parentNode;
711
- }
712
-
713
- if (parentElement.nodeName === 'A') {
714
- parentElement.setAttribute('href', link)
715
- parentElement.innerText = value
716
- }
717
- }
718
- }
719
- } else if (core.elements.selectedElement && core.elements.selectedElement.tagName === 'IMG') {
720
- const a = document.createElement('a');
721
- a.href = link
722
- a.target = '_blank'
723
- core.elements.selectedElement.parentNode.insertBefore(a, core.elements.selectedElement);
724
- a.appendChild(core.elements.selectedElement);
725
- core.elements.selectedElement.alt = value
726
- } else {
727
- const a = document.createElement('a');
728
- a.href = link
729
- a.target = '_blank'
730
- a.textContent = value || link
731
- core.insertNode(a);
732
- }
733
- modal.close()
734
- core.elements.editor.focus();
735
- } else {
736
- span.innerText = 'Invalid URL'
737
- }
738
- };
739
- core.updateCaretPosition()
740
- }
741
- },
742
- 'unlink': {
743
- title: 'Unlink',
744
- icon: SVG.UNLINK,
745
- type: 'button',
746
- click: (event, core, options) => {
747
- core.elements.iframeWindow.execCommand('unlink');
748
- core.elements.editor.focus();
749
- core.updateCaretPosition()
750
- }
751
- },
752
- 'horizontal_rule': {
753
- title: 'Horizontal Rule',
754
- icon: SVG.HORIZONTAL_RULE,
755
- type: 'button',
756
- click: (event, core, options) => {
757
- core.elements.iframeWindow.execCommand('insertHorizontalRule');
758
- core.elements.editor.focus();
759
- core.updateCaretPosition()
760
- }
761
- },
762
- 'table': {
763
- title: 'Table',
764
- icon: SVG.TABLE,
765
- type: 'table',
766
- create: (core, options, { rows, cols, header }) => {
767
- const table = document.createElement('table');
768
- table.style.borderCollapse = 'collapse';
769
- table.style.width = core.elements.editor.offsetWidth - 50 + 'px';
770
-
771
- for (let i = 0; i <= rows; i++) {
772
- const tr = document.createElement('tr');
773
- for (let j = 0; j <= cols; j++) {
774
- const td = i === 0 && header ? document.createElement('th') : document.createElement('td');
775
- td.style.border = '1px solid black';
776
- td.appendChild(document.createTextNode('\u00A0')); // Non-breaking space
777
- tr.appendChild(td);
778
- }
779
- table.appendChild(tr);
780
- }
781
-
782
- core.insertNode(table);
783
- core.elements.editor.focus();
784
- core.updateCaretPosition()
785
- }
786
- },
787
- 'mention': {
788
- title: 'Mention',
789
- icon: SVG.MENTION,
790
- type: 'mention',
791
- },
792
- 'emoji': {
793
- title: 'Emoji',
794
- icon: SVG.EMOJI,
795
- type: 'category',
796
- categories: EMOJI_CATEGORIES
797
- },
798
- 'special_character': {
799
- title: 'Special Character',
800
- icon: SVG.SPECIAL_CHARACTER,
801
- type: 'category',
802
- categories: SPECIAL_CHARACTERS
803
- },
804
- 'pexels': {
805
- title: 'Pexels',
806
- icon: SVG.PEXELS,
807
- type: 'gallery',
808
- suggestions: async (options, page) => pexels.suggestions(options, page),
809
- search: async (options, search, page) => pexels.search(options, search, page),
810
- },
811
- 'giphy': {
812
- title: 'GIPHY',
813
- icon: SVG.GIPHY_SVG,
814
- type: 'gallery',
815
- suggestions: async (options, page) => giphy.suggestions(options, page),
816
- search: async (options, search, page) => giphy.search(options, search, page),
817
- },
818
- 'tenor': {
819
- title: 'Tenor',
820
- icon: SVG.TENOR,
821
- type: 'gallery',
822
- suggestions: async (options, next) => tenor.suggestions(options, next),
823
- search: async (options, search) => tenor.search(options, search),
824
- },
825
- 'image': {
826
- title: "Image",
827
- icon: SVG.IMAGE,
828
- type: 'dropdown',
829
- options: [
830
- { label: 'Upload from device', value: 'upload' },
831
- { label: 'Using Link', value: 'link' },
832
- ],
833
- click: async (event, core, options) => {
834
- if (event.target.getAttribute('data-value') === 'upload') {
835
-
836
- const onInputKeydown = (event) => {
837
- if (event.key === 'Enter') {
838
- event.preventDefault();
839
- button.click();
840
- }
841
- };
842
-
843
- // Top
844
- const top = document.createElement('div');
845
- top.style.display = 'flex';
846
- top.style.gap = '16px';
847
-
848
- // Top - left
849
- const left = document.createElement('div');
850
- left.style.display = 'flex';
851
- left.style.flexDirection = 'column';
852
- left.style.gap = '16px';
853
- left.style.flex = '1';
854
-
855
- const uploadBox = document.createElement('div');
856
- uploadBox.className = `${options.classPrefix}${CLASSES.UPLOAD_IMG_BOX}`;
857
- uploadBox.innerHTML = `
858
- <div style="text-align: center; color: #6b7280;">
859
- <div style="font-size: 32px; font-weight: 500;">+</div>
860
- <div style="font-size: 14px;">Click to browse files</div>
861
- </div>
862
- `;
863
-
864
- const fileInput = document.createElement('input');
865
- fileInput.type = 'file';
866
- fileInput.accept = MIMETYPE.IMAGE;
867
- fileInput.style.display = 'none';
868
- uploadBox.addEventListener('click', () => fileInput.click());
869
-
870
- left.append(uploadBox, fileInput);
871
-
872
- // Top - right
873
- const right = document.createElement('div');
874
- right.style.display = 'flex';
875
- right.style.flexDirection = 'column';
876
- right.style.flex = '1';
877
-
878
- const previewBox = document.createElement('div');
879
- previewBox.className = `${options.classPrefix}${CLASSES.UPLOAD_IMG_PREVIEW_BOX}`;
880
-
881
- const imgPreview = document.createElement('img');
882
- imgPreview.className = `${options.classPrefix}${CLASSES.UPLOAD_IMG_PREVIEW}`;
883
- imgPreview.style.display = 'none';
884
-
885
- previewBox.append(imgPreview);
886
- right.append(previewBox);
887
-
888
- // Complete top
889
- top.append(left, right)
890
-
891
- // other fields
892
- const span = document.createElement('div');
893
- span.className = 'warning';
894
-
895
- const altLabel = document.createElement('label');
896
- altLabel.innerText = 'Image Description (Alt Text)';
897
- altLabel.style.display = "block";
898
- altLabel.style.paddingLeft = "4px";
899
- altLabel.style.marginTop = '4px';
900
- altLabel.style.marginBottom = '4px';
901
-
902
- const altInput = document.createElement('input');
903
- altInput.type = 'text';
904
- altInput.placeholder = 'Alternative Text';
905
- altInput.addEventListener('keydown', onInputKeydown);
906
-
907
- const body = document.createElement('div');
908
- body.append(top, span, altLabel, altInput);
909
-
910
- // Footer
911
- const footer = document.createElement('div');
912
- footer.style.display = 'flex';
913
- footer.style.justifyContent = 'space-between';
914
- footer.style.alignItems = 'center';
915
-
916
- const button = document.createElement('button');
917
- button.type = 'button';
918
- button.className = 'submit';
919
- button.innerText = 'Add';
920
-
921
- footer.append(button);
922
-
923
- const modal = openModal(
924
- {
925
- title: "Upload Image",
926
- bodyNode: body,
927
- footerNode: footer,
928
- },
929
- core,
930
- options
931
- );
932
- let base64String = '';
933
-
934
- fileInput.addEventListener('change', (event) => {
935
- const file = event.target.files[0];
936
- if (file && MIMETYPE.IMAGE.includes(file.type)) {
937
- const reader = new FileReader();
938
- reader.onload = () => {
939
- base64String = reader.result;
940
- imgPreview.src = base64String;
941
- imgPreview.style.display = 'block';
942
- };
943
- reader.readAsDataURL(file);
944
- span.innerText = "";
945
- }
946
- });
947
-
948
- fileInput.addEventListener('click', async () => {
949
- let fileFromNative = await core.handleFilePicker(MIMETYPE.IMAGE.split(','), 'image');
950
-
951
- if (fileFromNative) {
952
- base64String = fileFromNative.data;
953
- fileNameSpan.innerText = fileFromNative.name;
954
- imgPreview.src = base64String;
955
- imgPreview.style.display = 'block';
956
- span.innerText = ""; // Clear warning if file is selected
957
-
958
- }
959
- });
960
-
961
- button.onclick = async () => {
962
- if (!base64String) {
963
- span.innerText = 'Image not selected';
964
- return;
965
- }
966
- button.disabled = true;
967
- const originalText = button.innerText;
968
- button.innerHTML = ` <span style="margin-left: 8px;">${originalText}</span> ${SVG.SPINNER}`
969
- const img = document.createElement('img');
970
- img.src = await core.manuplateImage('base64', base64String);
971
- img.alt = altInput.value || "";
972
- button.innerText = originalText
973
-
974
- core.insertNode(img);
975
- modal.close();
976
- };
977
-
978
- }
979
- else if (event.target.getAttribute('data-value') === 'link') {
980
- const onInputKeydown = (event) => {
981
- if (event.key === 'Enter') {
982
- event.preventDefault();
983
- button.click();
984
- }
985
- }
986
-
987
- const body = document.createElement('div')
988
-
989
- const linkLabel = document.createElement("label");
990
- linkLabel.innerText = "Link";
991
- linkLabel.style.display = "block";
992
- linkLabel.style.paddingLeft = "4px";
993
-
994
- const input = document.createElement('input');
995
- input.value = ''
996
- input.type = 'text'
997
- input.placeholder = 'Insert the link'
998
- input.addEventListener('keydown', onInputKeydown);
999
-
1000
- const textLabel = document.createElement("label");
1001
- textLabel.innerText = "Alternative Text";
1002
- textLabel.style.display = "block";
1003
- textLabel.style.marginTop = "8px";
1004
- textLabel.style.paddingLeft = "4px";
1005
-
1006
- const altInput = document.createElement('input');
1007
- altInput.value = ''
1008
- altInput.style.marginTop = '4px'
1009
- altInput.type = 'text'
1010
- altInput.placeholder = 'Alternative Text'
1011
- altInput.addEventListener('keydown', onInputKeydown);
1012
-
1013
- const span = document.createElement('span');
1014
- span.className = 'warning'
1015
- body.append(linkLabel, input, span, textLabel, altInput);
1016
-
1017
- const footer = document.createElement('div')
1018
- const button = document.createElement('button')
1019
- button.type = 'button';
1020
- button.className = 'submit';
1021
- button.innerText = 'Add';
1022
- footer.append(button)
1023
-
1024
- const modal = openModal({
1025
- title: "Image",
1026
- bodyNode: body,
1027
- footerNode: footer,
1028
-
1029
- }, core, options)
1030
-
1031
- button.onclick = () => {
1032
- const value = input.value
1033
- if (REGEX.URL.test(value)) {
1034
- button.disabled = true;
1035
- const img = document.createElement('img');
1036
- img.src = value
1037
- img.alt = altInput.value
1038
- core.insertNode(img);
1039
- modal.close()
1040
- } else {
1041
- span.innerText = 'Invalid url'
1042
- }
1043
- };
1044
- }
1045
- core.elements.editor.focus();
1046
- core.updateCaretPosition()
1047
- }
1048
- },
1049
- 'video': {
1050
- title: "Video",
1051
- icon: SVG.VIDEO,
1052
- type: 'button',
1053
- click: (event, core) => {
1054
- const fileInput = document.createElement('input');
1055
- fileInput.type = 'file';
1056
- fileInput.accept = MIMETYPE.VIDEO
1057
- fileInput.style.display = 'none';
1058
- fileInput.addEventListener('change', async (event) => {
1059
- const file = event.target.files[0]
1060
- if (MIMETYPE.VIDEO.includes(file.type)) {
1061
- const url = await core.uploadVideo(file);
1062
- if (url) {
1063
- const a = document.createElement('a');
1064
- a.href = url.videoUrl
1065
- a.dataset.leksyPreview = 'video-url'
1066
- a.target = '_blank'
1067
- const img = document.createElement('img');
1068
- img.src = url.thumbnailUrl
1069
- a.appendChild(img)
1070
- core.insertNode(a);
1071
- }
1072
- }
1073
- });
1074
-
1075
- fileInput.addEventListener('click', async () => {
1076
- let file = await core.handleFilePicker(MIMETYPE.VIDEO.split(','), 'video');
1077
- if (file && MIMETYPE.VIDEO.includes(file?.type)) {
1078
- const url = await core.uploadVideo(file);
1079
- if (url) {
1080
- const a = document.createElement('a');
1081
- a.href = url.videoUrl
1082
- a.dataset.leksyPreview = 'video-url'
1083
- a.target = '_blank'
1084
- const img = document.createElement('img');
1085
- img.src = url.thumbnailUrl
1086
- a.appendChild(img)
1087
- core.insertNode(a);
1088
- }
1089
- }
1090
- });
1091
-
1092
- fileInput.click();
1093
- core.updateCaretPosition()
1094
- core.elements.editor.focus();
1095
- }
1096
- },
1097
- 'attachment': {
1098
- title: 'Attachment',
1099
- icon: SVG.ATTACHMENT,
1100
- type: 'button',
1101
- click: (event, core) => {
1102
- const fileInput = document.createElement('input');
1103
- fileInput.type = 'file';
1104
- fileInput.style.display = 'none';
1105
- fileInput.multiple = true;
1106
- fileInput.addEventListener('change', (event) => {
1107
- core.onAttachment(event.target.files);
1108
- });
1109
-
1110
- fileInput.addEventListener('click', async () => {
1111
- let files = await core.handleFilePicker(["*"], 'attachment');
1112
- if (files) {
1113
- core.onAttachment(files);
1114
- }
1115
-
1116
- });
1117
- fileInput.click();
1118
- core.updateCaretPosition()
1119
- }
1120
- },
1121
- 'cut': {
1122
- title: 'Cut',
1123
- icon: SVG.CUT,
1124
- type: 'button',
1125
- click: (event, core) => {
1126
- if (!core.state.range) return
1127
- core.elements.editor.focus();
1128
-
1129
- const container = document.createElement('div');
1130
- container.appendChild(core.state.range.cloneContents());
1131
- const htmlContent = container.innerHTML;
1132
-
1133
- const clipboardItem = new ClipboardItem({
1134
- 'text/html': new Blob([htmlContent], { type: 'text/html' })
1135
- });
1136
- navigator.clipboard.write([clipboardItem]).then(() => {
1137
- core.state.range.deleteContents();
1138
- core.updateCaretPosition();
1139
- })
1140
- }
1141
- },
1142
- 'copy': {
1143
- title: 'Copy',
1144
- icon: SVG.COPY,
1145
- type: 'button',
1146
- click: (event, core) => {
1147
- if (!core.state.range) return
1148
- core.elements.editor.focus();
1149
-
1150
- const container = document.createElement('div');
1151
- container.appendChild(core.state.range.cloneContents());
1152
- const htmlContent = container.innerHTML;
1153
-
1154
- const clipboardItem = new ClipboardItem({
1155
- 'text/html': new Blob([htmlContent], { type: 'text/html' })
1156
- });
1157
- navigator.clipboard.write([clipboardItem]).then(() => {
1158
- core.updateCaretPosition();
1159
- })
1160
- }
1161
- },
1162
- 'paste': {
1163
- title: 'Paste',
1164
- icon: SVG.PASTE,
1165
- type: 'button',
1166
- click: async (event, core) => {
1167
- core.elements.editor.focus();
1168
-
1169
- const clipboardItems = await navigator.clipboard.read();
1170
- let pastedContentNode;
1171
-
1172
- for (const item of clipboardItems) {
1173
- if (item.types.includes('text/html')) {
1174
- const blob = await item.getType('text/html');
1175
- const container = document.createElement('div');
1176
- if (blob) {
1177
- container.innerHTML = await blob.text();
1178
- pastedContentNode = container;
1179
- }
1180
- break;
1181
- } else if (item.types.includes('text/plain')) {
1182
- const blob = await item.getType('text/plain');
1183
- const plainTextContent = await blob.text();
1184
- pastedContentNode = document.createTextNode(plainTextContent);
1185
- break;
1186
- }
1187
- else if (item.types.find(type => type.startsWith('image/'))) {
1188
- const blob = await item.getType(item.types.find(type => type.startsWith('image/')));
1189
- const reader = new FileReader();
1190
-
1191
- reader.onload = async function (event) {
1192
- const img = document.createElement('img');
1193
- const base64String = event.target.result;
1194
- img.src = await core.manuplateImage('base64', base64String);
1195
- pastedContentNode = img;
1196
- };
1197
-
1198
- reader.readAsDataURL(blob);
1199
- break;
1200
- }
1201
- }
1202
- if (pastedContentNode) {
1203
- core.state.range.deleteContents();
1204
- core.insertNode(pastedContentNode)
1205
- }
1206
- core.updateCaretPosition();
1207
- }
1208
- },
1209
- 'select_all': {
1210
- title: 'Select All',
1211
- icon: SVG.SELECT_ALL,
1212
- type: 'button',
1213
- click: (event, core) => {
1214
- core.elements.editor.focus();
1215
- core.elements.iframeWindow.execCommand('selectAll');
1216
- core.updateCaretPosition()
1217
- }
1218
- },
1219
- 'fullscreen': {
1220
- title: 'Full screen',
1221
- icon: SVG.FULLSCREEN,
1222
- type: 'button',
1223
- click: (event, core) => {
1224
- const isExists = core.elements.base.classList.toggle('fullscreen')
1225
- changeToolbarHtmlByName(core, 'fullscreen', isExists ? SVG.FULLSCREEN_EXIT : SVG.FULLSCREEN)
1226
- }
1227
- },
1228
- 'print': {
1229
- title: 'Print',
1230
- icon: SVG.PRINT,
1231
- type: 'button',
1232
- click: (event, core) => {
1233
- core.elements.iframeContainer.contentWindow.print()
1234
- }
1235
- },
1236
- 'embed': {
1237
- title: 'Embed Link',
1238
- icon: SVG.EMBED,
1239
- type: 'button',
1240
- click: (event, core, options) => {
1241
- const onInputKeydown = (event) => {
1242
- if (event.key === 'Enter') {
1243
- event.preventDefault();
1244
- button.click();
1245
- }
1246
- };
1247
-
1248
- const body = document.createElement('div');
1249
- body.className = "link"
1250
-
1251
- // Input for Embed URL
1252
- const embedLabel = document.createElement("label");
1253
- embedLabel.innerText = "Embed URL";
1254
- embedLabel.style.display = "block";
1255
- embedLabel.style.paddingLeft = "4px";
1256
- const urlInput = document.createElement('input');
1257
- urlInput.type = 'text';
1258
- urlInput.placeholder = 'Enter Embed URL';
1259
- urlInput.style.marginBottom = '10px';
1260
- urlInput.addEventListener('keydown', onInputKeydown);
1261
-
1262
- // Input for Width (%)
1263
- const widthLabel = document.createElement("label");
1264
- widthLabel.innerText = "Width";
1265
- widthLabel.style.display = "block";
1266
- widthLabel.style.paddingLeft = "4px";
1267
- const widthInput = document.createElement('input');
1268
- widthInput.type = 'text';
1269
- widthInput.placeholder = 'Width (px)';
1270
- widthInput.style.marginBottom = '10px';
1271
- widthInput.value = "400"; // Default to 400px
1272
-
1273
- // Input for Height (%)
1274
- const heightLabel = document.createElement("label");
1275
- heightLabel.innerText = "Height";
1276
- heightLabel.style.display = "block";
1277
- heightLabel.style.paddingLeft = "4px";
1278
- const heightInput = document.createElement('input');
1279
- heightInput.type = 'text';
1280
- heightInput.placeholder = 'Height (px)';
1281
- heightInput.style.marginBottom = '10px';
1282
- heightInput.value = "400"; // Default to 400px
1283
-
1284
- // Warning span
1285
- const span = document.createElement('span');
1286
- span.className = 'warning';
1287
-
1288
- body.append(embedLabel, urlInput, widthLabel, widthInput, heightLabel, heightInput, span);
1289
-
1290
- const footer = document.createElement('div');
1291
- const button = document.createElement('button');
1292
- button.type = 'button';
1293
- button.className = 'submit';
1294
- button.innerText = 'Add';
1295
- footer.append(button);
1296
-
1297
- const modal = openModal(
1298
- {
1299
- title: "Embed URL",
1300
- bodyNode: body,
1301
- footerNode: footer,
1302
- },
1303
- core,
1304
- options
1305
- );
1306
-
1307
- button.onclick = () => {
1308
- const enteredUrl = urlInput.value.trim();
1309
-
1310
- const { platform, id } = extractSocialMediaId(enteredUrl);
1311
- if (!platform || !id) {
1312
- span.innerText = 'Invalid URL';
1313
- return;
1314
- }
1315
-
1316
- const url = constructEmbedUrl(platform, id);
1317
- if (!url) {
1318
- span.innerText = 'Unsupported platform';
1319
- return;
1320
- }
1321
- const wrapper = document.createElement('figure');
1322
- wrapper.className = 'iframe-wrapper';
1323
- wrapper.style.height = `${heightInput.value}px`
1324
- wrapper.style.width = `${widthInput.value}px`
1325
- const embed = document.createElement('iframe');
1326
- embed.src = url;
1327
- embed.style.display = "inline-block"
1328
- embed.style.height = "100%"
1329
- embed.style.width = "100%"
1330
- embed.style.border = "none";
1331
- wrapper.append(embed)
1332
- core.insertNode(wrapper);
1333
- modal.close();
1334
- };
1335
- core.elements.editor.focus();
1336
- core.updateCaretPosition()
1337
- }
1338
- },
1339
- 'table_of_content': {
1340
- title: 'Table of Content',
1341
- icon: SVG.TABLE_OF_CONTENT,
1342
- type: 'button',
1343
- click: (event, core, options) => {
1344
- insertTOC(core, options);
1345
- }
1346
- },
1347
-
1348
- }
1349
-
1350
- export default PLUGINS
1351
- export {
1352
- applyTextFormat,
1353
- applyOrderList,
1354
- applyUnorderedList
1355
- }