@editora/core 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.css +1 -0
- package/dist/editora.min.js +517 -4
- package/dist/editora.min.js.map +1 -0
- package/dist/editora.umd.js +517 -4
- package/dist/editora.umd.js.map +1 -0
- package/dist/index-BK2lHfHK.js +2 -0
- package/dist/index-BK2lHfHK.js.map +1 -0
- package/dist/index-BS4zT-KN.mjs +1119 -0
- package/dist/index-BS4zT-KN.mjs.map +1 -0
- package/dist/index.cjs.js +517 -4
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.esm.js +1398 -122
- package/dist/index.esm.js.map +1 -0
- package/dist/webcomponent.cjs.js +2 -0
- package/dist/webcomponent.cjs.js.map +1 -0
- package/dist/webcomponent.esm.js +6 -0
- package/dist/webcomponent.esm.js.map +1 -0
- package/dist/webcomponent.min.js +4073 -0
- package/dist/webcomponent.min.js.map +1 -0
- package/dist/webcomponent.umd.js +4073 -0
- package/dist/webcomponent.umd.js.map +1 -0
- package/package.json +52 -6
package/dist/index.esm.js
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { P as g, E as y, a as f, T as b, R as v } from "./index-BS4zT-KN.mjs";
|
|
2
|
+
import { C as B, c as G, F as K, d as V, S as W, b as X } from "./index-BS4zT-KN.mjs";
|
|
3
|
+
class k {
|
|
4
|
+
constructor(e) {
|
|
5
|
+
if (this.listeners = [], e instanceof g)
|
|
6
|
+
this.pluginManager = e;
|
|
7
|
+
else {
|
|
8
|
+
const i = e;
|
|
9
|
+
this.pluginManager = new g(), i.plugins && Array.isArray(i.plugins) && i.plugins.forEach((n) => {
|
|
10
|
+
this.pluginManager.register(n);
|
|
11
|
+
}), i.element && (this.domElement = i.element, this.setupDOMElement(i));
|
|
12
|
+
}
|
|
13
|
+
const t = this.pluginManager.buildSchema();
|
|
14
|
+
this.state = y.create(t), this.commands = this.pluginManager.getCommands();
|
|
4
15
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
16
|
+
setupDOMElement(e) {
|
|
17
|
+
this.domElement && (e.enableToolbar !== !1 && e.toolbarElement ? this.toolbarElement = e.toolbarElement : e.enableToolbar !== !1 && (this.toolbarElement = document.createElement("div"), this.toolbarElement.className = "editora-toolbar-container", this.domElement.appendChild(this.toolbarElement)), this.contentElement = document.createElement("div"), this.contentElement.contentEditable = "true", this.contentElement.className = "editora-content", this.contentElement.style.minHeight = "200px", this.contentElement.style.outline = "none", this.contentElement.style.padding = "12px", e.content && (this.contentElement.innerHTML = e.content), this.domElement.appendChild(this.contentElement), this.contentElement.addEventListener("input", () => {
|
|
18
|
+
this.listeners.forEach((t) => t(this.state));
|
|
19
|
+
}));
|
|
8
20
|
}
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
setupKeyboardShortcuts(e) {
|
|
22
|
+
const t = {};
|
|
23
|
+
e.forEach((i) => {
|
|
24
|
+
i.shortcut && (t[i.shortcut.toLowerCase()] = i.command);
|
|
25
|
+
}), document.addEventListener("keydown", (i) => {
|
|
26
|
+
if (this.contentElement !== document.activeElement && !(document.activeElement instanceof HTMLElement && document.activeElement.contentEditable === "true"))
|
|
27
|
+
return;
|
|
28
|
+
const n = [];
|
|
29
|
+
(i.ctrlKey || i.metaKey) && n.push("ctrl"), i.shiftKey && n.push("shift"), i.altKey && n.push("alt");
|
|
30
|
+
const s = i.key.toLowerCase(), o = n.length > 0 ? `${n.join("+")}+${s}` : s, r = t[o];
|
|
31
|
+
r && (i.preventDefault(), this.execCommand(r));
|
|
32
|
+
});
|
|
11
33
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.listeners = [], this.pluginManager = e;
|
|
16
|
-
const t = e.buildSchema();
|
|
17
|
-
this.state = l.create(t), this.commands = e.getCommands();
|
|
34
|
+
handleToolbarCommand(e, t) {
|
|
35
|
+
const n = this.pluginManager.getToolbarItems().find((s) => s.id && s.id === e || s.command === e);
|
|
36
|
+
n && (t !== void 0 ? this.execCommand(n.command, t) : this.execCommand(n.command));
|
|
18
37
|
}
|
|
19
38
|
setState(e) {
|
|
20
39
|
this.state = e, this.listeners.forEach((t) => t(e));
|
|
@@ -24,54 +43,33 @@ class y {
|
|
|
24
43
|
this.listeners = this.listeners.filter((t) => t !== e);
|
|
25
44
|
};
|
|
26
45
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return i ? (this.setState(i), !0) : !1;
|
|
32
|
-
}
|
|
33
|
-
setContent(e) {
|
|
34
|
-
this.setState(this.state.apply(e));
|
|
35
|
-
}
|
|
36
|
-
getContent() {
|
|
37
|
-
return this.state.doc;
|
|
46
|
+
// Alias for onChange to support both patterns
|
|
47
|
+
on(e, t) {
|
|
48
|
+
return e === "change" || e === "input" ? this.onChange(t) : () => {
|
|
49
|
+
};
|
|
38
50
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
constructor(e, t) {
|
|
42
|
-
this.nodes = new Map(Object.entries(e)), this.marks = new Map(Object.entries(t));
|
|
51
|
+
getElement() {
|
|
52
|
+
return this.contentElement || this.domElement || null;
|
|
43
53
|
}
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
execCommand(e, t) {
|
|
55
|
+
const i = this.commands[e];
|
|
56
|
+
if (!i)
|
|
57
|
+
return console.warn(`Command not found: ${e}`), !1;
|
|
58
|
+
let n;
|
|
59
|
+
return t !== void 0 ? n = i(this.state, t) : n = i(this.state), n ? (this.setState(n), !0) : !1;
|
|
46
60
|
}
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
setContent(e) {
|
|
62
|
+
typeof e == "string" ? this.contentElement && (this.contentElement.innerHTML = e) : this.setState(this.state.apply(e));
|
|
49
63
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
constructor() {
|
|
53
|
-
this.plugins = [];
|
|
54
|
-
}
|
|
55
|
-
register(e) {
|
|
56
|
-
this.plugins.push(e);
|
|
57
|
-
}
|
|
58
|
-
buildSchema() {
|
|
59
|
-
const e = {}, t = {};
|
|
60
|
-
return this.plugins.forEach((i) => {
|
|
61
|
-
i.nodes && Object.assign(e, i.nodes), i.marks && Object.assign(t, i.marks);
|
|
62
|
-
}), new g(e, t);
|
|
63
|
-
}
|
|
64
|
-
getCommands() {
|
|
65
|
-
const e = {};
|
|
66
|
-
return this.plugins.forEach((t) => {
|
|
67
|
-
t.commands && Object.assign(e, t.commands);
|
|
68
|
-
}), e;
|
|
64
|
+
getContent() {
|
|
65
|
+
return this.contentElement ? this.contentElement.innerHTML : this.state.doc;
|
|
69
66
|
}
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
destroy() {
|
|
68
|
+
this.listeners = [], this.contentElement && this.contentElement.removeEventListener("input", () => {
|
|
69
|
+
});
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
|
-
class
|
|
72
|
+
class E {
|
|
75
73
|
constructor(e) {
|
|
76
74
|
this.initialized = !1, this.plugin = e;
|
|
77
75
|
}
|
|
@@ -79,37 +77,40 @@ class m {
|
|
|
79
77
|
* Safe initialization
|
|
80
78
|
*/
|
|
81
79
|
initialize(e) {
|
|
80
|
+
var t, i;
|
|
82
81
|
if (this.initialized)
|
|
83
82
|
return console.warn(`Plugin "${this.plugin.name}" already initialized`), !1;
|
|
84
83
|
try {
|
|
85
|
-
return this.context = e, this.plugin.context
|
|
86
|
-
} catch (
|
|
87
|
-
return console.error(`Failed to initialize plugin "${this.plugin.name}":`,
|
|
84
|
+
return this.context = e, (t = this.plugin.context) != null && t.initialize && this.plugin.context.initialize(), (i = this.plugin.context) != null && i.onEditorReady && e.provider && this.plugin.context.onEditorReady(e), this.initialized = !0, !0;
|
|
85
|
+
} catch (n) {
|
|
86
|
+
return console.error(`Failed to initialize plugin "${this.plugin.name}":`, n), !1;
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
/**
|
|
91
90
|
* Safe destruction
|
|
92
91
|
*/
|
|
93
92
|
destroy() {
|
|
93
|
+
var e;
|
|
94
94
|
if (!this.initialized)
|
|
95
95
|
return !1;
|
|
96
96
|
try {
|
|
97
|
-
return this.plugin.context
|
|
98
|
-
} catch (
|
|
99
|
-
return console.error(`Failed to destroy plugin "${this.plugin.name}":`,
|
|
97
|
+
return (e = this.plugin.context) != null && e.destroy && this.plugin.context.destroy(), this.initialized = !1, this.context = void 0, !0;
|
|
98
|
+
} catch (t) {
|
|
99
|
+
return console.error(`Failed to destroy plugin "${this.plugin.name}":`, t), !1;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
103
|
* Safe command execution
|
|
104
104
|
*/
|
|
105
105
|
executeCommand(e, ...t) {
|
|
106
|
+
var i;
|
|
106
107
|
if (!this.initialized)
|
|
107
108
|
return console.warn(`Plugin "${this.plugin.name}" not initialized, cannot execute command "${e}"`), null;
|
|
108
109
|
try {
|
|
109
|
-
const i = this.plugin.commands
|
|
110
|
-
return
|
|
111
|
-
} catch (
|
|
112
|
-
return console.error(`Error executing command "${e}" in plugin "${this.plugin.name}":`,
|
|
110
|
+
const n = (i = this.plugin.commands) == null ? void 0 : i[e];
|
|
111
|
+
return n ? n(...t) : (console.warn(`Command "${e}" not found in plugin "${this.plugin.name}"`), null);
|
|
112
|
+
} catch (n) {
|
|
113
|
+
return console.error(`Error executing command "${e}" in plugin "${this.plugin.name}":`, n), null;
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
/**
|
|
@@ -137,12 +138,12 @@ class m {
|
|
|
137
138
|
return this.context;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
|
-
function
|
|
141
|
-
return new
|
|
141
|
+
function M(a) {
|
|
142
|
+
return new E(a);
|
|
142
143
|
}
|
|
143
|
-
class
|
|
144
|
+
class L {
|
|
144
145
|
constructor(e) {
|
|
145
|
-
this.shortcuts = /* @__PURE__ */ new Map(), this.enabled = !0, this.isMac = typeof navigator
|
|
146
|
+
this.shortcuts = /* @__PURE__ */ new Map(), this.enabled = !0, this.isMac = typeof navigator != "undefined" && navigator.platform.toUpperCase().indexOf("MAC") >= 0, (e == null ? void 0 : e.enabled) === !1 && (this.enabled = !1), this.registerDefaultShortcuts(), e != null && e.shortcuts && e.shortcuts.forEach((t) => this.registerShortcut(t)), e != null && e.customShortcuts && Object.values(e.customShortcuts).forEach((t) => {
|
|
146
147
|
this.registerShortcut(t);
|
|
147
148
|
});
|
|
148
149
|
}
|
|
@@ -427,8 +428,8 @@ class M {
|
|
|
427
428
|
}
|
|
428
429
|
handleKeyDown(e, t) {
|
|
429
430
|
if (!this.enabled) return !1;
|
|
430
|
-
const i = this.getEventKey(e),
|
|
431
|
-
return
|
|
431
|
+
const i = this.getEventKey(e), n = this.shortcuts.get(i);
|
|
432
|
+
return n ? (n.preventDefault !== !1 && (e.preventDefault(), e.stopPropagation()), t(n.command, n.params), !0) : !1;
|
|
432
433
|
}
|
|
433
434
|
enable() {
|
|
434
435
|
this.enabled = !0;
|
|
@@ -453,19 +454,19 @@ class M {
|
|
|
453
454
|
}
|
|
454
455
|
getShortcutsHelp() {
|
|
455
456
|
const e = this.getAllShortcuts(), t = /* @__PURE__ */ new Map();
|
|
456
|
-
e.forEach((
|
|
457
|
-
const
|
|
458
|
-
t.has(
|
|
457
|
+
e.forEach((n) => {
|
|
458
|
+
const s = this.getShortcutCategory(n.command);
|
|
459
|
+
t.has(s) || t.set(s, []), t.get(s).push(n);
|
|
459
460
|
});
|
|
460
461
|
let i = `# Keyboard Shortcuts
|
|
461
462
|
|
|
462
463
|
`;
|
|
463
|
-
return t.forEach((
|
|
464
|
-
i += `## ${
|
|
464
|
+
return t.forEach((n, s) => {
|
|
465
|
+
i += `## ${s}
|
|
465
466
|
|
|
466
|
-
`,
|
|
467
|
-
const
|
|
468
|
-
i += `- **${
|
|
467
|
+
`, n.forEach((o) => {
|
|
468
|
+
const r = this.getShortcutDescription(o);
|
|
469
|
+
i += `- **${r}**: ${o.description || o.command}
|
|
469
470
|
`;
|
|
470
471
|
}), i += `
|
|
471
472
|
`;
|
|
@@ -475,17 +476,17 @@ class M {
|
|
|
475
476
|
return e.includes("toggle") && (e.includes("Bold") || e.includes("Italic") || e.includes("Underline") || e.includes("Strike") || e.includes("Code") || e.includes("Super") || e.includes("Sub")) ? "Text Formatting" : e.includes("Heading") || e.includes("Paragraph") ? "Block Formatting" : e.includes("List") || e.includes("Checklist") ? "Lists" : e.includes("Alignment") || e.includes("Indent") ? "Alignment & Indentation" : e.includes("undo") || e.includes("redo") ? "History" : e.includes("insert") ? "Insert" : e.includes("find") || e.includes("replace") ? "Find & Replace" : e.includes("Accessibility") || e.includes("spell") ? "Tools" : "Other";
|
|
476
477
|
}
|
|
477
478
|
}
|
|
478
|
-
function
|
|
479
|
+
function I(a = {}) {
|
|
479
480
|
const {
|
|
480
481
|
enabled: e = !1,
|
|
481
482
|
provider: t = "browser",
|
|
482
483
|
apiUrl: i = "",
|
|
483
|
-
apiHeaders:
|
|
484
|
-
language:
|
|
485
|
-
customDictionary:
|
|
486
|
-
ignoreAllCaps:
|
|
487
|
-
ignoreNumbers:
|
|
488
|
-
} =
|
|
484
|
+
apiHeaders: n = {},
|
|
485
|
+
language: s = "en",
|
|
486
|
+
customDictionary: o = [],
|
|
487
|
+
ignoreAllCaps: r = !0,
|
|
488
|
+
ignoreNumbers: l = !0
|
|
489
|
+
} = a;
|
|
489
490
|
return {
|
|
490
491
|
name: "spellcheck",
|
|
491
492
|
context: {
|
|
@@ -493,7 +494,7 @@ function b(n = {}) {
|
|
|
493
494
|
if (e)
|
|
494
495
|
switch (console.log("[Spellcheck Plugin] Initialized", {
|
|
495
496
|
provider: t,
|
|
496
|
-
language:
|
|
497
|
+
language: s
|
|
497
498
|
}), t) {
|
|
498
499
|
case "browser":
|
|
499
500
|
console.log("[Spellcheck] Using browser spellcheck");
|
|
@@ -509,13 +510,13 @@ function b(n = {}) {
|
|
|
509
510
|
destroy: () => {
|
|
510
511
|
console.log("[Spellcheck Plugin] Destroyed");
|
|
511
512
|
},
|
|
512
|
-
onEditorReady: (
|
|
513
|
+
onEditorReady: (d) => {
|
|
513
514
|
console.log("[Spellcheck Plugin] Editor ready");
|
|
514
515
|
}
|
|
515
516
|
},
|
|
516
517
|
commands: {
|
|
517
518
|
toggleSpellcheck: () => (console.log("[Spellcheck] Toggle command (not implemented)"), null),
|
|
518
|
-
addToDictionary: (
|
|
519
|
+
addToDictionary: (d) => (console.log("[Spellcheck] Add to dictionary:", d), null),
|
|
519
520
|
checkSpelling: async () => (console.log("[Spellcheck] Check spelling (not implemented)"), null)
|
|
520
521
|
},
|
|
521
522
|
toolbar: e ? [
|
|
@@ -528,22 +529,22 @@ function b(n = {}) {
|
|
|
528
529
|
] : []
|
|
529
530
|
};
|
|
530
531
|
}
|
|
531
|
-
function
|
|
532
|
+
function T(a = {}) {
|
|
532
533
|
const {
|
|
533
534
|
uploadUrl: e = "",
|
|
534
535
|
libraryUrl: t = "",
|
|
535
536
|
maxFileSize: i = 10 * 1024 * 1024,
|
|
536
537
|
// 10MB
|
|
537
|
-
allowedTypes:
|
|
538
|
-
headers:
|
|
539
|
-
withCredentials:
|
|
540
|
-
chunkSize:
|
|
538
|
+
allowedTypes: n = ["image/jpeg", "image/png", "image/gif", "image/webp"],
|
|
539
|
+
headers: s = {},
|
|
540
|
+
withCredentials: o = !1,
|
|
541
|
+
chunkSize: r = 1024 * 1024,
|
|
541
542
|
// 1MB chunks
|
|
542
|
-
enableChunking:
|
|
543
|
-
onProgress:
|
|
544
|
-
onError:
|
|
545
|
-
onSuccess:
|
|
546
|
-
} =
|
|
543
|
+
enableChunking: l = !0,
|
|
544
|
+
onProgress: d,
|
|
545
|
+
onError: h,
|
|
546
|
+
onSuccess: w
|
|
547
|
+
} = a;
|
|
547
548
|
return {
|
|
548
549
|
name: "media",
|
|
549
550
|
context: {
|
|
@@ -552,35 +553,35 @@ function v(n = {}) {
|
|
|
552
553
|
uploadUrl: e,
|
|
553
554
|
libraryUrl: t,
|
|
554
555
|
maxFileSize: i,
|
|
555
|
-
allowedTypes:
|
|
556
|
+
allowedTypes: n
|
|
556
557
|
}), e || console.warn("[Media] No uploadUrl provided - upload will not work");
|
|
557
558
|
},
|
|
558
559
|
destroy: () => {
|
|
559
560
|
console.log("[Media Plugin] Destroyed");
|
|
560
561
|
},
|
|
561
|
-
onEditorReady: (
|
|
562
|
+
onEditorReady: (c) => {
|
|
562
563
|
console.log("[Media Plugin] Editor ready");
|
|
563
564
|
}
|
|
564
565
|
},
|
|
565
566
|
commands: {
|
|
566
|
-
insertImage: async (
|
|
567
|
-
if (console.log("[Media] Insert image command (not implemented)",
|
|
567
|
+
insertImage: async (c) => {
|
|
568
|
+
if (console.log("[Media] Insert image command (not implemented)", c), !c)
|
|
568
569
|
return console.log("[Media] No file provided - should open picker"), null;
|
|
569
|
-
if (!
|
|
570
|
-
const
|
|
571
|
-
return
|
|
570
|
+
if (!n.includes(c.type)) {
|
|
571
|
+
const p = new Error(`File type ${c.type} not allowed`);
|
|
572
|
+
return h == null || h(p), null;
|
|
572
573
|
}
|
|
573
|
-
if (
|
|
574
|
-
const
|
|
575
|
-
return
|
|
574
|
+
if (c.size > i) {
|
|
575
|
+
const p = new Error(`File size ${c.size} exceeds max ${i}`);
|
|
576
|
+
return h == null || h(p), null;
|
|
576
577
|
}
|
|
577
578
|
return null;
|
|
578
579
|
},
|
|
579
580
|
openMediaLibrary: () => (console.log("[Media] Open media library (not implemented)"), t || console.warn("[Media] No libraryUrl provided"), null),
|
|
580
|
-
uploadMedia: async (
|
|
581
|
-
name:
|
|
582
|
-
size:
|
|
583
|
-
type:
|
|
581
|
+
uploadMedia: async (c) => (console.log("[Media] Upload media (not implemented)", {
|
|
582
|
+
name: c.name,
|
|
583
|
+
size: c.size,
|
|
584
|
+
type: c.type
|
|
584
585
|
}), null)
|
|
585
586
|
},
|
|
586
587
|
toolbar: [
|
|
@@ -599,14 +600,1289 @@ function v(n = {}) {
|
|
|
599
600
|
]
|
|
600
601
|
};
|
|
601
602
|
}
|
|
603
|
+
class m {
|
|
604
|
+
constructor(e, t = { anchor: 0, head: 0 }) {
|
|
605
|
+
this.doc = e, this.selection = t;
|
|
606
|
+
}
|
|
607
|
+
getDocument() {
|
|
608
|
+
return this.doc;
|
|
609
|
+
}
|
|
610
|
+
setDocument(e) {
|
|
611
|
+
return new m(e, this.selection);
|
|
612
|
+
}
|
|
613
|
+
getSelection() {
|
|
614
|
+
return { ...this.selection };
|
|
615
|
+
}
|
|
616
|
+
setSelection(e) {
|
|
617
|
+
return new m(this.doc, e);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Create a new model with updated document and selection
|
|
621
|
+
*/
|
|
622
|
+
update(e, t) {
|
|
623
|
+
return new m(
|
|
624
|
+
e || this.doc,
|
|
625
|
+
t || this.selection
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Get text content
|
|
630
|
+
*/
|
|
631
|
+
getTextContent() {
|
|
632
|
+
return "";
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Check if selection is empty
|
|
636
|
+
*/
|
|
637
|
+
isSelectionEmpty() {
|
|
638
|
+
return this.selection.anchor === this.selection.head;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
class u {
|
|
642
|
+
constructor(e) {
|
|
643
|
+
this.config = {
|
|
644
|
+
closeOnEscape: !0,
|
|
645
|
+
closeOnBackdrop: !0,
|
|
646
|
+
...e
|
|
647
|
+
}, this.element = this.createElement(), this.attachEventListeners();
|
|
648
|
+
}
|
|
649
|
+
createElement() {
|
|
650
|
+
const e = document.createElement("dialog");
|
|
651
|
+
if (e.className = "editora-dialog", this.config.width && (e.style.width = this.config.width), this.config.height && (e.style.height = this.config.height), e.innerHTML = `
|
|
652
|
+
<div class="editora-dialog-container">
|
|
653
|
+
<div class="editora-dialog-header">
|
|
654
|
+
<h3>${this.config.title}</h3>
|
|
655
|
+
<button class="editora-dialog-close" aria-label="Close">×</button>
|
|
656
|
+
</div>
|
|
657
|
+
<div class="editora-dialog-body">
|
|
658
|
+
${typeof this.config.content == "string" ? this.config.content : ""}
|
|
659
|
+
</div>
|
|
660
|
+
<div class="editora-dialog-footer">
|
|
661
|
+
<button type="button" class="editora-btn editora-btn-cancel">Cancel</button>
|
|
662
|
+
<button type="button" class="editora-btn editora-btn-primary">OK</button>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
`, typeof this.config.content != "string") {
|
|
666
|
+
const t = e.querySelector(".editora-dialog-body");
|
|
667
|
+
t && (t.innerHTML = "", t.appendChild(this.config.content));
|
|
668
|
+
}
|
|
669
|
+
return e;
|
|
670
|
+
}
|
|
671
|
+
attachEventListeners() {
|
|
672
|
+
const e = this.element.querySelector(".editora-dialog-close");
|
|
673
|
+
e == null || e.addEventListener("click", () => this.close());
|
|
674
|
+
const t = this.element.querySelector(".editora-btn-cancel");
|
|
675
|
+
t == null || t.addEventListener("click", () => {
|
|
676
|
+
var n, s;
|
|
677
|
+
(s = (n = this.config).onCancel) == null || s.call(n), this.close();
|
|
678
|
+
});
|
|
679
|
+
const i = this.element.querySelector(".editora-btn-primary");
|
|
680
|
+
i == null || i.addEventListener("click", () => this.handleSubmit()), this.config.closeOnEscape && this.element.addEventListener("cancel", (n) => {
|
|
681
|
+
n.preventDefault(), this.close();
|
|
682
|
+
}), this.config.closeOnBackdrop && this.element.addEventListener("click", (n) => {
|
|
683
|
+
const s = this.element.getBoundingClientRect();
|
|
684
|
+
(n.clientX < s.left || n.clientX > s.right || n.clientY < s.top || n.clientY > s.bottom) && this.close();
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
handleSubmit() {
|
|
688
|
+
var t, i, n, s;
|
|
689
|
+
const e = this.element.querySelector("form");
|
|
690
|
+
if (e) {
|
|
691
|
+
const o = new FormData(e);
|
|
692
|
+
(i = (t = this.config).onSubmit) == null || i.call(t, o);
|
|
693
|
+
} else {
|
|
694
|
+
const o = this.element.querySelectorAll("input, select, textarea"), r = new FormData();
|
|
695
|
+
o.forEach((l) => {
|
|
696
|
+
l.name && r.append(l.name, l.value);
|
|
697
|
+
}), (s = (n = this.config).onSubmit) == null || s.call(n, r);
|
|
698
|
+
}
|
|
699
|
+
this.close();
|
|
700
|
+
}
|
|
701
|
+
show() {
|
|
702
|
+
document.body.appendChild(this.element), this.element.showModal();
|
|
703
|
+
}
|
|
704
|
+
close() {
|
|
705
|
+
this.element.close(), this.element.remove();
|
|
706
|
+
}
|
|
707
|
+
destroy() {
|
|
708
|
+
this.close();
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
class D {
|
|
712
|
+
constructor(e) {
|
|
713
|
+
this.isOpen = !1, this.config = e, this.selectedValue = e.value, this.element = this.createElement(), this.attachEventListeners();
|
|
714
|
+
}
|
|
715
|
+
createElement() {
|
|
716
|
+
const e = document.createElement("div");
|
|
717
|
+
e.className = "editora-dropdown", this.config.width && (e.style.width = this.config.width);
|
|
718
|
+
const t = this.config.options.find((n) => n.value === this.selectedValue), i = (t == null ? void 0 : t.label) || this.config.placeholder || "Select...";
|
|
719
|
+
return e.innerHTML = `
|
|
720
|
+
<button class="editora-dropdown-toggle" type="button">
|
|
721
|
+
<span class="editora-dropdown-label">${i}</span>
|
|
722
|
+
<span class="editora-dropdown-arrow">▼</span>
|
|
723
|
+
</button>
|
|
724
|
+
<div class="editora-dropdown-menu" style="display: none;">
|
|
725
|
+
${this.config.options.map((n) => `
|
|
726
|
+
<div class="editora-dropdown-item" data-value="${n.value}">
|
|
727
|
+
${n.icon ? `<span class="editora-dropdown-icon">${n.icon}</span>` : ""}
|
|
728
|
+
<span>${n.label}</span>
|
|
729
|
+
</div>
|
|
730
|
+
`).join("")}
|
|
731
|
+
</div>
|
|
732
|
+
`, e;
|
|
733
|
+
}
|
|
734
|
+
attachEventListeners() {
|
|
735
|
+
const e = this.element.querySelector(".editora-dropdown-toggle"), t = this.element.querySelector(".editora-dropdown-menu");
|
|
736
|
+
e.addEventListener("click", (n) => {
|
|
737
|
+
n.stopPropagation(), this.isOpen = !this.isOpen, t.style.display = this.isOpen ? "block" : "none";
|
|
738
|
+
}), this.element.querySelectorAll(".editora-dropdown-item").forEach((n) => {
|
|
739
|
+
n.addEventListener("click", () => {
|
|
740
|
+
var o, r;
|
|
741
|
+
const s = n.getAttribute("data-value");
|
|
742
|
+
s && (this.setValue(s), (r = (o = this.config).onChange) == null || r.call(o, s), this.close());
|
|
743
|
+
});
|
|
744
|
+
}), document.addEventListener("click", (n) => {
|
|
745
|
+
this.element.contains(n.target) || this.close();
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
setValue(e) {
|
|
749
|
+
this.selectedValue = e;
|
|
750
|
+
const t = this.config.options.find((n) => n.value === e), i = this.element.querySelector(".editora-dropdown-label");
|
|
751
|
+
i && t && (i.textContent = t.label);
|
|
752
|
+
}
|
|
753
|
+
getValue() {
|
|
754
|
+
return this.selectedValue;
|
|
755
|
+
}
|
|
756
|
+
close() {
|
|
757
|
+
this.isOpen = !1;
|
|
758
|
+
const e = this.element.querySelector(".editora-dropdown-menu");
|
|
759
|
+
e && (e.style.display = "none");
|
|
760
|
+
}
|
|
761
|
+
getElement() {
|
|
762
|
+
return this.element;
|
|
763
|
+
}
|
|
764
|
+
destroy() {
|
|
765
|
+
this.element.remove();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
class F {
|
|
769
|
+
constructor(e) {
|
|
770
|
+
this.defaultPresets = [
|
|
771
|
+
"#000000",
|
|
772
|
+
"#434343",
|
|
773
|
+
"#666666",
|
|
774
|
+
"#999999",
|
|
775
|
+
"#B7B7B7",
|
|
776
|
+
"#CCCCCC",
|
|
777
|
+
"#D9D9D9",
|
|
778
|
+
"#EFEFEF",
|
|
779
|
+
"#F3F3F3",
|
|
780
|
+
"#FFFFFF",
|
|
781
|
+
"#980000",
|
|
782
|
+
"#FF0000",
|
|
783
|
+
"#FF9900",
|
|
784
|
+
"#FFFF00",
|
|
785
|
+
"#00FF00",
|
|
786
|
+
"#00FFFF",
|
|
787
|
+
"#4A86E8",
|
|
788
|
+
"#0000FF",
|
|
789
|
+
"#9900FF",
|
|
790
|
+
"#FF00FF",
|
|
791
|
+
"#E6B8AF",
|
|
792
|
+
"#F4CCCC",
|
|
793
|
+
"#FCE5CD",
|
|
794
|
+
"#FFF2CC",
|
|
795
|
+
"#D9EAD3",
|
|
796
|
+
"#D0E0E3",
|
|
797
|
+
"#C9DAF8",
|
|
798
|
+
"#CFE2F3",
|
|
799
|
+
"#D9D2E9",
|
|
800
|
+
"#EAD1DC"
|
|
801
|
+
], this.config = {
|
|
802
|
+
presetColors: this.defaultPresets,
|
|
803
|
+
...e
|
|
804
|
+
}, this.selectedColor = e.value, this.element = this.createElement(), this.attachEventListeners();
|
|
805
|
+
}
|
|
806
|
+
createElement() {
|
|
807
|
+
var t;
|
|
808
|
+
const e = document.createElement("div");
|
|
809
|
+
return e.className = "editora-color-picker", e.innerHTML = `
|
|
810
|
+
<div class="editora-color-picker-input">
|
|
811
|
+
<input type="color" value="${this.selectedColor || "#000000"}" />
|
|
812
|
+
<input type="text" value="${this.selectedColor || "#000000"}" placeholder="#000000" />
|
|
813
|
+
</div>
|
|
814
|
+
<div class="editora-color-picker-presets">
|
|
815
|
+
${(t = this.config.presetColors) == null ? void 0 : t.map((i) => `
|
|
816
|
+
<button
|
|
817
|
+
class="editora-color-preset"
|
|
818
|
+
style="background-color: ${i};"
|
|
819
|
+
data-color="${i}"
|
|
820
|
+
title="${i}"
|
|
821
|
+
></button>
|
|
822
|
+
`).join("")}
|
|
823
|
+
</div>
|
|
824
|
+
`, e;
|
|
825
|
+
}
|
|
826
|
+
attachEventListeners() {
|
|
827
|
+
const e = this.element.querySelector('input[type="color"]'), t = this.element.querySelector('input[type="text"]');
|
|
828
|
+
e.addEventListener("input", (n) => {
|
|
829
|
+
var o, r;
|
|
830
|
+
const s = n.target.value;
|
|
831
|
+
t.value = s, this.selectedColor = s, (r = (o = this.config).onChange) == null || r.call(o, s);
|
|
832
|
+
}), t.addEventListener("input", (n) => {
|
|
833
|
+
var o, r;
|
|
834
|
+
const s = n.target.value;
|
|
835
|
+
/^#[0-9A-Fa-f]{6}$/.test(s) && (e.value = s, this.selectedColor = s, (r = (o = this.config).onChange) == null || r.call(o, s));
|
|
836
|
+
}), this.element.querySelectorAll(".editora-color-preset").forEach((n) => {
|
|
837
|
+
n.addEventListener("click", () => {
|
|
838
|
+
var o, r;
|
|
839
|
+
const s = n.getAttribute("data-color");
|
|
840
|
+
s && (e.value = s, t.value = s, this.selectedColor = s, (r = (o = this.config).onChange) == null || r.call(o, s));
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
getValue() {
|
|
845
|
+
return this.selectedColor;
|
|
846
|
+
}
|
|
847
|
+
setValue(e) {
|
|
848
|
+
this.selectedColor = e;
|
|
849
|
+
const t = this.element.querySelector('input[type="color"]'), i = this.element.querySelector('input[type="text"]');
|
|
850
|
+
t && (t.value = e), i && (i.value = e);
|
|
851
|
+
}
|
|
852
|
+
getElement() {
|
|
853
|
+
return this.element;
|
|
854
|
+
}
|
|
855
|
+
destroy() {
|
|
856
|
+
this.element.remove();
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
class j {
|
|
860
|
+
constructor(e) {
|
|
861
|
+
const { initialUrl: t = "", initialText: i = "", onSubmit: n, onCancel: s } = e;
|
|
862
|
+
this.dialog = new u({
|
|
863
|
+
title: "Insert/Edit Link",
|
|
864
|
+
content: this.createFormHTML(t, i),
|
|
865
|
+
onSubmit: (o) => {
|
|
866
|
+
const r = (o.get("url") || "").trim(), l = (o.get("text") || "").trim(), d = o.get("openInNewTab") === "on";
|
|
867
|
+
if (!r) {
|
|
868
|
+
alert("Please enter a URL");
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
n({ url: r, text: l, openInNewTab: d }), this.dialog.close();
|
|
872
|
+
},
|
|
873
|
+
onCancel: () => {
|
|
874
|
+
s == null || s(), this.dialog.close();
|
|
875
|
+
},
|
|
876
|
+
width: "500px"
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
createFormHTML(e, t) {
|
|
880
|
+
return `
|
|
881
|
+
<form class="link-dialog-form">
|
|
882
|
+
<div class="form-group">
|
|
883
|
+
<label for="link-url">URL</label>
|
|
884
|
+
<input
|
|
885
|
+
type="url"
|
|
886
|
+
id="link-url"
|
|
887
|
+
name="url"
|
|
888
|
+
placeholder="https://example.com"
|
|
889
|
+
value="${this.escapeHtml(e)}"
|
|
890
|
+
required
|
|
891
|
+
autofocus
|
|
892
|
+
/>
|
|
893
|
+
<small>Enter the web address (URL) for the link</small>
|
|
894
|
+
</div>
|
|
895
|
+
|
|
896
|
+
<div class="form-group">
|
|
897
|
+
<label for="link-text">Link Text</label>
|
|
898
|
+
<input
|
|
899
|
+
type="text"
|
|
900
|
+
id="link-text"
|
|
901
|
+
name="text"
|
|
902
|
+
placeholder="Click here"
|
|
903
|
+
value="${this.escapeHtml(t)}"
|
|
904
|
+
/>
|
|
905
|
+
<small>Text to display for the link (leave empty to use URL)</small>
|
|
906
|
+
</div>
|
|
907
|
+
|
|
908
|
+
<div class="form-group">
|
|
909
|
+
<label class="checkbox-label">
|
|
910
|
+
<input
|
|
911
|
+
type="checkbox"
|
|
912
|
+
name="openInNewTab"
|
|
913
|
+
id="link-new-tab"
|
|
914
|
+
/>
|
|
915
|
+
<span>Open link in new tab</span>
|
|
916
|
+
</label>
|
|
917
|
+
</div>
|
|
918
|
+
|
|
919
|
+
<div class="form-actions">
|
|
920
|
+
<button type="button" class="btn btn-secondary" data-action="cancel">
|
|
921
|
+
Cancel
|
|
922
|
+
</button>
|
|
923
|
+
<button type="submit" class="btn btn-primary">
|
|
924
|
+
Insert Link
|
|
925
|
+
</button>
|
|
926
|
+
</div>
|
|
927
|
+
</form>
|
|
928
|
+
`;
|
|
929
|
+
}
|
|
930
|
+
escapeHtml(e) {
|
|
931
|
+
const t = document.createElement("div");
|
|
932
|
+
return t.textContent = e, t.innerHTML;
|
|
933
|
+
}
|
|
934
|
+
show() {
|
|
935
|
+
this.dialog.show();
|
|
936
|
+
}
|
|
937
|
+
close() {
|
|
938
|
+
this.dialog.close();
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
class q {
|
|
942
|
+
constructor(e) {
|
|
943
|
+
const { onSubmit: t, onCancel: i } = e;
|
|
944
|
+
this.dialog = new u({
|
|
945
|
+
title: "Insert Table",
|
|
946
|
+
content: this.createFormHTML(),
|
|
947
|
+
onSubmit: (n) => {
|
|
948
|
+
const s = parseInt(n.get("rows"), 10), o = parseInt(n.get("cols"), 10), r = n.get("headerRow") === "on";
|
|
949
|
+
if (s < 1 || s > 100) {
|
|
950
|
+
alert("Please enter a valid number of rows (1-100)");
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (o < 1 || o > 20) {
|
|
954
|
+
alert("Please enter a valid number of columns (1-20)");
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
t({ rows: s, cols: o, headerRow: r }), this.dialog.close();
|
|
958
|
+
},
|
|
959
|
+
onCancel: () => {
|
|
960
|
+
i == null || i(), this.dialog.close();
|
|
961
|
+
},
|
|
962
|
+
width: "400px"
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
createFormHTML() {
|
|
966
|
+
return `
|
|
967
|
+
<form class="table-dialog-form">
|
|
968
|
+
<div class="form-group">
|
|
969
|
+
<label for="table-rows">Rows</label>
|
|
970
|
+
<input
|
|
971
|
+
type="number"
|
|
972
|
+
id="table-rows"
|
|
973
|
+
name="rows"
|
|
974
|
+
min="1"
|
|
975
|
+
max="100"
|
|
976
|
+
value="3"
|
|
977
|
+
required
|
|
978
|
+
autofocus
|
|
979
|
+
/>
|
|
980
|
+
<small>Number of rows (1-100)</small>
|
|
981
|
+
</div>
|
|
982
|
+
|
|
983
|
+
<div class="form-group">
|
|
984
|
+
<label for="table-cols">Columns</label>
|
|
985
|
+
<input
|
|
986
|
+
type="number"
|
|
987
|
+
id="table-cols"
|
|
988
|
+
name="cols"
|
|
989
|
+
min="1"
|
|
990
|
+
max="20"
|
|
991
|
+
value="3"
|
|
992
|
+
required
|
|
993
|
+
/>
|
|
994
|
+
<small>Number of columns (1-20)</small>
|
|
995
|
+
</div>
|
|
996
|
+
|
|
997
|
+
<div class="form-group">
|
|
998
|
+
<label class="checkbox-label">
|
|
999
|
+
<input
|
|
1000
|
+
type="checkbox"
|
|
1001
|
+
name="headerRow"
|
|
1002
|
+
id="table-header"
|
|
1003
|
+
checked
|
|
1004
|
+
/>
|
|
1005
|
+
<span>Include header row</span>
|
|
1006
|
+
</label>
|
|
1007
|
+
</div>
|
|
1008
|
+
|
|
1009
|
+
<div class="table-preview">
|
|
1010
|
+
<strong>Preview:</strong>
|
|
1011
|
+
<div id="table-preview-content" style="margin-top: 8px; font-size: 12px; color: #666;">
|
|
1012
|
+
3 rows × 3 columns with header
|
|
1013
|
+
</div>
|
|
1014
|
+
</div>
|
|
1015
|
+
|
|
1016
|
+
<div class="form-actions">
|
|
1017
|
+
<button type="button" class="btn btn-secondary" data-action="cancel">
|
|
1018
|
+
Cancel
|
|
1019
|
+
</button>
|
|
1020
|
+
<button type="submit" class="btn btn-primary">
|
|
1021
|
+
Insert Table
|
|
1022
|
+
</button>
|
|
1023
|
+
</div>
|
|
1024
|
+
</form>
|
|
1025
|
+
`;
|
|
1026
|
+
}
|
|
1027
|
+
show() {
|
|
1028
|
+
this.dialog.show(), this.attachPreviewListeners();
|
|
1029
|
+
}
|
|
1030
|
+
close() {
|
|
1031
|
+
this.dialog.close();
|
|
1032
|
+
}
|
|
1033
|
+
attachPreviewListeners() {
|
|
1034
|
+
const e = document.querySelector(".table-dialog-form");
|
|
1035
|
+
if (!e) return;
|
|
1036
|
+
const t = e.querySelector("#table-rows"), i = e.querySelector("#table-cols"), n = e.querySelector("#table-header"), s = e.querySelector("#table-preview-content"), o = () => {
|
|
1037
|
+
const r = parseInt((t == null ? void 0 : t.value) || "3", 10), l = parseInt((i == null ? void 0 : i.value) || "3", 10), d = n == null ? void 0 : n.checked;
|
|
1038
|
+
s && (s.textContent = `${r} rows × ${l} columns${d ? " with header" : ""}`);
|
|
1039
|
+
};
|
|
1040
|
+
t == null || t.addEventListener("input", o), i == null || i.addEventListener("input", o), n == null || n.addEventListener("change", o);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
class A {
|
|
1044
|
+
constructor(e) {
|
|
1045
|
+
this.config = e;
|
|
1046
|
+
const { onSubmit: t, onCancel: i } = e;
|
|
1047
|
+
this.dialog = new u({
|
|
1048
|
+
title: "Insert Image",
|
|
1049
|
+
content: this.createFormHTML(),
|
|
1050
|
+
onSubmit: (n) => {
|
|
1051
|
+
const s = (n.get("src") || "").trim(), o = (n.get("alt") || "").trim(), r = (n.get("width") || "").trim(), l = (n.get("height") || "").trim();
|
|
1052
|
+
if (!s) {
|
|
1053
|
+
alert("Please enter an image URL or upload an image");
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
t({
|
|
1057
|
+
src: s,
|
|
1058
|
+
alt: o,
|
|
1059
|
+
width: r || void 0,
|
|
1060
|
+
height: l || void 0
|
|
1061
|
+
}), this.dialog.close();
|
|
1062
|
+
},
|
|
1063
|
+
onCancel: () => {
|
|
1064
|
+
i == null || i(), this.dialog.close();
|
|
1065
|
+
},
|
|
1066
|
+
width: "500px"
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
createFormHTML() {
|
|
1070
|
+
return `
|
|
1071
|
+
<form class="image-dialog-form">
|
|
1072
|
+
${this.config.allowUpload ? `
|
|
1073
|
+
<div class="form-group">
|
|
1074
|
+
<label for="image-upload">Upload Image</label>
|
|
1075
|
+
<input
|
|
1076
|
+
type="file"
|
|
1077
|
+
id="image-upload"
|
|
1078
|
+
accept="image/*"
|
|
1079
|
+
/>
|
|
1080
|
+
<small>Or upload an image from your computer</small>
|
|
1081
|
+
</div>
|
|
1082
|
+
<div class="form-divider">OR</div>
|
|
1083
|
+
` : ""}
|
|
1084
|
+
|
|
1085
|
+
<div class="form-group">
|
|
1086
|
+
<label for="image-src">Image URL</label>
|
|
1087
|
+
<input
|
|
1088
|
+
type="url"
|
|
1089
|
+
id="image-src"
|
|
1090
|
+
name="src"
|
|
1091
|
+
placeholder="https://example.com/image.jpg"
|
|
1092
|
+
${this.config.allowUpload ? "" : "required autofocus"}
|
|
1093
|
+
/>
|
|
1094
|
+
<small>Enter the web address (URL) of the image</small>
|
|
1095
|
+
</div>
|
|
1096
|
+
|
|
1097
|
+
<div class="form-group">
|
|
1098
|
+
<label for="image-alt">Alt Text</label>
|
|
1099
|
+
<input
|
|
1100
|
+
type="text"
|
|
1101
|
+
id="image-alt"
|
|
1102
|
+
name="alt"
|
|
1103
|
+
placeholder="Description of the image"
|
|
1104
|
+
/>
|
|
1105
|
+
<small>Alternative text for accessibility (recommended)</small>
|
|
1106
|
+
</div>
|
|
1107
|
+
|
|
1108
|
+
<div class="form-row">
|
|
1109
|
+
<div class="form-group">
|
|
1110
|
+
<label for="image-width">Width</label>
|
|
1111
|
+
<input
|
|
1112
|
+
type="text"
|
|
1113
|
+
id="image-width"
|
|
1114
|
+
name="width"
|
|
1115
|
+
placeholder="auto"
|
|
1116
|
+
/>
|
|
1117
|
+
</div>
|
|
1118
|
+
|
|
1119
|
+
<div class="form-group">
|
|
1120
|
+
<label for="image-height">Height</label>
|
|
1121
|
+
<input
|
|
1122
|
+
type="text"
|
|
1123
|
+
id="image-height"
|
|
1124
|
+
name="height"
|
|
1125
|
+
placeholder="auto"
|
|
1126
|
+
/>
|
|
1127
|
+
</div>
|
|
1128
|
+
</div>
|
|
1129
|
+
|
|
1130
|
+
<div class="image-preview" id="image-preview" style="display: none;">
|
|
1131
|
+
<strong>Preview:</strong>
|
|
1132
|
+
<img id="preview-img" style="max-width: 100%; max-height: 200px; margin-top: 8px;" />
|
|
1133
|
+
</div>
|
|
1134
|
+
|
|
1135
|
+
<div class="form-actions">
|
|
1136
|
+
<button type="button" class="btn btn-secondary" data-action="cancel">
|
|
1137
|
+
Cancel
|
|
1138
|
+
</button>
|
|
1139
|
+
<button type="submit" class="btn btn-primary">
|
|
1140
|
+
Insert Image
|
|
1141
|
+
</button>
|
|
1142
|
+
</div>
|
|
1143
|
+
</form>
|
|
1144
|
+
`;
|
|
1145
|
+
}
|
|
1146
|
+
show() {
|
|
1147
|
+
this.dialog.show(), this.attachImagePreview(), this.attachFileUpload();
|
|
1148
|
+
}
|
|
1149
|
+
close() {
|
|
1150
|
+
this.dialog.close();
|
|
1151
|
+
}
|
|
1152
|
+
attachImagePreview() {
|
|
1153
|
+
const e = document.querySelector(".image-dialog-form");
|
|
1154
|
+
if (!e) return;
|
|
1155
|
+
const t = e.querySelector("#image-src"), i = e.querySelector("#image-preview"), n = e.querySelector("#preview-img");
|
|
1156
|
+
t == null || t.addEventListener("input", () => {
|
|
1157
|
+
const s = t.value.trim();
|
|
1158
|
+
s && this.isValidImageUrl(s) ? (n.src = s, i.style.display = "block", n.onerror = () => {
|
|
1159
|
+
i.style.display = "none";
|
|
1160
|
+
}) : i.style.display = "none";
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
attachFileUpload() {
|
|
1164
|
+
if (!this.config.allowUpload) return;
|
|
1165
|
+
const e = document.querySelector(".image-dialog-form");
|
|
1166
|
+
if (!e) return;
|
|
1167
|
+
const t = e.querySelector("#image-upload"), i = e.querySelector("#image-src");
|
|
1168
|
+
t == null || t.addEventListener("change", async (n) => {
|
|
1169
|
+
var r;
|
|
1170
|
+
const s = (r = n.target.files) == null ? void 0 : r[0];
|
|
1171
|
+
if (!s) return;
|
|
1172
|
+
const o = new FileReader();
|
|
1173
|
+
o.onload = (l) => {
|
|
1174
|
+
var h;
|
|
1175
|
+
const d = (h = l.target) == null ? void 0 : h.result;
|
|
1176
|
+
i.value = d, i.dispatchEvent(new Event("input"));
|
|
1177
|
+
}, o.readAsDataURL(s);
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
isValidImageUrl(e) {
|
|
1181
|
+
try {
|
|
1182
|
+
return new URL(e), /\.(jpg|jpeg|png|gif|svg|webp|bmp)$/i.test(e) || e.startsWith("data:image/");
|
|
1183
|
+
} catch (t) {
|
|
1184
|
+
return !1;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
class P {
|
|
1189
|
+
constructor(e) {
|
|
1190
|
+
this.onSubmit = e.onSubmit;
|
|
1191
|
+
const t = this.createContent();
|
|
1192
|
+
this.dialog = new u({
|
|
1193
|
+
title: "Insert Math Equation",
|
|
1194
|
+
content: t,
|
|
1195
|
+
buttons: [
|
|
1196
|
+
{
|
|
1197
|
+
label: "Cancel",
|
|
1198
|
+
onClick: () => this.dialog.close()
|
|
1199
|
+
},
|
|
1200
|
+
{
|
|
1201
|
+
label: "Insert",
|
|
1202
|
+
onClick: () => this.handleSubmit(),
|
|
1203
|
+
primary: !0
|
|
1204
|
+
}
|
|
1205
|
+
]
|
|
1206
|
+
}), this.latexInput = t.querySelector("#math-latex"), this.previewDiv = t.querySelector("#math-preview"), this.displayTypeSelect = t.querySelector("#math-display-type"), this.latexInput.addEventListener("input", () => this.updatePreview());
|
|
1207
|
+
}
|
|
1208
|
+
createContent() {
|
|
1209
|
+
const e = document.createElement("div");
|
|
1210
|
+
return e.className = "math-dialog-content", e.style.minWidth = "500px", e.innerHTML = `
|
|
1211
|
+
<style>
|
|
1212
|
+
.math-dialog-content {
|
|
1213
|
+
display: flex;
|
|
1214
|
+
flex-direction: column;
|
|
1215
|
+
gap: 1rem;
|
|
1216
|
+
}
|
|
1217
|
+
.math-form-group {
|
|
1218
|
+
display: flex;
|
|
1219
|
+
flex-direction: column;
|
|
1220
|
+
gap: 0.5rem;
|
|
1221
|
+
}
|
|
1222
|
+
.math-form-group label {
|
|
1223
|
+
font-weight: 500;
|
|
1224
|
+
color: #333;
|
|
1225
|
+
}
|
|
1226
|
+
.math-latex-input {
|
|
1227
|
+
width: 100%;
|
|
1228
|
+
min-height: 80px;
|
|
1229
|
+
padding: 0.5rem;
|
|
1230
|
+
border: 1px solid #ddd;
|
|
1231
|
+
border-radius: 4px;
|
|
1232
|
+
font-family: 'Courier New', monospace;
|
|
1233
|
+
font-size: 14px;
|
|
1234
|
+
resize: vertical;
|
|
1235
|
+
}
|
|
1236
|
+
.math-preview {
|
|
1237
|
+
padding: 1rem;
|
|
1238
|
+
background: #f9f9f9;
|
|
1239
|
+
border: 1px solid #ddd;
|
|
1240
|
+
border-radius: 4px;
|
|
1241
|
+
min-height: 60px;
|
|
1242
|
+
font-size: 18px;
|
|
1243
|
+
text-align: center;
|
|
1244
|
+
}
|
|
1245
|
+
.math-preview:empty::before {
|
|
1246
|
+
content: 'Preview will appear here...';
|
|
1247
|
+
color: #999;
|
|
1248
|
+
font-style: italic;
|
|
1249
|
+
}
|
|
1250
|
+
.math-symbols {
|
|
1251
|
+
display: grid;
|
|
1252
|
+
grid-template-columns: repeat(8, 1fr);
|
|
1253
|
+
gap: 0.5rem;
|
|
1254
|
+
padding: 0.5rem;
|
|
1255
|
+
background: #f5f5f5;
|
|
1256
|
+
border-radius: 4px;
|
|
1257
|
+
}
|
|
1258
|
+
.math-symbol-btn {
|
|
1259
|
+
padding: 0.5rem;
|
|
1260
|
+
border: 1px solid #ddd;
|
|
1261
|
+
background: white;
|
|
1262
|
+
border-radius: 4px;
|
|
1263
|
+
cursor: pointer;
|
|
1264
|
+
font-size: 18px;
|
|
1265
|
+
transition: all 0.2s;
|
|
1266
|
+
}
|
|
1267
|
+
.math-symbol-btn:hover {
|
|
1268
|
+
background: #e3f2fd;
|
|
1269
|
+
border-color: #2196f3;
|
|
1270
|
+
}
|
|
1271
|
+
.math-display-select {
|
|
1272
|
+
padding: 0.5rem;
|
|
1273
|
+
border: 1px solid #ddd;
|
|
1274
|
+
border-radius: 4px;
|
|
1275
|
+
font-size: 14px;
|
|
1276
|
+
}
|
|
1277
|
+
</style>
|
|
1278
|
+
|
|
1279
|
+
<div class="math-form-group">
|
|
1280
|
+
<label for="math-latex">LaTeX Expression:</label>
|
|
1281
|
+
<textarea
|
|
1282
|
+
id="math-latex"
|
|
1283
|
+
class="math-latex-input"
|
|
1284
|
+
placeholder="Enter LaTeX (e.g., x^2 + y^2 = r^2)"
|
|
1285
|
+
></textarea>
|
|
1286
|
+
</div>
|
|
1287
|
+
|
|
1288
|
+
<div class="math-form-group">
|
|
1289
|
+
<label>Common Symbols:</label>
|
|
1290
|
+
<div class="math-symbols">
|
|
1291
|
+
<button class="math-symbol-btn" data-symbol="\\frac{a}{b}" title="Fraction">a/b</button>
|
|
1292
|
+
<button class="math-symbol-btn" data-symbol="x^{2}" title="Superscript">x²</button>
|
|
1293
|
+
<button class="math-symbol-btn" data-symbol="x_{i}" title="Subscript">xᵢ</button>
|
|
1294
|
+
<button class="math-symbol-btn" data-symbol="\\sqrt{x}" title="Square root">√x</button>
|
|
1295
|
+
<button class="math-symbol-btn" data-symbol="\\sum" title="Sum">∑</button>
|
|
1296
|
+
<button class="math-symbol-btn" data-symbol="\\int" title="Integral">∫</button>
|
|
1297
|
+
<button class="math-symbol-btn" data-symbol="\\pi" title="Pi">π</button>
|
|
1298
|
+
<button class="math-symbol-btn" data-symbol="\\alpha" title="Alpha">α</button>
|
|
1299
|
+
<button class="math-symbol-btn" data-symbol="\\beta" title="Beta">β</button>
|
|
1300
|
+
<button class="math-symbol-btn" data-symbol="\\gamma" title="Gamma">γ</button>
|
|
1301
|
+
<button class="math-symbol-btn" data-symbol="\\theta" title="Theta">θ</button>
|
|
1302
|
+
<button class="math-symbol-btn" data-symbol="\\lambda" title="Lambda">λ</button>
|
|
1303
|
+
<button class="math-symbol-btn" data-symbol="\\infty" title="Infinity">∞</button>
|
|
1304
|
+
<button class="math-symbol-btn" data-symbol="\\leq" title="Less or equal">≤</button>
|
|
1305
|
+
<button class="math-symbol-btn" data-symbol="\\geq" title="Greater or equal">≥</button>
|
|
1306
|
+
<button class="math-symbol-btn" data-symbol="\\neq" title="Not equal">≠</button>
|
|
1307
|
+
</div>
|
|
1308
|
+
</div>
|
|
1309
|
+
|
|
1310
|
+
<div class="math-form-group">
|
|
1311
|
+
<label for="math-preview">Preview:</label>
|
|
1312
|
+
<div id="math-preview" class="math-preview"></div>
|
|
1313
|
+
</div>
|
|
1314
|
+
|
|
1315
|
+
<div class="math-form-group">
|
|
1316
|
+
<label for="math-display-type">Display Type:</label>
|
|
1317
|
+
<select id="math-display-type" class="math-display-select">
|
|
1318
|
+
<option value="inline">Inline (within text)</option>
|
|
1319
|
+
<option value="block">Block (centered, new line)</option>
|
|
1320
|
+
</select>
|
|
1321
|
+
</div>
|
|
1322
|
+
`, e.querySelectorAll(".math-symbol-btn").forEach((i) => {
|
|
1323
|
+
i.addEventListener("click", (n) => {
|
|
1324
|
+
n.preventDefault();
|
|
1325
|
+
const s = i.getAttribute("data-symbol");
|
|
1326
|
+
if (s && this.latexInput) {
|
|
1327
|
+
const o = this.latexInput.selectionStart, r = this.latexInput.selectionEnd, l = this.latexInput.value;
|
|
1328
|
+
this.latexInput.value = l.substring(0, o) + s + l.substring(r), this.latexInput.focus(), this.latexInput.selectionStart = this.latexInput.selectionEnd = o + s.length, this.updatePreview();
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
}), e;
|
|
1332
|
+
}
|
|
1333
|
+
updatePreview() {
|
|
1334
|
+
const e = this.latexInput.value.trim();
|
|
1335
|
+
if (!e) {
|
|
1336
|
+
this.previewDiv.textContent = "";
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
let t = e.replace(/\\frac\{([^}]+)\}\{([^}]+)\}/g, "($1)/($2)").replace(/\^\{([^}]+)\}/g, "^$1").replace(/\_\{([^}]+)\}/g, "_$1").replace(/\\sqrt\{([^}]+)\}/g, "√($1)").replace(/\\sum/g, "∑").replace(/\\int/g, "∫").replace(/\\pi/g, "π").replace(/\\alpha/g, "α").replace(/\\beta/g, "β").replace(/\\gamma/g, "γ").replace(/\\theta/g, "θ").replace(/\\lambda/g, "λ").replace(/\\infty/g, "∞").replace(/\\leq/g, "≤").replace(/\\geq/g, "≥").replace(/\\neq/g, "≠");
|
|
1340
|
+
this.previewDiv.textContent = t;
|
|
1341
|
+
}
|
|
1342
|
+
handleSubmit() {
|
|
1343
|
+
const e = this.latexInput.value.trim();
|
|
1344
|
+
if (!e) {
|
|
1345
|
+
alert("Please enter a LaTeX expression.");
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
const t = this.displayTypeSelect.value;
|
|
1349
|
+
this.onSubmit({ latex: e, display: t }), this.dialog.close();
|
|
1350
|
+
}
|
|
1351
|
+
show() {
|
|
1352
|
+
this.dialog.show(), this.latexInput.value = "", this.previewDiv.textContent = "", this.displayTypeSelect.value = "inline", setTimeout(() => this.latexInput.focus(), 100);
|
|
1353
|
+
}
|
|
1354
|
+
close() {
|
|
1355
|
+
this.dialog.close();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
class $ {
|
|
1359
|
+
constructor(e) {
|
|
1360
|
+
this.characters = {
|
|
1361
|
+
common: ["©", "®", "™", "§", "¶", "†", "‡", "•", "‣", "⁃", "◦", "▪", "▫", "◊", "○", "●", "□", "■", "△", "▲", "▽", "▼", "◇", "◆", "★", "☆"],
|
|
1362
|
+
arrows: ["←", "→", "↑", "↓", "↔", "↕", "⇐", "⇒", "⇑", "⇓", "⇔", "⇕", "⟵", "⟶", "⟷", "↖", "↗", "↘", "↙", "⇖", "⇗", "⇘", "⇙"],
|
|
1363
|
+
currency: ["$", "€", "£", "¥", "₹", "₽", "₩", "₪", "₦", "฿", "₴", "₡", "₵", "₸", "₫", "₱", "₲", "₳", "₭"],
|
|
1364
|
+
math: ["±", "×", "÷", "=", "≠", "≈", "≡", "≤", "≥", "<", ">", "∞", "∑", "∏", "∫", "∂", "√", "∛", "∜", "°", "′", "″", "∠", "∟", "⊥", "∥"],
|
|
1365
|
+
greek: ["α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ"],
|
|
1366
|
+
punctuation: ["‚", "„", "“", "”", "‘", "’", "«", "»", "‹", "›", "—", "–", "…", "·", "¡", "¿", "‰", "′", "″", "‴"],
|
|
1367
|
+
superscript: ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", "⁺", "⁻", "⁼", "⁽", "⁾", "ⁿ", "ⁱ"],
|
|
1368
|
+
subscript: ["₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", "₊", "₋", "₌", "₍", "₎"]
|
|
1369
|
+
}, this.onSelect = e.onSelect;
|
|
1370
|
+
const t = this.createContent();
|
|
1371
|
+
this.dialog = new u({
|
|
1372
|
+
title: "Insert Special Character",
|
|
1373
|
+
content: t,
|
|
1374
|
+
buttons: [
|
|
1375
|
+
{
|
|
1376
|
+
label: "Close",
|
|
1377
|
+
onClick: () => this.dialog.close()
|
|
1378
|
+
}
|
|
1379
|
+
]
|
|
1380
|
+
}), this.searchInput = t.querySelector("#char-search"), this.categorySelect = t.querySelector("#char-category"), this.charactersGrid = t.querySelector("#char-grid"), this.searchInput.addEventListener("input", () => this.filterCharacters()), this.categorySelect.addEventListener("change", () => this.updateGrid()), this.updateGrid();
|
|
1381
|
+
}
|
|
1382
|
+
createContent() {
|
|
1383
|
+
const e = document.createElement("div");
|
|
1384
|
+
return e.className = "character-dialog-content", e.style.minWidth = "500px", e.innerHTML = `
|
|
1385
|
+
<style>
|
|
1386
|
+
.character-dialog-content {
|
|
1387
|
+
display: flex;
|
|
1388
|
+
flex-direction: column;
|
|
1389
|
+
gap: 1rem;
|
|
1390
|
+
}
|
|
1391
|
+
.char-controls {
|
|
1392
|
+
display: flex;
|
|
1393
|
+
gap: 0.5rem;
|
|
1394
|
+
}
|
|
1395
|
+
.char-search, .char-category {
|
|
1396
|
+
padding: 0.5rem;
|
|
1397
|
+
border: 1px solid #ddd;
|
|
1398
|
+
border-radius: 4px;
|
|
1399
|
+
font-size: 14px;
|
|
1400
|
+
}
|
|
1401
|
+
.char-search {
|
|
1402
|
+
flex: 1;
|
|
1403
|
+
}
|
|
1404
|
+
.char-category {
|
|
1405
|
+
min-width: 150px;
|
|
1406
|
+
}
|
|
1407
|
+
.char-grid {
|
|
1408
|
+
display: grid;
|
|
1409
|
+
grid-template-columns: repeat(10, 1fr);
|
|
1410
|
+
gap: 0.5rem;
|
|
1411
|
+
max-height: 300px;
|
|
1412
|
+
overflow-y: auto;
|
|
1413
|
+
padding: 1rem;
|
|
1414
|
+
background: #f9f9f9;
|
|
1415
|
+
border: 1px solid #ddd;
|
|
1416
|
+
border-radius: 4px;
|
|
1417
|
+
}
|
|
1418
|
+
.char-button {
|
|
1419
|
+
aspect-ratio: 1;
|
|
1420
|
+
display: flex;
|
|
1421
|
+
align-items: center;
|
|
1422
|
+
justify-content: center;
|
|
1423
|
+
padding: 0.5rem;
|
|
1424
|
+
border: 1px solid #ddd;
|
|
1425
|
+
background: white;
|
|
1426
|
+
border-radius: 4px;
|
|
1427
|
+
cursor: pointer;
|
|
1428
|
+
font-size: 20px;
|
|
1429
|
+
transition: all 0.2s;
|
|
1430
|
+
}
|
|
1431
|
+
.char-button:hover {
|
|
1432
|
+
background: #e3f2fd;
|
|
1433
|
+
border-color: #2196f3;
|
|
1434
|
+
transform: scale(1.1);
|
|
1435
|
+
}
|
|
1436
|
+
.char-info {
|
|
1437
|
+
padding: 0.5rem;
|
|
1438
|
+
background: #f5f5f5;
|
|
1439
|
+
border-radius: 4px;
|
|
1440
|
+
font-size: 12px;
|
|
1441
|
+
color: #666;
|
|
1442
|
+
text-align: center;
|
|
1443
|
+
}
|
|
1444
|
+
</style>
|
|
1445
|
+
|
|
1446
|
+
<div class="char-controls">
|
|
1447
|
+
<input
|
|
1448
|
+
type="text"
|
|
1449
|
+
id="char-search"
|
|
1450
|
+
class="char-search"
|
|
1451
|
+
placeholder="Search characters..."
|
|
1452
|
+
/>
|
|
1453
|
+
<select id="char-category" class="char-category">
|
|
1454
|
+
<option value="all">All Categories</option>
|
|
1455
|
+
<option value="common">Common Symbols</option>
|
|
1456
|
+
<option value="arrows">Arrows</option>
|
|
1457
|
+
<option value="currency">Currency</option>
|
|
1458
|
+
<option value="math">Mathematics</option>
|
|
1459
|
+
<option value="greek">Greek Letters</option>
|
|
1460
|
+
<option value="punctuation">Punctuation</option>
|
|
1461
|
+
<option value="superscript">Superscript</option>
|
|
1462
|
+
<option value="subscript">Subscript</option>
|
|
1463
|
+
</select>
|
|
1464
|
+
</div>
|
|
1465
|
+
|
|
1466
|
+
<div id="char-grid" class="char-grid"></div>
|
|
1467
|
+
|
|
1468
|
+
<div class="char-info">
|
|
1469
|
+
Click any character to insert it into your document.
|
|
1470
|
+
</div>
|
|
1471
|
+
`, e;
|
|
1472
|
+
}
|
|
1473
|
+
updateGrid() {
|
|
1474
|
+
const e = this.categorySelect.value;
|
|
1475
|
+
this.charactersGrid.innerHTML = "";
|
|
1476
|
+
let t = [];
|
|
1477
|
+
e === "all" ? t = Object.values(this.characters).flat() : t = this.characters[e] || [], t.forEach((i) => {
|
|
1478
|
+
const n = document.createElement("button");
|
|
1479
|
+
n.className = "char-button", n.textContent = i, n.title = `Insert ${i} (U+${i.charCodeAt(0).toString(16).toUpperCase()})`, n.addEventListener("click", (s) => {
|
|
1480
|
+
s.preventDefault(), this.handleSelect(i);
|
|
1481
|
+
}), this.charactersGrid.appendChild(n);
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
filterCharacters() {
|
|
1485
|
+
const e = this.searchInput.value.toLowerCase();
|
|
1486
|
+
this.charactersGrid.querySelectorAll(".char-button").forEach((i) => {
|
|
1487
|
+
const n = i.textContent || "", s = `u+${n.charCodeAt(0).toString(16)}`, o = !e || n.includes(e) || s.includes(e);
|
|
1488
|
+
i.style.display = o ? "flex" : "none";
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
handleSelect(e) {
|
|
1492
|
+
this.onSelect(e), this.dialog.close();
|
|
1493
|
+
}
|
|
1494
|
+
show() {
|
|
1495
|
+
this.dialog.show(), this.searchInput.value = "", this.categorySelect.value = "all", this.updateGrid(), setTimeout(() => this.searchInput.focus(), 100);
|
|
1496
|
+
}
|
|
1497
|
+
close() {
|
|
1498
|
+
this.dialog.close();
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
class H {
|
|
1502
|
+
constructor(e) {
|
|
1503
|
+
this.emojis = {
|
|
1504
|
+
popular: ["😀", "😃", "😄", "😁", "😊", "😍", "🤩", "😘", "😗", "😚", "😙", "🙂", "🤗", "🤔", "🤨", "😐", "😑", "😶", "🙄", "😏", "👍", "👎", "👌", "✌️", "🤞", "🤝", "👏", "🙌", "👋", "🤚", "✋", "🖐️", "🖖", "👊", "✊", "🤛", "🤜", "💪"],
|
|
1505
|
+
smileys: ["😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "😉", "😊", "😇", "🥰", "😍", "🤩", "😘", "😗", "😚", "😙", "😋", "😛", "😜", "🤪", "😝", "🤑", "🤗", "🤭", "🤫", "🤔"],
|
|
1506
|
+
emotions: ["😳", "😞", "😟", "😠", "😡", "🤬", "😔", "😕", "🙁", "😬", "🥺", "😣", "😖", "😫", "😩", "🥱", "😤", "😮", "😱", "😨", "😰", "😥", "😢", "😭", "😪", "😓", "🤤", "😴", "😷", "🤒"],
|
|
1507
|
+
gestures: ["👍", "👎", "👌", "✌️", "🤞", "🤟", "🤘", "🤙", "👈", "👉", "👆", "👇", "☝️", "👋", "🤚", "✋", "🖐️", "🖖", "👏", "🙌", "👐", "🤲", "🤝", "🙏", "✍️", "💅", "🤳", "💪", "🦾", "🦿"],
|
|
1508
|
+
people: ["👶", "👧", "🧒", "👦", "👩", "🧑", "👨", "👵", "🧓", "👴", "👲", "👳", "🧕", "👮", "👷", "💂", "🕵️", "👩⚕️", "👨⚕️", "👩🎓", "👨🎓", "👩🏫", "👨🏫", "👩⚖️", "👨⚖️", "👩🌾", "👨🌾", "👩🍳", "👨🍳", "👩🔧"],
|
|
1509
|
+
animals: ["🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", "🐷", "🐸", "🐵", "🙈", "🙉", "🙊", "🐔", "🐧", "🐦", "🐤", "🐣", "🐥", "🦆", "🦅", "🦉", "🦇", "🐺", "🐗"],
|
|
1510
|
+
nature: ["🌵", "🎄", "🌲", "🌳", "🌴", "🌱", "🌿", "☘️", "🍀", "🎍", "🎋", "🍃", "🍂", "🍁", "🍄", "🌾", "💐", "🌷", "🌹", "🥀", "🌺", "🌸", "🌼", "🌻", "🌞", "🌝", "🌛", "🌜", "⭐", "🌟"],
|
|
1511
|
+
food: ["🍏", "🍎", "🍐", "🍊", "🍋", "🍌", "🍉", "🍇", "🍓", "🍈", "🍒", "🍑", "🥭", "🍍", "🥥", "🥝", "🍅", "🍆", "🥑", "🥦", "🥬", "🥒", "🌶️", "🌽", "🥕", "🧄", "🧅", "🥔", "🍠", "🥐"],
|
|
1512
|
+
objects: ["⌚", "📱", "💻", "⌨️", "🖥️", "🖨️", "🖱️", "🕹️", "💾", "💿", "📀", "📷", "📹", "🎥", "📞", "☎️", "📟", "📠", "📺", "📻", "🎙️", "🎚️", "🎛️", "🧭", "⏱️", "⏰", "📡", "🔋", "🔌", "💡"],
|
|
1513
|
+
symbols: ["❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎", "💔", "❣️", "💕", "💞", "💓", "💗", "💖", "💘", "💝", "✨", "💫", "⭐", "🌟", "✔️", "✅", "❌", "❎", "➕", "➖", "✖️", "➗"],
|
|
1514
|
+
activities: ["⚽", "🏀", "🏈", "⚾", "🥎", "🎾", "🏐", "🏉", "🥏", "🎱", "🪀", "🏓", "🏸", "🏒", "🏑", "🥍", "🏏", "🪃", "🥅", "⛳", "🪁", "🏹", "🎣", "🤿", "🥊", "🥋", "🎽", "🛹", "🛼", "🛷"]
|
|
1515
|
+
}, this.onSelect = e.onSelect;
|
|
1516
|
+
const t = this.createContent();
|
|
1517
|
+
this.dialog = new u({
|
|
1518
|
+
title: "Insert Emoji",
|
|
1519
|
+
content: t,
|
|
1520
|
+
buttons: [
|
|
1521
|
+
{
|
|
1522
|
+
label: "Close",
|
|
1523
|
+
onClick: () => this.dialog.close()
|
|
1524
|
+
}
|
|
1525
|
+
]
|
|
1526
|
+
}), this.searchInput = t.querySelector("#emoji-search"), this.categorySelect = t.querySelector("#emoji-category"), this.emojisGrid = t.querySelector("#emoji-grid"), this.searchInput.addEventListener("input", () => this.filterEmojis()), this.categorySelect.addEventListener("change", () => this.updateGrid()), this.updateGrid();
|
|
1527
|
+
}
|
|
1528
|
+
createContent() {
|
|
1529
|
+
const e = document.createElement("div");
|
|
1530
|
+
return e.className = "emoji-dialog-content", e.style.minWidth = "500px", e.innerHTML = `
|
|
1531
|
+
<style>
|
|
1532
|
+
.emoji-dialog-content {
|
|
1533
|
+
display: flex;
|
|
1534
|
+
flex-direction: column;
|
|
1535
|
+
gap: 1rem;
|
|
1536
|
+
}
|
|
1537
|
+
.emoji-controls {
|
|
1538
|
+
display: flex;
|
|
1539
|
+
gap: 0.5rem;
|
|
1540
|
+
}
|
|
1541
|
+
.emoji-search, .emoji-category {
|
|
1542
|
+
padding: 0.5rem;
|
|
1543
|
+
border: 1px solid #ddd;
|
|
1544
|
+
border-radius: 4px;
|
|
1545
|
+
font-size: 14px;
|
|
1546
|
+
}
|
|
1547
|
+
.emoji-search {
|
|
1548
|
+
flex: 1;
|
|
1549
|
+
}
|
|
1550
|
+
.emoji-category {
|
|
1551
|
+
min-width: 150px;
|
|
1552
|
+
}
|
|
1553
|
+
.emoji-grid {
|
|
1554
|
+
display: grid;
|
|
1555
|
+
grid-template-columns: repeat(10, 1fr);
|
|
1556
|
+
gap: 0.5rem;
|
|
1557
|
+
max-height: 350px;
|
|
1558
|
+
overflow-y: auto;
|
|
1559
|
+
padding: 1rem;
|
|
1560
|
+
background: #f9f9f9;
|
|
1561
|
+
border: 1px solid #ddd;
|
|
1562
|
+
border-radius: 4px;
|
|
1563
|
+
}
|
|
1564
|
+
.emoji-button {
|
|
1565
|
+
aspect-ratio: 1;
|
|
1566
|
+
display: flex;
|
|
1567
|
+
align-items: center;
|
|
1568
|
+
justify-content: center;
|
|
1569
|
+
padding: 0.5rem;
|
|
1570
|
+
border: 1px solid #ddd;
|
|
1571
|
+
background: white;
|
|
1572
|
+
border-radius: 4px;
|
|
1573
|
+
cursor: pointer;
|
|
1574
|
+
font-size: 24px;
|
|
1575
|
+
transition: all 0.2s;
|
|
1576
|
+
}
|
|
1577
|
+
.emoji-button:hover {
|
|
1578
|
+
background: #fff3e0;
|
|
1579
|
+
border-color: #ff9800;
|
|
1580
|
+
transform: scale(1.15);
|
|
1581
|
+
}
|
|
1582
|
+
.emoji-info {
|
|
1583
|
+
padding: 0.5rem;
|
|
1584
|
+
background: #f5f5f5;
|
|
1585
|
+
border-radius: 4px;
|
|
1586
|
+
font-size: 12px;
|
|
1587
|
+
color: #666;
|
|
1588
|
+
text-align: center;
|
|
1589
|
+
}
|
|
1590
|
+
</style>
|
|
1591
|
+
|
|
1592
|
+
<div class="emoji-controls">
|
|
1593
|
+
<input
|
|
1594
|
+
type="text"
|
|
1595
|
+
id="emoji-search"
|
|
1596
|
+
class="emoji-search"
|
|
1597
|
+
placeholder="Search emojis..."
|
|
1598
|
+
/>
|
|
1599
|
+
<select id="emoji-category" class="emoji-category">
|
|
1600
|
+
<option value="all">All Emojis</option>
|
|
1601
|
+
<option value="popular">Popular</option>
|
|
1602
|
+
<option value="smileys">Smileys & Faces</option>
|
|
1603
|
+
<option value="emotions">Emotions</option>
|
|
1604
|
+
<option value="gestures">Gestures & Hands</option>
|
|
1605
|
+
<option value="people">People</option>
|
|
1606
|
+
<option value="animals">Animals</option>
|
|
1607
|
+
<option value="nature">Nature</option>
|
|
1608
|
+
<option value="food">Food & Drink</option>
|
|
1609
|
+
<option value="objects">Objects</option>
|
|
1610
|
+
<option value="symbols">Symbols</option>
|
|
1611
|
+
<option value="activities">Activities & Sports</option>
|
|
1612
|
+
</select>
|
|
1613
|
+
</div>
|
|
1614
|
+
|
|
1615
|
+
<div id="emoji-grid" class="emoji-grid"></div>
|
|
1616
|
+
|
|
1617
|
+
<div class="emoji-info">
|
|
1618
|
+
Click any emoji to insert it into your document.
|
|
1619
|
+
</div>
|
|
1620
|
+
`, e;
|
|
1621
|
+
}
|
|
1622
|
+
updateGrid() {
|
|
1623
|
+
const e = this.categorySelect.value;
|
|
1624
|
+
this.emojisGrid.innerHTML = "";
|
|
1625
|
+
let t = [];
|
|
1626
|
+
e === "all" ? t = Object.values(this.emojis).flat() : t = this.emojis[e] || [], t.forEach((i) => {
|
|
1627
|
+
const n = document.createElement("button");
|
|
1628
|
+
n.className = "emoji-button", n.textContent = i, n.title = `Insert ${i}`, n.addEventListener("click", (s) => {
|
|
1629
|
+
s.preventDefault(), this.handleSelect(i);
|
|
1630
|
+
}), this.emojisGrid.appendChild(n);
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
filterEmojis() {
|
|
1634
|
+
const e = this.searchInput.value.toLowerCase();
|
|
1635
|
+
this.emojisGrid.querySelectorAll(".emoji-button").forEach((i) => {
|
|
1636
|
+
const n = i.textContent || "", s = !e || n.includes(e);
|
|
1637
|
+
i.style.display = s ? "flex" : "none";
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
handleSelect(e) {
|
|
1641
|
+
this.onSelect(e), this.dialog.close();
|
|
1642
|
+
}
|
|
1643
|
+
show() {
|
|
1644
|
+
this.dialog.show(), this.searchInput.value = "", this.categorySelect.value = "popular", this.updateGrid(), setTimeout(() => this.searchInput.focus(), 100);
|
|
1645
|
+
}
|
|
1646
|
+
close() {
|
|
1647
|
+
this.dialog.close();
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
class S {
|
|
1651
|
+
constructor(e = {}) {
|
|
1652
|
+
this.changeListeners = [], this.options = e, this.engine = new f({
|
|
1653
|
+
content: e.content,
|
|
1654
|
+
plugins: e.plugins || [],
|
|
1655
|
+
readonly: e.readonly
|
|
1656
|
+
}), e.onChange && this.onChange(e.onChange), this.engine.on("change", () => {
|
|
1657
|
+
const t = this.getHTML();
|
|
1658
|
+
this.changeListeners.forEach((i) => i(t));
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Mount editor to DOM elements
|
|
1663
|
+
*/
|
|
1664
|
+
mount(e, t) {
|
|
1665
|
+
this.contentElement = e, this.toolbarElement = t, this.contentElement.contentEditable = this.options.readonly ? "false" : "true", this.contentElement.className = "rte-content", this.options.content && (this.contentElement.innerHTML = this.options.content), this.options.toolbar !== !1 && this.toolbarElement && (this.toolbar = new b(
|
|
1666
|
+
{
|
|
1667
|
+
items: typeof this.options.toolbar == "string" ? this.options.toolbar : void 0
|
|
1668
|
+
},
|
|
1669
|
+
this.options.plugins || []
|
|
1670
|
+
), this.toolbar.setCommandHandler((i, n) => {
|
|
1671
|
+
this.execCommand(i, n);
|
|
1672
|
+
}), this.toolbar.render(this.toolbarElement)), this.contentElement.addEventListener("input", () => {
|
|
1673
|
+
const i = this.getHTML();
|
|
1674
|
+
this.changeListeners.forEach((n) => n(i));
|
|
1675
|
+
}), this.options.onInit && this.options.onInit(this.getAPI());
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Get HTML content
|
|
1679
|
+
*/
|
|
1680
|
+
getHTML() {
|
|
1681
|
+
var e;
|
|
1682
|
+
return ((e = this.contentElement) == null ? void 0 : e.innerHTML) || "";
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Set HTML content
|
|
1686
|
+
*/
|
|
1687
|
+
setHTML(e) {
|
|
1688
|
+
this.contentElement && (this.contentElement.innerHTML = e);
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Execute command
|
|
1692
|
+
*/
|
|
1693
|
+
execCommand(e, t) {
|
|
1694
|
+
const i = this.engine.execCommand(e, t);
|
|
1695
|
+
return this.toolbar, i;
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Focus editor
|
|
1699
|
+
*/
|
|
1700
|
+
focus() {
|
|
1701
|
+
var e;
|
|
1702
|
+
(e = this.contentElement) == null || e.focus();
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Blur editor
|
|
1706
|
+
*/
|
|
1707
|
+
blur() {
|
|
1708
|
+
var e;
|
|
1709
|
+
(e = this.contentElement) == null || e.blur();
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Register change listener
|
|
1713
|
+
*/
|
|
1714
|
+
onChange(e) {
|
|
1715
|
+
return this.changeListeners.push(e), () => {
|
|
1716
|
+
const t = this.changeListeners.indexOf(e);
|
|
1717
|
+
t > -1 && this.changeListeners.splice(t, 1);
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Register command
|
|
1722
|
+
*/
|
|
1723
|
+
registerCommand(e, t) {
|
|
1724
|
+
if (typeof window != "undefined") {
|
|
1725
|
+
const i = window.__editorCommands || /* @__PURE__ */ new Map();
|
|
1726
|
+
i.set(e, t), window.__editorCommands = i;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Get state
|
|
1731
|
+
*/
|
|
1732
|
+
getState() {
|
|
1733
|
+
return {
|
|
1734
|
+
plugins: this.options.plugins,
|
|
1735
|
+
config: this.options,
|
|
1736
|
+
engine: this.engine.getState()
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Get public API
|
|
1741
|
+
*/
|
|
1742
|
+
getAPI() {
|
|
1743
|
+
var e;
|
|
1744
|
+
return {
|
|
1745
|
+
getHTML: () => this.getHTML(),
|
|
1746
|
+
setHTML: (t) => this.setHTML(t),
|
|
1747
|
+
execCommand: (t, i) => this.execCommand(t, i),
|
|
1748
|
+
focus: () => this.focus(),
|
|
1749
|
+
blur: () => this.blur(),
|
|
1750
|
+
destroy: () => this.destroy(),
|
|
1751
|
+
registerCommand: (t, i) => this.registerCommand(t, i),
|
|
1752
|
+
onChange: (t) => this.onChange(t),
|
|
1753
|
+
getState: () => this.getState(),
|
|
1754
|
+
toolbar: {
|
|
1755
|
+
items: ((e = this.options.plugins) == null ? void 0 : e.flatMap((t) => t.toolbar || [])) || []
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Destroy editor
|
|
1761
|
+
*/
|
|
1762
|
+
destroy() {
|
|
1763
|
+
var e;
|
|
1764
|
+
this.engine.destroy(), (e = this.toolbar) == null || e.destroy(), this.changeListeners = [], this.options.onDestroy && this.options.onDestroy();
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
function z(a) {
|
|
1768
|
+
return new S(a);
|
|
1769
|
+
}
|
|
1770
|
+
class x {
|
|
1771
|
+
constructor(e) {
|
|
1772
|
+
this.options = e, this.containerElement = e.element;
|
|
1773
|
+
const t = this.resolvePlugins(e.plugins);
|
|
1774
|
+
this.engine = new f({
|
|
1775
|
+
content: e.content,
|
|
1776
|
+
plugins: t,
|
|
1777
|
+
readonly: e.readonly
|
|
1778
|
+
}), this.initializeDOM();
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Resolve plugins from string or array
|
|
1782
|
+
*/
|
|
1783
|
+
resolvePlugins(e) {
|
|
1784
|
+
return e ? typeof e == "string" ? (console.warn("String-based plugin loading not yet implemented for VanillaAdapter"), []) : e : [];
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Initialize DOM elements
|
|
1788
|
+
*/
|
|
1789
|
+
initializeDOM() {
|
|
1790
|
+
this.containerElement.innerHTML = "", this.containerElement.classList.add("editora-editor"), this.options.enableToolbar !== !1 && this.options.toolbar !== !1 && (this.toolbarElement = document.createElement("div"), this.toolbarElement.className = "editora-toolbar-container", this.containerElement.appendChild(this.toolbarElement), this.toolbar = new b(
|
|
1791
|
+
{
|
|
1792
|
+
items: typeof this.options.toolbar == "string" ? this.options.toolbar : void 0
|
|
1793
|
+
},
|
|
1794
|
+
this.options.plugins || []
|
|
1795
|
+
), this.toolbar.setCommandHandler((e, t) => {
|
|
1796
|
+
this.execCommand(e, t);
|
|
1797
|
+
}), this.toolbar.render(this.toolbarElement)), this.contentElement = document.createElement("div"), this.contentElement.className = "editora-content", this.contentElement.contentEditable = this.options.readonly ? "false" : "true", this.contentElement.style.minHeight = "200px", this.contentElement.style.outline = "none", this.contentElement.style.padding = "12px", this.options.content && (this.contentElement.innerHTML = this.options.content), this.containerElement.appendChild(this.contentElement), this.contentElement.addEventListener("input", () => {
|
|
1798
|
+
this.containerElement.dispatchEvent(new CustomEvent("change", {
|
|
1799
|
+
detail: { html: this.getContent() }
|
|
1800
|
+
}));
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Get content
|
|
1805
|
+
*/
|
|
1806
|
+
getContent() {
|
|
1807
|
+
var e;
|
|
1808
|
+
return ((e = this.contentElement) == null ? void 0 : e.innerHTML) || "";
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* Set content
|
|
1812
|
+
*/
|
|
1813
|
+
setContent(e) {
|
|
1814
|
+
this.contentElement && (this.contentElement.innerHTML = e);
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Execute command
|
|
1818
|
+
*/
|
|
1819
|
+
execCommand(e, t) {
|
|
1820
|
+
return this.engine.execCommand(e, t);
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Focus editor
|
|
1824
|
+
*/
|
|
1825
|
+
focus() {
|
|
1826
|
+
var e;
|
|
1827
|
+
(e = this.contentElement) == null || e.focus();
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Add event listener
|
|
1831
|
+
*/
|
|
1832
|
+
on(e, t) {
|
|
1833
|
+
const i = (n) => {
|
|
1834
|
+
t(n.detail);
|
|
1835
|
+
};
|
|
1836
|
+
return this.containerElement.addEventListener(e, i), () => {
|
|
1837
|
+
this.containerElement.removeEventListener(e, i);
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Destroy editor
|
|
1842
|
+
*/
|
|
1843
|
+
destroy() {
|
|
1844
|
+
var e;
|
|
1845
|
+
this.engine.destroy(), (e = this.toolbar) == null || e.destroy(), this.containerElement.innerHTML = "";
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
function U(a) {
|
|
1849
|
+
return new x(a);
|
|
1850
|
+
}
|
|
1851
|
+
function R() {
|
|
1852
|
+
typeof window != "undefined" && !customElements.get("editora-editor") && customElements.define("editora-editor", v);
|
|
1853
|
+
}
|
|
602
1854
|
export {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
1855
|
+
$ as CharacterDialog,
|
|
1856
|
+
F as ColorPicker,
|
|
1857
|
+
B as CommandRegistry,
|
|
1858
|
+
G as ConfigResolver,
|
|
1859
|
+
u as Dialog,
|
|
1860
|
+
m as DocumentModel,
|
|
1861
|
+
D as Dropdown,
|
|
1862
|
+
k as Editor,
|
|
1863
|
+
f as EditorEngine,
|
|
1864
|
+
y as EditorState,
|
|
1865
|
+
H as EmojiDialog,
|
|
1866
|
+
K as FloatingToolbar,
|
|
1867
|
+
A as ImageDialog,
|
|
1868
|
+
L as KeyboardShortcutManager,
|
|
1869
|
+
j as LinkDialog,
|
|
1870
|
+
P as MathDialog,
|
|
1871
|
+
V as PluginLoader,
|
|
1872
|
+
g as PluginManager,
|
|
1873
|
+
E as PluginRuntime,
|
|
1874
|
+
S as ReactAdapter,
|
|
1875
|
+
v as RichTextEditorElement,
|
|
1876
|
+
W as Schema,
|
|
1877
|
+
X as StatusBar,
|
|
1878
|
+
q as TableDialog,
|
|
1879
|
+
b as ToolbarRenderer,
|
|
1880
|
+
x as VanillaAdapter,
|
|
1881
|
+
U as createEditor,
|
|
1882
|
+
T as createMediaPlugin,
|
|
1883
|
+
M as createPluginRuntime,
|
|
1884
|
+
z as createReactAdapter,
|
|
1885
|
+
I as createSpellcheckPlugin,
|
|
1886
|
+
R as initWebComponent
|
|
612
1887
|
};
|
|
1888
|
+
//# sourceMappingURL=index.esm.js.map
|