leksy-editor 1.0.4 → 1.0.6
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/constant.js +1 -1
- package/index.js +4 -3
- package/package.json +1 -1
- package/plugin.js +47 -2
- package/utilities.js +41 -4
package/constant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const WHILE_LISTED_TAG = "br|p|div|pre|blockquote|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|thead|tbody|tr|th|td|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary|font|article|section|main|header|footer|aside|mark|cite|abbr|dfn|nav|caption|picture|dl|dt|dd"
|
|
1
|
+
const WHILE_LISTED_TAG = "br|p|div|pre|blockquote|h|h1|h2|h3|h4|h5|h6|ol|ul|li|hr|figure|figcaption|img|iframe|audio|video|source|table|thead|tbody|tr|th|td|a|b|strong|var|i|em|u|ins|s|span|strike|del|sub|sup|code|svg|path|details|summary|font|article|section|main|header|footer|aside|mark|cite|abbr|dfn|nav|caption|picture|dl|dt|dd|time|label|kbd|samp|wbr";
|
|
2
2
|
const WHILE_LISTED_ATTRIBUTES = "align|alt|bgcolor|border|cellpadding|cellspacing|color|colspan|contenteditable|dir|download|face|height|href|lang|name|rowspan|size|src|style|target|title|valign|width|background|media|sizes|srcset";
|
|
3
3
|
|
|
4
4
|
const REGEX = {
|
package/index.js
CHANGED
|
@@ -20,6 +20,7 @@ class LeksyEditor {
|
|
|
20
20
|
* @property {String} pexelsApiKey
|
|
21
21
|
* @property {String} tenorApiKey
|
|
22
22
|
* @property {Object} cssVariables
|
|
23
|
+
* @property {Boolean} disablePastedColorStyles
|
|
23
24
|
*/
|
|
24
25
|
/**
|
|
25
26
|
*
|
|
@@ -457,7 +458,7 @@ class LeksyEditor {
|
|
|
457
458
|
CSS_VARIABLES[key] = cssVariable[key];
|
|
458
459
|
}
|
|
459
460
|
});
|
|
460
|
-
|
|
461
|
+
|
|
461
462
|
CSS.IFRAME_VARIABLES = `
|
|
462
463
|
:root {
|
|
463
464
|
--primary: ${CSS_VARIABLES.primary};
|
|
@@ -601,12 +602,12 @@ class LeksyEditor {
|
|
|
601
602
|
}
|
|
602
603
|
|
|
603
604
|
const pastedData = clipboardData.getData('text/html') || clipboardData.getData('text/plain');
|
|
604
|
-
core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(pastedData))
|
|
605
|
+
core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(pastedData, options, core))
|
|
605
606
|
};
|
|
606
607
|
contentEditableDiv.ondrop = (e) => {
|
|
607
608
|
e.preventDefault();
|
|
608
609
|
const droppedData = e.dataTransfer.getData('text/html') || e.dataTransfer.getData('text/plain');
|
|
609
|
-
core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(droppedData))
|
|
610
|
+
core.elements.iframeWindow.execCommand('insertHtml', false, cleanHTML(droppedData, options, core))
|
|
610
611
|
};
|
|
611
612
|
contentEditableDiv.onblur = (e) => {
|
|
612
613
|
core.onBlur(e.target.innerHTML)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leksy-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Leksy Editor is an alternative to traditional WYSIWYG editors, designed primarily for creating mail templates, blogs, and documents without any content manipulation.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
package/plugin.js
CHANGED
|
@@ -136,12 +136,12 @@ const PLUGINS = {
|
|
|
136
136
|
title: 'Code View',
|
|
137
137
|
icon: SVG.CODE_VIEW,
|
|
138
138
|
type: 'button',
|
|
139
|
-
click: (event, core) => {
|
|
139
|
+
click: (event, core, options) => {
|
|
140
140
|
if (core.state.isCodeViewOpen) {
|
|
141
141
|
core.elements.codeview.style.display = 'none'
|
|
142
142
|
core.elements.iframeContainer.style.display = 'block'
|
|
143
143
|
if (core.elements.stepper) core.elements.stepper.style.display = 'flex'
|
|
144
|
-
const _cleanHTML = cleanHTML(core.elements.codeview.value)
|
|
144
|
+
const _cleanHTML = cleanHTML(core.elements.codeview.value, options, core)
|
|
145
145
|
core.onChange(_cleanHTML);
|
|
146
146
|
core.elements.editor.innerHTML = _cleanHTML
|
|
147
147
|
core.elements.editor.focus()
|
|
@@ -314,6 +314,42 @@ const PLUGINS = {
|
|
|
314
314
|
core.updateCaretPosition()
|
|
315
315
|
}
|
|
316
316
|
},
|
|
317
|
+
'remove_font_color': {
|
|
318
|
+
title: 'Remove Font Color',
|
|
319
|
+
icon: `${SVG.REMOVE_FORMAT}${SVG.FONT_COLOR}`,
|
|
320
|
+
type: "button",
|
|
321
|
+
click: (event, core, options) => {
|
|
322
|
+
if (!core.state.range) return;
|
|
323
|
+
core.elements.editor.focus();
|
|
324
|
+
const container = document.createElement('div');
|
|
325
|
+
const clonedContents = core.state.range.cloneContents();
|
|
326
|
+
container.appendChild(clonedContents);
|
|
327
|
+
if (container.innerHTML === container.innerText) {
|
|
328
|
+
const parentEl = core.state.range.startContainer.parentElement;
|
|
329
|
+
parentEl.removeAttribute('color');
|
|
330
|
+
parentEl.style.removeProperty('color');
|
|
331
|
+
|
|
332
|
+
} else {
|
|
333
|
+
container.querySelectorAll('[style]').forEach((el) => {
|
|
334
|
+
el.style.removeProperty('color');
|
|
335
|
+
if (!el.getAttribute('style')) {
|
|
336
|
+
el.removeAttribute('style');
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
container.querySelectorAll('font[color]').forEach((el) => {
|
|
340
|
+
el.removeAttribute('color');
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
core.state.range.deleteContents();
|
|
344
|
+
const frag = document.createDocumentFragment();
|
|
345
|
+
Array.from(container.childNodes).forEach((node) => {
|
|
346
|
+
frag.appendChild(node);
|
|
347
|
+
});
|
|
348
|
+
core.state.range.insertNode(frag);
|
|
349
|
+
core.updateCaretPosition();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
},
|
|
317
353
|
'highlight_color': {
|
|
318
354
|
title: 'Highlight Color',
|
|
319
355
|
icon: SVG.HIGHLIGHT_COLOR,
|
|
@@ -324,6 +360,15 @@ const PLUGINS = {
|
|
|
324
360
|
core.updateCaretPosition()
|
|
325
361
|
}
|
|
326
362
|
},
|
|
363
|
+
'remove_highlight_color': {
|
|
364
|
+
title: 'Remove Highlight Color',
|
|
365
|
+
icon: `${SVG.REMOVE_FORMAT}${SVG.HIGHLIGHT_COLOR}`,
|
|
366
|
+
type: "button",
|
|
367
|
+
click: (event, core, options) => {
|
|
368
|
+
core.elements.iframeWindow.execCommand('hiliteColor', false, "transparent"); // Apply color to selected text
|
|
369
|
+
core.updateCaretPosition()
|
|
370
|
+
}
|
|
371
|
+
},
|
|
327
372
|
'text_style': {
|
|
328
373
|
title: 'Text Style',
|
|
329
374
|
icon: SVG.TEXT_STYLE,
|
package/utilities.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CLASSES, FONT_SIZES, FONTS, FORMATS, GIPHY_POWERED_IMAGE, REGEX, RESIZER_PLUGINS, SOCIAL_MEDIA_BASEURLS, SOCIAL_MEDIA_PATTERNS, SVG, TABLE_PLUGINS } from "./constant";
|
|
2
2
|
|
|
3
|
-
const traverseAndClean = (element) => {
|
|
3
|
+
const traverseAndClean = (element, options, core) => {
|
|
4
4
|
if (!element?.childNodes) return;
|
|
5
5
|
|
|
6
6
|
// Iterate through child nodes
|
|
@@ -22,22 +22,58 @@ const traverseAndClean = (element) => {
|
|
|
22
22
|
attributesToRemove.push(attr.name);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
if (options?.disablePastedColorStyles) {
|
|
26
|
+
const nodeStyles = node.style
|
|
27
|
+
let bgColor = nodeStyles.backgroundColor;
|
|
28
|
+
let textColor = nodeStyles.color;
|
|
29
|
+
let currentNode = core?.state.range.startContainer;
|
|
30
|
+
let bodyBg = getEffectiveBackgroundColor(currentNode);
|
|
31
|
+
if (bodyBg && bodyBg?.startsWith('rgb')) {
|
|
32
|
+
bodyBg = rgbToHex(bodyBg)
|
|
33
|
+
}
|
|
34
|
+
if (bgColor && bgColor?.startsWith('rgb')) {
|
|
35
|
+
bgColor = rgbToHex(bgColor)
|
|
36
|
+
}
|
|
37
|
+
if (textColor && textColor?.startsWith('rgb')) {
|
|
38
|
+
textColor = rgbToHex(textColor)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (bodyBg === bgColor) node.style.removeProperty('background-color');
|
|
42
|
+
if (bodyBg === textColor) node.style.removeProperty('color');
|
|
43
|
+
}
|
|
25
44
|
attributesToRemove.forEach(attr => node.removeAttribute(attr));
|
|
26
45
|
|
|
27
46
|
// Process child nodes
|
|
28
|
-
traverseAndClean(node);
|
|
47
|
+
traverseAndClean(node, options,core);
|
|
29
48
|
}
|
|
30
49
|
}
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
|
|
34
|
-
const
|
|
53
|
+
const getEffectiveBackgroundColor = (element) => {
|
|
54
|
+
let el = element;
|
|
55
|
+
while (el) {
|
|
56
|
+
const bgColor = window.getComputedStyle(el).backgroundColor;
|
|
57
|
+
|
|
58
|
+
if (bgColor !== 'rgba(0, 0, 0, 0)' &&
|
|
59
|
+
bgColor !== 'transparent' &&
|
|
60
|
+
bgColor !== '') {
|
|
61
|
+
return bgColor;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
el = el !== document.body ? el.parentElement : null;
|
|
65
|
+
}
|
|
66
|
+
const bodyBg = window.getComputedStyle(document.body).backgroundColor;
|
|
67
|
+
return bodyBg
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const cleanHTML = (html, options, core) => {
|
|
35
71
|
// Create a temporary div to manipulate the HTML
|
|
36
72
|
const tempDiv = document.createElement('div');
|
|
37
73
|
tempDiv.innerHTML = html;
|
|
38
74
|
|
|
39
75
|
// Traverse through all elements and clean attributes
|
|
40
|
-
traverseAndClean(tempDiv);
|
|
76
|
+
traverseAndClean(tempDiv, options, core);
|
|
41
77
|
|
|
42
78
|
// Return manipulated HTML
|
|
43
79
|
return tempDiv.innerHTML;
|
|
@@ -142,6 +178,7 @@ const makeToolbarColor = (_plugin, options, core) => {
|
|
|
142
178
|
|
|
143
179
|
const pluginIcon = document.createElement('div');
|
|
144
180
|
pluginIcon.innerHTML = _plugin.icon;
|
|
181
|
+
pluginIcon.onclick = () => pluginButton.click()
|
|
145
182
|
pluginContainer.append(pluginButton, pluginIcon)
|
|
146
183
|
return pluginContainer
|
|
147
184
|
}
|