(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".ce-hint--align-start{text-align:left}.ce-hint--align-center{text-align:center}.ce-hint__description{opacity:.6;margin-top:3px}")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); var Ce = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {}; function Ke(n) { return n && n.__esModule && Object.prototype.hasOwnProperty.call(n, "default") ? n.default : n; } function Xn(n) { if (n.__esModule) return n; var e = n.default; if (typeof e == "function") { var t = function o() { return this instanceof o ? Reflect.construct(e, arguments, this.constructor) : e.apply(this, arguments); }; t.prototype = e.prototype; } else t = {}; return Object.defineProperty(t, "__esModule", { value: !0 }), Object.keys(n).forEach(function(o) { var i = Object.getOwnPropertyDescriptor(n, o); Object.defineProperty(t, o, i.get ? i : { enumerable: !0, get: function() { return n[o]; } }); }), t; } function ot() { } Object.assign(ot, { default: ot, register: ot, revert: function() { }, __esModule: !0 }); Element.prototype.matches || (Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(n) { const e = (this.document || this.ownerDocument).querySelectorAll(n); let t = e.length; for (; --t >= 0 && e.item(t) !== this; ) ; return t > -1; }); Element.prototype.closest || (Element.prototype.closest = function(n) { let e = this; if (!document.documentElement.contains(e)) return null; do { if (e.matches(n)) return e; e = e.parentElement || e.parentNode; } while (e !== null); return null; }); Element.prototype.prepend || (Element.prototype.prepend = function(e) { const t = document.createDocumentFragment(); Array.isArray(e) || (e = [e]), e.forEach((o) => { const i = o instanceof Node; t.appendChild(i ? o : document.createTextNode(o)); }), this.insertBefore(t, this.firstChild); }); Element.prototype.scrollIntoViewIfNeeded || (Element.prototype.scrollIntoViewIfNeeded = function(n) { n = arguments.length === 0 ? !0 : !!n; const e = this.parentNode, t = window.getComputedStyle(e, null), o = parseInt(t.getPropertyValue("border-top-width")), i = parseInt(t.getPropertyValue("border-left-width")), s = this.offsetTop - e.offsetTop < e.scrollTop, r = this.offsetTop - e.offsetTop + this.clientHeight - o > e.scrollTop + e.clientHeight, a = this.offsetLeft - e.offsetLeft < e.scrollLeft, l = this.offsetLeft - e.offsetLeft + this.clientWidth - i > e.scrollLeft + e.clientWidth, c = s && !r; (s || r) && n && (e.scrollTop = this.offsetTop - e.offsetTop - e.clientHeight / 2 - o + this.clientHeight / 2), (a || l) && n && (e.scrollLeft = this.offsetLeft - e.offsetLeft - e.clientWidth / 2 - i + this.clientWidth / 2), (s || r || a || l) && !n && this.scrollIntoView(c); }); window.requestIdleCallback = window.requestIdleCallback || function(n) { const e = Date.now(); return setTimeout(function() { n({ didTimeout: !1, timeRemaining: function() { return Math.max(0, 50 - (Date.now() - e)); } }); }, 1); }; window.cancelIdleCallback = window.cancelIdleCallback || function(n) { clearTimeout(n); }; let Vn = (n = 21) => crypto.getRandomValues(new Uint8Array(n)).reduce((e, t) => (t &= 63, t < 36 ? e += t.toString(36) : t < 62 ? e += (t - 26).toString(36).toUpperCase() : t > 62 ? e += "-" : e += "_", e), ""); var Lo = /* @__PURE__ */ ((n) => (n.VERBOSE = "VERBOSE", n.INFO = "INFO", n.WARN = "WARN", n.ERROR = "ERROR", n))(Lo || {}); const y = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91, SLASH: 191 }, qn = { LEFT: 0, WHEEL: 1, RIGHT: 2, BACKWARD: 3, FORWARD: 4 }; function Ie(n, e, t = "log", o, i = "color: inherit") { if (!("console" in window) || !window.console[t]) return; const s = ["info", "log", "warn", "error"].includes(t), r = []; switch (Ie.logLevel) { case "ERROR": if (t !== "error") return; break; case "WARN": if (!["error", "warn"].includes(t)) return; break; case "INFO": if (!s || n) return; break; } o && r.push(o); const a = "Editor.js 2.31.0-rc.8", l = `line-height: 1em; color: #006FEA; display: inline-block; font-size: 11px; line-height: 1em; background-color: #fff; padding: 4px 9px; border-radius: 30px; border: 1px solid rgba(56, 138, 229, 0.16); margin: 4px 5px 4px 0;`; n && (s ? (r.unshift(l, i), e = `%c${a}%c ${e}`) : e = `( ${a} )${e}`); try { s ? o ? console[t](`${e} %o`, ...r) : console[t](e, ...r) : console[t](e); } catch { } } Ie.logLevel = "VERBOSE"; function Zn(n) { Ie.logLevel = n; } const S = Ie.bind(window, !1), X = Ie.bind(window, !0); function le(n) { return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); } function A(n) { return le(n) === "function" || le(n) === "asyncfunction"; } function D(n) { return le(n) === "object"; } function te(n) { return le(n) === "string"; } function Gn(n) { return le(n) === "boolean"; } function yo(n) { return le(n) === "number"; } function wo(n) { return le(n) === "undefined"; } function V(n) { return n ? Object.keys(n).length === 0 && n.constructor === Object : !0; } function Po(n) { return n > 47 && n < 58 || // number keys n === 32 || n === 13 || // Space bar & return key(s) n === 229 || // processing key input for certain languages — Chinese, Japanese, etc. n > 64 && n < 91 || // letter keys n > 95 && n < 112 || // Numpad keys n > 185 && n < 193 || // ;=,-./` (in order) n > 218 && n < 223; } async function Qn(n, e = () => { }, t = () => { }) { async function o(i, s, r) { try { await i.function(i.data), await s(wo(i.data) ? {} : i.data); } catch { r(wo(i.data) ? {} : i.data); } } return n.reduce(async (i, s) => (await i, o(s, e, t)), Promise.resolve()); } function No(n) { return Array.prototype.slice.call(n); } function Fe(n, e) { return function() { const t = this, o = arguments; window.setTimeout(() => n.apply(t, o), e); }; } function Jn(n) { return n.name.split(".").pop(); } function ei(n) { return /^[-\w]+\/([-+\w]+|\*)$/.test(n); } function Eo(n, e, t) { let o; return (...i) => { const s = this, r = () => { o = null, t || n.apply(s, i); }, a = t && !o; window.clearTimeout(o), o = window.setTimeout(r, e), a && n.apply(s, i); }; } function dt(n, e, t = void 0) { let o, i, s, r = null, a = 0; t || (t = {}); const l = function() { a = t.leading === !1 ? 0 : Date.now(), r = null, s = n.apply(o, i), r || (o = i = null); }; return function() { const c = Date.now(); !a && t.leading === !1 && (a = c); const u = e - (c - a); return o = this, i = arguments, u <= 0 || u > e ? (r && (clearTimeout(r), r = null), a = c, s = n.apply(o, i), r || (o = i = null)) : !r && t.trailing !== !1 && (r = setTimeout(l, u)), s; }; } function ti() { const n = { win: !1, mac: !1, x11: !1, linux: !1 }, e = Object.keys(n).find((t) => window.navigator.appVersion.toLowerCase().indexOf(t) !== -1); return e && (n[e] = !0), n; } function je(n) { return n[0].toUpperCase() + n.slice(1); } function ut(n, ...e) { if (!e.length) return n; const t = e.shift(); if (D(n) && D(t)) for (const o in t) D(t[o]) ? (n[o] || Object.assign(n, { [o]: {} }), ut(n[o], t[o])) : Object.assign(n, { [o]: t[o] }); return ut(n, ...e); } function vt(n) { const e = ti(); return n = n.replace(/shift/gi, "⇧").replace(/backspace/gi, "⌫").replace(/enter/gi, "⏎").replace(/up/gi, "↑").replace(/left/gi, "→").replace(/down/gi, "↓").replace(/right/gi, "←").replace(/escape/gi, "⎋").replace(/insert/gi, "Ins").replace(/delete/gi, "␡").replace(/\+/gi, " + "), e.mac ? n = n.replace(/ctrl|cmd/gi, "⌘").replace(/alt/gi, "⌥") : n = n.replace(/cmd/gi, "Ctrl").replace(/windows/gi, "WIN"), n; } function oi(n) { try { return new URL(n).href; } catch { } return n.substring(0, 2) === "//" ? window.location.protocol + n : window.location.origin + n; } function ni() { return Vn(10); } function ii(n) { window.open(n, "_blank"); } function si(n = "") { return `${n}${Math.floor(Math.random() * 1e8).toString(16)}`; } function ht(n, e, t) { const o = `«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`; n && X(o, "warn"); } function me(n, e, t) { const o = t.value ? "value" : "get", i = t[o], s = `#${e}Cache`; if (t[o] = function(...r) { return this[s] === void 0 && (this[s] = i.apply(this, ...r)), this[s]; }, o === "get" && t.set) { const r = t.set; t.set = function(a) { delete n[s], r.apply(this, a); }; } return t; } const Ro = 650; function be() { return window.matchMedia(`(max-width: ${Ro}px)`).matches; } const pt = typeof window < "u" && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1); function ri(n, e) { const t = Array.isArray(n) || D(n), o = Array.isArray(e) || D(e); return t || o ? JSON.stringify(n) === JSON.stringify(e) : n === e; } class d { /** * Check if passed tag has no closed tag * * @param {HTMLElement} tag - element to check * @returns {boolean} */ static isSingleTag(e) { return e.tagName && [ "AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "META", "PARAM", "SOURCE", "TRACK", "WBR" ].includes(e.tagName); } /** * Check if element is BR or WBR * * @param {HTMLElement} element - element to check * @returns {boolean} */ static isLineBreakTag(e) { return e && e.tagName && [ "BR", "WBR" ].includes(e.tagName); } /** * Helper for making Elements with class name and attributes * * @param {string} tagName - new Element tag name * @param {string[]|string} [classNames] - list or name of CSS class name(s) * @param {object} [attributes] - any attributes * @returns {HTMLElement} */ static make(e, t = null, o = {}) { const i = document.createElement(e); if (Array.isArray(t)) { const s = t.filter((r) => r !== void 0); i.classList.add(...s); } else t && i.classList.add(t); for (const s in o) Object.prototype.hasOwnProperty.call(o, s) && (i[s] = o[s]); return i; } /** * Creates Text Node with the passed content * * @param {string} content - text content * @returns {Text} */ static text(e) { return document.createTextNode(e); } /** * Append one or several elements to the parent * * @param {Element|DocumentFragment} parent - where to append * @param {Element|Element[]|DocumentFragment|Text|Text[]} elements - element or elements list */ static append(e, t) { Array.isArray(t) ? t.forEach((o) => e.appendChild(o)) : e.appendChild(t); } /** * Append element or a couple to the beginning of the parent elements * * @param {Element} parent - where to append * @param {Element|Element[]} elements - element or elements list */ static prepend(e, t) { Array.isArray(t) ? (t = t.reverse(), t.forEach((o) => e.prepend(o))) : e.prepend(t); } /** * Swap two elements in parent * * @param {HTMLElement} el1 - from * @param {HTMLElement} el2 - to * @deprecated */ static swap(e, t) { const o = document.createElement("div"), i = e.parentNode; i.insertBefore(o, e), i.insertBefore(e, t), i.insertBefore(t, o), i.removeChild(o); } /** * Selector Decorator * * Returns first match * * @param {Element} el - element we searching inside. Default - DOM Document * @param {string} selector - searching string * @returns {Element} */ static find(e = document, t) { return e.querySelector(t); } /** * Get Element by Id * * @param {string} id - id to find * @returns {HTMLElement | null} */ static get(e) { return document.getElementById(e); } /** * Selector Decorator. * * Returns all matches * * @param {Element|Document} el - element we searching inside. Default - DOM Document * @param {string} selector - searching string * @returns {NodeList} */ static findAll(e = document, t) { return e.querySelectorAll(t); } /** * Returns CSS selector for all text inputs */ static get allInputsSelector() { return "[contenteditable=true], textarea, input:not([type]), " + ["text", "password", "email", "number", "search", "tel", "url"].map((t) => `input[type="${t}"]`).join(", "); } /** * Find all contenteditable, textarea and editable input elements passed holder contains * * @param holder - element where to find inputs */ static findAllInputs(e) { return No(e.querySelectorAll(d.allInputsSelector)).reduce((t, o) => d.isNativeInput(o) || d.containsOnlyInlineElements(o) ? [...t, o] : [...t, ...d.getDeepestBlockElements(o)], []); } /** * Search for deepest node which is Leaf. * Leaf is the vertex that doesn't have any child nodes * * @description Method recursively goes throw the all Node until it finds the Leaf * @param {Node} node - root Node. From this vertex we start Deep-first search * {@link https://en.wikipedia.org/wiki/Depth-first_search} * @param {boolean} [atLast] - find last text node * @returns - it can be text Node or Element Node, so that caret will able to work with it * Can return null if node is Document or DocumentFragment, or node is not attached to the DOM */ static getDeepestNode(e, t = !1) { const o = t ? "lastChild" : "firstChild", i = t ? "previousSibling" : "nextSibling"; if (e && e.nodeType === Node.ELEMENT_NODE && e[o]) { let s = e[o]; if (d.isSingleTag(s) && !d.isNativeInput(s) && !d.isLineBreakTag(s)) if (s[i]) s = s[i]; else if (s.parentNode[i]) s = s.parentNode[i]; else return s.parentNode; return this.getDeepestNode(s, t); } return e; } /** * Check if object is DOM node * * @param {*} node - object to check * @returns {boolean} */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static isElement(e) { return yo(e) ? !1 : e && e.nodeType && e.nodeType === Node.ELEMENT_NODE; } /** * Check if object is DocumentFragment node * * @param {object} node - object to check * @returns {boolean} */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static isFragment(e) { return yo(e) ? !1 : e && e.nodeType && e.nodeType === Node.DOCUMENT_FRAGMENT_NODE; } /** * Check if passed element is contenteditable * * @param {HTMLElement} element - html element to check * @returns {boolean} */ static isContentEditable(e) { return e.contentEditable === "true"; } /** * Checks target if it is native input * * @param {*} target - HTML element or string * @returns {boolean} */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static isNativeInput(e) { const t = [ "INPUT", "TEXTAREA" ]; return e && e.tagName ? t.includes(e.tagName) : !1; } /** * Checks if we can set caret * * @param {HTMLElement} target - target to check * @returns {boolean} */ static canSetCaret(e) { let t = !0; if (d.isNativeInput(e)) switch (e.type) { case "file": case "checkbox": case "radio": case "hidden": case "submit": case "button": case "image": case "reset": t = !1; break; } else t = d.isContentEditable(e); return t; } /** * Checks node if it is empty * * @description Method checks simple Node without any childs for emptiness * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method * @param {Node} node - node to check * @param {string} [ignoreChars] - char or substring to treat as empty * @returns {boolean} true if it is empty */ static isNodeEmpty(e, t) { let o; return this.isSingleTag(e) && !this.isLineBreakTag(e) ? !1 : (this.isElement(e) && this.isNativeInput(e) ? o = e.value : o = e.textContent.replace("​", ""), t && (o = o.replace(new RegExp(t, "g"), "")), o.length === 0); } /** * checks node if it is doesn't have any child nodes * * @param {Node} node - node to check * @returns {boolean} */ static isLeaf(e) { return e ? e.childNodes.length === 0 : !1; } /** * breadth-first search (BFS) * {@link https://en.wikipedia.org/wiki/Breadth-first_search} * * @description Pushes to stack all DOM leafs and checks for emptiness * @param {Node} node - node to check * @param {string} [ignoreChars] - char or substring to treat as empty * @returns {boolean} */ static isEmpty(e, t) { const o = [e]; for (; o.length > 0; ) if (e = o.shift(), !!e) { if (this.isLeaf(e) && !this.isNodeEmpty(e, t)) return !1; e.childNodes && o.push(...Array.from(e.childNodes)); } return !0; } /** * Check if string contains html elements * * @param {string} str - string to check * @returns {boolean} */ static isHTMLString(e) { const t = d.make("div"); return t.innerHTML = e, t.childElementCount > 0; } /** * Return length of node`s text content * * @param {Node} node - node with content * @returns {number} */ static getContentLength(e) { return d.isNativeInput(e) ? e.value.length : e.nodeType === Node.TEXT_NODE ? e.length : e.textContent.length; } /** * Return array of names of block html elements * * @returns {string[]} */ static get blockElements() { return [ "address", "article", "aside", "blockquote", "canvas", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "nav", "noscript", "ol", "output", "p", "pre", "ruby", "section", "table", "tbody", "thead", "tr", "tfoot", "ul", "video" ]; } /** * Check if passed content includes only inline elements * * @param {string|HTMLElement} data - element or html string * @returns {boolean} */ static containsOnlyInlineElements(e) { let t; te(e) ? (t = document.createElement("div"), t.innerHTML = e) : t = e; const o = (i) => !d.blockElements.includes(i.tagName.toLowerCase()) && Array.from(i.children).every(o); return Array.from(t.children).every(o); } /** * Find and return all block elements in the passed parent (including subtree) * * @param {HTMLElement} parent - root element * @returns {HTMLElement[]} */ static getDeepestBlockElements(e) { return d.containsOnlyInlineElements(e) ? [e] : Array.from(e.children).reduce((t, o) => [...t, ...d.getDeepestBlockElements(o)], []); } /** * Helper for get holder from {string} or return HTMLElement * * @param {string | HTMLElement} element - holder's id or holder's HTML Element * @returns {HTMLElement} */ static getHolder(e) { return te(e) ? document.getElementById(e) : e; } /** * Returns true if element is anchor (is A tag) * * @param {Element} element - element to check * @returns {boolean} */ static isAnchor(e) { return e.tagName.toLowerCase() === "a"; } /** * Return element's offset related to the document * * @todo handle case when editor initialized in scrollable popup * @param el - element to compute offset */ static offset(e) { const t = e.getBoundingClientRect(), o = window.pageXOffset || document.documentElement.scrollLeft, i = window.pageYOffset || document.documentElement.scrollTop, s = t.top + i, r = t.left + o; return { top: s, left: r, bottom: s + t.height, right: r + t.width }; } } function ai(n) { return !/[^\t\n\r ]/.test(n); } function li(n) { const e = window.getComputedStyle(n), t = parseFloat(e.fontSize), o = parseFloat(e.lineHeight) || t * 1.2, i = parseFloat(e.paddingTop), s = parseFloat(e.borderTopWidth), r = parseFloat(e.marginTop), a = t * 0.8, l = (o - t) / 2; return r + s + i + l + a; } function Do(n) { n.dataset.empty = d.isEmpty(n) ? "true" : "false"; } const ci = { blockTunes: { toggler: { "Click to tune": "", "or drag to move": "" } }, inlineToolbar: { converter: { "Convert to": "" } }, toolbar: { toolbox: { Add: "" } }, popover: { Filter: "", "Nothing found": "", "Convert to": "" } }, di = { Text: "", Link: "", Bold: "", Italic: "" }, ui = { link: { "Add a link": "" }, stub: { "The block can not be displayed correctly.": "" } }, hi = { delete: { Delete: "", "Click to delete": "" }, moveUp: { "Move up": "" }, moveDown: { "Move down": "" } }, Fo = { ui: ci, toolNames: di, tools: ui, blockTunes: hi }, jo = class he { /** * Type-safe translation for internal UI texts: * Perform translation of the string by namespace and a key * * @example I18n.ui(I18nInternalNS.ui.blockTunes.toggler, 'Click to tune') * @param internalNamespace - path to translated string in dictionary * @param dictKey - dictionary key. Better to use default locale original text */ static ui(e, t) { return he._t(e, t); } /** * Translate for external strings that is not presented in default dictionary. * For example, for user-specified tool names * * @param namespace - path to translated string in dictionary * @param dictKey - dictionary key. Better to use default locale original text */ static t(e, t) { return he._t(e, t); } /** * Adjust module for using external dictionary * * @param dictionary - new messages list to override default */ static setDictionary(e) { he.currentDictionary = e; } /** * Perform translation both for internal and external namespaces * If there is no translation found, returns passed key as a translated message * * @param namespace - path to translated string in dictionary * @param dictKey - dictionary key. Better to use default locale original text */ static _t(e, t) { const o = he.getNamespace(e); return !o || !o[t] ? t : o[t]; } /** * Find messages section by namespace path * * @param namespace - path to section */ static getNamespace(e) { return e.split(".").reduce((o, i) => !o || !Object.keys(o).length ? {} : o[i], he.currentDictionary); } }; jo.currentDictionary = Fo; let z = jo; class Ho extends Error { } class Oe { constructor() { this.subscribers = {}; } /** * Subscribe any event on callback * * @param eventName - event name * @param callback - subscriber */ on(e, t) { e in this.subscribers || (this.subscribers[e] = []), this.subscribers[e].push(t); } /** * Subscribe any event on callback. Callback will be called once and be removed from subscribers array after call. * * @param eventName - event name * @param callback - subscriber */ once(e, t) { e in this.subscribers || (this.subscribers[e] = []); const o = (i) => { const s = t(i), r = this.subscribers[e].indexOf(o); return r !== -1 && this.subscribers[e].splice(r, 1), s; }; this.subscribers[e].push(o); } /** * Emit callbacks with passed data * * @param eventName - event name * @param data - subscribers get this data when they were fired */ emit(e, t) { V(this.subscribers) || !this.subscribers[e] || this.subscribers[e].reduce((o, i) => { const s = i(o); return s !== void 0 ? s : o; }, t); } /** * Unsubscribe callback from event * * @param eventName - event name * @param callback - event handler */ off(e, t) { if (this.subscribers[e] === void 0) { console.warn(`EventDispatcher .off(): there is no subscribers for event "${e.toString()}". Probably, .off() called before .on()`); return; } for (let o = 0; o < this.subscribers[e].length; o++) if (this.subscribers[e][o] === t) { delete this.subscribers[e][o]; break; } } /** * Destroyer * clears subscribers list */ destroy() { this.subscribers = {}; } } function J(n) { Object.setPrototypeOf(this, { /** * Block id * * @returns {string} */ get id() { return n.id; }, /** * Tool name * * @returns {string} */ get name() { return n.name; }, /** * Tool config passed on Editor's initialization * * @returns {ToolConfig} */ get config() { return n.config; }, /** * .ce-block element, that wraps plugin contents * * @returns {HTMLElement} */ get holder() { return n.holder; }, /** * True if Block content is empty * * @returns {boolean} */ get isEmpty() { return n.isEmpty; }, /** * True if Block is selected with Cross-Block selection * * @returns {boolean} */ get selected() { return n.selected; }, /** * Set Block's stretch state * * @param {boolean} state — state to set */ set stretched(t) { n.stretched = t; }, /** * True if Block is stretched * * @returns {boolean} */ get stretched() { return n.stretched; }, /** * True if Block has inputs to be focused */ get focusable() { return n.focusable; }, /** * Call Tool method with errors handler under-the-hood * * @param {string} methodName - method to call * @param {object} param - object with parameters * @returns {unknown} */ call(t, o) { return n.call(t, o); }, /** * Save Block content * * @returns {Promise} */ save() { return n.save(); }, /** * Validate Block data * * @param {BlockToolData} data - data to validate * @returns {Promise} */ validate(t) { return n.validate(t); }, /** * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback * Can be useful for block changes invisible for editor core. */ dispatchChange() { n.dispatchChange(); }, /** * Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3") * This method returns the entry that is related to the Block (depended on the Block data) */ getActiveToolboxEntry() { return n.getActiveToolboxEntry(); } }); } class _e { constructor() { this.allListeners = []; } /** * Assigns event listener on element and returns unique identifier * * @param {EventTarget} element - DOM element that needs to be listened * @param {string} eventType - event type * @param {Function} handler - method that will be fired on event * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} */ on(e, t, o, i = !1) { const s = si("l"), r = { id: s, element: e, eventType: t, handler: o, options: i }; if (!this.findOne(e, t, o)) return this.allListeners.push(r), e.addEventListener(t, o, i), s; } /** * Removes event listener from element * * @param {EventTarget} element - DOM element that we removing listener * @param {string} eventType - event type * @param {Function} handler - remove handler, if element listens several handlers on the same event type * @param {boolean|AddEventListenerOptions} options - useCapture or {capture, passive, once} */ off(e, t, o, i) { const s = this.findAll(e, t, o); s.forEach((r, a) => { const l = this.allListeners.indexOf(s[a]); l > -1 && (this.allListeners.splice(l, 1), r.element.removeEventListener(r.eventType, r.handler, r.options)); }); } /** * Removes listener by id * * @param {string} id - listener identifier */ offById(e) { const t = this.findById(e); t && t.element.removeEventListener(t.eventType, t.handler, t.options); } /** * Finds and returns first listener by passed params * * @param {EventTarget} element - event target * @param {string} [eventType] - event type * @param {Function} [handler] - event handler * @returns {ListenerData|null} */ findOne(e, t, o) { const i = this.findAll(e, t, o); return i.length > 0 ? i[0] : null; } /** * Return all stored listeners by passed params * * @param {EventTarget} element - event target * @param {string} eventType - event type * @param {Function} handler - event handler * @returns {ListenerData[]} */ findAll(e, t, o) { let i; const s = e ? this.findByEventTarget(e) : []; return e && t && o ? i = s.filter((r) => r.eventType === t && r.handler === o) : e && t ? i = s.filter((r) => r.eventType === t) : i = s, i; } /** * Removes all listeners */ removeAll() { this.allListeners.map((e) => { e.element.removeEventListener(e.eventType, e.handler, e.options); }), this.allListeners = []; } /** * Module cleanup on destruction */ destroy() { this.removeAll(); } /** * Search method: looks for listener by passed element * * @param {EventTarget} element - searching element * @returns {Array} listeners that found on element */ findByEventTarget(e) { return this.allListeners.filter((t) => { if (t.element === e) return t; }); } /** * Search method: looks for listener by passed event type * * @param {string} eventType - event type * @returns {ListenerData[]} listeners that found on element */ findByType(e) { return this.allListeners.filter((t) => { if (t.eventType === e) return t; }); } /** * Search method: looks for listener by passed handler * * @param {Function} handler - event handler * @returns {ListenerData[]} listeners that found on element */ findByHandler(e) { return this.allListeners.filter((t) => { if (t.handler === e) return t; }); } /** * Returns listener data found by id * * @param {string} id - listener identifier * @returns {ListenerData} */ findById(e) { return this.allListeners.find((t) => t.id === e); } } class E { /** * @class * @param options - Module options * @param options.config - Module config * @param options.eventsDispatcher - Common event bus */ constructor({ config: e, eventsDispatcher: t }) { if (this.nodes = {}, this.listeners = new _e(), this.readOnlyMutableListeners = { /** * Assigns event listener on DOM element and pushes into special array that might be removed * * @param {EventTarget} element - DOM Element * @param {string} eventType - Event name * @param {Function} handler - Event handler * @param {boolean|AddEventListenerOptions} options - Listening options */ on: (o, i, s, r = !1) => { this.mutableListenerIds.push( this.listeners.on(o, i, s, r) ); }, /** * Clears all mutable listeners */ clearAll: () => { for (const o of this.mutableListenerIds) this.listeners.offById(o); this.mutableListenerIds = []; } }, this.mutableListenerIds = [], new.target === E) throw new TypeError("Constructors for abstract class Module are not allowed."); this.config = e, this.eventsDispatcher = t; } /** * Editor modules setter * * @param {EditorModules} Editor - Editor's Modules */ set state(e) { this.Editor = e; } /** * Remove memorized nodes */ removeAllNodes() { for (const e in this.nodes) { const t = this.nodes[e]; t instanceof HTMLElement && t.remove(); } } /** * Returns true if current direction is RTL (Right-To-Left) */ get isRtl() { return this.config.i18n.direction === "rtl"; } } class b { constructor() { this.instance = null, this.selection = null, this.savedSelectionRange = null, this.isFakeBackgroundEnabled = !1, this.commandBackground = "backColor", this.commandRemoveFormat = "removeFormat"; } /** * Editor styles * * @returns {{editorWrapper: string, editorZone: string}} */ static get CSS() { return { editorWrapper: "codex-editor", editorZone: "codex-editor__redactor" }; } /** * Returns selected anchor * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode} * * @returns {Node|null} */ static get anchorNode() { const e = window.getSelection(); return e ? e.anchorNode : null; } /** * Returns selected anchor element * * @returns {Element|null} */ static get anchorElement() { const e = window.getSelection(); if (!e) return null; const t = e.anchorNode; return t ? d.isElement(t) ? t : t.parentElement : null; } /** * Returns selection offset according to the anchor node * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset} * * @returns {number|null} */ static get anchorOffset() { const e = window.getSelection(); return e ? e.anchorOffset : null; } /** * Is current selection range collapsed * * @returns {boolean|null} */ static get isCollapsed() { const e = window.getSelection(); return e ? e.isCollapsed : null; } /** * Check current selection if it is at Editor's zone * * @returns {boolean} */ static get isAtEditor() { return this.isSelectionAtEditor(b.get()); } /** * Check if passed selection is at Editor's zone * * @param selection - Selection object to check */ static isSelectionAtEditor(e) { if (!e) return !1; let t = e.anchorNode || e.focusNode; t && t.nodeType === Node.TEXT_NODE && (t = t.parentNode); let o = null; return t && t instanceof Element && (o = t.closest(`.${b.CSS.editorZone}`)), o ? o.nodeType === Node.ELEMENT_NODE : !1; } /** * Check if passed range at Editor zone * * @param range - range to check */ static isRangeAtEditor(e) { if (!e) return; let t = e.startContainer; t && t.nodeType === Node.TEXT_NODE && (t = t.parentNode); let o = null; return t && t instanceof Element && (o = t.closest(`.${b.CSS.editorZone}`)), o ? o.nodeType === Node.ELEMENT_NODE : !1; } /** * Methods return boolean that true if selection exists on the page */ static get isSelectionExists() { return !!b.get().anchorNode; } /** * Return first range * * @returns {Range|null} */ static get range() { return this.getRangeFromSelection(this.get()); } /** * Returns range from passed Selection object * * @param selection - Selection object to get Range from */ static getRangeFromSelection(e) { return e && e.rangeCount ? e.getRangeAt(0) : null; } /** * Calculates position and size of selected text * * @returns {DOMRect | ClientRect} */ static get rect() { let e = document.selection, t, o = { x: 0, y: 0, width: 0, height: 0 }; if (e && e.type !== "Control") return e = e, t = e.createRange(), o.x = t.boundingLeft, o.y = t.boundingTop, o.width = t.boundingWidth, o.height = t.boundingHeight, o; if (!window.getSelection) return S("Method window.getSelection is not supported", "warn"), o; if (e = window.getSelection(), e.rangeCount === null || isNaN(e.rangeCount)) return S("Method SelectionUtils.rangeCount is not supported", "warn"), o; if (e.rangeCount === 0) return o; if (t = e.getRangeAt(0).cloneRange(), t.getBoundingClientRect && (o = t.getBoundingClientRect()), o.x === 0 && o.y === 0) { const i = document.createElement("span"); if (i.getBoundingClientRect) { i.appendChild(document.createTextNode("​")), t.insertNode(i), o = i.getBoundingClientRect(); const s = i.parentNode; s.removeChild(i), s.normalize(); } } return o; } /** * Returns selected text as String * * @returns {string} */ static get text() { return window.getSelection ? window.getSelection().toString() : ""; } /** * Returns window SelectionUtils * {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection} * * @returns {Selection} */ static get() { return window.getSelection(); } /** * Set focus to contenteditable or native input element * * @param element - element where to set focus * @param offset - offset of cursor */ static setCursor(e, t = 0) { const o = document.createRange(), i = window.getSelection(); return d.isNativeInput(e) ? d.canSetCaret(e) ? (e.focus(), e.selectionStart = e.selectionEnd = t, e.getBoundingClientRect()) : void 0 : (o.setStart(e, t), o.setEnd(e, t), i.removeAllRanges(), i.addRange(o), o.getBoundingClientRect()); } /** * Check if current range exists and belongs to container * * @param container - where range should be */ static isRangeInsideContainer(e) { const t = b.range; return t === null ? !1 : e.contains(t.startContainer); } /** * Adds fake cursor to the current range */ static addFakeCursor() { const e = b.range; if (e === null) return; const t = d.make("span", "codex-editor__fake-cursor"); t.dataset.mutationFree = "true", e.collapse(), e.insertNode(t); } /** * Check if passed element contains a fake cursor * * @param el - where to check */ static isFakeCursorInsideContainer(e) { return d.find(e, ".codex-editor__fake-cursor") !== null; } /** * Removes fake cursor from a container * * @param container - container to look for */ static removeFakeCursor(e = document.body) { const t = d.find(e, ".codex-editor__fake-cursor"); t && t.remove(); } /** * Removes fake background */ removeFakeBackground() { this.isFakeBackgroundEnabled && (this.isFakeBackgroundEnabled = !1, document.execCommand(this.commandRemoveFormat)); } /** * Sets fake background */ setFakeBackground() { document.execCommand(this.commandBackground, !1, "#a8d6ff"), this.isFakeBackgroundEnabled = !0; } /** * Save SelectionUtils's range */ save() { this.savedSelectionRange = b.range; } /** * Restore saved SelectionUtils's range */ restore() { if (!this.savedSelectionRange) return; const e = window.getSelection(); e.removeAllRanges(), e.addRange(this.savedSelectionRange); } /** * Clears saved selection */ clearSaved() { this.savedSelectionRange = null; } /** * Collapse current selection */ collapseToEnd() { const e = window.getSelection(), t = document.createRange(); t.selectNodeContents(e.focusNode), t.collapse(!1), e.removeAllRanges(), e.addRange(t); } /** * Looks ahead to find passed tag from current selection * * @param {string} tagName - tag to found * @param {string} [className] - tag's class name * @param {number} [searchDepth] - count of tags that can be included. For better performance. * @returns {HTMLElement|null} */ findParentTag(e, t, o = 10) { const i = window.getSelection(); let s = null; return !i || !i.anchorNode || !i.focusNode ? null : ([ /** the Node in which the selection begins */ i.anchorNode, /** the Node in which the selection ends */ i.focusNode ].forEach((a) => { let l = o; for (; l > 0 && a.parentNode && !(a.tagName === e && (s = a, t && a.classList && !a.classList.contains(t) && (s = null), s)); ) a = a.parentNode, l--; }), s); } /** * Expands selection range to the passed parent node * * @param {HTMLElement} element - element which contents should be selected */ expandToTag(e) { const t = window.getSelection(); t.removeAllRanges(); const o = document.createRange(); o.selectNodeContents(e), t.addRange(o); } } function pi(n, e) { const { type: t, target: o, addedNodes: i, removedNodes: s } = n; return n.type === "attributes" && n.attributeName === "data-empty" ? !1 : !!(e.contains(o) || t === "childList" && (Array.from(i).some((l) => l === e) || Array.from(s).some((l) => l === e))); } const ft = "redactor dom changed", $o = "block changed", zo = "fake cursor is about to be toggled", Uo = "fake cursor have been set", Te = "editor mobile layout toggled"; function gt(n, e) { if (!n.conversionConfig) return !1; const t = n.conversionConfig[e]; return A(t) || te(t); } function He(n, e) { return gt(n.tool, e); } function Wo(n, e) { return Object.entries(n).some(([t, o]) => e[t] && ri(e[t], o)); } async function Yo(n, e) { const o = (await n.save()).data, i = e.find((s) => s.name === n.name); return i !== void 0 && !gt(i, "export") ? [] : e.reduce((s, r) => { if (!gt(r, "import") || r.toolbox === void 0) return s; const a = r.toolbox.filter((l) => { if (V(l) || l.icon === void 0) return !1; if (l.data !== void 0) { if (Wo(l.data, o)) return !1; } else if (r.name === n.name) return !1; return !0; }); return s.push({ ...r, toolbox: a }), s; }, []); } function xo(n, e) { return n.mergeable ? n.name === e.name ? !0 : He(e, "export") && He(n, "import") : !1; } function fi(n, e) { const t = e == null ? void 0 : e.export; return A(t) ? t(n) : te(t) ? n[t] : (t !== void 0 && S("Conversion «export» property must be a string or function. String means key of saved data object to export. Function should export processed string to export."), ""); } function Bo(n, e, t) { const o = e == null ? void 0 : e.import; return A(o) ? o(n, t) : te(o) ? { [o]: n } : (o !== void 0 && S("Conversion «import» property must be a string or function. String means key of tool data to import. Function accepts a imported string and return composed tool data."), {}); } var _ = /* @__PURE__ */ ((n) => (n.Default = "default", n.Separator = "separator", n.Html = "html", n))(_ || {}), ee = /* @__PURE__ */ ((n) => (n.APPEND_CALLBACK = "appendCallback", n.RENDERED = "rendered", n.MOVED = "moved", n.UPDATED = "updated", n.REMOVED = "removed", n.ON_PASTE = "onPaste", n))(ee || {}); class R extends Oe { /** * @param options - block constructor options * @param [options.id] - block's id. Will be generated if omitted. * @param options.data - Tool's initial data * @param options.tool — block's tool * @param options.api - Editor API module for pass it to the Block Tunes * @param options.readOnly - Read-Only flag * @param [eventBus] - Editor common event bus. Allows to subscribe on some Editor events. Could be omitted when "virtual" Block is created. See BlocksAPI@composeBlockData. */ constructor({ id: e = ni(), data: t, tool: o, readOnly: i, tunesData: s }, r) { super(), this.cachedInputs = [], this.toolRenderedElement = null, this.tunesInstances = /* @__PURE__ */ new Map(), this.defaultTunesInstances = /* @__PURE__ */ new Map(), this.unavailableTunesData = {}, this.inputIndex = 0, this.editorEventBus = null, this.handleFocus = () => { this.dropInputsCache(), this.updateCurrentInput(); }, this.didMutated = (a = void 0) => { const l = a === void 0, c = a instanceof InputEvent; !l && !c && this.detectToolRootChange(a); let u; l || c ? u = !0 : u = !(a.length > 0 && a.every((p) => { const { addedNodes: g, removedNodes: f, target: v } = p; return [ ...Array.from(g), ...Array.from(f), v ].some((T) => (d.isElement(T) || (T = T.parentElement), T && T.closest('[data-mutation-free="true"]') !== null)); })), u && (this.dropInputsCache(), this.updateCurrentInput(), this.toggleInputsEmptyMark(), this.call( "updated" /* UPDATED */ ), this.emit("didMutated", this)); }, this.name = o.name, this.id = e, this.settings = o.settings, this.config = o.settings.config || {}, this.editorEventBus = r || null, this.blockAPI = new J(this), this.tool = o, this.toolInstance = o.create(t, this.blockAPI, i), this.tunes = o.tunes, this.composeTunes(s), this.holder = this.compose(), window.requestIdleCallback(() => { this.watchBlockMutations(), this.addInputEvents(), this.toggleInputsEmptyMark(); }); } /** * CSS classes for the Block * * @returns {{wrapper: string, content: string}} */ static get CSS() { return { wrapper: "ce-block", wrapperStretched: "ce-block--stretched", content: "ce-block__content", selected: "ce-block--selected", dropTarget: "ce-block--drop-target" }; } /** * Find and return all editable elements (contenteditable and native inputs) in the Tool HTML */ get inputs() { if (this.cachedInputs.length !== 0) return this.cachedInputs; const e = d.findAllInputs(this.holder); return this.inputIndex > e.length - 1 && (this.inputIndex = e.length - 1), this.cachedInputs = e, e; } /** * Return current Tool`s input * If Block doesn't contain inputs, return undefined */ get currentInput() { return this.inputs[this.inputIndex]; } /** * Set input index to the passed element * * @param element - HTML Element to set as current input */ set currentInput(e) { const t = this.inputs.findIndex((o) => o === e || o.contains(e)); t !== -1 && (this.inputIndex = t); } /** * Return first Tool`s input * If Block doesn't contain inputs, return undefined */ get firstInput() { return this.inputs[0]; } /** * Return first Tool`s input * If Block doesn't contain inputs, return undefined */ get lastInput() { const e = this.inputs; return e[e.length - 1]; } /** * Return next Tool`s input or undefined if it doesn't exist * If Block doesn't contain inputs, return undefined */ get nextInput() { return this.inputs[this.inputIndex + 1]; } /** * Return previous Tool`s input or undefined if it doesn't exist * If Block doesn't contain inputs, return undefined */ get previousInput() { return this.inputs[this.inputIndex - 1]; } /** * Get Block's JSON data * * @returns {object} */ get data() { return this.save().then((e) => e && !V(e.data) ? e.data : {}); } /** * Returns tool's sanitizer config * * @returns {object} */ get sanitize() { return this.tool.sanitizeConfig; } /** * is block mergeable * We plugin have merge function then we call it mergeable * * @returns {boolean} */ get mergeable() { return A(this.toolInstance.merge); } /** * If Block contains inputs, it is focusable */ get focusable() { return this.inputs.length !== 0; } /** * Check block for emptiness * * @returns {boolean} */ get isEmpty() { const e = d.isEmpty(this.pluginsContent, "/"), t = !this.hasMedia; return e && t; } /** * Check if block has a media content such as images, iframe and other * * @returns {boolean} */ get hasMedia() { const e = [ "img", "iframe", "video", "audio", "source", "input", "textarea", "twitterwidget" ]; return !!this.holder.querySelector(e.join(",")); } /** * Set selected state * We don't need to mark Block as Selected when it is empty * * @param {boolean} state - 'true' to select, 'false' to remove selection */ set selected(e) { var i, s; this.holder.classList.toggle(R.CSS.selected, e); const t = e === !0 && b.isRangeInsideContainer(this.holder), o = e === !1 && b.isFakeCursorInsideContainer(this.holder); (t || o) && ((i = this.editorEventBus) == null || i.emit(zo, { state: e }), t ? b.addFakeCursor() : b.removeFakeCursor(this.holder), (s = this.editorEventBus) == null || s.emit(Uo, { state: e })); } /** * Returns True if it is Selected * * @returns {boolean} */ get selected() { return this.holder.classList.contains(R.CSS.selected); } /** * Set stretched state * * @param {boolean} state - 'true' to enable, 'false' to disable stretched state */ set stretched(e) { this.holder.classList.toggle(R.CSS.wrapperStretched, e); } /** * Return Block's stretched state * * @returns {boolean} */ get stretched() { return this.holder.classList.contains(R.CSS.wrapperStretched); } /** * Toggle drop target state * * @param {boolean} state - 'true' if block is drop target, false otherwise */ set dropTarget(e) { this.holder.classList.toggle(R.CSS.dropTarget, e); } /** * Returns Plugins content * * @returns {HTMLElement} */ get pluginsContent() { return this.toolRenderedElement; } /** * Calls Tool's method * * Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function * * @param {string} methodName - method to call * @param {object} params - method argument */ call(e, t) { if (A(this.toolInstance[e])) { e === "appendCallback" && S( "`appendCallback` hook is deprecated and will be removed in the next major release. Use `rendered` hook instead", "warn" ); try { this.toolInstance[e].call(this.toolInstance, t); } catch (o) { S(`Error during '${e}' call: ${o.message}`, "error"); } } } /** * Call plugins merge method * * @param {BlockToolData} data - data to merge */ async mergeWith(e) { await this.toolInstance.merge(e); } /** * Extracts data from Block * Groups Tool's save processing time * * @returns {object} */ async save() { const e = await this.toolInstance.save(this.pluginsContent), t = this.unavailableTunesData; [ ...this.tunesInstances.entries(), ...this.defaultTunesInstances.entries() ].forEach(([s, r]) => { if (A(r.save)) try { t[s] = r.save(); } catch (a) { S(`Tune ${r.constructor.name} save method throws an Error %o`, "warn", a); } }); const o = window.performance.now(); let i; return Promise.resolve(e).then((s) => (i = window.performance.now(), { id: this.id, tool: this.name, data: s, tunes: t, time: i - o })).catch((s) => { S(`Saving process for ${this.name} tool failed due to the ${s}`, "log", "red"); }); } /** * Uses Tool's validation method to check the correctness of output data * Tool's validation method is optional * * @description Method returns true|false whether data passed the validation or not * @param {BlockToolData} data - data to validate * @returns {Promise} valid */ async validate(e) { let t = !0; return this.toolInstance.validate instanceof Function && (t = await this.toolInstance.validate(e)), t; } /** * Returns data to render in Block Tunes menu. * Splits block tunes into 2 groups: block specific tunes and common tunes */ getTunes() { const e = [], t = [], o = typeof this.toolInstance.renderSettings == "function" ? this.toolInstance.renderSettings() : []; return d.isElement(o) ? e.push({ type: _.Html, element: o }) : Array.isArray(o) ? e.push(...o) : e.push(o), [ ...this.tunesInstances.values(), ...this.defaultTunesInstances.values() ].map((s) => s.render()).forEach((s) => { d.isElement(s) ? t.push({ type: _.Html, element: s }) : Array.isArray(s) ? t.push(...s) : t.push(s); }), { toolTunes: e, commonTunes: t }; } /** * Update current input index with selection anchor node */ updateCurrentInput() { this.currentInput = d.isNativeInput(document.activeElement) || !b.anchorNode ? document.activeElement : b.anchorNode; } /** * Allows to say Editor that Block was changed. Used to manually trigger Editor's 'onChange' callback * Can be useful for block changes invisible for editor core. */ dispatchChange() { this.didMutated(); } /** * Call Tool instance destroy method */ destroy() { this.unwatchBlockMutations(), this.removeInputEvents(), super.destroy(), A(this.toolInstance.destroy) && this.toolInstance.destroy(); } /** * Tool could specify several entries to be displayed at the Toolbox (for example, "Heading 1", "Heading 2", "Heading 3") * This method returns the entry that is related to the Block (depended on the Block data) */ async getActiveToolboxEntry() { const e = this.tool.toolbox; if (e.length === 1) return Promise.resolve(this.tool.toolbox[0]); const t = await this.data, o = e; return o == null ? void 0 : o.find((i) => Wo(i.data, t)); } /** * Exports Block data as string using conversion config */ async exportDataAsString() { const e = await this.data; return fi(e, this.tool.conversionConfig); } /** * Make default Block wrappers and put Tool`s content there * * @returns {HTMLDivElement} */ compose() { const e = d.make("div", R.CSS.wrapper), t = d.make("div", R.CSS.content), o = this.toolInstance.render(); e.dataset.id = this.id, this.toolRenderedElement = o, t.appendChild(this.toolRenderedElement); let i = t; return [...this.tunesInstances.values(), ...this.defaultTunesInstances.values()].forEach((s) => { if (A(s.wrap)) try { i = s.wrap(i); } catch (r) { S(`Tune ${s.constructor.name} wrap method throws an Error %o`, "warn", r); } }), e.appendChild(i), e; } /** * Instantiate Block Tunes * * @param tunesData - current Block tunes data * @private */ composeTunes(e) { Array.from(this.tunes.values()).forEach((t) => { (t.isInternal ? this.defaultTunesInstances : this.tunesInstances).set(t.name, t.create(e[t.name], this.blockAPI)); }), Object.entries(e).forEach(([t, o]) => { this.tunesInstances.has(t) || (this.unavailableTunesData[t] = o); }); } /** * Adds focus event listeners to all inputs and contenteditable */ addInputEvents() { this.inputs.forEach((e) => { e.addEventListener("focus", this.handleFocus), d.isNativeInput(e) && e.addEventListener("input", this.didMutated); }); } /** * removes focus event listeners from all inputs and contenteditable */ removeInputEvents() { this.inputs.forEach((e) => { e.removeEventListener("focus", this.handleFocus), d.isNativeInput(e) && e.removeEventListener("input", this.didMutated); }); } /** * Listen common editor Dom Changed event and detect mutations related to the Block */ watchBlockMutations() { var e; this.redactorDomChangedCallback = (t) => { const { mutations: o } = t; o.some((s) => pi(s, this.toolRenderedElement)) && this.didMutated(o); }, (e = this.editorEventBus) == null || e.on(ft, this.redactorDomChangedCallback); } /** * Remove redactor dom change event listener */ unwatchBlockMutations() { var e; (e = this.editorEventBus) == null || e.off(ft, this.redactorDomChangedCallback); } /** * Sometimes Tool can replace own main element, for example H2 -> H4 or UL -> OL * We need to detect such changes and update a link to tools main element with the new one * * @param mutations - records of block content mutations */ detectToolRootChange(e) { e.forEach((t) => { if (Array.from(t.removedNodes).includes(this.toolRenderedElement)) { const i = t.addedNodes[t.addedNodes.length - 1]; this.toolRenderedElement = i; } }); } /** * Clears inputs cached value */ dropInputsCache() { this.cachedInputs = []; } /** * Mark inputs with 'data-empty' attribute with the empty state */ toggleInputsEmptyMark() { this.inputs.forEach(Do); } } class gi extends E { constructor() { super(...arguments), this.insert = (e = this.config.defaultBlock, t = {}, o = {}, i, s, r, a) => { const l = this.Editor.BlockManager.insert({ id: a, tool: e, data: t, index: i, needToFocus: s, replace: r }); return new J(l); }, this.composeBlockData = async (e) => { const t = this.Editor.Tools.blockTools.get(e); return new R({ tool: t, api: this.Editor.API, readOnly: !0, data: {}, tunesData: {} }).data; }, this.update = async (e, t, o) => { const { BlockManager: i } = this.Editor, s = i.getBlockById(e); if (s === void 0) throw new Error(`Block with id "${e}" not found`); const r = await i.update(s, t, o); return new J(r); }, this.convert = async (e, t, o) => { var h, p; const { BlockManager: i, Tools: s } = this.Editor, r = i.getBlockById(e); if (!r) throw new Error(`Block with id "${e}" not found`); const a = s.blockTools.get(r.name), l = s.blockTools.get(t); if (!l) throw new Error(`Block Tool with type "${t}" not found`); const c = ((h = a == null ? void 0 : a.conversionConfig) == null ? void 0 : h.export) !== void 0, u = ((p = l.conversionConfig) == null ? void 0 : p.import) !== void 0; if (c && u) { const g = await i.convert(r, t, o); return new J(g); } else { const g = [ c ? !1 : je(r.name), u ? !1 : je(t) ].filter(Boolean).join(" and "); throw new Error(`Conversion from "${r.name}" to "${t}" is not possible. ${g} tool(s) should provide a "conversionConfig"`); } }, this.insertMany = (e, t = this.Editor.BlockManager.blocks.length - 1) => { this.validateIndex(t); const o = e.map(({ id: i, type: s, data: r }) => this.Editor.BlockManager.composeBlock({ id: i, tool: s || this.config.defaultBlock, data: r })); return this.Editor.BlockManager.insertMany(o, t), o.map((i) => new J(i)); }; } /** * Available methods * * @returns {Blocks} */ get methods() { return { clear: () => this.clear(), render: (e) => this.render(e), renderFromHTML: (e) => this.renderFromHTML(e), delete: (e) => this.delete(e), swap: (e, t) => this.swap(e, t), move: (e, t) => this.move(e, t), getBlockByIndex: (e) => this.getBlockByIndex(e), getById: (e) => this.getById(e), getCurrentBlockIndex: () => this.getCurrentBlockIndex(), getBlockIndex: (e) => this.getBlockIndex(e), getBlocksCount: () => this.getBlocksCount(), getBlockByElement: (e) => this.getBlockByElement(e), stretchBlock: (e, t = !0) => this.stretchBlock(e, t), insertNewBlock: () => this.insertNewBlock(), insert: this.insert, insertMany: this.insertMany, update: this.update, composeBlockData: this.composeBlockData, convert: this.convert }; } /** * Returns Blocks count * * @returns {number} */ getBlocksCount() { return this.Editor.BlockManager.blocks.length; } /** * Returns current block index * * @returns {number} */ getCurrentBlockIndex() { return this.Editor.BlockManager.currentBlockIndex; } /** * Returns the index of Block by id; * * @param id - block id */ getBlockIndex(e) { const t = this.Editor.BlockManager.getBlockById(e); if (!t) { X("There is no block with id `" + e + "`", "warn"); return; } return this.Editor.BlockManager.getBlockIndex(t); } /** * Returns BlockAPI object by Block index * * @param {number} index - index to get */ getBlockByIndex(e) { const t = this.Editor.BlockManager.getBlockByIndex(e); if (t === void 0) { X("There is no block at index `" + e + "`", "warn"); return; } return new J(t); } /** * Returns BlockAPI object by Block id * * @param id - id of block to get */ getById(e) { const t = this.Editor.BlockManager.getBlockById(e); return t === void 0 ? (X("There is no block with id `" + e + "`", "warn"), null) : new J(t); } /** * Get Block API object by any child html element * * @param element - html element to get Block by */ getBlockByElement(e) { const t = this.Editor.BlockManager.getBlock(e); if (t === void 0) { X("There is no block corresponding to element `" + e + "`", "warn"); return; } return new J(t); } /** * Call Block Manager method that swap Blocks * * @param {number} fromIndex - position of first Block * @param {number} toIndex - position of second Block * @deprecated — use 'move' instead */ swap(e, t) { S( "`blocks.swap()` method is deprecated and will be removed in the next major release. Use `block.move()` method instead", "info" ), this.Editor.BlockManager.swap(e, t); } /** * Move block from one index to another * * @param {number} toIndex - index to move to * @param {number} fromIndex - index to move from */ move(e, t) { this.Editor.BlockManager.move(e, t); } /** * Deletes Block * * @param {number} blockIndex - index of Block to delete */ delete(e = this.Editor.BlockManager.currentBlockIndex) { try { const t = this.Editor.BlockManager.getBlockByIndex(e); this.Editor.BlockManager.removeBlock(t); } catch (t) { X(t, "warn"); return; } this.Editor.BlockManager.blocks.length === 0 && this.Editor.BlockManager.insert(), this.Editor.BlockManager.currentBlock && this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END), this.Editor.Toolbar.close(); } /** * Clear Editor's area */ async clear() { await this.Editor.BlockManager.clear(!0), this.Editor.InlineToolbar.close(); } /** * Fills Editor with Blocks data * * @param {OutputData} data — Saved Editor data */ async render(e) { if (e === void 0 || e.blocks === void 0) throw new Error("Incorrect data passed to the render() method"); this.Editor.ModificationsObserver.disable(), await this.Editor.BlockManager.clear(), await this.Editor.Renderer.render(e.blocks), this.Editor.ModificationsObserver.enable(); } /** * Render passed HTML string * * @param {string} data - HTML string to render * @returns {Promise} */ renderFromHTML(e) { return this.Editor.BlockManager.clear(), this.Editor.Paste.processText(e, !0); } /** * Stretch Block's content * * @param {number} index - index of Block to stretch * @param {boolean} status - true to enable, false to disable * @deprecated Use BlockAPI interface to stretch Blocks */ stretchBlock(e, t = !0) { ht( !0, "blocks.stretchBlock()", "BlockAPI" ); const o = this.Editor.BlockManager.getBlockByIndex(e); o && (o.stretched = t); } /** * Insert new Block * After set caret to this Block * * @todo remove in 3.0.0 * @deprecated with insert() method */ insertNewBlock() { S("Method blocks.insertNewBlock() is deprecated and it will be removed in the next major release. Use blocks.insert() instead.", "warn"), this.insert(); } /** * Validated block index and throws an error if it's invalid * * @param index - index to validate */ validateIndex(e) { if (typeof e != "number") throw new Error("Index should be a number"); if (e < 0) throw new Error("Index should be greater than or equal to 0"); if (e === null) throw new Error("Index should be greater than or equal to 0"); } } function mi(n, e) { return typeof n == "number" ? e.BlockManager.getBlockByIndex(n) : typeof n == "string" ? e.BlockManager.getBlockById(n) : e.BlockManager.getBlockById(n.id); } class bi extends E { constructor() { super(...arguments), this.setToFirstBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.firstBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.firstBlock, e, t), !0) : !1, this.setToLastBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.lastBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.lastBlock, e, t), !0) : !1, this.setToPreviousBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.previousBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.previousBlock, e, t), !0) : !1, this.setToNextBlock = (e = this.Editor.Caret.positions.DEFAULT, t = 0) => this.Editor.BlockManager.nextBlock ? (this.Editor.Caret.setToBlock(this.Editor.BlockManager.nextBlock, e, t), !0) : !1, this.setToBlock = (e, t = this.Editor.Caret.positions.DEFAULT, o = 0) => { const i = mi(e, this.Editor); return i === void 0 ? !1 : (this.Editor.Caret.setToBlock(i, t, o), !0); }, this.focus = (e = !1) => e ? this.setToLastBlock(this.Editor.Caret.positions.END) : this.setToFirstBlock(this.Editor.Caret.positions.START); } /** * Available methods * * @returns {Caret} */ get methods() { return { setToFirstBlock: this.setToFirstBlock, setToLastBlock: this.setToLastBlock, setToPreviousBlock: this.setToPreviousBlock, setToNextBlock: this.setToNextBlock, setToBlock: this.setToBlock, focus: this.focus }; } } class vi extends E { /** * Available methods * * @returns {Events} */ get methods() { return { emit: (e, t) => this.emit(e, t), off: (e, t) => this.off(e, t), on: (e, t) => this.on(e, t) }; } /** * Subscribe on Events * * @param {string} eventName - event name to subscribe * @param {Function} callback - event handler */ on(e, t) { this.eventsDispatcher.on(e, t); } /** * Emit event with data * * @param {string} eventName - event to emit * @param {object} data - event's data */ emit(e, t) { this.eventsDispatcher.emit(e, t); } /** * Unsubscribe from Event * * @param {string} eventName - event to unsubscribe * @param {Function} callback - event handler */ off(e, t) { this.eventsDispatcher.off(e, t); } } class kt extends E { /** * Return namespace section for tool or block tune * * @param toolName - tool name * @param isTune - is tool a block tune */ static getNamespace(e, t) { return t ? `blockTunes.${e}` : `tools.${e}`; } /** * Return I18n API methods with global dictionary access */ get methods() { return { t: () => { X("I18n.t() method can be accessed only from Tools", "warn"); } }; } /** * Return I18n API methods with tool namespaced dictionary * * @param toolName - tool name * @param isTune - is tool a block tune */ getMethodsForTool(e, t) { return Object.assign( this.methods, { t: (o) => z.t(kt.getNamespace(e, t), o) } ); } } class ki extends E { /** * Editor.js Core API modules */ get methods() { return { blocks: this.Editor.BlocksAPI.methods, caret: this.Editor.CaretAPI.methods, tools: this.Editor.ToolsAPI.methods, events: this.Editor.EventsAPI.methods, listeners: this.Editor.ListenersAPI.methods, notifier: this.Editor.NotifierAPI.methods, sanitizer: this.Editor.SanitizerAPI.methods, saver: this.Editor.SaverAPI.methods, selection: this.Editor.SelectionAPI.methods, styles: this.Editor.StylesAPI.classes, toolbar: this.Editor.ToolbarAPI.methods, inlineToolbar: this.Editor.InlineToolbarAPI.methods, tooltip: this.Editor.TooltipAPI.methods, i18n: this.Editor.I18nAPI.methods, readOnly: this.Editor.ReadOnlyAPI.methods, ui: this.Editor.UiAPI.methods }; } /** * Returns Editor.js Core API methods for passed tool * * @param toolName - tool name * @param isTune - is tool a block tune */ getMethodsForTool(e, t) { return Object.assign( this.methods, { i18n: this.Editor.I18nAPI.getMethodsForTool(e, t) } ); } } class yi extends E { /** * Available methods * * @returns {InlineToolbar} */ get methods() { return { close: () => this.close(), open: () => this.open() }; } /** * Open Inline Toolbar */ open() { this.Editor.InlineToolbar.tryToShow(); } /** * Close Inline Toolbar */ close() { this.Editor.InlineToolbar.close(); } } class wi extends E { /** * Available methods * * @returns {Listeners} */ get methods() { return { on: (e, t, o, i) => this.on(e, t, o, i), off: (e, t, o, i) => this.off(e, t, o, i), offById: (e) => this.offById(e) }; } /** * Ads a DOM event listener. Return it's id. * * @param {HTMLElement} element - Element to set handler to * @param {string} eventType - event type * @param {() => void} handler - event handler * @param {boolean} useCapture - capture event or not */ on(e, t, o, i) { return this.listeners.on(e, t, o, i); } /** * Removes DOM listener from element * * @param {Element} element - Element to remove handler from * @param eventType - event type * @param handler - event handler * @param {boolean} useCapture - capture event or not */ off(e, t, o, i) { this.listeners.off(e, t, o, i); } /** * Removes DOM listener by the listener id * * @param id - id of the listener to remove */ offById(e) { this.listeners.offById(e); } } var Ko = { exports: {} }; (function(n, e) { (function(t, o) { n.exports = o(); })(window, function() { return function(t) { var o = {}; function i(s) { if (o[s]) return o[s].exports; var r = o[s] = { i: s, l: !1, exports: {} }; return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; } return i.m = t, i.c = o, i.d = function(s, r, a) { i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); }, i.r = function(s) { typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); }, i.t = function(s, r) { if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) return s; var a = /* @__PURE__ */ Object.create(null); if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") for (var l in s) i.d(a, l, (function(c) { return s[c]; }).bind(null, l)); return a; }, i.n = function(s) { var r = s && s.__esModule ? function() { return s.default; } : function() { return s; }; return i.d(r, "a", r), r; }, i.o = function(s, r) { return Object.prototype.hasOwnProperty.call(s, r); }, i.p = "/", i(i.s = 0); }([function(t, o, i) { i(1), /*! * Codex JavaScript Notification module * https://github.com/codex-team/js-notifier */ t.exports = function() { var s = i(6), r = "cdx-notify--bounce-in", a = null; return { show: function(l) { if (l.message) { (function() { if (a) return !0; a = s.getWrapper(), document.body.appendChild(a); })(); var c = null, u = l.time || 8e3; switch (l.type) { case "confirm": c = s.confirm(l); break; case "prompt": c = s.prompt(l); break; default: c = s.alert(l), window.setTimeout(function() { c.remove(); }, u); } a.appendChild(c), c.classList.add(r); } } }; }(); }, function(t, o, i) { var s = i(2); typeof s == "string" && (s = [[t.i, s, ""]]); var r = { hmr: !0, transform: void 0, insertInto: void 0 }; i(4)(s, r), s.locals && (t.exports = s.locals); }, function(t, o, i) { (t.exports = i(3)(!1)).push([t.i, `.cdx-notify--error{background:#fffbfb!important}.cdx-notify--error::before{background:#fb5d5d!important}.cdx-notify__input{max-width:130px;padding:5px 10px;background:#f7f7f7;border:0;border-radius:3px;font-size:13px;color:#656b7c;outline:0}.cdx-notify__input:-ms-input-placeholder{color:#656b7c}.cdx-notify__input::placeholder{color:#656b7c}.cdx-notify__input:focus:-ms-input-placeholder{color:rgba(101,107,124,.3)}.cdx-notify__input:focus::placeholder{color:rgba(101,107,124,.3)}.cdx-notify__button{border:none;border-radius:3px;font-size:13px;padding:5px 10px;cursor:pointer}.cdx-notify__button:last-child{margin-left:10px}.cdx-notify__button--cancel{background:#f2f5f7;box-shadow:0 2px 1px 0 rgba(16,19,29,0);color:#656b7c}.cdx-notify__button--cancel:hover{background:#eee}.cdx-notify__button--confirm{background:#34c992;box-shadow:0 1px 1px 0 rgba(18,49,35,.05);color:#fff}.cdx-notify__button--confirm:hover{background:#33b082}.cdx-notify__btns-wrapper{display:-ms-flexbox;display:flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;margin-top:5px}.cdx-notify__cross{position:absolute;top:5px;right:5px;width:10px;height:10px;padding:5px;opacity:.54;cursor:pointer}.cdx-notify__cross::after,.cdx-notify__cross::before{content:'';position:absolute;left:9px;top:5px;height:12px;width:2px;background:#575d67}.cdx-notify__cross::before{transform:rotate(-45deg)}.cdx-notify__cross::after{transform:rotate(45deg)}.cdx-notify__cross:hover{opacity:1}.cdx-notifies{position:fixed;z-index:2;bottom:20px;left:20px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.cdx-notify{position:relative;width:220px;margin-top:15px;padding:13px 16px;background:#fff;box-shadow:0 11px 17px 0 rgba(23,32,61,.13);border-radius:5px;font-size:14px;line-height:1.4em;word-wrap:break-word}.cdx-notify::before{content:'';position:absolute;display:block;top:0;left:0;width:3px;height:calc(100% - 6px);margin:3px;border-radius:5px;background:0 0}@keyframes bounceIn{0%{opacity:0;transform:scale(.3)}50%{opacity:1;transform:scale(1.05)}70%{transform:scale(.9)}100%{transform:scale(1)}}.cdx-notify--bounce-in{animation-name:bounceIn;animation-duration:.6s;animation-iteration-count:1}.cdx-notify--success{background:#fafffe!important}.cdx-notify--success::before{background:#41ffb1!important}`, ""]); }, function(t, o) { t.exports = function(i) { var s = []; return s.toString = function() { return this.map(function(r) { var a = function(l, c) { var u = l[1] || "", h = l[3]; if (!h) return u; if (c && typeof btoa == "function") { var p = (f = h, "/*# sourceMappingURL=data:application/json;charset=utf-8;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(f)))) + " */"), g = h.sources.map(function(v) { return "/*# sourceURL=" + h.sourceRoot + v + " */"; }); return [u].concat(g).concat([p]).join(` `); } var f; return [u].join(` `); }(r, i); return r[2] ? "@media " + r[2] + "{" + a + "}" : a; }).join(""); }, s.i = function(r, a) { typeof r == "string" && (r = [[null, r, ""]]); for (var l = {}, c = 0; c < this.length; c++) { var u = this[c][0]; typeof u == "number" && (l[u] = !0); } for (c = 0; c < r.length; c++) { var h = r[c]; typeof h[0] == "number" && l[h[0]] || (a && !h[2] ? h[2] = a : a && (h[2] = "(" + h[2] + ") and (" + a + ")"), s.push(h)); } }, s; }; }, function(t, o, i) { var s, r, a = {}, l = (s = function() { return window && document && document.all && !window.atob; }, function() { return r === void 0 && (r = s.apply(this, arguments)), r; }), c = function(k) { var m = {}; return function(w) { if (typeof w == "function") return w(); if (m[w] === void 0) { var x = (function(I) { return document.querySelector(I); }).call(this, w); if (window.HTMLIFrameElement && x instanceof window.HTMLIFrameElement) try { x = x.contentDocument.head; } catch { x = null; } m[w] = x; } return m[w]; }; }(), u = null, h = 0, p = [], g = i(5); function f(k, m) { for (var w = 0; w < k.length; w++) { var x = k[w], I = a[x.id]; if (I) { I.refs++; for (var C = 0; C < I.parts.length; C++) I.parts[C](x.parts[C]); for (; C < x.parts.length; C++) I.parts.push(F(x.parts[C], m)); } else { var N = []; for (C = 0; C < x.parts.length; C++) N.push(F(x.parts[C], m)); a[x.id] = { id: x.id, refs: 1, parts: N }; } } } function v(k, m) { for (var w = [], x = {}, I = 0; I < k.length; I++) { var C = k[I], N = m.base ? C[0] + m.base : C[0], B = { css: C[1], media: C[2], sourceMap: C[3] }; x[N] ? x[N].parts.push(B) : w.push(x[N] = { id: N, parts: [B] }); } return w; } function O(k, m) { var w = c(k.insertInto); if (!w) throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid."); var x = p[p.length - 1]; if (k.insertAt === "top") x ? x.nextSibling ? w.insertBefore(m, x.nextSibling) : w.appendChild(m) : w.insertBefore(m, w.firstChild), p.push(m); else if (k.insertAt === "bottom") w.appendChild(m); else { if (typeof k.insertAt != "object" || !k.insertAt.before) throw new Error(`[Style Loader] Invalid value for parameter 'insertAt' ('options.insertAt') found. Must be 'top', 'bottom', or Object. (https://github.com/webpack-contrib/style-loader#insertat) `); var I = c(k.insertInto + " " + k.insertAt.before); w.insertBefore(m, I); } } function T(k) { if (k.parentNode === null) return !1; k.parentNode.removeChild(k); var m = p.indexOf(k); m >= 0 && p.splice(m, 1); } function M(k) { var m = document.createElement("style"); return k.attrs.type === void 0 && (k.attrs.type = "text/css"), q(m, k.attrs), O(k, m), m; } function q(k, m) { Object.keys(m).forEach(function(w) { k.setAttribute(w, m[w]); }); } function F(k, m) { var w, x, I, C; if (m.transform && k.css) { if (!(C = m.transform(k.css))) return function() { }; k.css = C; } if (m.singleton) { var N = h++; w = u || (u = M(m)), x = ie.bind(null, w, N, !1), I = ie.bind(null, w, N, !0); } else k.sourceMap && typeof URL == "function" && typeof URL.createObjectURL == "function" && typeof URL.revokeObjectURL == "function" && typeof Blob == "function" && typeof btoa == "function" ? (w = function(B) { var W = document.createElement("link"); return B.attrs.type === void 0 && (B.attrs.type = "text/css"), B.attrs.rel = "stylesheet", q(W, B.attrs), O(B, W), W; }(m), x = (function(B, W, ve) { var se = ve.css, tt = ve.sourceMap, Yn = W.convertToAbsoluteUrls === void 0 && tt; (W.convertToAbsoluteUrls || Yn) && (se = g(se)), tt && (se += ` /*# sourceMappingURL=data:application/json;base64,` + btoa(unescape(encodeURIComponent(JSON.stringify(tt)))) + " */"); var Kn = new Blob([se], { type: "text/css" }), ko = B.href; B.href = URL.createObjectURL(Kn), ko && URL.revokeObjectURL(ko); }).bind(null, w, m), I = function() { T(w), w.href && URL.revokeObjectURL(w.href); }) : (w = M(m), x = (function(B, W) { var ve = W.css, se = W.media; if (se && B.setAttribute("media", se), B.styleSheet) B.styleSheet.cssText = ve; else { for (; B.firstChild; ) B.removeChild(B.firstChild); B.appendChild(document.createTextNode(ve)); } }).bind(null, w), I = function() { T(w); }); return x(k), function(B) { if (B) { if (B.css === k.css && B.media === k.media && B.sourceMap === k.sourceMap) return; x(k = B); } else I(); }; } t.exports = function(k, m) { if (typeof DEBUG < "u" && DEBUG && typeof document != "object") throw new Error("The style-loader cannot be used in a non-browser environment"); (m = m || {}).attrs = typeof m.attrs == "object" ? m.attrs : {}, m.singleton || typeof m.singleton == "boolean" || (m.singleton = l()), m.insertInto || (m.insertInto = "head"), m.insertAt || (m.insertAt = "bottom"); var w = v(k, m); return f(w, m), function(x) { for (var I = [], C = 0; C < w.length; C++) { var N = w[C]; (B = a[N.id]).refs--, I.push(B); } for (x && f(v(x, m), m), C = 0; C < I.length; C++) { var B; if ((B = I[C]).refs === 0) { for (var W = 0; W < B.parts.length; W++) B.parts[W](); delete a[B.id]; } } }; }; var H, Q = (H = [], function(k, m) { return H[k] = m, H.filter(Boolean).join(` `); }); function ie(k, m, w, x) { var I = w ? "" : x.css; if (k.styleSheet) k.styleSheet.cssText = Q(m, I); else { var C = document.createTextNode(I), N = k.childNodes; N[m] && k.removeChild(N[m]), N.length ? k.insertBefore(C, N[m]) : k.appendChild(C); } } }, function(t, o) { t.exports = function(i) { var s = typeof window < "u" && window.location; if (!s) throw new Error("fixUrls requires window.location"); if (!i || typeof i != "string") return i; var r = s.protocol + "//" + s.host, a = r + s.pathname.replace(/\/[^\/]*$/, "/"); return i.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(l, c) { var u, h = c.trim().replace(/^"(.*)"$/, function(p, g) { return g; }).replace(/^'(.*)'$/, function(p, g) { return g; }); return /^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(h) ? l : (u = h.indexOf("//") === 0 ? h : h.indexOf("/") === 0 ? r + h : a + h.replace(/^\.\//, ""), "url(" + JSON.stringify(u) + ")"); }); }; }, function(t, o, i) { var s, r, a, l, c, u, h, p, g; t.exports = (s = "cdx-notifies", r = "cdx-notify", a = "cdx-notify__cross", l = "cdx-notify__button--confirm", c = "cdx-notify__button--cancel", u = "cdx-notify__input", h = "cdx-notify__button", p = "cdx-notify__btns-wrapper", { alert: g = function(f) { var v = document.createElement("DIV"), O = document.createElement("DIV"), T = f.message, M = f.style; return v.classList.add(r), M && v.classList.add(r + "--" + M), v.innerHTML = T, O.classList.add(a), O.addEventListener("click", v.remove.bind(v)), v.appendChild(O), v; }, confirm: function(f) { var v = g(f), O = document.createElement("div"), T = document.createElement("button"), M = document.createElement("button"), q = v.querySelector("." + a), F = f.cancelHandler, H = f.okHandler; return O.classList.add(p), T.innerHTML = f.okText || "Confirm", M.innerHTML = f.cancelText || "Cancel", T.classList.add(h), M.classList.add(h), T.classList.add(l), M.classList.add(c), F && typeof F == "function" && (M.addEventListener("click", F), q.addEventListener("click", F)), H && typeof H == "function" && T.addEventListener("click", H), T.addEventListener("click", v.remove.bind(v)), M.addEventListener("click", v.remove.bind(v)), O.appendChild(T), O.appendChild(M), v.appendChild(O), v; }, prompt: function(f) { var v = g(f), O = document.createElement("div"), T = document.createElement("button"), M = document.createElement("input"), q = v.querySelector("." + a), F = f.cancelHandler, H = f.okHandler; return O.classList.add(p), T.innerHTML = f.okText || "Ok", T.classList.add(h), T.classList.add(l), M.classList.add(u), f.placeholder && M.setAttribute("placeholder", f.placeholder), f.default && (M.value = f.default), f.inputType && (M.type = f.inputType), F && typeof F == "function" && q.addEventListener("click", F), H && typeof H == "function" && T.addEventListener("click", function() { H(M.value); }), T.addEventListener("click", v.remove.bind(v)), O.appendChild(M), O.appendChild(T), v.appendChild(O), v; }, getWrapper: function() { var f = document.createElement("DIV"); return f.classList.add(s), f; } }); }]); }); })(Ko); var Ei = Ko.exports; const xi = /* @__PURE__ */ Ke(Ei); class Bi { /** * Show web notification * * @param {NotifierOptions | ConfirmNotifierOptions | PromptNotifierOptions} options - notification options */ show(e) { xi.show(e); } } class Ci extends E { /** * @param moduleConfiguration - Module Configuration * @param moduleConfiguration.config - Editor's config * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher */ constructor({ config: e, eventsDispatcher: t }) { super({ config: e, eventsDispatcher: t }), this.notifier = new Bi(); } /** * Available methods */ get methods() { return { show: (e) => this.show(e) }; } /** * Show notification * * @param {NotifierOptions} options - message option */ show(e) { return this.notifier.show(e); } } class Ti extends E { /** * Available methods */ get methods() { const e = () => this.isEnabled; return { toggle: (t) => this.toggle(t), get isEnabled() { return e(); } }; } /** * Set or toggle read-only state * * @param {boolean|undefined} state - set or toggle state * @returns {boolean} current value */ toggle(e) { return this.Editor.ReadOnly.toggle(e); } /** * Returns current read-only state */ get isEnabled() { return this.Editor.ReadOnly.isEnabled; } } var Xo = { exports: {} }; (function(n, e) { (function(t, o) { n.exports = o(); })(Ce, function() { function t(h) { var p = h.tags, g = Object.keys(p), f = g.map(function(v) { return typeof p[v]; }).every(function(v) { return v === "object" || v === "boolean" || v === "function"; }); if (!f) throw new Error("The configuration was invalid"); this.config = h; } var o = ["P", "LI", "TD", "TH", "DIV", "H1", "H2", "H3", "H4", "H5", "H6", "PRE"]; function i(h) { return o.indexOf(h.nodeName) !== -1; } var s = ["A", "B", "STRONG", "I", "EM", "SUB", "SUP", "U", "STRIKE"]; function r(h) { return s.indexOf(h.nodeName) !== -1; } t.prototype.clean = function(h) { const p = document.implementation.createHTMLDocument(), g = p.createElement("div"); return g.innerHTML = h, this._sanitize(p, g), g.innerHTML; }, t.prototype._sanitize = function(h, p) { var g = a(h, p), f = g.firstChild(); if (f) do { if (f.nodeType === Node.TEXT_NODE) if (f.data.trim() === "" && (f.previousElementSibling && i(f.previousElementSibling) || f.nextElementSibling && i(f.nextElementSibling))) { p.removeChild(f), this._sanitize(h, p); break; } else continue; if (f.nodeType === Node.COMMENT_NODE) { p.removeChild(f), this._sanitize(h, p); break; } var v = r(f), O; v && (O = Array.prototype.some.call(f.childNodes, i)); var T = !!p.parentNode, M = i(p) && i(f) && T, q = f.nodeName.toLowerCase(), F = l(this.config, q, f), H = v && O; if (H || c(f, F) || !this.config.keepNestedBlockElements && M) { if (!(f.nodeName === "SCRIPT" || f.nodeName === "STYLE")) for (; f.childNodes.length > 0; ) p.insertBefore(f.childNodes[0], f); p.removeChild(f), this._sanitize(h, p); break; } for (var Q = 0; Q < f.attributes.length; Q += 1) { var ie = f.attributes[Q]; u(ie, F, f) && (f.removeAttribute(ie.name), Q = Q - 1); } this._sanitize(h, f); } while (f = g.nextSibling()); }; function a(h, p) { return h.createTreeWalker( p, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT, null, !1 ); } function l(h, p, g) { return typeof h.tags[p] == "function" ? h.tags[p](g) : h.tags[p]; } function c(h, p) { return typeof p > "u" ? !0 : typeof p == "boolean" ? !p : !1; } function u(h, p, g) { var f = h.name.toLowerCase(); return p === !0 ? !1 : typeof p[f] == "function" ? !p[f](h.value, g) : typeof p[f] > "u" || p[f] === !1 ? !0 : typeof p[f] == "string" ? p[f] !== h.value : !1; } return t; }); })(Xo); var Si = Xo.exports; const Ii = /* @__PURE__ */ Ke(Si); function yt(n, e) { return n.map((t) => { const o = A(e) ? e(t.tool) : e; return V(o) || (t.data = wt(t.data, o)), t; }); } function Z(n, e = {}) { const t = { tags: e }; return new Ii(t).clean(n); } function wt(n, e) { return Array.isArray(n) ? Oi(n, e) : D(n) ? _i(n, e) : te(n) ? Mi(n, e) : n; } function Oi(n, e) { return n.map((t) => wt(t, e)); } function _i(n, e) { const t = {}; for (const o in n) { if (!Object.prototype.hasOwnProperty.call(n, o)) continue; const i = n[o], s = Ai(e[o]) ? e[o] : e; t[o] = wt(i, s); } return t; } function Mi(n, e) { return D(e) ? Z(n, e) : e === !1 ? Z(n, {}) : n; } function Ai(n) { return D(n) || Gn(n) || A(n); } class Li extends E { /** * Available methods * * @returns {SanitizerConfig} */ get methods() { return { clean: (e, t) => this.clean(e, t) }; } /** * Perform sanitizing of a string * * @param {string} taintString - what to sanitize * @param {SanitizerConfig} config - sanitizer config * @returns {string} */ clean(e, t) { return Z(e, t); } } class Pi extends E { /** * Available methods * * @returns {Saver} */ get methods() { return { save: () => this.save() }; } /** * Return Editor's data * * @returns {OutputData} */ save() { const e = "Editor's content can not be saved in read-only mode"; return this.Editor.ReadOnly.isEnabled ? (X(e, "warn"), Promise.reject(new Error(e))) : this.Editor.Saver.save(); } } class Ni extends E { constructor() { super(...arguments), this.selectionUtils = new b(); } /** * Available methods * * @returns {SelectionAPIInterface} */ get methods() { return { findParentTag: (e, t) => this.findParentTag(e, t), expandToTag: (e) => this.expandToTag(e), save: () => this.selectionUtils.save(), restore: () => this.selectionUtils.restore(), setFakeBackground: () => this.selectionUtils.setFakeBackground(), removeFakeBackground: () => this.selectionUtils.removeFakeBackground() }; } /** * Looks ahead from selection and find passed tag with class name * * @param {string} tagName - tag to find * @param {string} className - tag's class name * @returns {HTMLElement|null} */ findParentTag(e, t) { return this.selectionUtils.findParentTag(e, t); } /** * Expand selection to passed tag * * @param {HTMLElement} node - tag that should contain selection */ expandToTag(e) { this.selectionUtils.expandToTag(e); } } class Ri extends E { /** * Available methods */ get methods() { return { getBlockTools: () => Array.from(this.Editor.Tools.blockTools.values()) }; } } class Di extends E { /** * Exported classes */ get classes() { return { /** * Base Block styles */ block: "cdx-block", /** * Inline Tools styles */ inlineToolButton: "ce-inline-tool", inlineToolButtonActive: "ce-inline-tool--active", /** * UI elements */ input: "cdx-input", loader: "cdx-loader", button: "cdx-button", /** * Settings styles */ settingsButton: "cdx-settings-button", settingsButtonActive: "cdx-settings-button--active" }; } } class Fi extends E { /** * Available methods * * @returns {Toolbar} */ get methods() { return { close: () => this.close(), open: () => this.open(), toggleBlockSettings: (e) => this.toggleBlockSettings(e), toggleToolbox: (e) => this.toggleToolbox(e) }; } /** * Open toolbar */ open() { this.Editor.Toolbar.moveAndOpen(); } /** * Close toolbar and all included elements */ close() { this.Editor.Toolbar.close(); } /** * Toggles Block Setting of the current block * * @param {boolean} openingState — opening state of Block Setting */ toggleBlockSettings(e) { if (this.Editor.BlockManager.currentBlockIndex === -1) { X("Could't toggle the Toolbar because there is no block selected ", "warn"); return; } e ?? !this.Editor.BlockSettings.opened ? (this.Editor.Toolbar.moveAndOpen(), this.Editor.BlockSettings.open()) : this.Editor.BlockSettings.close(); } /** * Open toolbox * * @param {boolean} openingState - Opening state of toolbox */ toggleToolbox(e) { if (this.Editor.BlockManager.currentBlockIndex === -1) { X("Could't toggle the Toolbox because there is no block selected ", "warn"); return; } e ?? !this.Editor.Toolbar.toolbox.opened ? (this.Editor.Toolbar.moveAndOpen(), this.Editor.Toolbar.toolbox.open()) : this.Editor.Toolbar.toolbox.close(); } } var Vo = { exports: {} }; /*! * CodeX.Tooltips * * @version 1.0.5 * * @licence MIT * @author CodeX * * */ (function(n, e) { (function(t, o) { n.exports = o(); })(window, function() { return function(t) { var o = {}; function i(s) { if (o[s]) return o[s].exports; var r = o[s] = { i: s, l: !1, exports: {} }; return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; } return i.m = t, i.c = o, i.d = function(s, r, a) { i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); }, i.r = function(s) { typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); }, i.t = function(s, r) { if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) return s; var a = /* @__PURE__ */ Object.create(null); if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") for (var l in s) i.d(a, l, (function(c) { return s[c]; }).bind(null, l)); return a; }, i.n = function(s) { var r = s && s.__esModule ? function() { return s.default; } : function() { return s; }; return i.d(r, "a", r), r; }, i.o = function(s, r) { return Object.prototype.hasOwnProperty.call(s, r); }, i.p = "", i(i.s = 0); }([function(t, o, i) { t.exports = i(1); }, function(t, o, i) { i.r(o), i.d(o, "default", function() { return s; }); class s { constructor() { this.nodes = { wrapper: null, content: null }, this.showed = !1, this.offsetTop = 10, this.offsetLeft = 10, this.offsetRight = 10, this.hidingDelay = 0, this.handleWindowScroll = () => { this.showed && this.hide(!0); }, this.loadStyles(), this.prepare(), window.addEventListener("scroll", this.handleWindowScroll, { passive: !0 }); } get CSS() { return { tooltip: "ct", tooltipContent: "ct__content", tooltipShown: "ct--shown", placement: { left: "ct--left", bottom: "ct--bottom", right: "ct--right", top: "ct--top" } }; } show(a, l, c) { this.nodes.wrapper || this.prepare(), this.hidingTimeout && clearTimeout(this.hidingTimeout); const u = Object.assign({ placement: "bottom", marginTop: 0, marginLeft: 0, marginRight: 0, marginBottom: 0, delay: 70, hidingDelay: 0 }, c); if (u.hidingDelay && (this.hidingDelay = u.hidingDelay), this.nodes.content.innerHTML = "", typeof l == "string") this.nodes.content.appendChild(document.createTextNode(l)); else { if (!(l instanceof Node)) throw Error("[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. But " + typeof l + " given."); this.nodes.content.appendChild(l); } switch (this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement)), u.placement) { case "top": this.placeTop(a, u); break; case "left": this.placeLeft(a, u); break; case "right": this.placeRight(a, u); break; case "bottom": default: this.placeBottom(a, u); } u && u.delay ? this.showingTimeout = setTimeout(() => { this.nodes.wrapper.classList.add(this.CSS.tooltipShown), this.showed = !0; }, u.delay) : (this.nodes.wrapper.classList.add(this.CSS.tooltipShown), this.showed = !0); } hide(a = !1) { if (this.hidingDelay && !a) return this.hidingTimeout && clearTimeout(this.hidingTimeout), void (this.hidingTimeout = setTimeout(() => { this.hide(!0); }, this.hidingDelay)); this.nodes.wrapper.classList.remove(this.CSS.tooltipShown), this.showed = !1, this.showingTimeout && clearTimeout(this.showingTimeout); } onHover(a, l, c) { a.addEventListener("mouseenter", () => { this.show(a, l, c); }), a.addEventListener("mouseleave", () => { this.hide(); }); } destroy() { this.nodes.wrapper.remove(), window.removeEventListener("scroll", this.handleWindowScroll); } prepare() { this.nodes.wrapper = this.make("div", this.CSS.tooltip), this.nodes.content = this.make("div", this.CSS.tooltipContent), this.append(this.nodes.wrapper, this.nodes.content), this.append(document.body, this.nodes.wrapper); } loadStyles() { const a = "codex-tooltips-style"; if (document.getElementById(a)) return; const l = i(2), c = this.make("style", null, { textContent: l.toString(), id: a }); this.prepend(document.head, c); } placeBottom(a, l) { const c = a.getBoundingClientRect(), u = c.left + a.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2, h = c.bottom + window.pageYOffset + this.offsetTop + l.marginTop; this.applyPlacement("bottom", u, h); } placeTop(a, l) { const c = a.getBoundingClientRect(), u = c.left + a.clientWidth / 2 - this.nodes.wrapper.offsetWidth / 2, h = c.top + window.pageYOffset - this.nodes.wrapper.clientHeight - this.offsetTop; this.applyPlacement("top", u, h); } placeLeft(a, l) { const c = a.getBoundingClientRect(), u = c.left - this.nodes.wrapper.offsetWidth - this.offsetLeft - l.marginLeft, h = c.top + window.pageYOffset + a.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2; this.applyPlacement("left", u, h); } placeRight(a, l) { const c = a.getBoundingClientRect(), u = c.right + this.offsetRight + l.marginRight, h = c.top + window.pageYOffset + a.clientHeight / 2 - this.nodes.wrapper.offsetHeight / 2; this.applyPlacement("right", u, h); } applyPlacement(a, l, c) { this.nodes.wrapper.classList.add(this.CSS.placement[a]), this.nodes.wrapper.style.left = l + "px", this.nodes.wrapper.style.top = c + "px"; } make(a, l = null, c = {}) { const u = document.createElement(a); Array.isArray(l) ? u.classList.add(...l) : l && u.classList.add(l); for (const h in c) c.hasOwnProperty(h) && (u[h] = c[h]); return u; } append(a, l) { Array.isArray(l) ? l.forEach((c) => a.appendChild(c)) : a.appendChild(l); } prepend(a, l) { Array.isArray(l) ? (l = l.reverse()).forEach((c) => a.prepend(c)) : a.prepend(l); } } }, function(t, o) { t.exports = `.ct{z-index:999;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1),-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);will-change:opacity,top,left;-webkit-box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);border-radius:9px}.ct,.ct:before{position:absolute;top:0;left:0}.ct:before{content:"";bottom:0;right:0;background-color:#1d202b;z-index:-1;border-radius:4px}@supports(-webkit-mask-box-image:url("")){.ct:before{border-radius:0;-webkit-mask-box-image:url('data:image/svg+xml;charset=utf-8,') 48% 41% 37.9% 53.3%}}@media (--mobile){.ct{display:none}}.ct__content{padding:6px 10px;color:#cdd1e0;font-size:12px;text-align:center;letter-spacing:.02em;line-height:1em}.ct:after{content:"";width:8px;height:8px;position:absolute;background-color:#1d202b;z-index:-1}.ct--bottom{-webkit-transform:translateY(5px);transform:translateY(5px)}.ct--bottom:after{top:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--top{-webkit-transform:translateY(-5px);transform:translateY(-5px)}.ct--top:after{top:auto;bottom:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--left{-webkit-transform:translateX(-5px);transform:translateX(-5px)}.ct--left:after{top:50%;left:auto;right:0;-webkit-transform:translate(41.6%,-50%) rotate(-45deg);transform:translate(41.6%,-50%) rotate(-45deg)}.ct--right{-webkit-transform:translateX(5px);transform:translateX(5px)}.ct--right:after{top:50%;left:0;-webkit-transform:translate(-41.6%,-50%) rotate(-45deg);transform:translate(-41.6%,-50%) rotate(-45deg)}.ct--shown{opacity:1;-webkit-transform:none;transform:none}`; }]).default; }); })(Vo); var ji = Vo.exports; const Hi = /* @__PURE__ */ Ke(ji); let U = null; function Et() { U || (U = new Hi()); } function $i(n, e, t) { Et(), U == null || U.show(n, e, t); } function $e(n = !1) { Et(), U == null || U.hide(n); } function ze(n, e, t) { Et(), U == null || U.onHover(n, e, t); } function zi() { U == null || U.destroy(), U = null; } class Ui extends E { /** * @class * @param moduleConfiguration - Module Configuration * @param moduleConfiguration.config - Editor's config * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher */ constructor({ config: e, eventsDispatcher: t }) { super({ config: e, eventsDispatcher: t }); } /** * Available methods */ get methods() { return { show: (e, t, o) => this.show(e, t, o), hide: () => this.hide(), onHover: (e, t, o) => this.onHover(e, t, o) }; } /** * Method show tooltip on element with passed HTML content * * @param {HTMLElement} element - element on which tooltip should be shown * @param {TooltipContent} content - tooltip content * @param {TooltipOptions} options - tooltip options */ show(e, t, o) { $i(e, t, o); } /** * Method hides tooltip on HTML page */ hide() { $e(); } /** * Decorator for showing Tooltip by mouseenter/mouseleave * * @param {HTMLElement} element - element on which tooltip should be shown * @param {TooltipContent} content - tooltip content * @param {TooltipOptions} options - tooltip options */ onHover(e, t, o) { ze(e, t, o); } } class Wi extends E { /** * Available methods / getters */ get methods() { return { nodes: this.editorNodes /** * There can be added some UI methods, like toggleThinMode() etc */ }; } /** * Exported classes */ get editorNodes() { return { /** * Top-level editor instance wrapper */ wrapper: this.Editor.UI.nodes.wrapper, /** * Element that holds all the Blocks */ redactor: this.Editor.UI.nodes.redactor }; } } function qo(n, e) { const t = {}; return Object.entries(n).forEach(([o, i]) => { if (D(i)) { const s = e ? `${e}.${o}` : o; Object.values(i).every((a) => te(a)) ? t[o] = s : t[o] = qo(i, s); return; } t[o] = i; }), t; } const K = qo(Fo); function Yi(n, e) { const t = {}; return Object.keys(n).forEach((o) => { const i = e[o]; i !== void 0 ? t[i] = n[o] : t[o] = n[o]; }), t; } const Zo = class Ee { /** * @param {HTMLElement[]} nodeList — the list of iterable HTML-items * @param {string} focusedCssClass - user-provided CSS-class that will be set in flipping process */ constructor(e, t) { this.cursor = -1, this.items = [], this.items = e || [], this.focusedCssClass = t; } /** * Returns Focused button Node * * @returns {HTMLElement} */ get currentItem() { return this.cursor === -1 ? null : this.items[this.cursor]; } /** * Sets cursor to specified position * * @param cursorPosition - new cursor position */ setCursor(e) { e < this.items.length && e >= -1 && (this.dropCursor(), this.cursor = e, this.items[this.cursor].classList.add(this.focusedCssClass)); } /** * Sets items. Can be used when iterable items changed dynamically * * @param {HTMLElement[]} nodeList - nodes to iterate */ setItems(e) { this.items = e; } /** * Sets cursor next to the current */ next() { this.cursor = this.leafNodesAndReturnIndex(Ee.directions.RIGHT); } /** * Sets cursor before current */ previous() { this.cursor = this.leafNodesAndReturnIndex(Ee.directions.LEFT); } /** * Sets cursor to the default position and removes CSS-class from previously focused item */ dropCursor() { this.cursor !== -1 && (this.items[this.cursor].classList.remove(this.focusedCssClass), this.cursor = -1); } /** * Leafs nodes inside the target list from active element * * @param {string} direction - leaf direction. Can be 'left' or 'right' * @returns {number} index of focused node */ leafNodesAndReturnIndex(e) { if (this.items.length === 0) return this.cursor; let t = this.cursor; return t === -1 ? t = e === Ee.directions.RIGHT ? -1 : 0 : this.items[t].classList.remove(this.focusedCssClass), e === Ee.directions.RIGHT ? t = (t + 1) % this.items.length : t = (this.items.length + t - 1) % this.items.length, d.canSetCaret(this.items[t]) && Fe(() => b.setCursor(this.items[t]), 50)(), this.items[t].classList.add(this.focusedCssClass), t; } }; Zo.directions = { RIGHT: "right", LEFT: "left" }; let ke = Zo; class ce { /** * @param options - different constructing settings */ constructor(e) { this.iterator = null, this.activated = !1, this.flipCallbacks = [], this.onKeyDown = (t) => { if (this.isEventReadyForHandling(t)) switch (ce.usedKeys.includes(t.keyCode) && t.preventDefault(), t.keyCode) { case y.TAB: this.handleTabPress(t); break; case y.LEFT: case y.UP: this.flipLeft(); break; case y.RIGHT: case y.DOWN: this.flipRight(); break; case y.ENTER: this.handleEnterPress(t); break; } }, this.iterator = new ke(e.items, e.focusedItemClass), this.activateCallback = e.activateCallback, this.allowedKeys = e.allowedKeys || ce.usedKeys; } /** * True if flipper is currently activated */ get isActivated() { return this.activated; } /** * Array of keys (codes) that is handled by Flipper * Used to: * - preventDefault only for this keys, not all keydowns (@see constructor) * - to skip external behaviours only for these keys, when filler is activated (@see BlockEvents@arrowRightAndDown) */ static get usedKeys() { return [ y.TAB, y.LEFT, y.RIGHT, y.ENTER, y.UP, y.DOWN ]; } /** * Active tab/arrows handling by flipper * * @param items - Some modules (like, InlineToolbar, BlockSettings) might refresh buttons dynamically * @param cursorPosition - index of the item that should be focused once flipper is activated */ activate(e, t) { this.activated = !0, e && this.iterator.setItems(e), t !== void 0 && this.iterator.setCursor(t), document.addEventListener("keydown", this.onKeyDown, !0); } /** * Disable tab/arrows handling by flipper */ deactivate() { this.activated = !1, this.dropCursor(), document.removeEventListener("keydown", this.onKeyDown); } /** * Focus first item */ focusFirst() { this.dropCursor(), this.flipRight(); } /** * Focuses previous flipper iterator item */ flipLeft() { this.iterator.previous(), this.flipCallback(); } /** * Focuses next flipper iterator item */ flipRight() { this.iterator.next(), this.flipCallback(); } /** * Return true if some button is focused */ hasFocus() { return !!this.iterator.currentItem; } /** * Registeres function that should be executed on each navigation action * * @param cb - function to execute */ onFlip(e) { this.flipCallbacks.push(e); } /** * Unregisteres function that is executed on each navigation action * * @param cb - function to stop executing */ removeOnFlip(e) { this.flipCallbacks = this.flipCallbacks.filter((t) => t !== e); } /** * Drops flipper's iterator cursor * * @see DomIterator#dropCursor */ dropCursor() { this.iterator.dropCursor(); } /** * This function is fired before handling flipper keycodes * The result of this function defines if it is need to be handled or not * * @param {KeyboardEvent} event - keydown keyboard event * @returns {boolean} */ isEventReadyForHandling(e) { return this.activated && this.allowedKeys.includes(e.keyCode); } /** * When flipper is activated tab press will leaf the items * * @param {KeyboardEvent} event - tab keydown event */ handleTabPress(e) { switch (e.shiftKey ? ke.directions.LEFT : ke.directions.RIGHT) { case ke.directions.RIGHT: this.flipRight(); break; case ke.directions.LEFT: this.flipLeft(); break; } } /** * Enter press will click current item if flipper is activated * * @param {KeyboardEvent} event - enter keydown event */ handleEnterPress(e) { this.activated && (this.iterator.currentItem && (e.stopPropagation(), e.preventDefault(), this.iterator.currentItem.click()), A(this.activateCallback) && this.activateCallback(this.iterator.currentItem)); } /** * Fired after flipping in any direction */ flipCallback() { this.iterator.currentItem && this.iterator.currentItem.scrollIntoViewIfNeeded(), this.flipCallbacks.forEach((e) => e()); } } const Ki = '', Xi = '', Vi = '', qi = '', Zi = '', Gi = '', Qi = '', Ji = '', Co = '', es = '', ts = '', Go = '', os = '', ns = '', is = '', ss = "__", rs = "--"; function ne(n) { return (e, t) => [[n, e].filter((i) => !!i).join(ss), t].filter((i) => !!i).join(rs); } const ye = ne("ce-hint"), we = { root: ye(), alignedStart: ye(null, "align-left"), alignedCenter: ye(null, "align-center"), title: ye("title"), description: ye("description") }; class as { /** * Constructs the hint content instance * * @param params - hint content parameters */ constructor(e) { this.nodes = { root: d.make("div", [we.root, e.alignment === "center" ? we.alignedCenter : we.alignedStart]), title: d.make("div", we.title, { textContent: e.title }) }, this.nodes.root.appendChild(this.nodes.title), e.description !== void 0 && (this.nodes.description = d.make("div", we.description, { textContent: e.description }), this.nodes.root.appendChild(this.nodes.description)); } /** * Returns the root element of the hint content */ getElement() { return this.nodes.root; } } class xt { /** * Constructs the instance * * @param params - instance parameters */ constructor(e) { this.params = e; } /** * Item name if exists */ get name() { if (this.params !== void 0 && "name" in this.params) return this.params.name; } /** * Destroys the instance */ destroy() { $e(); } /** * Called when children popover is opened (if exists) */ onChildrenOpen() { var e; this.params !== void 0 && "children" in this.params && typeof ((e = this.params.children) == null ? void 0 : e.onOpen) == "function" && this.params.children.onOpen(); } /** * Called when children popover is closed (if exists) */ onChildrenClose() { var e; this.params !== void 0 && "children" in this.params && typeof ((e = this.params.children) == null ? void 0 : e.onClose) == "function" && this.params.children.onClose(); } /** * Called on popover item click */ handleClick() { var e, t; this.params !== void 0 && "onActivate" in this.params && ((t = (e = this.params).onActivate) == null || t.call(e, this.params)); } /** * Adds hint to the item element if hint data is provided * * @param itemElement - popover item root element to add hint to * @param hintData - hint data */ addHint(e, t) { const o = new as(t); ze(e, o.getElement(), { placement: t.position, hidingDelay: 100 }); } /** * Returns item children that are represented as popover items */ get children() { var e; return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.items) !== void 0 ? this.params.children.items : []; } /** * Returns true if item has any type of children */ get hasChildren() { return this.children.length > 0; } /** * Returns true if item children should be open instantly after popover is opened and not on item click/hover */ get isChildrenOpen() { var e; return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.isOpen) === !0; } /** * True if item children items should be navigatable via keyboard */ get isChildrenFlippable() { var e; return !(this.params === void 0 || !("children" in this.params) || ((e = this.params.children) == null ? void 0 : e.isFlippable) === !1); } /** * Returns true if item has children that should be searchable */ get isChildrenSearchable() { var e; return this.params !== void 0 && "children" in this.params && ((e = this.params.children) == null ? void 0 : e.searchable) === !0; } /** * True if popover should close once item is activated */ get closeOnActivate() { return this.params !== void 0 && "closeOnActivate" in this.params && this.params.closeOnActivate; } /** * True if item is active */ get isActive() { return this.params === void 0 || !("isActive" in this.params) ? !1 : typeof this.params.isActive == "function" ? this.params.isActive() : this.params.isActive === !0; } } const Y = ne("ce-popover-item"), L = { container: Y(), active: Y(null, "active"), disabled: Y(null, "disabled"), focused: Y(null, "focused"), hidden: Y(null, "hidden"), confirmationState: Y(null, "confirmation"), noHover: Y(null, "no-hover"), noFocus: Y(null, "no-focus"), title: Y("title"), secondaryTitle: Y("secondary-title"), icon: Y("icon"), iconTool: Y("icon", "tool"), iconChevronRight: Y("icon", "chevron-right"), wobbleAnimation: ne("wobble")() }; class re extends xt { /** * Constructs popover item instance * * @param params - popover item construction params * @param renderParams - popover item render params. * The parameters that are not set by user via popover api but rather depend on technical implementation */ constructor(e, t) { super(e), this.params = e, this.nodes = { root: null, icon: null }, this.confirmationState = null, this.removeSpecialFocusBehavior = () => { var o; (o = this.nodes.root) == null || o.classList.remove(L.noFocus); }, this.removeSpecialHoverBehavior = () => { var o; (o = this.nodes.root) == null || o.classList.remove(L.noHover); }, this.onErrorAnimationEnd = () => { var o, i; (o = this.nodes.icon) == null || o.classList.remove(L.wobbleAnimation), (i = this.nodes.icon) == null || i.removeEventListener("animationend", this.onErrorAnimationEnd); }, this.nodes.root = this.make(e, t); } /** * True if item is disabled and hence not clickable */ get isDisabled() { return this.params.isDisabled === !0; } /** * Exposes popover item toggle parameter */ get toggle() { return this.params.toggle; } /** * Item title */ get title() { return this.params.title; } /** * True if confirmation state is enabled for popover item */ get isConfirmationStateEnabled() { return this.confirmationState !== null; } /** * True if item is focused in keyboard navigation process */ get isFocused() { return this.nodes.root === null ? !1 : this.nodes.root.classList.contains(L.focused); } /** * Returns popover item root element */ getElement() { return this.nodes.root; } /** * Called on popover item click */ handleClick() { if (this.isConfirmationStateEnabled && this.confirmationState !== null) { this.activateOrEnableConfirmationMode(this.confirmationState); return; } this.activateOrEnableConfirmationMode(this.params); } /** * Toggles item active state * * @param isActive - true if item should strictly should become active */ toggleActive(e) { var t; (t = this.nodes.root) == null || t.classList.toggle(L.active, e); } /** * Toggles item hidden state * * @param isHidden - true if item should be hidden */ toggleHidden(e) { var t; (t = this.nodes.root) == null || t.classList.toggle(L.hidden, e); } /** * Resets popover item to its original state */ reset() { this.isConfirmationStateEnabled && this.disableConfirmationMode(); } /** * Method called once item becomes focused during keyboard navigation */ onFocus() { this.disableSpecialHoverAndFocusBehavior(); } /** * Constructs HTML element corresponding to popover item params * * @param params - item construction params * @param renderParams - popover item render params */ make(e, t) { var s, r; const o = (t == null ? void 0 : t.wrapperTag) || "div", i = d.make(o, L.container, { type: o === "button" ? "button" : void 0 }); return e.name && (i.dataset.itemName = e.name), this.nodes.icon = d.make("div", [L.icon, L.iconTool], { innerHTML: e.icon || Qi }), i.appendChild(this.nodes.icon), e.title !== void 0 && i.appendChild(d.make("div", L.title, { innerHTML: e.title || "" })), e.secondaryLabel && i.appendChild(d.make("div", L.secondaryTitle, { textContent: e.secondaryLabel })), this.hasChildren && i.appendChild(d.make("div", [L.icon, L.iconChevronRight], { innerHTML: qi })), this.isActive && i.classList.add(L.active), e.isDisabled && i.classList.add(L.disabled), e.hint !== void 0 && ((s = t == null ? void 0 : t.hint) == null ? void 0 : s.enabled) !== !1 && this.addHint(i, { ...e.hint, position: ((r = t == null ? void 0 : t.hint) == null ? void 0 : r.position) || "right" }), i; } /** * Activates confirmation mode for the item. * * @param newState - new popover item params that should be applied */ enableConfirmationMode(e) { if (this.nodes.root === null) return; const t = { ...this.params, ...e, confirmation: "confirmation" in e ? e.confirmation : void 0 }, o = this.make(t); this.nodes.root.innerHTML = o.innerHTML, this.nodes.root.classList.add(L.confirmationState), this.confirmationState = e, this.enableSpecialHoverAndFocusBehavior(); } /** * Returns item to its original state */ disableConfirmationMode() { if (this.nodes.root === null) return; const e = this.make(this.params); this.nodes.root.innerHTML = e.innerHTML, this.nodes.root.classList.remove(L.confirmationState), this.confirmationState = null, this.disableSpecialHoverAndFocusBehavior(); } /** * Enables special focus and hover behavior for item in confirmation state. * This is needed to prevent item from being highlighted as hovered/focused just after click. */ enableSpecialHoverAndFocusBehavior() { var e, t, o; (e = this.nodes.root) == null || e.classList.add(L.noHover), (t = this.nodes.root) == null || t.classList.add(L.noFocus), (o = this.nodes.root) == null || o.addEventListener("mouseleave", this.removeSpecialHoverBehavior, { once: !0 }); } /** * Disables special focus and hover behavior */ disableSpecialHoverAndFocusBehavior() { var e; this.removeSpecialFocusBehavior(), this.removeSpecialHoverBehavior(), (e = this.nodes.root) == null || e.removeEventListener("mouseleave", this.removeSpecialHoverBehavior); } /** * Executes item's onActivate callback if the item has no confirmation configured * * @param item - item to activate or bring to confirmation mode */ activateOrEnableConfirmationMode(e) { var t; if (!("confirmation" in e) || e.confirmation === void 0) try { (t = e.onActivate) == null || t.call(e, e), this.disableConfirmationMode(); } catch { this.animateError(); } else this.enableConfirmationMode(e.confirmation); } /** * Animates item which symbolizes that error occured while executing 'onActivate()' callback */ animateError() { var e, t, o; (e = this.nodes.icon) != null && e.classList.contains(L.wobbleAnimation) || ((t = this.nodes.icon) == null || t.classList.add(L.wobbleAnimation), (o = this.nodes.icon) == null || o.addEventListener("animationend", this.onErrorAnimationEnd)); } } const nt = ne("ce-popover-item-separator"), it = { container: nt(), line: nt("line"), hidden: nt(null, "hidden") }; class Qo extends xt { /** * Constructs the instance */ constructor() { super(), this.nodes = { root: d.make("div", it.container), line: d.make("div", it.line) }, this.nodes.root.appendChild(this.nodes.line); } /** * Returns popover separator root element */ getElement() { return this.nodes.root; } /** * Toggles item hidden state * * @param isHidden - true if item should be hidden */ toggleHidden(e) { var t; (t = this.nodes.root) == null || t.classList.toggle(it.hidden, e); } } var G = /* @__PURE__ */ ((n) => (n.Closed = "closed", n.ClosedOnActivate = "closed-on-activate", n))(G || {}); const $ = ne("ce-popover"), P = { popover: $(), popoverContainer: $("container"), popoverOpenTop: $(null, "open-top"), popoverOpenLeft: $(null, "open-left"), popoverOpened: $(null, "opened"), search: $("search"), nothingFoundMessage: $("nothing-found-message"), nothingFoundMessageDisplayed: $("nothing-found-message", "displayed"), items: $("items"), overlay: $("overlay"), overlayHidden: $("overlay", "hidden"), popoverNested: $(null, "nested"), getPopoverNestedClass: (n) => $(null, `nested-level-${n.toString()}`), popoverInline: $(null, "inline"), popoverHeader: $("header") }; var fe = /* @__PURE__ */ ((n) => (n.NestingLevel = "--nesting-level", n.PopoverHeight = "--popover-height", n.InlinePopoverWidth = "--inline-popover-width", n.TriggerItemLeft = "--trigger-item-left", n.TriggerItemTop = "--trigger-item-top", n))(fe || {}); const To = ne("ce-popover-item-html"), So = { root: To(), hidden: To(null, "hidden") }; class Se extends xt { /** * Constructs the instance * * @param params – instance parameters * @param renderParams – popover item render params. * The parameters that are not set by user via popover api but rather depend on technical implementation */ constructor(e, t) { var o, i; super(e), this.nodes = { root: d.make("div", So.root) }, this.nodes.root.appendChild(e.element), e.name && (this.nodes.root.dataset.itemName = e.name), e.hint !== void 0 && ((o = t == null ? void 0 : t.hint) == null ? void 0 : o.enabled) !== !1 && this.addHint(this.nodes.root, { ...e.hint, position: ((i = t == null ? void 0 : t.hint) == null ? void 0 : i.position) || "right" }); } /** * Returns popover item root element */ getElement() { return this.nodes.root; } /** * Toggles item hidden state * * @param isHidden - true if item should be hidden */ toggleHidden(e) { var t; (t = this.nodes.root) == null || t.classList.toggle(So.hidden, e); } /** * Returns list of buttons and inputs inside custom content */ getControls() { const e = this.nodes.root.querySelectorAll( `button, ${d.allInputsSelector}` ); return Array.from(e); } } class Jo extends Oe { /** * Constructs the instance * * @param params - popover construction params * @param itemsRenderParams - popover item render params. * The parameters that are not set by user via popover api but rather depend on technical implementation */ constructor(e, t = {}) { super(), this.params = e, this.itemsRenderParams = t, this.listeners = new _e(), this.messages = { nothingFound: "Nothing found", search: "Search" }, this.items = this.buildItems(e.items), e.messages && (this.messages = { ...this.messages, ...e.messages }), this.nodes = {}, this.nodes.popoverContainer = d.make("div", [P.popoverContainer]), this.nodes.nothingFoundMessage = d.make("div", [P.nothingFoundMessage], { textContent: this.messages.nothingFound }), this.nodes.popoverContainer.appendChild(this.nodes.nothingFoundMessage), this.nodes.items = d.make("div", [P.items]), this.items.forEach((o) => { const i = o.getElement(); i !== null && this.nodes.items.appendChild(i); }), this.nodes.popoverContainer.appendChild(this.nodes.items), this.listeners.on(this.nodes.popoverContainer, "click", (o) => this.handleClick(o)), this.nodes.popover = d.make("div", [ P.popover, this.params.class ]), this.nodes.popover.appendChild(this.nodes.popoverContainer); } /** * List of default popover items that are searchable and may have confirmation state */ get itemsDefault() { return this.items.filter((e) => e instanceof re); } /** * Returns HTML element corresponding to the popover */ getElement() { return this.nodes.popover; } /** * Open popover */ show() { this.nodes.popover.classList.add(P.popoverOpened), this.search !== void 0 && this.search.focus(); } /** * Closes popover */ hide() { this.nodes.popover.classList.remove(P.popoverOpened), this.nodes.popover.classList.remove(P.popoverOpenTop), this.itemsDefault.forEach((e) => e.reset()), this.search !== void 0 && this.search.clear(), this.emit(G.Closed); } /** * Clears memory */ destroy() { var e; this.items.forEach((t) => t.destroy()), this.nodes.popover.remove(), this.listeners.removeAll(), (e = this.search) == null || e.destroy(); } /** * Looks for the item by name and imitates click on it * * @param name - name of the item to activate */ activateItemByName(e) { const t = this.items.find((o) => o.name === e); this.handleItemClick(t); } /** * Factory method for creating popover items * * @param items - list of items params */ buildItems(e) { return e.map((t) => { switch (t.type) { case _.Separator: return new Qo(); case _.Html: return new Se(t, this.itemsRenderParams[_.Html]); default: return new re(t, this.itemsRenderParams[_.Default]); } }); } /** * Retrieves popover item that is the target of the specified event * * @param event - event to retrieve popover item from */ getTargetItem(e) { return this.items.filter((t) => t instanceof re || t instanceof Se).find((t) => { const o = t.getElement(); return o === null ? !1 : e.composedPath().includes(o); }); } /** * Handles popover item click * * @param item - item to handle click of */ handleItemClick(e) { if (!("isDisabled" in e && e.isDisabled)) { if (e.hasChildren) { this.showNestedItems(e), "handleClick" in e && typeof e.handleClick == "function" && e.handleClick(); return; } this.itemsDefault.filter((t) => t !== e).forEach((t) => t.reset()), "handleClick" in e && typeof e.handleClick == "function" && e.handleClick(), this.toggleItemActivenessIfNeeded(e), e.closeOnActivate && (this.hide(), this.emit(G.ClosedOnActivate)); } } /** * Handles clicks inside popover * * @param event - item to handle click of */ handleClick(e) { const t = this.getTargetItem(e); t !== void 0 && this.handleItemClick(t); } /** * - Toggles item active state, if clicked popover item has property 'toggle' set to true. * * - Performs radiobutton-like behavior if the item has property 'toggle' set to string key. * (All the other items with the same key get inactive, and the item gets active) * * @param clickedItem - popover item that was clicked */ toggleItemActivenessIfNeeded(e) { if (e instanceof re && (e.toggle === !0 && e.toggleActive(), typeof e.toggle == "string")) { const t = this.itemsDefault.filter((o) => o.toggle === e.toggle); if (t.length === 1) { e.toggleActive(); return; } t.forEach((o) => { o.toggleActive(o === e); }); } } } var Ue = /* @__PURE__ */ ((n) => (n.Search = "search", n))(Ue || {}); const st = ne("cdx-search-field"), rt = { wrapper: st(), icon: st("icon"), input: st("input") }; class ls extends Oe { /** * @param options - available config * @param options.items - searchable items list * @param options.placeholder - input placeholder */ constructor({ items: e, placeholder: t }) { super(), this.listeners = new _e(), this.items = e, this.wrapper = d.make("div", rt.wrapper); const o = d.make("div", rt.icon, { innerHTML: os }); this.input = d.make("input", rt.input, { placeholder: t, /** * Used to prevent focusing on the input by Tab key * (Popover in the Toolbar lays below the blocks, * so Tab in the last block will focus this hidden input if this property is not set) */ tabIndex: -1 }), this.wrapper.appendChild(o), this.wrapper.appendChild(this.input), this.listeners.on(this.input, "input", () => { this.searchQuery = this.input.value, this.emit(Ue.Search, { query: this.searchQuery, items: this.foundItems }); }); } /** * Returns search field element */ getElement() { return this.wrapper; } /** * Sets focus to the input */ focus() { this.input.focus(); } /** * Clears search query and results */ clear() { this.input.value = "", this.searchQuery = "", this.emit(Ue.Search, { query: "", items: this.foundItems }); } /** * Clears memory */ destroy() { this.listeners.removeAll(); } /** * Returns list of found items for the current search query */ get foundItems() { return this.items.filter((e) => this.checkItem(e)); } /** * Contains logic for checking whether passed item conforms the search query * * @param item - item to be checked */ checkItem(e) { var i, s; const t = ((i = e.title) == null ? void 0 : i.toLowerCase()) || "", o = (s = this.searchQuery) == null ? void 0 : s.toLowerCase(); return o !== void 0 ? t.includes(o) : !1; } } var cs = Object.defineProperty, ds = Object.getOwnPropertyDescriptor, us = (n, e, t, o) => { for (var i = o > 1 ? void 0 : o ? ds(e, t) : e, s = n.length - 1, r; s >= 0; s--) (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); return o && i && cs(e, t, i), i; }; const en = class tn extends Jo { /** * Construct the instance * * @param params - popover params * @param itemsRenderParams – popover item render params. * The parameters that are not set by user via popover api but rather depend on technical implementation */ constructor(e, t) { super(e, t), this.nestingLevel = 0, this.nestedPopoverTriggerItem = null, this.previouslyHoveredItem = null, this.scopeElement = document.body, this.hide = () => { var o; super.hide(), this.destroyNestedPopoverIfExists(), (o = this.flipper) == null || o.deactivate(), this.previouslyHoveredItem = null; }, this.onFlip = () => { const o = this.itemsDefault.find((i) => i.isFocused); o == null || o.onFocus(); }, this.onSearch = (o) => { var a; const i = o.query === "", s = o.items.length === 0; this.items.forEach((l) => { let c = !1; l instanceof re ? c = !o.items.includes(l) : (l instanceof Qo || l instanceof Se) && (c = s || !i), l.toggleHidden(c); }), this.toggleNothingFoundMessage(s); const r = o.query === "" ? this.flippableElements : o.items.map((l) => l.getElement()); (a = this.flipper) != null && a.isActivated && (this.flipper.deactivate(), this.flipper.activate(r)); }, e.nestingLevel !== void 0 && (this.nestingLevel = e.nestingLevel), this.nestingLevel > 0 && this.nodes.popover.classList.add(P.popoverNested), e.scopeElement !== void 0 && (this.scopeElement = e.scopeElement), this.nodes.popoverContainer !== null && this.listeners.on(this.nodes.popoverContainer, "mouseover", (o) => this.handleHover(o)), e.searchable && this.addSearch(), e.flippable !== !1 && (this.flipper = new ce({ items: this.flippableElements, focusedItemClass: L.focused, allowedKeys: [ y.TAB, y.UP, y.DOWN, y.ENTER ] }), this.flipper.onFlip(this.onFlip)); } /** * Returns true if some item inside popover is focused */ hasFocus() { return this.flipper === void 0 ? !1 : this.flipper.hasFocus(); } /** * Scroll position inside items container of the popover */ get scrollTop() { return this.nodes.items === null ? 0 : this.nodes.items.scrollTop; } /** * Returns visible element offset top */ get offsetTop() { return this.nodes.popoverContainer === null ? 0 : this.nodes.popoverContainer.offsetTop; } /** * Open popover */ show() { var e; this.nodes.popover.style.setProperty(fe.PopoverHeight, this.size.height + "px"), this.shouldOpenBottom || this.nodes.popover.classList.add(P.popoverOpenTop), this.shouldOpenRight || this.nodes.popover.classList.add(P.popoverOpenLeft), super.show(), (e = this.flipper) == null || e.activate(this.flippableElements); } /** * Clears memory */ destroy() { this.hide(), super.destroy(); } /** * Handles displaying nested items for the item. * * @param item – item to show nested popover for */ showNestedItems(e) { this.nestedPopover !== null && this.nestedPopover !== void 0 || (this.nestedPopoverTriggerItem = e, this.showNestedPopoverForItem(e)); } /** * Handles hover events inside popover items container * * @param event - hover event data */ handleHover(e) { const t = this.getTargetItem(e); t !== void 0 && this.previouslyHoveredItem !== t && (this.destroyNestedPopoverIfExists(), this.previouslyHoveredItem = t, t.hasChildren && this.showNestedPopoverForItem(t)); } /** * Sets CSS variable with position of item near which nested popover should be displayed. * Is used for correct positioning of the nested popover * * @param nestedPopoverEl - nested popover element * @param item – item near which nested popover should be displayed */ setTriggerItemPosition(e, t) { const o = t.getElement(), i = (o ? o.offsetTop : 0) - this.scrollTop, s = this.offsetTop + i; e.style.setProperty(fe.TriggerItemTop, s + "px"); } /** * Destroys existing nested popover */ destroyNestedPopoverIfExists() { var e, t; this.nestedPopover === void 0 || this.nestedPopover === null || (this.nestedPopover.off(G.ClosedOnActivate, this.hide), this.nestedPopover.hide(), this.nestedPopover.destroy(), this.nestedPopover.getElement().remove(), this.nestedPopover = null, (e = this.flipper) == null || e.activate(this.flippableElements), (t = this.nestedPopoverTriggerItem) == null || t.onChildrenClose()); } /** * Creates and displays nested popover for specified item. * Is used only on desktop * * @param item - item to display nested popover by */ showNestedPopoverForItem(e) { var o; this.nestedPopover = new tn({ searchable: e.isChildrenSearchable, items: e.children, nestingLevel: this.nestingLevel + 1, flippable: e.isChildrenFlippable, messages: this.messages }), e.onChildrenOpen(), this.nestedPopover.on(G.ClosedOnActivate, this.hide); const t = this.nestedPopover.getElement(); return this.nodes.popover.appendChild(t), this.setTriggerItemPosition(t, e), t.style.setProperty(fe.NestingLevel, this.nestedPopover.nestingLevel.toString()), this.nestedPopover.show(), (o = this.flipper) == null || o.deactivate(), this.nestedPopover; } /** * Checks if popover should be opened bottom. * It should happen when there is enough space below or not enough space above */ get shouldOpenBottom() { if (this.nodes.popover === void 0 || this.nodes.popover === null) return !1; const e = this.nodes.popoverContainer.getBoundingClientRect(), t = this.scopeElement.getBoundingClientRect(), o = this.size.height, i = e.top + o, s = e.top - o, r = Math.min(window.innerHeight, t.bottom); return s < t.top || i <= r; } /** * Checks if popover should be opened left. * It should happen when there is enough space in the right or not enough space in the left */ get shouldOpenRight() { if (this.nodes.popover === void 0 || this.nodes.popover === null) return !1; const e = this.nodes.popover.getBoundingClientRect(), t = this.scopeElement.getBoundingClientRect(), o = this.size.width, i = e.right + o, s = e.left - o, r = Math.min(window.innerWidth, t.right); return s < t.left || i <= r; } get size() { var i; const e = { height: 0, width: 0 }; if (this.nodes.popover === null) return e; const t = this.nodes.popover.cloneNode(!0); t.style.visibility = "hidden", t.style.position = "absolute", t.style.top = "-1000px", t.classList.add(P.popoverOpened), (i = t.querySelector("." + P.popoverNested)) == null || i.remove(), document.body.appendChild(t); const o = t.querySelector("." + P.popoverContainer); return e.height = o.offsetHeight, e.width = o.offsetWidth, t.remove(), e; } /** * Returns list of elements available for keyboard navigation. */ get flippableElements() { return this.items.map((t) => { if (t instanceof re) return t.getElement(); if (t instanceof Se) return t.getControls(); }).flat().filter((t) => t != null); } /** * Adds search to the popover */ addSearch() { this.search = new ls({ items: this.itemsDefault, placeholder: this.messages.search }), this.search.on(Ue.Search, this.onSearch); const e = this.search.getElement(); e.classList.add(P.search), this.nodes.popoverContainer.insertBefore(e, this.nodes.popoverContainer.firstChild); } /** * Toggles nothing found message visibility * * @param isDisplayed - true if the message should be displayed */ toggleNothingFoundMessage(e) { this.nodes.nothingFoundMessage.classList.toggle(P.nothingFoundMessageDisplayed, e); } }; us([ me ], en.prototype, "size", 1); let Bt = en; class hs extends Bt { /** * Constructs the instance * * @param params - instance parameters */ constructor(e) { const t = !be(); super( { ...e, class: P.popoverInline }, { [_.Default]: { /** * We use button instead of div here to fix bug associated with focus loss (which leads to selection change) on click in safari * * @todo figure out better way to solve the issue */ wrapperTag: "button", hint: { position: "top", alignment: "center", enabled: t } }, [_.Html]: { hint: { position: "top", alignment: "center", enabled: t } } } ), this.items.forEach((o) => { !(o instanceof re) && !(o instanceof Se) || o.hasChildren && o.isChildrenOpen && this.showNestedItems(o); }); } /** * Returns visible element offset top */ get offsetLeft() { return this.nodes.popoverContainer === null ? 0 : this.nodes.popoverContainer.offsetLeft; } /** * Open popover */ show() { this.nestingLevel === 0 && this.nodes.popover.style.setProperty( fe.InlinePopoverWidth, this.size.width + "px" ), super.show(); } /** * Disable hover event handling. * Overrides parent's class behavior */ handleHover() { } /** * Sets CSS variable with position of item near which nested popover should be displayed. * Is used to position nested popover right below clicked item * * @param nestedPopoverEl - nested popover element * @param item – item near which nested popover should be displayed */ setTriggerItemPosition(e, t) { const o = t.getElement(), i = o ? o.offsetLeft : 0, s = this.offsetLeft + i; e.style.setProperty( fe.TriggerItemLeft, s + "px" ); } /** * Handles displaying nested items for the item. * Overriding in order to add toggling behaviour * * @param item – item to toggle nested popover for */ showNestedItems(e) { if (this.nestedPopoverTriggerItem === e) { this.destroyNestedPopoverIfExists(), this.nestedPopoverTriggerItem = null; return; } super.showNestedItems(e); } /** * Creates and displays nested popover for specified item. * Is used only on desktop * * @param item - item to display nested popover by */ showNestedPopoverForItem(e) { const t = super.showNestedPopoverForItem(e); return t.getElement().classList.add(P.getPopoverNestedClass(t.nestingLevel)), t; } /** * Overrides default item click handling. * Helps to close nested popover once other item is clicked. * * @param item - clicked item */ handleItemClick(e) { var t; e !== this.nestedPopoverTriggerItem && ((t = this.nestedPopoverTriggerItem) == null || t.handleClick(), super.destroyNestedPopoverIfExists()), super.handleItemClick(e); } } const on = class xe { constructor() { this.scrollPosition = null; } /** * Locks body element scroll */ lock() { pt ? this.lockHard() : document.body.classList.add(xe.CSS.scrollLocked); } /** * Unlocks body element scroll */ unlock() { pt ? this.unlockHard() : document.body.classList.remove(xe.CSS.scrollLocked); } /** * Locks scroll in a hard way (via setting fixed position to body element) */ lockHard() { this.scrollPosition = window.pageYOffset, document.documentElement.style.setProperty( "--window-scroll-offset", `${this.scrollPosition}px` ), document.body.classList.add(xe.CSS.scrollLockedHard); } /** * Unlocks hard scroll lock */ unlockHard() { document.body.classList.remove(xe.CSS.scrollLockedHard), this.scrollPosition !== null && window.scrollTo(0, this.scrollPosition), this.scrollPosition = null; } }; on.CSS = { scrollLocked: "ce-scroll-locked", scrollLockedHard: "ce-scroll-locked--hard" }; let ps = on; const at = ne("ce-popover-header"), lt = { root: at(), text: at("text"), backButton: at("back-button") }; class fs { /** * Constructs the instance * * @param params - popover header params */ constructor({ text: e, onBackButtonClick: t }) { this.listeners = new _e(), this.text = e, this.onBackButtonClick = t, this.nodes = { root: d.make("div", [lt.root]), backButton: d.make("button", [lt.backButton]), text: d.make("div", [lt.text]) }, this.nodes.backButton.innerHTML = Vi, this.nodes.root.appendChild(this.nodes.backButton), this.listeners.on(this.nodes.backButton, "click", this.onBackButtonClick), this.nodes.text.innerText = this.text, this.nodes.root.appendChild(this.nodes.text); } /** * Returns popover header root html element */ getElement() { return this.nodes.root; } /** * Destroys the instance */ destroy() { this.nodes.root.remove(), this.listeners.destroy(); } } class gs { constructor() { this.history = []; } /** * Push new popover state * * @param state - new state */ push(e) { this.history.push(e); } /** * Pop last popover state */ pop() { return this.history.pop(); } /** * Title retrieved from the current state */ get currentTitle() { return this.history.length === 0 ? "" : this.history[this.history.length - 1].title; } /** * Items list retrieved from the current state */ get currentItems() { return this.history.length === 0 ? [] : this.history[this.history.length - 1].items; } /** * Returns history to initial popover state */ reset() { for (; this.history.length > 1; ) this.pop(); } } class nn extends Jo { /** * Construct the instance * * @param params - popover params */ constructor(e) { super(e, { [_.Default]: { hint: { enabled: !1 } }, [_.Html]: { hint: { enabled: !1 } } }), this.scrollLocker = new ps(), this.history = new gs(), this.isHidden = !0, this.nodes.overlay = d.make("div", [P.overlay, P.overlayHidden]), this.nodes.popover.insertBefore(this.nodes.overlay, this.nodes.popover.firstChild), this.listeners.on(this.nodes.overlay, "click", () => { this.hide(); }), this.history.push({ items: e.items }); } /** * Open popover */ show() { this.nodes.overlay.classList.remove(P.overlayHidden), super.show(), this.scrollLocker.lock(), this.isHidden = !1; } /** * Closes popover */ hide() { this.isHidden || (super.hide(), this.nodes.overlay.classList.add(P.overlayHidden), this.scrollLocker.unlock(), this.history.reset(), this.isHidden = !0); } /** * Clears memory */ destroy() { super.destroy(), this.scrollLocker.unlock(); } /** * Handles displaying nested items for the item * * @param item – item to show nested popover for */ showNestedItems(e) { this.updateItemsAndHeader(e.children, e.title), this.history.push({ title: e.title, items: e.children }); } /** * Removes rendered popover items and header and displays new ones * * @param items - new popover items * @param title - new popover header text */ updateItemsAndHeader(e, t) { if (this.header !== null && this.header !== void 0 && (this.header.destroy(), this.header = null), t !== void 0) { this.header = new fs({ text: t, onBackButtonClick: () => { this.history.pop(), this.updateItemsAndHeader(this.history.currentItems, this.history.currentTitle); } }); const o = this.header.getElement(); o !== null && this.nodes.popoverContainer.insertBefore(o, this.nodes.popoverContainer.firstChild); } this.items.forEach((o) => { var i; return (i = o.getElement()) == null ? void 0 : i.remove(); }), this.items = this.buildItems(e), this.items.forEach((o) => { var s; const i = o.getElement(); i !== null && ((s = this.nodes.items) == null || s.appendChild(i)); }); } } class ms extends E { constructor() { super(...arguments), this.opened = !1, this.selection = new b(), this.popover = null, this.close = () => { this.opened && (this.opened = !1, b.isAtEditor || this.selection.restore(), this.selection.clearSaved(), !this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted && this.Editor.BlockManager.currentBlock && this.Editor.BlockSelection.unselectBlock(this.Editor.BlockManager.currentBlock), this.eventsDispatcher.emit(this.events.closed), this.popover && (this.popover.off(G.Closed, this.onPopoverClose), this.popover.destroy(), this.popover.getElement().remove(), this.popover = null)); }, this.onPopoverClose = () => { this.close(); }; } /** * Module Events */ get events() { return { opened: "block-settings-opened", closed: "block-settings-closed" }; } /** * Block Settings CSS */ get CSS() { return { settings: "ce-settings" }; } /** * Getter for inner popover's flipper instance * * @todo remove once BlockSettings becomes standalone non-module class */ get flipper() { var e; if (this.popover !== null) return "flipper" in this.popover ? (e = this.popover) == null ? void 0 : e.flipper : void 0; } /** * Panel with block settings with 2 sections: * - Tool's Settings * - Default Settings [Move, Remove, etc] */ make() { this.nodes.wrapper = d.make("div", [this.CSS.settings]), this.eventsDispatcher.on(Te, this.close); } /** * Destroys module */ destroy() { this.removeAllNodes(), this.listeners.destroy(), this.eventsDispatcher.off(Te, this.close); } /** * Open Block Settings pane * * @param targetBlock - near which Block we should open BlockSettings */ async open(e = this.Editor.BlockManager.currentBlock) { var s; this.opened = !0, this.selection.save(), this.Editor.BlockSelection.selectBlock(e), this.Editor.BlockSelection.clearCache(); const { toolTunes: t, commonTunes: o } = e.getTunes(); this.eventsDispatcher.emit(this.events.opened); const i = be() ? nn : Bt; this.popover = new i({ searchable: !0, items: await this.getTunesItems(e, o, t), scopeElement: this.Editor.API.methods.ui.nodes.redactor, messages: { nothingFound: z.ui(K.ui.popover, "Nothing found"), search: z.ui(K.ui.popover, "Filter") } }), this.popover.on(G.Closed, this.onPopoverClose), (s = this.nodes.wrapper) == null || s.append(this.popover.getElement()), this.popover.show(); } /** * Returns root block settings element */ getElement() { return this.nodes.wrapper; } /** * Returns list of items to be displayed in block tunes menu. * Merges tool specific tunes, conversion menu and common tunes in one list in predefined order * * @param currentBlock – block we are about to open block tunes for * @param commonTunes – common tunes * @param toolTunes - tool specific tunes */ async getTunesItems(e, t, o) { const i = []; o !== void 0 && o.length > 0 && (i.push(...o), i.push({ type: _.Separator })); const s = Array.from(this.Editor.Tools.blockTools.values()), a = (await Yo(e, s)).reduce((l, c) => (c.toolbox.forEach((u) => { l.push({ icon: u.icon, title: z.t(K.toolNames, u.title), name: c.name, closeOnActivate: !0, onActivate: async () => { const { BlockManager: h, Caret: p, Toolbar: g } = this.Editor, f = await h.convert(e, c.name, u.data); g.close(), p.setToBlock(f, p.positions.END); } }); }), l), []); return a.length > 0 && (i.push({ icon: Go, name: "convert-to", title: z.ui(K.ui.popover, "Convert to"), children: { searchable: !0, items: a } }), i.push({ type: _.Separator })), i.push(...t), i.map((l) => this.resolveTuneAliases(l)); } /** * Resolves aliases in tunes menu items * * @param item - item with resolved aliases */ resolveTuneAliases(e) { if (e.type === _.Separator || e.type === _.Html) return e; const t = Yi(e, { label: "title" }); return e.confirmation && (t.confirmation = this.resolveTuneAliases(e.confirmation)), t; } } var sn = { exports: {} }; /*! * Library for handling keyboard shortcuts * @copyright CodeX (https://codex.so) * @license MIT * @author CodeX (https://codex.so) * @version 1.2.0 */ (function(n, e) { (function(t, o) { n.exports = o(); })(window, function() { return function(t) { var o = {}; function i(s) { if (o[s]) return o[s].exports; var r = o[s] = { i: s, l: !1, exports: {} }; return t[s].call(r.exports, r, r.exports, i), r.l = !0, r.exports; } return i.m = t, i.c = o, i.d = function(s, r, a) { i.o(s, r) || Object.defineProperty(s, r, { enumerable: !0, get: a }); }, i.r = function(s) { typeof Symbol < "u" && Symbol.toStringTag && Object.defineProperty(s, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(s, "__esModule", { value: !0 }); }, i.t = function(s, r) { if (1 & r && (s = i(s)), 8 & r || 4 & r && typeof s == "object" && s && s.__esModule) return s; var a = /* @__PURE__ */ Object.create(null); if (i.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: s }), 2 & r && typeof s != "string") for (var l in s) i.d(a, l, (function(c) { return s[c]; }).bind(null, l)); return a; }, i.n = function(s) { var r = s && s.__esModule ? function() { return s.default; } : function() { return s; }; return i.d(r, "a", r), r; }, i.o = function(s, r) { return Object.prototype.hasOwnProperty.call(s, r); }, i.p = "", i(i.s = 0); }([function(t, o, i) { function s(l, c) { for (var u = 0; u < c.length; u++) { var h = c[u]; h.enumerable = h.enumerable || !1, h.configurable = !0, "value" in h && (h.writable = !0), Object.defineProperty(l, h.key, h); } } function r(l, c, u) { return c && s(l.prototype, c), u && s(l, u), l; } i.r(o); var a = function() { function l(c) { var u = this; (function(h, p) { if (!(h instanceof p)) throw new TypeError("Cannot call a class as a function"); })(this, l), this.commands = {}, this.keys = {}, this.name = c.name, this.parseShortcutName(c.name), this.element = c.on, this.callback = c.callback, this.executeShortcut = function(h) { u.execute(h); }, this.element.addEventListener("keydown", this.executeShortcut, !1); } return r(l, null, [{ key: "supportedCommands", get: function() { return { SHIFT: ["SHIFT"], CMD: ["CMD", "CONTROL", "COMMAND", "WINDOWS", "CTRL"], ALT: ["ALT", "OPTION"] }; } }, { key: "keyCodes", get: function() { return { 0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, BACKSPACE: 8, ENTER: 13, ESCAPE: 27, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40, INSERT: 45, DELETE: 46, ".": 190 }; } }]), r(l, [{ key: "parseShortcutName", value: function(c) { c = c.split("+"); for (var u = 0; u < c.length; u++) { c[u] = c[u].toUpperCase(); var h = !1; for (var p in l.supportedCommands) if (l.supportedCommands[p].includes(c[u])) { h = this.commands[p] = !0; break; } h || (this.keys[c[u]] = !0); } for (var g in l.supportedCommands) this.commands[g] || (this.commands[g] = !1); } }, { key: "execute", value: function(c) { var u, h = { CMD: c.ctrlKey || c.metaKey, SHIFT: c.shiftKey, ALT: c.altKey }, p = !0; for (u in this.commands) this.commands[u] !== h[u] && (p = !1); var g, f = !0; for (g in this.keys) f = f && c.keyCode === l.keyCodes[g]; p && f && this.callback(c); } }, { key: "remove", value: function() { this.element.removeEventListener("keydown", this.executeShortcut); } }]), l; }(); o.default = a; }]).default; }); })(sn); var bs = sn.exports; const vs = /* @__PURE__ */ Ke(bs); class ks { constructor() { this.registeredShortcuts = /* @__PURE__ */ new Map(); } /** * Register shortcut * * @param shortcut - shortcut options */ add(e) { if (this.findShortcut(e.on, e.name)) throw Error( `Shortcut ${e.name} is already registered for ${e.on}. Please remove it before add a new handler.` ); const o = new vs({ name: e.name, on: e.on, callback: e.handler }), i = this.registeredShortcuts.get(e.on) || []; this.registeredShortcuts.set(e.on, [...i, o]); } /** * Remove shortcut * * @param element - Element shortcut is set for * @param name - shortcut name */ remove(e, t) { const o = this.findShortcut(e, t); if (!o) return; o.remove(); const s = this.registeredShortcuts.get(e).filter((r) => r !== o); if (s.length === 0) { this.registeredShortcuts.delete(e); return; } this.registeredShortcuts.set(e, s); } /** * Get Shortcut instance if exist * * @param element - Element shorcut is set for * @param shortcut - shortcut name * @returns {number} index - shortcut index if exist */ findShortcut(e, t) { return (this.registeredShortcuts.get(e) || []).find(({ name: i }) => i === t); } } const ge = new ks(); var ys = Object.defineProperty, ws = Object.getOwnPropertyDescriptor, rn = (n, e, t, o) => { for (var i = o > 1 ? void 0 : o ? ws(e, t) : e, s = n.length - 1, r; s >= 0; s--) (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); return o && i && ys(e, t, i), i; }, Le = /* @__PURE__ */ ((n) => (n.Opened = "toolbox-opened", n.Closed = "toolbox-closed", n.BlockAdded = "toolbox-block-added", n))(Le || {}); const Ct = class an extends Oe { /** * Toolbox constructor * * @param options - available parameters * @param options.api - Editor API methods * @param options.tools - Tools available to check whether some of them should be displayed at the Toolbox or not */ constructor({ api: e, tools: t, i18nLabels: o }) { super(), this.opened = !1, this.listeners = new _e(), this.popover = null, this.handleMobileLayoutToggle = () => { this.destroyPopover(), this.initPopover(); }, this.onPopoverClose = () => { this.opened = !1, this.emit( "toolbox-closed" /* Closed */ ); }, this.api = e, this.tools = t, this.i18nLabels = o, this.enableShortcuts(), this.nodes = { toolbox: d.make("div", an.CSS.toolbox) }, this.initPopover(), this.api.events.on(Te, this.handleMobileLayoutToggle); } /** * Returns True if Toolbox is Empty and nothing to show * * @returns {boolean} */ get isEmpty() { return this.toolsToBeDisplayed.length === 0; } /** * CSS styles */ static get CSS() { return { toolbox: "ce-toolbox" }; } /** * Returns root block settings element */ getElement() { return this.nodes.toolbox; } /** * Returns true if the Toolbox has the Flipper activated and the Flipper has selected button */ hasFocus() { if (this.popover !== null) return "hasFocus" in this.popover ? this.popover.hasFocus() : void 0; } /** * Destroy Module */ destroy() { var e; super.destroy(), this.nodes && this.nodes.toolbox && this.nodes.toolbox.remove(), this.removeAllShortcuts(), (e = this.popover) == null || e.off(G.Closed, this.onPopoverClose), this.listeners.destroy(), this.api.events.off(Te, this.handleMobileLayoutToggle); } /** * Toolbox Tool's button click handler * * @param toolName - tool type to be activated * @param blockDataOverrides - Block data predefined by the activated Toolbox item */ toolButtonActivated(e, t) { this.insertNewBlock(e, t); } /** * Open Toolbox with Tools */ open() { var e; this.isEmpty || ((e = this.popover) == null || e.show(), this.opened = !0, this.emit( "toolbox-opened" /* Opened */ )); } /** * Close Toolbox */ close() { var e; (e = this.popover) == null || e.hide(), this.opened = !1, this.emit( "toolbox-closed" /* Closed */ ); } /** * Close Toolbox */ toggle() { this.opened ? this.close() : this.open(); } /** * Creates toolbox popover and appends it inside wrapper element */ initPopover() { var t; const e = be() ? nn : Bt; this.popover = new e({ scopeElement: this.api.ui.nodes.redactor, searchable: !0, messages: { nothingFound: this.i18nLabels.nothingFound, search: this.i18nLabels.filter }, items: this.toolboxItemsToBeDisplayed }), this.popover.on(G.Closed, this.onPopoverClose), (t = this.nodes.toolbox) == null || t.append(this.popover.getElement()); } /** * Destroys popover instance and removes it from DOM */ destroyPopover() { this.popover !== null && (this.popover.hide(), this.popover.off(G.Closed, this.onPopoverClose), this.popover.destroy(), this.popover = null), this.nodes.toolbox !== null && (this.nodes.toolbox.innerHTML = ""); } get toolsToBeDisplayed() { const e = []; return this.tools.forEach((t) => { t.toolbox && e.push(t); }), e; } get toolboxItemsToBeDisplayed() { const e = (t, o, i = !0) => ({ icon: t.icon, title: z.t(K.toolNames, t.title || je(o.name)), name: o.name, onActivate: () => { this.toolButtonActivated(o.name, t.data); }, secondaryLabel: o.shortcut && i ? vt(o.shortcut) : "" }); return this.toolsToBeDisplayed.reduce((t, o) => (Array.isArray(o.toolbox) ? o.toolbox.forEach((i, s) => { t.push(e(i, o, s === 0)); }) : o.toolbox !== void 0 && t.push(e(o.toolbox, o)), t), []); } /** * Iterate all tools and enable theirs shortcuts if specified */ enableShortcuts() { this.toolsToBeDisplayed.forEach((e) => { const t = e.shortcut; t && this.enableShortcutForTool(e.name, t); }); } /** * Enable shortcut Block Tool implemented shortcut * * @param {string} toolName - Tool name * @param {string} shortcut - shortcut according to the ShortcutData Module format */ enableShortcutForTool(e, t) { ge.add({ name: t, on: this.api.ui.nodes.redactor, handler: async (o) => { o.preventDefault(); const i = this.api.blocks.getCurrentBlockIndex(), s = this.api.blocks.getBlockByIndex(i); if (s) try { const r = await this.api.blocks.convert(s.id, e); this.api.caret.setToBlock(r, "end"); return; } catch { } this.insertNewBlock(e); } }); } /** * Removes all added shortcuts * Fired when the Read-Only mode is activated */ removeAllShortcuts() { this.toolsToBeDisplayed.forEach((e) => { const t = e.shortcut; t && ge.remove(this.api.ui.nodes.redactor, t); }); } /** * Inserts new block * Can be called when button clicked on Toolbox or by ShortcutData * * @param {string} toolName - Tool name * @param blockDataOverrides - predefined Block data */ async insertNewBlock(e, t) { const o = this.api.blocks.getCurrentBlockIndex(), i = this.api.blocks.getBlockByIndex(o); if (!i) return; const s = i.isEmpty ? o : o + 1; let r; if (t) { const l = await this.api.blocks.composeBlockData(e); r = Object.assign(l, t); } const a = this.api.blocks.insert( e, r, void 0, s, void 0, i.isEmpty ); a.call(ee.APPEND_CALLBACK), this.api.caret.setToBlock(s), this.emit("toolbox-block-added", { block: a }), this.api.toolbar.close(); } }; rn([ me ], Ct.prototype, "toolsToBeDisplayed", 1); rn([ me ], Ct.prototype, "toolboxItemsToBeDisplayed", 1); let Es = Ct; const ln = "block hovered"; async function xs(n, e) { const t = navigator.keyboard; if (!t) return e; try { return (await t.getLayoutMap()).get(n) || e; } catch (o) { return console.error(o), e; } } class Bs extends E { /** * @class * @param moduleConfiguration - Module Configuration * @param moduleConfiguration.config - Editor's config * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher */ constructor({ config: e, eventsDispatcher: t }) { super({ config: e, eventsDispatcher: t }), this.toolboxInstance = null; } /** * CSS styles * * @returns {object} */ get CSS() { return { toolbar: "ce-toolbar", content: "ce-toolbar__content", actions: "ce-toolbar__actions", actionsOpened: "ce-toolbar__actions--opened", toolbarOpened: "ce-toolbar--opened", openedToolboxHolderModifier: "codex-editor--toolbox-opened", plusButton: "ce-toolbar__plus", plusButtonShortcut: "ce-toolbar__plus-shortcut", settingsToggler: "ce-toolbar__settings-btn", settingsTogglerHidden: "ce-toolbar__settings-btn--hidden" }; } /** * Returns the Toolbar opening state * * @returns {boolean} */ get opened() { return this.nodes.wrapper.classList.contains(this.CSS.toolbarOpened); } /** * Public interface for accessing the Toolbox */ get toolbox() { var e; return { opened: (e = this.toolboxInstance) == null ? void 0 : e.opened, close: () => { var t; (t = this.toolboxInstance) == null || t.close(); }, open: () => { if (this.toolboxInstance === null) { S("toolbox.open() called before initialization is finished", "warn"); return; } this.Editor.BlockManager.currentBlock = this.hoveredBlock, this.toolboxInstance.open(); }, toggle: () => { if (this.toolboxInstance === null) { S("toolbox.toggle() called before initialization is finished", "warn"); return; } this.toolboxInstance.toggle(); }, hasFocus: () => { var t; return (t = this.toolboxInstance) == null ? void 0 : t.hasFocus(); } }; } /** * Block actions appearance manipulations */ get blockActions() { return { hide: () => { this.nodes.actions.classList.remove(this.CSS.actionsOpened); }, show: () => { this.nodes.actions.classList.add(this.CSS.actionsOpened); } }; } /** * Methods for working with Block Tunes toggler */ get blockTunesToggler() { return { hide: () => this.nodes.settingsToggler.classList.add(this.CSS.settingsTogglerHidden), show: () => this.nodes.settingsToggler.classList.remove(this.CSS.settingsTogglerHidden) }; } /** * Toggles read-only mode * * @param {boolean} readOnlyEnabled - read-only mode */ toggleReadOnly(e) { e ? (this.destroy(), this.Editor.BlockSettings.destroy(), this.disableModuleBindings()) : window.requestIdleCallback(() => { this.drawUI(), this.enableModuleBindings(); }, { timeout: 2e3 }); } /** * Move Toolbar to the passed (or current) Block * * @param block - block to move Toolbar near it */ moveAndOpen(e = this.Editor.BlockManager.currentBlock) { if (this.toolboxInstance === null) { S("Can't open Toolbar since Editor initialization is not finished yet", "warn"); return; } if (this.toolboxInstance.opened && this.toolboxInstance.close(), this.Editor.BlockSettings.opened && this.Editor.BlockSettings.close(), !e) return; this.hoveredBlock = e; const t = e.holder, { isMobile: o } = this.Editor.UI; let i; const s = 20, r = e.firstInput, a = t.getBoundingClientRect(), l = r !== void 0 ? r.getBoundingClientRect() : null, c = l !== null ? l.top - a.top : null, u = c !== null ? c > s : void 0; if (o) i = t.offsetTop + t.offsetHeight; else if (r === void 0 || u) { const h = parseInt(window.getComputedStyle(e.pluginsContent).paddingTop); i = t.offsetTop + h; } else { const h = li(r), p = parseInt(window.getComputedStyle(this.nodes.plusButton).height, 10), g = 8; i = t.offsetTop + h - p + g + c; } this.nodes.wrapper.style.top = `${Math.floor(i)}px`, this.Editor.BlockManager.blocks.length === 1 && e.isEmpty ? this.blockTunesToggler.hide() : this.blockTunesToggler.show(), this.open(); } /** * Close the Toolbar */ close() { var e, t; this.Editor.ReadOnly.isEnabled || ((e = this.nodes.wrapper) == null || e.classList.remove(this.CSS.toolbarOpened), this.blockActions.hide(), (t = this.toolboxInstance) == null || t.close(), this.Editor.BlockSettings.close(), this.reset()); } /** * Reset the Toolbar position to prevent DOM height growth, for example after blocks deletion */ reset() { this.nodes.wrapper.style.top = "unset"; } /** * Open Toolbar with Plus Button and Actions * * @param {boolean} withBlockActions - by default, Toolbar opens with Block Actions. * This flag allows to open Toolbar without Actions. */ open(e = !0) { this.nodes.wrapper.classList.add(this.CSS.toolbarOpened), e ? this.blockActions.show() : this.blockActions.hide(); } /** * Draws Toolbar elements */ async make() { this.nodes.wrapper = d.make("div", this.CSS.toolbar), ["content", "actions"].forEach((s) => { this.nodes[s] = d.make("div", this.CSS[s]); }), d.append(this.nodes.wrapper, this.nodes.content), d.append(this.nodes.content, this.nodes.actions), this.nodes.plusButton = d.make("div", this.CSS.plusButton, { innerHTML: ts }), d.append(this.nodes.actions, this.nodes.plusButton), this.readOnlyMutableListeners.on(this.nodes.plusButton, "click", () => { $e(!0), this.plusButtonClicked(); }, !1); const e = d.make("div"); e.appendChild(document.createTextNode(z.ui(K.ui.toolbar.toolbox, "Add"))), e.appendChild(d.make("div", this.CSS.plusButtonShortcut, { textContent: "/" })), ze(this.nodes.plusButton, e, { hidingDelay: 400 }), this.nodes.settingsToggler = d.make("span", this.CSS.settingsToggler, { innerHTML: es }), d.append(this.nodes.actions, this.nodes.settingsToggler); const t = d.make("div"), o = d.text(z.ui(K.ui.blockTunes.toggler, "Click to tune")), i = await xs("Slash", "/"); t.appendChild(o), t.appendChild(d.make("div", this.CSS.plusButtonShortcut, { textContent: vt(`CMD + ${i}`) })), ze(this.nodes.settingsToggler, t, { hidingDelay: 400 }), d.append(this.nodes.actions, this.makeToolbox()), d.append(this.nodes.actions, this.Editor.BlockSettings.getElement()), d.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); } /** * Creates the Toolbox instance and return it's rendered element */ makeToolbox() { return this.toolboxInstance = new Es({ api: this.Editor.API.methods, tools: this.Editor.Tools.blockTools, i18nLabels: { filter: z.ui(K.ui.popover, "Filter"), nothingFound: z.ui(K.ui.popover, "Nothing found") } }), this.toolboxInstance.on(Le.Opened, () => { this.Editor.UI.nodes.wrapper.classList.add(this.CSS.openedToolboxHolderModifier); }), this.toolboxInstance.on(Le.Closed, () => { this.Editor.UI.nodes.wrapper.classList.remove(this.CSS.openedToolboxHolderModifier); }), this.toolboxInstance.on(Le.BlockAdded, ({ block: e }) => { const { BlockManager: t, Caret: o } = this.Editor, i = t.getBlockById(e.id); i.inputs.length === 0 && (i === t.lastBlock ? (t.insertAtEnd(), o.setToBlock(t.lastBlock)) : o.setToBlock(t.nextBlock)); }), this.toolboxInstance.getElement(); } /** * Handler for Plus Button */ plusButtonClicked() { var e; this.Editor.BlockManager.currentBlock = this.hoveredBlock, (e = this.toolboxInstance) == null || e.toggle(); } /** * Enable bindings */ enableModuleBindings() { this.readOnlyMutableListeners.on(this.nodes.settingsToggler, "mousedown", (e) => { var t; e.stopPropagation(), this.settingsTogglerClicked(), (t = this.toolboxInstance) != null && t.opened && this.toolboxInstance.close(), $e(!0); }, !0), be() || this.eventsDispatcher.on(ln, (e) => { var t; this.Editor.BlockSettings.opened || (t = this.toolboxInstance) != null && t.opened || this.moveAndOpen(e.block); }); } /** * Disable bindings */ disableModuleBindings() { this.readOnlyMutableListeners.clearAll(); } /** * Clicks on the Block Settings toggler */ settingsTogglerClicked() { this.Editor.BlockManager.currentBlock = this.hoveredBlock, this.Editor.BlockSettings.opened ? this.Editor.BlockSettings.close() : this.Editor.BlockSettings.open(this.hoveredBlock); } /** * Draws Toolbar UI * * Toolbar contains BlockSettings and Toolbox. * That's why at first we draw its components and then Toolbar itself * * Steps: * - Make Toolbar dependent components like BlockSettings, Toolbox and so on * - Make itself and append dependent nodes to itself * */ drawUI() { this.Editor.BlockSettings.make(), this.make(); } /** * Removes all created and saved HTMLElements * It is used in Read-Only mode */ destroy() { this.removeAllNodes(), this.toolboxInstance && this.toolboxInstance.destroy(); } } var ae = /* @__PURE__ */ ((n) => (n[n.Block = 0] = "Block", n[n.Inline = 1] = "Inline", n[n.Tune = 2] = "Tune", n))(ae || {}), Pe = /* @__PURE__ */ ((n) => (n.Shortcut = "shortcut", n.Toolbox = "toolbox", n.EnabledInlineTools = "inlineToolbar", n.EnabledBlockTunes = "tunes", n.Config = "config", n))(Pe || {}), cn = /* @__PURE__ */ ((n) => (n.Shortcut = "shortcut", n.SanitizeConfig = "sanitize", n))(cn || {}), pe = /* @__PURE__ */ ((n) => (n.IsEnabledLineBreaks = "enableLineBreaks", n.Toolbox = "toolbox", n.ConversionConfig = "conversionConfig", n.IsReadOnlySupported = "isReadOnlySupported", n.PasteConfig = "pasteConfig", n))(pe || {}), We = /* @__PURE__ */ ((n) => (n.IsInline = "isInline", n.Title = "title", n.IsReadOnlySupported = "isReadOnlySupported", n))(We || {}), mt = /* @__PURE__ */ ((n) => (n.IsTune = "isTune", n))(mt || {}); class Tt { /** * @class * @param {ConstructorOptions} options - Constructor options */ constructor({ name: e, constructable: t, config: o, api: i, isDefault: s, isInternal: r = !1, defaultPlaceholder: a }) { this.api = i, this.name = e, this.constructable = t, this.config = o, this.isDefault = s, this.isInternal = r, this.defaultPlaceholder = a; } /** * Returns Tool user configuration */ get settings() { const e = this.config.config || {}; return this.isDefault && !("placeholder" in e) && this.defaultPlaceholder && (e.placeholder = this.defaultPlaceholder), e; } /** * Calls Tool's reset method */ reset() { if (A(this.constructable.reset)) return this.constructable.reset(); } /** * Calls Tool's prepare method */ prepare() { if (A(this.constructable.prepare)) return this.constructable.prepare({ toolName: this.name, config: this.settings }); } /** * Returns shortcut for Tool (internal or specified by user) */ get shortcut() { const e = this.constructable.shortcut; return this.config.shortcut || e; } /** * Returns Tool's sanitizer configuration */ get sanitizeConfig() { return this.constructable.sanitize || {}; } /** * Returns true if Tools is inline */ isInline() { return this.type === ae.Inline; } /** * Returns true if Tools is block */ isBlock() { return this.type === ae.Block; } /** * Returns true if Tools is tune */ isTune() { return this.type === ae.Tune; } } class Cs extends E { /** * @param moduleConfiguration - Module Configuration * @param moduleConfiguration.config - Editor's config * @param moduleConfiguration.eventsDispatcher - Editor's event dispatcher */ constructor({ config: e, eventsDispatcher: t }) { super({ config: e, eventsDispatcher: t }), this.CSS = { inlineToolbar: "ce-inline-toolbar" }, this.opened = !1, this.popover = null, this.toolbarVerticalMargin = be() ? 20 : 6, this.tools = /* @__PURE__ */ new Map(), window.requestIdleCallback(() => { this.make(); }, { timeout: 2e3 }); } /** * Moving / appearance * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * Shows Inline Toolbar if something is selected * * @param [needToClose] - pass true to close toolbar if it is not allowed. * Avoid to use it just for closing IT, better call .close() clearly. */ async tryToShow(e = !1) { e && this.close(), this.allowedToShow() && (await this.open(), this.Editor.Toolbar.close()); } /** * Hides Inline Toolbar */ close() { var e, t; if (this.opened) { for (const [o, i] of this.tools) { const s = this.getToolShortcut(o.name); s !== void 0 && ge.remove(this.Editor.UI.nodes.redactor, s), A(i.clear) && i.clear(); } this.tools = /* @__PURE__ */ new Map(), this.reset(), this.opened = !1, (e = this.popover) == null || e.hide(), (t = this.popover) == null || t.destroy(), this.popover = null; } } /** * Check if node is contained by Inline Toolbar * * @param {Node} node — node to check */ containsNode(e) { return this.nodes.wrapper === void 0 ? !1 : this.nodes.wrapper.contains(e); } /** * Removes UI and its components */ destroy() { var e; this.removeAllNodes(), (e = this.popover) == null || e.destroy(), this.popover = null; } /** * Making DOM */ make() { this.nodes.wrapper = d.make("div", [ this.CSS.inlineToolbar, ...this.isRtl ? [this.Editor.UI.CSS.editorRtlFix] : [] ]), d.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); } /** * Shows Inline Toolbar */ async open() { var t; if (this.opened) return; this.opened = !0, this.popover !== null && this.popover.destroy(), this.createToolsInstances(); const e = await this.getPopoverItems(); this.popover = new hs({ items: e, scopeElement: this.Editor.API.methods.ui.nodes.redactor, messages: { nothingFound: z.ui(K.ui.popover, "Nothing found"), search: z.ui(K.ui.popover, "Filter") } }), this.move(this.popover.size.width), (t = this.nodes.wrapper) == null || t.append(this.popover.getElement()), this.popover.show(); } /** * Move Toolbar to the selected text * * @param popoverWidth - width of the toolbar popover */ move(e) { const t = b.rect, o = this.Editor.UI.nodes.wrapper.getBoundingClientRect(), i = { x: t.x - o.x, y: t.y + t.height - // + window.scrollY o.top + this.toolbarVerticalMargin }; i.x + e + o.x > this.Editor.UI.contentRect.right && (i.x = this.Editor.UI.contentRect.right - e - o.x), this.nodes.wrapper.style.left = Math.floor(i.x) + "px", this.nodes.wrapper.style.top = Math.floor(i.y) + "px"; } /** * Clear orientation classes and reset position */ reset() { this.nodes.wrapper.style.left = "0", this.nodes.wrapper.style.top = "0"; } /** * Need to show Inline Toolbar or not */ allowedToShow() { const e = ["IMG", "INPUT"], t = b.get(), o = b.text; if (!t || !t.anchorNode || t.isCollapsed || o.length < 1) return !1; const i = d.isElement(t.anchorNode) ? t.anchorNode : t.anchorNode.parentElement; if (i === null || t !== null && e.includes(i.tagName)) return !1; const s = this.Editor.BlockManager.getBlock(t.anchorNode); return !s || this.getTools().some((c) => s.tool.inlineTools.has(c.name)) === !1 ? !1 : i.closest("[contenteditable]") !== null; } /** * Working with Tools * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * Returns tools that are available for current block * * Used to check if Inline Toolbar could be shown * and to render tools in the Inline Toolbar */ getTools() { const e = this.Editor.BlockManager.currentBlock; return e ? Array.from(e.tool.inlineTools.values()).filter((o) => !(this.Editor.ReadOnly.isEnabled && o.isReadOnlySupported !== !0)) : []; } /** * Constructs tools instances and saves them to this.tools */ createToolsInstances() { this.tools = /* @__PURE__ */ new Map(), this.getTools().forEach((t) => { const o = t.create(); this.tools.set(t, o); }); } /** * Returns Popover Items for tools segregated by their appearance type: regular items and custom html elements. */ async getPopoverItems() { const e = []; let t = 0; for (const [o, i] of this.tools) { const s = await i.render(), r = this.getToolShortcut(o.name); if (r !== void 0) try { this.enableShortcuts(o.name, r); } catch { } const a = r !== void 0 ? vt(r) : void 0, l = z.t( K.toolNames, o.title || je(o.name) ); [s].flat().forEach((c) => { var h, p; const u = { name: o.name, onActivate: () => { this.toolClicked(i); }, hint: { title: l, description: a } }; if (d.isElement(c)) { const g = { ...u, element: c, type: _.Html }; if (A(i.renderActions)) { const f = i.renderActions(); g.children = { isOpen: (h = i.checkState) == null ? void 0 : h.call(i, b.get()), /** Disable keyboard navigation in actions, as it might conflict with enter press handling */ isFlippable: !1, items: [ { type: _.Html, element: f } ] }; } else (p = i.checkState) == null || p.call(i, b.get()); e.push(g); } else if (c.type === _.Html) e.push({ ...u, ...c, type: _.Html }); else if (c.type === _.Separator) e.push({ type: _.Separator }); else { const g = { ...u, ...c, type: _.Default }; "children" in g && t !== 0 && e.push({ type: _.Separator }), e.push(g), "children" in g && t < this.tools.size - 1 && e.push({ type: _.Separator }); } }), t++; } return e; } /** * Get shortcut name for tool * * @param toolName — Tool name */ getToolShortcut(e) { const { Tools: t } = this.Editor, o = t.inlineTools.get(e), i = t.internal.inlineTools; return Array.from(i.keys()).includes(e) ? this.inlineTools[e][cn.Shortcut] : o == null ? void 0 : o.shortcut; } /** * Enable Tool shortcut with Editor Shortcuts Module * * @param toolName - tool name * @param shortcut - shortcut according to the ShortcutData Module format */ enableShortcuts(e, t) { ge.add({ name: t, handler: (o) => { var s; const { currentBlock: i } = this.Editor.BlockManager; i && i.tool.enabledInlineTools && (o.preventDefault(), (s = this.popover) == null || s.activateItemByName(e)); }, /** * We need to bind shortcut to the document to make it work in read-only mode */ on: document }); } /** * Inline Tool button clicks * * @param tool - Tool's instance */ toolClicked(e) { var o; const t = b.range; (o = e.surround) == null || o.call(e, t), this.checkToolsState(); } /** * Check Tools` state by selection */ checkToolsState() { var e; (e = this.tools) == null || e.forEach((t) => { var o; (o = t.checkState) == null || o.call(t, b.get()); }); } /** * Get inline tools tools * Tools that has isInline is true */ get inlineTools() { const e = {}; return Array.from(this.Editor.Tools.inlineTools.entries()).forEach(([t, o]) => { e[t] = o.create(); }), e; } } function dn() { const n = window.getSelection(); if (n === null) return [null, 0]; let e = n.focusNode, t = n.focusOffset; return e === null ? [null, 0] : (e.nodeType !== Node.TEXT_NODE && e.childNodes.length > 0 && (e.childNodes[t] ? (e = e.childNodes[t], t = 0) : (e = e.childNodes[t - 1], t = e.textContent.length)), [e, t]); } function un(n, e, t, o) { const i = document.createRange(); o === "left" ? (i.setStart(n, 0), i.setEnd(e, t)) : (i.setStart(e, t), i.setEnd(n, n.childNodes.length)); const s = i.cloneContents(), r = document.createElement("div"); r.appendChild(s); const a = r.textContent || ""; return ai(a); } function Ne(n) { const e = d.getDeepestNode(n); if (e === null || d.isEmpty(n)) return !0; if (d.isNativeInput(e)) return e.selectionEnd === 0; if (d.isEmpty(n)) return !0; const [t, o] = dn(); return t === null ? !1 : un(n, t, o, "left"); } function Re(n) { const e = d.getDeepestNode(n, !0); if (e === null) return !0; if (d.isNativeInput(e)) return e.selectionEnd === e.value.length; const [t, o] = dn(); return t === null ? !1 : un(n, t, o, "right"); } var hn = {}, St = {}, Xe = {}, de = {}, It = {}, Ot = {}; Object.defineProperty(Ot, "__esModule", { value: !0 }); Ot.allInputsSelector = Ts; function Ts() { var n = ["text", "password", "email", "number", "search", "tel", "url"]; return "[contenteditable=true], textarea, input:not([type]), " + n.map(function(e) { return 'input[type="'.concat(e, '"]'); }).join(", "); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.allInputsSelector = void 0; var e = Ot; Object.defineProperty(n, "allInputsSelector", { enumerable: !0, get: function() { return e.allInputsSelector; } }); })(It); var ue = {}, _t = {}; Object.defineProperty(_t, "__esModule", { value: !0 }); _t.isNativeInput = Ss; function Ss(n) { var e = [ "INPUT", "TEXTAREA" ]; return n && n.tagName ? e.includes(n.tagName) : !1; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isNativeInput = void 0; var e = _t; Object.defineProperty(n, "isNativeInput", { enumerable: !0, get: function() { return e.isNativeInput; } }); })(ue); var pn = {}, Mt = {}; Object.defineProperty(Mt, "__esModule", { value: !0 }); Mt.append = Is; function Is(n, e) { Array.isArray(e) ? e.forEach(function(t) { n.appendChild(t); }) : n.appendChild(e); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.append = void 0; var e = Mt; Object.defineProperty(n, "append", { enumerable: !0, get: function() { return e.append; } }); })(pn); var At = {}, Lt = {}; Object.defineProperty(Lt, "__esModule", { value: !0 }); Lt.blockElements = Os; function Os() { return [ "address", "article", "aside", "blockquote", "canvas", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "nav", "noscript", "ol", "output", "p", "pre", "ruby", "section", "table", "tbody", "thead", "tr", "tfoot", "ul", "video" ]; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.blockElements = void 0; var e = Lt; Object.defineProperty(n, "blockElements", { enumerable: !0, get: function() { return e.blockElements; } }); })(At); var fn = {}, Pt = {}; Object.defineProperty(Pt, "__esModule", { value: !0 }); Pt.calculateBaseline = _s; function _s(n) { var e = window.getComputedStyle(n), t = parseFloat(e.fontSize), o = parseFloat(e.lineHeight) || t * 1.2, i = parseFloat(e.paddingTop), s = parseFloat(e.borderTopWidth), r = parseFloat(e.marginTop), a = t * 0.8, l = (o - t) / 2, c = r + s + i + l + a; return c; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.calculateBaseline = void 0; var e = Pt; Object.defineProperty(n, "calculateBaseline", { enumerable: !0, get: function() { return e.calculateBaseline; } }); })(fn); var gn = {}, Nt = {}, Rt = {}, Dt = {}; Object.defineProperty(Dt, "__esModule", { value: !0 }); Dt.isContentEditable = Ms; function Ms(n) { return n.contentEditable === "true"; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isContentEditable = void 0; var e = Dt; Object.defineProperty(n, "isContentEditable", { enumerable: !0, get: function() { return e.isContentEditable; } }); })(Rt); Object.defineProperty(Nt, "__esModule", { value: !0 }); Nt.canSetCaret = Ps; var As = ue, Ls = Rt; function Ps(n) { var e = !0; if ((0, As.isNativeInput)(n)) switch (n.type) { case "file": case "checkbox": case "radio": case "hidden": case "submit": case "button": case "image": case "reset": e = !1; break; } else e = (0, Ls.isContentEditable)(n); return e; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.canSetCaret = void 0; var e = Nt; Object.defineProperty(n, "canSetCaret", { enumerable: !0, get: function() { return e.canSetCaret; } }); })(gn); var Ve = {}, Ft = {}; function Ns(n, e, t) { const o = t.value !== void 0 ? "value" : "get", i = t[o], s = `#${e}Cache`; if (t[o] = function(...r) { return this[s] === void 0 && (this[s] = i.apply(this, r)), this[s]; }, o === "get" && t.set) { const r = t.set; t.set = function(a) { delete n[s], r.apply(this, a); }; } return t; } function mn() { const n = { win: !1, mac: !1, x11: !1, linux: !1 }, e = Object.keys(n).find((t) => window.navigator.appVersion.toLowerCase().indexOf(t) !== -1); return e !== void 0 && (n[e] = !0), n; } function jt(n) { return n != null && n !== "" && (typeof n != "object" || Object.keys(n).length > 0); } function Rs(n) { return !jt(n); } const Ds = () => typeof window < "u" && window.navigator !== null && jt(window.navigator.platform) && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1); function Fs(n) { const e = mn(); return n = n.replace(/shift/gi, "⇧").replace(/backspace/gi, "⌫").replace(/enter/gi, "⏎").replace(/up/gi, "↑").replace(/left/gi, "→").replace(/down/gi, "↓").replace(/right/gi, "←").replace(/escape/gi, "⎋").replace(/insert/gi, "Ins").replace(/delete/gi, "␡").replace(/\+/gi, "+"), e.mac ? n = n.replace(/ctrl|cmd/gi, "⌘").replace(/alt/gi, "⌥") : n = n.replace(/cmd/gi, "Ctrl").replace(/windows/gi, "WIN"), n; } function js(n) { return n[0].toUpperCase() + n.slice(1); } function Hs(n) { const e = document.createElement("div"); e.style.position = "absolute", e.style.left = "-999px", e.style.bottom = "-999px", e.innerHTML = n, document.body.appendChild(e); const t = window.getSelection(), o = document.createRange(); if (o.selectNode(e), t === null) throw new Error("Cannot copy text to clipboard"); t.removeAllRanges(), t.addRange(o), document.execCommand("copy"), document.body.removeChild(e); } function $s(n, e, t) { let o; return (...i) => { const s = this, r = () => { o = void 0, t !== !0 && n.apply(s, i); }, a = t === !0 && o !== void 0; window.clearTimeout(o), o = window.setTimeout(r, e), a && n.apply(s, i); }; } function oe(n) { return Object.prototype.toString.call(n).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); } function zs(n) { return oe(n) === "boolean"; } function bn(n) { return oe(n) === "function" || oe(n) === "asyncfunction"; } function Us(n) { return bn(n) && /^\s*class\s+/.test(n.toString()); } function Ws(n) { return oe(n) === "number"; } function De(n) { return oe(n) === "object"; } function Ys(n) { return Promise.resolve(n) === n; } function Ks(n) { return oe(n) === "string"; } function Xs(n) { return oe(n) === "undefined"; } function bt(n, ...e) { if (!e.length) return n; const t = e.shift(); if (De(n) && De(t)) for (const o in t) De(t[o]) ? (n[o] === void 0 && Object.assign(n, { [o]: {} }), bt(n[o], t[o])) : Object.assign(n, { [o]: t[o] }); return bt(n, ...e); } function Vs(n, e, t) { const o = `«${e}» is deprecated and will be removed in the next major release. Please use the «${t}» instead.`; n && console.warn(o); } function qs(n) { try { return new URL(n).href; } catch { } return n.substring(0, 2) === "//" ? window.location.protocol + n : window.location.origin + n; } function Zs(n) { return n > 47 && n < 58 || n === 32 || n === 13 || n === 229 || n > 64 && n < 91 || n > 95 && n < 112 || n > 185 && n < 193 || n > 218 && n < 223; } const Gs = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91, SLASH: 191 }, Qs = { LEFT: 0, WHEEL: 1, RIGHT: 2, BACKWARD: 3, FORWARD: 4 }; let Js = class { constructor() { this.completed = Promise.resolve(); } /** * Add new promise to queue * @param operation - promise should be added to queue */ add(e) { return new Promise((t, o) => { this.completed = this.completed.then(e).then(t).catch(o); }); } }; function er(n, e, t = void 0) { let o, i, s, r = null, a = 0; t || (t = {}); const l = function() { a = t.leading === !1 ? 0 : Date.now(), r = null, s = n.apply(o, i), r === null && (o = i = null); }; return function() { const c = Date.now(); !a && t.leading === !1 && (a = c); const u = e - (c - a); return o = this, i = arguments, u <= 0 || u > e ? (r && (clearTimeout(r), r = null), a = c, s = n.apply(o, i), r === null && (o = i = null)) : !r && t.trailing !== !1 && (r = setTimeout(l, u)), s; }; } const tr = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, PromiseQueue: Js, beautifyShortcut: Fs, cacheable: Ns, capitalize: js, copyTextToClipboard: Hs, debounce: $s, deepMerge: bt, deprecationAssert: Vs, getUserOS: mn, getValidUrl: qs, isBoolean: zs, isClass: Us, isEmpty: Rs, isFunction: bn, isIosDevice: Ds, isNumber: Ws, isObject: De, isPrintableKey: Zs, isPromise: Ys, isString: Ks, isUndefined: Xs, keyCodes: Gs, mouseButtons: Qs, notEmpty: jt, throttle: er, typeOf: oe }, Symbol.toStringTag, { value: "Module" })), Ht = /* @__PURE__ */ Xn(tr); Object.defineProperty(Ft, "__esModule", { value: !0 }); Ft.containsOnlyInlineElements = ir; var or = Ht, nr = At; function ir(n) { var e; (0, or.isString)(n) ? (e = document.createElement("div"), e.innerHTML = n) : e = n; var t = function(o) { return !(0, nr.blockElements)().includes(o.tagName.toLowerCase()) && Array.from(o.children).every(t); }; return Array.from(e.children).every(t); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.containsOnlyInlineElements = void 0; var e = Ft; Object.defineProperty(n, "containsOnlyInlineElements", { enumerable: !0, get: function() { return e.containsOnlyInlineElements; } }); })(Ve); var vn = {}, $t = {}, qe = {}, zt = {}; Object.defineProperty(zt, "__esModule", { value: !0 }); zt.make = sr; function sr(n, e, t) { var o; e === void 0 && (e = null), t === void 0 && (t = {}); var i = document.createElement(n); if (Array.isArray(e)) { var s = e.filter(function(a) { return a !== void 0; }); (o = i.classList).add.apply(o, s); } else e !== null && i.classList.add(e); for (var r in t) Object.prototype.hasOwnProperty.call(t, r) && (i[r] = t[r]); return i; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.make = void 0; var e = zt; Object.defineProperty(n, "make", { enumerable: !0, get: function() { return e.make; } }); })(qe); Object.defineProperty($t, "__esModule", { value: !0 }); $t.fragmentToString = ar; var rr = qe; function ar(n) { var e = (0, rr.make)("div"); return e.appendChild(n), e.innerHTML; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.fragmentToString = void 0; var e = $t; Object.defineProperty(n, "fragmentToString", { enumerable: !0, get: function() { return e.fragmentToString; } }); })(vn); var kn = {}, Ut = {}; Object.defineProperty(Ut, "__esModule", { value: !0 }); Ut.getContentLength = cr; var lr = ue; function cr(n) { var e, t; return (0, lr.isNativeInput)(n) ? n.value.length : n.nodeType === Node.TEXT_NODE ? n.length : (t = (e = n.textContent) === null || e === void 0 ? void 0 : e.length) !== null && t !== void 0 ? t : 0; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getContentLength = void 0; var e = Ut; Object.defineProperty(n, "getContentLength", { enumerable: !0, get: function() { return e.getContentLength; } }); })(kn); var Wt = {}, Yt = {}, Io = Ce && Ce.__spreadArray || function(n, e, t) { if (t || arguments.length === 2) for (var o = 0, i = e.length, s; o < i; o++) (s || !(o in e)) && (s || (s = Array.prototype.slice.call(e, 0, o)), s[o] = e[o]); return n.concat(s || Array.prototype.slice.call(e)); }; Object.defineProperty(Yt, "__esModule", { value: !0 }); Yt.getDeepestBlockElements = yn; var dr = Ve; function yn(n) { return (0, dr.containsOnlyInlineElements)(n) ? [n] : Array.from(n.children).reduce(function(e, t) { return Io(Io([], e, !0), yn(t), !0); }, []); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getDeepestBlockElements = void 0; var e = Yt; Object.defineProperty(n, "getDeepestBlockElements", { enumerable: !0, get: function() { return e.getDeepestBlockElements; } }); })(Wt); var wn = {}, Kt = {}, Ze = {}, Xt = {}; Object.defineProperty(Xt, "__esModule", { value: !0 }); Xt.isLineBreakTag = ur; function ur(n) { return [ "BR", "WBR" ].includes(n.tagName); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isLineBreakTag = void 0; var e = Xt; Object.defineProperty(n, "isLineBreakTag", { enumerable: !0, get: function() { return e.isLineBreakTag; } }); })(Ze); var Ge = {}, Vt = {}; Object.defineProperty(Vt, "__esModule", { value: !0 }); Vt.isSingleTag = hr; function hr(n) { return [ "AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "META", "PARAM", "SOURCE", "TRACK", "WBR" ].includes(n.tagName); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isSingleTag = void 0; var e = Vt; Object.defineProperty(n, "isSingleTag", { enumerable: !0, get: function() { return e.isSingleTag; } }); })(Ge); Object.defineProperty(Kt, "__esModule", { value: !0 }); Kt.getDeepestNode = En; var pr = ue, fr = Ze, gr = Ge; function En(n, e) { e === void 0 && (e = !1); var t = e ? "lastChild" : "firstChild", o = e ? "previousSibling" : "nextSibling"; if (n.nodeType === Node.ELEMENT_NODE && n[t]) { var i = n[t]; if ((0, gr.isSingleTag)(i) && !(0, pr.isNativeInput)(i) && !(0, fr.isLineBreakTag)(i)) if (i[o]) i = i[o]; else if (i.parentNode !== null && i.parentNode[o]) i = i.parentNode[o]; else return i.parentNode; return En(i, e); } return n; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getDeepestNode = void 0; var e = Kt; Object.defineProperty(n, "getDeepestNode", { enumerable: !0, get: function() { return e.getDeepestNode; } }); })(wn); var xn = {}, qt = {}, Me = Ce && Ce.__spreadArray || function(n, e, t) { if (t || arguments.length === 2) for (var o = 0, i = e.length, s; o < i; o++) (s || !(o in e)) && (s || (s = Array.prototype.slice.call(e, 0, o)), s[o] = e[o]); return n.concat(s || Array.prototype.slice.call(e)); }; Object.defineProperty(qt, "__esModule", { value: !0 }); qt.findAllInputs = yr; var mr = Ve, br = Wt, vr = It, kr = ue; function yr(n) { return Array.from(n.querySelectorAll((0, vr.allInputsSelector)())).reduce(function(e, t) { return (0, kr.isNativeInput)(t) || (0, mr.containsOnlyInlineElements)(t) ? Me(Me([], e, !0), [t], !1) : Me(Me([], e, !0), (0, br.getDeepestBlockElements)(t), !0); }, []); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.findAllInputs = void 0; var e = qt; Object.defineProperty(n, "findAllInputs", { enumerable: !0, get: function() { return e.findAllInputs; } }); })(xn); var Bn = {}, Zt = {}; Object.defineProperty(Zt, "__esModule", { value: !0 }); Zt.isCollapsedWhitespaces = wr; function wr(n) { return !/[^\t\n\r ]/.test(n); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isCollapsedWhitespaces = void 0; var e = Zt; Object.defineProperty(n, "isCollapsedWhitespaces", { enumerable: !0, get: function() { return e.isCollapsedWhitespaces; } }); })(Bn); var Gt = {}, Qt = {}; Object.defineProperty(Qt, "__esModule", { value: !0 }); Qt.isElement = xr; var Er = Ht; function xr(n) { return (0, Er.isNumber)(n) ? !1 : !!n && !!n.nodeType && n.nodeType === Node.ELEMENT_NODE; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isElement = void 0; var e = Qt; Object.defineProperty(n, "isElement", { enumerable: !0, get: function() { return e.isElement; } }); })(Gt); var Cn = {}, Jt = {}, eo = {}, to = {}; Object.defineProperty(to, "__esModule", { value: !0 }); to.isLeaf = Br; function Br(n) { return n === null ? !1 : n.childNodes.length === 0; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isLeaf = void 0; var e = to; Object.defineProperty(n, "isLeaf", { enumerable: !0, get: function() { return e.isLeaf; } }); })(eo); var oo = {}, no = {}; Object.defineProperty(no, "__esModule", { value: !0 }); no.isNodeEmpty = Or; var Cr = Ze, Tr = Gt, Sr = ue, Ir = Ge; function Or(n, e) { var t = ""; return (0, Ir.isSingleTag)(n) && !(0, Cr.isLineBreakTag)(n) ? !1 : ((0, Tr.isElement)(n) && (0, Sr.isNativeInput)(n) ? t = n.value : n.textContent !== null && (t = n.textContent.replace("​", "")), e !== void 0 && (t = t.replace(new RegExp(e, "g"), "")), t.trim().length === 0); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isNodeEmpty = void 0; var e = no; Object.defineProperty(n, "isNodeEmpty", { enumerable: !0, get: function() { return e.isNodeEmpty; } }); })(oo); Object.defineProperty(Jt, "__esModule", { value: !0 }); Jt.isEmpty = Ar; var _r = eo, Mr = oo; function Ar(n, e) { n.normalize(); for (var t = [n]; t.length > 0; ) { var o = t.shift(); if (o) { if (n = o, (0, _r.isLeaf)(n) && !(0, Mr.isNodeEmpty)(n, e)) return !1; t.push.apply(t, Array.from(n.childNodes)); } } return !0; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isEmpty = void 0; var e = Jt; Object.defineProperty(n, "isEmpty", { enumerable: !0, get: function() { return e.isEmpty; } }); })(Cn); var Tn = {}, io = {}; Object.defineProperty(io, "__esModule", { value: !0 }); io.isFragment = Pr; var Lr = Ht; function Pr(n) { return (0, Lr.isNumber)(n) ? !1 : !!n && !!n.nodeType && n.nodeType === Node.DOCUMENT_FRAGMENT_NODE; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isFragment = void 0; var e = io; Object.defineProperty(n, "isFragment", { enumerable: !0, get: function() { return e.isFragment; } }); })(Tn); var Sn = {}, so = {}; Object.defineProperty(so, "__esModule", { value: !0 }); so.isHTMLString = Rr; var Nr = qe; function Rr(n) { var e = (0, Nr.make)("div"); return e.innerHTML = n, e.childElementCount > 0; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isHTMLString = void 0; var e = so; Object.defineProperty(n, "isHTMLString", { enumerable: !0, get: function() { return e.isHTMLString; } }); })(Sn); var In = {}, ro = {}; Object.defineProperty(ro, "__esModule", { value: !0 }); ro.offset = Dr; function Dr(n) { var e = n.getBoundingClientRect(), t = window.pageXOffset || document.documentElement.scrollLeft, o = window.pageYOffset || document.documentElement.scrollTop, i = e.top + o, s = e.left + t; return { top: i, left: s, bottom: i + e.height, right: s + e.width }; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.offset = void 0; var e = ro; Object.defineProperty(n, "offset", { enumerable: !0, get: function() { return e.offset; } }); })(In); var On = {}, ao = {}; Object.defineProperty(ao, "__esModule", { value: !0 }); ao.prepend = Fr; function Fr(n, e) { Array.isArray(e) ? (e = e.reverse(), e.forEach(function(t) { return n.prepend(t); })) : n.prepend(e); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.prepend = void 0; var e = ao; Object.defineProperty(n, "prepend", { enumerable: !0, get: function() { return e.prepend; } }); })(On); (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.prepend = n.offset = n.make = n.isLineBreakTag = n.isSingleTag = n.isNodeEmpty = n.isLeaf = n.isHTMLString = n.isFragment = n.isEmpty = n.isElement = n.isContentEditable = n.isCollapsedWhitespaces = n.findAllInputs = n.isNativeInput = n.allInputsSelector = n.getDeepestNode = n.getDeepestBlockElements = n.getContentLength = n.fragmentToString = n.containsOnlyInlineElements = n.canSetCaret = n.calculateBaseline = n.blockElements = n.append = void 0; var e = It; Object.defineProperty(n, "allInputsSelector", { enumerable: !0, get: function() { return e.allInputsSelector; } }); var t = ue; Object.defineProperty(n, "isNativeInput", { enumerable: !0, get: function() { return t.isNativeInput; } }); var o = pn; Object.defineProperty(n, "append", { enumerable: !0, get: function() { return o.append; } }); var i = At; Object.defineProperty(n, "blockElements", { enumerable: !0, get: function() { return i.blockElements; } }); var s = fn; Object.defineProperty(n, "calculateBaseline", { enumerable: !0, get: function() { return s.calculateBaseline; } }); var r = gn; Object.defineProperty(n, "canSetCaret", { enumerable: !0, get: function() { return r.canSetCaret; } }); var a = Ve; Object.defineProperty(n, "containsOnlyInlineElements", { enumerable: !0, get: function() { return a.containsOnlyInlineElements; } }); var l = vn; Object.defineProperty(n, "fragmentToString", { enumerable: !0, get: function() { return l.fragmentToString; } }); var c = kn; Object.defineProperty(n, "getContentLength", { enumerable: !0, get: function() { return c.getContentLength; } }); var u = Wt; Object.defineProperty(n, "getDeepestBlockElements", { enumerable: !0, get: function() { return u.getDeepestBlockElements; } }); var h = wn; Object.defineProperty(n, "getDeepestNode", { enumerable: !0, get: function() { return h.getDeepestNode; } }); var p = xn; Object.defineProperty(n, "findAllInputs", { enumerable: !0, get: function() { return p.findAllInputs; } }); var g = Bn; Object.defineProperty(n, "isCollapsedWhitespaces", { enumerable: !0, get: function() { return g.isCollapsedWhitespaces; } }); var f = Rt; Object.defineProperty(n, "isContentEditable", { enumerable: !0, get: function() { return f.isContentEditable; } }); var v = Gt; Object.defineProperty(n, "isElement", { enumerable: !0, get: function() { return v.isElement; } }); var O = Cn; Object.defineProperty(n, "isEmpty", { enumerable: !0, get: function() { return O.isEmpty; } }); var T = Tn; Object.defineProperty(n, "isFragment", { enumerable: !0, get: function() { return T.isFragment; } }); var M = Sn; Object.defineProperty(n, "isHTMLString", { enumerable: !0, get: function() { return M.isHTMLString; } }); var q = eo; Object.defineProperty(n, "isLeaf", { enumerable: !0, get: function() { return q.isLeaf; } }); var F = oo; Object.defineProperty(n, "isNodeEmpty", { enumerable: !0, get: function() { return F.isNodeEmpty; } }); var H = Ze; Object.defineProperty(n, "isLineBreakTag", { enumerable: !0, get: function() { return H.isLineBreakTag; } }); var Q = Ge; Object.defineProperty(n, "isSingleTag", { enumerable: !0, get: function() { return Q.isSingleTag; } }); var ie = qe; Object.defineProperty(n, "make", { enumerable: !0, get: function() { return ie.make; } }); var k = In; Object.defineProperty(n, "offset", { enumerable: !0, get: function() { return k.offset; } }); var m = On; Object.defineProperty(n, "prepend", { enumerable: !0, get: function() { return m.prepend; } }); })(de); var Qe = {}; Object.defineProperty(Qe, "__esModule", { value: !0 }); Qe.getContenteditableSlice = Hr; var jr = de; function Hr(n, e, t, o, i) { var s; i === void 0 && (i = !1); var r = document.createRange(); if (o === "left" ? (r.setStart(n, 0), r.setEnd(e, t)) : (r.setStart(e, t), r.setEnd(n, n.childNodes.length)), i === !0) { var a = r.extractContents(); return (0, jr.fragmentToString)(a); } var l = r.cloneContents(), c = document.createElement("div"); c.appendChild(l); var u = (s = c.textContent) !== null && s !== void 0 ? s : ""; return u; } Object.defineProperty(Xe, "__esModule", { value: !0 }); Xe.checkContenteditableSliceForEmptiness = Ur; var $r = de, zr = Qe; function Ur(n, e, t, o) { var i = (0, zr.getContenteditableSlice)(n, e, t, o); return (0, $r.isCollapsedWhitespaces)(i); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.checkContenteditableSliceForEmptiness = void 0; var e = Xe; Object.defineProperty(n, "checkContenteditableSliceForEmptiness", { enumerable: !0, get: function() { return e.checkContenteditableSliceForEmptiness; } }); })(St); var _n = {}; (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getContenteditableSlice = void 0; var e = Qe; Object.defineProperty(n, "getContenteditableSlice", { enumerable: !0, get: function() { return e.getContenteditableSlice; } }); })(_n); var Mn = {}, lo = {}; Object.defineProperty(lo, "__esModule", { value: !0 }); lo.focus = Yr; var Wr = de; function Yr(n, e) { var t, o; if (e === void 0 && (e = !0), (0, Wr.isNativeInput)(n)) { n.focus(); var i = e ? 0 : n.value.length; n.setSelectionRange(i, i); } else { var s = document.createRange(), r = window.getSelection(); if (!r) return; var a = function(p) { var g = document.createTextNode(""); p.appendChild(g), s.setStart(g, 0), s.setEnd(g, 0); }, l = function(p) { return p != null; }, c = n.childNodes, u = e ? c[0] : c[c.length - 1]; if (l(u)) { for (; l(u) && u.nodeType !== Node.TEXT_NODE; ) u = e ? u.firstChild : u.lastChild; if (l(u) && u.nodeType === Node.TEXT_NODE) { var h = (o = (t = u.textContent) === null || t === void 0 ? void 0 : t.length) !== null && o !== void 0 ? o : 0, i = e ? 0 : h; s.setStart(u, i), s.setEnd(u, i); } else a(n); } else a(n); r.removeAllRanges(), r.addRange(s); } } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.focus = void 0; var e = lo; Object.defineProperty(n, "focus", { enumerable: !0, get: function() { return e.focus; } }); })(Mn); var co = {}, Je = {}; Object.defineProperty(Je, "__esModule", { value: !0 }); Je.getCaretNodeAndOffset = Kr; function Kr() { var n = window.getSelection(); if (n === null) return [null, 0]; var e = n.focusNode, t = n.focusOffset; return e === null ? [null, 0] : (e.nodeType !== Node.TEXT_NODE && e.childNodes.length > 0 && (e.childNodes[t] !== void 0 ? (e = e.childNodes[t], t = 0) : (e = e.childNodes[t - 1], e.textContent !== null && (t = e.textContent.length))), [e, t]); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getCaretNodeAndOffset = void 0; var e = Je; Object.defineProperty(n, "getCaretNodeAndOffset", { enumerable: !0, get: function() { return e.getCaretNodeAndOffset; } }); })(co); var An = {}, et = {}; Object.defineProperty(et, "__esModule", { value: !0 }); et.getRange = Xr; function Xr() { var n = window.getSelection(); return n && n.rangeCount ? n.getRangeAt(0) : null; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.getRange = void 0; var e = et; Object.defineProperty(n, "getRange", { enumerable: !0, get: function() { return e.getRange; } }); })(An); var Ln = {}, uo = {}; Object.defineProperty(uo, "__esModule", { value: !0 }); uo.isCaretAtEndOfInput = Zr; var Oo = de, Vr = co, qr = St; function Zr(n) { var e = (0, Oo.getDeepestNode)(n, !0); if (e === null) return !0; if ((0, Oo.isNativeInput)(e)) return e.selectionEnd === e.value.length; var t = (0, Vr.getCaretNodeAndOffset)(), o = t[0], i = t[1]; return o === null ? !1 : (0, qr.checkContenteditableSliceForEmptiness)(n, o, i, "right"); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isCaretAtEndOfInput = void 0; var e = uo; Object.defineProperty(n, "isCaretAtEndOfInput", { enumerable: !0, get: function() { return e.isCaretAtEndOfInput; } }); })(Ln); var Pn = {}, ho = {}; Object.defineProperty(ho, "__esModule", { value: !0 }); ho.isCaretAtStartOfInput = Jr; var Ae = de, Gr = Je, Qr = Xe; function Jr(n) { var e = (0, Ae.getDeepestNode)(n); if (e === null || (0, Ae.isEmpty)(n)) return !0; if ((0, Ae.isNativeInput)(e)) return e.selectionEnd === 0; if ((0, Ae.isEmpty)(n)) return !0; var t = (0, Gr.getCaretNodeAndOffset)(), o = t[0], i = t[1]; return o === null ? !1 : (0, Qr.checkContenteditableSliceForEmptiness)(n, o, i, "left"); } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.isCaretAtStartOfInput = void 0; var e = ho; Object.defineProperty(n, "isCaretAtStartOfInput", { enumerable: !0, get: function() { return e.isCaretAtStartOfInput; } }); })(Pn); var Nn = {}, po = {}; Object.defineProperty(po, "__esModule", { value: !0 }); po.save = oa; var ea = de, ta = et; function oa() { var n = (0, ta.getRange)(), e = (0, ea.make)("span"); if (e.id = "cursor", e.hidden = !0, !!n) return n.insertNode(e), function() { var o = window.getSelection(); o && (n.setStartAfter(e), n.setEndAfter(e), o.removeAllRanges(), o.addRange(n), setTimeout(function() { e.remove(); }, 150)); }; } (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.save = void 0; var e = po; Object.defineProperty(n, "save", { enumerable: !0, get: function() { return e.save; } }); })(Nn); (function(n) { Object.defineProperty(n, "__esModule", { value: !0 }), n.save = n.isCaretAtStartOfInput = n.isCaretAtEndOfInput = n.getRange = n.getCaretNodeAndOffset = n.focus = n.getContenteditableSlice = n.checkContenteditableSliceForEmptiness = void 0; var e = St; Object.defineProperty(n, "checkContenteditableSliceForEmptiness", { enumerable: !0, get: function() { return e.checkContenteditableSliceForEmptiness; } }); var t = _n; Object.defineProperty(n, "getContenteditableSlice", { enumerable: !0, get: function() { return t.getContenteditableSlice; } }); var o = Mn; Object.defineProperty(n, "focus", { enumerable: !0, get: function() { return o.focus; } }); var i = co; Object.defineProperty(n, "getCaretNodeAndOffset", { enumerable: !0, get: function() { return i.getCaretNodeAndOffset; } }); var s = An; Object.defineProperty(n, "getRange", { enumerable: !0, get: function() { return s.getRange; } }); var r = Ln; Object.defineProperty(n, "isCaretAtEndOfInput", { enumerable: !0, get: function() { return r.isCaretAtEndOfInput; } }); var a = Pn; Object.defineProperty(n, "isCaretAtStartOfInput", { enumerable: !0, get: function() { return a.isCaretAtStartOfInput; } }); var l = Nn; Object.defineProperty(n, "save", { enumerable: !0, get: function() { return l.save; } }); })(hn); class na extends E { /** * All keydowns on Block * * @param {KeyboardEvent} event - keydown */ keydown(e) { switch (this.beforeKeydownProcessing(e), e.keyCode) { case y.BACKSPACE: this.backspace(e); break; case y.DELETE: this.delete(e); break; case y.ENTER: this.enter(e); break; case y.DOWN: case y.RIGHT: this.arrowRightAndDown(e); break; case y.UP: case y.LEFT: this.arrowLeftAndUp(e); break; case y.TAB: this.tabPressed(e); break; } e.key === "/" && !e.ctrlKey && !e.metaKey && this.slashPressed(e), e.code === "Slash" && (e.ctrlKey || e.metaKey) && (e.preventDefault(), this.commandSlashPressed()); } /** * Fires on keydown before event processing * * @param {KeyboardEvent} event - keydown */ beforeKeydownProcessing(e) { this.needToolbarClosing(e) && Po(e.keyCode) && (this.Editor.Toolbar.close(), e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || this.Editor.BlockSelection.clearSelection(e)); } /** * Key up on Block: * - shows Inline Toolbar if something selected * - shows conversion toolbar with 85% of block selection * * @param {KeyboardEvent} event - keyup event */ keyup(e) { e.shiftKey || this.Editor.UI.checkEmptiness(); } /** * Add drop target styles * * @param {DragEvent} event - drag over event */ dragOver(e) { const t = this.Editor.BlockManager.getBlockByChildNode(e.target); t.dropTarget = !0; } /** * Remove drop target style * * @param {DragEvent} event - drag leave event */ dragLeave(e) { const t = this.Editor.BlockManager.getBlockByChildNode(e.target); t.dropTarget = !1; } /** * Copying selected blocks * Before putting to the clipboard we sanitize all blocks and then copy to the clipboard * * @param {ClipboardEvent} event - clipboard event */ handleCommandC(e) { const { BlockSelection: t } = this.Editor; t.anyBlockSelected && t.copySelectedBlocks(e); } /** * Copy and Delete selected Blocks * * @param {ClipboardEvent} event - clipboard event */ handleCommandX(e) { const { BlockSelection: t, BlockManager: o, Caret: i } = this.Editor; t.anyBlockSelected && t.copySelectedBlocks(e).then(() => { const s = o.removeSelectedBlocks(), r = o.insertDefaultBlockAtIndex(s, !0); i.setToBlock(r, i.positions.START), t.clearSelection(e); }); } /** * Tab pressed inside a Block. * * @param {KeyboardEvent} event - keydown */ tabPressed(e) { const { InlineToolbar: t, Caret: o } = this.Editor; if (t.opened) return; (e.shiftKey ? o.navigatePrevious(!0) : o.navigateNext(!0)) && e.preventDefault(); } /** * '/' + 'command' keydown inside a Block */ commandSlashPressed() { this.Editor.BlockSelection.selectedBlocks.length > 1 || this.activateBlockSettings(); } /** * '/' keydown inside a Block * * @param event - keydown */ slashPressed(e) { !this.Editor.UI.nodes.wrapper.contains(e.target) || !this.Editor.BlockManager.currentBlock.isEmpty || (e.preventDefault(), this.Editor.Caret.insertContentAtCaretPosition("/"), this.activateToolbox()); } /** * ENTER pressed on block * * @param {KeyboardEvent} event - keydown */ enter(e) { const { BlockManager: t, UI: o } = this.Editor, i = t.currentBlock; if (i === void 0 || i.tool.isLineBreaksEnabled || o.someToolbarOpened && o.someFlipperButtonFocused || e.shiftKey && !pt) return; let s = i; i.currentInput !== void 0 && Ne(i.currentInput) && !i.hasMedia ? this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex) : i.currentInput && Re(i.currentInput) ? s = this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex + 1) : s = this.Editor.BlockManager.split(), this.Editor.Caret.setToBlock(s), this.Editor.Toolbar.moveAndOpen(s), e.preventDefault(); } /** * Handle backspace keydown on Block * * @param {KeyboardEvent} event - keydown */ backspace(e) { const { BlockManager: t, Caret: o } = this.Editor, { currentBlock: i, previousBlock: s } = t; if (i === void 0 || !b.isCollapsed || !i.currentInput || !Ne(i.currentInput)) return; if (e.preventDefault(), this.Editor.Toolbar.close(), !(i.currentInput === i.firstInput)) { o.navigatePrevious(); return; } if (s === null) return; if (s.isEmpty) { t.removeBlock(s); return; } if (i.isEmpty) { t.removeBlock(i); const l = t.currentBlock; o.setToBlock(l, o.positions.END); return; } xo(s, i) ? this.mergeBlocks(s, i) : o.setToBlock(s, o.positions.END); } /** * Handles delete keydown on Block * Removes char after the caret. * If caret is at the end of the block, merge next block with current * * @param {KeyboardEvent} event - keydown */ delete(e) { const { BlockManager: t, Caret: o } = this.Editor, { currentBlock: i, nextBlock: s } = t; if (!b.isCollapsed || !Re(i.currentInput)) return; if (e.preventDefault(), this.Editor.Toolbar.close(), !(i.currentInput === i.lastInput)) { o.navigateNext(); return; } if (s === null) return; if (s.isEmpty) { t.removeBlock(s); return; } if (i.isEmpty) { t.removeBlock(i), o.setToBlock(s, o.positions.START); return; } xo(i, s) ? this.mergeBlocks(i, s) : o.setToBlock(s, o.positions.START); } /** * Merge passed Blocks * * @param targetBlock - to which Block we want to merge * @param blockToMerge - what Block we want to merge */ mergeBlocks(e, t) { const { BlockManager: o, Toolbar: i } = this.Editor; e.lastInput !== void 0 && (hn.focus(e.lastInput, !1), o.mergeBlocks(e, t).then(() => { i.close(); })); } /** * Handle right and down keyboard keys * * @param {KeyboardEvent} event - keyboard event */ arrowRightAndDown(e) { const t = ce.usedKeys.includes(e.keyCode) && (!e.shiftKey || e.keyCode === y.TAB); if (this.Editor.UI.someToolbarOpened && t) return; this.Editor.Toolbar.close(); const { currentBlock: o } = this.Editor.BlockManager, s = ((o == null ? void 0 : o.currentInput) !== void 0 ? Re(o.currentInput) : void 0) || this.Editor.BlockSelection.anyBlockSelected; if (e.shiftKey && e.keyCode === y.DOWN && s) { this.Editor.CrossBlockSelection.toggleBlockSelectedState(); return; } if (e.keyCode === y.DOWN || e.keyCode === y.RIGHT && !this.isRtl ? this.Editor.Caret.navigateNext() : this.Editor.Caret.navigatePrevious()) { e.preventDefault(); return; } Fe(() => { this.Editor.BlockManager.currentBlock && this.Editor.BlockManager.currentBlock.updateCurrentInput(); }, 20)(), this.Editor.BlockSelection.clearSelection(e); } /** * Handle left and up keyboard keys * * @param {KeyboardEvent} event - keyboard event */ arrowLeftAndUp(e) { if (this.Editor.UI.someToolbarOpened) { if (ce.usedKeys.includes(e.keyCode) && (!e.shiftKey || e.keyCode === y.TAB)) return; this.Editor.UI.closeAllToolbars(); } this.Editor.Toolbar.close(); const { currentBlock: t } = this.Editor.BlockManager, i = ((t == null ? void 0 : t.currentInput) !== void 0 ? Ne(t.currentInput) : void 0) || this.Editor.BlockSelection.anyBlockSelected; if (e.shiftKey && e.keyCode === y.UP && i) { this.Editor.CrossBlockSelection.toggleBlockSelectedState(!1); return; } if (e.keyCode === y.UP || e.keyCode === y.LEFT && !this.isRtl ? this.Editor.Caret.navigatePrevious() : this.Editor.Caret.navigateNext()) { e.preventDefault(); return; } Fe(() => { this.Editor.BlockManager.currentBlock && this.Editor.BlockManager.currentBlock.updateCurrentInput(); }, 20)(), this.Editor.BlockSelection.clearSelection(e); } /** * Cases when we need to close Toolbar * * @param {KeyboardEvent} event - keyboard event */ needToolbarClosing(e) { const t = e.keyCode === y.ENTER && this.Editor.Toolbar.toolbox.opened, o = e.keyCode === y.ENTER && this.Editor.BlockSettings.opened, i = e.keyCode === y.ENTER && this.Editor.InlineToolbar.opened, s = e.keyCode === y.TAB; return !(e.shiftKey || s || t || o || i); } /** * If Toolbox is not open, then just open it and show plus button */ activateToolbox() { this.Editor.Toolbar.opened || this.Editor.Toolbar.moveAndOpen(), this.Editor.Toolbar.toolbox.open(); } /** * Open Toolbar and show BlockSettings before flipping Tools */ activateBlockSettings() { this.Editor.Toolbar.opened || this.Editor.Toolbar.moveAndOpen(), this.Editor.BlockSettings.opened || this.Editor.BlockSettings.open(); } } class ct { /** * @class * @param {HTMLElement} workingArea — editor`s working node */ constructor(e) { this.blocks = [], this.workingArea = e; } /** * Get length of Block instances array * * @returns {number} */ get length() { return this.blocks.length; } /** * Get Block instances array * * @returns {Block[]} */ get array() { return this.blocks; } /** * Get blocks html elements array * * @returns {HTMLElement[]} */ get nodes() { return No(this.workingArea.children); } /** * Proxy trap to implement array-like setter * * @example * blocks[0] = new Block(...) * @param {Blocks} instance — Blocks instance * @param {PropertyKey} property — block index or any Blocks class property key to set * @param {Block} value — value to set * @returns {boolean} */ static set(e, t, o) { return isNaN(Number(t)) ? (Reflect.set(e, t, o), !0) : (e.insert(+t, o), !0); } /** * Proxy trap to implement array-like getter * * @param {Blocks} instance — Blocks instance * @param {PropertyKey} property — Blocks class property key * @returns {Block|*} */ static get(e, t) { return isNaN(Number(t)) ? Reflect.get(e, t) : e.get(+t); } /** * Push new Block to the blocks array and append it to working area * * @param {Block} block - Block to add */ push(e) { this.blocks.push(e), this.insertToDOM(e); } /** * Swaps blocks with indexes first and second * * @param {number} first - first block index * @param {number} second - second block index * @deprecated — use 'move' instead */ swap(e, t) { const o = this.blocks[t]; d.swap(this.blocks[e].holder, o.holder), this.blocks[t] = this.blocks[e], this.blocks[e] = o; } /** * Move a block from one to another index * * @param {number} toIndex - new index of the block * @param {number} fromIndex - block to move */ move(e, t) { const o = this.blocks.splice(t, 1)[0], i = e - 1, s = Math.max(0, i), r = this.blocks[s]; e > 0 ? this.insertToDOM(o, "afterend", r) : this.insertToDOM(o, "beforebegin", r), this.blocks.splice(e, 0, o); const a = this.composeBlockEvent("move", { fromIndex: t, toIndex: e }); o.call(ee.MOVED, a); } /** * Insert new Block at passed index * * @param {number} index — index to insert Block * @param {Block} block — Block to insert * @param {boolean} replace — it true, replace block on given index */ insert(e, t, o = !1) { if (!this.length) { this.push(t); return; } e > this.length && (e = this.length), o && (this.blocks[e].holder.remove(), this.blocks[e].call(ee.REMOVED)); const i = o ? 1 : 0; if (this.blocks.splice(e, i, t), e > 0) { const s = this.blocks[e - 1]; this.insertToDOM(t, "afterend", s); } else { const s = this.blocks[e + 1]; s ? this.insertToDOM(t, "beforebegin", s) : this.insertToDOM(t); } } /** * Replaces block under passed index with passed block * * @param index - index of existed block * @param block - new block */ replace(e, t) { if (this.blocks[e] === void 0) throw Error("Incorrect index"); this.blocks[e].holder.replaceWith(t.holder), this.blocks[e] = t; } /** * Inserts several blocks at once * * @param blocks - blocks to insert * @param index - index to insert blocks at */ insertMany(e, t) { const o = new DocumentFragment(); for (const i of e) o.appendChild(i.holder); if (this.length > 0) { if (t > 0) { const i = Math.min(t - 1, this.length - 1); this.blocks[i].holder.after(o); } else t === 0 && this.workingArea.prepend(o); this.blocks.splice(t, 0, ...e); } else this.blocks.push(...e), this.workingArea.appendChild(o); e.forEach((i) => i.call(ee.RENDERED)); } /** * Remove block * * @param {number} index - index of Block to remove */ remove(e) { isNaN(e) && (e = this.length - 1), this.blocks[e].holder.remove(), this.blocks[e].call(ee.REMOVED), this.blocks.splice(e, 1); } /** * Remove all blocks */ removeAll() { this.workingArea.innerHTML = "", this.blocks.forEach((e) => e.call(ee.REMOVED)), this.blocks.length = 0; } /** * Insert Block after passed target * * @todo decide if this method is necessary * @param {Block} targetBlock — target after which Block should be inserted * @param {Block} newBlock — Block to insert */ insertAfter(e, t) { const o = this.blocks.indexOf(e); this.insert(o + 1, t); } /** * Get Block by index * * @param {number} index — Block index * @returns {Block} */ get(e) { return this.blocks[e]; } /** * Return index of passed Block * * @param {Block} block - Block to find * @returns {number} */ indexOf(e) { return this.blocks.indexOf(e); } /** * Insert new Block into DOM * * @param {Block} block - Block to insert * @param {InsertPosition} position — insert position (if set, will use insertAdjacentElement) * @param {Block} target — Block related to position */ insertToDOM(e, t, o) { t ? o.holder.insertAdjacentElement(t, e.holder) : this.workingArea.appendChild(e.holder), e.call(ee.RENDERED); } /** * Composes Block event with passed type and details * * @param {string} type - event type * @param {object} detail - event detail */ composeBlockEvent(e, t) { return new CustomEvent(e, { detail: t }); } } const _o = "block-removed", Mo = "block-added", ia = "block-moved", Ao = "block-changed"; class sa { constructor() { this.completed = Promise.resolve(); } /** * Add new promise to queue * * @param operation - promise should be added to queue */ add(e) { return new Promise((t, o) => { this.completed = this.completed.then(e).then(t).catch(o); }); } } class ra extends E { constructor() { super(...arguments), this._currentBlockIndex = -1, this._blocks = null; } /** * Returns current Block index * * @returns {number} */ get currentBlockIndex() { return this._currentBlockIndex; } /** * Set current Block index and fire Block lifecycle callbacks * * @param {number} newIndex - index of Block to set as current */ set currentBlockIndex(e) { this._currentBlockIndex = e; } /** * returns first Block * * @returns {Block} */ get firstBlock() { return this._blocks[0]; } /** * returns last Block * * @returns {Block} */ get lastBlock() { return this._blocks[this._blocks.length - 1]; } /** * Get current Block instance * * @returns {Block} */ get currentBlock() { return this._blocks[this.currentBlockIndex]; } /** * Set passed Block as a current * * @param block - block to set as a current */ set currentBlock(e) { this.currentBlockIndex = this.getBlockIndex(e); } /** * Returns next Block instance * * @returns {Block|null} */ get nextBlock() { return this.currentBlockIndex === this._blocks.length - 1 ? null : this._blocks[this.currentBlockIndex + 1]; } /** * Return first Block with inputs after current Block * * @returns {Block | undefined} */ get nextContentfulBlock() { return this.blocks.slice(this.currentBlockIndex + 1).find((t) => !!t.inputs.length); } /** * Return first Block with inputs before current Block * * @returns {Block | undefined} */ get previousContentfulBlock() { return this.blocks.slice(0, this.currentBlockIndex).reverse().find((t) => !!t.inputs.length); } /** * Returns previous Block instance * * @returns {Block|null} */ get previousBlock() { return this.currentBlockIndex === 0 ? null : this._blocks[this.currentBlockIndex - 1]; } /** * Get array of Block instances * * @returns {Block[]} {@link Blocks#array} */ get blocks() { return this._blocks.array; } /** * Check if each Block is empty * * @returns {boolean} */ get isEditorEmpty() { return this.blocks.every((e) => e.isEmpty); } /** * Should be called after Editor.UI preparation * Define this._blocks property */ prepare() { const e = new ct(this.Editor.UI.nodes.redactor); this._blocks = new Proxy(e, { set: ct.set, get: ct.get }), this.listeners.on( document, "copy", (t) => this.Editor.BlockEvents.handleCommandC(t) ); } /** * Toggle read-only state * * If readOnly is true: * - Unbind event handlers from created Blocks * * if readOnly is false: * - Bind event handlers to all existing Blocks * * @param {boolean} readOnlyEnabled - "read only" state */ toggleReadOnly(e) { e ? this.disableModuleBindings() : this.enableModuleBindings(); } /** * Creates Block instance by tool name * * @param {object} options - block creation options * @param {string} options.tool - tools passed in editor config {@link EditorConfig#tools} * @param {string} [options.id] - unique id for this block * @param {BlockToolData} [options.data] - constructor params * @returns {Block} */ composeBlock({ tool: e, data: t = {}, id: o = void 0, tunes: i = {} }) { const s = this.Editor.ReadOnly.isEnabled, r = this.Editor.Tools.blockTools.get(e), a = new R({ id: o, data: t, tool: r, api: this.Editor.API, readOnly: s, tunesData: i }, this.eventsDispatcher); return s || window.requestIdleCallback(() => { this.bindBlockEvents(a); }, { timeout: 2e3 }), a; } /** * Insert new block into _blocks * * @param {object} options - insert options * @param {string} [options.id] - block's unique id * @param {string} [options.tool] - plugin name, by default method inserts the default block type * @param {object} [options.data] - plugin data * @param {number} [options.index] - index where to insert new Block * @param {boolean} [options.needToFocus] - flag shows if needed to update current Block index * @param {boolean} [options.replace] - flag shows if block by passed index should be replaced with inserted one * @returns {Block} */ insert({ id: e = void 0, tool: t = this.config.defaultBlock, data: o = {}, index: i, needToFocus: s = !0, replace: r = !1, tunes: a = {} } = {}) { let l = i; l === void 0 && (l = this.currentBlockIndex + (r ? 0 : 1)); const c = this.composeBlock({ id: e, tool: t, data: o, tunes: a }); return r && this.blockDidMutated(_o, this.getBlockByIndex(l), { index: l }), this._blocks.insert(l, c, r), this.blockDidMutated(Mo, c, { index: l }), s ? this.currentBlockIndex = l : l <= this.currentBlockIndex && this.currentBlockIndex++, c; } /** * Inserts several blocks at once * * @param blocks - blocks to insert * @param index - index where to insert */ insertMany(e, t = 0) { this._blocks.insertMany(e, t); } /** * Update Block data. * * Currently we don't have an 'update' method in the Tools API, so we just create a new block with the same id and type * Should not trigger 'block-removed' or 'block-added' events. * * If neither data nor tunes is provided, return the provided block instead. * * @param block - block to update * @param data - (optional) new data * @param tunes - (optional) tune data */ async update(e, t, o) { if (!t && !o) return e; const i = await e.data, s = this.composeBlock({ id: e.id, tool: e.name, data: Object.assign({}, i, t ?? {}), tunes: o ?? e.tunes }), r = this.getBlockIndex(e); return this._blocks.replace(r, s), this.blockDidMutated(Ao, s, { index: r }), s; } /** * Replace passed Block with the new one with specified Tool and data * * @param block - block to replace * @param newTool - new Tool name * @param data - new Tool data */ replace(e, t, o) { const i = this.getBlockIndex(e); return this.insert({ tool: t, data: o, index: i, replace: !0 }); } /** * Insert pasted content. Call onPaste callback after insert. * * @param {string} toolName - name of Tool to insert * @param {PasteEvent} pasteEvent - pasted data * @param {boolean} replace - should replace current block */ paste(e, t, o = !1) { const i = this.insert({ tool: e, replace: o }); try { window.requestIdleCallback(() => { i.call(ee.ON_PASTE, t); }); } catch (s) { S(`${e}: onPaste callback call is failed`, "error", s); } return i; } /** * Insert new default block at passed index * * @param {number} index - index where Block should be inserted * @param {boolean} needToFocus - if true, updates current Block index * * TODO: Remove method and use insert() with index instead (?) * @returns {Block} inserted Block */ insertDefaultBlockAtIndex(e, t = !1) { const o = this.composeBlock({ tool: this.config.defaultBlock }); return this._blocks[e] = o, this.blockDidMutated(Mo, o, { index: e }), t ? this.currentBlockIndex = e : e <= this.currentBlockIndex && this.currentBlockIndex++, o; } /** * Always inserts at the end * * @returns {Block} */ insertAtEnd() { return this.currentBlockIndex = this.blocks.length - 1, this.insert(); } /** * Merge two blocks * * @param {Block} targetBlock - previous block will be append to this block * @param {Block} blockToMerge - block that will be merged with target block * @returns {Promise} - the sequence that can be continued */ async mergeBlocks(e, t) { let o; if (e.name === t.name && e.mergeable) { const i = await t.data; if (V(i)) { console.error("Could not merge Block. Failed to extract original Block data."); return; } const [s] = yt([i], e.tool.sanitizeConfig); o = s; } else if (e.mergeable && He(t, "export") && He(e, "import")) { const i = await t.exportDataAsString(), s = Z(i, e.tool.sanitizeConfig); o = Bo(s, e.tool.conversionConfig); } o !== void 0 && (await e.mergeWith(o), this.removeBlock(t), this.currentBlockIndex = this._blocks.indexOf(e)); } /** * Remove passed Block * * @param block - Block to remove * @param addLastBlock - if true, adds new default block at the end. @todo remove this logic and use event-bus instead */ removeBlock(e, t = !0) { return new Promise((o) => { const i = this._blocks.indexOf(e); if (!this.validateIndex(i)) throw new Error("Can't find a Block to remove"); e.destroy(), this._blocks.remove(i), this.blockDidMutated(_o, e, { index: i }), this.currentBlockIndex >= i && this.currentBlockIndex--, this.blocks.length ? i === 0 && (this.currentBlockIndex = 0) : (this.unsetCurrentBlock(), t && this.insert()), o(); }); } /** * Remove only selected Blocks * and returns first Block index where started removing... * * @returns {number|undefined} */ removeSelectedBlocks() { let e; for (let t = this.blocks.length - 1; t >= 0; t--) this.blocks[t].selected && (this.removeBlock(this.blocks[t]), e = t); return e; } /** * Attention! * After removing insert the new default typed Block and focus on it * Removes all blocks */ removeAllBlocks() { for (let e = this.blocks.length - 1; e >= 0; e--) this._blocks.remove(e); this.unsetCurrentBlock(), this.insert(), this.currentBlock.firstInput.focus(); } /** * Split current Block * 1. Extract content from Caret position to the Block`s end * 2. Insert a new Block below current one with extracted content * * @returns {Block} */ split() { const e = this.Editor.Caret.extractFragmentFromCaretPosition(), t = d.make("div"); t.appendChild(e); const o = { text: d.isEmpty(t) ? "" : t.innerHTML }; return this.insert({ data: o }); } /** * Returns Block by passed index * * @param {number} index - index to get. -1 to get last * @returns {Block} */ getBlockByIndex(e) { return e === -1 && (e = this._blocks.length - 1), this._blocks[e]; } /** * Returns an index for passed Block * * @param block - block to find index */ getBlockIndex(e) { return this._blocks.indexOf(e); } /** * Returns the Block by passed id * * @param id - id of block to get * @returns {Block} */ getBlockById(e) { return this._blocks.array.find((t) => t.id === e); } /** * Get Block instance by html element * * @param {Node} element - html element to get Block by */ getBlock(e) { d.isElement(e) || (e = e.parentNode); const t = this._blocks.nodes, o = e.closest(`.${R.CSS.wrapper}`), i = t.indexOf(o); if (i >= 0) return this._blocks[i]; } /** * 1) Find first-level Block from passed child Node * 2) Mark it as current * * @param {Node} childNode - look ahead from this node. * @returns {Block | undefined} can return undefined in case when the passed child note is not a part of the current editor instance */ setCurrentBlockByChildNode(e) { d.isElement(e) || (e = e.parentNode); const t = e.closest(`.${R.CSS.wrapper}`); if (!t) return; const o = t.closest(`.${this.Editor.UI.CSS.editorWrapper}`); if (o != null && o.isEqualNode(this.Editor.UI.nodes.wrapper)) return this.currentBlockIndex = this._blocks.nodes.indexOf(t), this.currentBlock.updateCurrentInput(), this.currentBlock; } /** * Return block which contents passed node * * @param {Node} childNode - node to get Block by * @returns {Block} */ getBlockByChildNode(e) { if (!e || !(e instanceof Node)) return; d.isElement(e) || (e = e.parentNode); const t = e.closest(`.${R.CSS.wrapper}`); return this.blocks.find((o) => o.holder === t); } /** * Swap Blocks Position * * @param {number} fromIndex - index of first block * @param {number} toIndex - index of second block * @deprecated — use 'move' instead */ swap(e, t) { this._blocks.swap(e, t), this.currentBlockIndex = t; } /** * Move a block to a new index * * @param {number} toIndex - index where to move Block * @param {number} fromIndex - index of Block to move */ move(e, t = this.currentBlockIndex) { if (isNaN(e) || isNaN(t)) { S("Warning during 'move' call: incorrect indices provided.", "warn"); return; } if (!this.validateIndex(e) || !this.validateIndex(t)) { S("Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.", "warn"); return; } this._blocks.move(e, t), this.currentBlockIndex = e, this.blockDidMutated(ia, this.currentBlock, { fromIndex: t, toIndex: e }); } /** * Converts passed Block to the new Tool * Uses Conversion Config * * @param blockToConvert - Block that should be converted * @param targetToolName - name of the Tool to convert to * @param blockDataOverrides - optional new Block data overrides */ async convert(e, t, o) { if (!await e.save()) throw new Error("Could not convert Block. Failed to extract original Block data."); const s = this.Editor.Tools.blockTools.get(t); if (!s) throw new Error(`Could not convert Block. Tool «${t}» not found.`); const r = await e.exportDataAsString(), a = Z( r, s.sanitizeConfig ); let l = Bo(a, s.conversionConfig, s.settings); return o && (l = Object.assign(l, o)), this.replace(e, s.name, l); } /** * Sets current Block Index -1 which means unknown * and clear highlights */ unsetCurrentBlock() { this.currentBlockIndex = -1; } /** * Clears Editor * * @param {boolean} needToAddDefaultBlock - 1) in internal calls (for example, in api.blocks.render) * we don't need to add an empty default block * 2) in api.blocks.clear we should add empty block */ async clear(e = !1) { const t = new sa(); this.blocks.forEach((o) => { t.add(async () => { await this.removeBlock(o, !1); }); }), await t.completed, this.unsetCurrentBlock(), e && this.insert(), this.Editor.UI.checkEmptiness(); } /** * Cleans up all the block tools' resources * This is called when editor is destroyed */ async destroy() { await Promise.all(this.blocks.map((e) => e.destroy())); } /** * Bind Block events * * @param {Block} block - Block to which event should be bound */ bindBlockEvents(e) { const { BlockEvents: t } = this.Editor; this.readOnlyMutableListeners.on(e.holder, "keydown", (o) => { t.keydown(o); }), this.readOnlyMutableListeners.on(e.holder, "keyup", (o) => { t.keyup(o); }), this.readOnlyMutableListeners.on(e.holder, "dragover", (o) => { t.dragOver(o); }), this.readOnlyMutableListeners.on(e.holder, "dragleave", (o) => { t.dragLeave(o); }), e.on("didMutated", (o) => this.blockDidMutated(Ao, o, { index: this.getBlockIndex(o) })); } /** * Disable mutable handlers and bindings */ disableModuleBindings() { this.readOnlyMutableListeners.clearAll(); } /** * Enables all module handlers and bindings for all Blocks */ enableModuleBindings() { this.readOnlyMutableListeners.on( document, "cut", (e) => this.Editor.BlockEvents.handleCommandX(e) ), this.blocks.forEach((e) => { this.bindBlockEvents(e); }); } /** * Validates that the given index is not lower than 0 or higher than the amount of blocks * * @param {number} index - index of blocks array to validate * @returns {boolean} */ validateIndex(e) { return !(e < 0 || e >= this._blocks.length); } /** * Block mutation callback * * @param mutationType - what happened with block * @param block - mutated block * @param detailData - additional data to pass with change event */ blockDidMutated(e, t, o) { const i = new CustomEvent(e, { detail: { target: new J(t), ...o } }); return this.eventsDispatcher.emit($o, { event: i }), t; } } class aa extends E { constructor() { super(...arguments), this.anyBlockSelectedCache = null, this.needToSelectAll = !1, this.nativeInputSelected = !1, this.readyToBlockSelection = !1; } /** * Sanitizer Config * * @returns {SanitizerConfig} */ get sanitizerConfig() { return { p: {}, h1: {}, h2: {}, h3: {}, h4: {}, h5: {}, h6: {}, ol: {}, ul: {}, li: {}, br: !0, img: { src: !0, width: !0, height: !0 }, a: { href: !0 }, b: {}, i: {}, u: {} }; } /** * Flag that identifies all Blocks selection * * @returns {boolean} */ get allBlocksSelected() { const { BlockManager: e } = this.Editor; return e.blocks.every((t) => t.selected === !0); } /** * Set selected all blocks * * @param {boolean} state - state to set */ set allBlocksSelected(e) { const { BlockManager: t } = this.Editor; t.blocks.forEach((o) => { o.selected = e; }), this.clearCache(); } /** * Flag that identifies any Block selection * * @returns {boolean} */ get anyBlockSelected() { const { BlockManager: e } = this.Editor; return this.anyBlockSelectedCache === null && (this.anyBlockSelectedCache = e.blocks.some((t) => t.selected === !0)), this.anyBlockSelectedCache; } /** * Return selected Blocks array * * @returns {Block[]} */ get selectedBlocks() { return this.Editor.BlockManager.blocks.filter((e) => e.selected); } /** * Module Preparation * Registers Shortcuts CMD+A and CMD+C * to select all and copy them */ prepare() { this.selection = new b(), ge.add({ name: "CMD+A", handler: (e) => { const { BlockManager: t, ReadOnly: o } = this.Editor; if (o.isEnabled) { e.preventDefault(), this.selectAllBlocks(); return; } t.currentBlock && this.handleCommandA(e); }, on: this.Editor.UI.nodes.redactor }); } /** * Toggle read-only state * * - Remove all ranges * - Unselect all Blocks */ toggleReadOnly() { b.get().removeAllRanges(), this.allBlocksSelected = !1; } /** * Remove selection of Block * * @param {number?} index - Block index according to the BlockManager's indexes */ unSelectBlockByIndex(e) { const { BlockManager: t } = this.Editor; let o; isNaN(e) ? o = t.currentBlock : o = t.getBlockByIndex(e), o.selected = !1, this.clearCache(); } /** * Clear selection from Blocks * * @param {Event} reason - event caused clear of selection * @param {boolean} restoreSelection - if true, restore saved selection */ clearSelection(e, t = !1) { const { BlockManager: o, Caret: i, RectangleSelection: s } = this.Editor; this.needToSelectAll = !1, this.nativeInputSelected = !1, this.readyToBlockSelection = !1; const r = e && e instanceof KeyboardEvent, a = r && Po(e.keyCode); if (this.anyBlockSelected && r && a && !b.isSelectionExists) { const l = o.removeSelectedBlocks(); o.insertDefaultBlockAtIndex(l, !0), i.setToBlock(o.currentBlock), Fe(() => { const c = e.key; i.insertContentAtCaretPosition(c.length > 1 ? "" : c); }, 20)(); } if (this.Editor.CrossBlockSelection.clear(e), !this.anyBlockSelected || s.isRectActivated()) { this.Editor.RectangleSelection.clearSelection(); return; } t && this.selection.restore(), this.allBlocksSelected = !1; } /** * Reduce each Block and copy its content * * @param {ClipboardEvent} e - copy/cut event * @returns {Promise} */ copySelectedBlocks(e) { e.preventDefault(); const t = d.make("div"); this.selectedBlocks.forEach((s) => { const r = Z(s.holder.innerHTML, this.sanitizerConfig), a = d.make("p"); a.innerHTML = r, t.appendChild(a); }); const o = Array.from(t.childNodes).map((s) => s.textContent).join(` `), i = t.innerHTML; return e.clipboardData.setData("text/plain", o), e.clipboardData.setData("text/html", i), Promise.all(this.selectedBlocks.map((s) => s.save())).then((s) => { try { e.clipboardData.setData(this.Editor.Paste.MIME_TYPE, JSON.stringify(s)); } catch { } }); } /** * Select Block by its index * * @param {number?} index - Block index according to the BlockManager's indexes */ selectBlockByIndex(e) { const { BlockManager: t } = this.Editor, o = t.getBlockByIndex(e); o !== void 0 && this.selectBlock(o); } /** * Select passed Block * * @param {Block} block - Block to select */ selectBlock(e) { this.selection.save(), b.get().removeAllRanges(), e.selected = !0, this.clearCache(), this.Editor.InlineToolbar.close(); } /** * Remove selection from passed Block * * @param {Block} block - Block to unselect */ unselectBlock(e) { e.selected = !1, this.clearCache(); } /** * Clear anyBlockSelected cache */ clearCache() { this.anyBlockSelectedCache = null; } /** * Module destruction * De-registers Shortcut CMD+A */ destroy() { ge.remove(this.Editor.UI.nodes.redactor, "CMD+A"); } /** * First CMD+A selects all input content by native behaviour, * next CMD+A keypress selects all blocks * * @param {KeyboardEvent} event - keyboard event */ handleCommandA(e) { if (this.Editor.RectangleSelection.clearSelection(), d.isNativeInput(e.target) && !this.readyToBlockSelection) { this.readyToBlockSelection = !0; return; } const t = this.Editor.BlockManager.getBlock(e.target), o = t.inputs; if (o.length > 1 && !this.readyToBlockSelection) { this.readyToBlockSelection = !0; return; } if (o.length === 1 && !this.needToSelectAll) { this.needToSelectAll = !0; return; } this.needToSelectAll ? (e.preventDefault(), this.selectAllBlocks(), this.needToSelectAll = !1, this.readyToBlockSelection = !1) : this.readyToBlockSelection && (e.preventDefault(), this.selectBlock(t), this.needToSelectAll = !0); } /** * Select All Blocks * Each Block has selected setter that makes Block copyable */ selectAllBlocks() { this.selection.save(), b.get().removeAllRanges(), this.allBlocksSelected = !0, this.Editor.InlineToolbar.close(); } } class Ye extends E { /** * Allowed caret positions in input * * @static * @returns {{START: string, END: string, DEFAULT: string}} */ get positions() { return { START: "start", END: "end", DEFAULT: "default" }; } /** * Elements styles that can be useful for Caret Module */ static get CSS() { return { shadowCaret: "cdx-shadow-caret" }; } /** * Method gets Block instance and puts caret to the text node with offset * There two ways that method applies caret position: * - first found text node: sets at the beginning, but you can pass an offset * - last found text node: sets at the end of the node. Also, you can customize the behaviour * * @param {Block} block - Block class * @param {string} position - position where to set caret. * If default - leave default behaviour and apply offset if it's passed * @param {number} offset - caret offset regarding to the text node */ setToBlock(e, t = this.positions.DEFAULT, o = 0) { var c; const { BlockManager: i, BlockSelection: s } = this.Editor; if (s.clearSelection(), !e.focusable) { (c = window.getSelection()) == null || c.removeAllRanges(), s.selectBlock(e), i.currentBlock = e; return; } let r; switch (t) { case this.positions.START: r = e.firstInput; break; case this.positions.END: r = e.lastInput; break; default: r = e.currentInput; } if (!r) return; const a = d.getDeepestNode(r, t === this.positions.END), l = d.getContentLength(a); switch (!0) { case t === this.positions.START: o = 0; break; case t === this.positions.END: case o > l: o = l; break; } this.set(a, o), i.setCurrentBlockByChildNode(e.holder), i.currentBlock.currentInput = r; } /** * Set caret to the current input of current Block. * * @param {HTMLElement} input - input where caret should be set * @param {string} position - position of the caret. * If default - leave default behaviour and apply offset if it's passed * @param {number} offset - caret offset regarding to the text node */ setToInput(e, t = this.positions.DEFAULT, o = 0) { const { currentBlock: i } = this.Editor.BlockManager, s = d.getDeepestNode(e); switch (t) { case this.positions.START: this.set(s, 0); break; case this.positions.END: this.set(s, d.getContentLength(s)); break; default: o && this.set(s, o); } i.currentInput = e; } /** * Creates Document Range and sets caret to the element with offset * * @param {HTMLElement} element - target node. * @param {number} offset - offset */ set(e, t = 0) { const { top: i, bottom: s } = b.setCursor(e, t), { innerHeight: r } = window; i < 0 ? window.scrollBy(0, i - 30) : s > r && window.scrollBy(0, s - r + 30); } /** * Set Caret to the last Block * If last block is not empty, append another empty block */ setToTheLastBlock() { const e = this.Editor.BlockManager.lastBlock; if (e) if (e.tool.isDefault && e.isEmpty) this.setToBlock(e); else { const t = this.Editor.BlockManager.insertAtEnd(); this.setToBlock(t); } } /** * Extract content fragment of current Block from Caret position to the end of the Block */ extractFragmentFromCaretPosition() { const e = b.get(); if (e.rangeCount) { const t = e.getRangeAt(0), o = this.Editor.BlockManager.currentBlock.currentInput; if (t.deleteContents(), o) if (d.isNativeInput(o)) { const i = o, s = document.createDocumentFragment(), r = i.value.substring(0, i.selectionStart), a = i.value.substring(i.selectionStart); return s.textContent = a, i.value = r, s; } else { const i = t.cloneRange(); return i.selectNodeContents(o), i.setStart(t.endContainer, t.endOffset), i.extractContents(); } } } /** * Set's caret to the next Block or Tool`s input * Before moving caret, we should check if caret position is at the end of Plugins node * Using {@link Dom#getDeepestNode} to get a last node and match with current selection * * @param {boolean} force - pass true to skip check for caret position */ navigateNext(e = !1) { const { BlockManager: t } = this.Editor, { currentBlock: o, nextBlock: i } = t; if (o === void 0) return !1; const { nextInput: s, currentInput: r } = o, a = r !== void 0 ? Re(r) : void 0; let l = i; const c = e || a || !o.focusable; if (s && c) return this.setToInput(s, this.positions.START), !0; if (l === null) { if (o.tool.isDefault || !c) return !1; l = t.insertAtEnd(); } return c ? (this.setToBlock(l, this.positions.START), !0) : !1; } /** * Set's caret to the previous Tool`s input or Block * Before moving caret, we should check if caret position is start of the Plugins node * Using {@link Dom#getDeepestNode} to get a last node and match with current selection * * @param {boolean} force - pass true to skip check for caret position */ navigatePrevious(e = !1) { const { currentBlock: t, previousBlock: o } = this.Editor.BlockManager; if (!t) return !1; const { previousInput: i, currentInput: s } = t, r = s !== void 0 ? Ne(s) : void 0, a = e || r || !t.focusable; return i && a ? (this.setToInput(i, this.positions.END), !0) : o !== null && a ? (this.setToBlock(o, this.positions.END), !0) : !1; } /** * Inserts shadow element after passed element where caret can be placed * * @param {Element} element - element after which shadow caret should be inserted */ createShadow(e) { const t = document.createElement("span"); t.classList.add(Ye.CSS.shadowCaret), e.insertAdjacentElement("beforeend", t); } /** * Restores caret position * * @param {HTMLElement} element - element where caret should be restored */ restoreCaret(e) { const t = e.querySelector(`.${Ye.CSS.shadowCaret}`); if (!t) return; new b().expandToTag(t); const i = document.createRange(); i.selectNode(t), i.extractContents(); } /** * Inserts passed content at caret position * * @param {string} content - content to insert */ insertContentAtCaretPosition(e) { const t = document.createDocumentFragment(), o = document.createElement("div"), i = b.get(), s = b.range; o.innerHTML = e, Array.from(o.childNodes).forEach((c) => t.appendChild(c)), t.childNodes.length === 0 && t.appendChild(new Text()); const r = t.lastChild; s.deleteContents(), s.insertNode(t); const a = document.createRange(), l = r.nodeType === Node.TEXT_NODE ? r : r.firstChild; l !== null && l.textContent !== null && a.setStart(l, l.textContent.length), i.removeAllRanges(), i.addRange(a); } } class la extends E { constructor() { super(...arguments), this.onMouseUp = () => { this.listeners.off(document, "mouseover", this.onMouseOver), this.listeners.off(document, "mouseup", this.onMouseUp); }, this.onMouseOver = (e) => { const { BlockManager: t, BlockSelection: o } = this.Editor; if (e.relatedTarget === null && e.target === null) return; const i = t.getBlockByChildNode(e.relatedTarget) || this.lastSelectedBlock, s = t.getBlockByChildNode(e.target); if (!(!i || !s) && s !== i) { if (i === this.firstSelectedBlock) { b.get().removeAllRanges(), i.selected = !0, s.selected = !0, o.clearCache(); return; } if (s === this.firstSelectedBlock) { i.selected = !1, s.selected = !1, o.clearCache(); return; } this.Editor.InlineToolbar.close(), this.toggleBlocksSelectedState(i, s), this.lastSelectedBlock = s; } }; } /** * Module preparation * * @returns {Promise} */ async prepare() { this.listeners.on(document, "mousedown", (e) => { this.enableCrossBlockSelection(e); }); } /** * Sets up listeners * * @param {MouseEvent} event - mouse down event */ watchSelection(e) { if (e.button !== qn.LEFT) return; const { BlockManager: t } = this.Editor; this.firstSelectedBlock = t.getBlock(e.target), this.lastSelectedBlock = this.firstSelectedBlock, this.listeners.on(document, "mouseover", this.onMouseOver), this.listeners.on(document, "mouseup", this.onMouseUp); } /** * Return boolean is cross block selection started: * there should be at least 2 selected blocks */ get isCrossBlockSelectionStarted() { return !!this.firstSelectedBlock && !!this.lastSelectedBlock && this.firstSelectedBlock !== this.lastSelectedBlock; } /** * Change selection state of the next Block * Used for CBS via Shift + arrow keys * * @param {boolean} next - if true, toggle next block. Previous otherwise */ toggleBlockSelectedState(e = !0) { const { BlockManager: t, BlockSelection: o } = this.Editor; this.lastSelectedBlock || (this.lastSelectedBlock = this.firstSelectedBlock = t.currentBlock), this.firstSelectedBlock === this.lastSelectedBlock && (this.firstSelectedBlock.selected = !0, o.clearCache(), b.get().removeAllRanges()); const i = t.blocks.indexOf(this.lastSelectedBlock) + (e ? 1 : -1), s = t.blocks[i]; s && (this.lastSelectedBlock.selected !== s.selected ? (s.selected = !0, o.clearCache()) : (this.lastSelectedBlock.selected = !1, o.clearCache()), this.lastSelectedBlock = s, this.Editor.InlineToolbar.close(), s.holder.scrollIntoView({ block: "nearest" })); } /** * Clear saved state * * @param {Event} reason - event caused clear of selection */ clear(e) { const { BlockManager: t, BlockSelection: o, Caret: i } = this.Editor, s = t.blocks.indexOf(this.firstSelectedBlock), r = t.blocks.indexOf(this.lastSelectedBlock); if (o.anyBlockSelected && s > -1 && r > -1 && e && e instanceof KeyboardEvent) switch (e.keyCode) { case y.DOWN: case y.RIGHT: i.setToBlock(t.blocks[Math.max(s, r)], i.positions.END); break; case y.UP: case y.LEFT: i.setToBlock(t.blocks[Math.min(s, r)], i.positions.START); break; default: i.setToBlock(t.blocks[Math.max(s, r)], i.positions.END); } this.firstSelectedBlock = this.lastSelectedBlock = null; } /** * Enables Cross Block Selection * * @param {MouseEvent} event - mouse down event */ enableCrossBlockSelection(e) { const { UI: t } = this.Editor; b.isCollapsed || this.Editor.BlockSelection.clearSelection(e), t.nodes.redactor.contains(e.target) ? this.watchSelection(e) : this.Editor.BlockSelection.clearSelection(e); } /** * Change blocks selection state between passed two blocks. * * @param {Block} firstBlock - first block in range * @param {Block} lastBlock - last block in range */ toggleBlocksSelectedState(e, t) { const { BlockManager: o, BlockSelection: i } = this.Editor, s = o.blocks.indexOf(e), r = o.blocks.indexOf(t), a = e.selected !== t.selected; for (let l = Math.min(s, r); l <= Math.max(s, r); l++) { const c = o.blocks[l]; c !== this.firstSelectedBlock && c !== (a ? e : t) && (o.blocks[l].selected = !o.blocks[l].selected, i.clearCache()); } } } class ca extends E { constructor() { super(...arguments), this.isStartedAtEditor = !1; } /** * Toggle read-only state * * if state is true: * - disable all drag-n-drop event handlers * * if state is false: * - restore drag-n-drop event handlers * * @param {boolean} readOnlyEnabled - "read only" state */ toggleReadOnly(e) { e ? this.disableModuleBindings() : this.enableModuleBindings(); } /** * Add drag events listeners to editor zone */ enableModuleBindings() { const { UI: e } = this.Editor; this.readOnlyMutableListeners.on(e.nodes.holder, "drop", async (t) => { await this.processDrop(t); }, !0), this.readOnlyMutableListeners.on(e.nodes.holder, "dragstart", () => { this.processDragStart(); }), this.readOnlyMutableListeners.on(e.nodes.holder, "dragover", (t) => { this.processDragOver(t); }, !0); } /** * Unbind drag-n-drop event handlers */ disableModuleBindings() { this.readOnlyMutableListeners.clearAll(); } /** * Handle drop event * * @param {DragEvent} dropEvent - drop event */ async processDrop(e) { const { BlockManager: t, Paste: o, Caret: i } = this.Editor; e.preventDefault(), t.blocks.forEach((r) => { r.dropTarget = !1; }), b.isAtEditor && !b.isCollapsed && this.isStartedAtEditor && document.execCommand("delete"), this.isStartedAtEditor = !1; const s = t.setCurrentBlockByChildNode(e.target); if (s) this.Editor.Caret.setToBlock(s, i.positions.END); else { const r = t.setCurrentBlockByChildNode(t.lastBlock.holder); this.Editor.Caret.setToBlock(r, i.positions.END); } await o.processDataTransfer(e.dataTransfer, !0); } /** * Handle drag start event */ processDragStart() { b.isAtEditor && !b.isCollapsed && (this.isStartedAtEditor = !0), this.Editor.InlineToolbar.close(); } /** * @param {DragEvent} dragEvent - drag event */ processDragOver(e) { e.preventDefault(); } } const da = 180, ua = 400; class ha extends E { /** * Prepare the module * * @param options - options used by the modification observer module * @param options.config - Editor configuration object * @param options.eventsDispatcher - common Editor event bus */ constructor({ config: e, eventsDispatcher: t }) { super({ config: e, eventsDispatcher: t }), this.disabled = !1, this.batchingTimeout = null, this.batchingOnChangeQueue = /* @__PURE__ */ new Map(), this.batchTime = ua, this.mutationObserver = new MutationObserver((o) => { this.redactorChanged(o); }), this.eventsDispatcher.on($o, (o) => { this.particularBlockChanged(o.event); }), this.eventsDispatcher.on(zo, () => { this.disable(); }), this.eventsDispatcher.on(Uo, () => { this.enable(); }); } /** * Enables onChange event */ enable() { this.mutationObserver.observe( this.Editor.UI.nodes.redactor, { childList: !0, subtree: !0, characterData: !0, attributes: !0 } ), this.disabled = !1; } /** * Disables onChange event */ disable() { this.mutationObserver.disconnect(), this.disabled = !0; } /** * Call onChange event passed to Editor.js configuration * * @param event - some of our custom change events */ particularBlockChanged(e) { this.disabled || !A(this.config.onChange) || (this.batchingOnChangeQueue.set(`block:${e.detail.target.id}:event:${e.type}`, e), this.batchingTimeout && clearTimeout(this.batchingTimeout), this.batchingTimeout = setTimeout(() => { let t; this.batchingOnChangeQueue.size === 1 ? t = this.batchingOnChangeQueue.values().next().value : t = Array.from(this.batchingOnChangeQueue.values()), this.config.onChange && this.config.onChange(this.Editor.API.methods, t), this.batchingOnChangeQueue.clear(); }, this.batchTime)); } /** * Fired on every blocks wrapper dom change * * @param mutations - mutations happened */ redactorChanged(e) { this.eventsDispatcher.emit(ft, { mutations: e }); } } const Rn = class Dn extends E { constructor() { super(...arguments), this.MIME_TYPE = "application/x-editor-js", this.toolsTags = {}, this.tagsByTool = {}, this.toolsPatterns = [], this.toolsFiles = {}, this.exceptionList = [], this.processTool = (e) => { try { const t = e.create({}, {}, !1); if (e.pasteConfig === !1) { this.exceptionList.push(e.name); return; } if (!A(t.onPaste)) return; this.getTagsConfig(e), this.getFilesConfig(e), this.getPatternsConfig(e); } catch (t) { S( `Paste handling for «${e.name}» Tool hasn't been set up because of the error`, "warn", t ); } }, this.handlePasteEvent = async (e) => { const { BlockManager: t, Toolbar: o } = this.Editor, i = t.setCurrentBlockByChildNode(e.target); !i || this.isNativeBehaviour(e.target) && !e.clipboardData.types.includes("Files") || i && this.exceptionList.includes(i.name) || (e.preventDefault(), this.processDataTransfer(e.clipboardData), o.close()); }; } /** * Set onPaste callback and collect tools` paste configurations */ async prepare() { this.processTools(); } /** * Set read-only state * * @param {boolean} readOnlyEnabled - read only flag value */ toggleReadOnly(e) { e ? this.unsetCallback() : this.setCallback(); } /** * Handle pasted or dropped data transfer object * * @param {DataTransfer} dataTransfer - pasted or dropped data transfer object * @param {boolean} isDragNDrop - true if data transfer comes from drag'n'drop events */ async processDataTransfer(e, t = !1) { const { Tools: o } = this.Editor, i = e.types; if ((i.includes ? i.includes("Files") : i.contains("Files")) && !V(this.toolsFiles)) { await this.processFiles(e.files); return; } const r = e.getData(this.MIME_TYPE), a = e.getData("text/plain"); let l = e.getData("text/html"); if (r) try { this.insertEditorJSData(JSON.parse(r)); return; } catch { } t && a.trim() && l.trim() && (l = "

" + (l.trim() ? l : a) + "

"); const c = Object.keys(this.toolsTags).reduce((p, g) => (p[g.toLowerCase()] = this.toolsTags[g].sanitizationConfig ?? {}, p), {}), u = Object.assign({}, c, o.getAllInlineToolsSanitizeConfig(), { br: {} }), h = Z(l, u); !h.trim() || h.trim() === a || !d.isHTMLString(h) ? await this.processText(a) : await this.processText(h, !0); } /** * Process pasted text and divide them into Blocks * * @param {string} data - text to process. Can be HTML or plain. * @param {boolean} isHTML - if passed string is HTML, this parameter should be true */ async processText(e, t = !1) { const { Caret: o, BlockManager: i } = this.Editor, s = t ? this.processHTML(e) : this.processPlain(e); if (!s.length) return; if (s.length === 1) { s[0].isBlock ? this.processSingleBlock(s.pop()) : this.processInlinePaste(s.pop()); return; } const a = i.currentBlock && i.currentBlock.tool.isDefault && i.currentBlock.isEmpty; s.map( async (l, c) => this.insertBlock(l, c === 0 && a) ), i.currentBlock && o.setToBlock(i.currentBlock, o.positions.END); } /** * Set onPaste callback handler */ setCallback() { this.listeners.on(this.Editor.UI.nodes.holder, "paste", this.handlePasteEvent); } /** * Unset onPaste callback handler */ unsetCallback() { this.listeners.off(this.Editor.UI.nodes.holder, "paste", this.handlePasteEvent); } /** * Get and process tool`s paste configs */ processTools() { const e = this.Editor.Tools.blockTools; Array.from(e.values()).forEach(this.processTool); } /** * Get tags name list from either tag name or sanitization config. * * @param {string | object} tagOrSanitizeConfig - tag name or sanitize config object. * @returns {string[]} array of tags. */ collectTagNames(e) { return te(e) ? [e] : D(e) ? Object.keys(e) : []; } /** * Get tags to substitute by Tool * * @param tool - BlockTool object */ getTagsConfig(e) { if (e.pasteConfig === !1) return; const t = e.pasteConfig.tags || [], o = []; t.forEach((i) => { const s = this.collectTagNames(i); o.push(...s), s.forEach((r) => { if (Object.prototype.hasOwnProperty.call(this.toolsTags, r)) { S( `Paste handler for «${e.name}» Tool on «${r}» tag is skipped because it is already used by «${this.toolsTags[r].tool.name}» Tool.`, "warn" ); return; } const a = D(i) ? i[r] : null; this.toolsTags[r.toUpperCase()] = { tool: e, sanitizationConfig: a }; }); }), this.tagsByTool[e.name] = o.map((i) => i.toUpperCase()); } /** * Get files` types and extensions to substitute by Tool * * @param tool - BlockTool object */ getFilesConfig(e) { if (e.pasteConfig === !1) return; const { files: t = {} } = e.pasteConfig; let { extensions: o, mimeTypes: i } = t; !o && !i || (o && !Array.isArray(o) && (S(`«extensions» property of the onDrop config for «${e.name}» Tool should be an array`), o = []), i && !Array.isArray(i) && (S(`«mimeTypes» property of the onDrop config for «${e.name}» Tool should be an array`), i = []), i && (i = i.filter((s) => ei(s) ? !0 : (S(`MIME type value «${s}» for the «${e.name}» Tool is not a valid MIME type`, "warn"), !1))), this.toolsFiles[e.name] = { extensions: o || [], mimeTypes: i || [] }); } /** * Get RegExp patterns to substitute by Tool * * @param tool - BlockTool object */ getPatternsConfig(e) { e.pasteConfig === !1 || !e.pasteConfig.patterns || V(e.pasteConfig.patterns) || Object.entries(e.pasteConfig.patterns).forEach(([t, o]) => { o instanceof RegExp || S( `Pattern ${o} for «${e.name}» Tool is skipped because it should be a Regexp instance.`, "warn" ), this.toolsPatterns.push({ key: t, pattern: o, tool: e }); }); } /** * Check if browser behavior suits better * * @param {EventTarget} element - element where content has been pasted * @returns {boolean} */ isNativeBehaviour(e) { return d.isNativeInput(e); } /** * Get files from data transfer object and insert related Tools * * @param {FileList} items - pasted or dropped items */ async processFiles(e) { const { BlockManager: t } = this.Editor; let o; o = await Promise.all( Array.from(e).map((r) => this.processFile(r)) ), o = o.filter((r) => !!r); const s = t.currentBlock.tool.isDefault && t.currentBlock.isEmpty; o.forEach( (r, a) => { t.paste(r.type, r.event, a === 0 && s); } ); } /** * Get information about file and find Tool to handle it * * @param {File} file - file to process */ async processFile(e) { const t = Jn(e), o = Object.entries(this.toolsFiles).find(([r, { mimeTypes: a, extensions: l }]) => { const [c, u] = e.type.split("/"), h = l.find((g) => g.toLowerCase() === t.toLowerCase()), p = a.find((g) => { const [f, v] = g.split("/"); return f === c && (v === u || v === "*"); }); return !!h || !!p; }); if (!o) return; const [i] = o; return { event: this.composePasteEvent("file", { file: e }), type: i }; } /** * Split HTML string to blocks and return it as array of Block data * * @param {string} innerHTML - html string to process * @returns {PasteData[]} */ processHTML(e) { const { Tools: t } = this.Editor, o = d.make("DIV"); return o.innerHTML = e, this.getNodes(o).map((s) => { let r, a = t.defaultTool, l = !1; switch (s.nodeType) { case Node.DOCUMENT_FRAGMENT_NODE: r = d.make("div"), r.appendChild(s); break; case Node.ELEMENT_NODE: r = s, l = !0, this.toolsTags[r.tagName] && (a = this.toolsTags[r.tagName].tool); break; } const { tags: c } = a.pasteConfig || { tags: [] }, u = c.reduce((g, f) => (this.collectTagNames(f).forEach((O) => { const T = D(f) ? f[O] : null; g[O.toLowerCase()] = T || {}; }), g), {}), h = Object.assign({}, u, a.baseSanitizeConfig); if (r.tagName.toLowerCase() === "table") { const g = Z(r.outerHTML, h); r = d.make("div", void 0, { innerHTML: g }).firstChild; } else r.innerHTML = Z(r.innerHTML, h); const p = this.composePasteEvent("tag", { data: r }); return { content: r, isBlock: l, tool: a.name, event: p }; }).filter((s) => { const r = d.isEmpty(s.content), a = d.isSingleTag(s.content); return !r || a; }); } /** * Split plain text by new line symbols and return it as array of Block data * * @param {string} plain - string to process * @returns {PasteData[]} */ processPlain(e) { const { defaultBlock: t } = this.config; if (!e) return []; const o = t; return e.split(/\r?\n/).filter((i) => i.trim()).map((i) => { const s = d.make("div"); s.textContent = i; const r = this.composePasteEvent("tag", { data: s }); return { content: s, tool: o, isBlock: !1, event: r }; }); } /** * Process paste of single Block tool content * * @param {PasteData} dataToInsert - data of Block to insert */ async processSingleBlock(e) { const { Caret: t, BlockManager: o } = this.Editor, { currentBlock: i } = o; if (!i || e.tool !== i.name || !d.containsOnlyInlineElements(e.content.innerHTML)) { this.insertBlock(e, (i == null ? void 0 : i.tool.isDefault) && i.isEmpty); return; } t.insertContentAtCaretPosition(e.content.innerHTML); } /** * Process paste to single Block: * 1. Find patterns` matches * 2. Insert new block if it is not the same type as current one * 3. Just insert text if there is no substitutions * * @param {PasteData} dataToInsert - data of Block to insert */ async processInlinePaste(e) { const { BlockManager: t, Caret: o } = this.Editor, { content: i } = e; if (t.currentBlock && t.currentBlock.tool.isDefault && i.textContent.length < Dn.PATTERN_PROCESSING_MAX_LENGTH) { const r = await this.processPattern(i.textContent); if (r) { const a = t.currentBlock && t.currentBlock.tool.isDefault && t.currentBlock.isEmpty, l = t.paste(r.tool, r.event, a); o.setToBlock(l, o.positions.END); return; } } if (t.currentBlock && t.currentBlock.currentInput) { const r = t.currentBlock.tool.baseSanitizeConfig; document.execCommand( "insertHTML", !1, Z(i.innerHTML, r) ); } else this.insertBlock(e); } /** * Get patterns` matches * * @param {string} text - text to process * @returns {Promise<{event: PasteEvent, tool: string}>} */ async processPattern(e) { const t = this.toolsPatterns.find((i) => { const s = i.pattern.exec(e); return s ? e === s.shift() : !1; }); return t ? { event: this.composePasteEvent("pattern", { key: t.key, data: e }), tool: t.tool.name } : void 0; } /** * Insert pasted Block content to Editor * * @param {PasteData} data - data to insert * @param {boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block * @returns {void} */ insertBlock(e, t = !1) { const { BlockManager: o, Caret: i } = this.Editor, { currentBlock: s } = o; let r; if (t && s && s.isEmpty) { r = o.paste(e.tool, e.event, !0), i.setToBlock(r, i.positions.END); return; } r = o.paste(e.tool, e.event), i.setToBlock(r, i.positions.END); } /** * Insert data passed as application/x-editor-js JSON * * @param {Array} blocks — Blocks' data to insert * @returns {void} */ insertEditorJSData(e) { const { BlockManager: t, Caret: o, Tools: i } = this.Editor; yt( e, (r) => i.blockTools.get(r).sanitizeConfig ).forEach(({ tool: r, data: a }, l) => { let c = !1; l === 0 && (c = t.currentBlock && t.currentBlock.tool.isDefault && t.currentBlock.isEmpty); const u = t.insert({ tool: r, data: a, replace: c }); o.setToBlock(u, o.positions.END); }); } /** * Fetch nodes from Element node * * @param {Node} node - current node * @param {Node[]} nodes - processed nodes * @param {Node} destNode - destination node */ processElementNode(e, t, o) { const i = Object.keys(this.toolsTags), s = e, { tool: r } = this.toolsTags[s.tagName] || {}, a = this.tagsByTool[r == null ? void 0 : r.name] || [], l = i.includes(s.tagName), c = d.blockElements.includes(s.tagName.toLowerCase()), u = Array.from(s.children).some( ({ tagName: p }) => i.includes(p) && !a.includes(p) ), h = Array.from(s.children).some( ({ tagName: p }) => d.blockElements.includes(p.toLowerCase()) ); if (!c && !l && !u) return o.appendChild(s), [...t, o]; if (l && !u || c && !h && !u) return [...t, o, s]; } /** * Recursively divide HTML string to two types of nodes: * 1. Block element * 2. Document Fragments contained text and markup tags like a, b, i etc. * * @param {Node} wrapper - wrapper of paster HTML content * @returns {Node[]} */ getNodes(e) { const t = Array.from(e.childNodes); let o; const i = (s, r) => { if (d.isEmpty(r) && !d.isSingleTag(r)) return s; const a = s[s.length - 1]; let l = new DocumentFragment(); switch (a && d.isFragment(a) && (l = s.pop()), r.nodeType) { case Node.ELEMENT_NODE: if (o = this.processElementNode(r, s, l), o) return o; break; case Node.TEXT_NODE: return l.appendChild(r), [...s, l]; default: return [...s, l]; } return [...s, ...Array.from(r.childNodes).reduce(i, [])]; }; return t.reduce(i, []); } /** * Compose paste event with passed type and detail * * @param {string} type - event type * @param {PasteEventDetail} detail - event detail */ composePasteEvent(e, t) { return new CustomEvent(e, { detail: t }); } }; Rn.PATTERN_PROCESSING_MAX_LENGTH = 450; let pa = Rn; class fa extends E { constructor() { super(...arguments), this.toolsDontSupportReadOnly = [], this.readOnlyEnabled = !1; } /** * Returns state of read only mode */ get isEnabled() { return this.readOnlyEnabled; } /** * Set initial state */ async prepare() { const { Tools: e } = this.Editor, { blockTools: t } = e, o = []; Array.from(t.entries()).forEach(([i, s]) => { s.isReadOnlySupported || o.push(i); }), this.toolsDontSupportReadOnly = o, this.config.readOnly && o.length > 0 && this.throwCriticalError(), this.toggle(this.config.readOnly, !0); } /** * Set read-only mode or toggle current state * Call all Modules `toggleReadOnly` method and re-render Editor * * @param state - (optional) read-only state or toggle * @param isInitial - (optional) true when editor is initializing */ async toggle(e = !this.readOnlyEnabled, t = !1) { e && this.toolsDontSupportReadOnly.length > 0 && this.throwCriticalError(); const o = this.readOnlyEnabled; this.readOnlyEnabled = e; for (const s in this.Editor) this.Editor[s].toggleReadOnly && this.Editor[s].toggleReadOnly(e); if (o === e) return this.readOnlyEnabled; if (t) return this.readOnlyEnabled; this.Editor.ModificationsObserver.disable(); const i = await this.Editor.Saver.save(); return await this.Editor.BlockManager.clear(), await this.Editor.Renderer.render(i.blocks), this.Editor.ModificationsObserver.enable(), this.readOnlyEnabled; } /** * Throws an error about tools which don't support read-only mode */ throwCriticalError() { throw new Ho( `To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(", ")} don't support read-only mode.` ); } } class Be extends E { constructor() { super(...arguments), this.isRectSelectionActivated = !1, this.SCROLL_SPEED = 3, this.HEIGHT_OF_SCROLL_ZONE = 40, this.BOTTOM_SCROLL_ZONE = 1, this.TOP_SCROLL_ZONE = 2, this.MAIN_MOUSE_BUTTON = 0, this.mousedown = !1, this.isScrolling = !1, this.inScrollZone = null, this.startX = 0, this.startY = 0, this.mouseX = 0, this.mouseY = 0, this.stackOfSelected = [], this.listenerIds = []; } /** * CSS classes for the Block * * @returns {{wrapper: string, content: string}} */ static get CSS() { return { overlay: "codex-editor-overlay", overlayContainer: "codex-editor-overlay__container", rect: "codex-editor-overlay__rectangle", topScrollZone: "codex-editor-overlay__scroll-zone--top", bottomScrollZone: "codex-editor-overlay__scroll-zone--bottom" }; } /** * Module Preparation * Creating rect and hang handlers */ prepare() { this.enableModuleBindings(); } /** * Init rect params * * @param {number} pageX - X coord of mouse * @param {number} pageY - Y coord of mouse */ startSelection(e, t) { const o = document.elementFromPoint(e - window.pageXOffset, t - window.pageYOffset); o.closest(`.${this.Editor.Toolbar.CSS.toolbar}`) || (this.Editor.BlockSelection.allBlocksSelected = !1, this.clearSelection(), this.stackOfSelected = []); const s = [ `.${R.CSS.content}`, `.${this.Editor.Toolbar.CSS.toolbar}`, `.${this.Editor.InlineToolbar.CSS.inlineToolbar}` ], r = o.closest("." + this.Editor.UI.CSS.editorWrapper), a = s.some((l) => !!o.closest(l)); !r || a || (this.mousedown = !0, this.startX = e, this.startY = t); } /** * Clear all params to end selection */ endSelection() { this.mousedown = !1, this.startX = 0, this.startY = 0, this.overlayRectangle.style.display = "none"; } /** * is RectSelection Activated */ isRectActivated() { return this.isRectSelectionActivated; } /** * Mark that selection is end */ clearSelection() { this.isRectSelectionActivated = !1; } /** * Sets Module necessary event handlers */ enableModuleBindings() { const { container: e } = this.genHTML(); this.listeners.on(e, "mousedown", (t) => { this.processMouseDown(t); }, !1), this.listeners.on(document.body, "mousemove", dt((t) => { this.processMouseMove(t); }, 10), { passive: !0 }), this.listeners.on(document.body, "mouseleave", () => { this.processMouseLeave(); }), this.listeners.on(window, "scroll", dt((t) => { this.processScroll(t); }, 10), { passive: !0 }), this.listeners.on(document.body, "mouseup", () => { this.processMouseUp(); }, !1); } /** * Handle mouse down events * * @param {MouseEvent} mouseEvent - mouse event payload */ processMouseDown(e) { if (e.button !== this.MAIN_MOUSE_BUTTON) return; e.target.closest(d.allInputsSelector) !== null || this.startSelection(e.pageX, e.pageY); } /** * Handle mouse move events * * @param {MouseEvent} mouseEvent - mouse event payload */ processMouseMove(e) { this.changingRectangle(e), this.scrollByZones(e.clientY); } /** * Handle mouse leave */ processMouseLeave() { this.clearSelection(), this.endSelection(); } /** * @param {MouseEvent} mouseEvent - mouse event payload */ processScroll(e) { this.changingRectangle(e); } /** * Handle mouse up */ processMouseUp() { this.clearSelection(), this.endSelection(); } /** * Scroll If mouse in scroll zone * * @param {number} clientY - Y coord of mouse */ scrollByZones(e) { if (this.inScrollZone = null, e <= this.HEIGHT_OF_SCROLL_ZONE && (this.inScrollZone = this.TOP_SCROLL_ZONE), document.documentElement.clientHeight - e <= this.HEIGHT_OF_SCROLL_ZONE && (this.inScrollZone = this.BOTTOM_SCROLL_ZONE), !this.inScrollZone) { this.isScrolling = !1; return; } this.isScrolling || (this.scrollVertical(this.inScrollZone === this.TOP_SCROLL_ZONE ? -this.SCROLL_SPEED : this.SCROLL_SPEED), this.isScrolling = !0); } /** * Generates required HTML elements * * @returns {Object} */ genHTML() { const { UI: e } = this.Editor, t = e.nodes.holder.querySelector("." + e.CSS.editorWrapper), o = d.make("div", Be.CSS.overlay, {}), i = d.make("div", Be.CSS.overlayContainer, {}), s = d.make("div", Be.CSS.rect, {}); return i.appendChild(s), o.appendChild(i), t.appendChild(o), this.overlayRectangle = s, { container: t, overlay: o }; } /** * Activates scrolling if blockSelection is active and mouse is in scroll zone * * @param {number} speed - speed of scrolling */ scrollVertical(e) { if (!(this.inScrollZone && this.mousedown)) return; const t = window.pageYOffset; window.scrollBy(0, e), this.mouseY += window.pageYOffset - t, setTimeout(() => { this.scrollVertical(e); }, 0); } /** * Handles the change in the rectangle and its effect * * @param {MouseEvent} event - mouse event */ changingRectangle(e) { if (!this.mousedown) return; e.pageY !== void 0 && (this.mouseX = e.pageX, this.mouseY = e.pageY); const { rightPos: t, leftPos: o, index: i } = this.genInfoForMouseSelection(), s = this.startX > t && this.mouseX > t, r = this.startX < o && this.mouseX < o; this.rectCrossesBlocks = !(s || r), this.isRectSelectionActivated || (this.rectCrossesBlocks = !1, this.isRectSelectionActivated = !0, this.shrinkRectangleToPoint(), this.overlayRectangle.style.display = "block"), this.updateRectangleSize(), this.Editor.Toolbar.close(), i !== void 0 && (this.trySelectNextBlock(i), this.inverseSelection(), b.get().removeAllRanges()); } /** * Shrink rect to singular point */ shrinkRectangleToPoint() { this.overlayRectangle.style.left = `${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.top = `${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.bottom = `calc(100% - ${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.right = `calc(100% - ${this.startX - window.pageXOffset}px`; } /** * Select or unselect all of blocks in array if rect is out or in selectable area */ inverseSelection() { const t = this.Editor.BlockManager.getBlockByIndex(this.stackOfSelected[0]).selected; if (this.rectCrossesBlocks && !t) for (const o of this.stackOfSelected) this.Editor.BlockSelection.selectBlockByIndex(o); if (!this.rectCrossesBlocks && t) for (const o of this.stackOfSelected) this.Editor.BlockSelection.unSelectBlockByIndex(o); } /** * Updates size of rectangle */ updateRectangleSize() { this.mouseY >= this.startY ? (this.overlayRectangle.style.top = `${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.bottom = `calc(100% - ${this.mouseY - window.pageYOffset}px`) : (this.overlayRectangle.style.bottom = `calc(100% - ${this.startY - window.pageYOffset}px`, this.overlayRectangle.style.top = `${this.mouseY - window.pageYOffset}px`), this.mouseX >= this.startX ? (this.overlayRectangle.style.left = `${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.right = `calc(100% - ${this.mouseX - window.pageXOffset}px`) : (this.overlayRectangle.style.right = `calc(100% - ${this.startX - window.pageXOffset}px`, this.overlayRectangle.style.left = `${this.mouseX - window.pageXOffset}px`); } /** * Collects information needed to determine the behavior of the rectangle * * @returns {object} index - index next Block, leftPos - start of left border of Block, rightPos - right border */ genInfoForMouseSelection() { const t = document.body.offsetWidth / 2, o = this.mouseY - window.pageYOffset, i = document.elementFromPoint(t, o), s = this.Editor.BlockManager.getBlockByChildNode(i); let r; s !== void 0 && (r = this.Editor.BlockManager.blocks.findIndex((h) => h.holder === s.holder)); const a = this.Editor.BlockManager.lastBlock.holder.querySelector("." + R.CSS.content), l = Number.parseInt(window.getComputedStyle(a).width, 10) / 2, c = t - l, u = t + l; return { index: r, leftPos: c, rightPos: u }; } /** * Select block with index index * * @param index - index of block in redactor */ addBlockInSelection(e) { this.rectCrossesBlocks && this.Editor.BlockSelection.selectBlockByIndex(e), this.stackOfSelected.push(e); } /** * Adds a block to the selection and determines which blocks should be selected * * @param {object} index - index of new block in the reactor */ trySelectNextBlock(e) { const t = this.stackOfSelected[this.stackOfSelected.length - 1] === e, o = this.stackOfSelected.length, i = 1, s = -1, r = 0; if (t) return; const a = this.stackOfSelected[o - 1] - this.stackOfSelected[o - 2] > 0; let l = r; o > 1 && (l = a ? i : s); const c = e > this.stackOfSelected[o - 1] && l === i, u = e < this.stackOfSelected[o - 1] && l === s, p = !(c || u || l === r); if (!p && (e > this.stackOfSelected[o - 1] || this.stackOfSelected[o - 1] === void 0)) { let v = this.stackOfSelected[o - 1] + 1 || e; for (v; v <= e; v++) this.addBlockInSelection(v); return; } if (!p && e < this.stackOfSelected[o - 1]) { for (let v = this.stackOfSelected[o - 1] - 1; v >= e; v--) this.addBlockInSelection(v); return; } if (!p) return; let g = o - 1, f; for (e > this.stackOfSelected[o - 1] ? f = () => e > this.stackOfSelected[g] : f = () => e < this.stackOfSelected[g]; f(); ) this.rectCrossesBlocks && this.Editor.BlockSelection.unSelectBlockByIndex(this.stackOfSelected[g]), this.stackOfSelected.pop(), g--; } } class ga extends E { /** * Renders passed blocks as one batch * * @param blocksData - blocks to render */ async render(e) { return new Promise((t) => { const { Tools: o, BlockManager: i } = this.Editor; if (e.length === 0) i.insert(); else { const s = e.map(({ type: r, data: a, tunes: l, id: c }) => { o.available.has(r) === !1 && (X(`Tool «${r}» is not found. Check 'tools' property at the Editor.js config.`, "warn"), a = this.composeStubDataForTool(r, a, c), r = o.stubTool); let u; try { u = i.composeBlock({ id: c, tool: r, data: a, tunes: l }); } catch (h) { S(`Block «${r}» skipped because of plugins error`, "error", { data: a, error: h }), a = this.composeStubDataForTool(r, a, c), r = o.stubTool, u = i.composeBlock({ id: c, tool: r, data: a, tunes: l }); } return u; }); i.insertMany(s); } window.requestIdleCallback(() => { t(); }, { timeout: 2e3 }); }); } /** * Create data for the Stub Tool that will be used instead of unavailable tool * * @param tool - unavailable tool name to stub * @param data - data of unavailable block * @param [id] - id of unavailable block */ composeStubDataForTool(e, t, o) { const { Tools: i } = this.Editor; let s = e; if (i.unavailable.has(e)) { const r = i.unavailable.get(e).toolbox; r !== void 0 && r[0].title !== void 0 && (s = r[0].title); } return { savedData: { id: o, type: e, data: t }, title: s }; } } class ma extends E { /** * Composes new chain of Promises to fire them alternatelly * * @returns {OutputData} */ async save() { const { BlockManager: e, Tools: t } = this.Editor, o = e.blocks, i = []; try { o.forEach((a) => { i.push(this.getSavedData(a)); }); const s = await Promise.all(i), r = await yt(s, (a) => t.blockTools.get(a).sanitizeConfig); return this.makeOutput(r); } catch (s) { X("Saving failed due to the Error %o", "error", s); } } /** * Saves and validates * * @param {Block} block - Editor's Tool * @returns {ValidatedData} - Tool's validated data */ async getSavedData(e) { const t = await e.save(), o = t && await e.validate(t.data); return { ...t, isValid: o }; } /** * Creates output object with saved data, time and version of editor * * @param {ValidatedData} allExtractedData - data extracted from Blocks * @returns {OutputData} */ makeOutput(e) { const t = []; return e.forEach(({ id: o, tool: i, data: s, tunes: r, isValid: a }) => { if (!a) { S(`Block «${i}» skipped because saved data is invalid`); return; } if (i === this.Editor.Tools.stubTool) { t.push(s); return; } const l = { id: o, type: i, data: s, ...!V(r) && { tunes: r } }; t.push(l); }), { time: +/* @__PURE__ */ new Date(), blocks: t, version: "2.31.0-rc.8" }; } } (function() { try { if (typeof document < "u") { var n = document.createElement("style"); n.appendChild(document.createTextNode(".ce-paragraph{line-height:1.6em;outline:none}.ce-block:only-of-type .ce-paragraph[data-placeholder-active]:empty:before,.ce-block:only-of-type .ce-paragraph[data-placeholder-active][data-empty=true]:before{content:attr(data-placeholder-active)}.ce-paragraph p:first-of-type{margin-top:0}.ce-paragraph p:last-of-type{margin-bottom:0}")), document.head.appendChild(n); } } catch (e) { console.error("vite-plugin-css-injected-by-js", e); } })(); const ba = ''; function va(n) { const e = document.createElement("div"); e.innerHTML = n.trim(); const t = document.createDocumentFragment(); return t.append(...Array.from(e.childNodes)), t; } /** * Base Paragraph Block for the Editor.js. * Represents a regular text block * * @author CodeX (team@codex.so) * @copyright CodeX 2018 * @license The MIT License (MIT) */ class fo { /** * Default placeholder for Paragraph Tool * * @returns {string} * @class */ static get DEFAULT_PLACEHOLDER() { return ""; } /** * Render plugin`s main Element and fill it with saved data * * @param {object} params - constructor params * @param {ParagraphData} params.data - previously saved data * @param {ParagraphConfig} params.config - user config for Tool * @param {object} params.api - editor.js api * @param {boolean} readOnly - read only mode flag */ constructor({ data: e, config: t, api: o, readOnly: i }) { this.api = o, this.readOnly = i, this._CSS = { block: this.api.styles.block, wrapper: "ce-paragraph" }, this.readOnly || (this.onKeyUp = this.onKeyUp.bind(this)), this._placeholder = t.placeholder ? t.placeholder : fo.DEFAULT_PLACEHOLDER, this._data = e ?? {}, this._element = null, this._preserveBlank = t.preserveBlank ?? !1; } /** * Check if text content is empty and set empty string to inner html. * We need this because some browsers (e.g. Safari) insert
into empty contenteditanle elements * * @param {KeyboardEvent} e - key up event */ onKeyUp(e) { if (e.code !== "Backspace" && e.code !== "Delete" || !this._element) return; const { textContent: t } = this._element; t === "" && (this._element.innerHTML = ""); } /** * Create Tool's view * * @returns {HTMLDivElement} * @private */ drawView() { const e = document.createElement("DIV"); return e.classList.add(this._CSS.wrapper, this._CSS.block), e.contentEditable = "false", e.dataset.placeholderActive = this.api.i18n.t(this._placeholder), this._data.text && (e.innerHTML = this._data.text), this.readOnly || (e.contentEditable = "true", e.addEventListener("keyup", this.onKeyUp)), e; } /** * Return Tool's view * * @returns {HTMLDivElement} */ render() { return this._element = this.drawView(), this._element; } /** * Method that specified how to merge two Text blocks. * Called by Editor.js by backspace at the beginning of the Block * * @param {ParagraphData} data * @public */ merge(e) { if (!this._element) return; this._data.text += e.text; const t = va(e.text); this._element.appendChild(t), this._element.normalize(); } /** * Validate Paragraph block data: * - check for emptiness * * @param {ParagraphData} savedData — data received after saving * @returns {boolean} false if saved data is not correct, otherwise true * @public */ validate(e) { return !(e.text.trim() === "" && !this._preserveBlank); } /** * Extract Tool's data from the view * * @param {HTMLDivElement} toolsContent - Paragraph tools rendered view * @returns {ParagraphData} - saved data * @public */ save(e) { return { text: e.innerHTML }; } /** * On paste callback fired from Editor. * * @param {HTMLPasteEvent} event - event with pasted data */ onPaste(e) { const t = { text: e.detail.data.innerHTML }; this._data = t, window.requestAnimationFrame(() => { this._element && (this._element.innerHTML = this._data.text || ""); }); } /** * Enable Conversion Toolbar. Paragraph can be converted to/from other tools * @returns {ConversionConfig} */ static get conversionConfig() { return { export: "text", // to convert Paragraph to other block, use 'text' property of saved data import: "text" // to covert other block's exported string to Paragraph, fill 'text' property of tool data }; } /** * Sanitizer rules * @returns {SanitizerConfig} - Edtior.js sanitizer config */ static get sanitize() { return { text: { br: !0 } }; } /** * Returns true to notify the core that read-only mode is supported * * @returns {boolean} */ static get isReadOnlySupported() { return !0; } /** * Used by Editor paste handling API. * Provides configuration to handle P tags. * * @returns {PasteConfig} - Paragraph Paste Setting */ static get pasteConfig() { return { tags: ["P"] }; } /** * Icon and title for displaying at the Toolbox * * @returns {ToolboxConfig} - Paragraph Toolbox Setting */ static get toolbox() { return { icon: ba, title: "Text" }; } } class go { constructor() { this.commandName = "bold"; } /** * Sanitizer Rule * Leave tags * * @returns {object} */ static get sanitize() { return { b: {} }; } /** * Create button for Inline Toolbar */ render() { return { icon: Ki, name: "bold", onActivate: () => { document.execCommand(this.commandName); }, isActive: () => document.queryCommandState(this.commandName) }; } /** * Set a shortcut * * @returns {boolean} */ get shortcut() { return "CMD+B"; } } go.isInline = !0; go.title = "Bold"; class mo { constructor() { this.commandName = "italic", this.CSS = { button: "ce-inline-tool", buttonActive: "ce-inline-tool--active", buttonModifier: "ce-inline-tool--italic" }, this.nodes = { button: null }; } /** * Sanitizer Rule * Leave tags * * @returns {object} */ static get sanitize() { return { i: {} }; } /** * Create button for Inline Toolbar */ render() { return this.nodes.button = document.createElement("button"), this.nodes.button.type = "button", this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier), this.nodes.button.innerHTML = Ji, this.nodes.button; } /** * Wrap range with tag */ surround() { document.execCommand(this.commandName); } /** * Check selection and set activated state to button if there are tag */ checkState() { const e = document.queryCommandState(this.commandName); return this.nodes.button.classList.toggle(this.CSS.buttonActive, e), e; } /** * Set a shortcut */ get shortcut() { return "CMD+I"; } } mo.isInline = !0; mo.title = "Italic"; class bo { /** * @param api - Editor.js API */ constructor({ api: e }) { this.commandLink = "createLink", this.commandUnlink = "unlink", this.ENTER_KEY = 13, this.CSS = { button: "ce-inline-tool", buttonActive: "ce-inline-tool--active", buttonModifier: "ce-inline-tool--link", buttonUnlink: "ce-inline-tool--unlink", input: "ce-inline-tool-input", inputShowed: "ce-inline-tool-input--showed" }, this.nodes = { button: null, input: null }, this.inputOpened = !1, this.toolbar = e.toolbar, this.inlineToolbar = e.inlineToolbar, this.notifier = e.notifier, this.i18n = e.i18n, this.selection = new b(); } /** * Sanitizer Rule * Leave tags * * @returns {object} */ static get sanitize() { return { a: { href: !0, target: "_blank", rel: "nofollow" } }; } /** * Create button for Inline Toolbar */ render() { return this.nodes.button = document.createElement("button"), this.nodes.button.type = "button", this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier), this.nodes.button.innerHTML = Co, this.nodes.button; } /** * Input for the link */ renderActions() { return this.nodes.input = document.createElement("input"), this.nodes.input.placeholder = this.i18n.t("Add a link"), this.nodes.input.enterKeyHint = "done", this.nodes.input.classList.add(this.CSS.input), this.nodes.input.addEventListener("keydown", (e) => { e.keyCode === this.ENTER_KEY && this.enterPressed(e); }), this.nodes.input; } /** * Handle clicks on the Inline Toolbar icon * * @param {Range} range - range to wrap with link */ surround(e) { if (e) { this.inputOpened ? (this.selection.restore(), this.selection.removeFakeBackground()) : (this.selection.setFakeBackground(), this.selection.save()); const t = this.selection.findParentTag("A"); if (t) { this.selection.expandToTag(t), this.unlink(), this.closeActions(), this.checkState(), this.toolbar.close(); return; } } this.toggleActions(); } /** * Check selection and set activated state to button if there are tag */ checkState() { const e = this.selection.findParentTag("A"); if (e) { this.nodes.button.innerHTML = ns, this.nodes.button.classList.add(this.CSS.buttonUnlink), this.nodes.button.classList.add(this.CSS.buttonActive), this.openActions(); const t = e.getAttribute("href"); this.nodes.input.value = t !== "null" ? t : "", this.selection.save(); } else this.nodes.button.innerHTML = Co, this.nodes.button.classList.remove(this.CSS.buttonUnlink), this.nodes.button.classList.remove(this.CSS.buttonActive); return !!e; } /** * Function called with Inline Toolbar closing */ clear() { this.closeActions(); } /** * Set a shortcut */ get shortcut() { return "CMD+K"; } /** * Show/close link input */ toggleActions() { this.inputOpened ? this.closeActions(!1) : this.openActions(!0); } /** * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope. */ openActions(e = !1) { this.nodes.input.classList.add(this.CSS.inputShowed), e && this.nodes.input.focus(), this.inputOpened = !0; } /** * Close input * * @param {boolean} clearSavedSelection — we don't need to clear saved selection * on toggle-clicks on the icon of opened Toolbar */ closeActions(e = !0) { if (this.selection.isFakeBackgroundEnabled) { const t = new b(); t.save(), this.selection.restore(), this.selection.removeFakeBackground(), t.restore(); } this.nodes.input.classList.remove(this.CSS.inputShowed), this.nodes.input.value = "", e && this.selection.clearSaved(), this.inputOpened = !1; } /** * Enter pressed on input * * @param {KeyboardEvent} event - enter keydown event */ enterPressed(e) { let t = this.nodes.input.value || ""; if (!t.trim()) { this.selection.restore(), this.unlink(), e.preventDefault(), this.closeActions(); return; } if (!this.validateURL(t)) { this.notifier.show({ message: "Pasted link is not valid.", style: "error" }), S("Incorrect Link pasted", "warn", t); return; } t = this.prepareLink(t), this.selection.restore(), this.selection.removeFakeBackground(), this.insertLink(t), e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), this.selection.collapseToEnd(), this.inlineToolbar.close(); } /** * Detects if passed string is URL * * @param {string} str - string to validate * @returns {boolean} */ validateURL(e) { return !/\s/.test(e); } /** * Process link before injection * - sanitize * - add protocol for links like 'google.com' * * @param {string} link - raw user input */ prepareLink(e) { return e = e.trim(), e = this.addProtocol(e), e; } /** * Add 'http' protocol to the links like 'vc.ru', 'google.com' * * @param {string} link - string to process */ addProtocol(e) { if (/^(\w+):(\/\/)?/.test(e)) return e; const t = /^\/[^/\s]/.test(e), o = e.substring(0, 1) === "#", i = /^\/\/[^/\s]/.test(e); return !t && !o && !i && (e = "http://" + e), e; } /** * Inserts tag with "href" * * @param {string} link - "href" value */ insertLink(e) { const t = this.selection.findParentTag("A"); t && this.selection.expandToTag(t), document.execCommand(this.commandLink, !1, e); } /** * Removes tag */ unlink() { document.execCommand(this.commandUnlink); } } bo.isInline = !0; bo.title = "Link"; class Fn { /** * @param api - Editor.js API */ constructor({ api: e }) { this.i18nAPI = e.i18n, this.blocksAPI = e.blocks, this.selectionAPI = e.selection, this.toolsAPI = e.tools, this.caretAPI = e.caret; } /** * Returns tool's UI config */ async render() { const e = b.get(), t = this.blocksAPI.getBlockByElement(e.anchorNode); if (t === void 0) return []; const o = this.toolsAPI.getBlockTools(), i = await Yo(t, o); if (i.length === 0) return []; const s = i.reduce((c, u) => { var h; return (h = u.toolbox) == null || h.forEach((p) => { c.push({ icon: p.icon, title: z.t(K.toolNames, p.title), name: u.name, closeOnActivate: !0, onActivate: async () => { const g = await this.blocksAPI.convert(t.id, u.name, p.data); this.caretAPI.setToBlock(g, "end"); } }); }), c; }, []), r = await t.getActiveToolboxEntry(), a = r !== void 0 ? r.icon : Go, l = !be(); return { icon: a, name: "convert-to", hint: { title: this.i18nAPI.t("Convert to") }, children: { searchable: l, items: s, onOpen: () => { l && (this.selectionAPI.setFakeBackground(), this.selectionAPI.save()); }, onClose: () => { l && (this.selectionAPI.restore(), this.selectionAPI.removeFakeBackground()); } } }; } } Fn.isInline = !0; class jn { /** * @param options - constructor options * @param options.data - stub tool data * @param options.api - Editor.js API */ constructor({ data: e, api: t }) { this.CSS = { wrapper: "ce-stub", info: "ce-stub__info", title: "ce-stub__title", subtitle: "ce-stub__subtitle" }, this.api = t, this.title = e.title || this.api.i18n.t("Error"), this.subtitle = this.api.i18n.t("The block can not be displayed correctly."), this.savedData = e.savedData, this.wrapper = this.make(); } /** * Returns stub holder * * @returns {HTMLElement} */ render() { return this.wrapper; } /** * Return original Tool data * * @returns {BlockToolData} */ save() { return this.savedData; } /** * Create Tool html markup * * @returns {HTMLElement} */ make() { const e = d.make("div", this.CSS.wrapper), t = is, o = d.make("div", this.CSS.info), i = d.make("div", this.CSS.title, { textContent: this.title }), s = d.make("div", this.CSS.subtitle, { textContent: this.subtitle }); return e.innerHTML = t, o.appendChild(i), o.appendChild(s), e.appendChild(o), e; } } jn.isReadOnlySupported = !0; class ka extends Tt { constructor() { super(...arguments), this.type = ae.Inline; } /** * Returns title for Inline Tool if specified by user */ get title() { return this.constructable[We.Title]; } /** * Constructs new InlineTool instance from constructable */ create() { return new this.constructable({ api: this.api, config: this.settings }); } /** * Allows inline tool to be available in read-only mode * Can be used, for example, by comments tool */ get isReadOnlySupported() { return this.constructable[We.IsReadOnlySupported] ?? !1; } } class ya extends Tt { constructor() { super(...arguments), this.type = ae.Tune; } /** * Constructs new BlockTune instance from constructable * * @param data - Tune data * @param block - Block API object */ create(e, t) { return new this.constructable({ api: this.api, config: this.settings, block: t, data: e }); } } class j extends Map { /** * Returns Block Tools collection */ get blockTools() { const e = Array.from(this.entries()).filter(([, t]) => t.isBlock()); return new j(e); } /** * Returns Inline Tools collection */ get inlineTools() { const e = Array.from(this.entries()).filter(([, t]) => t.isInline()); return new j(e); } /** * Returns Block Tunes collection */ get blockTunes() { const e = Array.from(this.entries()).filter(([, t]) => t.isTune()); return new j(e); } /** * Returns internal Tools collection */ get internalTools() { const e = Array.from(this.entries()).filter(([, t]) => t.isInternal); return new j(e); } /** * Returns Tools collection provided by user */ get externalTools() { const e = Array.from(this.entries()).filter(([, t]) => !t.isInternal); return new j(e); } } var wa = Object.defineProperty, Ea = Object.getOwnPropertyDescriptor, Hn = (n, e, t, o) => { for (var i = o > 1 ? void 0 : o ? Ea(e, t) : e, s = n.length - 1, r; s >= 0; s--) (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); return o && i && wa(e, t, i), i; }; class vo extends Tt { constructor() { super(...arguments), this.type = ae.Block, this.inlineTools = new j(), this.tunes = new j(); } /** * Creates new Tool instance * * @param data - Tool data * @param block - BlockAPI for current Block * @param readOnly - True if Editor is in read-only mode */ create(e, t, o) { return new this.constructable({ data: e, block: t, readOnly: o, api: this.api, config: this.settings }); } /** * Returns true if read-only mode is supported by Tool */ get isReadOnlySupported() { return this.constructable[pe.IsReadOnlySupported] === !0; } /** * Returns true if Tool supports linebreaks */ get isLineBreaksEnabled() { return this.constructable[pe.IsEnabledLineBreaks]; } /** * Returns Tool toolbox configuration (internal or user-specified). * * Merges internal and user-defined toolbox configs based on the following rules: * * - If both internal and user-defined toolbox configs are arrays their items are merged. * Length of the second one is kept. * * - If both are objects their properties are merged. * * - If one is an object and another is an array than internal config is replaced with user-defined * config. This is made to allow user to override default tool's toolbox representation (single/multiple entries) */ get toolbox() { const e = this.constructable[pe.Toolbox], t = this.config[Pe.Toolbox]; if (!V(e) && t !== !1) return t ? Array.isArray(e) ? Array.isArray(t) ? t.map((o, i) => { const s = e[i]; return s ? { ...s, ...o } : o; }) : [t] : Array.isArray(t) ? t : [ { ...e, ...t } ] : Array.isArray(e) ? e : [e]; } /** * Returns Tool conversion configuration */ get conversionConfig() { return this.constructable[pe.ConversionConfig]; } /** * Returns enabled inline tools for Tool */ get enabledInlineTools() { return this.config[Pe.EnabledInlineTools] || !1; } /** * Returns enabled tunes for Tool */ get enabledBlockTunes() { return this.config[Pe.EnabledBlockTunes]; } /** * Returns Tool paste configuration */ get pasteConfig() { return this.constructable[pe.PasteConfig] ?? {}; } get sanitizeConfig() { const e = super.sanitizeConfig, t = this.baseSanitizeConfig; if (V(e)) return t; const o = {}; for (const i in e) if (Object.prototype.hasOwnProperty.call(e, i)) { const s = e[i]; D(s) ? o[i] = Object.assign({}, t, s) : o[i] = s; } return o; } get baseSanitizeConfig() { const e = {}; return Array.from(this.inlineTools.values()).forEach((t) => Object.assign(e, t.sanitizeConfig)), Array.from(this.tunes.values()).forEach((t) => Object.assign(e, t.sanitizeConfig)), e; } } Hn([ me ], vo.prototype, "sanitizeConfig", 1); Hn([ me ], vo.prototype, "baseSanitizeConfig", 1); class xa { /** * @class * @param config - tools config * @param editorConfig - EditorJS config * @param api - EditorJS API module */ constructor(e, t, o) { this.api = o, this.config = e, this.editorConfig = t; } /** * Returns Tool object based on it's type * * @param name - tool name */ get(e) { const { class: t, isInternal: o = !1, ...i } = this.config[e], s = this.getConstructor(t), r = t[mt.IsTune]; return new s({ name: e, constructable: t, config: i, api: this.api.getMethodsForTool(e, r), isDefault: e === this.editorConfig.defaultBlock, defaultPlaceholder: this.editorConfig.placeholder, isInternal: o }); } /** * Find appropriate Tool object constructor for Tool constructable * * @param constructable - Tools constructable */ getConstructor(e) { switch (!0) { case e[We.IsInline]: return ka; case e[mt.IsTune]: return ya; default: return vo; } } } class $n { /** * MoveDownTune constructor * * @param {API} api — Editor's API */ constructor({ api: e }) { this.CSS = { animation: "wobble" }, this.api = e; } /** * Tune's appearance in block settings menu */ render() { return { icon: Xi, title: this.api.i18n.t("Move down"), onActivate: () => this.handleClick(), name: "move-down" }; } /** * Handle clicks on 'move down' button */ handleClick() { const e = this.api.blocks.getCurrentBlockIndex(), t = this.api.blocks.getBlockByIndex(e + 1); if (!t) throw new Error("Unable to move Block down since it is already the last"); const o = t.holder, i = o.getBoundingClientRect(); let s = Math.abs(window.innerHeight - o.offsetHeight); i.top < window.innerHeight && (s = window.scrollY + o.offsetHeight), window.scrollTo(0, s), this.api.blocks.move(e + 1), this.api.toolbar.toggleBlockSettings(!0); } } $n.isTune = !0; class zn { /** * DeleteTune constructor * * @param {API} api - Editor's API */ constructor({ api: e }) { this.api = e; } /** * Tune's appearance in block settings menu */ render() { return { icon: Gi, title: this.api.i18n.t("Delete"), name: "delete", confirmation: { title: this.api.i18n.t("Click to delete"), onActivate: () => this.handleClick() } }; } /** * Delete block conditions passed */ handleClick() { this.api.blocks.delete(); } } zn.isTune = !0; class Un { /** * MoveUpTune constructor * * @param {API} api - Editor's API */ constructor({ api: e }) { this.CSS = { animation: "wobble" }, this.api = e; } /** * Tune's appearance in block settings menu */ render() { return { icon: Zi, title: this.api.i18n.t("Move up"), onActivate: () => this.handleClick(), name: "move-up" }; } /** * Move current block up */ handleClick() { const e = this.api.blocks.getCurrentBlockIndex(), t = this.api.blocks.getBlockByIndex(e), o = this.api.blocks.getBlockByIndex(e - 1); if (e === 0 || !t || !o) throw new Error("Unable to move Block up since it is already the first"); const i = t.holder, s = o.holder, r = i.getBoundingClientRect(), a = s.getBoundingClientRect(); let l; a.top > 0 ? l = Math.abs(r.top) - Math.abs(a.top) : l = Math.abs(r.top) + a.height, window.scrollBy(0, -1 * l), this.api.blocks.move(e - 1), this.api.toolbar.toggleBlockSettings(!0); } } Un.isTune = !0; var Ba = Object.defineProperty, Ca = Object.getOwnPropertyDescriptor, Ta = (n, e, t, o) => { for (var i = o > 1 ? void 0 : o ? Ca(e, t) : e, s = n.length - 1, r; s >= 0; s--) (r = n[s]) && (i = (o ? r(e, t, i) : r(i)) || i); return o && i && Ba(e, t, i), i; }; class Wn extends E { constructor() { super(...arguments), this.stubTool = "stub", this.toolsAvailable = new j(), this.toolsUnavailable = new j(); } /** * Returns available Tools */ get available() { return this.toolsAvailable; } /** * Returns unavailable Tools */ get unavailable() { return this.toolsUnavailable; } /** * Return Tools for the Inline Toolbar */ get inlineTools() { return this.available.inlineTools; } /** * Return editor block tools */ get blockTools() { return this.available.blockTools; } /** * Return available Block Tunes * * @returns {object} - object of Inline Tool's classes */ get blockTunes() { return this.available.blockTunes; } /** * Returns default Tool object */ get defaultTool() { return this.blockTools.get(this.config.defaultBlock); } /** * Returns internal tools */ get internal() { return this.available.internalTools; } /** * Creates instances via passed or default configuration * * @returns {Promise} */ async prepare() { if (this.validateTools(), this.config.tools = ut({}, this.internalTools, this.config.tools), !Object.prototype.hasOwnProperty.call(this.config, "tools") || Object.keys(this.config.tools).length === 0) throw Error("Can't start without tools"); const e = this.prepareConfig(); this.factory = new xa(e, this.config, this.Editor.API); const t = this.getListOfPrepareFunctions(e); if (t.length === 0) return Promise.resolve(); await Qn(t, (o) => { this.toolPrepareMethodSuccess(o); }, (o) => { this.toolPrepareMethodFallback(o); }), this.prepareBlockTools(); } getAllInlineToolsSanitizeConfig() { const e = {}; return Array.from(this.inlineTools.values()).forEach((t) => { Object.assign(e, t.sanitizeConfig); }), e; } /** * Calls each Tool reset method to clean up anything set by Tool */ destroy() { Object.values(this.available).forEach(async (e) => { A(e.reset) && await e.reset(); }); } /** * Returns internal tools * Includes Bold, Italic, Link and Paragraph */ get internalTools() { return { convertTo: { class: Fn, isInternal: !0 }, link: { class: bo, isInternal: !0 }, bold: { class: go, isInternal: !0 }, italic: { class: mo, isInternal: !0 }, paragraph: { class: fo, inlineToolbar: !0, isInternal: !0 }, stub: { class: jn, isInternal: !0 }, moveUp: { class: Un, isInternal: !0 }, delete: { class: zn, isInternal: !0 }, moveDown: { class: $n, isInternal: !0 } }; } /** * Tool prepare method success callback * * @param {object} data - append tool to available list */ toolPrepareMethodSuccess(e) { const t = this.factory.get(e.toolName); if (t.isInline()) { const i = ["render"].filter((s) => !t.create()[s]); if (i.length) { S( `Incorrect Inline Tool: ${t.name}. Some of required methods is not implemented %o`, "warn", i ), this.toolsUnavailable.set(t.name, t); return; } } this.toolsAvailable.set(t.name, t); } /** * Tool prepare method fail callback * * @param {object} data - append tool to unavailable list */ toolPrepareMethodFallback(e) { this.toolsUnavailable.set(e.toolName, this.factory.get(e.toolName)); } /** * Binds prepare function of plugins with user or default config * * @returns {Array} list of functions that needs to be fired sequentially * @param config - tools config */ getListOfPrepareFunctions(e) { const t = []; return Object.entries(e).forEach(([o, i]) => { t.push({ // eslint-disable-next-line @typescript-eslint/no-empty-function function: A(i.class.prepare) ? i.class.prepare : () => { }, data: { toolName: o, config: i.config } }); }), t; } /** * Assign enabled Inline Tools and Block Tunes for Block Tool */ prepareBlockTools() { Array.from(this.blockTools.values()).forEach((e) => { this.assignInlineToolsToBlockTool(e), this.assignBlockTunesToBlockTool(e); }); } /** * Assign enabled Inline Tools for Block Tool * * @param tool - Block Tool */ assignInlineToolsToBlockTool(e) { if (this.config.inlineToolbar !== !1) { if (e.enabledInlineTools === !0) { e.inlineTools = new j( Array.isArray(this.config.inlineToolbar) ? this.config.inlineToolbar.map((t) => [t, this.inlineTools.get(t)]) : Array.from(this.inlineTools.entries()) ); return; } Array.isArray(e.enabledInlineTools) && (e.inlineTools = new j( /** Prepend ConvertTo Inline Tool */ ["convertTo", ...e.enabledInlineTools].map((t) => [t, this.inlineTools.get(t)]) )); } } /** * Assign enabled Block Tunes for Block Tool * * @param tool — Block Tool */ assignBlockTunesToBlockTool(e) { if (e.enabledBlockTunes !== !1) { if (Array.isArray(e.enabledBlockTunes)) { const t = new j( e.enabledBlockTunes.map((o) => [o, this.blockTunes.get(o)]) ); e.tunes = new j([...t, ...this.blockTunes.internalTools]); return; } if (Array.isArray(this.config.tunes)) { const t = new j( this.config.tunes.map((o) => [o, this.blockTunes.get(o)]) ); e.tunes = new j([...t, ...this.blockTunes.internalTools]); return; } e.tunes = this.blockTunes.internalTools; } } /** * Validate Tools configuration objects and throw Error for user if it is invalid */ validateTools() { for (const e in this.config.tools) if (Object.prototype.hasOwnProperty.call(this.config.tools, e)) { if (e in this.internalTools) return; const t = this.config.tools[e]; if (!A(t) && !A(t.class)) throw Error( `Tool «${e}» must be a constructor function or an object with function in the «class» property` ); } } /** * Unify tools config */ prepareConfig() { const e = {}; for (const t in this.config.tools) D(this.config.tools[t]) ? e[t] = this.config.tools[t] : e[t] = { class: this.config.tools[t] }; return e; } } Ta([ me ], Wn.prototype, "getAllInlineToolsSanitizeConfig", 1); const Sa = `:root{--selectionColor: #e1f2ff;--inlineSelectionColor: #d4ecff;--bg-light: #eff2f5;--grayText: #707684;--color-dark: #1D202B;--color-active-icon: #388AE5;--color-gray-border: rgba(201, 201, 204, .48);--content-width: 650px;--narrow-mode-right-padding: 50px;--toolbox-buttons-size: 26px;--toolbox-buttons-size--mobile: 36px;--icon-size: 20px;--icon-size--mobile: 28px;--block-padding-vertical: .4em;--color-line-gray: #EFF0F1 }.codex-editor{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:1}.codex-editor .hide{display:none}.codex-editor__redactor [contenteditable]:empty:after{content:"\\feff"}@media (min-width: 651px){.codex-editor--narrow .codex-editor__redactor{margin-right:50px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .codex-editor__redactor{margin-left:50px;margin-right:0}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__actions{right:-5px}}.codex-editor-copyable{position:absolute;height:1px;width:1px;top:-400%;opacity:.001}.codex-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:999;pointer-events:none;overflow:hidden}.codex-editor-overlay__container{position:relative;pointer-events:auto;z-index:0}.codex-editor-overlay__rectangle{position:absolute;pointer-events:none;background-color:#2eaadc33;border:1px solid transparent}.codex-editor svg{max-height:100%}.codex-editor path{stroke:currentColor}.codex-editor ::-moz-selection{background-color:#d4ecff}.codex-editor ::selection{background-color:#d4ecff}.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus:before{opacity:0!important}.ce-scroll-locked{overflow:hidden}.ce-scroll-locked--hard{overflow:hidden;top:calc(-1 * var(--window-scroll-offset));position:fixed;width:100%}.ce-toolbar{position:absolute;left:0;right:0;top:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;will-change:opacity,top;display:none}.ce-toolbar--opened{display:block}.ce-toolbar__content{max-width:650px;margin:0 auto;position:relative}.ce-toolbar__plus{color:#1d202b;cursor:pointer;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-ms-flex-negative:0;flex-shrink:0}@media (max-width: 650px){.ce-toolbar__plus{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__plus:hover{background-color:#eff2f5}}.ce-toolbar__plus--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-toolbar__plus-shortcut{opacity:.6;word-spacing:-2px;margin-top:5px}@media (max-width: 650px){.ce-toolbar__plus{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__plus--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__plus--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__actions{position:absolute;right:100%;opacity:0;display:-webkit-box;display:-ms-flexbox;display:flex;padding-right:5px}.ce-toolbar__actions--opened{opacity:1}@media (max-width: 650px){.ce-toolbar__actions{right:auto}}.ce-toolbar__settings-btn{color:#1d202b;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;margin-left:3px;cursor:pointer;user-select:none}@media (max-width: 650px){.ce-toolbar__settings-btn{width:36px;height:36px}}@media (hover: hover){.ce-toolbar__settings-btn:hover{background-color:#eff2f5}}.ce-toolbar__settings-btn--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@media (min-width: 651px){.ce-toolbar__settings-btn{width:24px}}.ce-toolbar__settings-btn--hidden{display:none}@media (max-width: 650px){.ce-toolbar__settings-btn{position:absolute;background-color:#fff;border:1px solid #E8E8EB;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;z-index:2;position:static}.ce-toolbar__settings-btn--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__settings-btn--right-oriented:before{left:auto;right:15px;margin-left:0}}.ce-toolbar__plus svg,.ce-toolbar__settings-btn svg{width:24px;height:24px}@media (min-width: 651px){.codex-editor--narrow .ce-toolbar__plus{left:5px}}@media (min-width: 651px){.codex-editor--narrow .ce-toolbox .ce-popover{right:0;left:auto;left:initial}}.ce-inline-toolbar{--y-offset: 8px;--color-background-icon-active: rgba(56, 138, 229, .1);--color-text-icon-active: #388AE5;--color-text-primary: black;position:absolute;visibility:hidden;-webkit-transition:opacity .25s ease;transition:opacity .25s ease;will-change:opacity,left,top;top:0;left:0;z-index:3;opacity:1;visibility:visible}.ce-inline-toolbar [hidden]{display:none!important}.ce-inline-toolbar__toggler-and-button-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;padding:0 6px}.ce-inline-toolbar__buttons{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown{display:-webkit-box;display:-ms-flexbox;display:flex;padding:6px;margin:0 6px 0 -6px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer;border-right:1px solid rgba(201,201,204,.48);-webkit-box-sizing:border-box;box-sizing:border-box}@media (hover: hover){.ce-inline-toolbar__dropdown:hover{background:#eff2f5}}.ce-inline-toolbar__dropdown--hidden{display:none}.ce-inline-toolbar__dropdown-content,.ce-inline-toolbar__dropdown-arrow{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown-content svg,.ce-inline-toolbar__dropdown-arrow svg{width:20px;height:20px}.ce-inline-toolbar__shortcut{opacity:.6;word-spacing:-3px;margin-top:3px}.ce-inline-tool{color:var(--color-text-primary);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:0;border-radius:4px;line-height:normal;height:100%;padding:0;width:28px;background-color:transparent;cursor:pointer}@media (max-width: 650px){.ce-inline-tool{width:36px;height:36px}}@media (hover: hover){.ce-inline-tool:hover{background-color:#f8f8f8}}.ce-inline-tool svg{display:block;width:20px;height:20px}@media (max-width: 650px){.ce-inline-tool svg{width:28px;height:28px}}.ce-inline-tool--link .icon--unlink,.ce-inline-tool--unlink .icon--link{display:none}.ce-inline-tool--unlink .icon--unlink{display:inline-block;margin-bottom:-1px}.ce-inline-tool-input{background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:4px 8px;font-size:14px;line-height:22px;outline:none;margin:0;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;display:none;font-weight:500;-webkit-appearance:none;font-family:inherit}@media (max-width: 650px){.ce-inline-tool-input{font-size:15px;font-weight:500}}.ce-inline-tool-input::-webkit-input-placeholder{color:#707684}.ce-inline-tool-input::-moz-placeholder{color:#707684}.ce-inline-tool-input:-ms-input-placeholder{color:#707684}.ce-inline-tool-input::-ms-input-placeholder{color:#707684}.ce-inline-tool-input::placeholder{color:#707684}.ce-inline-tool-input--showed{display:block}.ce-inline-tool--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}@-webkit-keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.ce-block{-webkit-animation:fade-in .3s ease;animation:fade-in .3s ease;-webkit-animation-fill-mode:none;animation-fill-mode:none;-webkit-animation-fill-mode:initial;animation-fill-mode:initial}.ce-block:first-of-type{margin-top:0}.ce-block--selected .ce-block__content{background:#e1f2ff}.ce-block--selected .ce-block__content [contenteditable]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ce-block--selected .ce-block__content img,.ce-block--selected .ce-block__content .ce-stub{opacity:.55}.ce-block--stretched .ce-block__content{max-width:none}.ce-block__content{position:relative;max-width:650px;margin:0 auto;-webkit-transition:background-color .15s ease;transition:background-color .15s ease}.ce-block--drop-target .ce-block__content:before{content:"";position:absolute;top:100%;left:-20px;margin-top:-1px;height:8px;width:8px;border:solid #388AE5;border-width:1px 1px 0 0;-webkit-transform-origin:right;transform-origin:right;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ce-block--drop-target .ce-block__content:after{content:"";position:absolute;top:100%;height:1px;width:100%;color:#388ae5;background:repeating-linear-gradient(90deg,#388AE5,#388AE5 1px,#fff 1px,#fff 6px)}.ce-block a{cursor:pointer;-webkit-text-decoration:underline;text-decoration:underline}.ce-block b{font-weight:700}.ce-block i{font-style:italic}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@-webkit-keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}@keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scale3d(1,1,1);transform:scaleZ(1)}}.cdx-block{padding:.4em 0}.cdx-block::-webkit-input-placeholder{line-height:normal!important}.cdx-input{border:1px solid rgba(201,201,204,.48);-webkit-box-shadow:inset 0 1px 2px 0 rgba(35,44,72,.06);box-shadow:inset 0 1px 2px #232c480f;border-radius:3px;padding:10px 12px;outline:none;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.cdx-input[data-placeholder]:before{position:static!important}.cdx-input[data-placeholder]:before{display:inline-block;width:0;white-space:nowrap;pointer-events:none}.cdx-settings-button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0;min-width:26px;min-height:26px}.cdx-settings-button--focused{background:rgba(34,186,255,.08)!important}.cdx-settings-button--focused{-webkit-box-shadow:inset 0 0 0px 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px #07a1e314}.cdx-settings-button--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.cdx-settings-button--active{color:#388ae5}.cdx-settings-button svg{width:auto;height:auto}@media (max-width: 650px){.cdx-settings-button svg{width:28px;height:28px}}@media (max-width: 650px){.cdx-settings-button{width:36px;height:36px;border-radius:8px}}@media (hover: hover){.cdx-settings-button:hover{background-color:#eff2f5}}.cdx-loader{position:relative;border:1px solid rgba(201,201,204,.48)}.cdx-loader:before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-11px 0 0 -11px;border:2px solid rgba(201,201,204,.48);border-left-color:#388ae5;border-radius:50%;-webkit-animation:cdxRotation 1.2s infinite linear;animation:cdxRotation 1.2s infinite linear}@-webkit-keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.cdx-button{padding:13px;border-radius:3px;border:1px solid rgba(201,201,204,.48);font-size:14.9px;background:#fff;-webkit-box-shadow:0 2px 2px 0 rgba(18,30,57,.04);box-shadow:0 2px 2px #121e390a;color:#707684;text-align:center;cursor:pointer}@media (hover: hover){.cdx-button:hover{background:#FBFCFE;-webkit-box-shadow:0 1px 3px 0 rgba(18,30,57,.08);box-shadow:0 1px 3px #121e3914}}.cdx-button svg{height:20px;margin-right:.2em;margin-top:-2px}.ce-stub{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:12px 18px;margin:10px 0;border-radius:10px;background:#eff2f5;border:1px solid #EFF0F1;color:#707684;font-size:14px}.ce-stub svg{width:20px;height:20px}.ce-stub__info{margin-left:14px}.ce-stub__title{font-weight:500;text-transform:capitalize}.codex-editor.codex-editor--rtl{direction:rtl}.codex-editor.codex-editor--rtl .cdx-list{padding-left:0;padding-right:40px}.codex-editor.codex-editor--rtl .ce-toolbar__plus{right:-26px;left:auto}.codex-editor.codex-editor--rtl .ce-toolbar__actions{right:auto;left:-26px}@media (max-width: 650px){.codex-editor.codex-editor--rtl .ce-toolbar__actions{margin-left:0;margin-right:auto;padding-right:0;padding-left:10px}}.codex-editor.codex-editor--rtl .ce-settings{left:5px;right:auto}.codex-editor.codex-editor--rtl .ce-settings:before{right:auto;left:25px}.codex-editor.codex-editor--rtl .ce-settings__button:not(:nth-child(3n+3)){margin-left:3px;margin-right:0}.codex-editor.codex-editor--rtl .ce-conversion-tool__icon{margin-right:0;margin-left:10px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown{border-right:0px solid transparent;border-left:1px solid rgba(201,201,204,.48);margin:0 -6px 0 6px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown .icon--toggler-down{margin-left:0;margin-right:4px}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__plus{left:0;right:5px}}@media (min-width: 651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__actions{left:-5px}}.cdx-search-field{--icon-margin-right: 10px;background:#F8F8F8;border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:2px;display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto}.cdx-search-field__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:var(--icon-margin-right)}.cdx-search-field__icon svg{width:20px;height:20px;color:#707684}.cdx-search-field__input{font-size:14px;outline:none;font-weight:500;font-family:inherit;border:0;background:transparent;margin:0;padding:0;line-height:22px;min-width:calc(100% - 26px - var(--icon-margin-right))}.cdx-search-field__input::-webkit-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-moz-placeholder{color:#707684;font-weight:500}.cdx-search-field__input:-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::placeholder{color:#707684;font-weight:500}.ce-popover{--border-radius: 6px;--width: 200px;--max-height: 270px;--padding: 6px;--offset-from-target: 8px;--color-border: #EFF0F1;--color-shadow: rgba(13, 20, 33, .1);--color-background: white;--color-text-primary: black;--color-text-secondary: #707684;--color-border-icon: rgba(201, 201, 204, .48);--color-border-icon-disabled: #EFF0F1;--color-text-icon-active: #388AE5;--color-background-icon-active: rgba(56, 138, 229, .1);--color-background-item-focus: rgba(34, 186, 255, .08);--color-shadow-item-focus: rgba(7, 161, 227, .08);--color-background-item-hover: #F8F8F8;--color-background-item-confirm: #E24A4A;--color-background-item-confirm-hover: #CE4343;--popover-top: calc(100% + var(--offset-from-target));--popover-left: 0;--nested-popover-overlap: 4px;--icon-size: 20px;--item-padding: 3px;--item-height: calc(var(--icon-size) + 2 * var(--item-padding))}.ce-popover__container{min-width:var(--width);width:var(--width);max-height:var(--max-height);border-radius:var(--border-radius);overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:0px 3px 15px -3px var(--color-shadow);box-shadow:0 3px 15px -3px var(--color-shadow);position:absolute;left:var(--popover-left);top:var(--popover-top);background:var(--color-background);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;z-index:4;opacity:0;max-height:0;pointer-events:none;padding:0;border:none}.ce-popover--opened>.ce-popover__container{opacity:1;padding:var(--padding);max-height:var(--max-height);pointer-events:auto;-webkit-animation:panelShowing .1s ease;animation:panelShowing .1s ease;border:1px solid var(--color-border)}@media (max-width: 650px){.ce-popover--opened>.ce-popover__container{-webkit-animation:panelShowingMobile .25s ease;animation:panelShowingMobile .25s ease}}.ce-popover--open-top .ce-popover__container{--popover-top: calc(-1 * (var(--offset-from-target) + var(--popover-height)))}.ce-popover--open-left .ce-popover__container{--popover-left: calc(-1 * var(--width) + 100%)}.ce-popover__items{overflow-y:auto;-ms-scroll-chaining:none;overscroll-behavior:contain}@media (max-width: 650px){.ce-popover__overlay{position:fixed;top:0;bottom:0;left:0;right:0;background:#1D202B;z-index:3;opacity:.5;-webkit-transition:opacity .12s ease-in;transition:opacity .12s ease-in;will-change:opacity;visibility:visible}}.ce-popover__overlay--hidden{display:none}@media (max-width: 650px){.ce-popover .ce-popover__container{--offset: 5px;position:fixed;max-width:none;min-width:calc(100% - var(--offset) * 2);left:var(--offset);right:var(--offset);bottom:calc(var(--offset) + env(safe-area-inset-bottom));top:auto;border-radius:10px}}.ce-popover__search{margin-bottom:5px}.ce-popover__nothing-found-message{color:#707684;display:none;cursor:default;padding:3px;font-size:14px;line-height:20px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ce-popover__nothing-found-message--displayed{display:block}.ce-popover--nested .ce-popover__container{--popover-left: calc(var(--nesting-level) * (var(--width) - var(--nested-popover-overlap)));top:calc(var(--trigger-item-top) - var(--nested-popover-overlap));position:absolute}.ce-popover--open-top.ce-popover--nested .ce-popover__container{top:calc(var(--trigger-item-top) - var(--popover-height) + var(--item-height) + var(--offset-from-target) + var(--nested-popover-overlap))}.ce-popover--open-left .ce-popover--nested .ce-popover__container{--popover-left: calc(-1 * (var(--nesting-level) + 1) * var(--width) + 100%)}.ce-popover-item-separator{padding:4px 3px}.ce-popover-item-separator--hidden{display:none}.ce-popover-item-separator__line{height:1px;background:var(--color-border);width:100%}.ce-popover-item-html--hidden{display:none}.ce-popover-item{--border-radius: 6px;border-radius:var(--border-radius);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:var(--item-padding);color:var(--color-text-primary);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:none;background:transparent}@media (max-width: 650px){.ce-popover-item{padding:4px}}.ce-popover-item:not(:last-of-type){margin-bottom:1px}.ce-popover-item__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover-item__icon{width:36px;height:36px;border-radius:8px}.ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover-item__icon--tool{margin-right:4px}.ce-popover-item__title{font-size:14px;line-height:20px;font-weight:500;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-right:auto}@media (max-width: 650px){.ce-popover-item__title{font-size:16px}}.ce-popover-item__secondary-title{color:var(--color-text-secondary);font-size:12px;white-space:nowrap;letter-spacing:-.1em;padding-right:5px;opacity:.6}@media (max-width: 650px){.ce-popover-item__secondary-title{display:none}}.ce-popover-item--active{background:var(--color-background-icon-active);color:var(--color-text-icon-active)}.ce-popover-item--disabled{color:var(--color-text-secondary);cursor:default;pointer-events:none}.ce-popover-item--focused:not(.ce-popover-item--no-focus){background:var(--color-background-item-focus)!important}.ce-popover-item--hidden{display:none}@media (hover: hover){.ce-popover-item:hover{cursor:pointer}.ce-popover-item:hover:not(.ce-popover-item--no-hover){background-color:var(--color-background-item-hover)}}.ce-popover-item--confirmation{background:var(--color-background-item-confirm)}.ce-popover-item--confirmation .ce-popover-item__title,.ce-popover-item--confirmation .ce-popover-item__icon{color:#fff}@media (hover: hover){.ce-popover-item--confirmation:not(.ce-popover-item--no-hover):hover{background:var(--color-background-item-confirm-hover)}}.ce-popover-item--confirmation:not(.ce-popover-item--no-focus).ce-popover-item--focused{background:var(--color-background-item-confirm-hover)!important}@-webkit-keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.wobble{-webkit-animation-name:wobble;animation-name:wobble;-webkit-animation-duration:.4s;animation-duration:.4s}@-webkit-keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}@keyframes wobble{0%{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-9%,0,0);transform:translate3d(-9%,0,0)}30%{-webkit-transform:translate3d(9%,0,0);transform:translate3d(9%,0,0)}45%{-webkit-transform:translate3d(-4%,0,0);transform:translate3d(-4%,0,0)}60%{-webkit-transform:translate3d(4%,0,0);transform:translate3d(4%,0,0)}75%{-webkit-transform:translate3d(-1%,0,0);transform:translate3d(-1%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translateZ(0)}}.ce-popover-header{margin-bottom:8px;margin-top:4px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover-header__text{font-size:18px;font-weight:600}.ce-popover-header__back-button{border:0;background:transparent;width:36px;height:36px;color:var(--color-text-primary)}.ce-popover-header__back-button svg{display:block;width:28px;height:28px}.ce-popover--inline{--height: 38px;--height-mobile: 46px;--container-padding: 4px;position:relative}.ce-popover--inline .ce-popover__custom-content{margin-bottom:0}.ce-popover--inline .ce-popover__items{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-popover--inline .ce-popover__container{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;padding:var(--container-padding);height:var(--height);top:0;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;width:-webkit-max-content;width:-moz-max-content;width:max-content;-webkit-animation:none;animation:none}@media (max-width: 650px){.ce-popover--inline .ce-popover__container{height:var(--height-mobile);position:absolute}}.ce-popover--inline .ce-popover-item-separator{padding:0 4px}.ce-popover--inline .ce-popover-item-separator__line{height:100%;width:1px}.ce-popover--inline .ce-popover-item{border-radius:4px;padding:4px}.ce-popover--inline .ce-popover-item__icon--tool{-webkit-box-shadow:none;box-shadow:none;background:transparent;margin-right:0}.ce-popover--inline .ce-popover-item__icon{width:auto;width:initial;height:auto;height:initial}.ce-popover--inline .ce-popover-item__icon svg{width:20px;height:20px}@media (max-width: 650px){.ce-popover--inline .ce-popover-item__icon svg{width:28px;height:28px}}.ce-popover--inline .ce-popover-item:not(:last-of-type){margin-bottom:0;margin-bottom:initial}.ce-popover--inline .ce-popover-item-html{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-popover--inline .ce-popover-item__icon--chevron-right{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{--offset: 3px;left:0;top:calc(var(--height) + var(--offset))}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested-level-1 .ce-popover__container{top:calc(var(--height-mobile) + var(--offset))}}.ce-popover--inline .ce-popover--nested .ce-popover__container{min-width:var(--width);width:var(--width);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;padding:6px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.ce-popover--inline .ce-popover--nested .ce-popover__items{display:block;width:100%}.ce-popover--inline .ce-popover--nested .ce-popover-item{border-radius:6px;padding:3px}@media (max-width: 650px){.ce-popover--inline .ce-popover--nested .ce-popover-item{padding:4px}}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon--tool{margin-right:4px}.ce-popover--inline .ce-popover--nested .ce-popover-item__icon{width:26px;height:26px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator{padding:4px 3px}.ce-popover--inline .ce-popover--nested .ce-popover-item-separator__line{width:100%;height:1px}.codex-editor [data-placeholder]:empty:before,.codex-editor [data-placeholder][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text;content:attr(data-placeholder)}.codex-editor [data-placeholder-active]:empty:before,.codex-editor [data-placeholder-active][data-empty=true]:before{pointer-events:none;color:#707684;cursor:text}.codex-editor [data-placeholder-active]:empty:focus:before,.codex-editor [data-placeholder-active][data-empty=true]:focus:before{content:attr(data-placeholder-active)} `; class Ia extends E { constructor() { super(...arguments), this.isMobile = !1, this.contentRectCache = null, this.resizeDebouncer = Eo(() => { this.windowResize(); }, 200), this.selectionChangeDebounced = Eo(() => { this.selectionChanged(); }, da), this.documentTouchedListener = (e) => { this.documentTouched(e); }; } /** * Editor.js UI CSS class names * * @returns {{editorWrapper: string, editorZone: string}} */ get CSS() { return { editorWrapper: "codex-editor", editorWrapperNarrow: "codex-editor--narrow", editorZone: "codex-editor__redactor", editorZoneHidden: "codex-editor__redactor--hidden", editorEmpty: "codex-editor--empty", editorRtlFix: "codex-editor--rtl" }; } /** * Return Width of center column of Editor * * @returns {DOMRect} */ get contentRect() { if (this.contentRectCache !== null) return this.contentRectCache; const e = this.nodes.wrapper.querySelector(`.${R.CSS.content}`); return e ? (this.contentRectCache = e.getBoundingClientRect(), this.contentRectCache) : { width: 650, left: 0, right: 0 }; } /** * Making main interface */ async prepare() { this.setIsMobile(), this.make(), this.loadStyles(); } /** * Toggle read-only state * * If readOnly is true: * - removes all listeners from main UI module elements * * if readOnly is false: * - enables all listeners to UI module elements * * @param {boolean} readOnlyEnabled - "read only" state */ toggleReadOnly(e) { e ? this.unbindReadOnlySensitiveListeners() : window.requestIdleCallback(() => { this.bindReadOnlySensitiveListeners(); }, { timeout: 2e3 }); } /** * Check if Editor is empty and set CSS class to wrapper */ checkEmptiness() { const { BlockManager: e } = this.Editor; this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty, e.isEditorEmpty); } /** * Check if one of Toolbar is opened * Used to prevent global keydowns (for example, Enter) conflicts with Enter-on-toolbar * * @returns {boolean} */ get someToolbarOpened() { const { Toolbar: e, BlockSettings: t, InlineToolbar: o } = this.Editor; return !!(t.opened || o.opened || e.toolbox.opened); } /** * Check for some Flipper-buttons is under focus */ get someFlipperButtonFocused() { return this.Editor.Toolbar.toolbox.hasFocus() ? !0 : Object.entries(this.Editor).filter(([e, t]) => t.flipper instanceof ce).some(([e, t]) => t.flipper.hasFocus()); } /** * Clean editor`s UI */ destroy() { this.nodes.holder.innerHTML = "", this.unbindReadOnlyInsensitiveListeners(); } /** * Close all Editor's toolbars */ closeAllToolbars() { const { Toolbar: e, BlockSettings: t, InlineToolbar: o } = this.Editor; t.close(), o.close(), e.toolbox.close(); } /** * Check for mobile mode and save the result */ setIsMobile() { const e = window.innerWidth < Ro; e !== this.isMobile && this.eventsDispatcher.emit(Te, { isEnabled: this.isMobile }), this.isMobile = e; } /** * Makes Editor.js interface */ make() { this.nodes.holder = d.getHolder(this.config.holder), this.nodes.wrapper = d.make("div", [ this.CSS.editorWrapper, ...this.isRtl ? [this.CSS.editorRtlFix] : [] ]), this.nodes.redactor = d.make("div", this.CSS.editorZone), this.nodes.holder.offsetWidth < this.contentRect.width && this.nodes.wrapper.classList.add(this.CSS.editorWrapperNarrow), this.nodes.redactor.style.paddingBottom = this.config.minHeight + "px", this.nodes.wrapper.appendChild(this.nodes.redactor), this.nodes.holder.appendChild(this.nodes.wrapper), this.bindReadOnlyInsensitiveListeners(); } /** * Appends CSS */ loadStyles() { const e = "editor-js-styles"; if (d.get(e)) return; const t = d.make("style", null, { id: e, textContent: Sa.toString() }); this.config.style && !V(this.config.style) && this.config.style.nonce && t.setAttribute("nonce", this.config.style.nonce), d.prepend(document.head, t); } /** * Adds listeners that should work both in read-only and read-write modes */ bindReadOnlyInsensitiveListeners() { this.listeners.on(document, "selectionchange", this.selectionChangeDebounced), this.listeners.on(window, "resize", this.resizeDebouncer, { passive: !0 }), this.listeners.on(this.nodes.redactor, "mousedown", this.documentTouchedListener, { capture: !0, passive: !0 }), this.listeners.on(this.nodes.redactor, "touchstart", this.documentTouchedListener, { capture: !0, passive: !0 }); } /** * Removes listeners that should work both in read-only and read-write modes */ unbindReadOnlyInsensitiveListeners() { this.listeners.off(document, "selectionchange", this.selectionChangeDebounced), this.listeners.off(window, "resize", this.resizeDebouncer), this.listeners.off(this.nodes.redactor, "mousedown", this.documentTouchedListener), this.listeners.off(this.nodes.redactor, "touchstart", this.documentTouchedListener); } /** * Adds listeners that should work only in read-only mode */ bindReadOnlySensitiveListeners() { this.readOnlyMutableListeners.on(this.nodes.redactor, "click", (e) => { this.redactorClicked(e); }, !1), this.readOnlyMutableListeners.on(document, "keydown", (e) => { this.documentKeydown(e); }, !0), this.readOnlyMutableListeners.on(document, "mousedown", (e) => { this.documentClicked(e); }, !0), this.watchBlockHoveredEvents(), this.enableInputsEmptyMark(); } /** * Listen redactor mousemove to emit 'block-hovered' event */ watchBlockHoveredEvents() { let e; this.readOnlyMutableListeners.on(this.nodes.redactor, "mousemove", dt((t) => { const o = t.target.closest(".ce-block"); this.Editor.BlockSelection.anyBlockSelected || o && e !== o && (e = o, this.eventsDispatcher.emit(ln, { block: this.Editor.BlockManager.getBlockByChildNode(o) })); }, 20), { passive: !0 }); } /** * Unbind events that should work only in read-only mode */ unbindReadOnlySensitiveListeners() { this.readOnlyMutableListeners.clearAll(); } /** * Resize window handler */ windowResize() { this.contentRectCache = null, this.setIsMobile(); } /** * All keydowns on document * * @param {KeyboardEvent} event - keyboard event */ documentKeydown(e) { switch (e.keyCode) { case y.ENTER: this.enterPressed(e); break; case y.BACKSPACE: case y.DELETE: this.backspacePressed(e); break; case y.ESC: this.escapePressed(e); break; default: this.defaultBehaviour(e); break; } } /** * Ignore all other document's keydown events * * @param {KeyboardEvent} event - keyboard event */ defaultBehaviour(e) { const { currentBlock: t } = this.Editor.BlockManager, o = e.target.closest(`.${this.CSS.editorWrapper}`), i = e.altKey || e.ctrlKey || e.metaKey || e.shiftKey; if (t !== void 0 && o === null) { this.Editor.BlockEvents.keydown(e); return; } o || t && i || (this.Editor.BlockManager.unsetCurrentBlock(), this.Editor.Toolbar.close()); } /** * @param {KeyboardEvent} event - keyboard event */ backspacePressed(e) { const { BlockManager: t, BlockSelection: o, Caret: i } = this.Editor; if (o.anyBlockSelected && !b.isSelectionExists) { const s = t.removeSelectedBlocks(), r = t.insertDefaultBlockAtIndex(s, !0); i.setToBlock(r, i.positions.START), o.clearSelection(e), e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(); } } /** * Escape pressed * If some of Toolbar components are opened, then close it otherwise close Toolbar * * @param {Event} event - escape keydown event */ escapePressed(e) { this.Editor.BlockSelection.clearSelection(e), this.Editor.Toolbar.toolbox.opened ? (this.Editor.Toolbar.toolbox.close(), this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock, this.Editor.Caret.positions.END)) : this.Editor.BlockSettings.opened ? this.Editor.BlockSettings.close() : this.Editor.InlineToolbar.opened ? this.Editor.InlineToolbar.close() : this.Editor.Toolbar.close(); } /** * Enter pressed on document * * @param {KeyboardEvent} event - keyboard event */ enterPressed(e) { const { BlockManager: t, BlockSelection: o } = this.Editor; if (this.someToolbarOpened) return; const i = t.currentBlockIndex >= 0; if (o.anyBlockSelected && !b.isSelectionExists) { o.clearSelection(e), e.preventDefault(), e.stopImmediatePropagation(), e.stopPropagation(); return; } if (!this.someToolbarOpened && i && e.target.tagName === "BODY") { const s = this.Editor.BlockManager.insert(); e.preventDefault(), this.Editor.Caret.setToBlock(s), this.Editor.Toolbar.moveAndOpen(s); } this.Editor.BlockSelection.clearSelection(e); } /** * All clicks on document * * @param {MouseEvent} event - Click event */ documentClicked(e) { var a, l; if (!e.isTrusted) return; const t = e.target; this.nodes.holder.contains(t) || b.isAtEditor || (this.Editor.BlockManager.unsetCurrentBlock(), this.Editor.Toolbar.close()); const i = (a = this.Editor.BlockSettings.nodes.wrapper) == null ? void 0 : a.contains(t), s = (l = this.Editor.Toolbar.nodes.settingsToggler) == null ? void 0 : l.contains(t), r = i || s; if (this.Editor.BlockSettings.opened && !r) { this.Editor.BlockSettings.close(); const c = this.Editor.BlockManager.getBlockByChildNode(t); this.Editor.Toolbar.moveAndOpen(c); } this.Editor.BlockSelection.clearSelection(e); } /** * First touch on editor * Fired before click * * Used to change current block — we need to do it before 'selectionChange' event. * Also: * - Move and show the Toolbar * - Set a Caret * * @param event - touch or mouse event */ documentTouched(e) { let t = e.target; if (t === this.nodes.redactor) { const o = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX, i = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY; t = document.elementFromPoint(o, i); } try { this.Editor.BlockManager.setCurrentBlockByChildNode(t); } catch { this.Editor.RectangleSelection.isRectActivated() || this.Editor.Caret.setToTheLastBlock(); } this.Editor.ReadOnly.isEnabled || this.Editor.Toolbar.moveAndOpen(); } /** * All clicks on the redactor zone * * @param {MouseEvent} event - click event * @description * - By clicks on the Editor's bottom zone: * - if last Block is empty, set a Caret to this * - otherwise, add a new empty Block and set a Caret to that */ redactorClicked(e) { if (!b.isCollapsed) return; const t = e.target, o = e.metaKey || e.ctrlKey; if (d.isAnchor(t) && o) { e.stopImmediatePropagation(), e.stopPropagation(); const i = t.getAttribute("href"), s = oi(i); ii(s); return; } this.processBottomZoneClick(e); } /** * Check if user clicks on the Editor's bottom zone: * - set caret to the last block * - or add new empty block * * @param event - click event */ processBottomZoneClick(e) { const t = this.Editor.BlockManager.getBlockByIndex(-1), o = d.offset(t.holder).bottom, i = e.pageY, { BlockSelection: s } = this.Editor; if (e.target instanceof Element && e.target.isEqualNode(this.nodes.redactor) && /** * If there is cross block selection started, target will be equal to redactor so we need additional check */ !s.anyBlockSelected && /** * Prevent caret jumping (to last block) when clicking between blocks */ o < i) { e.stopImmediatePropagation(), e.stopPropagation(); const { BlockManager: a, Caret: l, Toolbar: c } = this.Editor; (!a.lastBlock.tool.isDefault || !a.lastBlock.isEmpty) && a.insertAtEnd(), l.setToTheLastBlock(), c.moveAndOpen(a.lastBlock); } } /** * Handle selection changes on mobile devices * Uses for showing the Inline Toolbar */ selectionChanged() { const { CrossBlockSelection: e, BlockSelection: t } = this.Editor, o = b.anchorElement; if (e.isCrossBlockSelectionStarted && t.anyBlockSelected && b.get().removeAllRanges(), !o) { b.range || this.Editor.InlineToolbar.close(); return; } const i = o.closest(`.${R.CSS.content}`); (i === null || i.closest(`.${b.CSS.editorWrapper}`) !== this.nodes.wrapper) && (this.Editor.InlineToolbar.containsNode(o) || this.Editor.InlineToolbar.close(), !(o.dataset.inlineToolbar === "true")) || (this.Editor.BlockManager.currentBlock || this.Editor.BlockManager.setCurrentBlockByChildNode(o), this.Editor.InlineToolbar.tryToShow(!0)); } /** * Editor.js provides and ability to show placeholders for empty contenteditable elements * * This method watches for input and focus events and toggles 'data-empty' attribute * to workaroud the case, when inputs contains only
s and has no visible content * Then, CSS could rely on this attribute to show placeholders */ enableInputsEmptyMark() { function e(t) { const o = t.target; Do(o); } this.readOnlyMutableListeners.on(this.nodes.wrapper, "input", e), this.readOnlyMutableListeners.on(this.nodes.wrapper, "focusin", e), this.readOnlyMutableListeners.on(this.nodes.wrapper, "focusout", e); } } const Oa = { // API Modules BlocksAPI: gi, CaretAPI: bi, EventsAPI: vi, I18nAPI: kt, API: ki, InlineToolbarAPI: yi, ListenersAPI: wi, NotifierAPI: Ci, ReadOnlyAPI: Ti, SanitizerAPI: Li, SaverAPI: Pi, SelectionAPI: Ni, ToolsAPI: Ri, StylesAPI: Di, ToolbarAPI: Fi, TooltipAPI: Ui, UiAPI: Wi, // Toolbar Modules BlockSettings: ms, Toolbar: Bs, InlineToolbar: Cs, // Modules BlockEvents: na, BlockManager: ra, BlockSelection: aa, Caret: Ye, CrossBlockSelection: la, DragNDrop: ca, ModificationsObserver: ha, Paste: pa, ReadOnly: fa, RectangleSelection: Be, Renderer: ga, Saver: ma, Tools: Wn, UI: Ia }; class _a { /** * @param {EditorConfig} config - user configuration */ constructor(e) { this.moduleInstances = {}, this.eventsDispatcher = new Oe(); let t, o; this.isReady = new Promise((i, s) => { t = i, o = s; }), Promise.resolve().then(async () => { this.configuration = e, this.validate(), this.init(), await this.start(), await this.render(); const { BlockManager: i, Caret: s, UI: r, ModificationsObserver: a } = this.moduleInstances; r.checkEmptiness(), a.enable(), this.configuration.autofocus === !0 && this.configuration.readOnly !== !0 && s.setToBlock(i.blocks[0], s.positions.START), t(); }).catch((i) => { S(`Editor.js is not ready because of ${i}`, "error"), o(i); }); } /** * Setting for configuration * * @param {EditorConfig|string} config - Editor's config to set */ set configuration(e) { var o, i; D(e) ? this.config = { ...e } : this.config = { holder: e }, ht(!!this.config.holderId, "config.holderId", "config.holder"), this.config.holderId && !this.config.holder && (this.config.holder = this.config.holderId, this.config.holderId = null), this.config.holder == null && (this.config.holder = "editorjs"), this.config.logLevel || (this.config.logLevel = Lo.VERBOSE), Zn(this.config.logLevel), ht(!!this.config.initialBlock, "config.initialBlock", "config.defaultBlock"), this.config.defaultBlock = this.config.defaultBlock || this.config.initialBlock || "paragraph", this.config.minHeight = this.config.minHeight !== void 0 ? this.config.minHeight : 300; const t = { type: this.config.defaultBlock, data: {} }; this.config.placeholder = this.config.placeholder || !1, this.config.sanitizer = this.config.sanitizer || { p: !0, b: !0, a: !0 }, this.config.hideToolbar = this.config.hideToolbar ? this.config.hideToolbar : !1, this.config.tools = this.config.tools || {}, this.config.i18n = this.config.i18n || {}, this.config.data = this.config.data || { blocks: [] }, this.config.onReady = this.config.onReady || (() => { }), this.config.onChange = this.config.onChange || (() => { }), this.config.inlineToolbar = this.config.inlineToolbar !== void 0 ? this.config.inlineToolbar : !0, (V(this.config.data) || !this.config.data.blocks || this.config.data.blocks.length === 0) && (this.config.data = { blocks: [t] }), this.config.readOnly = this.config.readOnly || !1, (o = this.config.i18n) != null && o.messages && z.setDictionary(this.config.i18n.messages), this.config.i18n.direction = ((i = this.config.i18n) == null ? void 0 : i.direction) || "ltr"; } /** * Returns private property * * @returns {EditorConfig} */ get configuration() { return this.config; } /** * Checks for required fields in Editor's config */ validate() { const { holderId: e, holder: t } = this.config; if (e && t) throw Error("«holderId» and «holder» param can't assign at the same time."); if (te(t) && !d.get(t)) throw Error(`element with ID «${t}» is missing. Pass correct holder's ID.`); if (t && D(t) && !d.isElement(t)) throw Error("«holder» value must be an Element node"); } /** * Initializes modules: * - make and save instances * - configure */ init() { this.constructModules(), this.configureModules(); } /** * Start Editor! * * Get list of modules that needs to be prepared and return a sequence (Promise) * * @returns {Promise} */ async start() { await [ "Tools", "UI", "BlockManager", "Paste", "BlockSelection", "RectangleSelection", "CrossBlockSelection", "ReadOnly" ].reduce( (t, o) => t.then(async () => { try { await this.moduleInstances[o].prepare(); } catch (i) { if (i instanceof Ho) throw new Error(i.message); S(`Module ${o} was skipped because of %o`, "warn", i); } }), Promise.resolve() ); } /** * Render initial data */ render() { return this.moduleInstances.Renderer.render(this.config.data.blocks); } /** * Make modules instances and save it to the @property this.moduleInstances */ constructModules() { Object.entries(Oa).forEach(([e, t]) => { try { this.moduleInstances[e] = new t({ config: this.configuration, eventsDispatcher: this.eventsDispatcher }); } catch (o) { S("[constructModules]", `Module ${e} skipped because`, "error", o); } }); } /** * Modules instances configuration: * - pass other modules to the 'state' property * - ... */ configureModules() { for (const e in this.moduleInstances) Object.prototype.hasOwnProperty.call(this.moduleInstances, e) && (this.moduleInstances[e].state = this.getModulesDiff(e)); } /** * Return modules without passed name * * @param {string} name - module for witch modules difference should be calculated */ getModulesDiff(e) { const t = {}; for (const o in this.moduleInstances) o !== e && (t[o] = this.moduleInstances[o]); return t; } } /** * Editor.js * * @license Apache-2.0 * @see Editor.js * @author CodeX Team */ class Aa { /** Editor version */ static get version() { return "2.31.0-rc.8"; } /** * @param {EditorConfig|string|undefined} [configuration] - user configuration */ constructor(e) { let t = () => { }; D(e) && A(e.onReady) && (t = e.onReady); const o = new _a(e); this.isReady = o.isReady.then(() => { this.exportAPI(o), t(); }); } /** * Export external API methods * * @param {Core} editor — Editor's instance */ exportAPI(e) { const t = ["configuration"], o = () => { Object.values(e.moduleInstances).forEach((s) => { A(s.destroy) && s.destroy(), s.listeners.removeAll(); }), zi(), e = null; for (const s in this) Object.prototype.hasOwnProperty.call(this, s) && delete this[s]; Object.setPrototypeOf(this, null); }; t.forEach((s) => { this[s] = e[s]; }), this.destroy = o, Object.setPrototypeOf(this, e.moduleInstances.API.methods), delete this.exportAPI, Object.entries({ blocks: { clear: "clear", render: "render" }, caret: { focus: "focus" }, events: { on: "on", off: "off", emit: "emit" }, saver: { save: "save" } }).forEach(([s, r]) => { Object.entries(r).forEach(([a, l]) => { this[l] = e.moduleInstances.API.methods[s][a]; }); }); } } export { Aa as default };