Packages
ckeditor5_phoenix
1.27.1
1.28.2
1.28.1
1.28.0
1.27.2
1.27.1
1.27.0
1.26.0
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.0
1.19.0
1.18.0
1.17.2
1.17.1
1.17.0
1.16.1
1.16.0
1.15.8
1.15.7
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.0
1.4.1
1.4.0
1.3.0
1.2.1
1.2.0
1.1.0
1.0.8
1.0.7
CKEditor 5 integration for Phoenix Framework
Current section
Files
Jump to
Current section
Files
dist/index.mjs
function H(r, t) {
if (!r || r.size !== t.size)
return !1;
for (const [e, a] of r)
if (!t.has(e) || t.get(e) !== a)
return !1;
return !0;
}
class K {
/**
* Map of registered items.
*/
items = /* @__PURE__ */ new Map();
/**
* Map of initialization errors for items that failed to register.
*/
initializationErrors = /* @__PURE__ */ new Map();
/**
* Map of pending callbacks waiting for items to be registered or fail.
*/
pendingCallbacks = /* @__PURE__ */ new Map();
/**
* Set of watchers that observe changes to the registry.
*/
watchers = /* @__PURE__ */ new Set();
/**
* Batch nesting depth. When > 0, watcher notifications are deferred.
*/
batchDepth = 0;
/**
* Snapshot of the last state dispatched to watchers, used for change detection.
*/
lastNotifiedItems = null;
lastNotifiedErrors = null;
/**
* Executes a function on an item.
* If the item is not yet registered, it will wait for it to be registered.
*
* @param id The ID of the item.
* @param onSuccess The function to execute.
* @param onError Optional error callback.
* @returns A promise that resolves with the result of the function.
*/
execute(t, e, a) {
const n = this.items.get(t), i = this.initializationErrors.get(t);
return i ? (a?.(i), Promise.reject(i)) : n ? Promise.resolve(e(n)) : new Promise((s, c) => {
const o = this.getPendingCallbacks(t);
o.success.push(async (u) => {
s(await e(u));
}), a ? o.error.push(a) : o.error.push(c);
});
}
/**
* Reactively binds a mount/unmount lifecycle to a single registry item.
*
* @param id The ID of the item to observe.
* @param onMount Function executed when the item mounts.
* @returns A function that stops observing and immediately runs any pending cleanup.
*/
mountEffect(t, e) {
let a, n, i = !1;
const s = this.watch((c) => {
const o = c.get(t);
if (o !== n && (a?.(), a = void 0, n = o, !!o))
try {
const u = e(o);
i ? (u?.(), s()) : a = u;
} catch (u) {
throw console.error(u), u;
}
});
return () => {
i = !0, n && (s(), a?.(), a = void 0);
};
}
/**
* Registers an item.
*
* @param id The ID of the item.
* @param item The item instance.
*/
register(t, e) {
this.batch(() => {
if (this.items.has(t))
throw new Error(`Item with ID "${t}" is already registered.`);
this.resetErrors(t), this.items.set(t, e);
const a = this.pendingCallbacks.get(t);
a && (a.success.forEach((n) => n(e)), this.pendingCallbacks.delete(t)), this.items.size === 1 && t !== null && this.register(null, e);
});
}
/**
* Registers an error for an item.
*
* @param id The ID of the item.
* @param error The error to register.
*/
error(t, e) {
this.batch(() => {
this.items.delete(t), this.initializationErrors.set(t, e);
const a = this.pendingCallbacks.get(t);
a && (a.error.forEach((n) => n(e)), this.pendingCallbacks.delete(t)), this.initializationErrors.size === 1 && !this.items.size && this.error(null, e);
});
}
/**
* Resets errors for an item.
*
* @param id The ID of the item.
*/
resetErrors(t) {
const { initializationErrors: e } = this;
e.has(null) && e.get(null) === e.get(t) && e.delete(null), e.delete(t);
}
/**
* Un-registers an item.
*
* @param id The ID of the item.
* @param resetPendingCallbacks If true resets pending callbacks.
*/
unregister(t, e = !0) {
this.batch(() => {
t && this.items.get(null) === this.items.get(t) && this.unregister(null, !1), this.items.delete(t), e && this.pendingCallbacks.delete(t), this.resetErrors(t);
});
}
/**
* Gets all registered items.
*
* @returns An array of all registered items.
*/
getItems() {
return Array.from(this.items.values());
}
/**
* Returns single registered item.
*
* @returns Registered item.
*/
getItem(t) {
return this.items.get(t);
}
/**
* Checks if an item with the given ID is registered.
*
* @param id The ID of the item.
* @returns `true` if the item is registered, `false` otherwise.
*/
hasItem(t) {
return this.items.has(t);
}
/**
* Gets a promise that resolves with the item instance for the given ID.
* If the item is not registered yet, it will wait for it to be registered.
*
* @param id The ID of the item.
* @returns A promise that resolves with the item instance.
*/
waitFor(t) {
return new Promise((e, a) => {
this.execute(t, e, a);
});
}
/**
* Destroys all registered items and clears the registry.
* This will call the `destroy` method on each item.
*/
async destroyAll() {
const t = Array.from(new Set(this.items.values())).map((e) => e.destroy());
this.items.clear(), this.pendingCallbacks.clear(), await Promise.all(t), this.flushWatchers();
}
/**
* Destroys all registered editors and removes all watchers.
*/
async reset() {
await this.destroyAll(), this.watchers.clear();
}
/**
* Executes a callback while deferring all watcher notifications.
* A single notification is fired synchronously after the callback returns,
* but only if the registry actually changed.
*
* Batches can be nested — watchers are notified only when the outermost
* batch completes.
*
* @param fn The callback to execute.
* @returns The return value of the callback.
*/
batch(t) {
this.batchDepth++;
try {
return t();
} finally {
this.batchDepth--, this.batchDepth === 0 && this.flushWatchers();
}
}
/**
* Registers a watcher that will be called whenever the registry changes.
*
* @param watcher The watcher function to register.
* @returns A function to unregister the watcher.
*/
watch(t) {
return this.watchers.add(t), t(
new Map(this.items),
new Map(this.initializationErrors)
), this.unwatch.bind(this, t);
}
/**
* Un-registers a watcher.
*
* @param watcher The watcher function to unregister.
*/
unwatch(t) {
this.watchers.delete(t);
}
/**
* Immediately dispatches the current state to all watchers if it changed.
*/
flushWatchers() {
H(this.lastNotifiedItems, this.items) && H(this.lastNotifiedErrors, this.initializationErrors) || (this.lastNotifiedItems = new Map(this.items), this.lastNotifiedErrors = new Map(this.initializationErrors), this.watchers.forEach((t) => t(
new Map(this.items),
new Map(this.initializationErrors)
)));
}
/**
* Gets or creates pending callbacks for a specific ID.
*
* @param id The ID of the item.
* @returns The pending callbacks structure.
*/
getPendingCallbacks(t) {
let e = this.pendingCallbacks.get(t);
return e || (e = { success: [], error: [] }, this.pendingCallbacks.set(t, e)), e;
}
}
function rt(r) {
return r.replace(/[-_\s]+(.)?/g, (t, e) => e ? e.toUpperCase() : "").replace(/^./, (t) => t.toLowerCase());
}
function I(r, t) {
let e = null;
return (...a) => {
e && clearTimeout(e), e = setTimeout(() => {
t(...a);
}, r);
};
}
function at(r) {
if (Object.prototype.toString.call(r) !== "[object Object]")
return !1;
const t = Object.getPrototypeOf(r);
return t === Object.prototype || t === null;
}
function O(r) {
if (Array.isArray(r))
return r.map(O);
if (at(r)) {
const t = /* @__PURE__ */ Object.create(null);
for (const [e, a] of Object.entries(r))
t[rt(e)] = O(a);
return t;
}
return r;
}
function nt(r, t) {
const e = Object.entries(r).filter(([a, n]) => t(n, a));
return Object.fromEntries(e);
}
function it() {
const r = document.querySelector('meta[name="csrf-token"]');
if (r)
return r.getAttribute("content");
const t = document.cookie.match(/(?:^|; )_csrf_token=([^;]*)/);
return t ? decodeURIComponent(t[1]) : null;
}
class S {
/**
* The current state of the hook.
*/
state = "mounting";
/**
* The DOM element the hook is attached to.
* It includes an `instance` property to hold the hook instance.
*/
el;
/**
* Callbacks to run before the hook is destroyed.
*/
_beforeDestroyCallbacks = [];
/**
* Registers a callback to be called before the hook is destroyed.
* Callbacks are called in LIFO order (last registered, first called).
*/
onBeforeDestroy(t) {
this._beforeDestroyCallbacks.push(t);
}
/**
* Pushes an event from the client to the LiveView server process.
* @param _event The name of the event.
* @param _payload The data to send with the event.
* @param _callback An optional function to be called with the server's reply.
*/
pushEvent;
/**
* Pushes an event to another hook on the page.
* @param _selector The CSS selector of the target element with the hook.
* @param _event The name of the event.
* @param _payload The data to send with the event.
* @param _callback An optional function to be called with the reply.
*/
pushEventTo;
/**
* Registers a handler for an event pushed from the server.
* @param _event The name of the event to handle.
* @param _callback The function to execute when the event is received.
*/
handleEvent;
/**
* Called when the hook has been mounted to the DOM.
* This is the ideal place for initialization code.
*/
mounted() {
}
/**
* Called when the element has been removed from the DOM.
* Perfect for cleanup tasks.
*/
destroyed() {
}
/**
* Called when the element has been updated by a LiveView patch.
*/
updated() {
}
/**
* Checks if the hook is in the process of being destroyed.
*/
isBeingDestroyed() {
return this.state === "destroyed" || this.state === "destroying";
}
/**
* Runs all registered before-destroy callbacks and clears the list.
* Called internally by makeHook before destroyed().
*/
_runBeforeDestroyCallbacks() {
for (const t of this._beforeDestroyCallbacks.reverse())
t();
this._beforeDestroyCallbacks = [];
}
}
function N(r) {
return {
/**
* The mounted lifecycle callback for the LiveView hook object.
* It creates an instance of the user-defined hook class and sets up the necessary properties and methods.
*/
async mounted() {
const t = new r();
this.el.instance = t, t.el = this.el, t.pushEvent = (a, n, i) => this.pushEvent?.(a, n, i), t.pushEventTo = (a, n, i, s) => this.pushEventTo?.(a, n, i, s), t.handleEvent = (a, n) => this.handleEvent?.(a, n), t.state = "mounting";
const e = await t.mounted?.();
return t.state = "mounted", e;
},
/**
* The beforeUpdate lifecycle callback that delegates to the hook instance.
*/
beforeUpdate() {
this.el.instance.beforeUpdate?.();
},
/**
* The destroyed lifecycle callback that delegates to the hook instance.
*/
async destroyed() {
const { instance: t } = this.el;
t.state = "destroying", t._runBeforeDestroyCallbacks(), await t.destroyed?.(), t.state = "destroyed";
},
/**
* The disconnected lifecycle callback that delegates to the hook instance.
*/
disconnected() {
this.el.instance.disconnected?.();
},
/**
* The reconnected lifecycle callback that delegates to the hook instance.
*/
reconnected() {
this.el.instance.reconnected?.();
},
/**
* The updated lifecycle callback that delegates to the hook instance.
*/
updated() {
return this.el.instance.updated?.();
}
};
}
function Y(r) {
return Object.keys(r).length === 0 && r.constructor === Object;
}
function st(r) {
return r == null;
}
function j(r, t) {
const e = Object.entries(r).map(([a, n]) => [a, t(n, a)]);
return Object.fromEntries(e);
}
function q(r) {
if (r === null)
return null;
const t = Number.parseInt(r, 10);
return Number.isNaN(t) ? null : t;
}
function ot(r) {
return r == null || r.trim() === "" ? null : JSON.parse(r);
}
function ct(r, t) {
if (r === t)
return !0;
const e = Object.keys(r), a = Object.keys(t);
if (e.length !== a.length)
return !1;
for (const n of e)
if (r[n] !== t[n] || !Object.prototype.hasOwnProperty.call(t, n))
return !1;
return !0;
}
function ut() {
return Math.random().toString(36).substring(2);
}
function lt(r, {
timeOutAfter: t = 500,
retryAfter: e = 100
} = {}) {
return new Promise((a, n) => {
const i = Date.now();
let s = null;
const c = setTimeout(() => {
n(s ?? new Error("Timeout"));
}, t), o = async () => {
try {
const u = await r();
clearTimeout(c), a(u);
} catch (u) {
s = u, Date.now() - i > t ? n(u) : setTimeout(o, e);
}
};
o();
});
}
function dt(r, t) {
const e = ht(r), a = /* @__PURE__ */ new Set([
...Object.keys(e),
...Object.keys(t.roots ?? {})
]), n = Array.from(a).reduce((s, c) => ({
...s,
[c]: {
...t.roots?.[c],
...c === "main" ? t.root : {},
/* v8 ignore next 5 */
...c in e ? {
initialData: e[c]
} : {}
}
}), Object.create(t.roots || {})), i = {
...t,
roots: n
};
return delete i.root, i;
}
function ht(r) {
return typeof r == "string" ? { main: r } : { ...r };
}
function mt(r, t, e) {
const a = pt(t);
if (!r.editorName || r.editorName === "ClassicEditor")
return {
...e,
attachTo: a.main
};
const n = /* @__PURE__ */ new Set([
...Object.keys(a),
...Object.keys(e.roots ?? {})
]), i = Array.from(n).reduce((c, o) => ({
...c,
[o]: {
/* v8 ignore next */
...e.roots?.[o],
...o === "main" ? e.root : {},
/* v8 ignore next 5 */
...o in a ? {
element: a[o]
} : {}
}
}), Object.create(e.roots || {})), s = {
...e,
roots: i
};
return delete s.root, s;
}
function pt(r) {
return r instanceof HTMLElement ? { main: r } : { ...r };
}
function ft(r) {
const t = [
r.ui?.element,
r.ui?.view?.toolbar?.element,
r.ui?.view?.menuBarView?.element
].filter(Boolean);
for (const i of t)
n(i);
const e = r.ui?.view?.body?._bodyCollectionContainer;
e?.isConnected && n(e);
const a = r.editing?.view;
if (a)
for (const i of a.domRoots.values())
i instanceof HTMLElement && (i.removeAttribute("contenteditable"), i.removeAttribute("role"), i.removeAttribute("aria-label"), i.removeAttribute("aria-multiline"), i.removeAttribute("spellcheck"), i.classList.remove(
"ck",
"ck-content",
"ck-editor__editable",
"ck-rounded-corners",
"ck-editor__editable_inline",
"ck-blurred",
"ck-focused"
), n(i));
function n(i) {
i.hasAttribute("data-cke-controlled") ? i.innerHTML = "" : i.remove();
}
}
const R = /* @__PURE__ */ Symbol.for("context-editor-watchdog");
async function wt({ context: r, creator: t, config: e }) {
const a = ut();
await r.add({
creator: t.create.bind(t),
id: a,
type: "editor",
config: e
});
const n = r.getItem(a), i = {
state: "available",
editorContextId: a,
context: r
};
n[R] = i;
const s = r.destroy.bind(r);
return r.destroy = async () => (i.state = "unavailable", s()), {
...i,
editor: n
};
}
function gt(r) {
return R in r ? r[R] : null;
}
function B(r) {
return "addEditable" in r.ui;
}
function T(r) {
return ["inline", "classic", "balloon", "decoupled"].includes(r);
}
async function bt(r) {
const t = await import("ckeditor5"), a = {
inline: t.InlineEditor,
balloon: t.BalloonEditor,
classic: t.ClassicEditor,
decoupled: t.DecoupledEditor,
multiroot: t.MultiRootEditor
}[r];
if (!a)
throw new Error(`Unsupported editor type: ${r}`);
return a;
}
class $ {
static the = new $();
/**
* Map of registered custom plugins.
*/
plugins = /* @__PURE__ */ new Map();
/**
* Private constructor to enforce singleton pattern.
*/
constructor() {
}
/**
* Registers a custom plugin for the CKEditor.
*
* @param name The name of the plugin.
* @param reader The plugin reader function that returns the plugin constructor.
* @returns A function to unregister the plugin.
*/
register(t, e) {
if (this.plugins.has(t))
throw new Error(`Plugin with name "${t}" is already registered.`);
return this.plugins.set(t, e), this.unregister.bind(this, t);
}
/**
* Removes a custom plugin by its name.
*
* @param name The name of the plugin to unregister.
* @throws Will throw an error if the plugin is not registered.
*/
unregister(t) {
if (!this.plugins.has(t))
throw new Error(`Plugin with name "${t}" is not registered.`);
this.plugins.delete(t);
}
/**
* Removes all custom editor plugins.
* This is useful for cleanup in tests or when reloading plugins.
*/
unregisterAll() {
this.plugins.clear();
}
/**
* Retrieves a custom plugin by its name.
*
* @param name The name of the plugin.
* @returns The plugin constructor or undefined if not found.
*/
async get(t) {
return this.plugins.get(t)?.();
}
/**
* Checks if a plugin with the given name is registered.
*
* @param name The name of the plugin.
* @returns `true` if the plugin is registered, `false` otherwise.
*/
has(t) {
return this.plugins.has(t);
}
}
async function J(r) {
const t = await import("ckeditor5");
let e = null;
const a = r.map(async (n) => {
const i = await $.the.get(n);
if (i)
return i;
const { [n]: s } = t;
if (s)
return s;
if (!e)
try {
e = await import("ckeditor5-premium-features");
} catch (o) {
console.error(`Failed to load premium package: ${o}`);
}
const { [n]: c } = e || {};
if (c)
return c;
throw new Error(`Plugin "${n}" not found in base or premium packages.`);
});
return {
loadedPlugins: await Promise.all(a),
hasPremium: !!e
};
}
async function G(r, t) {
const e = [r.ui, r.content];
return await Promise.all(
[
F("ckeditor5", e),
/* v8 ignore next */
t && F("ckeditor5-premium-features", e)
].filter((n) => !!n)
).then((n) => n.flat());
}
async function F(r, t) {
return await Promise.all(
t.filter((e) => e !== "en").map(async (e) => {
const a = await yt(r, e);
return a?.default ?? a;
}).filter(Boolean)
);
}
async function yt(r, t) {
try {
if (r === "ckeditor5")
switch (t) {
case "af":
return await import("ckeditor5/translations/af.js");
case "ar":
return await import("ckeditor5/translations/ar.js");
case "ast":
return await import("ckeditor5/translations/ast.js");
case "az":
return await import("ckeditor5/translations/az.js");
case "bg":
return await import("ckeditor5/translations/bg.js");
case "bn":
return await import("ckeditor5/translations/bn.js");
case "bs":
return await import("ckeditor5/translations/bs.js");
case "ca":
return await import("ckeditor5/translations/ca.js");
case "cs":
return await import("ckeditor5/translations/cs.js");
case "da":
return await import("ckeditor5/translations/da.js");
case "de":
return await import("ckeditor5/translations/de.js");
case "de-ch":
return await import("ckeditor5/translations/de-ch.js");
case "el":
return await import("ckeditor5/translations/el.js");
case "en":
return await import("ckeditor5/translations/en.js");
case "en-au":
return await import("ckeditor5/translations/en-au.js");
case "en-gb":
return await import("ckeditor5/translations/en-gb.js");
case "eo":
return await import("ckeditor5/translations/eo.js");
case "es":
return await import("ckeditor5/translations/es.js");
case "es-co":
return await import("ckeditor5/translations/es-co.js");
case "et":
return await import("ckeditor5/translations/et.js");
case "eu":
return await import("ckeditor5/translations/eu.js");
case "fa":
return await import("ckeditor5/translations/fa.js");
case "fi":
return await import("ckeditor5/translations/fi.js");
case "fr":
return await import("ckeditor5/translations/fr.js");
case "gl":
return await import("ckeditor5/translations/gl.js");
case "gu":
return await import("ckeditor5/translations/gu.js");
case "he":
return await import("ckeditor5/translations/he.js");
case "hi":
return await import("ckeditor5/translations/hi.js");
case "hr":
return await import("ckeditor5/translations/hr.js");
case "hu":
return await import("ckeditor5/translations/hu.js");
case "hy":
return await import("ckeditor5/translations/hy.js");
case "id":
return await import("ckeditor5/translations/id.js");
case "it":
return await import("ckeditor5/translations/it.js");
case "ja":
return await import("ckeditor5/translations/ja.js");
case "jv":
return await import("ckeditor5/translations/jv.js");
case "kk":
return await import("ckeditor5/translations/kk.js");
case "km":
return await import("ckeditor5/translations/km.js");
case "kn":
return await import("ckeditor5/translations/kn.js");
case "ko":
return await import("ckeditor5/translations/ko.js");
case "ku":
return await import("ckeditor5/translations/ku.js");
case "lt":
return await import("ckeditor5/translations/lt.js");
case "lv":
return await import("ckeditor5/translations/lv.js");
case "ms":
return await import("ckeditor5/translations/ms.js");
case "nb":
return await import("ckeditor5/translations/nb.js");
case "ne":
return await import("ckeditor5/translations/ne.js");
case "nl":
return await import("ckeditor5/translations/nl.js");
case "no":
return await import("ckeditor5/translations/no.js");
case "oc":
return await import("ckeditor5/translations/oc.js");
case "pl":
return await import("ckeditor5/translations/pl.js");
case "pt":
return await import("ckeditor5/translations/pt.js");
case "pt-br":
return await import("ckeditor5/translations/pt-br.js");
case "ro":
return await import("ckeditor5/translations/ro.js");
case "ru":
return await import("ckeditor5/translations/ru.js");
case "si":
return await import("ckeditor5/translations/si.js");
case "sk":
return await import("ckeditor5/translations/sk.js");
case "sl":
return await import("ckeditor5/translations/sl.js");
case "sq":
return await import("ckeditor5/translations/sq.js");
case "sr":
return await import("ckeditor5/translations/sr.js");
case "sr-latn":
return await import("ckeditor5/translations/sr-latn.js");
case "sv":
return await import("ckeditor5/translations/sv.js");
case "th":
return await import("ckeditor5/translations/th.js");
case "tk":
return await import("ckeditor5/translations/tk.js");
case "tr":
return await import("ckeditor5/translations/tr.js");
case "tt":
return await import("ckeditor5/translations/tt.js");
case "ug":
return await import("ckeditor5/translations/ug.js");
case "uk":
return await import("ckeditor5/translations/uk.js");
case "ur":
return await import("ckeditor5/translations/ur.js");
case "uz":
return await import("ckeditor5/translations/uz.js");
case "vi":
return await import("ckeditor5/translations/vi.js");
case "zh":
return await import("ckeditor5/translations/zh.js");
case "zh-cn":
return await import("ckeditor5/translations/zh-cn.js");
default:
return console.warn(`Language ${t} not found in ckeditor5 translations`), null;
}
else
switch (t) {
case "af":
return await import("ckeditor5-premium-features/translations/af.js");
case "ar":
return await import("ckeditor5-premium-features/translations/ar.js");
case "ast":
return await import("ckeditor5-premium-features/translations/ast.js");
case "az":
return await import("ckeditor5-premium-features/translations/az.js");
case "bg":
return await import("ckeditor5-premium-features/translations/bg.js");
case "bn":
return await import("ckeditor5-premium-features/translations/bn.js");
case "bs":
return await import("ckeditor5-premium-features/translations/bs.js");
case "ca":
return await import("ckeditor5-premium-features/translations/ca.js");
case "cs":
return await import("ckeditor5-premium-features/translations/cs.js");
case "da":
return await import("ckeditor5-premium-features/translations/da.js");
case "de":
return await import("ckeditor5-premium-features/translations/de.js");
case "de-ch":
return await import("ckeditor5-premium-features/translations/de-ch.js");
case "el":
return await import("ckeditor5-premium-features/translations/el.js");
case "en":
return await import("ckeditor5-premium-features/translations/en.js");
case "en-au":
return await import("ckeditor5-premium-features/translations/en-au.js");
case "en-gb":
return await import("ckeditor5-premium-features/translations/en-gb.js");
case "eo":
return await import("ckeditor5-premium-features/translations/eo.js");
case "es":
return await import("ckeditor5-premium-features/translations/es.js");
case "es-co":
return await import("ckeditor5-premium-features/translations/es-co.js");
case "et":
return await import("ckeditor5-premium-features/translations/et.js");
case "eu":
return await import("ckeditor5-premium-features/translations/eu.js");
case "fa":
return await import("ckeditor5-premium-features/translations/fa.js");
case "fi":
return await import("ckeditor5-premium-features/translations/fi.js");
case "fr":
return await import("ckeditor5-premium-features/translations/fr.js");
case "gl":
return await import("ckeditor5-premium-features/translations/gl.js");
case "gu":
return await import("ckeditor5-premium-features/translations/gu.js");
case "he":
return await import("ckeditor5-premium-features/translations/he.js");
case "hi":
return await import("ckeditor5-premium-features/translations/hi.js");
case "hr":
return await import("ckeditor5-premium-features/translations/hr.js");
case "hu":
return await import("ckeditor5-premium-features/translations/hu.js");
case "hy":
return await import("ckeditor5-premium-features/translations/hy.js");
case "id":
return await import("ckeditor5-premium-features/translations/id.js");
case "it":
return await import("ckeditor5-premium-features/translations/it.js");
case "ja":
return await import("ckeditor5-premium-features/translations/ja.js");
case "jv":
return await import("ckeditor5-premium-features/translations/jv.js");
case "kk":
return await import("ckeditor5-premium-features/translations/kk.js");
case "km":
return await import("ckeditor5-premium-features/translations/km.js");
case "kn":
return await import("ckeditor5-premium-features/translations/kn.js");
case "ko":
return await import("ckeditor5-premium-features/translations/ko.js");
case "ku":
return await import("ckeditor5-premium-features/translations/ku.js");
case "lt":
return await import("ckeditor5-premium-features/translations/lt.js");
case "lv":
return await import("ckeditor5-premium-features/translations/lv.js");
case "ms":
return await import("ckeditor5-premium-features/translations/ms.js");
case "nb":
return await import("ckeditor5-premium-features/translations/nb.js");
case "ne":
return await import("ckeditor5-premium-features/translations/ne.js");
case "nl":
return await import("ckeditor5-premium-features/translations/nl.js");
case "no":
return await import("ckeditor5-premium-features/translations/no.js");
case "oc":
return await import("ckeditor5-premium-features/translations/oc.js");
case "pl":
return await import("ckeditor5-premium-features/translations/pl.js");
case "pt":
return await import("ckeditor5-premium-features/translations/pt.js");
case "pt-br":
return await import("ckeditor5-premium-features/translations/pt-br.js");
case "ro":
return await import("ckeditor5-premium-features/translations/ro.js");
case "ru":
return await import("ckeditor5-premium-features/translations/ru.js");
case "si":
return await import("ckeditor5-premium-features/translations/si.js");
case "sk":
return await import("ckeditor5-premium-features/translations/sk.js");
case "sl":
return await import("ckeditor5-premium-features/translations/sl.js");
case "sq":
return await import("ckeditor5-premium-features/translations/sq.js");
case "sr":
return await import("ckeditor5-premium-features/translations/sr.js");
case "sr-latn":
return await import("ckeditor5-premium-features/translations/sr-latn.js");
case "sv":
return await import("ckeditor5-premium-features/translations/sv.js");
case "th":
return await import("ckeditor5-premium-features/translations/th.js");
case "tk":
return await import("ckeditor5-premium-features/translations/tk.js");
case "tr":
return await import("ckeditor5-premium-features/translations/tr.js");
case "tt":
return await import("ckeditor5-premium-features/translations/tt.js");
case "ug":
return await import("ckeditor5-premium-features/translations/ug.js");
case "uk":
return await import("ckeditor5-premium-features/translations/uk.js");
case "ur":
return await import("ckeditor5-premium-features/translations/ur.js");
case "uz":
return await import("ckeditor5-premium-features/translations/uz.js");
case "vi":
return await import("ckeditor5-premium-features/translations/vi.js");
case "zh":
return await import("ckeditor5-premium-features/translations/zh.js");
case "zh-cn":
return await import("ckeditor5-premium-features/translations/zh-cn.js");
default:
return console.warn(`Language ${t} not found in premium translations`), await import("ckeditor5-premium-features/translations/en.js");
}
} catch (e) {
return console.error(`Failed to load translation for ${r}/${t}:`, e), null;
}
}
function X(r) {
return j(r, (t) => ({
dictionary: t
}));
}
function Q(r) {
const t = Z(r);
return j(t, ({ content: e }) => e);
}
function L(r) {
const t = Z(r), e = j(t, ({ initialValue: a }) => a);
return nt(e, (a) => typeof a == "string");
}
function Z(r) {
const t = document.querySelectorAll(
[
`[data-cke-editor-id="${r}"][data-cke-editable-root-name]`,
"[data-cke-editable-root-name]:not([data-cke-editor-id])"
].join(", ")
), e = Array.from(t).reduce((c, o) => {
const u = o.getAttribute("data-cke-editable-root-name"), l = o.getAttribute("data-cke-editable-initial-value") || "", d = o.querySelector("[data-cke-editable-content]");
return !u || !d ? c : {
...c,
[u]: {
content: d,
initialValue: l
}
};
}, /* @__PURE__ */ Object.create({})), a = document.querySelector(`[phx-hook="CKEditor5"][id="${r}"]`);
if (!a)
return e;
const n = a.getAttribute("data-cke-initial-value") || "", i = a.querySelector(`#${r}_editor `), s = e.main;
return s ? {
...e,
main: {
...s,
initialValue: s.initialValue || n
}
} : i ? {
...e,
main: {
content: i,
initialValue: n
}
} : e;
}
const W = ["inline", "classic", "balloon", "decoupled", "multiroot"];
function kt(r) {
const t = r.getAttribute("data-cke-preset");
if (!t)
throw new Error('CKEditor5 hook requires a "cke-preset" attribute on the element.');
const { type: e, config: a, license: n, watchdog: i, ...s } = JSON.parse(t);
if (!e || !a || !n)
throw new Error('CKEditor5 hook configuration must include "editor", "config", and "license" properties.');
if (!W.includes(e))
throw new Error(`Invalid editor type: ${e}. Must be one of: ${W.join(", ")}.`);
return {
type: e,
license: n,
watchdog: i,
config: O(a),
customTranslations: s.customTranslations || s.custom_translations
};
}
function x(r) {
if (!r || typeof r != "object")
return r;
if (Array.isArray(r))
return r.map((a) => x(a));
const t = r;
if (t.$element && typeof t.$element == "string") {
const a = document.querySelector(t.$element);
return a || console.warn(`Element not found for selector: ${t.$element}`), a || null;
}
const e = /* @__PURE__ */ Object.create(null);
for (const [a, n] of Object.entries(r))
e[a] = x(n);
return e;
}
function D(r, t, e) {
if (!e || typeof e != "object")
return e;
if (Array.isArray(e))
return e.map((i) => D(r, t, i));
const a = e;
if (a.$translation && typeof a.$translation == "string") {
const i = a.$translation, s = Et(r, i, t);
return s === void 0 && console.warn(`Translation not found for key: ${i}`), s !== void 0 ? s : null;
}
const n = /* @__PURE__ */ Object.create(null);
for (const [i, s] of Object.entries(e))
n[i] = D(r, t, s);
return n;
}
function Et(r, t, e) {
for (const a of r) {
const n = a[e];
if (n?.dictionary && t in n.dictionary)
return n.dictionary[t];
}
}
function vt(r, t) {
const { editing: e } = r;
e.view.change((a) => {
a.setStyle("height", `${t}px`, e.view.document.getRoot());
});
}
const V = /* @__PURE__ */ Symbol.for("elixir-editor-watchdog");
async function Ct(r, t) {
const { EditorWatchdog: e } = await import("ckeditor5"), a = new e(null, t ?? {
crashNumberLimit: 10,
minimumNonErrorTimePeriod: 5e3
});
return a.setCreator(async () => {
const n = await r();
return n[V] = a, n;
}), a;
}
function Pt(r) {
return V in r ? r[V] : null;
}
class y extends K {
static the = new y();
}
function At(r) {
const t = r.getAttribute("data-cke-context");
if (!t)
throw new Error('CKEditor5 hook requires a "data-cke-context" attribute on the element.');
const { config: e, ...a } = JSON.parse(t);
return {
config: O(e),
customTranslations: a.customTranslations || a.custom_translations,
watchdogConfig: a.watchdogConfig || a.watchdog_config
};
}
class Tt extends S {
/**
* The promise that resolves to the context instance.
*/
contextPromise = null;
/**
* Attributes for the context instance.
*/
get attrs() {
const t = (a) => this.el.getAttribute(a) || null, e = {
id: this.el.id,
config: At(this.el),
language: {
ui: t("data-cke-language") || "en",
content: t("data-cke-content-language") || "en"
}
};
return Object.defineProperty(this, "attrs", {
value: e,
writable: !1,
configurable: !1,
enumerable: !0
}), e;
}
/**
* Mounts the context component.
*/
async mounted() {
const { id: t, language: e } = this.attrs, { customTranslations: a, watchdogConfig: n, config: { plugins: i, ...s } } = this.attrs.config, { loadedPlugins: c, hasPremium: o } = await J(i ?? []), l = [
...await G(e, o),
X(a?.dictionary || {})
].filter((m) => !Y(m));
let d = x(s);
d = D([...l].reverse(), e.ui, d), this.contextPromise = (async () => {
const { ContextWatchdog: m, Context: E } = await import("ckeditor5"), b = new m(E, {
crashNumberLimit: 10,
...n
});
return await b.create({
...d,
language: e,
plugins: c,
...l.length && {
translations: l
}
}), b.on("itemError", (...P) => {
console.error("Context item error:", ...P);
}), b;
})();
const w = await this.contextPromise;
this.isBeingDestroyed() || y.the.register(t, w);
}
/**
* Destroys the context component. Unmounts root from the editor.
*/
async destroyed() {
const { id: t } = this.attrs;
this.el.style.display = "none";
try {
await (await this.contextPromise)?.destroy();
} finally {
this.contextPromise = null, y.the.hasItem(t) && y.the.unregister(t);
}
}
}
function It(r) {
return r.hasAttribute("data-cke-context");
}
function Ot(r) {
let t = r;
for (; t; ) {
if (It(t))
return t;
t = t.parentElement;
}
return null;
}
async function xt(r) {
const t = Ot(r);
return t ? y.the.waitFor(t.id) : null;
}
const Dt = N(Tt);
function St(r, t) {
const e = /* @__PURE__ */ new Set();
return (a) => {
let n = !1;
return r.model.enqueueChange({ isUndoable: !1 }, (i) => {
const s = r.model.document.getRoot(t);
if (s) {
for (const c of e)
a && c in a || (i.removeAttribute(c, s), e.delete(c), n = !0);
for (const [c, o] of Object.entries(a ?? {}))
i.setAttribute(c, o, s), e.add(c), n = !0;
}
}), n;
};
}
async function Nt() {
const { Plugin: r, FileRepository: t } = await import("ckeditor5");
return class extends r {
/**
* The name of the plugin.
*/
static get pluginName() {
return "PhoenixUploadAdapter";
}
static get requires() {
return [t];
}
/**
* Initializes the plugin.
*/
init() {
const { editor: a } = this, { plugins: n, config: i } = a, s = i.get("phoenixUpload.url");
if (!s || n.has("SimpleUploadAdapter") || n.has("Base64UploadAdapter") || n.has("CKFinderUploadAdapter"))
return;
const c = n.get(t);
c.createUploadAdapter = (o) => new Mt(o, s);
}
};
}
class Mt {
loader;
uploadUrl;
abortController = null;
constructor(t, e) {
this.loader = t, this.uploadUrl = e;
}
/**
* Starts the upload process.
*/
async upload() {
const t = await this.loader.file;
this.abortController = new AbortController();
const e = new FormData();
e.append("file", t), t.size && (this.loader.uploadTotal = t.size, this.loader.uploaded = 0);
const a = {}, n = it();
n && (a["X-CSRF-Token"] = n);
try {
const i = await fetch(this.uploadUrl, {
method: "POST",
headers: a,
body: e,
signal: this.abortController.signal
});
if (!i.ok) {
let c = "Couldn't upload file!";
try {
const o = await i.json();
o?.error?.message && (c = o.error.message);
} catch {
}
throw new Error(c);
}
return this.loader.uploaded = this.loader.uploadTotal, {
default: (await i.json()).url
};
} catch (i) {
throw i.name === "AbortError" ? i : i.message || "Couldn't upload file!";
}
}
/**
* Aborts the upload process.
*/
/* v8 ignore next 4 */
abort() {
this.abortController?.abort(), this.abortController = null;
}
}
async function Rt({
editorId: r,
saveDebounceMs: t
}) {
const { Plugin: e } = await import("ckeditor5");
return class extends e {
/**
* The input element to synchronize with.
*/
input = null;
/**
* The form element reference for cleanup.
*/
form = null;
/**
* The name of the plugin.
*/
static get pluginName() {
return "SyncEditorWithInput";
}
/**
* Initializes the plugin.
*/
afterInit() {
const { editor: n } = this;
this.input = document.getElementById(`${r}_input`), this.input && (n.model.document.on("change:data", I(t, () => this.sync())), n.once("ready", this.sync), this.form = this.input.closest("form"), this.form?.addEventListener("submit", this.sync));
}
/**
* Synchronizes the editor's content with the input field.
*/
sync = () => {
const n = this.editor.getData();
this.input.value = n, this.input.dispatchEvent(new Event("input", { bubbles: !0 }));
};
/**
* Destroys the plugin.
*/
destroy() {
this.form && this.form.removeEventListener("submit", this.sync), this.input = null, this.form = null;
}
};
}
const U = /* @__PURE__ */ Symbol("suppress-phoenix-sync");
async function Vt(r) {
const { Plugin: t } = await import("ckeditor5"), { editorId: e, saveDebounceMs: a, events: n, pushEvent: i, handleEvent: s } = r;
return class extends t {
/**
* The name of the plugin.
*/
static get pluginName() {
return "SyncEditorWithPhoenix";
}
/**
* Initializes the plugin.
*/
init() {
const { editor: o } = this;
n.change && this.setupTypingContentPush(), n.blur && this.setupEventPush("blur"), n.focus && this.setupEventPush("focus"), n.ready && this.editor.once("ready", () => {
i("ckeditor5:ready", {
editorId: e,
data: M(o)
});
}), s("ckeditor5:set-data", ({ editorId: u, data: l }) => {
(st(u) || u === e) && o.setData(l);
});
}
/**
* Setups the content push event for the editor.
*/
setupTypingContentPush() {
const { editor: o } = this;
let u = null, l = !1;
const d = () => {
if (l)
return;
const m = M(o);
(!u || !ct(u, m)) && (i(
"ckeditor5:change",
{
editorId: e,
data: m
}
), u = m);
}, w = I(a, d);
o.model.document.on("change:data", I(10, (m) => {
if (Ut(m)) {
u = null;
return;
}
o.ui.focusTracker.isFocused ? w() : d();
})), o.once("ready", d), o.once("destroy", () => {
l = !0;
});
}
/**
* Setups the event push for the editor.
*/
setupEventPush(o) {
const { editor: u } = this, l = () => {
const { isFocused: d } = u.ui.focusTracker;
(d ? "focus" : "blur") === o && i(
`ckeditor5:${o}`,
{
editorId: e,
data: M(u)
}
);
};
u.ui.focusTracker.on("change:isFocused", l);
}
};
}
function M(r) {
return r.model.document.getRootNames().reduce((e, a) => (e[a] = r.getData({ rootName: a }), e), /* @__PURE__ */ Object.create({}));
}
function Ut(r) {
const t = r[U];
return delete r[U], !!t;
}
function jt(r) {
let t = !1;
const e = (a) => {
t || (a[U] = !0);
};
return r.model.document.once("change:data", e, { priority: "highest" }), () => {
t = !0, r.model.document.off("change:data", e);
};
}
class tt {
/**
* The DOM element being observed for attribute changes.
*/
el;
/**
* The name of the specific root in a multi-root editor setup.
*/
rootName;
/**
* The name of the HTML attribute storing the value.
*/
valueAttrName;
/**
* The name of the HTML attribute storing the root attributes.
*/
rootAttrsAttrName;
/**
* A flag indicating whether the sentinel has been destroyed, used to prevent operations after cleanup.
*/
isDestroyed = !1;
/**
* Cleanup callbacks to be executed when the sentinel is destroyed.
*/
cleanupCallbacks = [];
/**
* The editor instance.
*/
editor;
/**
* When the editor is focused and the value attribute changes, we want to wait until it blurs to
* avoid disrupting the user while typing. This variable holds the pending value that should be applied
* once the editor blurs. It is set to null when there is no pending value or when the user makes changes in the editor,
* indicating that the pending value should be discarded.
*/
pendingValue = null;
/**
* Cache the previous value to avoid reacting to attribute changes that don't actually change the value.
* This can happen when the parent LiveView re-renders and sets the same value again, which would otherwise cause an
* unnecessary update in the editor.
*/
previousValue = null;
/**
* Updater created once the editor is ready. Tracks which root attributes
* were applied by this sentinel so it can clean them up independently of
* other consumers.
*/
attrsUpdater = null;
/**
* When the hook is mounted, we will wait for the editor to be registered and then set the initial value of the root.
* Accepts an options object to configure element, identifiers, and custom attribute names.
*/
constructor({
el: t,
editor: e,
rootName: a,
valueAttrName: n = "data-cke-value",
rootAttrsAttrName: i = "data-cke-root-attrs"
}) {
this.el = t, this.editor = e, this.rootName = a, this.valueAttrName = n, this.rootAttrsAttrName = i;
const { value: s } = this.attrs;
this.previousValue = s, this.setupSyncHandlers(e, this.rootName);
}
/**
* Helper to read and parse attributes from the element.
* It uses dynamically provided attribute names.
*/
get attrs() {
return {
rootAttributes: ot(this.el.getAttribute(this.rootAttrsAttrName)),
value: this.el.getAttribute(this.valueAttrName)
};
}
/**
* When the value attribute changes, we want to update the editor root value.
* However, if the editor is focused, we want to wait until it blurs to avoid disrupting the user while typing.
*/
async updated() {
const { editor: t } = this, { value: e, rootAttributes: a } = this.attrs;
if (!t || t.state === "destroyed" || this.isDestroyed)
return;
let n = () => {
};
t.model.enqueueChange({ isUndoable: !1 }, () => {
let i = this.attrsUpdater?.(a);
e !== this.previousValue && (this.previousValue = e, t.ui.focusTracker.isFocused ? this.pendingValue = e : (this.setRootValue(t, this.rootName, e), i = !0)), i && (n = jt(t));
}), n();
}
/**
* Sets up focus-aware sync handlers on the editor.
* Registers cleanup via onBeforeDestroy.
*/
setupSyncHandlers(t, e) {
this.attrsUpdater = St(t, e), this.attrsUpdater(this.attrs.rootAttributes);
const a = () => {
this.pendingValue = null;
}, n = () => {
!t.ui.focusTracker.isFocused && this.pendingValue !== null && (this.setRootValue(t, e, this.pendingValue), this.pendingValue = null);
};
t.model.document.on("change:data", a), t.ui.focusTracker.on("change:isFocused", n), this.cleanupCallbacks.push(() => {
t.model.document.off("change:data", a), t.ui.focusTracker.off("change:isFocused", n);
});
}
/**
* Sets the value of a specific root in the editor.
*/
setRootValue(t, e, a) {
t.getData({ rootName: e }) !== a && t.setData({ [e]: a });
}
/**
* Disconnects the observer and cleans up editor event listeners.
* This should be called manually when the element is removed from the DOM.
*/
destroy() {
this.isDestroyed = !0, this.cleanupCallbacks.forEach((t) => t()), this.cleanupCallbacks = [];
}
}
class h extends K {
static the = new h();
}
class $t extends S {
/**
* The sentinel instance responsible for tracking and updating root values and attributes
* for single-root editors.
*/
sentinel = null;
/**
* Attributes for the editor instance.
*/
get attrs() {
const { el: t } = this, e = t.getAttribute.bind(t), a = t.hasAttribute.bind(t), n = {
editorId: e("id"),
contextId: e("data-cke-context-id"),
preset: kt(t),
editableHeight: q(e("data-cke-editable-height")),
watchdog: a("data-cke-watchdog"),
events: {
change: a("data-cke-change-event"),
blur: a("data-cke-blur-event"),
focus: a("data-cke-focus-event"),
ready: a("data-cke-ready-event")
},
saveDebounceMs: q(e("data-cke-save-debounce-ms")) ?? 400,
language: {
ui: e("data-cke-language") || "en",
content: e("data-cke-content-language") || "en"
}
};
return Object.defineProperty(this, "attrs", {
value: n,
writable: !1,
configurable: !1,
enumerable: !0
}), n;
}
/**
* Mounts the editor component.
*/
async mounted() {
const { editorId: t } = this.attrs;
h.the.resetErrors(t);
try {
const e = await this.createEditor();
if (this.isBeingDestroyed())
return;
const a = h.the.mountEffect(t, (n) => (n.once("destroy", () => {
h.the.unregister(t, !1);
}, { priority: "highest" }), this.sentinel = new tt({
editor: n,
el: this.el,
rootName: "main",
valueAttrName: "data-cke-initial-value",
rootAttrsAttrName: "data-cke-root-attrs"
}), () => {
this.sentinel?.destroy(), this.sentinel = null;
}));
this.onBeforeDestroy(async () => {
h.the.unregister(t), a();
const n = gt(e), i = Pt(e);
n ? n.state !== "unavailable" && await n.context.remove(n.editorContextId) : i ? await i.destroy() : await e.destroy();
}), h.the.register(t, e);
} catch (e) {
console.error(e), h.the.error(t, e);
}
return this;
}
/**
* Watch attributes changes and sync value if something changed.
*/
async updated() {
this.sentinel?.updated();
}
/**
* Destroys editor component.
*/
async destroyed() {
this.el.style.display = "none";
}
/**
* Creates the CKEditor instance.
*/
async createEditor() {
const {
preset: t,
editorId: e,
contextId: a,
editableHeight: n,
events: i,
saveDebounceMs: s,
language: c,
watchdog: o
} = this.attrs, { customTranslations: u, type: l, license: d, config: { plugins: w, ...m } } = t, E = await bt(l), b = await (a ? y.the.waitFor(a) : xt(this.el)), P = async () => {
const { loadedPlugins: f, hasPremium: A } = await J(w);
T(l) && f.push(
await Rt({
editorId: e,
saveDebounceMs: s
})
), f.push(
...await Promise.all([
Vt(
{
editorId: e,
saveDebounceMs: s,
events: i,
pushEvent: this.pushEvent.bind(this),
handleEvent: this.handleEvent.bind(this)
}
),
Nt()
])
);
const k = [
...await G(c, A),
X(u?.dictionary || {})
].filter((C) => !Y(C));
let v = L(e);
T(l) && (v = v.main || "");
let g = Q(e);
if (!(g instanceof HTMLElement) && !("main" in g)) {
const C = l === "decoupled" ? ["main"] : Object.keys(v);
et(g, C) || (g = await _t(e, C), v = L(e));
}
T(l) && "main" in g && (g = g.main);
let p = {
...m,
licenseKey: d.key,
plugins: f,
language: c,
...k.length && {
translations: k
}
};
p = x(p), p = D([...k].reverse(), c.ui, p), p = mt(E, g, p), p = dt(v, p);
const z = await (async () => b ? (await wt({
context: b,
creator: E,
config: p
})).editor : E.create(p))();
return T(l) && n && vt(z, n), z;
};
if (o && !b) {
const f = await Ct(P, t.watchdog);
return f.on("error", (A, { causesRestart: _ }) => {
if (_) {
const k = h.the.getItem(e);
k && (ft(k), h.the.unregister(e));
}
}), f.on("restart", () => {
const A = f.editor;
h.the.register(e, A);
}), await f.create({}), f.editor;
}
return P();
}
}
function et(r, t) {
return t.every((e) => r[e]);
}
async function _t(r, t) {
return lt(
() => {
const e = Q(r);
if (!et(e, t))
throw new Error(
`It looks like not all required root elements are present yet.
* If you want to wait for them, ensure they are registered before editor initialization.
* If you want lazy initialize roots, consider removing root values from the \`initialData\` config and assign initial data in editable components.
Missing roots: ${t.filter((a) => !e[a]).join(", ")}.`
);
return e;
},
{ timeOutAfter: 2e3, retryAfter: 100 }
);
}
const zt = N($t);
class Ht extends S {
/**
* The sentinel instance responsible for tracking and updating root values and attributes.
*/
sentinel = null;
/**
* Attributes for the editable instance.
*/
get attrs() {
const t = {
editableId: this.el.getAttribute("id"),
editorId: this.el.getAttribute("data-cke-editor-id") || null,
rootName: this.el.getAttribute("data-cke-editable-root-name"),
initialValue: this.el.getAttribute("data-cke-editable-initial-value") || ""
};
return Object.defineProperty(this, "attrs", {
value: t,
writable: !1,
configurable: !1,
enumerable: !0
}), t;
}
/**
* Mounts the editable component.
*/
mounted() {
const { editableId: t, editorId: e, rootName: a, initialValue: n } = this.attrs, i = h.the.mountEffect(e, (s) => {
const c = this.el.querySelector("[data-cke-editable-content]");
if (this.isBeingDestroyed())
return;
const o = this.el.querySelector(`#${t}_input`);
if (B(s) && !s.model.document.getRoot(a)) {
const { ui: l, editing: d } = s;
s.addRoot(a, {
isUndoable: !1,
initialData: n
});
const w = l.view.createEditable(a, c);
l.addEditable(w), d.view.forceRender();
}
this.sentinel = new tt({
el: this.el,
editor: s,
rootName: a,
valueAttrName: "data-cke-editable-initial-value",
rootAttrsAttrName: "data-cke-editable-root-attrs"
});
const u = o ? Bt(o, s, a) : null;
return () => {
if (u?.(), this.sentinel?.destroy(), this.sentinel = null, s.state !== "destroyed") {
const l = s.model.document.getRoot(a);
l && B(s) && (s.ui.view.editables[a] && s.detachEditable(l), l.isAttached() && s.detachRoot(a, !1));
}
};
});
this.onBeforeDestroy(() => {
this.el.style.display = "none", i();
});
}
/**
* Watch attributes changes and sync value if something changed.
*/
async updated() {
this.sentinel?.updated();
}
}
const qt = N(Ht);
function Bt(r, t, e) {
const a = () => {
r.value = t.getData({ rootName: e });
}, n = I(200, a);
return t.model.document.on("change:data", n), a(), () => {
t.model.document.off("change:data", n);
};
}
class Ft extends S {
/**
* Attributes for the editable instance.
*/
get attrs() {
const t = {
editorId: this.el.getAttribute("data-cke-editor-id") || null,
name: this.el.getAttribute("data-cke-ui-part-name")
};
return Object.defineProperty(this, "attrs", {
value: t,
writable: !1,
configurable: !1,
enumerable: !0
}), t;
}
/**
* Mounts the UI part component.
*/
mounted() {
const { editorId: t, name: e } = this.attrs, a = h.the.mountEffect(t, (n) => {
if (this.isBeingDestroyed())
return;
const { ui: i } = n, s = Lt(e), c = i.view[s];
if (!c) {
console.error(`Unknown UI part name: "${e}". Supported names are "toolbar" and "menubar".`);
return;
}
return this.el.appendChild(c.element), () => {
this.el.innerHTML = "";
};
});
this.onBeforeDestroy(() => {
this.el.style.display = "none", a();
});
}
}
function Lt(r) {
switch (r) {
case "toolbar":
return "toolbar";
case "menubar":
return "menuBarView";
default:
return null;
}
}
const Wt = N(Ft), Gt = {
CKEditor5: zt,
CKEditable: qt,
CKUIPart: Wt,
CKContext: Dt
};
export {
y as ContextsRegistry,
$ as CustomEditorPluginsRegistry,
h as EditorsRegistry,
Gt as Hooks,
gt as unwrapEditorContext,
Pt as unwrapEditorWatchdog
};
//# sourceMappingURL=index.mjs.map